xref: /netbsd-src/sys/dev/ic/mx98905.c (revision 4b71a66d0f279143147d63ebfcfd8a59499a3684)
1 /*	$NetBSD: mx98905.c,v 1.13 2008/04/28 20:23:50 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Device driver for National Semiconductor DS8390/WD83C690 based ethernet
35  * adapters.
36  *
37  * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
38  *
39  * Copyright (C) 1993, David Greenman.  This software may be used, modified,
40  * copied, distributed, and sold, in both source and binary form provided that
41  * the above copyright and these terms are retained.  Under no circumstances is
42  * the author responsible for the proper functioning of this software, nor does
43  * the author assume any responsibility for damages incurred with its use.
44  */
45 
46 /*
47  * Special routines for the Macronix MX 98905.  For use with the "ne" driver.
48  */
49 
50 /*
51  * <URL:http://mail-index.NetBSD.org/port-arm32/1996/06/23/0005.html>:
52  * There are 2 types of etherh card.  One uses the macronics chipset MX98905
53  * and that chipset has a bug in it, in that it the MSB remote DMA
54  * register does not work.  There is a workaround for this which
55  * should be around soon.  In fact, I think only the buffer ram test
56  * ever transfers more than 256 bytes across the DMA channel, so diabling
57  * it will make the mx stuff work.
58  */
59 
60 #include <sys/param.h>
61 
62 __KERNEL_RCSID(0, "$NetBSD: mx98905.c,v 1.13 2008/04/28 20:23:50 martin Exp $");
63 
64 #include <sys/device.h>
65 #include <sys/mbuf.h>
66 #include <sys/socket.h>
67 #include <sys/syslog.h>
68 #include <sys/systm.h>
69 
70 #include <net/if.h>
71 #include <net/if_ether.h>
72 #include <net/if_media.h>
73 
74 #include <sys/bus.h>
75 
76 #include <dev/ic/dp8390reg.h>
77 #include <dev/ic/dp8390var.h>
78 #include <dev/ic/ne2000reg.h>
79 #include <dev/ic/ne2000var.h>
80 #include <dev/ic/mx98905var.h>
81 
82 #ifndef __BUS_SPACE_HAS_STREAM_METHODS
83 #define	bus_space_write_stream_2	bus_space_write_2
84 #define	bus_space_write_multi_stream_2	bus_space_write_multi_2
85 #define	bus_space_read_multi_stream_2	bus_space_read_multi_2
86 #endif /* __BUS_SPACE_HAS_STREAM_METHODS */
87 
88 static inline void mx98905_write_setup(struct dp8390_softc *, int, int);
89 static inline void mx98905_write_wait(struct dp8390_softc *);
90 
91 void
92 mx98905_attach(struct dp8390_softc *sc)
93 {
94 
95 	sc->ring_copy = mx98905_ring_copy;
96 	sc->write_mbuf = mx98905_write_mbuf;
97 	sc->read_hdr = mx98905_read_hdr;
98 }
99 
100 static inline void
101 mx98905_write_setup(sc, len, buf)
102 	struct dp8390_softc *sc;
103 	int len, buf;
104 {
105 	bus_space_tag_t nict = sc->sc_regt;
106 	bus_space_handle_t nich = sc->sc_regh;
107 
108 	/* Select page 0 registers. */
109 	NIC_BARRIER(nict, nich);
110 	bus_space_write_1(nict, nich, ED_P0_CR,
111 	    ED_CR_RD2 | ED_CR_PAGE_0 | ED_CR_STA);
112 	NIC_BARRIER(nict, nich);
113 
114 	/* Reset remote DMA complete flag. */
115 	bus_space_write_1(nict, nich, ED_P0_ISR, ED_ISR_RDC);
116 	NIC_BARRIER(nict, nich);
117 
118 	/* Set up DMA byte count. */
119 	bus_space_write_1(nict, nich, ED_P0_RBCR0, len);
120 	bus_space_write_1(nict, nich, ED_P0_RBCR1, len >> 8);
121 
122 	/* Set up destination address in NIC mem. */
123 	bus_space_write_1(nict, nich, ED_P0_RSAR0, buf);
124 	bus_space_write_1(nict, nich, ED_P0_RSAR1, buf >> 8);
125 
126 	/* Set remote DMA write. */
127 	NIC_BARRIER(nict, nich);
128 	bus_space_write_1(nict, nich,
129 	    ED_P0_CR, ED_CR_RD1 | ED_CR_PAGE_0 | ED_CR_STA);
130 	NIC_BARRIER(nict, nich);
131 }
132 
133 
134 static inline void
135 mx98905_write_wait(sc)
136 	struct dp8390_softc *sc;
137 {
138 	int maxwait = 100;	/* about 120us */
139 	bus_space_tag_t nict = sc->sc_regt;
140 	bus_space_handle_t nich = sc->sc_regh;
141 
142 	/*
143 	 * Wait for remote DMA to complete.  This is necessary because on the
144 	 * transmit side, data is handled internally by the NIC in bursts, and
145 	 * we can't start another remote DMA until this one completes.  Not
146 	 * waiting causes really bad things to happen - like the NIC wedging
147 	 * the bus.
148 	 */
149 	while (((bus_space_read_1(nict, nich, ED_P0_ISR) & ED_ISR_RDC) !=
150 	    ED_ISR_RDC) && --maxwait) {
151 		bus_space_read_1(nict, nich, ED_P0_CRDA1);
152 		bus_space_read_1(nict, nich, ED_P0_CRDA0);
153 		NIC_BARRIER(nict, nich);
154 		DELAY(1);
155 	}
156 
157 	if (maxwait == 0) {
158 		log(LOG_WARNING,
159 		    "%s: remote transmit DMA failed to complete\n",
160 		    device_xname(sc->sc_dev));
161 		dp8390_reset(sc);
162 	}
163 }
164 
165 /*
166  * Write an mbuf chain to the destination NIC memory address using programmed
167  * I/O.
168  */
169 int
170 mx98905_write_mbuf(sc, m, buf)
171 	struct dp8390_softc *sc;
172 	struct mbuf *m;
173 	int buf;
174 {
175 	struct ne2000_softc *nsc = (struct ne2000_softc *)sc;
176 	bus_space_tag_t nict = sc->sc_regt;
177 	bus_space_handle_t nich = sc->sc_regh;
178 	bus_space_tag_t asict = nsc->sc_asict;
179 	bus_space_handle_t asich = nsc->sc_asich;
180 	int savelen, dmalen, resid, len;
181 	u_int8_t *data, savebyte[2];
182 	int l, leftover;
183 #ifdef DIAGNOSTIC
184 	u_int8_t *lim;
185 #endif
186 	int i;
187 
188 	resid = savelen = m->m_pkthdr.len;
189 
190 	dmalen = min(resid, 254);
191 
192 	mx98905_write_setup(sc, dmalen, buf);
193 
194 	buf += dmalen;
195 	resid -= dmalen;
196 
197 	/*
198 	 * Transfer the mbuf chain to the NIC memory.  NE2000 cards
199 	 * require that data be transferred as words, and only words,
200 	 * so that case requires some extra code to patch over odd-length
201 	 * mbufs.
202 	 */
203 	/* NE2000s are a bit trickier. */
204 	/* Start out with no leftover data. */
205 	leftover = 0;
206 	savebyte[0] = savebyte[1] = 0;
207 
208 	for (; m != 0; m = m->m_next) {
209 		l = m->m_len;
210 		if (l == 0)
211 			continue;
212 		data = mtod(m, u_int8_t *);
213 #ifdef DIAGNOSTIC
214 		lim = data + l;
215 #endif
216 		while (l > 0) {
217 			if (leftover) {
218 				/*
219 				 * Data left over (from mbuf or
220 				 * realignment).  Buffer the next
221 				 * byte, and write it and the leftover
222 				 * data out.
223 				 */
224 				savebyte[1] = *data++;
225 				l--;
226 				bus_space_write_stream_2(asict, asich,
227 				    NE2000_ASIC_DATA, *(u_int16_t *)savebyte);
228 				dmalen -= 2;
229 				leftover = 0;
230 			} else if (BUS_SPACE_ALIGNED_POINTER(data,
231 			    u_int16_t) == 0) {
232 				/* Unaligned data; buffer the next byte. */
233 				savebyte[0] = *data++;
234 				l--;
235 				leftover = 1;
236 			} else {
237 				/*
238 				 * Aligned data; output contiguous
239 				 * words as much as we can, then
240 				 * buffer the remaining byte, if any.
241 				 */
242 				len = min(l, dmalen);
243 				leftover = len & 1;
244 				len &= ~1;
245 				bus_space_write_multi_stream_2(asict,
246 				    asich, NE2000_ASIC_DATA,
247 				    (u_int16_t *)data, len >> 1);
248 				dmalen -= len;
249 				data += len;
250 				if (leftover)
251 					savebyte[0] = *data++;
252 				l -= len + leftover;
253 			}
254 			if (dmalen == 0 && resid > 0) {
255 				mx98905_write_wait(sc);
256 				dmalen = min(resid, 254);
257 
258 				mx98905_write_setup(sc, dmalen, buf);
259 
260 				buf += dmalen;
261 				resid -= dmalen;
262 			}
263 		}
264 		if (l < 0)
265 			panic("mx98905_write_mbuf: negative len");
266 #ifdef DIAGNOSTIC
267 		if (data != lim)
268 			panic("mx98905_write_mbuf: data != lim");
269 #endif
270 	}
271 	if (leftover) {
272 		savebyte[1] = 0;
273 		bus_space_write_stream_2(asict, asich, NE2000_ASIC_DATA,
274 		    *(u_int16_t *)savebyte);
275 	}
276 	if (savelen < ETHER_MIN_LEN - ETHER_CRC_LEN) {
277 		for(i = 0; i < (ETHER_MIN_LEN - ETHER_CRC_LEN - savelen) >> 1;
278 		    i++)
279 			bus_space_write_stream_2(asict, asich,
280 			    NE2000_ASIC_DATA, 0);
281 		savelen = ETHER_MIN_LEN - ETHER_CRC_LEN;
282 	}
283 	NIC_BARRIER(nict, nich);
284 
285 	mx98905_write_wait(sc);
286 
287 	return (savelen);
288 }
289 
290 /*
291  * Given a source and destination address, copy 'amout' of a packet from
292  * the ring buffer into a linear destination buffer.  Takes into account
293  * ring-wrap.
294  */
295 int
296 mx98905_ring_copy(sc, src, vdst, amount)
297 	struct dp8390_softc *sc;
298 	int src;
299 	void *vdst;
300 	u_short amount;
301 {
302 	struct ne2000_softc *nsc = (struct ne2000_softc *)sc;
303 	uint8_t *dst = vdst;
304 	bus_space_tag_t nict = sc->sc_regt;
305 	bus_space_handle_t nich = sc->sc_regh;
306 	bus_space_tag_t asict = nsc->sc_asict;
307 	bus_space_handle_t asich = nsc->sc_asich;
308 	u_short tmp_amount;
309 	int useword = nsc->sc_useword;
310 
311 	/* Does copy wrap to lower addr in ring buffer? */
312 	if (src + amount > sc->mem_end) {
313 		tmp_amount = sc->mem_end - src;
314 
315 		/* Copy amount up to end of NIC memory. */
316 		mx98905_readmem(nict, nich, asict, asich, src,
317 		    (u_int8_t *)dst, tmp_amount, useword);
318 
319 		amount -= tmp_amount;
320 		src = sc->mem_ring;
321 		dst += tmp_amount;
322 	}
323 
324 	mx98905_readmem(nict, nich, asict, asich, src, dst, amount, useword);
325 
326 	return (src + amount);
327 }
328 
329 void
330 mx98905_read_hdr(sc, buf, hdr)
331 	struct dp8390_softc *sc;
332 	int buf;
333 	struct dp8390_ring *hdr;
334 {
335 	struct ne2000_softc *nsc = (struct ne2000_softc *)sc;
336 
337 	mx98905_readmem(sc->sc_regt, sc->sc_regh, nsc->sc_asict, nsc->sc_asich,
338 	    buf, (u_int8_t *)hdr, sizeof(struct dp8390_ring),
339 	    nsc->sc_useword);
340 #if BYTE_ORDER == BIG_ENDIAN
341 	hdr->count = bswap16(hdr->count);
342 #endif
343 }
344 
345 static inline void
346 mx98905_read_setup(bus_space_tag_t nict, bus_space_handle_t nich,
347     int len, int buf)
348 {
349 
350 	/* Select page 0 registers. */
351 	NIC_BARRIER(nict, nich);
352 	bus_space_write_1(nict, nich, ED_P0_CR,
353 	    ED_CR_RD2 | ED_CR_PAGE_0 | ED_CR_STA);
354 	NIC_BARRIER(nict, nich);
355 
356 	/* Set up DMA byte count. */
357 	bus_space_write_1(nict, nich, ED_P0_RBCR0, len);
358 	bus_space_write_1(nict, nich, ED_P0_RBCR1, len >> 8);
359 
360 	/* Set up source address in NIC mem. */
361 	bus_space_write_1(nict, nich, ED_P0_RSAR0, buf);
362 	bus_space_write_1(nict, nich, ED_P0_RSAR1, buf >> 8);
363 
364 	NIC_BARRIER(nict, nich);
365 	bus_space_write_1(nict, nich, ED_P0_CR,
366 	    ED_CR_RD0 | ED_CR_PAGE_0 | ED_CR_STA);
367 
368 	bus_space_barrier(nict, nich, 0, NE2000_NPORTS,
369 			  BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
370 }
371 
372 /*
373  * Given a NIC memory source address and a host memory destination address,
374  * copy 'amount' from NIC to host using programmed i/o.  The 'amount' is
375  * rounded up to a word - ok as long as mbufs are word sized.
376  */
377 void
378 mx98905_readmem(nict, nich, asict, asich, src, dst, amount, useword)
379 	bus_space_tag_t nict;
380 	bus_space_handle_t nich;
381 	bus_space_tag_t asict;
382 	bus_space_handle_t asich;
383 	int src;
384 	u_int8_t *dst;
385 	size_t amount;
386 	int useword;
387 {
388 	int len, resid;
389 
390 	resid = amount;
391 	/* Round up to a word. */
392 	if (resid & 1)
393 		++resid;
394 
395 	while (resid > 0) {
396 		len = min(resid, 254);
397 		mx98905_read_setup(nict, nich, len, src);
398 		if (useword)
399 			bus_space_read_multi_stream_2(asict, asich,
400 			    NE2000_ASIC_DATA, (u_int16_t *)dst, len >> 1);
401 		else
402 			bus_space_read_multi_1(asict, asich, NE2000_ASIC_DATA,
403 			    dst, len);
404 		resid -= len;
405 		src += len;
406 		dst += len;
407 	}
408 }
409