cs89x0isa.c revision 1.18
1/* $NetBSD: cs89x0isa.c,v 1.18 2016/06/10 13:27:14 ozaki-r Exp $ */
2
3/*
4 * Copyright 1997
5 * Digital Equipment Corporation. All rights reserved.
6 *
7 * This software is furnished under license and may be used and
8 * copied only in accordance with the following terms and conditions.
9 * Subject to these conditions, you may download, copy, install,
10 * use, modify and distribute this software in source and/or binary
11 * form. No title or ownership is transferred hereby.
12 *
13 * 1) Any source code used, modified or distributed must reproduce
14 *    and retain this copyright notice and list of conditions as
15 *    they appear in the source file.
16 *
17 * 2) No right is granted to use any trade name, trademark, or logo of
18 *    Digital Equipment Corporation. Neither the "Digital Equipment
19 *    Corporation" name nor any trademark or logo of Digital Equipment
20 *    Corporation may be used to endorse or promote products derived
21 *    from this software without the prior written permission of
22 *    Digital Equipment Corporation.
23 *
24 * 3) This software is provided "AS-IS" and any express or implied
25 *    warranties, including but not limited to, any implied warranties
26 *    of merchantability, fitness for a particular purpose, or
27 *    non-infringement are disclaimed. In no event shall DIGITAL be
28 *    liable for any damages whatsoever, and in particular, DIGITAL
29 *    shall not be liable for special, indirect, consequential, or
30 *    incidental damages or damages for lost profits, loss of
31 *    revenue or loss of use, whether such damages arise in contract,
32 *    negligence, tort, under statute, in equity, at law or otherwise,
33 *    even if advised of the possibility of such damage.
34 */
35
36/* isa DMA routines for cs89x0 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD: cs89x0isa.c,v 1.18 2016/06/10 13:27:14 ozaki-r Exp $");
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/mbuf.h>
44#include <sys/socket.h>
45#include <sys/device.h>
46
47#include <net/if.h>
48#include <net/if_ether.h>
49#include <net/if_media.h>
50
51#include <sys/bus.h>
52
53#include <dev/isa/isareg.h>
54#include <dev/isa/isavar.h>
55#include <dev/isa/isadmavar.h>
56
57#include <dev/ic/cs89x0reg.h>
58#include <dev/ic/cs89x0var.h>
59#include <dev/isa/cs89x0isavar.h>
60
61#define DMA_STATUS_BITS 0x0007	/* bit masks for checking DMA status */
62#define DMA_STATUS_OK 0x0004
63
64void
65cs_isa_dma_attach(struct cs_softc *sc)
66{
67	struct cs_softc_isa *isc = (struct cs_softc_isa *)sc;
68
69	if (isc->sc_drq == ISA_UNKNOWN_DRQ)
70		printf("%s: DMA channel unspecified, not using DMA\n",
71		    device_xname(sc->sc_dev));
72	else if (isc->sc_drq < 5 || isc->sc_drq > 7)
73		printf("%s: invalid DMA channel, not using DMA\n",
74		    device_xname(sc->sc_dev));
75	else {
76		bus_size_t maxsize;
77		bus_addr_t dma_addr;
78
79		maxsize = isa_dmamaxsize(isc->sc_ic, isc->sc_drq);
80		if (maxsize < CS8900_DMASIZE) {
81			printf("%s: max DMA size %lu is"
82			    " less than required %d\n",
83			    device_xname(sc->sc_dev), (u_long)maxsize,
84			    CS8900_DMASIZE);
85			goto after_dma_block;
86		}
87
88		if (isa_drq_alloc(isc->sc_ic, isc->sc_drq) != 0) {
89			aprint_error_dev(sc->sc_dev,
90			    "unable to reserve drq %d\n",
91			    isc->sc_drq);
92			goto after_dma_block;
93		}
94
95		if (isa_dmamap_create(isc->sc_ic, isc->sc_drq,
96		    CS8900_DMASIZE, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW) != 0) {
97			aprint_error_dev(sc->sc_dev,
98			    "unable to create ISA DMA map\n");
99			goto after_dma_block;
100		}
101		if (isa_dmamem_alloc(isc->sc_ic, isc->sc_drq,
102		    CS8900_DMASIZE, &dma_addr, BUS_DMA_NOWAIT) != 0) {
103			aprint_error_dev(sc->sc_dev,
104			    "unable to allocate DMA buffer\n");
105			goto after_dma_block;
106		}
107		if (isa_dmamem_map(isc->sc_ic, isc->sc_drq, dma_addr,
108		    CS8900_DMASIZE, (void **)&isc->sc_dmabase,
109		       BUS_DMA_NOWAIT | BUS_DMA_COHERENT /* XXX */ ) != 0) {
110			aprint_error_dev(sc->sc_dev,
111			    "unable to map DMA buffer\n");
112			isa_dmamem_free(isc->sc_ic, isc->sc_drq, dma_addr,
113			    CS8900_DMASIZE);
114			goto after_dma_block;
115		}
116
117		isc->sc_dmasize = CS8900_DMASIZE;
118		sc->sc_cfgflags |= CFGFLG_DMA_MODE;
119		isc->sc_dmaaddr = dma_addr;
120after_dma_block:
121		;
122	}
123}
124
125void cs_isa_dma_chipinit(struct cs_softc *sc)
126{
127	struct cs_softc_isa *isc = (struct cs_softc_isa *)sc;
128
129	if (sc->sc_cfgflags & CFGFLG_DMA_MODE) {
130		/*
131		 * First we program the DMA controller and ensure the memory
132		 * buffer is valid. If it isn't then we just go on without
133		 * DMA.
134		 */
135		if (isa_dmastart(isc->sc_ic, isc->sc_drq, isc->sc_dmabase,
136		    isc->sc_dmasize, NULL, DMAMODE_READ | DMAMODE_LOOPDEMAND,
137		    BUS_DMA_NOWAIT)) {
138			/* XXX XXX XXX */
139			panic("%s: unable to start DMA",
140			    device_xname(sc->sc_dev));
141		}
142		isc->sc_dmacur = isc->sc_dmabase;
143
144		/* interrupt when a DMA'd frame is received */
145		CS_WRITE_PACKET_PAGE(sc, PKTPG_RX_CFG,
146		    RX_CFG_ALL_IE | RX_CFG_RX_DMA_ONLY);
147
148		/*
149		 * set the DMA burst bit so we don't tie up the bus for too
150		 * long.
151		 */
152		if (isc->sc_dmasize == 16384) {
153			CS_WRITE_PACKET_PAGE(sc, PKTPG_BUS_CTL,
154			    ((CS_READ_PACKET_PAGE(sc, PKTPG_BUS_CTL) &
155			     ~BUS_CTL_DMA_SIZE) | BUS_CTL_DMA_BURST));
156		} else { /* use 64K */
157			CS_WRITE_PACKET_PAGE(sc, PKTPG_BUS_CTL,
158			    CS_READ_PACKET_PAGE(sc, PKTPG_BUS_CTL) |
159			     BUS_CTL_DMA_SIZE | BUS_CTL_DMA_BURST);
160		}
161
162		CS_WRITE_PACKET_PAGE(sc, PKTPG_DMA_CHANNEL, isc->sc_drq - 5);
163	}
164}
165
166void cs_process_rx_dma(struct cs_softc *sc)
167{
168	struct cs_softc_isa *isc = (struct cs_softc_isa *)sc;
169	struct ifnet *ifp;
170	u_int16_t num_dma_frames;
171	u_int16_t pkt_length;
172	u_int16_t status;
173	u_int to_copy;
174	char *dma_mem_ptr;
175	struct mbuf *m;
176	u_char *pBuff;
177	int pad;
178
179	/* initialise the pointers */
180	ifp = &sc->sc_ethercom.ec_if;
181
182	/* Read the number of frames DMAed. */
183	num_dma_frames = CS_READ_PACKET_PAGE(sc, PKTPG_DMA_FRAME_COUNT);
184	num_dma_frames &= (u_int16_t) (0x0fff);
185
186	/*
187	 * Loop till number of DMA frames ready to read is zero. After
188	 * reading the frame out of memory we must check if any have been
189	 * received while we were processing
190	 */
191	while (num_dma_frames != 0) {
192		dma_mem_ptr = isc->sc_dmacur;
193
194		/*
195		 * process all of the DMA frames in memory
196		 *
197		 * This loop relies on the dma_mem_ptr variable being set to the
198		 * next frames start address.
199		 */
200		for (; num_dma_frames > 0; num_dma_frames--) {
201
202			/*
203			 * Get the length and status of the packet. Only the
204			 * status is guaranteed to be at dma_mem_ptr, ie need
205			 * to check for wraparound before reading the length
206			 */
207			status = *((u_int16_t *) dma_mem_ptr);
208			dma_mem_ptr += 2;
209			if (dma_mem_ptr > (isc->sc_dmabase + isc->sc_dmasize)) {
210				dma_mem_ptr = isc->sc_dmabase;
211			}
212			pkt_length = *((u_int16_t *) dma_mem_ptr);
213			dma_mem_ptr += 2;
214
215			/* Do some sanity checks on the length and status. */
216			if ((pkt_length > ETHER_MAX_LEN) ||
217			    ((status & DMA_STATUS_BITS) != DMA_STATUS_OK)) {
218				/*
219				 * the SCO driver kills the adapter in this
220				 * situation
221				 */
222				/*
223				 * should increment the error count and reset
224				 * the DMA operation.
225				 */
226				printf("%s: cs_process_rx_dma: "
227				    "DMA buffer out of sync about to reset\n",
228				    device_xname(sc->sc_dev));
229				ifp->if_ierrors++;
230
231				/* skip the rest of the DMA buffer */
232				isa_dmaabort(isc->sc_ic, isc->sc_drq);
233
234				/* now reset the chip and reinitialise */
235				cs_init(&sc->sc_ethercom.ec_if);
236				return;
237			}
238			/* Check the status of the received packet. */
239			if (status & RX_EVENT_RX_OK) {
240				/* get a new mbuf */
241				MGETHDR(m, M_DONTWAIT, MT_DATA);
242				if (m == 0) {
243					printf("%s: cs_process_rx_dma: "
244					    "unable to allocate mbuf\n",
245					    device_xname(sc->sc_dev));
246					ifp->if_ierrors++;
247					/*
248					 * couldn't allocate an mbuf so
249					 * things are not good, may as well
250					 * drop all the packets I think.
251					 */
252					CS_READ_PACKET_PAGE(sc,
253					    PKTPG_DMA_FRAME_COUNT);
254
255					/* now reset DMA operation */
256					isa_dmaabort(isc->sc_ic, isc->sc_drq);
257
258					/*
259					 * now reset the chip and
260					 * reinitialise
261					 */
262					cs_init(&sc->sc_ethercom.ec_if);
263					return;
264				}
265				/*
266				 * save processing by always using a mbuf
267				 * cluster, guaranteed to fit packet
268				 */
269				MCLGET(m, M_DONTWAIT);
270				if ((m->m_flags & M_EXT) == 0) {
271					/* couldn't allocate an mbuf cluster */
272					printf("%s: cs_process_rx_dma: "
273					    "unable to allocate a cluster\n",
274					    device_xname(sc->sc_dev));
275					m_freem(m);
276
277					/* skip the frame */
278					CS_READ_PACKET_PAGE(sc, PKTPG_DMA_FRAME_COUNT);
279					isa_dmaabort(isc->sc_ic, isc->sc_drq);
280
281					/*
282					 * now reset the chip and
283					 * reinitialise
284					 */
285					cs_init(&sc->sc_ethercom.ec_if);
286					return;
287				}
288				m_set_rcvif(m, ifp);
289				m->m_pkthdr.len = pkt_length;
290				m->m_len = pkt_length;
291
292				/*
293				 * align ip header on word boundary for
294				 * ipintr
295				 */
296				pad = ALIGN(sizeof(struct ether_header)) -
297				    sizeof(struct ether_header);
298				m->m_data += pad;
299
300				/*
301				 * set up the buffer pointer to point to the
302				 * data area
303				 */
304				pBuff = mtod(m, char *);
305
306				/*
307				 * Read the frame into free_pktbuf
308				 * The buffer is circular buffer, either
309				 * 16K or 64K in length.
310				 *
311				 * need to check where the end of the buffer
312				 * is and go back to the start.
313				 */
314				if ((dma_mem_ptr + pkt_length) <
315				    (isc->sc_dmabase + isc->sc_dmasize)) {
316					/*
317					 * No wrap around. Copy the frame
318					 * header
319					 */
320					memcpy(pBuff, dma_mem_ptr, pkt_length);
321					dma_mem_ptr += pkt_length;
322				} else {
323					to_copy = (u_int)
324					    ((isc->sc_dmabase + isc->sc_dmasize) -
325					    dma_mem_ptr);
326
327					/* Copy the first half of the frame. */
328					memcpy(pBuff, dma_mem_ptr, to_copy);
329					pBuff += to_copy;
330
331					/*
332		                         * Rest of the frame is to be read
333		                         * from the first byte of the DMA
334		                         * memory.
335		                         */
336					/*
337					 * Get the number of bytes leftout in
338					 * the frame.
339					 */
340					to_copy = pkt_length - to_copy;
341
342					dma_mem_ptr = isc->sc_dmabase;
343
344					/* Copy rest of the frame. */
345					memcpy(pBuff, dma_mem_ptr, to_copy);
346					dma_mem_ptr += to_copy;
347				}
348
349				cs_ether_input(sc, m);
350			}
351			/* (status & RX_OK) */
352			else {
353				/* the frame was not received OK */
354				/* Increment the input error count */
355				ifp->if_ierrors++;
356
357				/*
358				 * If debugging is enabled then log error
359				 * messages if we got any.
360				 */
361				if ((ifp->if_flags & IFF_DEBUG) &&
362				    status != REG_NUM_RX_EVENT)
363					cs_print_rx_errors(sc, status);
364			}
365			/*
366			 * now update the current frame pointer. the
367			 * dma_mem_ptr should point to the next packet to be
368			 * received, without the alignment considerations.
369			 *
370			 * The cs8900 pads all frames to start at the next 32bit
371			 * aligned addres. hence we need to pad our offset
372			 * pointer.
373			 */
374			dma_mem_ptr += 3;
375			dma_mem_ptr = (char *)
376			    ((long) dma_mem_ptr & 0xfffffffc);
377			if (dma_mem_ptr < (isc->sc_dmabase + isc->sc_dmasize)) {
378				isc->sc_dmacur = dma_mem_ptr;
379			} else {
380				dma_mem_ptr = isc->sc_dmacur = isc->sc_dmabase;
381			}
382		} /* for all frames */
383		/* Read the number of frames DMAed again. */
384		num_dma_frames = CS_READ_PACKET_PAGE(sc, PKTPG_DMA_FRAME_COUNT);
385		num_dma_frames &= (u_int16_t) (0x0fff);
386	} /* while there are frames left */
387}
388