Commit | Line | Data |
---|---|---|
984263bc | 1 | /*- |
7261a835 | 2 | * Copyright (c) 1998, 1999, 2003 Scott Mitchell |
984263bc MD |
3 | * 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 | * | |
14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
24 | * SUCH DAMAGE. | |
25 | * | |
26 | * $Id: if_xe.c,v 1.20 1999/06/13 19:17:40 scott Exp $ | |
7261a835 | 27 | * $FreeBSD: src/sys/dev/xe/if_xe.c,v 1.39 2003/10/14 22:51:35 rsm Exp $ |
984263bc MD |
28 | */ |
29 | ||
30 | /* | |
31 | * Portions of this software were derived from Werner Koch's xirc2ps driver | |
32 | * for Linux under the terms of the following license (from v1.30 of the | |
33 | * xirc2ps driver): | |
34 | * | |
35 | * Copyright (c) 1997 by Werner Koch (dd9jn) | |
36 | * | |
37 | * Redistribution and use in source and binary forms, with or without | |
38 | * modification, are permitted provided that the following conditions | |
39 | * are met: | |
40 | * 1. Redistributions of source code must retain the above copyright | |
41 | * notice, and the entire permission notice in its entirety, | |
42 | * including the disclaimer of warranties. | |
43 | * 2. Redistributions in binary form must reproduce the above copyright | |
44 | * notice, this list of conditions and the following disclaimer in the | |
45 | * documentation and/or other materials provided with the distribution. | |
46 | * 3. The name of the author may not be used to endorse or promote | |
47 | * products derived from this software without specific prior | |
48 | * written permission. | |
49 | * | |
50 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
51 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
52 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
53 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
54 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
55 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
56 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
57 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
58 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
59 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
60 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
61 | */ | |
62 | ||
63 | /* | |
64 | * FreeBSD device driver for Xircom CreditCard PCMCIA Ethernet adapters. The | |
65 | * following cards are currently known to work with the driver: | |
66 | * Xircom CreditCard 10/100 (CE3) | |
67 | * Xircom CreditCard Ethernet + Modem 28 (CEM28) | |
68 | * Xircom CreditCard Ethernet 10/100 + Modem 56 (CEM56) | |
69 | * Xircom RealPort Ethernet 10 | |
70 | * Xircom RealPort Ethernet 10/100 | |
71 | * Xircom RealPort Ethernet 10/100 + Modem 56 (REM56, REM56G) | |
72 | * Intel EtherExpress Pro/100 PC Card Mobile Adapter 16 (Pro/100 M16A) | |
73 | * Compaq Netelligent 10/100 PC Card (CPQ-10/100) | |
74 | * | |
75 | * Some other cards *should* work, but support for them is either broken or in | |
76 | * an unknown state at the moment. I'm always interested in hearing from | |
77 | * people who own any of these cards: | |
78 | * Xircom CreditCard 10Base-T (PS-CE2-10) | |
79 | * Xircom CreditCard Ethernet + ModemII (CEM2) | |
80 | * Xircom CEM28 and CEM33 Ethernet/Modem cards (may be variants of CEM2?) | |
81 | * | |
82 | * Thanks to all who assisted with the development and testing of the driver, | |
83 | * especially: Werner Koch, Duke Kamstra, Duncan Barclay, Jason George, Dru | |
84 | * Nelson, Mike Kephart, Bill Rainey and Douglas Rand. Apologies if I've left | |
85 | * out anyone who deserves a mention here. | |
86 | * | |
87 | * Special thanks to Ade Lovett for both hosting the mailing list and doing | |
88 | * the CEM56/REM56 support code; and the FreeBSD UK Users' Group for hosting | |
89 | * the web pages. | |
90 | * | |
984263bc | 91 | * Author email: <scott@uk.freebsd.org> |
7261a835 | 92 | * Driver web page: http://ukug.uk.freebsd.org/~scott/xe_drv/ |
984263bc MD |
93 | */ |
94 | ||
95 | ||
96 | #include <sys/param.h> | |
984263bc MD |
97 | #include <sys/errno.h> |
98 | #include <sys/kernel.h> | |
99 | #include <sys/mbuf.h> | |
100 | #include <sys/select.h> | |
101 | #include <sys/socket.h> | |
102 | #include <sys/sockio.h> | |
7261a835 | 103 | #include <sys/sysctl.h> |
984263bc MD |
104 | #include <sys/systm.h> |
105 | #include <sys/uio.h> | |
78195a76 | 106 | #include <sys/serialize.h> |
984263bc MD |
107 | #include <sys/module.h> |
108 | #include <sys/bus.h> | |
984263bc | 109 | #include <sys/rman.h> |
1f7ab7c9 | 110 | #include <sys/thread2.h> |
9db4b353 | 111 | #include <sys/interrupt.h> |
984263bc MD |
112 | |
113 | #include <net/ethernet.h> | |
114 | #include <net/if.h> | |
1a3869d2 | 115 | #include <net/ifq_var.h> |
984263bc MD |
116 | #include <net/if_arp.h> |
117 | #include <net/if_dl.h> | |
118 | #include <net/if_media.h> | |
119 | #include <net/if_mib.h> | |
120 | #include <net/bpf.h> | |
121 | ||
1f2de5d4 MD |
122 | #include "if_xereg.h" |
123 | #include "if_xevar.h" | |
984263bc | 124 | |
984263bc MD |
125 | /* |
126 | * MII command structure | |
127 | */ | |
128 | struct xe_mii_frame { | |
129 | u_int8_t mii_stdelim; | |
130 | u_int8_t mii_opcode; | |
131 | u_int8_t mii_phyaddr; | |
132 | u_int8_t mii_regaddr; | |
133 | u_int8_t mii_turnaround; | |
134 | u_int16_t mii_data; | |
135 | }; | |
136 | ||
137 | /* | |
138 | * Media autonegotiation progress constants | |
139 | */ | |
140 | #define XE_AUTONEG_NONE 0 /* No autonegotiation in progress */ | |
141 | #define XE_AUTONEG_WAITING 1 /* Waiting for transmitter to go idle */ | |
142 | #define XE_AUTONEG_STARTED 2 /* Waiting for autonegotiation to complete */ | |
143 | #define XE_AUTONEG_100TX 3 /* Trying to force 100baseTX link */ | |
144 | #define XE_AUTONEG_FAIL 4 /* Autonegotiation failed */ | |
145 | ||
7261a835 SZ |
146 | /* |
147 | * Multicast hashing CRC constants | |
148 | */ | |
149 | #define XE_CRC_POLY 0x04c11db6 | |
984263bc MD |
150 | |
151 | /* | |
152 | * Prototypes start here | |
153 | */ | |
984263bc | 154 | static void xe_init (void *xscp); |
7cdc3d6e | 155 | static void xe_intr (void *xscp); |
f0a26983 | 156 | static void xe_start (struct ifnet *ifp, struct ifaltq_subque *); |
bd4539cc | 157 | static int xe_ioctl (struct ifnet *ifp, u_long command, caddr_t data, struct ucred *); |
984263bc MD |
158 | static void xe_watchdog (struct ifnet *ifp); |
159 | static int xe_media_change (struct ifnet *ifp); | |
160 | static void xe_media_status (struct ifnet *ifp, struct ifmediareq *mrp); | |
161 | static timeout_t xe_setmedia; | |
78195a76 | 162 | static timeout_t xe_setmedia_serialized; |
7261a835 | 163 | static void xe_reset (struct xe_softc *scp); |
984263bc MD |
164 | static void xe_stop (struct xe_softc *scp); |
165 | static void xe_enable_intr (struct xe_softc *scp); | |
166 | static void xe_disable_intr (struct xe_softc *scp); | |
7261a835 SZ |
167 | static void xe_set_multicast (struct xe_softc *scp); |
168 | static void xe_set_addr (struct xe_softc *scp, u_int8_t* addr, unsigned idx); | |
169 | static void xe_set_hash (struct xe_softc *scp, u_int8_t* addr); | |
984263bc | 170 | static int xe_pio_write_packet (struct xe_softc *scp, struct mbuf *mbp); |
984263bc MD |
171 | |
172 | /* | |
173 | * MII functions | |
174 | */ | |
175 | static void xe_mii_sync (struct xe_softc *scp); | |
176 | static int xe_mii_init (struct xe_softc *scp); | |
177 | static void xe_mii_send (struct xe_softc *scp, u_int32_t bits, int cnt); | |
178 | static int xe_mii_readreg (struct xe_softc *scp, struct xe_mii_frame *frame); | |
179 | static int xe_mii_writereg (struct xe_softc *scp, struct xe_mii_frame *frame); | |
180 | static u_int16_t xe_phy_readreg (struct xe_softc *scp, u_int16_t reg); | |
181 | static void xe_phy_writereg (struct xe_softc *scp, u_int16_t reg, u_int16_t data); | |
182 | ||
7261a835 | 183 | /* Debugging functions */ |
cdf89432 | 184 | static void xe_reg_dump (struct xe_softc *scp) __unused; |
984263bc | 185 | static void xe_mii_dump (struct xe_softc *scp); |
7261a835 SZ |
186 | |
187 | #define XE_DEBUG | |
188 | ||
189 | #ifdef XE_DEBUG | |
190 | ||
191 | /* sysctl vars */ | |
192 | SYSCTL_NODE(_hw, OID_AUTO, xe, CTLFLAG_RD, 0, "xe parameters"); | |
193 | ||
194 | /* | |
195 | * Debug logging levels - set with hw.xe.debug sysctl | |
196 | * 0 = None | |
197 | * 1 = More hardware details, probe/attach progress | |
198 | * 2 = Most function calls, ioctls and media selection progress | |
199 | * 3 = Everything - interrupts, packets in/out and multicast address setup | |
200 | */ | |
201 | int xe_debug = 1; | |
202 | SYSCTL_INT(_hw_xe, OID_AUTO, debug, CTLFLAG_RW, &xe_debug, 0, "xe debug level"); | |
203 | ||
e3869ec7 | 204 | #define DPRINTF(level, arg) if (xe_debug >= (level)) kprintf arg |
7261a835 SZ |
205 | #define IFPRINTF(level, arg) if (xe_debug >= (level)) if_printf arg |
206 | #define DEVPRINTF(level, arg) if (xe_debug >= (level)) device_printf arg | |
207 | #define XE_MII_DUMP(scp) if (xe_debug >= 3) xe_mii_dump(scp) | |
208 | #define XE_REG_DUMP(scp) if (xe_debug >= 3) xe_reg_dump(scp) | |
209 | ||
210 | #else /* !XE_DEBUG */ | |
211 | ||
212 | #define DPRINTF(level, arg) | |
213 | #define IFPRINTF(level, arg) | |
214 | #define DEVPRINTF(level, arg) | |
984263bc MD |
215 | #define XE_REG_DUMP(scp) |
216 | #define XE_MII_DUMP(scp) | |
7261a835 SZ |
217 | |
218 | #endif /* XE_DEBUG */ | |
984263bc | 219 | |
984263bc MD |
220 | /* |
221 | * The device entry is being removed, probably because someone ejected the | |
222 | * card. The interface should have been brought down manually before calling | |
223 | * this function; if not you may well lose packets. In any case, I shut down | |
224 | * the card and the interface, and hope for the best. | |
225 | */ | |
f572e449 JS |
226 | int |
227 | xe_detach(device_t dev) | |
228 | { | |
984263bc | 229 | struct xe_softc *sc = device_get_softc(dev); |
cdf89432 | 230 | struct ifnet *ifp = &sc->arpcom.ac_if; |
984263bc | 231 | |
cdf89432 | 232 | lwkt_serialize_enter(ifp->if_serializer); |
7cdc3d6e | 233 | |
cdf89432 | 234 | ifp->if_flags &= ~IFF_RUNNING; |
74a0ee08 | 235 | callout_stop(&sc->xe_timer); |
7cdc3d6e JS |
236 | bus_teardown_intr(dev, sc->irq_res, sc->intrhand); |
237 | ||
cdf89432 | 238 | lwkt_serialize_exit(ifp->if_serializer); |
7cdc3d6e | 239 | |
cdf89432 | 240 | ether_ifdetach(ifp); |
984263bc | 241 | xe_deactivate(dev); |
cdf89432 | 242 | |
984263bc MD |
243 | return 0; |
244 | } | |
245 | ||
246 | /* | |
247 | * Attach a device. | |
248 | */ | |
f572e449 JS |
249 | int |
250 | xe_attach (device_t dev) | |
251 | { | |
984263bc | 252 | struct xe_softc *scp = device_get_softc(dev); |
7cdc3d6e | 253 | int err; |
984263bc | 254 | |
7261a835 | 255 | DEVPRINTF(2, (dev, "attach\n")); |
984263bc | 256 | |
984263bc MD |
257 | /* Fill in some private data */ |
258 | scp->ifp = &scp->arpcom.ac_if; | |
259 | scp->ifm = &scp->ifmedia; | |
7261a835 | 260 | scp->autoneg_status = XE_AUTONEG_NONE; |
984263bc | 261 | |
984263bc | 262 | /* Initialise the ifnet structure */ |
3e4a09e7 | 263 | scp->ifp->if_softc = scp; |
2682bd2f | 264 | if_initname(scp->ifp, device_get_name(dev), device_get_unit(dev)); |
3e4a09e7 MD |
265 | scp->ifp->if_timer = 0; |
266 | scp->ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); | |
267 | scp->ifp->if_linkmib = &scp->mibdata; | |
268 | scp->ifp->if_linkmiblen = sizeof scp->mibdata; | |
3e4a09e7 MD |
269 | scp->ifp->if_start = xe_start; |
270 | scp->ifp->if_ioctl = xe_ioctl; | |
271 | scp->ifp->if_watchdog = xe_watchdog; | |
272 | scp->ifp->if_init = xe_init; | |
7261a835 | 273 | scp->ifp->if_baudrate = 100000000; |
1a3869d2 JS |
274 | ifq_set_maxlen(&scp->ifp->if_snd, IFQ_MAXLEN); |
275 | ifq_set_ready(&scp->ifp->if_snd); | |
984263bc MD |
276 | |
277 | /* Initialise the ifmedia structure */ | |
278 | ifmedia_init(scp->ifm, 0, xe_media_change, xe_media_status); | |
74a0ee08 | 279 | callout_init(&scp->xe_timer); |
984263bc | 280 | |
7261a835 | 281 | /* Add supported media types */ |
984263bc MD |
282 | if (scp->mohawk) { |
283 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_100_TX, 0, NULL); | |
7261a835 SZ |
284 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); |
285 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); | |
984263bc | 286 | } |
7261a835 SZ |
287 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL); |
288 | if (scp->ce2) | |
984263bc | 289 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_2, 0, NULL); |
984263bc MD |
290 | ifmedia_add(scp->ifm, IFM_ETHER|IFM_AUTO, 0, NULL); |
291 | ||
292 | /* Default is to autoselect best supported media type */ | |
293 | ifmedia_set(scp->ifm, IFM_ETHER|IFM_AUTO); | |
294 | ||
7261a835 SZ |
295 | /* Get the hardware into a known state */ |
296 | xe_reset(scp); | |
297 | ||
298 | /* Get hardware version numbers */ | |
299 | XE_SELECT_PAGE(4); | |
300 | scp->version = XE_INB(XE_BOV); | |
301 | if (scp->mohawk) | |
302 | scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4; | |
303 | else | |
304 | scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4; | |
305 | ||
984263bc | 306 | /* Print some useful information */ |
7261a835 | 307 | device_printf(dev, "%s %s, version 0x%02x/0x%02x%s%s\n", |
984263bc MD |
308 | scp->vendor, |
309 | scp->card_type, | |
310 | scp->version, | |
7261a835 | 311 | scp->srev, |
984263bc MD |
312 | scp->mohawk ? ", 100Mbps capable" : "", |
313 | scp->modem ? ", with modem" : ""); | |
7261a835 | 314 | |
984263bc MD |
315 | if (scp->mohawk) { |
316 | XE_SELECT_PAGE(0x10); | |
7261a835 SZ |
317 | DEVPRINTF(1, (dev, "DingoID=0x%04x, RevisionID=0x%04x, VendorID=0x%04x\n", |
318 | XE_INW(XE_DINGOID), | |
319 | XE_INW(XE_RevID), | |
320 | XE_INW(XE_VendorID))); | |
984263bc MD |
321 | } |
322 | if (scp->ce2) { | |
323 | XE_SELECT_PAGE(0x45); | |
7261a835 | 324 | DEVPRINTF(1, (dev, "CE2 version = 0x%#02x\n", XE_INB(XE_REV))); |
984263bc MD |
325 | } |
326 | ||
984263bc | 327 | /* Attach the interface */ |
78195a76 | 328 | ether_ifattach(scp->ifp, scp->arpcom.ac_enaddr, NULL); |
984263bc | 329 | |
4c77af2d SZ |
330 | ifq_set_cpuid(&scp->ifp->if_snd, rman_get_cpuid(scp->irq_res)); |
331 | ||
95893fe4 | 332 | err = bus_setup_intr(dev, scp->irq_res, INTR_MPSAFE, |
78195a76 | 333 | xe_intr, scp, &scp->intrhand, |
9db4b353 | 334 | scp->ifp->if_serializer); |
7cdc3d6e | 335 | if (err) { |
7261a835 | 336 | device_printf(dev, "Setup intr failed\n"); |
9db4b353 | 337 | ether_ifdetach(scp->ifp); |
7cdc3d6e JS |
338 | xe_deactivate(dev); |
339 | return err; | |
340 | } | |
341 | ||
984263bc MD |
342 | /* Done */ |
343 | return 0; | |
344 | } | |
345 | ||
346 | ||
347 | /* | |
7261a835 SZ |
348 | * Complete hardware intitialisation and enable output. Exits without doing |
349 | * anything if there's no address assigned to the card, or if media selection | |
350 | * is in progress (the latter implies we've already run this function). | |
984263bc MD |
351 | */ |
352 | static void | |
353 | xe_init(void *xscp) { | |
354 | struct xe_softc *scp = xscp; | |
7261a835 | 355 | u_int i; |
984263bc | 356 | |
7261a835 SZ |
357 | if (scp->autoneg_status != XE_AUTONEG_NONE) return; |
358 | ||
359 | IFPRINTF(2, (scp->ifp, "init\n")); | |
360 | ||
361 | crit_enter(); | |
984263bc | 362 | |
984263bc MD |
363 | /* Reset transmitter flags */ |
364 | scp->tx_queued = 0; | |
365 | scp->tx_tpr = 0; | |
7261a835 SZ |
366 | scp->tx_timeouts = 0; |
367 | scp->tx_thres = 64; | |
368 | scp->tx_min = ETHER_MIN_LEN - ETHER_CRC_LEN; | |
984263bc MD |
369 | scp->ifp->if_timer = 0; |
370 | ||
7261a835 SZ |
371 | /* Soft reset the card */ |
372 | XE_SELECT_PAGE(0); | |
373 | XE_OUTB(XE_CR, XE_CR_SOFT_RESET); | |
374 | DELAY(40000); | |
375 | XE_OUTB(XE_CR, 0); | |
376 | DELAY(40000); | |
377 | ||
378 | if (scp->mohawk) { | |
379 | /* | |
380 | * set GP1 and GP2 as outputs (bits 2 & 3) | |
381 | * set GP1 low to power on the ML6692 (bit 0) | |
382 | * set GP2 high to power on the 10Mhz chip (bit 1) | |
383 | */ | |
384 | XE_SELECT_PAGE(4); | |
385 | XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT|XE_GPR0_GP1_SELECT|XE_GPR0_GP2_OUT); | |
386 | } | |
387 | ||
388 | /* Shut off interrupts */ | |
389 | xe_disable_intr(scp); | |
390 | ||
391 | /* Wait for everything to wake up */ | |
392 | DELAY(500000); | |
393 | ||
394 | /* Check for PHY */ | |
395 | if (scp->mohawk) | |
396 | scp->phy_ok = xe_mii_init(scp); | |
984263bc | 397 | |
7261a835 | 398 | /* Disable 'source insertion' (not sure what that means) */ |
984263bc | 399 | XE_SELECT_PAGE(0x42); |
7261a835 | 400 | XE_OUTB(XE_SWC0, XE_SWC0_NO_SRC_INSERT); |
984263bc | 401 | |
7261a835 | 402 | /* Set 8K/24K Tx/Rx buffer split */ |
984263bc MD |
403 | if (scp->srev != 1) { |
404 | XE_SELECT_PAGE(2); | |
405 | XE_OUTW(XE_RBS, 0x2000); | |
406 | } | |
407 | ||
7261a835 SZ |
408 | /* Enable early transmit mode on Mohawk/Dingo */ |
409 | if (scp->mohawk) { | |
410 | XE_SELECT_PAGE(0x03); | |
411 | XE_OUTW(XE_TPT, scp->tx_thres); | |
412 | XE_SELECT_PAGE(0x01); | |
413 | XE_OUTB(XE_ECR, XE_INB(XE_ECR) | XE_ECR_EARLY_TX); | |
414 | } | |
415 | ||
416 | /* Put MAC address in first 'individual address' register */ | |
417 | XE_SELECT_PAGE(0x50); | |
418 | for (i = 0; i < 6; i++) | |
419 | XE_OUTB(0x08 + i, scp->arpcom.ac_enaddr[scp->mohawk ? 5 - i : i]); | |
420 | ||
984263bc | 421 | /* Set up multicast addresses */ |
7261a835 | 422 | xe_set_multicast(scp); |
984263bc | 423 | |
7261a835 | 424 | /* Fix the receive data offset -- reset can leave it off-by-one */ |
984263bc MD |
425 | XE_SELECT_PAGE(0); |
426 | XE_OUTW(XE_DO, 0x2000); | |
427 | ||
7261a835 SZ |
428 | /* Set interrupt masks */ |
429 | XE_SELECT_PAGE(1); | |
430 | XE_OUTB(XE_IMR0, XE_IMR0_TX_PACKET | XE_IMR0_MAC_INTR | XE_IMR0_RX_PACKET); | |
431 | ||
432 | /* Set MAC interrupt masks */ | |
433 | XE_SELECT_PAGE(0x40); | |
434 | XE_OUTB(XE_RX0Msk, | |
435 | ~(XE_RX0M_RX_OVERRUN | XE_RX0M_CRC_ERROR | |
436 | | XE_RX0M_ALIGN_ERROR | XE_RX0M_LONG_PACKET)); | |
437 | XE_OUTB(XE_TX0Msk, | |
438 | ~(XE_TX0M_SQE_FAIL | XE_TX0M_LATE_COLLISION | XE_TX0M_TX_UNDERRUN | |
439 | | XE_TX0M_16_COLLISIONS | XE_TX0M_NO_CARRIER)); | |
440 | ||
441 | /* Clear MAC status registers */ | |
442 | XE_SELECT_PAGE(0x40); | |
443 | XE_OUTB(XE_RST0, 0x00); | |
444 | XE_OUTB(XE_TXST0, 0x00); | |
445 | ||
446 | /* Enable receiver and put MAC online */ | |
447 | XE_SELECT_PAGE(0x40); | |
448 | XE_OUTB(XE_CMD0, XE_CMD0_RX_ENABLE|XE_CMD0_ONLINE); | |
449 | ||
450 | /* Set up IMR, enable interrupts */ | |
451 | xe_enable_intr(scp); | |
984263bc | 452 | |
7261a835 | 453 | /* Start media selection */ |
78195a76 | 454 | xe_setmedia_serialized(scp); |
984263bc | 455 | |
7261a835 SZ |
456 | /* Enable output */ |
457 | scp->ifp->if_flags |= IFF_RUNNING; | |
9ed293e0 | 458 | ifq_clr_oactive(&scp->ifp->if_snd); |
984263bc | 459 | |
9228feed | 460 | crit_exit(); |
984263bc MD |
461 | } |
462 | ||
463 | ||
464 | /* | |
7261a835 | 465 | * Start output on interface. Should be called at splimp() priority. Check |
9ed293e0 SZ |
466 | * that the output is idle (ie, OACTIVE is not set) before calling this |
467 | * function. If media selection is in progress we set OACTIVE ourselves | |
7261a835 | 468 | * and return immediately. |
984263bc MD |
469 | */ |
470 | static void | |
f0a26983 | 471 | xe_start(struct ifnet *ifp, struct ifaltq_subque *ifsq) { |
984263bc MD |
472 | struct xe_softc *scp = ifp->if_softc; |
473 | struct mbuf *mbp; | |
474 | ||
f0a26983 SZ |
475 | ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq); |
476 | ||
7261a835 | 477 | if (scp->autoneg_status != XE_AUTONEG_NONE) { |
9ed293e0 | 478 | ifq_set_oactive(&ifp->if_snd); |
7261a835 SZ |
479 | return; |
480 | } | |
481 | ||
482 | IFPRINTF(3, (ifp, "start\n")); | |
483 | ||
984263bc MD |
484 | /* |
485 | * Loop while there are packets to be sent, and space to send them. | |
486 | */ | |
487 | while (1) { | |
9db4b353 | 488 | /* Suck a packet off the send queue */ |
ac9843a1 | 489 | mbp = ifq_dequeue(&ifp->if_snd); |
984263bc MD |
490 | if (mbp == NULL) { |
491 | /* | |
9db4b353 SZ |
492 | * We are using the !OACTIVE flag to indicate to the outside |
493 | * world that we can accept an additional packet rather than | |
494 | * that the transmitter is _actually_ active. Indeed, the | |
495 | * transmitter may be active, but if we haven't filled all | |
496 | * the buffers with data then we still want to accept more. | |
984263bc | 497 | */ |
9ed293e0 | 498 | ifq_clr_oactive(&ifp->if_snd); |
984263bc MD |
499 | return; |
500 | } | |
501 | ||
502 | if (xe_pio_write_packet(scp, mbp) != 0) { | |
9ed293e0 | 503 | ifq_set_oactive(&ifp->if_snd); |
9db4b353 | 504 | ifq_prepend(&ifp->if_snd, mbp); |
984263bc MD |
505 | return; |
506 | } | |
507 | ||
7600679e | 508 | BPF_MTAP(ifp, mbp); |
984263bc | 509 | |
9db4b353 | 510 | ifp->if_timer = 5; /* In case we don't hear from the card again */ |
984263bc MD |
511 | scp->tx_queued++; |
512 | ||
513 | m_freem(mbp); | |
514 | } | |
515 | } | |
516 | ||
517 | ||
518 | /* | |
519 | * Process an ioctl request. Adapted from the ed driver. | |
520 | */ | |
521 | static int | |
bd4539cc | 522 | xe_ioctl (struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr) { |
984263bc | 523 | struct xe_softc *scp; |
9228feed | 524 | int error; |
984263bc MD |
525 | |
526 | scp = ifp->if_softc; | |
527 | error = 0; | |
528 | ||
9228feed | 529 | crit_enter(); |
984263bc MD |
530 | |
531 | switch (command) { | |
532 | ||
7261a835 SZ |
533 | case SIOCSIFFLAGS: |
534 | IFPRINTF(2, (ifp, "ioctl: SIOCSIFFLAGS: 0x%04x\n", ifp->if_flags)); | |
984263bc MD |
535 | /* |
536 | * If the interface is marked up and stopped, then start it. If it is | |
537 | * marked down and running, then stop it. | |
538 | */ | |
539 | if (ifp->if_flags & IFF_UP) { | |
540 | if (!(ifp->if_flags & IFF_RUNNING)) { | |
7261a835 | 541 | xe_reset(scp); |
984263bc MD |
542 | xe_init(scp); |
543 | } | |
544 | } | |
545 | else { | |
546 | if (ifp->if_flags & IFF_RUNNING) | |
547 | xe_stop(scp); | |
548 | } | |
7261a835 | 549 | /* FALL THROUGH (handle changes to PROMISC/ALLMULTI flags) */ |
984263bc | 550 | |
7261a835 SZ |
551 | case SIOCADDMULTI: |
552 | case SIOCDELMULTI: | |
553 | IFPRINTF(2, (ifp, "ioctl: SIOC{ADD,DEL}MULTI\n")); | |
984263bc | 554 | /* |
7261a835 SZ |
555 | * Multicast list has (maybe) changed; set the hardware filters |
556 | * accordingly. | |
984263bc | 557 | */ |
7261a835 | 558 | xe_set_multicast(scp); |
984263bc MD |
559 | error = 0; |
560 | break; | |
561 | ||
7261a835 SZ |
562 | case SIOCSIFMEDIA: |
563 | case SIOCGIFMEDIA: | |
564 | IFPRINTF(3, (ifp, "ioctl: bounce to ifmedia_ioctl\n")); | |
984263bc MD |
565 | /* |
566 | * Someone wants to get/set media options. | |
567 | */ | |
568 | error = ifmedia_ioctl(ifp, (struct ifreq *)data, &scp->ifmedia, command); | |
569 | break; | |
570 | ||
7261a835 SZ |
571 | default: |
572 | IFPRINTF(3, (ifp, "ioctl: bounce to ether_ioctl\n")); | |
4cde4dd5 JS |
573 | error = ether_ioctl(ifp, command, data); |
574 | break; | |
984263bc MD |
575 | } |
576 | ||
9228feed | 577 | crit_exit(); |
984263bc MD |
578 | |
579 | return error; | |
580 | } | |
581 | ||
582 | ||
583 | /* | |
584 | * Card interrupt handler. | |
585 | * | |
586 | * This function is probably more complicated than it needs to be, as it | |
587 | * attempts to deal with the case where multiple packets get sent between | |
588 | * interrupts. This is especially annoying when working out the collision | |
589 | * stats. Not sure whether this case ever really happens or not (maybe on a | |
590 | * slow/heavily loaded machine?) so it's probably best to leave this like it | |
591 | * is. | |
592 | * | |
593 | * Note that the crappy PIO used to get packets on and off the card means that | |
594 | * you will spend a lot of time in this routine -- I can get my P150 to spend | |
595 | * 90% of its time servicing interrupts if I really hammer the network. Could | |
596 | * fix this, but then you'd start dropping/losing packets. The moral of this | |
597 | * story? If you want good network performance _and_ some cycles left over to | |
598 | * get your work done, don't buy a Xircom card. Or convince them to tell me | |
599 | * how to do memory-mapped I/O :) | |
600 | */ | |
601 | static void | |
602 | xe_intr(void *xscp) | |
603 | { | |
604 | struct xe_softc *scp = (struct xe_softc *) xscp; | |
605 | struct ifnet *ifp; | |
7261a835 | 606 | u_int8_t psr, isr, esr, rsr, rst0, txst0, txst1, coll; |
984263bc MD |
607 | |
608 | ifp = &scp->arpcom.ac_if; | |
984263bc | 609 | |
7261a835 SZ |
610 | /* Disable interrupts */ |
611 | if (scp->mohawk) | |
612 | XE_OUTB(XE_CR, 0); | |
984263bc | 613 | |
7261a835 SZ |
614 | /* Cache current register page */ |
615 | psr = XE_INB(XE_PR); | |
984263bc | 616 | |
7261a835 SZ |
617 | /* Read ISR to see what caused this interrupt */ |
618 | while ((isr = XE_INB(XE_ISR)) != 0) { | |
619 | /* 0xff might mean the card is no longer around */ | |
620 | if (isr == 0xff) { | |
621 | IFPRINTF(3, (ifp, "intr: interrupt received for missing card?\n")); | |
622 | break; | |
623 | } | |
984263bc | 624 | |
7261a835 | 625 | /* Read other status registers */ |
984263bc | 626 | XE_SELECT_PAGE(0x40); |
7261a835 SZ |
627 | rst0 = XE_INB(XE_RST0); |
628 | XE_OUTB(XE_RST0, 0); | |
629 | txst0 = XE_INB(XE_TXST0); | |
630 | txst1 = XE_INB(XE_TXST1); | |
631 | coll = txst1 & XE_TXST1_RETRY_COUNT; | |
984263bc MD |
632 | XE_OUTB(XE_TXST0, 0); |
633 | XE_OUTB(XE_TXST1, 0); | |
634 | XE_SELECT_PAGE(0); | |
635 | ||
7261a835 SZ |
636 | IFPRINTF(3, (ifp, |
637 | "intr: ISR=0x%02x, RST=0x%02x, TXT=0x%02x%02x, COLL=0x%01x\n", | |
638 | isr, rst0, txst1, txst0, coll)); | |
984263bc | 639 | |
7261a835 | 640 | /* Handle transmitted packet(s) */ |
984263bc | 641 | if (isr & XE_ISR_TX_PACKET) { |
7261a835 SZ |
642 | u_int8_t tpr, sent; |
643 | ||
644 | /* Update packet count, accounting for rollover */ | |
645 | tpr = XE_INB(XE_TPR); | |
646 | sent = -scp->tx_tpr + tpr; | |
984263bc | 647 | |
7261a835 SZ |
648 | /* Update statistics if we actually sent anything */ |
649 | if (sent > 0) { | |
650 | scp->tx_tpr = tpr; | |
984263bc | 651 | scp->tx_queued -= sent; |
d40991ef SZ |
652 | IFNET_STAT_INC(ifp, opackets, sent); |
653 | IFNET_STAT_INC(ifp, collisions, coll); | |
984263bc MD |
654 | |
655 | /* | |
7261a835 SZ |
656 | * According to the Xircom manual, Dingo will sometimes manage to |
657 | * transmit a packet with triggering an interrupt. If this happens, | |
658 | * we have sent > 1 and the collision count only reflects collisions | |
659 | * on the last packet sent (the one that triggered the interrupt). | |
660 | * Collision stats might therefore be a bit low, but there doesn't | |
661 | * seem to be anything we can do about that. | |
984263bc | 662 | */ |
7261a835 SZ |
663 | switch (coll) { |
664 | case 0: | |
984263bc | 665 | break; |
7261a835 | 666 | case 1: |
984263bc MD |
667 | scp->mibdata.dot3StatsSingleCollisionFrames++; |
668 | scp->mibdata.dot3StatsCollFrequencies[0]++; | |
669 | break; | |
7261a835 SZ |
670 | default: |
671 | scp->mibdata.dot3StatsMultipleCollisionFrames++; | |
672 | scp->mibdata.dot3StatsCollFrequencies[coll-1]++; | |
984263bc | 673 | } |
984263bc MD |
674 | } |
675 | ifp->if_timer = 0; | |
9ed293e0 | 676 | ifq_clr_oactive(&ifp->if_snd); |
984263bc | 677 | } |
984263bc | 678 | |
7261a835 SZ |
679 | /* Handle most MAC interrupts */ |
680 | if (isr & XE_ISR_MAC_INTR) { | |
681 | #if 0 | |
682 | /* Carrier sense lost -- only in 10Mbit HDX mode */ | |
683 | if (txst0 & XE_TXST0_NO_CARRIER || !(txst1 & XE_TXST1_LINK_STATUS)) { | |
684 | /* XXX - Need to update media status here */ | |
685 | device_printf(scp->dev, "no carrier\n"); | |
686 | ifp->if_oerrors++; | |
687 | scp->mibdata.dot3StatsCarrierSenseErrors++; | |
688 | } | |
689 | #endif | |
690 | /* Excessive collisions -- try sending again */ | |
691 | if (txst0 & XE_TXST0_16_COLLISIONS) { | |
d40991ef SZ |
692 | IFNET_STAT_INC(ifp, collisions, 16); |
693 | IFNET_STAT_INC(ifp, oerrors, 1); | |
7261a835 SZ |
694 | scp->mibdata.dot3StatsExcessiveCollisions++; |
695 | scp->mibdata.dot3StatsMultipleCollisionFrames++; | |
696 | scp->mibdata.dot3StatsCollFrequencies[15]++; | |
697 | XE_OUTB(XE_CR, XE_CR_RESTART_TX); | |
698 | } | |
699 | /* Transmit underrun -- increase early transmit threshold */ | |
700 | if (txst0 & XE_TXST0_TX_UNDERRUN && scp->mohawk) { | |
701 | IFPRINTF(1, (ifp, "transmit underrun")); | |
702 | if (scp->tx_thres < ETHER_MAX_LEN) { | |
703 | if ((scp->tx_thres += 64) > ETHER_MAX_LEN) | |
704 | scp->tx_thres = ETHER_MAX_LEN; | |
705 | DPRINTF(1, (": increasing transmit threshold to %u", scp->tx_thres)); | |
706 | XE_SELECT_PAGE(0x3); | |
707 | XE_OUTW(XE_TPT, scp->tx_thres); | |
708 | XE_SELECT_PAGE(0x0); | |
709 | } | |
710 | DPRINTF(1, ("\n")); | |
d40991ef | 711 | IFNET_STAT_INC(ifp, oerrors, 1); |
7261a835 SZ |
712 | scp->mibdata.dot3StatsInternalMacTransmitErrors++; |
713 | } | |
714 | /* Late collision -- just complain about it */ | |
715 | if (txst0 & XE_TXST0_LATE_COLLISION) { | |
716 | if_printf(ifp, "late collision\n"); | |
d40991ef | 717 | IFNET_STAT_INC(ifp, oerrors, 1); |
7261a835 SZ |
718 | scp->mibdata.dot3StatsLateCollisions++; |
719 | } | |
720 | /* SQE test failure -- just complain about it */ | |
721 | if (txst0 & XE_TXST0_SQE_FAIL) { | |
722 | if_printf(ifp, "SQE test failure\n"); | |
d40991ef | 723 | IFNET_STAT_INC(ifp, oerrors, 1); |
7261a835 SZ |
724 | scp->mibdata.dot3StatsSQETestErrors++; |
725 | } | |
726 | /* Packet too long -- what happens to these */ | |
727 | if (rst0 & XE_RST0_LONG_PACKET) { | |
728 | if_printf(ifp, "received giant packet\n"); | |
d40991ef | 729 | IFNET_STAT_INC(ifp, ierrors, 1); |
7261a835 SZ |
730 | scp->mibdata.dot3StatsFrameTooLongs++; |
731 | } | |
732 | /* CRC error -- packet dropped */ | |
733 | if (rst0 & XE_RST0_CRC_ERROR) { | |
734 | if_printf(ifp, "CRC error\n"); | |
d40991ef | 735 | IFNET_STAT_INC(ifp, ierrors, 1); |
7261a835 SZ |
736 | scp->mibdata.dot3StatsFCSErrors++; |
737 | } | |
738 | } | |
984263bc | 739 | |
7261a835 | 740 | /* Handle received packet(s) */ |
984263bc | 741 | while ((esr = XE_INB(XE_ESR)) & XE_ESR_FULL_PACKET_RX) { |
7261a835 SZ |
742 | rsr = XE_INB(XE_RSR); |
743 | IFPRINTF(3, (ifp, "intr: ESR=0x%02x, RSR=0x%02x\n", esr, rsr)); | |
984263bc | 744 | |
7261a835 SZ |
745 | /* Make sure packet is a good one */ |
746 | if (rsr & XE_RSR_RX_OK) { | |
984263bc MD |
747 | struct ether_header *ehp; |
748 | struct mbuf *mbp; | |
749 | u_int16_t len; | |
750 | ||
7261a835 | 751 | len = XE_INW(XE_RBC) - ETHER_CRC_LEN; |
984263bc | 752 | |
7261a835 SZ |
753 | IFPRINTF(3, (ifp, "intr: receive length = %d\n", len)); |
754 | ||
755 | if (len == 0) { | |
d40991ef | 756 | IFNET_STAT_INC(ifp, iqdrops, 1); |
984263bc | 757 | continue; |
7261a835 | 758 | } |
984263bc | 759 | |
984263bc | 760 | /* |
7261a835 SZ |
761 | * Allocate mbuf to hold received packet. If the mbuf header isn't |
762 | * big enough, we attach an mbuf cluster to hold the packet. Note the | |
763 | * +=2 to align the packet data on a 32-bit boundary, and the +3 to | |
764 | * allow for the possibility of reading one more byte than the actual | |
765 | * packet length (we always read 16-bit words). | |
766 | * XXX - Surely there's a better way to do this alignment? | |
984263bc | 767 | */ |
b5523eac | 768 | MGETHDR(mbp, M_NOWAIT, MT_DATA); |
7261a835 | 769 | if (mbp == NULL) { |
d40991ef | 770 | IFNET_STAT_INC(ifp, iqdrops, 1); |
984263bc MD |
771 | continue; |
772 | } | |
984263bc | 773 | |
7261a835 | 774 | if (len + 3 > MHLEN) { |
b5523eac | 775 | MCLGET(mbp, M_NOWAIT); |
7261a835 SZ |
776 | if ((mbp->m_flags & M_EXT) == 0) { |
777 | m_freem(mbp); | |
d40991ef | 778 | IFNET_STAT_INC(ifp, iqdrops, 1); |
7261a835 | 779 | continue; |
984263bc MD |
780 | } |
781 | } | |
782 | ||
7261a835 SZ |
783 | mbp->m_data += 2; |
784 | ehp = mtod(mbp, struct ether_header *); | |
984263bc | 785 | |
7261a835 SZ |
786 | /* |
787 | * Now get the packet in PIO mode, including the Ethernet header but | |
788 | * omitting the trailing CRC. | |
789 | */ | |
790 | ||
791 | /* | |
792 | * Work around a bug in CE2 cards. There seems to be a problem with | |
793 | * duplicated and extraneous bytes in the receive buffer, but without | |
794 | * any real documentation for the CE2 it's hard to tell for sure. | |
795 | * XXX - Needs testing on CE2 hardware | |
796 | */ | |
797 | if (scp->srev == 0) { | |
798 | u_short rhs; | |
799 | ||
800 | XE_SELECT_PAGE(5); | |
801 | rhs = XE_INW(XE_RHSA); | |
802 | XE_SELECT_PAGE(0); | |
803 | ||
804 | rhs += 3; /* Skip control info */ | |
805 | ||
806 | if (rhs >= 0x8000) | |
807 | rhs = 0; | |
808 | ||
809 | if (rhs + len > 0x8000) { | |
810 | int i; | |
811 | ||
812 | for (i = 0; i < len; i++, rhs++) { | |
813 | ((char *)ehp)[i] = XE_INB(XE_EDP); | |
814 | if (rhs == 0x8000) { | |
815 | rhs = 0; | |
816 | i--; | |
984263bc MD |
817 | } |
818 | } | |
984263bc MD |
819 | } |
820 | else | |
821 | bus_space_read_multi_2(scp->bst, scp->bsh, XE_EDP, | |
7261a835 | 822 | (u_int16_t *) ehp, (len + 1) >> 1); |
984263bc | 823 | } |
7261a835 SZ |
824 | else |
825 | bus_space_read_multi_2(scp->bst, scp->bsh, XE_EDP, | |
826 | (u_int16_t *) ehp, (len + 1) >> 1); | |
827 | ||
828 | /* Deliver packet to upper layers */ | |
829 | mbp->m_pkthdr.rcvif = ifp; | |
830 | mbp->m_pkthdr.len = mbp->m_len = len; | |
73029d08 | 831 | ifp->if_input(ifp, mbp, NULL, -1); |
d40991ef | 832 | IFNET_STAT_INC(ifp, ipackets, 1); |
984263bc | 833 | } |
7261a835 SZ |
834 | else if (rsr & XE_RSR_ALIGN_ERROR) { |
835 | /* Packet alignment error -- drop packet */ | |
836 | if_printf(ifp, "alignment error\n"); | |
984263bc | 837 | scp->mibdata.dot3StatsAlignmentErrors++; |
d40991ef | 838 | IFNET_STAT_INC(ifp, ierrors, 1); |
984263bc | 839 | } |
7261a835 SZ |
840 | |
841 | /* Skip to next packet, if there is one */ | |
842 | XE_OUTW(XE_DO, 0x8000); | |
984263bc | 843 | } |
7261a835 SZ |
844 | |
845 | /* Clear receiver overruns now we have some free buffer space */ | |
846 | if (rst0 & XE_RST0_RX_OVERRUN) { | |
847 | IFPRINTF(1, (ifp, "receive overrun\n")); | |
d40991ef | 848 | IFNET_STAT_INC(ifp, ierrors, 1); |
7261a835 | 849 | scp->mibdata.dot3StatsInternalMacReceiveErrors++; |
984263bc MD |
850 | XE_OUTB(XE_CR, XE_CR_CLEAR_OVERRUN); |
851 | } | |
852 | } | |
853 | ||
7261a835 SZ |
854 | /* Restore saved page */ |
855 | XE_SELECT_PAGE(psr); | |
984263bc | 856 | |
7261a835 SZ |
857 | /* Re-enable interrupts */ |
858 | XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); | |
984263bc MD |
859 | } |
860 | ||
861 | ||
862 | /* | |
863 | * Device timeout/watchdog routine. Called automatically if we queue a packet | |
864 | * for transmission but don't get an interrupt within a specified timeout | |
865 | * (usually 5 seconds). When this happens we assume the worst and reset the | |
866 | * card. | |
867 | */ | |
868 | static void | |
869 | xe_watchdog(struct ifnet *ifp) { | |
870 | struct xe_softc *scp = ifp->if_softc; | |
871 | ||
2682bd2f | 872 | if_printf(ifp, "watchdog timeout; resetting card\n"); |
984263bc | 873 | scp->tx_timeouts++; |
d40991ef | 874 | IFNET_STAT_INC(ifp, oerrors, scp->tx_queued); |
984263bc | 875 | xe_stop(scp); |
7261a835 | 876 | xe_reset(scp); |
984263bc MD |
877 | xe_init(scp); |
878 | } | |
879 | ||
880 | ||
881 | /* | |
882 | * Change media selection. | |
883 | */ | |
884 | static int | |
885 | xe_media_change(struct ifnet *ifp) { | |
886 | struct xe_softc *scp = ifp->if_softc; | |
887 | ||
7261a835 | 888 | IFPRINTF(2, (ifp, "media_change\n")); |
984263bc MD |
889 | |
890 | if (IFM_TYPE(scp->ifm->ifm_media) != IFM_ETHER) | |
891 | return(EINVAL); | |
892 | ||
893 | /* | |
894 | * Some card/media combos aren't always possible -- filter those out here. | |
895 | */ | |
896 | if ((IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_AUTO || | |
897 | IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_100_TX) && !scp->phy_ok) | |
898 | return (EINVAL); | |
899 | ||
78195a76 | 900 | xe_setmedia_serialized(scp); |
984263bc MD |
901 | |
902 | return 0; | |
903 | } | |
904 | ||
905 | ||
906 | /* | |
907 | * Return current media selection. | |
908 | */ | |
909 | static void | |
910 | xe_media_status(struct ifnet *ifp, struct ifmediareq *mrp) { | |
911 | ||
7261a835 | 912 | IFPRINTF(3, (ifp, "media_status\n")); |
984263bc | 913 | |
7261a835 SZ |
914 | /* XXX - This is clearly wrong. Will fix once I have CE2 working */ |
915 | mrp->ifm_status = IFM_AVALID | IFM_ACTIVE; | |
984263bc | 916 | mrp->ifm_active = ((struct xe_softc *)ifp->if_softc)->media; |
984263bc MD |
917 | } |
918 | ||
78195a76 MD |
919 | static |
920 | void | |
921 | xe_setmedia(void *xscp) | |
922 | { | |
923 | struct xe_softc *scp = xscp; | |
924 | ||
925 | lwkt_serialize_enter(scp->arpcom.ac_if.if_serializer); | |
926 | xe_setmedia_serialized(xscp); | |
927 | lwkt_serialize_exit(scp->arpcom.ac_if.if_serializer); | |
928 | } | |
984263bc MD |
929 | |
930 | /* | |
931 | * Select active media. | |
932 | */ | |
78195a76 MD |
933 | static |
934 | void | |
935 | xe_setmedia_serialized(void *xscp) | |
936 | { | |
984263bc MD |
937 | struct xe_softc *scp = xscp; |
938 | u_int16_t bmcr, bmsr, anar, lpar; | |
939 | ||
7261a835 | 940 | IFPRINTF(2, (scp->ifp, "setmedia\n")); |
984263bc MD |
941 | |
942 | /* Cancel any pending timeout */ | |
74a0ee08 | 943 | callout_stop(&scp->xe_timer); |
984263bc MD |
944 | xe_disable_intr(scp); |
945 | ||
946 | /* Select media */ | |
947 | scp->media = IFM_ETHER; | |
948 | switch (IFM_SUBTYPE(scp->ifm->ifm_media)) { | |
949 | ||
950 | case IFM_AUTO: /* Autoselect media */ | |
951 | scp->media = IFM_ETHER|IFM_AUTO; | |
952 | ||
953 | /* | |
954 | * Autoselection is really awful. It goes something like this: | |
955 | * | |
956 | * Wait until the transmitter goes idle (2sec timeout). | |
957 | * Reset card | |
958 | * IF a 100Mbit PHY exists | |
959 | * Start NWAY autonegotiation (3.5sec timeout) | |
960 | * IF that succeeds | |
961 | * Select 100baseTX or 10baseT, whichever was detected | |
962 | * ELSE | |
963 | * Reset card | |
964 | * IF a 100Mbit PHY exists | |
965 | * Try to force a 100baseTX link (3sec timeout) | |
966 | * IF that succeeds | |
967 | * Select 100baseTX | |
968 | * ELSE | |
969 | * Disable the PHY | |
970 | * ENDIF | |
971 | * ENDIF | |
972 | * ENDIF | |
973 | * ENDIF | |
974 | * IF nothing selected so far | |
975 | * IF a 100Mbit PHY exists | |
976 | * Select 10baseT | |
977 | * ELSE | |
978 | * Select 10baseT or 10base2, whichever is connected | |
979 | * ENDIF | |
980 | * ENDIF | |
981 | */ | |
982 | switch (scp->autoneg_status) { | |
983 | ||
7261a835 SZ |
984 | case XE_AUTONEG_NONE: |
985 | IFPRINTF(2, (scp->ifp, "Waiting for idle transmitter\n")); | |
9ed293e0 | 986 | ifq_set_oactive(&scp->arpcom.ac_if.if_snd); |
984263bc | 987 | scp->autoneg_status = XE_AUTONEG_WAITING; |
7261a835 | 988 | /* FALL THROUGH */ |
984263bc | 989 | |
7261a835 SZ |
990 | case XE_AUTONEG_WAITING: |
991 | if (scp->tx_queued != 0) { | |
992 | callout_reset(&scp->xe_timer, hz / 2, xe_setmedia, scp); | |
993 | return; | |
994 | } | |
984263bc | 995 | if (scp->phy_ok) { |
7261a835 | 996 | IFPRINTF(2, (scp->ifp, "Starting autonegotiation\n")); |
984263bc MD |
997 | bmcr = xe_phy_readreg(scp, PHY_BMCR); |
998 | bmcr &= ~(PHY_BMCR_AUTONEGENBL); | |
999 | xe_phy_writereg(scp, PHY_BMCR, bmcr); | |
1000 | anar = xe_phy_readreg(scp, PHY_ANAR); | |
1001 | anar &= ~(PHY_ANAR_100BT4|PHY_ANAR_100BTXFULL|PHY_ANAR_10BTFULL); | |
1002 | anar |= PHY_ANAR_100BTXHALF|PHY_ANAR_10BTHALF; | |
1003 | xe_phy_writereg(scp, PHY_ANAR, anar); | |
1004 | bmcr |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; | |
1005 | xe_phy_writereg(scp, PHY_BMCR, bmcr); | |
1006 | scp->autoneg_status = XE_AUTONEG_STARTED; | |
74a0ee08 | 1007 | callout_reset(&scp->xe_timer, hz * 7 / 2, xe_setmedia, scp); |
984263bc MD |
1008 | return; |
1009 | } | |
1010 | else { | |
1011 | scp->autoneg_status = XE_AUTONEG_FAIL; | |
1012 | } | |
1013 | break; | |
1014 | ||
1015 | case XE_AUTONEG_STARTED: | |
1016 | bmsr = xe_phy_readreg(scp, PHY_BMSR); | |
1017 | lpar = xe_phy_readreg(scp, PHY_LPAR); | |
1018 | if (bmsr & (PHY_BMSR_AUTONEGCOMP|PHY_BMSR_LINKSTAT)) { | |
7261a835 | 1019 | IFPRINTF(2, (scp->ifp, "Autonegotiation complete!\n")); |
984263bc MD |
1020 | /* |
1021 | * XXX - Shouldn't have to do this, but (on my hub at least) the | |
1022 | * XXX - transmitter won't work after a successful autoneg. So we see | |
1023 | * XXX - what the negotiation result was and force that mode. I'm | |
1024 | * XXX - sure there is an easy fix for this. | |
1025 | */ | |
1026 | if (lpar & PHY_LPAR_100BTXHALF) { | |
1027 | xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL); | |
1028 | XE_MII_DUMP(scp); | |
1029 | XE_SELECT_PAGE(2); | |
1030 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08); | |
1031 | scp->media = IFM_ETHER|IFM_100_TX; | |
1032 | scp->autoneg_status = XE_AUTONEG_NONE; | |
1033 | } | |
1034 | else { | |
1035 | /* | |
1036 | * XXX - Bit of a hack going on in here. | |
1037 | * XXX - This is derived from Ken Hughes patch to the Linux driver | |
1038 | * XXX - to make it work with 10Mbit _autonegotiated_ links on CE3B | |
1039 | * XXX - cards. What's a CE3B and how's it differ from a plain CE3? | |
1040 | * XXX - these are the things we need to find out. | |
1041 | */ | |
1042 | xe_phy_writereg(scp, PHY_BMCR, 0x0000); | |
1043 | XE_SELECT_PAGE(2); | |
1044 | /* BEGIN HACK */ | |
1045 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08); | |
1046 | XE_SELECT_PAGE(0x42); | |
1047 | XE_OUTB(XE_SWC1, 0x80); | |
1048 | scp->media = IFM_ETHER|IFM_10_T; | |
1049 | scp->autoneg_status = XE_AUTONEG_NONE; | |
1050 | /* END HACK */ | |
1051 | /*XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08);*/ /* Disable PHY? */ | |
1052 | /*scp->autoneg_status = XE_AUTONEG_FAIL;*/ | |
1053 | } | |
1054 | } | |
1055 | else { | |
7261a835 | 1056 | IFPRINTF(2, (scp->ifp, "Autonegotiation failed; trying 100baseTX\n")); |
984263bc | 1057 | XE_MII_DUMP(scp); |
984263bc MD |
1058 | if (scp->phy_ok) { |
1059 | xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL); | |
1060 | scp->autoneg_status = XE_AUTONEG_100TX; | |
74a0ee08 | 1061 | callout_reset(&scp->xe_timer, hz * 3, xe_setmedia, scp); |
984263bc MD |
1062 | return; |
1063 | } | |
1064 | else { | |
1065 | scp->autoneg_status = XE_AUTONEG_FAIL; | |
1066 | } | |
1067 | } | |
1068 | break; | |
1069 | ||
1070 | case XE_AUTONEG_100TX: | |
7b9f668c | 1071 | xe_phy_readreg(scp, PHY_BMSR); |
984263bc MD |
1072 | bmsr = xe_phy_readreg(scp, PHY_BMSR); |
1073 | if (bmsr & PHY_BMSR_LINKSTAT) { | |
7261a835 | 1074 | IFPRINTF(2, (scp->ifp, "Got 100baseTX link!\n")); |
984263bc MD |
1075 | XE_MII_DUMP(scp); |
1076 | XE_SELECT_PAGE(2); | |
1077 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08); | |
1078 | scp->media = IFM_ETHER|IFM_100_TX; | |
1079 | scp->autoneg_status = XE_AUTONEG_NONE; | |
1080 | } | |
1081 | else { | |
7261a835 | 1082 | IFPRINTF(2, (scp->ifp, "Autonegotiation failed; disabling PHY\n")); |
984263bc MD |
1083 | XE_MII_DUMP(scp); |
1084 | xe_phy_writereg(scp, PHY_BMCR, 0x0000); | |
1085 | XE_SELECT_PAGE(2); | |
1086 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08); /* Disable PHY? */ | |
1087 | scp->autoneg_status = XE_AUTONEG_FAIL; | |
1088 | } | |
1089 | break; | |
1090 | } | |
1091 | ||
1092 | /* | |
1093 | * If we got down here _and_ autoneg_status is XE_AUTONEG_FAIL, then | |
1094 | * either autonegotiation failed, or never got started to begin with. In | |
1095 | * either case, select a suitable 10Mbit media and hope it works. We | |
1096 | * don't need to reset the card again, since it will have been done | |
1097 | * already by the big switch above. | |
1098 | */ | |
1099 | if (scp->autoneg_status == XE_AUTONEG_FAIL) { | |
7261a835 | 1100 | IFPRINTF(2, (scp->ifp, "Selecting 10baseX\n")); |
984263bc MD |
1101 | if (scp->mohawk) { |
1102 | XE_SELECT_PAGE(0x42); | |
1103 | XE_OUTB(XE_SWC1, 0x80); | |
1104 | scp->media = IFM_ETHER|IFM_10_T; | |
1105 | scp->autoneg_status = XE_AUTONEG_NONE; | |
1106 | } | |
1107 | else { | |
1108 | XE_SELECT_PAGE(4); | |
1109 | XE_OUTB(XE_GPR0, 4); | |
1110 | DELAY(50000); | |
1111 | XE_SELECT_PAGE(0x42); | |
1112 | XE_OUTB(XE_SWC1, (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? 0x80 : 0xc0); | |
1113 | scp->media = IFM_ETHER|((XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? IFM_10_T : IFM_10_2); | |
1114 | scp->autoneg_status = XE_AUTONEG_NONE; | |
1115 | } | |
1116 | } | |
1117 | break; | |
1118 | ||
1119 | ||
1120 | /* | |
1121 | * If a specific media has been requested, we just reset the card and | |
1122 | * select it (one small exception -- if 100baseTX is requested by there is | |
1123 | * no PHY, we fall back to 10baseT operation). | |
1124 | */ | |
1125 | case IFM_100_TX: /* Force 100baseTX */ | |
984263bc | 1126 | if (scp->phy_ok) { |
7261a835 | 1127 | IFPRINTF(2, (scp->ifp, "Selecting 100baseTX\n")); |
984263bc MD |
1128 | XE_SELECT_PAGE(0x42); |
1129 | XE_OUTB(XE_SWC1, 0); | |
1130 | xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL); | |
1131 | XE_SELECT_PAGE(2); | |
1132 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08); | |
1133 | scp->media |= IFM_100_TX; | |
1134 | break; | |
1135 | } | |
1136 | /* FALLTHROUGH */ | |
1137 | ||
1138 | case IFM_10_T: /* Force 10baseT */ | |
7261a835 | 1139 | IFPRINTF(2, (scp->ifp, "Selecting 10baseT\n")); |
984263bc MD |
1140 | if (scp->phy_ok) { |
1141 | xe_phy_writereg(scp, PHY_BMCR, 0x0000); | |
1142 | XE_SELECT_PAGE(2); | |
1143 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08); /* Disable PHY */ | |
1144 | } | |
1145 | XE_SELECT_PAGE(0x42); | |
1146 | XE_OUTB(XE_SWC1, 0x80); | |
1147 | scp->media |= IFM_10_T; | |
1148 | break; | |
1149 | ||
1150 | case IFM_10_2: | |
7261a835 | 1151 | IFPRINTF(2, (scp->ifp, "Selecting 10base2\n")); |
984263bc MD |
1152 | XE_SELECT_PAGE(0x42); |
1153 | XE_OUTB(XE_SWC1, 0xc0); | |
1154 | scp->media |= IFM_10_2; | |
1155 | break; | |
1156 | } | |
1157 | ||
1158 | ||
1159 | /* | |
1160 | * Finally, the LEDs are set to match whatever media was chosen and the | |
1161 | * transmitter is unblocked. | |
1162 | */ | |
7261a835 | 1163 | IFPRINTF(2, (scp->ifp, "Setting LEDs\n")); |
984263bc MD |
1164 | XE_SELECT_PAGE(2); |
1165 | switch (IFM_SUBTYPE(scp->media)) { | |
1166 | case IFM_100_TX: | |
1167 | case IFM_10_T: | |
1168 | XE_OUTB(XE_LED, 0x3b); | |
1169 | if (scp->dingo) | |
1170 | XE_OUTB(0x0b, 0x04); /* 100Mbit LED */ | |
1171 | break; | |
1172 | ||
1173 | case IFM_10_2: | |
1174 | XE_OUTB(XE_LED, 0x3a); | |
1175 | break; | |
1176 | } | |
1177 | ||
1178 | /* Restart output? */ | |
7261a835 | 1179 | xe_enable_intr(scp); |
9ed293e0 | 1180 | ifq_clr_oactive(&scp->ifp->if_snd); |
9db4b353 | 1181 | if_devstart(scp->ifp); |
984263bc MD |
1182 | } |
1183 | ||
1184 | ||
1185 | /* | |
1186 | * Hard reset (power cycle) the card. | |
1187 | */ | |
1188 | static void | |
7261a835 SZ |
1189 | xe_reset(struct xe_softc *scp) { |
1190 | IFPRINTF(2, (scp->ifp, "hard_reset\n")); | |
984263bc | 1191 | |
9228feed | 1192 | crit_enter(); |
984263bc | 1193 | |
7261a835 | 1194 | /* Power down */ |
984263bc | 1195 | XE_SELECT_PAGE(4); |
7261a835 | 1196 | XE_OUTB(XE_GPR1, 0); |
984263bc MD |
1197 | DELAY(40000); |
1198 | ||
7261a835 | 1199 | /* Power up again */ |
984263bc | 1200 | if (scp->mohawk) |
7261a835 | 1201 | XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN); |
984263bc | 1202 | else |
7261a835 | 1203 | XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN|XE_GPR1_AIC); |
984263bc | 1204 | |
984263bc | 1205 | DELAY(40000); |
984263bc MD |
1206 | XE_SELECT_PAGE(0); |
1207 | ||
9228feed | 1208 | crit_exit(); |
984263bc MD |
1209 | } |
1210 | ||
1211 | ||
1212 | /* | |
1213 | * Take interface offline. This is done by powering down the device, which I | |
1214 | * assume means just shutting down the transceiver and Ethernet logic. This | |
1215 | * requires a _hard_ reset to recover from, as we need to power up again. | |
1216 | */ | |
1217 | static void | |
1218 | xe_stop(struct xe_softc *scp) { | |
7261a835 | 1219 | IFPRINTF(2, (scp->ifp, "stop\n")); |
984263bc | 1220 | |
9228feed | 1221 | crit_enter(); |
984263bc MD |
1222 | |
1223 | /* | |
1224 | * Shut off interrupts. | |
1225 | */ | |
1226 | xe_disable_intr(scp); | |
1227 | ||
1228 | /* | |
1229 | * Power down. | |
1230 | */ | |
1231 | XE_SELECT_PAGE(4); | |
1232 | XE_OUTB(XE_GPR1, 0); | |
1233 | XE_SELECT_PAGE(0); | |
7261a835 SZ |
1234 | if (scp->mohawk) { |
1235 | /* | |
1236 | * set GP1 and GP2 as outputs (bits 2 & 3) | |
1237 | * set GP1 high to power on the ML6692 (bit 0) | |
1238 | * set GP2 low to power on the 10Mhz chip (bit 1) | |
1239 | */ | |
1240 | XE_SELECT_PAGE(4); | |
1241 | XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT|XE_GPR0_GP1_SELECT|XE_GPR0_GP1_OUT); | |
1242 | } | |
984263bc MD |
1243 | |
1244 | /* | |
1245 | * ~IFF_RUNNING == interface down. | |
1246 | */ | |
1247 | scp->ifp->if_flags &= ~IFF_RUNNING; | |
9ed293e0 | 1248 | ifq_clr_oactive(&scp->ifp->if_snd); |
984263bc MD |
1249 | scp->ifp->if_timer = 0; |
1250 | ||
9228feed | 1251 | crit_exit(); |
984263bc MD |
1252 | } |
1253 | ||
1254 | ||
1255 | /* | |
7261a835 | 1256 | * Enable interrupts from the card. |
984263bc MD |
1257 | */ |
1258 | static void | |
1259 | xe_enable_intr(struct xe_softc *scp) { | |
7261a835 | 1260 | IFPRINTF(2, (scp->ifp, "enable_intr\n")); |
984263bc MD |
1261 | |
1262 | XE_SELECT_PAGE(0); | |
1263 | XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Enable interrupts */ | |
1264 | if (scp->modem && !scp->dingo) { /* This bit is just magic */ | |
1265 | if (!(XE_INB(0x10) & 0x01)) { | |
1266 | XE_OUTB(0x10, 0x11); /* Unmask master int enable bit */ | |
1267 | } | |
1268 | } | |
1269 | } | |
1270 | ||
1271 | ||
1272 | /* | |
7261a835 | 1273 | * Disable interrupts from the card. |
984263bc MD |
1274 | */ |
1275 | static void | |
1276 | xe_disable_intr(struct xe_softc *scp) { | |
7261a835 | 1277 | IFPRINTF(2, (scp->ifp, "disable_intr\n")); |
984263bc MD |
1278 | |
1279 | XE_SELECT_PAGE(0); | |
1280 | XE_OUTB(XE_CR, 0); /* Disable interrupts */ | |
7261a835 | 1281 | if (scp->modem && !scp->dingo) { /* More magic */ |
984263bc MD |
1282 | XE_OUTB(0x10, 0x10); /* Mask the master int enable bit */ |
1283 | } | |
984263bc MD |
1284 | } |
1285 | ||
1286 | ||
1287 | /* | |
7261a835 | 1288 | * Set up multicast filter and promiscuous modes. |
984263bc MD |
1289 | */ |
1290 | static void | |
7261a835 | 1291 | xe_set_multicast(struct xe_softc *scp) { |
984263bc | 1292 | struct ifnet *ifp; |
c1126983 | 1293 | struct ifmultiaddr *ifma; |
7261a835 | 1294 | u_int count, i; |
984263bc MD |
1295 | |
1296 | ifp = &scp->arpcom.ac_if; | |
984263bc | 1297 | |
7261a835 SZ |
1298 | IFPRINTF(2, (ifp, "set_multicast\n")); |
1299 | ||
1300 | XE_SELECT_PAGE(0x42); | |
1301 | ||
1302 | /* Handle PROMISC flag */ | |
1303 | if (ifp->if_flags & IFF_PROMISC) { | |
1304 | XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_PROMISCUOUS); | |
1305 | return; | |
1306 | } | |
1307 | else | |
1308 | XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_PROMISCUOUS); | |
1309 | ||
1310 | /* Handle ALLMULTI flag */ | |
1311 | if (ifp->if_flags & IFF_ALLMULTI) { | |
1312 | XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_ALLMULTI); | |
1313 | return; | |
1314 | } | |
1315 | else | |
1316 | XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI); | |
1317 | ||
1318 | /* Iterate over multicast address list */ | |
c1126983 | 1319 | count = 0; |
441d34b2 | 1320 | TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { |
7261a835 SZ |
1321 | if (ifma->ifma_addr->sa_family != AF_LINK) |
1322 | continue; | |
1323 | ||
c1126983 | 1324 | count++; |
984263bc | 1325 | |
7261a835 SZ |
1326 | if (count < 10) |
1327 | /* First 9 use Individual Addresses for exact matching */ | |
1328 | xe_set_addr(scp, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), count); | |
1329 | else | |
1330 | if (scp->mohawk) | |
1331 | /* Use hash filter on Mohawk and Dingo */ | |
1332 | xe_set_hash(scp, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); | |
1333 | else | |
1334 | /* Nowhere else to put them on CE2 */ | |
1335 | break; | |
984263bc | 1336 | } |
7261a835 SZ |
1337 | |
1338 | IFPRINTF(2, (ifp, "set_multicast: count = %u\n", count)); | |
1339 | ||
1340 | /* Now do some cleanup and enable multicast handling as needed */ | |
1341 | if (count == 0) { | |
1342 | /* Disable all multicast handling */ | |
1343 | ||
984263bc | 1344 | XE_SELECT_PAGE(0x42); |
7261a835 SZ |
1345 | XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~(XE_SWC1_IA_ENABLE|XE_SWC1_ALLMULTI)); |
1346 | if (scp->mohawk) { | |
1347 | XE_SELECT_PAGE(0x02); | |
1348 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); | |
1349 | } | |
984263bc | 1350 | } |
7261a835 SZ |
1351 | else if (count < 10) { |
1352 | /* Full in any unused Individual Addresses with our MAC address */ | |
1353 | for (i = count + 1; i < 10; i++) | |
1354 | xe_set_addr(scp, (u_int8_t *)(&scp->arpcom.ac_enaddr), i); | |
1355 | /* Enable Individual Address matching only */ | |
984263bc | 1356 | XE_SELECT_PAGE(0x42); |
7261a835 SZ |
1357 | XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) | XE_SWC1_IA_ENABLE); |
1358 | if (scp->mohawk) { | |
1359 | XE_SELECT_PAGE(0x02); | |
1360 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); | |
1361 | } | |
1362 | } | |
1363 | else { | |
1364 | if (scp->mohawk) { | |
1365 | /* Check whether hash table is full */ | |
1366 | XE_SELECT_PAGE(0x58); | |
1367 | for (i = 0x08; i < 0x10; i++) | |
1368 | if (XE_INB(i) != 0xff) | |
1369 | break; | |
1370 | if (i == 0x10) { | |
1371 | /* Hash table full - enable promiscuous multicast matching */ | |
1372 | XE_SELECT_PAGE(0x42); | |
1373 | XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_IA_ENABLE) | XE_SWC1_ALLMULTI); | |
1374 | XE_SELECT_PAGE(0x02); | |
1375 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); | |
1376 | } | |
1377 | else { | |
1378 | /* Enable hash table and Individual Address matching */ | |
1379 | XE_SELECT_PAGE(0x42); | |
1380 | XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) | XE_SWC1_IA_ENABLE); | |
1381 | XE_SELECT_PAGE(0x02); | |
1382 | XE_OUTB(XE_MSR, XE_INB(XE_MSR) | XE_MSR_HASH_TABLE); | |
1383 | } | |
1384 | } | |
1385 | else { | |
1386 | /* Enable promiscuous multicast matching */ | |
1387 | XE_SELECT_PAGE(0x42); | |
1388 | XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_IA_ENABLE) | XE_SWC1_ALLMULTI); | |
1389 | } | |
984263bc MD |
1390 | } |
1391 | XE_SELECT_PAGE(0); | |
1392 | } | |
1393 | ||
1394 | ||
1395 | /* | |
7261a835 SZ |
1396 | * Copy the Ethernet multicast address in addr to the on-chip registers for |
1397 | * Individual Address idx. Assumes that addr is really a multicast address | |
1398 | * and that idx > 0 (slot 0 is always used for the card MAC address). | |
984263bc MD |
1399 | */ |
1400 | static void | |
7261a835 SZ |
1401 | xe_set_addr(struct xe_softc *scp, u_int8_t* addr, unsigned idx) { |
1402 | uint8_t page, reg; | |
1403 | u_int i; | |
984263bc | 1404 | |
7261a835 SZ |
1405 | /* |
1406 | * Individual Addresses are stored in registers 8-F of pages 0x50-0x57. IA1 | |
1407 | * therefore starts at register 0xE on page 0x50. The expressions below | |
1408 | * compute the starting page and register for any IA index > 0. | |
1409 | */ | |
1410 | --idx; | |
1411 | page = 0x50 + idx%4 + idx/4*3; | |
1412 | reg = 0x0e - 2 * (idx%4); | |
984263bc | 1413 | |
7261a835 SZ |
1414 | IFPRINTF(3, (scp->ifp, "set_addr: idx = %u, page = 0x%02x, reg = 0x%02x\n", |
1415 | idx+1, page, reg)); | |
984263bc | 1416 | |
7261a835 SZ |
1417 | /* |
1418 | * Copy the IA bytes. Note that the byte order is reversed for Mohawk and | |
1419 | * Dingo wrt. CE2 hardware. | |
1420 | */ | |
1421 | XE_SELECT_PAGE(page); | |
1422 | for (i = 0; i < 6; i++) { | |
1423 | #ifdef XE_DEBUG | |
1424 | if (i > 0) { | |
1425 | DPRINTF(3, (":%02x", addr[i])); | |
1426 | } else { | |
1427 | IFPRINTF(3, (scp->ifp, "set_addr: %02x", addr[0])); | |
984263bc | 1428 | } |
984263bc | 1429 | #endif |
7261a835 SZ |
1430 | XE_OUTB(reg, addr[scp->mohawk ? 5 - i : i]); |
1431 | if (++reg == 0x10) { | |
1432 | reg = 0x08; | |
1433 | XE_SELECT_PAGE(++page); | |
1434 | } | |
1435 | } | |
1436 | DPRINTF(3, ("\n")); | |
1437 | } | |
984263bc | 1438 | |
984263bc | 1439 | |
7261a835 SZ |
1440 | /* |
1441 | * Set the appropriate bit in the multicast hash table for the supplied | |
1442 | * Ethernet multicast address addr. Assumes that addr is really a multicast | |
1443 | * address. | |
1444 | */ | |
1445 | static void | |
1446 | xe_set_hash(struct xe_softc* scp, u_int8_t* addr) { | |
1447 | u_int32_t crc = 0xffffffff; | |
1448 | u_int8_t bit, byte, crc31, idx; | |
1449 | u_int i, j; | |
1450 | ||
1451 | /* Compute CRC of the address -- standard Ethernet CRC function */ | |
1452 | for (i = 0; i < 6; i++) { | |
1453 | byte = addr[i]; | |
1454 | for (j = 1; j <= 8; j++) { | |
1455 | if (crc & 0x80000000) | |
1456 | crc31 = 0x01; | |
984263bc | 1457 | else |
7261a835 SZ |
1458 | crc31 = 0; |
1459 | bit = crc31 ^ (byte & 0x01); | |
1460 | crc <<= 1; | |
1461 | byte >>= 1; | |
1462 | if (bit) | |
1463 | crc = (crc ^ XE_CRC_POLY)|1; | |
984263bc | 1464 | } |
984263bc MD |
1465 | } |
1466 | ||
7261a835 SZ |
1467 | IFPRINTF(3, (scp->ifp, "set_hash: CRC = 0x%08x\n", crc)); |
1468 | ||
1469 | /* Hash table index = 6 msbs of CRC, reversed */ | |
1470 | for (i = 0, idx = 0; i < 6; i++) { | |
1471 | idx >>= 1; | |
1472 | if (crc & 0x80000000) { | |
1473 | idx |= 0x20; | |
1474 | } | |
1475 | crc <<= 1; | |
1476 | } | |
1477 | ||
1478 | /* Top 3 bits of idx give register - 8, bottom 3 give bit within register */ | |
1479 | byte = idx >> 3 | 0x08; | |
1480 | bit = 0x01 << (idx & 0x07); | |
1481 | ||
1482 | IFPRINTF(3, (scp->ifp, | |
1483 | "set_hash: idx = 0x%02x, byte = 0x%02x, bit = 0x%02x\n", | |
1484 | idx, byte, bit)); | |
1485 | ||
1486 | XE_SELECT_PAGE(0x58); | |
1487 | XE_OUTB(byte, XE_INB(byte) | bit); | |
984263bc MD |
1488 | } |
1489 | ||
1490 | ||
1491 | /* | |
1492 | * Write an outgoing packet to the card using programmed I/O. | |
1493 | */ | |
1494 | static int | |
1495 | xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) { | |
7261a835 SZ |
1496 | u_int len, pad; |
1497 | u_char wantbyte; | |
984263bc | 1498 | u_int8_t *data; |
7261a835 | 1499 | u_int8_t savebyte[2]; |
984263bc MD |
1500 | |
1501 | /* Get total packet length */ | |
7261a835 SZ |
1502 | if (mbp->m_flags & M_PKTHDR) |
1503 | len = mbp->m_pkthdr.len; | |
1504 | else { | |
1505 | struct mbuf* mbp2 = mbp; | |
1506 | for (len = 0; mbp2 != NULL; len += mbp2->m_len, mbp2 = mbp2->m_next); | |
1507 | } | |
1508 | ||
1509 | IFPRINTF(3, (scp->ifp, "pio_write_packet: len = %u\n", len)); | |
984263bc MD |
1510 | |
1511 | /* Packets < minimum length may need to be padded out */ | |
1512 | pad = 0; | |
7261a835 SZ |
1513 | if (len < scp->tx_min) { |
1514 | pad = scp->tx_min - len; | |
1515 | len = scp->tx_min; | |
984263bc MD |
1516 | } |
1517 | ||
1518 | /* Check transmit buffer space */ | |
1519 | XE_SELECT_PAGE(0); | |
7261a835 SZ |
1520 | XE_OUTW(XE_TRS, len+2); /* Only effective on rev. 1 CE2 cards */ |
1521 | if ((XE_INW(XE_TSO) & 0x7fff) <= len + 2) | |
984263bc MD |
1522 | return 1; |
1523 | ||
1524 | /* Send packet length to card */ | |
1525 | XE_OUTW(XE_EDP, len); | |
1526 | ||
1527 | /* | |
1528 | * Write packet to card using PIO (code stolen from the ed driver) | |
1529 | */ | |
1530 | wantbyte = 0; | |
1531 | while (mbp != NULL) { | |
1532 | len = mbp->m_len; | |
1533 | if (len > 0) { | |
1534 | data = mtod(mbp, caddr_t); | |
1535 | if (wantbyte) { /* Finish the last word */ | |
1536 | savebyte[1] = *data; | |
1537 | XE_OUTW(XE_EDP, *(u_short *)savebyte); | |
1538 | data++; | |
1539 | len--; | |
1540 | wantbyte = 0; | |
1541 | } | |
1542 | if (len > 1) { /* Output contiguous words */ | |
1543 | bus_space_write_multi_2(scp->bst, scp->bsh, XE_EDP, (u_int16_t *) data, | |
1544 | len >> 1); | |
1545 | data += len & ~1; | |
1546 | len &= 1; | |
1547 | } | |
1548 | if (len == 1) { /* Save last byte, if necessary */ | |
1549 | savebyte[0] = *data; | |
1550 | wantbyte = 1; | |
1551 | } | |
1552 | } | |
1553 | mbp = mbp->m_next; | |
1554 | } | |
984263bc MD |
1555 | |
1556 | /* | |
7261a835 SZ |
1557 | * Send last byte of odd-length packets |
1558 | */ | |
1559 | if (wantbyte) | |
1560 | XE_OUTB(XE_EDP, savebyte[0]); | |
1561 | ||
1562 | /* | |
1563 | * Can just tell CE3 cards to send; short packets will be padded out with | |
1564 | * random cruft automatically. For CE2, manually pad the packet with | |
1565 | * garbage; it will be sent when the required number or bytes have been | |
1566 | * delivered to the card. | |
984263bc MD |
1567 | */ |
1568 | if (scp->mohawk) | |
7261a835 SZ |
1569 | XE_OUTB(XE_CR, XE_CR_TX_PACKET | XE_CR_RESTART_TX | XE_CR_ENABLE_INTR); |
1570 | else if (pad > 0) { | |
1571 | if (pad & 0x01) | |
1572 | XE_OUTB(XE_EDP, 0xaa); | |
1573 | pad >>= 1; | |
984263bc MD |
1574 | while (pad > 0) { |
1575 | XE_OUTW(XE_EDP, 0xdead); | |
1576 | pad--; | |
1577 | } | |
984263bc | 1578 | } |
984263bc | 1579 | |
7261a835 | 1580 | return 0; |
984263bc MD |
1581 | } |
1582 | ||
984263bc MD |
1583 | |
1584 | /************************************************************** | |
1585 | * * | |
1586 | * M I I F U N C T I O N S * | |
1587 | * * | |
1588 | **************************************************************/ | |
1589 | ||
1590 | /* | |
1591 | * Alternative MII/PHY handling code adapted from the xl driver. It doesn't | |
1592 | * seem to work any better than the xirc2_ps stuff, but it's cleaner code. | |
1593 | * XXX - this stuff shouldn't be here. It should all be abstracted off to | |
1594 | * XXX - some kind of common MII-handling code, shared by all drivers. But | |
1595 | * XXX - that's a whole other mission. | |
1596 | */ | |
1597 | #define XE_MII_SET(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) | (x)) | |
1598 | #define XE_MII_CLR(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) & ~(x)) | |
1599 | ||
1600 | ||
1601 | /* | |
1602 | * Sync the PHYs by setting data bit and strobing the clock 32 times. | |
1603 | */ | |
1604 | static void | |
1605 | xe_mii_sync(struct xe_softc *scp) { | |
f96d6c88 | 1606 | int i; |
984263bc MD |
1607 | |
1608 | XE_SELECT_PAGE(2); | |
1609 | XE_MII_SET(XE_MII_DIR|XE_MII_WRD); | |
1610 | ||
1611 | for (i = 0; i < 32; i++) { | |
1612 | XE_MII_SET(XE_MII_CLK); | |
1613 | DELAY(1); | |
1614 | XE_MII_CLR(XE_MII_CLK); | |
1615 | DELAY(1); | |
1616 | } | |
1617 | } | |
1618 | ||
1619 | ||
1620 | /* | |
1621 | * Look for a MII-compliant PHY. If we find one, reset it. | |
1622 | */ | |
1623 | static int | |
1624 | xe_mii_init(struct xe_softc *scp) { | |
1625 | u_int16_t status; | |
1626 | ||
1627 | status = xe_phy_readreg(scp, PHY_BMSR); | |
1628 | if ((status & 0xff00) != 0x7800) { | |
7261a835 | 1629 | IFPRINTF(2, (scp->ifp, "no PHY found, %0x\n", status)); |
984263bc MD |
1630 | return 0; |
1631 | } | |
1632 | else { | |
7261a835 | 1633 | IFPRINTF(2, (scp->ifp, "PHY OK!\n")); |
984263bc MD |
1634 | |
1635 | /* Reset the PHY */ | |
1636 | xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_RESET); | |
1637 | DELAY(500); | |
1638 | while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET); | |
1639 | XE_MII_DUMP(scp); | |
1640 | return 1; | |
1641 | } | |
1642 | } | |
1643 | ||
1644 | ||
1645 | /* | |
1646 | * Clock a series of bits through the MII. | |
1647 | */ | |
1648 | static void | |
1649 | xe_mii_send(struct xe_softc *scp, u_int32_t bits, int cnt) { | |
1650 | int i; | |
1651 | ||
1652 | XE_SELECT_PAGE(2); | |
1653 | XE_MII_CLR(XE_MII_CLK); | |
1654 | ||
1655 | for (i = (0x1 << (cnt - 1)); i; i >>= 1) { | |
1656 | if (bits & i) { | |
1657 | XE_MII_SET(XE_MII_WRD); | |
1658 | } else { | |
1659 | XE_MII_CLR(XE_MII_WRD); | |
1660 | } | |
1661 | DELAY(1); | |
1662 | XE_MII_CLR(XE_MII_CLK); | |
1663 | DELAY(1); | |
1664 | XE_MII_SET(XE_MII_CLK); | |
1665 | } | |
1666 | } | |
1667 | ||
1668 | ||
1669 | /* | |
1670 | * Read an PHY register through the MII. | |
1671 | */ | |
1672 | static int | |
1673 | xe_mii_readreg(struct xe_softc *scp, struct xe_mii_frame *frame) { | |
9228feed | 1674 | int i, ack; |
984263bc | 1675 | |
9228feed | 1676 | crit_enter(); |
984263bc MD |
1677 | |
1678 | /* | |
1679 | * Set up frame for RX. | |
1680 | */ | |
1681 | frame->mii_stdelim = XE_MII_STARTDELIM; | |
1682 | frame->mii_opcode = XE_MII_READOP; | |
1683 | frame->mii_turnaround = 0; | |
1684 | frame->mii_data = 0; | |
1685 | ||
1686 | XE_SELECT_PAGE(2); | |
1687 | XE_OUTB(XE_GPR2, 0); | |
1688 | ||
1689 | /* | |
1690 | * Turn on data xmit. | |
1691 | */ | |
1692 | XE_MII_SET(XE_MII_DIR); | |
1693 | ||
1694 | xe_mii_sync(scp); | |
1695 | ||
1696 | /* | |
1697 | * Send command/address info. | |
1698 | */ | |
1699 | xe_mii_send(scp, frame->mii_stdelim, 2); | |
1700 | xe_mii_send(scp, frame->mii_opcode, 2); | |
1701 | xe_mii_send(scp, frame->mii_phyaddr, 5); | |
1702 | xe_mii_send(scp, frame->mii_regaddr, 5); | |
1703 | ||
1704 | /* Idle bit */ | |
1705 | XE_MII_CLR((XE_MII_CLK|XE_MII_WRD)); | |
1706 | DELAY(1); | |
1707 | XE_MII_SET(XE_MII_CLK); | |
1708 | DELAY(1); | |
1709 | ||
1710 | /* Turn off xmit. */ | |
1711 | XE_MII_CLR(XE_MII_DIR); | |
1712 | ||
1713 | /* Check for ack */ | |
1714 | XE_MII_CLR(XE_MII_CLK); | |
1715 | DELAY(1); | |
1716 | ack = XE_INB(XE_GPR2) & XE_MII_RDD; | |
1717 | XE_MII_SET(XE_MII_CLK); | |
1718 | DELAY(1); | |
1719 | ||
1720 | /* | |
1721 | * Now try reading data bits. If the ack failed, we still | |
1722 | * need to clock through 16 cycles to keep the PHY(s) in sync. | |
1723 | */ | |
1724 | if (ack) { | |
1725 | for(i = 0; i < 16; i++) { | |
1726 | XE_MII_CLR(XE_MII_CLK); | |
1727 | DELAY(1); | |
1728 | XE_MII_SET(XE_MII_CLK); | |
1729 | DELAY(1); | |
1730 | } | |
1731 | goto fail; | |
1732 | } | |
1733 | ||
1734 | for (i = 0x8000; i; i >>= 1) { | |
1735 | XE_MII_CLR(XE_MII_CLK); | |
1736 | DELAY(1); | |
1737 | if (!ack) { | |
1738 | if (XE_INB(XE_GPR2) & XE_MII_RDD) | |
1739 | frame->mii_data |= i; | |
1740 | DELAY(1); | |
1741 | } | |
1742 | XE_MII_SET(XE_MII_CLK); | |
1743 | DELAY(1); | |
1744 | } | |
1745 | ||
1746 | fail: | |
1747 | ||
1748 | XE_MII_CLR(XE_MII_CLK); | |
1749 | DELAY(1); | |
1750 | XE_MII_SET(XE_MII_CLK); | |
1751 | DELAY(1); | |
1752 | ||
9228feed | 1753 | crit_exit(); |
984263bc MD |
1754 | |
1755 | if (ack) | |
1756 | return(1); | |
1757 | return(0); | |
1758 | } | |
1759 | ||
1760 | ||
1761 | /* | |
1762 | * Write to a PHY register through the MII. | |
1763 | */ | |
1764 | static int | |
1765 | xe_mii_writereg(struct xe_softc *scp, struct xe_mii_frame *frame) { | |
984263bc | 1766 | |
9228feed | 1767 | crit_enter(); |
984263bc MD |
1768 | |
1769 | /* | |
1770 | * Set up frame for TX. | |
1771 | */ | |
1772 | frame->mii_stdelim = XE_MII_STARTDELIM; | |
1773 | frame->mii_opcode = XE_MII_WRITEOP; | |
1774 | frame->mii_turnaround = XE_MII_TURNAROUND; | |
1775 | ||
1776 | XE_SELECT_PAGE(2); | |
1777 | ||
1778 | /* | |
1779 | * Turn on data output. | |
1780 | */ | |
1781 | XE_MII_SET(XE_MII_DIR); | |
1782 | ||
1783 | xe_mii_sync(scp); | |
1784 | ||
1785 | xe_mii_send(scp, frame->mii_stdelim, 2); | |
1786 | xe_mii_send(scp, frame->mii_opcode, 2); | |
1787 | xe_mii_send(scp, frame->mii_phyaddr, 5); | |
1788 | xe_mii_send(scp, frame->mii_regaddr, 5); | |
1789 | xe_mii_send(scp, frame->mii_turnaround, 2); | |
1790 | xe_mii_send(scp, frame->mii_data, 16); | |
1791 | ||
1792 | /* Idle bit. */ | |
1793 | XE_MII_SET(XE_MII_CLK); | |
1794 | DELAY(1); | |
1795 | XE_MII_CLR(XE_MII_CLK); | |
1796 | DELAY(1); | |
1797 | ||
1798 | /* | |
1799 | * Turn off xmit. | |
1800 | */ | |
1801 | XE_MII_CLR(XE_MII_DIR); | |
1802 | ||
9228feed | 1803 | crit_exit(); |
984263bc MD |
1804 | |
1805 | return(0); | |
1806 | } | |
1807 | ||
1808 | ||
1809 | /* | |
1810 | * Read a register from the PHY. | |
1811 | */ | |
1812 | static u_int16_t | |
1813 | xe_phy_readreg(struct xe_softc *scp, u_int16_t reg) { | |
1814 | struct xe_mii_frame frame; | |
1815 | ||
1816 | bzero((char *)&frame, sizeof(frame)); | |
1817 | ||
1818 | frame.mii_phyaddr = 0; | |
1819 | frame.mii_regaddr = reg; | |
1820 | xe_mii_readreg(scp, &frame); | |
1821 | ||
1822 | return(frame.mii_data); | |
1823 | } | |
1824 | ||
1825 | ||
1826 | /* | |
1827 | * Write to a PHY register. | |
1828 | */ | |
1829 | static void | |
1830 | xe_phy_writereg(struct xe_softc *scp, u_int16_t reg, u_int16_t data) { | |
1831 | struct xe_mii_frame frame; | |
1832 | ||
1833 | bzero((char *)&frame, sizeof(frame)); | |
1834 | ||
1835 | frame.mii_phyaddr = 0; | |
1836 | frame.mii_regaddr = reg; | |
1837 | frame.mii_data = data; | |
1838 | xe_mii_writereg(scp, &frame); | |
1839 | ||
1840 | return; | |
1841 | } | |
1842 | ||
1843 | ||
984263bc MD |
1844 | /* |
1845 | * A bit of debugging code. | |
1846 | */ | |
1847 | static void | |
1848 | xe_mii_dump(struct xe_softc *scp) { | |
9228feed | 1849 | int i; |
984263bc | 1850 | |
9228feed | 1851 | crit_enter(); |
984263bc | 1852 | |
2682bd2f | 1853 | if_printf(scp->ifp, "MII registers: "); |
984263bc | 1854 | for (i = 0; i < 2; i++) { |
e3869ec7 | 1855 | kprintf(" %d:%04x", i, xe_phy_readreg(scp, i)); |
984263bc MD |
1856 | } |
1857 | for (i = 4; i < 7; i++) { | |
e3869ec7 | 1858 | kprintf(" %d:%04x", i, xe_phy_readreg(scp, i)); |
984263bc | 1859 | } |
e3869ec7 | 1860 | kprintf("\n"); |
984263bc | 1861 | |
9228feed | 1862 | crit_exit(); |
984263bc MD |
1863 | } |
1864 | ||
1865 | static void | |
1866 | xe_reg_dump(struct xe_softc *scp) { | |
9228feed | 1867 | int page, i; |
984263bc | 1868 | |
9228feed | 1869 | crit_enter(); |
984263bc | 1870 | |
2682bd2f | 1871 | if_printf(scp->ifp, "Common registers: "); |
984263bc | 1872 | for (i = 0; i < 8; i++) { |
e3869ec7 | 1873 | kprintf(" %2.2x", XE_INB(i)); |
984263bc | 1874 | } |
e3869ec7 | 1875 | kprintf("\n"); |
984263bc MD |
1876 | |
1877 | for (page = 0; page <= 8; page++) { | |
2682bd2f | 1878 | if_printf(scp->ifp, "Register page %2.2x: ", page); |
984263bc MD |
1879 | XE_SELECT_PAGE(page); |
1880 | for (i = 8; i < 16; i++) { | |
e3869ec7 | 1881 | kprintf(" %2.2x", XE_INB(i)); |
984263bc | 1882 | } |
e3869ec7 | 1883 | kprintf("\n"); |
984263bc MD |
1884 | } |
1885 | ||
1886 | for (page = 0x10; page < 0x5f; page++) { | |
1887 | if ((page >= 0x11 && page <= 0x3f) || | |
1888 | (page == 0x41) || | |
1889 | (page >= 0x43 && page <= 0x4f) || | |
1890 | (page >= 0x59)) | |
1891 | continue; | |
2682bd2f | 1892 | if_printf(scp->ifp, "Register page %2.2x: ", page); |
984263bc MD |
1893 | XE_SELECT_PAGE(page); |
1894 | for (i = 8; i < 16; i++) { | |
e3869ec7 | 1895 | kprintf(" %2.2x", XE_INB(i)); |
984263bc | 1896 | } |
e3869ec7 | 1897 | kprintf("\n"); |
984263bc MD |
1898 | } |
1899 | ||
9228feed | 1900 | crit_exit(); |
984263bc | 1901 | } |
984263bc MD |
1902 | |
1903 | int | |
1904 | xe_activate(device_t dev) | |
1905 | { | |
1906 | struct xe_softc *sc = device_get_softc(dev); | |
7261a835 SZ |
1907 | int start, i; |
1908 | ||
1909 | DEVPRINTF(2, (dev, "activate\n")); | |
984263bc | 1910 | |
7261a835 | 1911 | if (!sc->modem) { |
984263bc MD |
1912 | sc->port_rid = 0; /* 0 is managed by pccard */ |
1913 | sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, | |
1914 | &sc->port_rid, 0, ~0, 16, RF_ACTIVE); | |
7261a835 | 1915 | } else if (sc->dingo) { |
984263bc MD |
1916 | /* |
1917 | * Find a 16 byte aligned ioport for the card. | |
1918 | */ | |
7261a835 | 1919 | DEVPRINTF(1, (dev, "Finding an aligned port for RealPort\n")); |
984263bc MD |
1920 | sc->port_rid = 1; /* 0 is managed by pccard */ |
1921 | start = 0x100; | |
1922 | do { | |
1923 | sc->port_res = bus_alloc_resource(dev, | |
1924 | SYS_RES_IOPORT, &sc->port_rid, start, 0x3ff, 16, | |
1925 | RF_ACTIVE); | |
1926 | if (sc->port_res == 0) | |
1927 | break; /* we failed */ | |
1928 | if ((rman_get_start(sc->port_res) & 0xf) == 0) | |
1929 | break; /* good */ | |
1930 | bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, | |
1931 | sc->port_res); | |
1932 | start = (rman_get_start(sc->port_res) + 15) & ~0xf; | |
1933 | } while (1); | |
7261a835 SZ |
1934 | DEVPRINTF(1, (dev, "RealPort port 0x%0lx, size 0x%0lx\n", |
1935 | bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), | |
1936 | bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid))); | |
1937 | } | |
1938 | else if (sc->ce2) { | |
1939 | /* | |
1940 | * Find contiguous I/O port for the Ethernet function on CEM2 and | |
1941 | * CEM3 cards. We allocate window 0 wherever pccard has decided | |
1942 | * it should be, then find an available window adjacent to it for | |
1943 | * the second function. Not sure that both windows are actually | |
1944 | * needed. | |
1945 | */ | |
1946 | DEVPRINTF(1, (dev, "Finding I/O port for CEM2/CEM3\n")); | |
1947 | sc->ce2_port_rid = 0; /* 0 is managed by pccard */ | |
1948 | sc->ce2_port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, | |
1949 | &sc->ce2_port_rid, 0, ~0, | |
1950 | 8, RF_ACTIVE); | |
1951 | if (!sc->ce2_port_res) { | |
1952 | device_printf(dev, "Cannot allocate I/O port for modem\n"); | |
1953 | return ENOMEM; | |
1954 | } | |
1955 | ||
1956 | sc->port_rid = 1; | |
1957 | start = bus_get_resource_start(dev, SYS_RES_IOPORT, | |
1958 | sc->ce2_port_rid); | |
1959 | for (i = 0; i < 2; i++) { | |
1960 | start += (i == 0 ? 8 : -24); | |
1961 | sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, | |
1962 | &sc->port_rid, start, | |
1963 | start + 18, 18, RF_ACTIVE); | |
1964 | if (sc->port_res == 0) | |
1965 | continue; /* Failed, try again if possible */ | |
1966 | if (bus_get_resource_start(dev, SYS_RES_IOPORT, | |
1967 | sc->port_rid) == start) | |
1968 | break; /* Success! */ | |
1969 | ||
1970 | bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, | |
1971 | sc->port_res); | |
1972 | sc->port_res = 0; | |
1973 | } | |
1974 | DEVPRINTF(1, (dev, "CEM2/CEM3 port 0x%0lx, size 0x%0lx\n", | |
984263bc | 1975 | bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), |
7261a835 | 1976 | bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid))); |
984263bc MD |
1977 | } |
1978 | if (!sc->port_res) { | |
984263bc | 1979 | device_printf(dev, "Cannot allocate ioport\n"); |
984263bc MD |
1980 | return ENOMEM; |
1981 | } | |
1982 | ||
1983 | sc->irq_rid = 0; | |
4e6d744d JS |
1984 | sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, |
1985 | RF_ACTIVE); | |
984263bc | 1986 | if (!sc->irq_res) { |
984263bc | 1987 | device_printf(dev, "Cannot allocate irq\n"); |
984263bc MD |
1988 | xe_deactivate(dev); |
1989 | return ENOMEM; | |
1990 | } | |
984263bc MD |
1991 | |
1992 | sc->bst = rman_get_bustag(sc->port_res); | |
1993 | sc->bsh = rman_get_bushandle(sc->port_res); | |
1994 | return (0); | |
1995 | } | |
1996 | ||
1997 | void | |
1998 | xe_deactivate(device_t dev) | |
1999 | { | |
2000 | struct xe_softc *sc = device_get_softc(dev); | |
7cdc3d6e | 2001 | |
7261a835 SZ |
2002 | DEVPRINTF(2, (dev, "deactivate\n")); |
2003 | xe_disable_intr(sc); | |
2004 | ||
984263bc MD |
2005 | if (sc->port_res) |
2006 | bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, | |
2007 | sc->port_res); | |
2008 | sc->port_res = 0; | |
7261a835 SZ |
2009 | if (sc->ce2_port_res) |
2010 | bus_release_resource(dev, SYS_RES_IOPORT, sc->ce2_port_rid, | |
2011 | sc->ce2_port_res); | |
2012 | sc->ce2_port_res = 0; | |
984263bc MD |
2013 | if (sc->irq_res) |
2014 | bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, | |
2015 | sc->irq_res); | |
2016 | sc->irq_res = 0; | |
984263bc | 2017 | } |