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