Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 1997, 1998 | |
3 | * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by Bill Paul. | |
16 | * 4. Neither the name of the author nor the names of any co-contributors | |
17 | * may be used to endorse or promote products derived from this software | |
18 | * without specific prior written permission. | |
19 | * | |
20 | * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND | |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD | |
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 | |
30 | * THE POSSIBILITY OF SUCH DAMAGE. | |
31 | * | |
32 | * $FreeBSD: src/sys/pci/if_vr.c,v 1.26.2.13 2003/02/06 04:46:20 silby Exp $ | |
33 | */ | |
34 | ||
35 | /* | |
36 | * VIA Rhine fast ethernet PCI NIC driver | |
37 | * | |
38 | * Supports various network adapters based on the VIA Rhine | |
39 | * and Rhine II PCI controllers, including the D-Link DFE530TX. | |
40 | * Datasheets are available at http://www.via.com.tw. | |
41 | * | |
42 | * Written by Bill Paul <wpaul@ctr.columbia.edu> | |
43 | * Electrical Engineering Department | |
44 | * Columbia University, New York City | |
45 | */ | |
46 | ||
47 | /* | |
48 | * The VIA Rhine controllers are similar in some respects to the | |
49 | * the DEC tulip chips, except less complicated. The controller | |
50 | * uses an MII bus and an external physical layer interface. The | |
51 | * receiver has a one entry perfect filter and a 64-bit hash table | |
52 | * multicast filter. Transmit and receive descriptors are similar | |
53 | * to the tulip. | |
54 | * | |
55 | * The Rhine has a serious flaw in its transmit DMA mechanism: | |
56 | * transmit buffers must be longword aligned. Unfortunately, | |
57 | * FreeBSD doesn't guarantee that mbufs will be filled in starting | |
58 | * at longword boundaries, so we have to do a buffer copy before | |
59 | * transmission. | |
60 | */ | |
61 | ||
b373db6a | 62 | #include "opt_ifpoll.h" |
2b71c8f1 | 63 | |
984263bc MD |
64 | #include <sys/param.h> |
65 | #include <sys/systm.h> | |
66 | #include <sys/sockio.h> | |
67 | #include <sys/mbuf.h> | |
68 | #include <sys/malloc.h> | |
69 | #include <sys/kernel.h> | |
70 | #include <sys/socket.h> | |
78195a76 | 71 | #include <sys/serialize.h> |
1f7ab7c9 MD |
72 | #include <sys/bus.h> |
73 | #include <sys/rman.h> | |
9db4b353 | 74 | #include <sys/interrupt.h> |
984263bc MD |
75 | |
76 | #include <net/if.h> | |
5f23b36c | 77 | #include <net/ifq_var.h> |
984263bc MD |
78 | #include <net/if_arp.h> |
79 | #include <net/ethernet.h> | |
80 | #include <net/if_dl.h> | |
81 | #include <net/if_media.h> | |
b373db6a | 82 | #include <net/if_poll.h> |
984263bc MD |
83 | |
84 | #include <net/bpf.h> | |
85 | ||
86 | #include <vm/vm.h> /* for vtophys */ | |
87 | #include <vm/pmap.h> /* for vtophys */ | |
984263bc | 88 | |
fa23be1c JS |
89 | #include <dev/netif/mii_layer/mii.h> |
90 | #include <dev/netif/mii_layer/miivar.h> | |
984263bc | 91 | |
dcb4b80d | 92 | #include "pcidevs.h" |
1f2de5d4 MD |
93 | #include <bus/pci/pcireg.h> |
94 | #include <bus/pci/pcivar.h> | |
984263bc MD |
95 | |
96 | #define VR_USEIOSPACE | |
97 | ||
fa23be1c | 98 | #include <dev/netif/vr/if_vrreg.h> |
984263bc MD |
99 | |
100 | /* "controller miibus0" required. See GENERIC if you get errors here. */ | |
101 | #include "miibus_if.h" | |
102 | ||
984263bc MD |
103 | #undef VR_USESWSHIFT |
104 | ||
105 | /* | |
106 | * Various supported device vendors/types and their names. | |
107 | */ | |
108 | static struct vr_type vr_devs[] = { | |
726ffe43 | 109 | { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT3043, |
984263bc | 110 | "VIA VT3043 Rhine I 10/100BaseTX" }, |
726ffe43 | 111 | { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT86C100A, |
984263bc | 112 | "VIA VT86C100A Rhine II 10/100BaseTX" }, |
726ffe43 | 113 | { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT6102, |
984263bc | 114 | "VIA VT6102 Rhine II 10/100BaseTX" }, |
726ffe43 | 115 | { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT6105, |
984263bc | 116 | "VIA VT6105 Rhine III 10/100BaseTX" }, |
726ffe43 | 117 | { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT6105M, |
984263bc | 118 | "VIA VT6105M Rhine III 10/100BaseTX" }, |
726ffe43 | 119 | { PCI_VENDOR_DELTA, PCI_PRODUCT_DELTA_RHINEII, |
984263bc | 120 | "Delta Electronics Rhine II 10/100BaseTX" }, |
726ffe43 | 121 | { PCI_VENDOR_ADDTRON, PCI_PRODUCT_ADDTRON_RHINEII, |
984263bc MD |
122 | "Addtron Technology Rhine II 10/100BaseTX" }, |
123 | { 0, 0, NULL } | |
124 | }; | |
125 | ||
fa23be1c JS |
126 | static int vr_probe(device_t); |
127 | static int vr_attach(device_t); | |
128 | static int vr_detach(device_t); | |
129 | ||
130 | static int vr_newbuf(struct vr_softc *, struct vr_chain_onefrag *, | |
131 | struct mbuf *); | |
e9175cc8 | 132 | static int vr_encap(struct vr_softc *, int, struct mbuf * ); |
fa23be1c JS |
133 | |
134 | static void vr_rxeof(struct vr_softc *); | |
135 | static void vr_rxeoc(struct vr_softc *); | |
136 | static void vr_txeof(struct vr_softc *); | |
137 | static void vr_txeoc(struct vr_softc *); | |
138 | static void vr_tick(void *); | |
139 | static void vr_intr(void *); | |
f0a26983 | 140 | static void vr_start(struct ifnet *, struct ifaltq_subque *); |
fa23be1c JS |
141 | static int vr_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); |
142 | static void vr_init(void *); | |
143 | static void vr_stop(struct vr_softc *); | |
144 | static void vr_watchdog(struct ifnet *); | |
145 | static void vr_shutdown(device_t); | |
146 | static int vr_ifmedia_upd(struct ifnet *); | |
147 | static void vr_ifmedia_sts(struct ifnet *, struct ifmediareq *); | |
984263bc MD |
148 | |
149 | #ifdef VR_USESWSHIFT | |
fa23be1c JS |
150 | static void vr_mii_sync(struct vr_softc *); |
151 | static void vr_mii_send(struct vr_softc *, uint32_t, int); | |
984263bc | 152 | #endif |
fa23be1c JS |
153 | static int vr_mii_readreg(struct vr_softc *, struct vr_mii_frame *); |
154 | static int vr_mii_writereg(struct vr_softc *, struct vr_mii_frame *); | |
155 | static int vr_miibus_readreg(device_t, int, int); | |
156 | static int vr_miibus_writereg(device_t, int, int, int); | |
157 | static void vr_miibus_statchg(device_t); | |
158 | ||
159 | static void vr_setcfg(struct vr_softc *, int); | |
fa23be1c JS |
160 | static void vr_setmulti(struct vr_softc *); |
161 | static void vr_reset(struct vr_softc *); | |
162 | static int vr_list_rx_init(struct vr_softc *); | |
163 | static int vr_list_tx_init(struct vr_softc *); | |
b373db6a SZ |
164 | #ifdef IFPOLL_ENABLE |
165 | static void vr_npoll(struct ifnet *, struct ifpoll_info *); | |
166 | static void vr_npoll_compat(struct ifnet *, void *, int); | |
9c095379 | 167 | #endif |
984263bc MD |
168 | |
169 | #ifdef VR_USEIOSPACE | |
170 | #define VR_RES SYS_RES_IOPORT | |
171 | #define VR_RID VR_PCI_LOIO | |
172 | #else | |
173 | #define VR_RES SYS_RES_MEMORY | |
174 | #define VR_RID VR_PCI_LOMEM | |
175 | #endif | |
176 | ||
177 | static device_method_t vr_methods[] = { | |
178 | /* Device interface */ | |
179 | DEVMETHOD(device_probe, vr_probe), | |
180 | DEVMETHOD(device_attach, vr_attach), | |
181 | DEVMETHOD(device_detach, vr_detach), | |
182 | DEVMETHOD(device_shutdown, vr_shutdown), | |
183 | ||
184 | /* bus interface */ | |
185 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
186 | DEVMETHOD(bus_driver_added, bus_generic_driver_added), | |
187 | ||
188 | /* MII interface */ | |
189 | DEVMETHOD(miibus_readreg, vr_miibus_readreg), | |
190 | DEVMETHOD(miibus_writereg, vr_miibus_writereg), | |
191 | DEVMETHOD(miibus_statchg, vr_miibus_statchg), | |
192 | ||
d3c9c58e | 193 | DEVMETHOD_END |
984263bc MD |
194 | }; |
195 | ||
196 | static driver_t vr_driver = { | |
197 | "vr", | |
198 | vr_methods, | |
199 | sizeof(struct vr_softc) | |
200 | }; | |
201 | ||
202 | static devclass_t vr_devclass; | |
203 | ||
32832096 | 204 | DECLARE_DUMMY_MODULE(if_vr); |
aa2b9d05 SW |
205 | DRIVER_MODULE(if_vr, pci, vr_driver, vr_devclass, NULL, NULL); |
206 | DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, NULL, NULL); | |
984263bc MD |
207 | |
208 | #define VR_SETBIT(sc, reg, x) \ | |
209 | CSR_WRITE_1(sc, reg, \ | |
fa23be1c | 210 | CSR_READ_1(sc, reg) | (x)) |
984263bc MD |
211 | |
212 | #define VR_CLRBIT(sc, reg, x) \ | |
213 | CSR_WRITE_1(sc, reg, \ | |
fa23be1c | 214 | CSR_READ_1(sc, reg) & ~(x)) |
984263bc MD |
215 | |
216 | #define VR_SETBIT16(sc, reg, x) \ | |
217 | CSR_WRITE_2(sc, reg, \ | |
fa23be1c | 218 | CSR_READ_2(sc, reg) | (x)) |
984263bc MD |
219 | |
220 | #define VR_CLRBIT16(sc, reg, x) \ | |
221 | CSR_WRITE_2(sc, reg, \ | |
fa23be1c | 222 | CSR_READ_2(sc, reg) & ~(x)) |
984263bc MD |
223 | |
224 | #define VR_SETBIT32(sc, reg, x) \ | |
225 | CSR_WRITE_4(sc, reg, \ | |
fa23be1c | 226 | CSR_READ_4(sc, reg) | (x)) |
984263bc MD |
227 | |
228 | #define VR_CLRBIT32(sc, reg, x) \ | |
229 | CSR_WRITE_4(sc, reg, \ | |
fa23be1c | 230 | CSR_READ_4(sc, reg) & ~(x)) |
984263bc MD |
231 | |
232 | #define SIO_SET(x) \ | |
233 | CSR_WRITE_1(sc, VR_MIICMD, \ | |
fa23be1c | 234 | CSR_READ_1(sc, VR_MIICMD) | (x)) |
984263bc MD |
235 | |
236 | #define SIO_CLR(x) \ | |
237 | CSR_WRITE_1(sc, VR_MIICMD, \ | |
fa23be1c | 238 | CSR_READ_1(sc, VR_MIICMD) & ~(x)) |
984263bc MD |
239 | |
240 | #ifdef VR_USESWSHIFT | |
241 | /* | |
242 | * Sync the PHYs by setting data bit and strobing the clock 32 times. | |
243 | */ | |
fa23be1c JS |
244 | static void |
245 | vr_mii_sync(struct vr_softc *sc) | |
984263bc | 246 | { |
fa23be1c | 247 | int i; |
984263bc MD |
248 | |
249 | SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); | |
250 | ||
251 | for (i = 0; i < 32; i++) { | |
252 | SIO_SET(VR_MIICMD_CLK); | |
253 | DELAY(1); | |
254 | SIO_CLR(VR_MIICMD_CLK); | |
255 | DELAY(1); | |
256 | } | |
984263bc MD |
257 | } |
258 | ||
259 | /* | |
260 | * Clock a series of bits through the MII. | |
261 | */ | |
fa23be1c JS |
262 | static void |
263 | vr_mii_send(struct vr_softc *sc, uint32_t bits, int cnt) | |
984263bc | 264 | { |
fa23be1c | 265 | int i; |
984263bc MD |
266 | |
267 | SIO_CLR(VR_MIICMD_CLK); | |
268 | ||
269 | for (i = (0x1 << (cnt - 1)); i; i >>= 1) { | |
fa23be1c | 270 | if (bits & i) |
984263bc | 271 | SIO_SET(VR_MIICMD_DATAIN); |
fa23be1c | 272 | else |
984263bc | 273 | SIO_CLR(VR_MIICMD_DATAIN); |
984263bc MD |
274 | DELAY(1); |
275 | SIO_CLR(VR_MIICMD_CLK); | |
276 | DELAY(1); | |
277 | SIO_SET(VR_MIICMD_CLK); | |
278 | } | |
279 | } | |
280 | #endif | |
281 | ||
282 | /* | |
283 | * Read an PHY register through the MII. | |
284 | */ | |
fa23be1c JS |
285 | static int |
286 | vr_mii_readreg(struct vr_softc *sc, struct vr_mii_frame *frame) | |
984263bc MD |
287 | #ifdef VR_USESWSHIFT |
288 | { | |
9228feed | 289 | int i, ack; |
984263bc | 290 | |
fa23be1c | 291 | /* Set up frame for RX. */ |
984263bc MD |
292 | frame->mii_stdelim = VR_MII_STARTDELIM; |
293 | frame->mii_opcode = VR_MII_READOP; | |
294 | frame->mii_turnaround = 0; | |
295 | frame->mii_data = 0; | |
296 | ||
297 | CSR_WRITE_1(sc, VR_MIICMD, 0); | |
298 | VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); | |
299 | ||
fa23be1c | 300 | /* Turn on data xmit. */ |
984263bc MD |
301 | SIO_SET(VR_MIICMD_DIR); |
302 | ||
303 | vr_mii_sync(sc); | |
304 | ||
fa23be1c | 305 | /* Send command/address info. */ |
984263bc MD |
306 | vr_mii_send(sc, frame->mii_stdelim, 2); |
307 | vr_mii_send(sc, frame->mii_opcode, 2); | |
308 | vr_mii_send(sc, frame->mii_phyaddr, 5); | |
309 | vr_mii_send(sc, frame->mii_regaddr, 5); | |
310 | ||
fa23be1c | 311 | /* Idle bit. */ |
984263bc MD |
312 | SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); |
313 | DELAY(1); | |
314 | SIO_SET(VR_MIICMD_CLK); | |
315 | DELAY(1); | |
316 | ||
317 | /* Turn off xmit. */ | |
318 | SIO_CLR(VR_MIICMD_DIR); | |
319 | ||
320 | /* Check for ack */ | |
321 | SIO_CLR(VR_MIICMD_CLK); | |
322 | DELAY(1); | |
323 | ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; | |
324 | SIO_SET(VR_MIICMD_CLK); | |
325 | DELAY(1); | |
326 | ||
327 | /* | |
328 | * Now try reading data bits. If the ack failed, we still | |
329 | * need to clock through 16 cycles to keep the PHY(s) in sync. | |
330 | */ | |
331 | if (ack) { | |
332 | for(i = 0; i < 16; i++) { | |
333 | SIO_CLR(VR_MIICMD_CLK); | |
334 | DELAY(1); | |
335 | SIO_SET(VR_MIICMD_CLK); | |
336 | DELAY(1); | |
337 | } | |
338 | goto fail; | |
339 | } | |
340 | ||
341 | for (i = 0x8000; i; i >>= 1) { | |
342 | SIO_CLR(VR_MIICMD_CLK); | |
343 | DELAY(1); | |
344 | if (!ack) { | |
345 | if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) | |
346 | frame->mii_data |= i; | |
347 | DELAY(1); | |
348 | } | |
349 | SIO_SET(VR_MIICMD_CLK); | |
350 | DELAY(1); | |
351 | } | |
352 | ||
353 | fail: | |
984263bc MD |
354 | SIO_CLR(VR_MIICMD_CLK); |
355 | DELAY(1); | |
356 | SIO_SET(VR_MIICMD_CLK); | |
357 | DELAY(1); | |
358 | ||
984263bc MD |
359 | if (ack) |
360 | return(1); | |
361 | return(0); | |
362 | } | |
363 | #else | |
364 | { | |
9228feed | 365 | int i; |
984263bc | 366 | |
fa23be1c | 367 | /* Set the PHY address. */ |
984263bc MD |
368 | CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| |
369 | frame->mii_phyaddr); | |
370 | ||
fa23be1c | 371 | /* Set the register address. */ |
984263bc MD |
372 | CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); |
373 | VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_READ_ENB); | |
374 | ||
375 | for (i = 0; i < 10000; i++) { | |
376 | if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_READ_ENB) == 0) | |
377 | break; | |
378 | DELAY(1); | |
379 | } | |
984263bc MD |
380 | frame->mii_data = CSR_READ_2(sc, VR_MIIDATA); |
381 | ||
984263bc MD |
382 | return(0); |
383 | } | |
384 | #endif | |
385 | ||
386 | ||
387 | /* | |
388 | * Write to a PHY register through the MII. | |
389 | */ | |
fa23be1c JS |
390 | static int |
391 | vr_mii_writereg(struct vr_softc *sc, struct vr_mii_frame *frame) | |
984263bc MD |
392 | #ifdef VR_USESWSHIFT |
393 | { | |
984263bc MD |
394 | CSR_WRITE_1(sc, VR_MIICMD, 0); |
395 | VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); | |
396 | ||
fa23be1c | 397 | /* Set up frame for TX. */ |
984263bc MD |
398 | frame->mii_stdelim = VR_MII_STARTDELIM; |
399 | frame->mii_opcode = VR_MII_WRITEOP; | |
400 | frame->mii_turnaround = VR_MII_TURNAROUND; | |
401 | ||
fa23be1c | 402 | /* Turn on data output. */ |
984263bc MD |
403 | SIO_SET(VR_MIICMD_DIR); |
404 | ||
405 | vr_mii_sync(sc); | |
406 | ||
407 | vr_mii_send(sc, frame->mii_stdelim, 2); | |
408 | vr_mii_send(sc, frame->mii_opcode, 2); | |
409 | vr_mii_send(sc, frame->mii_phyaddr, 5); | |
410 | vr_mii_send(sc, frame->mii_regaddr, 5); | |
411 | vr_mii_send(sc, frame->mii_turnaround, 2); | |
412 | vr_mii_send(sc, frame->mii_data, 16); | |
413 | ||
414 | /* Idle bit. */ | |
415 | SIO_SET(VR_MIICMD_CLK); | |
416 | DELAY(1); | |
417 | SIO_CLR(VR_MIICMD_CLK); | |
418 | DELAY(1); | |
419 | ||
fa23be1c | 420 | /* Turn off xmit. */ |
984263bc MD |
421 | SIO_CLR(VR_MIICMD_DIR); |
422 | ||
984263bc MD |
423 | return(0); |
424 | } | |
425 | #else | |
426 | { | |
9228feed | 427 | int i; |
984263bc | 428 | |
984263bc MD |
429 | /* Set the PHY-adress */ |
430 | CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| | |
431 | frame->mii_phyaddr); | |
432 | ||
fa23be1c | 433 | /* Set the register address and data to write. */ |
984263bc MD |
434 | CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); |
435 | CSR_WRITE_2(sc, VR_MIIDATA, frame->mii_data); | |
436 | ||
437 | VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_WRITE_ENB); | |
438 | ||
439 | for (i = 0; i < 10000; i++) { | |
440 | if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_WRITE_ENB) == 0) | |
441 | break; | |
442 | DELAY(1); | |
443 | } | |
984263bc MD |
444 | return(0); |
445 | } | |
446 | #endif | |
447 | ||
fa23be1c JS |
448 | static int |
449 | vr_miibus_readreg(device_t dev, int phy, int reg) | |
984263bc | 450 | { |
fa23be1c JS |
451 | struct vr_mii_frame frame; |
452 | struct vr_softc *sc; | |
984263bc MD |
453 | |
454 | sc = device_get_softc(dev); | |
455 | ||
456 | switch (sc->vr_revid) { | |
fa23be1c JS |
457 | case REV_ID_VT6102_APOLLO: |
458 | if (phy != 1) | |
459 | return(0); | |
460 | break; | |
461 | default: | |
462 | break; | |
463 | } | |
984263bc | 464 | |
fa23be1c | 465 | bzero(&frame, sizeof(frame)); |
984263bc MD |
466 | |
467 | frame.mii_phyaddr = phy; | |
468 | frame.mii_regaddr = reg; | |
469 | vr_mii_readreg(sc, &frame); | |
470 | ||
471 | return(frame.mii_data); | |
472 | } | |
473 | ||
fa23be1c JS |
474 | static int |
475 | vr_miibus_writereg(device_t dev, int phy, int reg, int data) | |
984263bc | 476 | { |
fa23be1c JS |
477 | struct vr_mii_frame frame; |
478 | struct vr_softc *sc; | |
984263bc MD |
479 | |
480 | sc = device_get_softc(dev); | |
481 | ||
482 | switch (sc->vr_revid) { | |
fa23be1c JS |
483 | case REV_ID_VT6102_APOLLO: |
484 | if (phy != 1) | |
485 | return 0; | |
486 | break; | |
487 | default: | |
488 | break; | |
489 | } | |
984263bc | 490 | |
fa23be1c | 491 | bzero(&frame, sizeof(frame)); |
984263bc MD |
492 | |
493 | frame.mii_phyaddr = phy; | |
494 | frame.mii_regaddr = reg; | |
495 | frame.mii_data = data; | |
496 | ||
497 | vr_mii_writereg(sc, &frame); | |
498 | ||
499 | return(0); | |
500 | } | |
501 | ||
fa23be1c JS |
502 | static void |
503 | vr_miibus_statchg(device_t dev) | |
984263bc | 504 | { |
fa23be1c JS |
505 | struct mii_data *mii; |
506 | struct vr_softc *sc; | |
984263bc MD |
507 | |
508 | sc = device_get_softc(dev); | |
509 | mii = device_get_softc(sc->vr_miibus); | |
510 | vr_setcfg(sc, mii->mii_media_active); | |
984263bc MD |
511 | } |
512 | ||
984263bc MD |
513 | /* |
514 | * Program the 64-bit multicast hash filter. | |
515 | */ | |
fa23be1c JS |
516 | static void |
517 | vr_setmulti(struct vr_softc *sc) | |
984263bc | 518 | { |
fa23be1c | 519 | struct ifnet *ifp; |
fa23be1c JS |
520 | uint32_t hashes[2] = { 0, 0 }; |
521 | struct ifmultiaddr *ifma; | |
522 | uint8_t rxfilt; | |
523 | int mcnt = 0; | |
984263bc MD |
524 | |
525 | ifp = &sc->arpcom.ac_if; | |
526 | ||
527 | rxfilt = CSR_READ_1(sc, VR_RXCFG); | |
528 | ||
529 | if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { | |
530 | rxfilt |= VR_RXCFG_RX_MULTI; | |
531 | CSR_WRITE_1(sc, VR_RXCFG, rxfilt); | |
532 | CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); | |
533 | CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); | |
534 | return; | |
535 | } | |
536 | ||
fa23be1c | 537 | /* First, zero out all the existing hash bits. */ |
984263bc MD |
538 | CSR_WRITE_4(sc, VR_MAR0, 0); |
539 | CSR_WRITE_4(sc, VR_MAR1, 0); | |
540 | ||
fa23be1c | 541 | /* Now program new ones. */ |
441d34b2 | 542 | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { |
22602e1a JS |
543 | int h; |
544 | ||
984263bc MD |
545 | if (ifma->ifma_addr->sa_family != AF_LINK) |
546 | continue; | |
22602e1a JS |
547 | |
548 | /* use the lower 6 bits */ | |
549 | h = (ether_crc32_be( | |
550 | LLADDR((struct sockaddr_dl *)ifma->ifma_addr), | |
551 | ETHER_ADDR_LEN) >> 26) & 0x0000003F; | |
984263bc MD |
552 | if (h < 32) |
553 | hashes[0] |= (1 << h); | |
554 | else | |
555 | hashes[1] |= (1 << (h - 32)); | |
556 | mcnt++; | |
557 | } | |
558 | ||
559 | if (mcnt) | |
560 | rxfilt |= VR_RXCFG_RX_MULTI; | |
561 | else | |
562 | rxfilt &= ~VR_RXCFG_RX_MULTI; | |
563 | ||
564 | CSR_WRITE_4(sc, VR_MAR0, hashes[0]); | |
565 | CSR_WRITE_4(sc, VR_MAR1, hashes[1]); | |
566 | CSR_WRITE_1(sc, VR_RXCFG, rxfilt); | |
984263bc MD |
567 | } |
568 | ||
569 | /* | |
570 | * In order to fiddle with the | |
571 | * 'full-duplex' and '100Mbps' bits in the netconfig register, we | |
572 | * first have to put the transmit and/or receive logic in the idle state. | |
573 | */ | |
fa23be1c JS |
574 | static void |
575 | vr_setcfg(struct vr_softc *sc, int media) | |
984263bc | 576 | { |
fa23be1c | 577 | int restart = 0; |
984263bc MD |
578 | |
579 | if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { | |
580 | restart = 1; | |
581 | VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); | |
582 | } | |
583 | ||
584 | if ((media & IFM_GMASK) == IFM_FDX) | |
585 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); | |
586 | else | |
587 | VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); | |
588 | ||
589 | if (restart) | |
590 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); | |
984263bc MD |
591 | } |
592 | ||
fa23be1c JS |
593 | static void |
594 | vr_reset(struct vr_softc *sc) | |
984263bc | 595 | { |
fa23be1c | 596 | int i; |
984263bc MD |
597 | |
598 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); | |
599 | ||
600 | for (i = 0; i < VR_TIMEOUT; i++) { | |
601 | DELAY(10); | |
602 | if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) | |
603 | break; | |
604 | } | |
605 | if (i == VR_TIMEOUT) { | |
fa23be1c JS |
606 | struct ifnet *ifp = &sc->arpcom.ac_if; |
607 | ||
608 | if (sc->vr_revid < REV_ID_VT3065_A) { | |
609 | if_printf(ifp, "reset never completed!\n"); | |
610 | } else { | |
984263bc | 611 | /* Use newer force reset command */ |
fa23be1c | 612 | if_printf(ifp, "Using force reset command.\n"); |
984263bc MD |
613 | VR_SETBIT(sc, VR_MISC_CR1, VR_MISCCR1_FORSRST); |
614 | } | |
615 | } | |
616 | ||
617 | /* Wait a little while for the chip to get its brains in order. */ | |
618 | DELAY(1000); | |
984263bc MD |
619 | } |
620 | ||
621 | /* | |
622 | * Probe for a VIA Rhine chip. Check the PCI vendor and device | |
623 | * IDs against our list and return a device name if we find a match. | |
624 | */ | |
fa23be1c JS |
625 | static int |
626 | vr_probe(device_t dev) | |
984263bc | 627 | { |
fa23be1c | 628 | struct vr_type *t; |
ebe47c70 | 629 | uint16_t vid, did; |
984263bc | 630 | |
ebe47c70 JS |
631 | vid = pci_get_vendor(dev); |
632 | did = pci_get_device(dev); | |
984263bc | 633 | |
ebe47c70 JS |
634 | for (t = vr_devs; t->vr_name != NULL; ++t) { |
635 | if (vid == t->vr_vid && did == t->vr_did) { | |
984263bc MD |
636 | device_set_desc(dev, t->vr_name); |
637 | return(0); | |
638 | } | |
984263bc MD |
639 | } |
640 | ||
641 | return(ENXIO); | |
642 | } | |
643 | ||
644 | /* | |
645 | * Attach the interface. Allocate softc structures, do ifmedia | |
646 | * setup and ethernet/BPF attach. | |
647 | */ | |
fa23be1c JS |
648 | static int |
649 | vr_attach(device_t dev) | |
984263bc | 650 | { |
9228feed | 651 | int i; |
fa23be1c | 652 | uint8_t eaddr[ETHER_ADDR_LEN]; |
fa23be1c JS |
653 | struct vr_softc *sc; |
654 | struct ifnet *ifp; | |
acf0d841 | 655 | int error = 0, rid; |
984263bc | 656 | |
984263bc | 657 | sc = device_get_softc(dev); |
9e6fd080 | 658 | callout_init(&sc->vr_stat_timer); |
984263bc MD |
659 | |
660 | /* | |
661 | * Handle power management nonsense. | |
662 | */ | |
5ed306d6 JS |
663 | if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { |
664 | uint32_t iobase, membase, irq; | |
665 | ||
666 | /* Save important PCI config data. */ | |
667 | iobase = pci_read_config(dev, VR_PCI_LOIO, 4); | |
668 | membase = pci_read_config(dev, VR_PCI_LOMEM, 4); | |
669 | irq = pci_read_config(dev, VR_PCI_INTLINE, 4); | |
670 | ||
671 | /* Reset the power state. */ | |
672 | device_printf(dev, "chip is in D%d power mode " | |
673 | "-- setting to D0\n", pci_get_powerstate(dev)); | |
674 | pci_set_powerstate(dev, PCI_POWERSTATE_D0); | |
675 | ||
676 | /* Restore PCI config data. */ | |
677 | pci_write_config(dev, VR_PCI_LOIO, iobase, 4); | |
678 | pci_write_config(dev, VR_PCI_LOMEM, membase, 4); | |
679 | pci_write_config(dev, VR_PCI_INTLINE, irq, 4); | |
984263bc MD |
680 | } |
681 | ||
5ed306d6 | 682 | pci_enable_busmaster(dev); |
984263bc | 683 | |
8f85b59f JS |
684 | sc->vr_revid = pci_get_revid(dev); |
685 | ||
984263bc | 686 | rid = VR_RID; |
fa23be1c | 687 | sc->vr_res = bus_alloc_resource_any(dev, VR_RES, &rid, RF_ACTIVE); |
984263bc MD |
688 | |
689 | if (sc->vr_res == NULL) { | |
fa23be1c | 690 | device_printf(dev, "couldn't map ports/memory\n"); |
62c51aee | 691 | return ENXIO; |
984263bc MD |
692 | } |
693 | ||
694 | sc->vr_btag = rman_get_bustag(sc->vr_res); | |
695 | sc->vr_bhandle = rman_get_bushandle(sc->vr_res); | |
696 | ||
697 | /* Allocate interrupt */ | |
698 | rid = 0; | |
fa23be1c JS |
699 | sc->vr_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, |
700 | RF_SHAREABLE | RF_ACTIVE); | |
984263bc MD |
701 | |
702 | if (sc->vr_irq == NULL) { | |
fa23be1c | 703 | device_printf(dev, "couldn't map interrupt\n"); |
984263bc MD |
704 | error = ENXIO; |
705 | goto fail; | |
706 | } | |
707 | ||
984263bc MD |
708 | /* |
709 | * Windows may put the chip in suspend mode when it | |
710 | * shuts down. Be sure to kick it in the head to wake it | |
711 | * up again. | |
712 | */ | |
713 | VR_CLRBIT(sc, VR_STICKHW, (VR_STICKHW_DS0|VR_STICKHW_DS1)); | |
714 | ||
ebe47c70 JS |
715 | ifp = &sc->arpcom.ac_if; |
716 | if_initname(ifp, device_get_name(dev), device_get_unit(dev)); | |
717 | ||
984263bc MD |
718 | /* Reset the adapter. */ |
719 | vr_reset(sc); | |
720 | ||
721 | /* | |
722 | * Turn on bit2 (MIION) in PCI configuration register 0x53 during | |
723 | * initialization and disable AUTOPOLL. | |
724 | */ | |
725 | pci_write_config(dev, VR_PCI_MODE, | |
726 | pci_read_config(dev, VR_PCI_MODE, 4) | (VR_MODE3_MIION << 24), 4); | |
727 | VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL); | |
728 | ||
729 | /* | |
730 | * Get station address. The way the Rhine chips work, | |
731 | * you're not allowed to directly access the EEPROM once | |
732 | * they've been programmed a special way. Consequently, | |
733 | * we need to read the node address from the PAR0 and PAR1 | |
734 | * registers. | |
735 | */ | |
736 | VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); | |
737 | DELAY(200); | |
738 | for (i = 0; i < ETHER_ADDR_LEN; i++) | |
739 | eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); | |
740 | ||
984263bc | 741 | sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, |
e9175cc8 | 742 | M_WAITOK | M_ZERO, 0, 0xffffffff, PAGE_SIZE, 0); |
984263bc MD |
743 | |
744 | if (sc->vr_ldata == NULL) { | |
fa23be1c | 745 | device_printf(dev, "no memory for list buffers!\n"); |
984263bc MD |
746 | error = ENXIO; |
747 | goto fail; | |
748 | } | |
749 | ||
e9175cc8 SZ |
750 | /* Initialize TX buffer */ |
751 | sc->vr_cdata.vr_tx_buf = contigmalloc(VR_TX_BUF_SIZE, M_DEVBUF, | |
752 | M_WAITOK, 0, 0xffffffff, PAGE_SIZE, 0); | |
753 | if (sc->vr_cdata.vr_tx_buf == NULL) { | |
754 | device_printf(dev, "can't allocate tx buffer!\n"); | |
755 | error = ENXIO; | |
756 | goto fail; | |
757 | } | |
758 | ||
759 | /* Set various TX indexes to invalid value */ | |
760 | sc->vr_cdata.vr_tx_free_idx = -1; | |
761 | sc->vr_cdata.vr_tx_tail_idx = -1; | |
762 | sc->vr_cdata.vr_tx_head_idx = -1; | |
763 | ||
984263bc | 764 | |
984263bc | 765 | ifp->if_softc = sc; |
984263bc MD |
766 | ifp->if_mtu = ETHERMTU; |
767 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; | |
768 | ifp->if_ioctl = vr_ioctl; | |
984263bc | 769 | ifp->if_start = vr_start; |
b373db6a SZ |
770 | #ifdef IFPOLL_ENABLE |
771 | ifp->if_npoll = vr_npoll; | |
9c095379 | 772 | #endif |
984263bc MD |
773 | ifp->if_watchdog = vr_watchdog; |
774 | ifp->if_init = vr_init; | |
775 | ifp->if_baudrate = 10000000; | |
5f23b36c JS |
776 | ifq_set_maxlen(&ifp->if_snd, VR_TX_LIST_CNT - 1); |
777 | ifq_set_ready(&ifp->if_snd); | |
984263bc MD |
778 | |
779 | /* | |
780 | * Do MII setup. | |
781 | */ | |
782 | if (mii_phy_probe(dev, &sc->vr_miibus, | |
783 | vr_ifmedia_upd, vr_ifmedia_sts)) { | |
fa23be1c | 784 | if_printf(ifp, "MII without any phy!\n"); |
984263bc MD |
785 | error = ENXIO; |
786 | goto fail; | |
787 | } | |
788 | ||
fa23be1c | 789 | /* Call MI attach routine. */ |
78195a76 | 790 | ether_ifattach(ifp, eaddr, NULL); |
984263bc | 791 | |
4c77af2d SZ |
792 | ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->vr_irq)); |
793 | ||
b373db6a SZ |
794 | #ifdef IFPOLL_ENABLE |
795 | ifpoll_compat_setup(&sc->vr_npoll, NULL, NULL, device_get_unit(dev), | |
796 | ifp->if_serializer); | |
797 | #endif | |
798 | ||
95893fe4 | 799 | error = bus_setup_intr(dev, sc->vr_irq, INTR_MPSAFE, |
78195a76 MD |
800 | vr_intr, sc, &sc->vr_intrhand, |
801 | ifp->if_serializer); | |
62c51aee JS |
802 | if (error) { |
803 | device_printf(dev, "couldn't set up irq\n"); | |
804 | ether_ifdetach(ifp); | |
805 | goto fail; | |
806 | } | |
9db4b353 | 807 | |
62c51aee JS |
808 | return 0; |
809 | ||
984263bc | 810 | fail: |
62c51aee | 811 | vr_detach(dev); |
984263bc MD |
812 | return(error); |
813 | } | |
814 | ||
fa23be1c JS |
815 | static int |
816 | vr_detach(device_t dev) | |
984263bc | 817 | { |
929a96d9 JS |
818 | struct vr_softc *sc = device_get_softc(dev); |
819 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
984263bc | 820 | |
62c51aee | 821 | if (device_is_attached(dev)) { |
cdf89432 | 822 | lwkt_serialize_enter(ifp->if_serializer); |
62c51aee | 823 | vr_stop(sc); |
cdf89432 SZ |
824 | bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); |
825 | lwkt_serialize_exit(ifp->if_serializer); | |
826 | ||
62c51aee JS |
827 | ether_ifdetach(ifp); |
828 | } | |
829 | if (sc->vr_miibus != NULL) | |
830 | device_delete_child(dev, sc->vr_miibus); | |
984263bc | 831 | bus_generic_detach(dev); |
984263bc | 832 | |
62c51aee JS |
833 | if (sc->vr_irq != NULL) |
834 | bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); | |
835 | if (sc->vr_res != NULL) | |
836 | bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); | |
837 | if (sc->vr_ldata != NULL) | |
838 | contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); | |
e9175cc8 SZ |
839 | if (sc->vr_cdata.vr_tx_buf != NULL) |
840 | contigfree(sc->vr_cdata.vr_tx_buf, VR_TX_BUF_SIZE, M_DEVBUF); | |
984263bc | 841 | |
984263bc MD |
842 | return(0); |
843 | } | |
844 | ||
845 | /* | |
846 | * Initialize the transmit descriptors. | |
847 | */ | |
fa23be1c JS |
848 | static int |
849 | vr_list_tx_init(struct vr_softc *sc) | |
984263bc | 850 | { |
fa23be1c JS |
851 | struct vr_chain_data *cd; |
852 | struct vr_list_data *ld; | |
e9175cc8 SZ |
853 | struct vr_chain *tx_chain; |
854 | int i; | |
984263bc MD |
855 | |
856 | cd = &sc->vr_cdata; | |
857 | ld = sc->vr_ldata; | |
e9175cc8 SZ |
858 | tx_chain = cd->vr_tx_chain; |
859 | ||
984263bc | 860 | for (i = 0; i < VR_TX_LIST_CNT; i++) { |
e9175cc8 | 861 | tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; |
984263bc | 862 | if (i == (VR_TX_LIST_CNT - 1)) |
e9175cc8 | 863 | tx_chain[i].vr_next_idx = 0; |
984263bc | 864 | else |
e9175cc8 | 865 | tx_chain[i].vr_next_idx = i + 1; |
984263bc MD |
866 | } |
867 | ||
e9175cc8 SZ |
868 | for (i = 0; i < VR_TX_LIST_CNT; ++i) { |
869 | void *tx_buf; | |
870 | int next_idx; | |
984263bc | 871 | |
e9175cc8 SZ |
872 | tx_buf = VR_TX_BUF(sc, i); |
873 | next_idx = tx_chain[i].vr_next_idx; | |
874 | ||
875 | tx_chain[i].vr_next_desc_paddr = | |
876 | vtophys(tx_chain[next_idx].vr_ptr); | |
877 | tx_chain[i].vr_buf_paddr = vtophys(tx_buf); | |
878 | } | |
879 | ||
880 | cd->vr_tx_free_idx = 0; | |
881 | cd->vr_tx_tail_idx = cd->vr_tx_head_idx = -1; | |
882 | ||
883 | return 0; | |
984263bc MD |
884 | } |
885 | ||
886 | ||
887 | /* | |
888 | * Initialize the RX descriptors and allocate mbufs for them. Note that | |
889 | * we arrange the descriptors in a closed ring, so that the last descriptor | |
890 | * points back to the first. | |
891 | */ | |
fa23be1c JS |
892 | static int |
893 | vr_list_rx_init(struct vr_softc *sc) | |
984263bc | 894 | { |
fa23be1c JS |
895 | struct vr_chain_data *cd; |
896 | struct vr_list_data *ld; | |
897 | int i, nexti; | |
984263bc MD |
898 | |
899 | cd = &sc->vr_cdata; | |
900 | ld = sc->vr_ldata; | |
901 | ||
902 | for (i = 0; i < VR_RX_LIST_CNT; i++) { | |
fa23be1c | 903 | cd->vr_rx_chain[i].vr_ptr = (struct vr_desc *)&ld->vr_rx_list[i]; |
984263bc MD |
904 | if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) |
905 | return(ENOBUFS); | |
fa23be1c JS |
906 | if (i == (VR_RX_LIST_CNT - 1)) |
907 | nexti = 0; | |
908 | else | |
909 | nexti = i + 1; | |
910 | cd->vr_rx_chain[i].vr_nextdesc = &cd->vr_rx_chain[nexti]; | |
911 | ld->vr_rx_list[i].vr_next = vtophys(&ld->vr_rx_list[nexti]); | |
984263bc MD |
912 | } |
913 | ||
914 | cd->vr_rx_head = &cd->vr_rx_chain[0]; | |
915 | ||
916 | return(0); | |
917 | } | |
918 | ||
919 | /* | |
920 | * Initialize an RX descriptor and attach an MBUF cluster. | |
921 | * Note: the length fields are only 11 bits wide, which means the | |
922 | * largest size we can specify is 2047. This is important because | |
923 | * MCLBYTES is 2048, so we have to subtract one otherwise we'll | |
924 | * overflow the field and make a mess. | |
925 | */ | |
fa23be1c JS |
926 | static int |
927 | vr_newbuf(struct vr_softc *sc, struct vr_chain_onefrag *c, struct mbuf *m) | |
984263bc | 928 | { |
fa23be1c | 929 | struct mbuf *m_new = NULL; |
984263bc MD |
930 | |
931 | if (m == NULL) { | |
b5523eac | 932 | m_new = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); |
984263bc | 933 | if (m_new == NULL) |
17b71a59 | 934 | return (ENOBUFS); |
984263bc MD |
935 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; |
936 | } else { | |
937 | m_new = m; | |
938 | m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; | |
939 | m_new->m_data = m_new->m_ext.ext_buf; | |
940 | } | |
941 | ||
fa23be1c | 942 | m_adj(m_new, sizeof(uint64_t)); |
984263bc MD |
943 | |
944 | c->vr_mbuf = m_new; | |
945 | c->vr_ptr->vr_status = VR_RXSTAT; | |
946 | c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); | |
947 | c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; | |
948 | ||
949 | return(0); | |
950 | } | |
951 | ||
952 | /* | |
953 | * A frame has been uploaded: pass the resulting mbuf chain up to | |
954 | * the higher level protocols. | |
955 | */ | |
fa23be1c JS |
956 | static void |
957 | vr_rxeof(struct vr_softc *sc) | |
984263bc | 958 | { |
fa23be1c JS |
959 | struct mbuf *m; |
960 | struct ifnet *ifp; | |
961 | struct vr_chain_onefrag *cur_rx; | |
962 | int total_len = 0; | |
963 | uint32_t rxstat; | |
984263bc MD |
964 | |
965 | ifp = &sc->arpcom.ac_if; | |
966 | ||
967 | while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & | |
968 | VR_RXSTAT_OWN)) { | |
fa23be1c | 969 | struct mbuf *m0 = NULL; |
984263bc MD |
970 | |
971 | cur_rx = sc->vr_cdata.vr_rx_head; | |
972 | sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; | |
973 | m = cur_rx->vr_mbuf; | |
974 | ||
975 | /* | |
976 | * If an error occurs, update stats, clear the | |
977 | * status word and leave the mbuf cluster in place: | |
978 | * it should simply get re-used next time this descriptor | |
979 | * comes up in the ring. | |
980 | */ | |
981 | if (rxstat & VR_RXSTAT_RXERR) { | |
d40991ef | 982 | IFNET_STAT_INC(ifp, ierrors, 1); |
fa23be1c | 983 | if_printf(ifp, "rx error (%02x):", rxstat & 0x000000ff); |
984263bc | 984 | if (rxstat & VR_RXSTAT_CRCERR) |
e3869ec7 | 985 | kprintf(" crc error"); |
984263bc | 986 | if (rxstat & VR_RXSTAT_FRAMEALIGNERR) |
e3869ec7 | 987 | kprintf(" frame alignment error\n"); |
984263bc | 988 | if (rxstat & VR_RXSTAT_FIFOOFLOW) |
e3869ec7 | 989 | kprintf(" FIFO overflow"); |
984263bc | 990 | if (rxstat & VR_RXSTAT_GIANT) |
e3869ec7 | 991 | kprintf(" received giant packet"); |
984263bc | 992 | if (rxstat & VR_RXSTAT_RUNT) |
e3869ec7 | 993 | kprintf(" received runt packet"); |
984263bc | 994 | if (rxstat & VR_RXSTAT_BUSERR) |
e3869ec7 | 995 | kprintf(" system bus error"); |
984263bc | 996 | if (rxstat & VR_RXSTAT_BUFFERR) |
e3869ec7 SW |
997 | kprintf("rx buffer error"); |
998 | kprintf("\n"); | |
984263bc MD |
999 | vr_newbuf(sc, cur_rx, m); |
1000 | continue; | |
1001 | } | |
1002 | ||
1003 | /* No errors; receive the packet. */ | |
1004 | total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); | |
1005 | ||
1006 | /* | |
1007 | * XXX The VIA Rhine chip includes the CRC with every | |
1008 | * received frame, and there's no way to turn this | |
1009 | * behavior off (at least, I can't find anything in | |
1010 | * the manual that explains how to do it) so we have | |
1011 | * to trim off the CRC manually. | |
1012 | */ | |
1013 | total_len -= ETHER_CRC_LEN; | |
1014 | ||
1015 | m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, | |
afd2da4d | 1016 | total_len + ETHER_ALIGN, 0, ifp); |
984263bc MD |
1017 | vr_newbuf(sc, cur_rx, m); |
1018 | if (m0 == NULL) { | |
d40991ef | 1019 | IFNET_STAT_INC(ifp, ierrors, 1); |
984263bc MD |
1020 | continue; |
1021 | } | |
1022 | m_adj(m0, ETHER_ALIGN); | |
1023 | m = m0; | |
1024 | ||
d40991ef | 1025 | IFNET_STAT_INC(ifp, ipackets, 1); |
73029d08 | 1026 | ifp->if_input(ifp, m, NULL, -1); |
984263bc | 1027 | } |
984263bc MD |
1028 | } |
1029 | ||
fa23be1c JS |
1030 | static void |
1031 | vr_rxeoc(struct vr_softc *sc) | |
984263bc | 1032 | { |
fa23be1c JS |
1033 | struct ifnet *ifp; |
1034 | int i; | |
984263bc MD |
1035 | |
1036 | ifp = &sc->arpcom.ac_if; | |
1037 | ||
d40991ef | 1038 | IFNET_STAT_INC(ifp, ierrors, 1); |
984263bc MD |
1039 | |
1040 | VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); | |
1041 | DELAY(10000); | |
1042 | ||
fa23be1c | 1043 | /* Wait for receiver to stop */ |
984263bc MD |
1044 | for (i = 0x400; |
1045 | i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RX_ON); | |
1046 | i--) | |
1047 | ; /* Wait for receiver to stop */ | |
1048 | ||
fa23be1c JS |
1049 | if (i == 0) { |
1050 | if_printf(ifp, "rx shutdown error!\n"); | |
984263bc MD |
1051 | sc->vr_flags |= VR_F_RESTART; |
1052 | return; | |
fa23be1c | 1053 | } |
984263bc MD |
1054 | |
1055 | vr_rxeof(sc); | |
1056 | ||
1057 | CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); | |
1058 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); | |
1059 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); | |
984263bc MD |
1060 | } |
1061 | ||
1062 | /* | |
1063 | * A frame was downloaded to the chip. It's safe for us to clean up | |
1064 | * the list buffers. | |
1065 | */ | |
fa23be1c JS |
1066 | static void |
1067 | vr_txeof(struct vr_softc *sc) | |
984263bc | 1068 | { |
e9175cc8 SZ |
1069 | struct vr_chain_data *cd; |
1070 | struct vr_chain *tx_chain; | |
fa23be1c | 1071 | struct ifnet *ifp; |
984263bc MD |
1072 | |
1073 | ifp = &sc->arpcom.ac_if; | |
e9175cc8 | 1074 | cd = &sc->vr_cdata; |
984263bc MD |
1075 | |
1076 | /* Reset the timeout timer; if_txeoc will clear it. */ | |
1077 | ifp->if_timer = 5; | |
1078 | ||
1079 | /* Sanity check. */ | |
e9175cc8 | 1080 | if (cd->vr_tx_head_idx == -1) |
984263bc MD |
1081 | return; |
1082 | ||
e9175cc8 SZ |
1083 | tx_chain = cd->vr_tx_chain; |
1084 | ||
984263bc MD |
1085 | /* |
1086 | * Go through our tx list and free mbufs for those | |
1087 | * frames that have been transmitted. | |
1088 | */ | |
e9175cc8 SZ |
1089 | while(tx_chain[cd->vr_tx_head_idx].vr_buf != NULL) { |
1090 | struct vr_chain *cur_tx; | |
fa23be1c JS |
1091 | uint32_t txstat; |
1092 | int i; | |
984263bc | 1093 | |
e9175cc8 | 1094 | cur_tx = &tx_chain[cd->vr_tx_head_idx]; |
984263bc MD |
1095 | txstat = cur_tx->vr_ptr->vr_status; |
1096 | ||
1097 | if ((txstat & VR_TXSTAT_ABRT) || | |
1098 | (txstat & VR_TXSTAT_UDF)) { | |
1099 | for (i = 0x400; | |
1100 | i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_TX_ON); | |
1101 | i--) | |
1102 | ; /* Wait for chip to shutdown */ | |
fa23be1c JS |
1103 | if (i == 0) { |
1104 | if_printf(ifp, "tx shutdown timeout\n"); | |
984263bc MD |
1105 | sc->vr_flags |= VR_F_RESTART; |
1106 | break; | |
1107 | } | |
1108 | VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; | |
1109 | CSR_WRITE_4(sc, VR_TXADDR, vtophys(cur_tx->vr_ptr)); | |
1110 | break; | |
1111 | } | |
1112 | ||
1113 | if (txstat & VR_TXSTAT_OWN) | |
1114 | break; | |
1115 | ||
1116 | if (txstat & VR_TXSTAT_ERRSUM) { | |
d40991ef | 1117 | IFNET_STAT_INC(ifp, oerrors, 1); |
984263bc | 1118 | if (txstat & VR_TXSTAT_DEFER) |
d40991ef | 1119 | IFNET_STAT_INC(ifp, collisions, 1); |
984263bc | 1120 | if (txstat & VR_TXSTAT_LATECOLL) |
d40991ef | 1121 | IFNET_STAT_INC(ifp, collisions, 1); |
984263bc MD |
1122 | } |
1123 | ||
d40991ef SZ |
1124 | IFNET_STAT_INC(ifp, collisions, |
1125 | (txstat & VR_TXSTAT_COLLCNT) >> 3); | |
984263bc | 1126 | |
d40991ef | 1127 | IFNET_STAT_INC(ifp, opackets, 1); |
e9175cc8 | 1128 | cur_tx->vr_buf = NULL; |
984263bc | 1129 | |
e9175cc8 SZ |
1130 | if (cd->vr_tx_head_idx == cd->vr_tx_tail_idx) { |
1131 | cd->vr_tx_head_idx = -1; | |
1132 | cd->vr_tx_tail_idx = -1; | |
984263bc MD |
1133 | break; |
1134 | } | |
1135 | ||
e9175cc8 | 1136 | cd->vr_tx_head_idx = cur_tx->vr_next_idx; |
984263bc | 1137 | } |
984263bc MD |
1138 | } |
1139 | ||
1140 | /* | |
1141 | * TX 'end of channel' interrupt handler. | |
1142 | */ | |
fa23be1c JS |
1143 | static void |
1144 | vr_txeoc(struct vr_softc *sc) | |
984263bc | 1145 | { |
fa23be1c | 1146 | struct ifnet *ifp; |
984263bc MD |
1147 | |
1148 | ifp = &sc->arpcom.ac_if; | |
1149 | ||
e9175cc8 | 1150 | if (sc->vr_cdata.vr_tx_head_idx == -1) { |
9ed293e0 | 1151 | ifq_clr_oactive(&ifp->if_snd); |
e9175cc8 | 1152 | sc->vr_cdata.vr_tx_tail_idx = -1; |
984263bc MD |
1153 | ifp->if_timer = 0; |
1154 | } | |
984263bc MD |
1155 | } |
1156 | ||
fa23be1c JS |
1157 | static void |
1158 | vr_tick(void *xsc) | |
984263bc | 1159 | { |
929a96d9 | 1160 | struct vr_softc *sc = xsc; |
78195a76 | 1161 | struct ifnet *ifp = &sc->arpcom.ac_if; |
fa23be1c | 1162 | struct mii_data *mii; |
984263bc | 1163 | |
78195a76 | 1164 | lwkt_serialize_enter(ifp->if_serializer); |
984263bc | 1165 | |
984263bc | 1166 | if (sc->vr_flags & VR_F_RESTART) { |
fa23be1c | 1167 | if_printf(&sc->arpcom.ac_if, "restarting\n"); |
984263bc MD |
1168 | vr_stop(sc); |
1169 | vr_reset(sc); | |
1170 | vr_init(sc); | |
1171 | sc->vr_flags &= ~VR_F_RESTART; | |
1172 | } | |
1173 | ||
1174 | mii = device_get_softc(sc->vr_miibus); | |
1175 | mii_tick(mii); | |
1176 | ||
9e6fd080 | 1177 | callout_reset(&sc->vr_stat_timer, hz, vr_tick, sc); |
984263bc | 1178 | |
78195a76 | 1179 | lwkt_serialize_exit(ifp->if_serializer); |
984263bc MD |
1180 | } |
1181 | ||
fa23be1c JS |
1182 | static void |
1183 | vr_intr(void *arg) | |
984263bc | 1184 | { |
fa23be1c JS |
1185 | struct vr_softc *sc; |
1186 | struct ifnet *ifp; | |
1187 | uint16_t status; | |
984263bc MD |
1188 | |
1189 | sc = arg; | |
1190 | ifp = &sc->arpcom.ac_if; | |
1191 | ||
1192 | /* Supress unwanted interrupts. */ | |
1193 | if (!(ifp->if_flags & IFF_UP)) { | |
1194 | vr_stop(sc); | |
1195 | return; | |
1196 | } | |
1197 | ||
1198 | /* Disable interrupts. */ | |
b373db6a | 1199 | if ((ifp->if_flags & IFF_NPOLLING) == 0) |
c8fb08d8 | 1200 | CSR_WRITE_2(sc, VR_IMR, 0x0000); |
984263bc MD |
1201 | |
1202 | for (;;) { | |
984263bc MD |
1203 | status = CSR_READ_2(sc, VR_ISR); |
1204 | if (status) | |
1205 | CSR_WRITE_2(sc, VR_ISR, status); | |
1206 | ||
1207 | if ((status & VR_INTRS) == 0) | |
1208 | break; | |
1209 | ||
1210 | if (status & VR_ISR_RX_OK) | |
1211 | vr_rxeof(sc); | |
1212 | ||
1213 | if (status & VR_ISR_RX_DROPPED) { | |
fa23be1c | 1214 | if_printf(ifp, "rx packet lost\n"); |
d40991ef | 1215 | IFNET_STAT_INC(ifp, ierrors, 1); |
984263bc MD |
1216 | } |
1217 | ||
1218 | if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || | |
dc5ac7ed | 1219 | (status & VR_ISR_RX_OFLOW)) { |
fa23be1c | 1220 | if_printf(ifp, "receive error (%04x)", status); |
984263bc | 1221 | if (status & VR_ISR_RX_NOBUF) |
e3869ec7 | 1222 | kprintf(" no buffers"); |
984263bc | 1223 | if (status & VR_ISR_RX_OFLOW) |
e3869ec7 | 1224 | kprintf(" overflow"); |
984263bc | 1225 | if (status & VR_ISR_RX_DROPPED) |
e3869ec7 SW |
1226 | kprintf(" packet lost"); |
1227 | kprintf("\n"); | |
984263bc MD |
1228 | vr_rxeoc(sc); |
1229 | } | |
1230 | ||
1231 | if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { | |
1232 | vr_reset(sc); | |
1233 | vr_init(sc); | |
1234 | break; | |
1235 | } | |
1236 | ||
1237 | if ((status & VR_ISR_TX_OK) || (status & VR_ISR_TX_ABRT) || | |
1238 | (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_UDFI)) { | |
1239 | vr_txeof(sc); | |
1240 | if ((status & VR_ISR_UDFI) || | |
1241 | (status & VR_ISR_TX_ABRT2) || | |
1242 | (status & VR_ISR_TX_ABRT)) { | |
d40991ef | 1243 | IFNET_STAT_INC(ifp, oerrors, 1); |
e9175cc8 SZ |
1244 | if (sc->vr_cdata.vr_tx_head_idx != -1) { |
1245 | VR_SETBIT16(sc, VR_COMMAND, | |
1246 | VR_CMD_TX_ON); | |
1247 | VR_SETBIT16(sc, VR_COMMAND, | |
1248 | VR_CMD_TX_GO); | |
984263bc | 1249 | } |
fa23be1c | 1250 | } else { |
984263bc | 1251 | vr_txeoc(sc); |
fa23be1c | 1252 | } |
984263bc MD |
1253 | } |
1254 | ||
1255 | } | |
1256 | ||
1257 | /* Re-enable interrupts. */ | |
b373db6a | 1258 | if ((ifp->if_flags & IFF_NPOLLING) == 0) |
c8fb08d8 | 1259 | CSR_WRITE_2(sc, VR_IMR, VR_INTRS); |
984263bc | 1260 | |
5f23b36c | 1261 | if (!ifq_is_empty(&ifp->if_snd)) |
9db4b353 | 1262 | if_devstart(ifp); |
984263bc MD |
1263 | } |
1264 | ||
1265 | /* | |
1266 | * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data | |
1267 | * pointers to the fragment pointers. | |
1268 | */ | |
fa23be1c | 1269 | static int |
e9175cc8 | 1270 | vr_encap(struct vr_softc *sc, int chain_idx, struct mbuf *m_head) |
984263bc | 1271 | { |
e9175cc8 SZ |
1272 | struct vr_chain *c; |
1273 | struct vr_desc *f; | |
1274 | caddr_t tx_buf; | |
1275 | int len; | |
984263bc | 1276 | |
e9175cc8 SZ |
1277 | KASSERT(chain_idx >= 0 && chain_idx < VR_TX_LIST_CNT, |
1278 | ("%s: chain idx(%d) out of range 0-%d", | |
1279 | sc->arpcom.ac_if.if_xname, chain_idx, VR_TX_LIST_CNT)); | |
984263bc MD |
1280 | |
1281 | /* | |
1282 | * The VIA Rhine wants packet buffers to be longword | |
1283 | * aligned, but very often our mbufs aren't. Rather than | |
1284 | * waste time trying to decide when to copy and when not | |
1285 | * to copy, just do it all the time. | |
1286 | */ | |
e9175cc8 SZ |
1287 | tx_buf = VR_TX_BUF(sc, chain_idx); |
1288 | m_copydata(m_head, 0, m_head->m_pkthdr.len, tx_buf); | |
1289 | len = m_head->m_pkthdr.len; | |
1290 | ||
5f0a6e55 JS |
1291 | /* |
1292 | * The Rhine chip doesn't auto-pad, so we have to make | |
1293 | * sure to pad short frames out to the minimum frame length | |
1294 | * ourselves. | |
1295 | */ | |
e9175cc8 SZ |
1296 | if (len < VR_MIN_FRAMELEN) { |
1297 | bzero(tx_buf + len, VR_MIN_FRAMELEN - len); | |
1298 | len = VR_MIN_FRAMELEN; | |
1299 | } | |
1300 | ||
1301 | c = &sc->vr_cdata.vr_tx_chain[chain_idx]; | |
1302 | c->vr_buf = tx_buf; | |
1303 | ||
5f0a6e55 | 1304 | f = c->vr_ptr; |
e9175cc8 SZ |
1305 | f->vr_data = c->vr_buf_paddr; |
1306 | f->vr_ctl = len; | |
1307 | f->vr_ctl |= (VR_TXCTL_TLINK | VR_TXCTL_FIRSTFRAG); | |
1308 | f->vr_ctl |= (VR_TXCTL_LASTFRAG | VR_TXCTL_FINT); | |
5f0a6e55 | 1309 | f->vr_status = 0; |
e9175cc8 | 1310 | f->vr_next = c->vr_next_desc_paddr; |
984263bc MD |
1311 | |
1312 | return(0); | |
1313 | } | |
1314 | ||
1315 | /* | |
1316 | * Main transmit routine. To avoid having to do mbuf copies, we put pointers | |
1317 | * to the mbuf data regions directly in the transmit lists. We also save a | |
1318 | * copy of the pointers since the transmit list fragment pointers are | |
1319 | * physical addresses. | |
1320 | */ | |
fa23be1c | 1321 | static void |
f0a26983 | 1322 | vr_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) |
984263bc | 1323 | { |
fa23be1c | 1324 | struct vr_softc *sc; |
e9175cc8 SZ |
1325 | struct vr_chain_data *cd; |
1326 | struct vr_chain *tx_chain; | |
1327 | int cur_tx_idx, start_tx_idx, prev_tx_idx; | |
984263bc | 1328 | |
f0a26983 SZ |
1329 | ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); |
1330 | ||
9ed293e0 | 1331 | if ((ifp->if_flags & IFF_RUNNING) == 0 || ifq_is_oactive(&ifp->if_snd)) |
984263bc MD |
1332 | return; |
1333 | ||
e9175cc8 SZ |
1334 | sc = ifp->if_softc; |
1335 | cd = &sc->vr_cdata; | |
1336 | tx_chain = cd->vr_tx_chain; | |
1337 | ||
1338 | start_tx_idx = cd->vr_tx_free_idx; | |
1339 | cur_tx_idx = prev_tx_idx = -1; | |
1340 | ||
fa23be1c | 1341 | /* Check for an available queue slot. If there are none, punt. */ |
e9175cc8 | 1342 | if (tx_chain[start_tx_idx].vr_buf != NULL) { |
9ed293e0 | 1343 | ifq_set_oactive(&ifp->if_snd); |
984263bc MD |
1344 | return; |
1345 | } | |
1346 | ||
9db4b353 | 1347 | while (tx_chain[cd->vr_tx_free_idx].vr_buf == NULL) { |
e9175cc8 SZ |
1348 | struct mbuf *m_head; |
1349 | struct vr_chain *cur_tx; | |
984263bc | 1350 | |
ac9843a1 | 1351 | m_head = ifq_dequeue(&ifp->if_snd); |
984263bc MD |
1352 | if (m_head == NULL) |
1353 | break; | |
1354 | ||
1355 | /* Pick a descriptor off the free list. */ | |
e9175cc8 SZ |
1356 | cur_tx_idx = cd->vr_tx_free_idx; |
1357 | cur_tx = &tx_chain[cur_tx_idx]; | |
984263bc MD |
1358 | |
1359 | /* Pack the data into the descriptor. */ | |
e9175cc8 | 1360 | if (vr_encap(sc, cur_tx_idx, m_head)) { |
9ed293e0 | 1361 | ifq_set_oactive(&ifp->if_snd); |
e9175cc8 | 1362 | cur_tx_idx = prev_tx_idx; |
984263bc MD |
1363 | break; |
1364 | } | |
1365 | ||
e9175cc8 SZ |
1366 | /* XXX */ |
1367 | if (cur_tx_idx != start_tx_idx) | |
984263bc MD |
1368 | VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; |
1369 | ||
5f0a6e55 JS |
1370 | BPF_MTAP(ifp, m_head); |
1371 | m_freem(m_head); | |
984263bc MD |
1372 | |
1373 | VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; | |
1374 | VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); | |
e9175cc8 SZ |
1375 | |
1376 | /* Iff everything went OK, we bump up free index. */ | |
1377 | prev_tx_idx = cur_tx_idx; | |
1378 | cd->vr_tx_free_idx = cur_tx->vr_next_idx; | |
984263bc MD |
1379 | } |
1380 | ||
fa23be1c | 1381 | /* If there are no frames queued, bail. */ |
e9175cc8 | 1382 | if (cur_tx_idx == -1) |
984263bc MD |
1383 | return; |
1384 | ||
e9175cc8 | 1385 | sc->vr_cdata.vr_tx_tail_idx = cur_tx_idx; |
984263bc | 1386 | |
e9175cc8 SZ |
1387 | if (sc->vr_cdata.vr_tx_head_idx == -1) |
1388 | sc->vr_cdata.vr_tx_head_idx = start_tx_idx; | |
984263bc MD |
1389 | |
1390 | /* | |
1391 | * Set a timeout in case the chip goes out to lunch. | |
1392 | */ | |
1393 | ifp->if_timer = 5; | |
984263bc MD |
1394 | } |
1395 | ||
fa23be1c JS |
1396 | static void |
1397 | vr_init(void *xsc) | |
984263bc | 1398 | { |
fa23be1c JS |
1399 | struct vr_softc *sc = xsc; |
1400 | struct ifnet *ifp = &sc->arpcom.ac_if; | |
1401 | struct mii_data *mii; | |
9228feed | 1402 | int i; |
984263bc | 1403 | |
984263bc MD |
1404 | mii = device_get_softc(sc->vr_miibus); |
1405 | ||
fa23be1c | 1406 | /* Cancel pending I/O and free all RX/TX buffers. */ |
984263bc MD |
1407 | vr_stop(sc); |
1408 | vr_reset(sc); | |
1409 | ||
fa23be1c | 1410 | /* Set our station address. */ |
984263bc MD |
1411 | for (i = 0; i < ETHER_ADDR_LEN; i++) |
1412 | CSR_WRITE_1(sc, VR_PAR0 + i, sc->arpcom.ac_enaddr[i]); | |
fa23be1c JS |
1413 | |
1414 | /* Set DMA size. */ | |
984263bc MD |
1415 | VR_CLRBIT(sc, VR_BCR0, VR_BCR0_DMA_LENGTH); |
1416 | VR_SETBIT(sc, VR_BCR0, VR_BCR0_DMA_STORENFWD); | |
1417 | ||
fa23be1c | 1418 | /* |
984263bc MD |
1419 | * BCR0 and BCR1 can override the RXCFG and TXCFG registers, |
1420 | * so we must set both. | |
1421 | */ | |
1422 | VR_CLRBIT(sc, VR_BCR0, VR_BCR0_RX_THRESH); | |
1423 | VR_SETBIT(sc, VR_BCR0, VR_BCR0_RXTHRESH128BYTES); | |
1424 | ||
1425 | VR_CLRBIT(sc, VR_BCR1, VR_BCR1_TX_THRESH); | |
1426 | VR_SETBIT(sc, VR_BCR1, VR_BCR1_TXTHRESHSTORENFWD); | |
1427 | ||
1428 | VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); | |
1429 | VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_128BYTES); | |
1430 | ||
1431 | VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); | |
1432 | VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); | |
1433 | ||
1434 | /* Init circular RX list. */ | |
1435 | if (vr_list_rx_init(sc) == ENOBUFS) { | |
984263bc | 1436 | vr_stop(sc); |
929a96d9 | 1437 | if_printf(ifp, "initialization failed: no memory for rx buffers\n"); |
984263bc MD |
1438 | return; |
1439 | } | |
1440 | ||
fa23be1c | 1441 | /* Init tx descriptors. */ |
984263bc MD |
1442 | vr_list_tx_init(sc); |
1443 | ||
1444 | /* If we want promiscuous mode, set the allframes bit. */ | |
1445 | if (ifp->if_flags & IFF_PROMISC) | |
1446 | VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); | |
1447 | else | |
1448 | VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); | |
1449 | ||
1450 | /* Set capture broadcast bit to capture broadcast frames. */ | |
1451 | if (ifp->if_flags & IFF_BROADCAST) | |
1452 | VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); | |
1453 | else | |
1454 | VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); | |
1455 | ||
1456 | /* | |
1457 | * Program the multicast filter, if necessary. | |
1458 | */ | |
1459 | vr_setmulti(sc); | |
1460 | ||
1461 | /* | |
1462 | * Load the address of the RX list. | |
1463 | */ | |
1464 | CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); | |
1465 | ||
1466 | /* Enable receiver and transmitter. */ | |
1467 | CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| | |
1468 | VR_CMD_TX_ON|VR_CMD_RX_ON| | |
1469 | VR_CMD_RX_GO); | |
1470 | ||
1471 | CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); | |
1472 | ||
1473 | /* | |
c8fb08d8 | 1474 | * Enable interrupts, unless we are polling. |
984263bc MD |
1475 | */ |
1476 | CSR_WRITE_2(sc, VR_ISR, 0xFFFF); | |
b373db6a SZ |
1477 | #ifdef IFPOLL_ENABLE |
1478 | if ((ifp->if_flags & IFF_NPOLLING) == 0) | |
d132539a | 1479 | #endif |
c8fb08d8 | 1480 | CSR_WRITE_2(sc, VR_IMR, VR_INTRS); |
984263bc MD |
1481 | |
1482 | mii_mediachg(mii); | |
1483 | ||
1484 | ifp->if_flags |= IFF_RUNNING; | |
9ed293e0 | 1485 | ifq_clr_oactive(&ifp->if_snd); |
984263bc | 1486 | |
9e6fd080 | 1487 | callout_reset(&sc->vr_stat_timer, hz, vr_tick, sc); |
984263bc MD |
1488 | } |
1489 | ||
1490 | /* | |
1491 | * Set media options. | |
1492 | */ | |
fa23be1c JS |
1493 | static int |
1494 | vr_ifmedia_upd(struct ifnet *ifp) | |
984263bc | 1495 | { |
fa23be1c | 1496 | struct vr_softc *sc; |
984263bc MD |
1497 | |
1498 | sc = ifp->if_softc; | |
1499 | ||
1500 | if (ifp->if_flags & IFF_UP) | |
1501 | vr_init(sc); | |
1502 | ||
1503 | return(0); | |
1504 | } | |
1505 | ||
1506 | /* | |
1507 | * Report current media status. | |
1508 | */ | |
fa23be1c JS |
1509 | static void |
1510 | vr_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) | |
984263bc | 1511 | { |
fa23be1c JS |
1512 | struct vr_softc *sc; |
1513 | struct mii_data *mii; | |
984263bc MD |
1514 | |
1515 | sc = ifp->if_softc; | |
1516 | mii = device_get_softc(sc->vr_miibus); | |
1517 | mii_pollstat(mii); | |
1518 | ifmr->ifm_active = mii->mii_media_active; | |
1519 | ifmr->ifm_status = mii->mii_media_status; | |
984263bc MD |
1520 | } |
1521 | ||
fa23be1c JS |
1522 | static int |
1523 | vr_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr) | |
984263bc | 1524 | { |
fa23be1c JS |
1525 | struct vr_softc *sc = ifp->if_softc; |
1526 | struct ifreq *ifr = (struct ifreq *) data; | |
1527 | struct mii_data *mii; | |
9228feed | 1528 | int error = 0; |
984263bc | 1529 | |
984263bc | 1530 | switch(command) { |
984263bc MD |
1531 | case SIOCSIFFLAGS: |
1532 | if (ifp->if_flags & IFF_UP) { | |
1533 | vr_init(sc); | |
1534 | } else { | |
1535 | if (ifp->if_flags & IFF_RUNNING) | |
1536 | vr_stop(sc); | |
1537 | } | |
1538 | error = 0; | |
1539 | break; | |
1540 | case SIOCADDMULTI: | |
1541 | case SIOCDELMULTI: | |
1542 | vr_setmulti(sc); | |
1543 | error = 0; | |
1544 | break; | |
1545 | case SIOCGIFMEDIA: | |
1546 | case SIOCSIFMEDIA: | |
1547 | mii = device_get_softc(sc->vr_miibus); | |
1548 | error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); | |
1549 | break; | |
1550 | default: | |
4cde4dd5 | 1551 | error = ether_ioctl(ifp, command, data); |
984263bc MD |
1552 | break; |
1553 | } | |
984263bc MD |
1554 | return(error); |
1555 | } | |
1556 | ||
b373db6a | 1557 | #ifdef IFPOLL_ENABLE |
9c095379 | 1558 | |
fa23be1c | 1559 | static void |
b373db6a | 1560 | vr_npoll_compat(struct ifnet *ifp, void *arg __unused, int count __unused) |
c8fb08d8 MD |
1561 | { |
1562 | struct vr_softc *sc = ifp->if_softc; | |
1563 | ||
b373db6a SZ |
1564 | ASSERT_SERIALIZED(ifp->if_serializer); |
1565 | vr_intr(sc); | |
1566 | } | |
1567 | ||
1568 | static void | |
1569 | vr_npoll(struct ifnet *ifp, struct ifpoll_info *info) | |
1570 | { | |
1571 | struct vr_softc *sc = ifp->if_softc; | |
1572 | ||
1573 | ASSERT_SERIALIZED(ifp->if_serializer); | |
1574 | ||
1575 | if (info != NULL) { | |
1576 | int cpuid = sc->vr_npoll.ifpc_cpuid; | |
1577 | ||
1578 | info->ifpi_rx[cpuid].poll_func = vr_npoll_compat; | |
1579 | info->ifpi_rx[cpuid].arg = NULL; | |
1580 | info->ifpi_rx[cpuid].serializer = ifp->if_serializer; | |
1581 | ||
1582 | if (ifp->if_flags & IFF_RUNNING) { | |
1583 | /* disable interrupts */ | |
1584 | CSR_WRITE_2(sc, VR_IMR, 0x0000); | |
1585 | } | |
dfd3b18b | 1586 | ifq_set_cpuid(&ifp->if_snd, cpuid); |
b373db6a SZ |
1587 | } else { |
1588 | if (ifp->if_flags & IFF_RUNNING) { | |
1589 | /* enable interrupts */ | |
1590 | CSR_WRITE_2(sc, VR_IMR, VR_INTRS); | |
1591 | } | |
dfd3b18b | 1592 | ifq_set_cpuid(&ifp->if_snd, rman_get_cpuid(sc->vr_irq)); |
9c095379 | 1593 | } |
c8fb08d8 | 1594 | } |
b373db6a SZ |
1595 | |
1596 | #endif /* IFPOLL_ENABLE */ | |
c8fb08d8 | 1597 | |
fa23be1c JS |
1598 | static void |
1599 | vr_watchdog(struct ifnet *ifp) | |
984263bc | 1600 | { |
fa23be1c | 1601 | struct vr_softc *sc; |
984263bc MD |
1602 | |
1603 | sc = ifp->if_softc; | |
1604 | ||
d40991ef | 1605 | IFNET_STAT_INC(ifp, oerrors, 1); |
fa23be1c | 1606 | if_printf(ifp, "watchdog timeout\n"); |
984263bc | 1607 | |
4f598e09 SZ |
1608 | vr_stop(sc); |
1609 | vr_reset(sc); | |
1610 | vr_init(sc); | |
984263bc | 1611 | |
5f23b36c | 1612 | if (!ifq_is_empty(&ifp->if_snd)) |
9db4b353 | 1613 | if_devstart(ifp); |
984263bc MD |
1614 | } |
1615 | ||
1616 | /* | |
1617 | * Stop the adapter and free any mbufs allocated to the | |
1618 | * RX and TX lists. | |
1619 | */ | |
fa23be1c JS |
1620 | static void |
1621 | vr_stop(struct vr_softc *sc) | |
984263bc | 1622 | { |
fa23be1c JS |
1623 | int i; |
1624 | struct ifnet *ifp; | |
984263bc MD |
1625 | |
1626 | ifp = &sc->arpcom.ac_if; | |
1627 | ifp->if_timer = 0; | |
1628 | ||
9e6fd080 | 1629 | callout_stop(&sc->vr_stat_timer); |
984263bc MD |
1630 | |
1631 | VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); | |
1632 | VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); | |
1633 | CSR_WRITE_2(sc, VR_IMR, 0x0000); | |
1634 | CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); | |
1635 | CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); | |
1636 | ||
1637 | /* | |
1638 | * Free data in the RX lists. | |
1639 | */ | |
1640 | for (i = 0; i < VR_RX_LIST_CNT; i++) { | |
1641 | if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { | |
1642 | m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); | |
1643 | sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; | |
1644 | } | |
1645 | } | |
e9175cc8 | 1646 | bzero(&sc->vr_ldata->vr_rx_list, sizeof(sc->vr_ldata->vr_rx_list)); |
984263bc MD |
1647 | |
1648 | /* | |
e9175cc8 | 1649 | * Reset the TX list buffer pointers. |
984263bc | 1650 | */ |
e9175cc8 SZ |
1651 | for (i = 0; i < VR_TX_LIST_CNT; i++) |
1652 | sc->vr_cdata.vr_tx_chain[i].vr_buf = NULL; | |
984263bc | 1653 | |
e9175cc8 | 1654 | bzero(&sc->vr_ldata->vr_tx_list, sizeof(sc->vr_ldata->vr_tx_list)); |
984263bc | 1655 | |
9ed293e0 SZ |
1656 | ifp->if_flags &= ~IFF_RUNNING; |
1657 | ifq_clr_oactive(&ifp->if_snd); | |
984263bc MD |
1658 | } |
1659 | ||
1660 | /* | |
1661 | * Stop all chip I/O so that the kernel's probe routines don't | |
1662 | * get confused by errant DMAs when rebooting. | |
1663 | */ | |
fa23be1c JS |
1664 | static void |
1665 | vr_shutdown(device_t dev) | |
984263bc | 1666 | { |
fa23be1c | 1667 | struct vr_softc *sc; |
984263bc MD |
1668 | |
1669 | sc = device_get_softc(dev); | |
1670 | ||
1671 | vr_stop(sc); | |
984263bc | 1672 | } |