diff options
author | Scott Mitchell <rsm@FreeBSD.org> | 2003-10-14 22:51:35 +0000 |
---|---|---|
committer | Scott Mitchell <rsm@FreeBSD.org> | 2003-10-14 22:51:35 +0000 |
commit | 846b8315a2ac2a07937ae665a535966f29a3e77b (patch) | |
tree | 707c3730f794082cc5ce8a3220088544d6421bd8 /sys/dev/xe/if_xe.c | |
parent | 5b28d0d2d96840ccf7d15526254cb054eceb3d1d (diff) | |
download | src-846b8315a2ac2a07937ae665a535966f29a3e77b.tar.gz src-846b8315a2ac2a07937ae665a535966f29a3e77b.zip |
Notes
Diffstat (limited to 'sys/dev/xe/if_xe.c')
-rw-r--r-- | sys/dev/xe/if_xe.c | 1247 |
1 files changed, 697 insertions, 550 deletions
diff --git a/sys/dev/xe/if_xe.c b/sys/dev/xe/if_xe.c index d802e8dfb5a7..3a6175a35b18 100644 --- a/sys/dev/xe/if_xe.c +++ b/sys/dev/xe/if_xe.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 1998, 1999 Scott Mitchell + * Copyright (c) 1998, 1999, 2003 Scott Mitchell * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,15 +30,6 @@ __FBSDID("$FreeBSD$"); /* - * XXX TODO XXX - * - * I've pushed this fairly far, but there are some things that need to be - * done here. I'm documenting them here in case I get destracted. -- imp - * - * xe_cem56fix -- need to figure out how to map the extra stuff. - */ - -/* * Portions of this software were derived from Werner Koch's xirc2ps driver * for Linux under the terms of the following license (from v1.30 of the * xirc2ps driver): @@ -99,14 +90,8 @@ __FBSDID("$FreeBSD$"); * the CEM56/REM56 support code; and the FreeBSD UK Users' Group for hosting * the web pages. * - * Contact points: - * - * Driver web page: http://ukug.uk.freebsd.org/~scott/xe_drv/ - * - * Mailing list: http://www.lovett.com/lists/freebsd-xircom/ - * or send "subscribe freebsd-xircom" to <majordomo@lovett.com> - * * Author email: <scott@uk.freebsd.org> + * Driver web page: http://ukug.uk.freebsd.org/~scott/xe_drv/ */ @@ -159,6 +144,10 @@ struct xe_mii_frame { #define XE_AUTONEG_100TX 3 /* Trying to force 100baseTX link */ #define XE_AUTONEG_FAIL 4 /* Autonegotiation failed */ +/* + * Multicast hashing CRC constants + */ +#define XE_CRC_POLY 0x04c11db6 /* * Prototypes start here @@ -170,16 +159,14 @@ static void xe_watchdog (struct ifnet *ifp); static int xe_media_change (struct ifnet *ifp); static void xe_media_status (struct ifnet *ifp, struct ifmediareq *mrp); static timeout_t xe_setmedia; -static void xe_hard_reset (struct xe_softc *scp); -static void xe_soft_reset (struct xe_softc *scp); +static void xe_reset (struct xe_softc *scp); static void xe_stop (struct xe_softc *scp); static void xe_enable_intr (struct xe_softc *scp); static void xe_disable_intr (struct xe_softc *scp); -static void xe_setmulti (struct xe_softc *scp); -static void xe_setaddrs (struct xe_softc *scp); +static void xe_set_multicast (struct xe_softc *scp); +static void xe_set_addr (struct xe_softc *scp, u_int8_t* addr, unsigned idx); +static void xe_set_hash (struct xe_softc *scp, u_int8_t* addr); static int xe_pio_write_packet (struct xe_softc *scp, struct mbuf *mbp); -static u_int32_t xe_compute_crc (u_int8_t *data, int len) __unused; -static int xe_compute_hashbit (u_int32_t crc) __unused; /* * MII functions @@ -192,12 +179,16 @@ static int xe_mii_writereg (struct xe_softc *scp, struct xe_mii_frame *fra static u_int16_t xe_phy_readreg (struct xe_softc *scp, u_int16_t reg); static void xe_phy_writereg (struct xe_softc *scp, u_int16_t reg, u_int16_t data); + /* - * Debug functions -- uncomment for VERY verbose dignostic information. - * Set to 1 for less verbose information + * Debug logging level (comment out for normal operation): + * 1 = Log more hardware details, xe_ioctl details + * 2 = Log most function calls, some (eg. multicast, MII) in detail + * 3 = Log everything, including all interrupts and packets sent */ -/* #define XE_DEBUG 2 */ -#ifdef XE_DEBUG +#define XE_DEBUG 0 + +#if XE_DEBUG > 2 #define XE_REG_DUMP(scp) xe_reg_dump((scp)) #define XE_MII_DUMP(scp) xe_mii_dump((scp)) static void xe_reg_dump (struct xe_softc *scp); @@ -207,6 +198,7 @@ static void xe_mii_dump (struct xe_softc *scp); #define XE_MII_DUMP(scp) #endif + /* * Attach a device. */ @@ -215,20 +207,16 @@ xe_attach (device_t dev) { struct xe_softc *scp = device_get_softc(dev); -#ifdef XE_DEBUG +#if XE_DEBUG > 1 device_printf(dev, "attach\n"); #endif - /* Fill in some private data */ + /* Initialise stuff... */ + scp->dev = dev; scp->ifp = &scp->arpcom.ac_if; scp->ifm = &scp->ifmedia; - scp->autoneg_status = 0; + scp->autoneg_status = XE_AUTONEG_NONE; - /* Hopefully safe to read this here */ - XE_SELECT_PAGE(4); - scp->version = XE_INB(XE_BOV); - - scp->dev = dev; /* Initialise the ifnet structure */ if (!scp->ifp->if_name) { scp->ifp->if_softc = scp; @@ -243,6 +231,7 @@ xe_attach (device_t dev) scp->ifp->if_ioctl = xe_ioctl; scp->ifp->if_watchdog = xe_watchdog; scp->ifp->if_init = xe_init; + scp->ifp->if_baudrate = 100000000; scp->ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; } @@ -250,44 +239,52 @@ xe_attach (device_t dev) ifmedia_init(scp->ifm, 0, xe_media_change, xe_media_status); callout_handle_init(&scp->chand); - /* - * Fill in supported media types. Some cards _do_ support full duplex - * operation, but this driver doesn't, yet. Therefore we leave those modes - * out of the list. We support some form of autoselection in all cases. - */ + /* Add supported media types */ if (scp->mohawk) { ifmedia_add(scp->ifm, IFM_ETHER|IFM_100_TX, 0, NULL); - ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); + ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); } - else { - ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL); + ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL); + if (scp->ce2) ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_2, 0, NULL); - } ifmedia_add(scp->ifm, IFM_ETHER|IFM_AUTO, 0, NULL); /* Default is to autoselect best supported media type */ ifmedia_set(scp->ifm, IFM_ETHER|IFM_AUTO); + /* Get the hardware into a known state */ + xe_reset(scp); + + /* Get hardware version numbers */ + XE_SELECT_PAGE(4); + scp->version = XE_INB(XE_BOV); + if (scp->mohawk) + scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4; + else + scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4; + /* Print some useful information */ - device_printf(dev, "%s %s, bonding version %#x%s%s\n", + device_printf(dev, "%s %s, version 0x%02x/0x%02x%s%s\n", scp->vendor, scp->card_type, scp->version, + scp->srev, scp->mohawk ? ", 100Mbps capable" : "", scp->modem ? ", with modem" : ""); +#if XE_DEBUG > 0 if (scp->mohawk) { XE_SELECT_PAGE(0x10); - device_printf(dev, "DingoID = %#x, RevisionID = %#x, VendorID = %#x\n", + device_printf(dev, "DingoID=0x%04x, RevisionID=0x%04x, VendorID=0x%04x\n", XE_INW(XE_DINGOID), XE_INW(XE_RevID), XE_INW(XE_VendorID)); } if (scp->ce2) { XE_SELECT_PAGE(0x45); - device_printf(dev, "CE2 version = %#x\n", XE_INB(XE_REV)); + device_printf(dev, "CE2 version = 0x%#02x\n", XE_INB(XE_REV)); } - - /* Print MAC address */ +#endif device_printf(dev, "Ethernet address %6D\n", scp->arpcom.ac_enaddr, ":"); /* Attach the interface */ @@ -299,105 +296,153 @@ xe_attach (device_t dev) /* - * Initialize device. Completes the reset procedure on the card and starts - * output. If there's an autonegotiation in progress we DON'T do anything; - * the media selection code will call us again when it's done. + * Complete hardware intitialisation and enable output. Exits without doing + * anything if there's no address assigned to the card, or if media selection + * is in progress (the latter implies we've already run this function). */ static void xe_init(void *xscp) { struct xe_softc *scp = xscp; + unsigned i; int s; -#ifdef XE_DEBUG + if (TAILQ_EMPTY(&scp->ifp->if_addrhead)) return; + + if (scp->autoneg_status != XE_AUTONEG_NONE) return; + +#if XE_DEBUG > 1 device_printf(scp->dev, "init\n"); #endif - if (TAILQ_EMPTY(&scp->ifp->if_addrhead)) return; + s = splimp(); /* Reset transmitter flags */ scp->tx_queued = 0; scp->tx_tpr = 0; - scp->tx_collisions = 0; + scp->tx_timeouts = 0; + scp->tx_thres = 64; + scp->tx_min = ETHER_MIN_LEN - ETHER_CRC_LEN; scp->ifp->if_timer = 0; - s = splimp(); + /* Soft reset the card */ + XE_SELECT_PAGE(0); + XE_OUTB(XE_CR, XE_CR_SOFT_RESET); + DELAY(40000); + XE_OUTB(XE_CR, 0); + DELAY(40000); + if (scp->mohawk) { + /* + * set GP1 and GP2 as outputs (bits 2 & 3) + * set GP1 low to power on the ML6692 (bit 0) + * set GP2 high to power on the 10Mhz chip (bit 1) + */ + XE_SELECT_PAGE(4); + XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT|XE_GPR0_GP1_SELECT|XE_GPR0_GP2_OUT); + } + + /* Shut off interrupts */ + xe_disable_intr(scp); + + /* Wait for everything to wake up */ + DELAY(500000); + + /* Check for PHY */ + if (scp->mohawk) + scp->phy_ok = xe_mii_init(scp); + + /* Disable 'source insertion' (not sure what that means) */ XE_SELECT_PAGE(0x42); - XE_OUTB(XE_SWC0, 0x20); /* Disable source insertion (WTF is that?) */ + XE_OUTB(XE_SWC0, XE_SWC0_NO_SRC_INSERT); - /* - * Set the 'local memory dividing line' -- splits the 32K card memory into - * 8K for transmit buffers and 24K for receive. This is done automatically - * on newer revision cards. - */ + /* Set 8K/24K Tx/Rx buffer split */ if (scp->srev != 1) { XE_SELECT_PAGE(2); XE_OUTW(XE_RBS, 0x2000); } + /* Enable early transmit mode on Mohawk/Dingo */ + if (scp->mohawk) { + XE_SELECT_PAGE(0x03); + XE_OUTW(XE_TPT, scp->tx_thres); + XE_SELECT_PAGE(0x01); + XE_OUTB(XE_ECR, XE_INB(XE_ECR) | XE_ECR_EARLY_TX); + } + + /* Put MAC address in first 'individual address' register */ + XE_SELECT_PAGE(0x50); + for (i = 0; i < 6; i++) + XE_OUTB(0x08 + i, scp->arpcom.ac_enaddr[scp->mohawk ? 5 - i : i]); + /* Set up multicast addresses */ - xe_setmulti(scp); + xe_set_multicast(scp); - /* Fix the data offset register -- reset leaves it off-by-one */ + /* Fix the receive data offset -- reset can leave it off-by-one */ XE_SELECT_PAGE(0); XE_OUTW(XE_DO, 0x2000); - /* - * Set MAC interrupt masks and clear status regs. The bit names are direct - * from the Linux code; I have no idea what most of them do. - */ - XE_SELECT_PAGE(0x40); /* Bit 7..0 */ - XE_OUTB(XE_RX0Msk, 0xff); /* ROK, RAB, rsv, RO, CRC, AE, PTL, MP */ - XE_OUTB(XE_TX0Msk, 0xff); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */ - XE_OUTB(XE_TX0Msk+1, 0xb0); /* rsv, rsv, PTD, EXT, rsv, rsv, rsv, rsv */ - XE_OUTB(XE_RST0, 0x00); /* ROK, RAB, REN, RO, CRC, AE, PTL, MP */ - XE_OUTB(XE_TXST0, 0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */ - XE_OUTB(XE_TXST1, 0x00); /* TEN, rsv, PTD, EXT, retry_counter:4 */ - - /* - * Check for an in-progress autonegotiation. If one is active, just set - * IFF_RUNNING and return. The media selection code will call us again when - * it's done. - */ - if (scp->autoneg_status) { - scp->ifp->if_flags |= IFF_RUNNING; - } - else { - /* Enable receiver, put MAC online */ - XE_SELECT_PAGE(0x40); - XE_OUTB(XE_CMD0, XE_CMD0_RX_ENABLE|XE_CMD0_ONLINE); - - /* Set up IMR, enable interrupts */ - xe_enable_intr(scp); + /* Set interrupt masks */ + XE_SELECT_PAGE(1); + XE_OUTB(XE_IMR0, XE_IMR0_TX_PACKET | XE_IMR0_MAC_INTR | XE_IMR0_RX_PACKET); + + /* Set MAC interrupt masks */ + XE_SELECT_PAGE(0x40); + XE_OUTB(XE_RX0Msk, + ~(XE_RX0M_RX_OVERRUN | XE_RX0M_CRC_ERROR + | XE_RX0M_ALIGN_ERROR | XE_RX0M_LONG_PACKET)); + XE_OUTB(XE_TX0Msk, + ~(XE_TX0M_SQE_FAIL | XE_TX0M_LATE_COLLISION | XE_TX0M_TX_UNDERRUN + | XE_TX0M_16_COLLISIONS | XE_TX0M_NO_CARRIER)); + + /* Clear MAC status registers */ + XE_SELECT_PAGE(0x40); + XE_OUTB(XE_RST0, 0x00); + XE_OUTB(XE_TXST0, 0x00); + + /* Enable receiver and put MAC online */ + XE_SELECT_PAGE(0x40); + XE_OUTB(XE_CMD0, XE_CMD0_RX_ENABLE|XE_CMD0_ONLINE); + + /* Set up IMR, enable interrupts */ + xe_enable_intr(scp); + + /* Start media selection */ + xe_setmedia(scp); - /* Attempt to start output */ - scp->ifp->if_flags |= IFF_RUNNING; - scp->ifp->if_flags &= ~IFF_OACTIVE; - xe_start(scp->ifp); - } + /* Enable output */ + scp->ifp->if_flags |= IFF_RUNNING; + scp->ifp->if_flags &= ~IFF_OACTIVE; (void)splx(s); } /* - * Start output on interface. We make two assumptions here: - * 1) that the current priority is set to splimp _before_ this code - * is called *and* is returned to the appropriate priority after - * return - * 2) that the IFF_OACTIVE flag is checked before this code is called - * (i.e. that the output part of the interface is idle) + * Start output on interface. Should be called at splimp() priority. Check + * that the output is idle (ie, IFF_OACTIVE is not set) before calling this + * function. If media selection is in progress we set IFF_OACTIVE ourselves + * and return immediately. */ static void xe_start(struct ifnet *ifp) { struct xe_softc *scp = ifp->if_softc; struct mbuf *mbp; + if (scp->autoneg_status != XE_AUTONEG_NONE) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + +#if XE_DEBUG > 2 + device_printf(scp->dev, "start\n"); +#endif + /* * Loop while there are packets to be sent, and space to send them. */ while (1) { - IF_DEQUEUE(&ifp->if_snd, mbp); /* Suck a packet off the send queue */ + /* Suck a packet off the send queue */ + IF_DEQUEUE(&ifp->if_snd, mbp); if (mbp == NULL) { /* @@ -412,7 +457,8 @@ xe_start(struct ifnet *ifp) { } if (xe_pio_write_packet(scp, mbp) != 0) { - IF_PREPEND(&ifp->if_snd, mbp); /* Push the packet back onto the queue */ + /* Push the packet back onto the queue */ + IF_PREPEND(&ifp->if_snd, mbp); ifp->if_flags |= IFF_OACTIVE; return; } @@ -420,7 +466,8 @@ xe_start(struct ifnet *ifp) { /* Tap off here if there is a bpf listener */ BPF_MTAP(ifp, mbp); - ifp->if_timer = 5; /* In case we don't hear from the card again */ + /* In case we don't hear from the card again... */ + ifp->if_timer = 5; scp->tx_queued++; m_freem(mbp); @@ -443,15 +490,17 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) { switch (command) { - case SIOCSIFFLAGS: + case SIOCSIFFLAGS: +#if XE_DEBUG > 1 + device_printf(scp->dev, "ioctl: SIOCSIFFLAGS: 0x%04x\n", ifp->if_flags); +#endif /* * If the interface is marked up and stopped, then start it. If it is * marked down and running, then stop it. */ if (ifp->if_flags & IFF_UP) { if (!(ifp->if_flags & IFF_RUNNING)) { - xe_hard_reset(scp); - xe_setmedia(scp); + xe_reset(scp); xe_init(scp); } } @@ -459,27 +508,36 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) { if (ifp->if_flags & IFF_RUNNING) xe_stop(scp); } - /* XXX: intentional fall-through ? */ - case SIOCADDMULTI: - case SIOCDELMULTI: + /* FALL THROUGH (handle changes to PROMISC/ALLMULTI flags) */ + + case SIOCADDMULTI: + case SIOCDELMULTI: +#if XE_DEBUG > 1 + device_printf(scp->dev, "ioctl: SIOC{ADD,DEL}MULTI\n"); +#endif /* - * Multicast list has (maybe) changed; set the hardware filter - * accordingly. This also serves to deal with promiscuous mode if we have - * a BPF listener active. + * Multicast list has (maybe) changed; set the hardware filters + * accordingly. */ - xe_setmulti(scp); + xe_set_multicast(scp); error = 0; break; - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: +#if XE_DEBUG > 1 + device_printf(scp->dev, "ioctl: bounce to ifmedia_ioctl\n"); +#endif /* * Someone wants to get/set media options. */ error = ifmedia_ioctl(ifp, (struct ifreq *)data, &scp->ifmedia, command); break; - default: + default: +#if XE_DEBUG > 1 + device_printf(scp->dev, "ioctl: bounce to ether_ioctl\n"); +#endif error = ether_ioctl(ifp, command, data); } @@ -512,245 +570,272 @@ xe_intr(void *xscp) { struct xe_softc *scp = (struct xe_softc *) xscp; struct ifnet *ifp; - u_int16_t rx_bytes, rxs, txs; - u_int8_t psr, isr, esr, rsr; + u_int8_t psr, isr, esr, rsr, rst0, txst0, txst1, coll; ifp = &scp->arpcom.ac_if; - rx_bytes = 0; /* Bytes received on this interrupt */ - if (scp->mohawk) { - XE_OUTB(XE_CR, 0); /* Disable interrupts */ - } + /* Disable interrupts */ + if (scp->mohawk) + XE_OUTB(XE_CR, 0); - psr = XE_INB(XE_PR); /* Stash the current register page */ + /* Cache current register page */ + psr = XE_INB(XE_PR); - /* - * Read ISR to see what caused this interrupt. Note that this clears the - * ISR on CE2 type cards. - */ - if ((isr = XE_INB(XE_ISR)) && isr != 0xff) { + /* Read ISR to see what caused this interrupt */ + while ((isr = XE_INB(XE_ISR)) != 0) { - esr = XE_INB(XE_ESR); /* Read the other status registers */ + /* 0xff might mean the card is no longer around */ + if (isr == 0xff) { +#if XE_DEBUG > 2 + device_printf(scp->dev, "intr: interrupt received for missing card?\n"); +#endif + break; + } + + /* Read other status registers */ XE_SELECT_PAGE(0x40); - rxs = XE_INB(XE_RST0); - XE_OUTB(XE_RST0, ~rxs & 0xff); - txs = XE_INB(XE_TXST0); - txs |= XE_INB(XE_TXST1) << 8; + rst0 = XE_INB(XE_RST0); + XE_OUTB(XE_RST0, 0); + txst0 = XE_INB(XE_TXST0); + txst1 = XE_INB(XE_TXST1); + coll = txst1 & XE_TXST1_RETRY_COUNT; XE_OUTB(XE_TXST0, 0); XE_OUTB(XE_TXST1, 0); XE_SELECT_PAGE(0); #if XE_DEBUG > 2 - printf("xe%d: ISR=%#2.2x ESR=%#2.2x RST=%#2.2x TXST=%#4.4x\n", unit, isr, esr, rxs, txs); + device_printf(scp->dev, "intr: ISR=0x%02x, RST=0x%02x, TXT=0x%02x%02x, COLL=0x%01x\n", isr, rst0, txst1, txst0, coll); #endif - /* - * Handle transmit interrupts - */ + /* Handle transmitted packet(s) */ if (isr & XE_ISR_TX_PACKET) { - u_int8_t new_tpr, sent; - - if ((new_tpr = XE_INB(XE_TPR)) < scp->tx_tpr) /* Update packet count */ - sent = (0xff - scp->tx_tpr) + new_tpr; /* TPR rolled over */ - else - sent = new_tpr - scp->tx_tpr; + u_int8_t tpr, sent; + + /* Update packet count, accounting for rollover */ + tpr = XE_INB(XE_TPR); + sent = -scp->tx_tpr + tpr; - if (sent > 0) { /* Packets sent since last interrupt */ - scp->tx_tpr = new_tpr; + /* Update statistics if we actually sent anything */ + if (sent > 0) { + scp->tx_tpr = tpr; scp->tx_queued -= sent; ifp->if_opackets += sent; - ifp->if_collisions += scp->tx_collisions; + ifp->if_collisions += coll; /* - * Collision stats are a PITA. If multiples frames have been sent, we - * distribute any outstanding collision count equally amongst them. - * However, if we're missing interrupts we're quite likely to also - * miss some collisions; thus the total count will be off anyway. - * Likewise, if we miss a frame dropped due to excessive collisions - * any outstanding collisions count will be held against the next - * frame to be successfully sent. Hopefully it averages out in the - * end! - * XXX - This will screw up if tx_collisions/sent > 14. FIX IT! + * According to the Xircom manual, Dingo will sometimes manage to + * transmit a packet with triggering an interrupt. If this happens, + * we have sent > 1 and the collision count only reflects collisions + * on the last packet sent (the one that triggered the interrupt). + * Collision stats might therefore be a bit low, but there doesn't + * seem to be anything we can do about that. */ - switch (scp->tx_collisions) { - case 0: + + switch (coll) { + case 0: break; - case 1: + case 1: scp->mibdata.dot3StatsSingleCollisionFrames++; scp->mibdata.dot3StatsCollFrequencies[0]++; break; - default: - if (sent == 1) { - scp->mibdata.dot3StatsMultipleCollisionFrames++; - scp->mibdata.dot3StatsCollFrequencies[scp->tx_collisions-1]++; - } - else { /* Distribute across multiple frames */ - scp->mibdata.dot3StatsMultipleCollisionFrames += sent; - scp->mibdata. - dot3StatsCollFrequencies[scp->tx_collisions/sent] += sent - scp->tx_collisions%sent; - scp->mibdata. - dot3StatsCollFrequencies[scp->tx_collisions/sent + 1] += scp->tx_collisions%sent; - } + default: + scp->mibdata.dot3StatsMultipleCollisionFrames++; + scp->mibdata.dot3StatsCollFrequencies[coll-1]++; } - scp->tx_collisions = 0; } ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; } - if (txs & 0x0002) { /* Excessive collisions (packet dropped) */ - ifp->if_collisions += 16; - ifp->if_oerrors++; - scp->tx_collisions = 0; - scp->mibdata.dot3StatsExcessiveCollisions++; - scp->mibdata.dot3StatsMultipleCollisionFrames++; - scp->mibdata.dot3StatsCollFrequencies[15]++; - XE_OUTB(XE_CR, XE_CR_RESTART_TX); - } - if (txs & 0x0040) /* Transmit aborted -- probably collisions */ - scp->tx_collisions++; + /* Handle most MAC interrupts */ + if (isr & XE_ISR_MAC_INTR) { - /* - * Handle receive interrupts - */ +#if 0 + /* Carrier sense lost -- only in 10Mbit HDX mode */ + if (txst0 & XE_TXST0_NO_CARRIER || !(txst1 & XE_TXST1_LINK_STATUS)) { + /* XXX - Need to update media status here */ + device_printf(scp->dev, "no carrier\n"); + ifp->if_oerrors++; + scp->mibdata.dot3StatsCarrierSenseErrors++; + } +#endif + /* Excessive collisions -- try sending again */ + if (txst0 & XE_TXST0_16_COLLISIONS) { + ifp->if_collisions += 16; + ifp->if_oerrors++; + scp->mibdata.dot3StatsExcessiveCollisions++; + scp->mibdata.dot3StatsMultipleCollisionFrames++; + scp->mibdata.dot3StatsCollFrequencies[15]++; + XE_OUTB(XE_CR, XE_CR_RESTART_TX); + } + /* Transmit underrun -- increase early transmit threshold */ + if (txst0 & XE_TXST0_TX_UNDERRUN && scp->mohawk) { + device_printf(scp->dev, "transmit underrun"); + if (scp->tx_thres < ETHER_MAX_LEN) { + if ((scp->tx_thres += 64) > ETHER_MAX_LEN) + scp->tx_thres = ETHER_MAX_LEN; + printf(": increasing transmit threshold to %u", scp->tx_thres); + XE_SELECT_PAGE(0x3); + XE_OUTW(XE_TPT, scp->tx_thres); + XE_SELECT_PAGE(0x0); + } + printf("\n"); + ifp->if_oerrors++; + scp->mibdata.dot3StatsInternalMacTransmitErrors++; + } + /* Late collision -- just complain about it */ + if (txst0 & XE_TXST0_LATE_COLLISION) { + device_printf(scp->dev, "late collision\n"); + ifp->if_oerrors++; + scp->mibdata.dot3StatsLateCollisions++; + } + /* SQE test failure -- just complain about it */ + if (txst0 & XE_TXST0_SQE_FAIL) { + device_printf(scp->dev, "SQE test failure\n"); + ifp->if_oerrors++; + scp->mibdata.dot3StatsSQETestErrors++; + } + /* Packet too long -- what happens to these */ + if (rst0 & XE_RST0_LONG_PACKET) { + device_printf(scp->dev, "received giant packet\n"); + ifp->if_ierrors++; + scp->mibdata.dot3StatsFrameTooLongs++; + } + /* CRC error -- packet dropped */ + if (rst0 & XE_RST0_CRC_ERROR) { + device_printf(scp->dev, "CRC error\n"); + ifp->if_ierrors++; + scp->mibdata.dot3StatsFCSErrors++; + } + } + + /* Handle received packet(s) */ while ((esr = XE_INB(XE_ESR)) & XE_ESR_FULL_PACKET_RX) { + rsr = XE_INB(XE_RSR); - if ((rsr = XE_INB(XE_RSR)) & XE_RSR_RX_OK) { +#if XE_DEBUG > 2 + device_printf(scp->dev, "intr: ESR=0x%02x, RSR=0x%02x\n", esr, rsr); +#endif + + /* Make sure packet is a good one */ + if (rsr & XE_RSR_RX_OK) { struct ether_header *ehp; struct mbuf *mbp; u_int16_t len; - len = XE_INW(XE_RBC); + len = XE_INW(XE_RBC) - ETHER_CRC_LEN; + +#if XE_DEBUG > 2 + device_printf(scp->dev, "intr: receive length = %d\n", len); +#endif - if (len == 0) + if (len == 0) { + ifp->if_iqdrops++; continue; + } -#if 0 /* - * Limit the amount of time we spend in this loop, dropping packets if - * necessary. The Linux code does this with considerably more - * finesse, adjusting the threshold dynamically. + * Allocate mbuf to hold received packet. If the mbuf header isn't + * big enough, we attach an mbuf cluster to hold the packet. Note the + * +=2 to align the packet data on a 32-bit boundary, and the +3 to + * allow for the possibility of reading one more byte than the actual + * packet length (we always read 16-bit words). + * XXX - Surely there's a better way to do this alignment? */ - if ((rx_bytes += len) > 22000) { + MGETHDR(mbp, M_DONTWAIT, MT_DATA); + if (mbp == NULL) { ifp->if_iqdrops++; - scp->mibData.dot3StatsMissedFrames++; - XE_OUTW(XE_DO, 0x8000); continue; } -#endif - - if (len & 0x01) - len++; - MGETHDR(mbp, M_DONTWAIT, MT_DATA); /* Allocate a header mbuf */ - if (mbp != NULL) { - mbp->m_pkthdr.rcvif = ifp; - mbp->m_pkthdr.len = mbp->m_len = len; - - /* - * If the mbuf header isn't big enough for the packet, attach an - * mbuf cluster to hold it. The +2 is to allow for the nasty little - * alignment hack below. - */ - if (len + 2 > MHLEN) { - MCLGET(mbp, M_DONTWAIT); - if ((mbp->m_flags & M_EXT) == 0) { - m_freem(mbp); - mbp = NULL; - } + if (len + 3 > MHLEN) { + MCLGET(mbp, M_DONTWAIT); + if ((mbp->m_flags & M_EXT) == 0) { + m_freem(mbp); + ifp->if_iqdrops++; + continue; } } - if (mbp != NULL) { - /* - * The Ethernet header is 14 bytes long; thus the actual packet data - * won't be 32-bit aligned when it's dumped into the mbuf. We - * offset everything by 2 bytes to fix this. Apparently the - * alignment is important for NFS, damn its eyes. - */ - mbp->m_data += 2; - ehp = mtod(mbp, struct ether_header *); + mbp->m_data += 2; + ehp = mtod(mbp, struct ether_header *); - /* - * Now get the packet, including the Ethernet header and trailer (?) - * We use programmed I/O, because we don't know how to do shared - * memory with these cards. So yes, it's real slow, and heavy on - * the interrupts (CPU on my P150 maxed out at ~950KBps incoming). - */ - if (scp->srev == 0) { /* Workaround a bug in old cards */ - u_short rhs; - - XE_SELECT_PAGE(5); - rhs = XE_INW(XE_RHSA); - XE_SELECT_PAGE(0); - - rhs += 3; /* Skip control info */ - - if (rhs >= 0x8000) - rhs = 0; - - if (rhs + len > 0x8000) { - int i; - - /* - * XXX - This i-- seems very wrong, but it's what the Linux guys - * XXX - do. Need someone with an old CE2 to test this for me. - * XXX - 99/3/28: Changed the first i-- to an i++, maybe that'll - * XXX - fix it? It seems as though the previous version would - * XXX - have caused an infinite loop (what, another one?). - */ - for (i = 0; i < len; i++, rhs++) { - ((char *)ehp)[i] = XE_INB(XE_EDP); - if (rhs == 0x8000) { - rhs = 0; - i--; - } + /* + * Now get the packet in PIO mode, including the Ethernet header but + * omitting the trailing CRC. + */ + + /* + * Work around a bug in CE2 cards. There seems to be a problem with + * duplicated and extraneous bytes in the receive buffer, but without + * any real documentation for the CE2 it's hard to tell for sure. + * XXX - Needs testing on CE2 hardware + */ + if (scp->srev == 0) { + u_short rhs; + + XE_SELECT_PAGE(5); + rhs = XE_INW(XE_RHSA); + XE_SELECT_PAGE(0); + + rhs += 3; /* Skip control info */ + + if (rhs >= 0x8000) + rhs = 0; + + if (rhs + len > 0x8000) { + int i; + + for (i = 0; i < len; i++, rhs++) { + ((char *)ehp)[i] = XE_INB(XE_EDP); + if (rhs == 0x8000) { + rhs = 0; + i--; } } - else - bus_space_read_multi_2(scp->bst, scp->bsh, XE_EDP, - (u_int16_t *) ehp, len >> 1); } else bus_space_read_multi_2(scp->bst, scp->bsh, XE_EDP, - (u_int16_t *) ehp, len >> 1); - - /* Deliver packet to upper layers */ - if (mbp != NULL) { - mbp->m_flags |= M_HASFCS; /* FCS is included in our - * packet */ - mbp->m_pkthdr.len = mbp->m_len = len; - (*ifp->if_input)(ifp, mbp); /* Send the packet on its way */ - ifp->if_ipackets++; /* Success! */ - } - XE_OUTW(XE_DO, 0x8000); /* skip_rx_packet command */ + (u_int16_t *) ehp, (len + 1) >> 1); } + else + bus_space_read_multi_2(scp->bst, scp->bsh, XE_EDP, + (u_int16_t *) ehp, (len + 1) >> 1); + + /* Deliver packet to upper layers */ + mbp->m_pkthdr.rcvif = ifp; + mbp->m_pkthdr.len = mbp->m_len = len; + (*ifp->if_input)(ifp, mbp); + ifp->if_ipackets++; } - else if (rsr & XE_RSR_LONG_PACKET) { /* Packet length >1518 bytes */ - scp->mibdata.dot3StatsFrameTooLongs++; - ifp->if_ierrors++; - } - else if (rsr & XE_RSR_CRC_ERROR) { /* Bad checksum on packet */ - scp->mibdata.dot3StatsFCSErrors++; - ifp->if_ierrors++; - } - else if (rsr & XE_RSR_ALIGN_ERROR) { /* Packet alignment error */ + + /* Packet alignment error -- drop packet */ + else if (rsr & XE_RSR_ALIGN_ERROR) { + device_printf(scp->dev, "alignment error\n"); scp->mibdata.dot3StatsAlignmentErrors++; ifp->if_ierrors++; } + + /* Skip to next packet, if there is one */ + XE_OUTW(XE_DO, 0x8000); } - if (rxs & 0x10) { /* Receiver overrun */ - scp->mibdata.dot3StatsInternalMacReceiveErrors++; + + /* Clear receiver overruns now we have some free buffer space */ + if (rst0 & XE_RST0_RX_OVERRUN) { +#if XE_DEBUG > 0 + device_printf(scp->dev, "receive overrun\n"); +#endif ifp->if_ierrors++; + scp->mibdata.dot3StatsInternalMacReceiveErrors++; XE_OUTB(XE_CR, XE_CR_CLEAR_OVERRUN); } } - XE_SELECT_PAGE(psr); /* Restore saved page */ - XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Re-enable interrupts */ + /* Restore saved page */ + XE_SELECT_PAGE(psr); - /* Could force an int here, instead of dropping packets? */ - /* XE_OUTB(XE_CR, XE_CR_ENABLE_INTR|XE_CE_FORCE_INTR); */ + /* Re-enable interrupts */ + XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); return; } @@ -766,12 +851,11 @@ static void xe_watchdog(struct ifnet *ifp) { struct xe_softc *scp = ifp->if_softc; - device_printf(scp->dev, "watchdog timeout; resetting card\n"); + device_printf(scp->dev, "watchdog timeout: resetting card\n"); scp->tx_timeouts++; ifp->if_oerrors += scp->tx_queued; xe_stop(scp); - xe_hard_reset(scp); - xe_setmedia(scp); + xe_reset(scp); xe_init(scp); } @@ -783,7 +867,7 @@ static int xe_media_change(struct ifnet *ifp) { struct xe_softc *scp = ifp->if_softc; -#ifdef XE_DEBUG +#if XE_DEBUG > 1 if_printf(ifp, "media_change\n"); #endif @@ -809,10 +893,12 @@ xe_media_change(struct ifnet *ifp) { static void xe_media_status(struct ifnet *ifp, struct ifmediareq *mrp) { -#ifdef XE_DEBUG +#if XE_DEBUG > 1 if_printf(ifp, "media_status\n"); #endif + /* XXX - This is clearly wrong. Will fix once I have CE2 working */ + mrp->ifm_status = IFM_AVALID | IFM_ACTIVE; mrp->ifm_active = ((struct xe_softc *)ifp->if_softc)->media; return; @@ -826,7 +912,7 @@ static void xe_setmedia(void *xscp) { struct xe_softc *scp = xscp; u_int16_t bmcr, bmsr, anar, lpar; -#ifdef XE_DEBUG +#if XE_DEBUG > 1 device_printf(scp->dev, "setmedia\n"); #endif @@ -872,17 +958,19 @@ static void xe_setmedia(void *xscp) { */ switch (scp->autoneg_status) { - case XE_AUTONEG_NONE: + case XE_AUTONEG_NONE: #if XE_DEBUG > 1 device_printf(scp->dev, "Waiting for idle transmitter\n"); #endif scp->arpcom.ac_if.if_flags |= IFF_OACTIVE; scp->autoneg_status = XE_AUTONEG_WAITING; - scp->chand = timeout(xe_setmedia, scp, hz * 2); - return; + /* FALL THROUGH */ - case XE_AUTONEG_WAITING: - xe_soft_reset(scp); + case XE_AUTONEG_WAITING: + if (scp->tx_queued != 0) { + scp->chand = timeout(xe_setmedia, scp, hz/2); + return; + } if (scp->phy_ok) { #if XE_DEBUG > 1 device_printf(scp->dev, "Starting autonegotiation\n"); @@ -897,7 +985,7 @@ static void xe_setmedia(void *xscp) { bmcr |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; xe_phy_writereg(scp, PHY_BMCR, bmcr); scp->autoneg_status = XE_AUTONEG_STARTED; - scp->chand = timeout(xe_setmedia, scp, hz * 7/2); + scp->chand = timeout(xe_setmedia, scp, hz * 7/2); return; } else { @@ -952,7 +1040,6 @@ static void xe_setmedia(void *xscp) { device_printf(scp->dev, "Autonegotiation failed; trying 100baseTX\n"); #endif XE_MII_DUMP(scp); - xe_soft_reset(scp); if (scp->phy_ok) { xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL); scp->autoneg_status = XE_AUTONEG_100TX; @@ -1027,7 +1114,6 @@ static void xe_setmedia(void *xscp) { * no PHY, we fall back to 10baseT operation). */ case IFM_100_TX: /* Force 100baseTX */ - xe_soft_reset(scp); if (scp->phy_ok) { #if XE_DEBUG > 1 device_printf(scp->dev, "Selecting 100baseTX\n"); @@ -1043,7 +1129,6 @@ static void xe_setmedia(void *xscp) { /* FALLTHROUGH */ case IFM_10_T: /* Force 10baseT */ - xe_soft_reset(scp); #if XE_DEBUG > 1 device_printf(scp->dev, "Selecting 10baseT\n"); #endif @@ -1058,7 +1143,6 @@ static void xe_setmedia(void *xscp) { break; case IFM_10_2: - xe_soft_reset(scp); #if XE_DEBUG > 1 device_printf(scp->dev, "Selecting 10base2\n"); #endif @@ -1091,8 +1175,9 @@ static void xe_setmedia(void *xscp) { } /* Restart output? */ + xe_enable_intr(scp); scp->ifp->if_flags &= ~IFF_OACTIVE; - xe_init(scp); + xe_start(scp->ifp); } @@ -1100,98 +1185,27 @@ static void xe_setmedia(void *xscp) { * Hard reset (power cycle) the card. */ static void -xe_hard_reset(struct xe_softc *scp) { +xe_reset(struct xe_softc *scp) { int s; -#ifdef XE_DEBUG - device_printf(scp->dev, "hard_reset\n"); +#if XE_DEBUG > 1 + device_printf(scp->dev, "reset\n"); #endif s = splimp(); - /* - * Power cycle the card. - */ + /* Power down */ XE_SELECT_PAGE(4); - XE_OUTB(XE_GPR1, 0); /* Power off */ + XE_OUTB(XE_GPR1, 0); DELAY(40000); + /* Power up again */ if (scp->mohawk) - XE_OUTB(XE_GPR1, 1); /* And back on again */ + XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN); else - XE_OUTB(XE_GPR1, 5); /* Also set AIC bit, whatever that is */ - DELAY(40000); - XE_SELECT_PAGE(0); + XE_OUTB(XE_GPR1, XE_GPR1_POWER_DOWN|XE_GPR1_AIC); - (void)splx(s); -} - - -/* - * Soft reset the card. Also makes sure that the ML6692 and 10Mbit controller - * are powered up, sets the silicon revision number in softc, disables - * interrupts and checks for the prescence of a 100Mbit PHY. This should - * leave us in a position where we can access the PHY and do media - * selection. The function imposes a 0.5s delay while the hardware powers up. - */ -static void -xe_soft_reset(struct xe_softc *scp) { - int s; - -#ifdef XE_DEBUG - device_printf(scp->dev, "soft_reset\n"); -#endif - - s = splimp(); - - /* - * Reset the card, (again). - */ - XE_SELECT_PAGE(0); - XE_OUTB(XE_CR, XE_CR_SOFT_RESET); DELAY(40000); - XE_OUTB(XE_CR, 0); - DELAY(40000); - - if (scp->mohawk) { - /* - * set GP1 and GP2 as outputs (bits 2 & 3) - * set GP1 low to power on the ML6692 (bit 0) - * set GP2 high to power on the 10Mhz chip (bit 1) - */ - XE_SELECT_PAGE(4); - XE_OUTB(XE_GPR0, 0x0e); - } - - /* - * Wait for everything to wake up. - */ - DELAY(500000); - - /* - * Get silicon revision number. - */ - XE_SELECT_PAGE(4); - if (scp->mohawk) - scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4; - else - scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4; -#ifdef XE_DEBUG - device_printf(scp->dev, "silicon revision = %d\n", scp->srev); -#endif - - /* - * Shut off interrupts. - */ - xe_disable_intr(scp); - - /* - * Check for PHY. - */ - if (scp->mohawk) { - scp->phy_ok = xe_mii_init(scp); - } - XE_SELECT_PAGE(0); (void)splx(s); @@ -1207,7 +1221,7 @@ static void xe_stop(struct xe_softc *scp) { int s; -#ifdef XE_DEBUG +#if XE_DEBUG > 1 device_printf(scp->dev, "stop\n"); #endif @@ -1224,6 +1238,15 @@ xe_stop(struct xe_softc *scp) { XE_SELECT_PAGE(4); XE_OUTB(XE_GPR1, 0); XE_SELECT_PAGE(0); + if (scp->mohawk) { + /* + * set GP1 and GP2 as outputs (bits 2 & 3) + * set GP1 high to power on the ML6692 (bit 0) + * set GP2 low to power on the 10Mhz chip (bit 1) + */ + XE_SELECT_PAGE(4); + XE_OUTB(XE_GPR0, XE_GPR0_GP2_SELECT|XE_GPR0_GP1_SELECT|XE_GPR0_GP1_OUT); + } /* * ~IFF_RUNNING == interface down. @@ -1237,19 +1260,15 @@ xe_stop(struct xe_softc *scp) { /* - * Enable Ethernet interrupts from the card. + * Enable interrupts from the card. */ static void xe_enable_intr(struct xe_softc *scp) { -#ifdef XE_DEBUG + +#if XE_DEBUG > 1 device_printf(scp->dev, "enable_intr\n"); #endif - XE_SELECT_PAGE(1); - XE_OUTB(XE_IMR0, 0xff); /* Unmask everything */ - XE_OUTB(XE_IMR1, 0x01); /* Unmask TX underrun detection */ - DELAY(1); - XE_SELECT_PAGE(0); XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Enable interrupts */ if (scp->modem && !scp->dingo) { /* This bit is just magic */ @@ -1261,132 +1280,236 @@ xe_enable_intr(struct xe_softc *scp) { /* - * Disable all Ethernet interrupts from the card. + * Disable interrupts from the card. */ static void xe_disable_intr(struct xe_softc *scp) { -#ifdef XE_DEBUG + +#if XE_DEBUG > 1 device_printf(scp->dev, "disable_intr\n"); #endif XE_SELECT_PAGE(0); XE_OUTB(XE_CR, 0); /* Disable interrupts */ - if (scp->modem && !scp->dingo) { /* More magic (does this work?) */ + if (scp->modem && !scp->dingo) { /* More magic */ XE_OUTB(0x10, 0x10); /* Mask the master int enable bit */ } - - XE_SELECT_PAGE(1); - XE_OUTB(XE_IMR0, 0); /* Forbid all interrupts */ - XE_OUTB(XE_IMR1, 0); - XE_SELECT_PAGE(0); } /* - * Set up multicast filter and promiscuous mode + * Set up multicast filter and promiscuous modes. */ static void -xe_setmulti(struct xe_softc *scp) { +xe_set_multicast(struct xe_softc *scp) { struct ifnet *ifp; struct ifmultiaddr *maddr; - int count; + unsigned count, i; + +#if XE_DEBUG > 1 + device_printf(scp->dev, "set_multicast\n"); +#endif ifp = &scp->arpcom.ac_if; - maddr = TAILQ_FIRST(&ifp->if_multiaddrs); + XE_SELECT_PAGE(0x42); - /* Get length of multicast list */ - for (count = 0; maddr != NULL; maddr = TAILQ_NEXT(maddr, ifma_link), count++); + /* Handle PROMISC flag */ + if (ifp->if_flags & IFF_PROMISC) { + XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_PROMISCUOUS); + return; + } + else + XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_PROMISCUOUS); - if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) || (count > 9)) { - /* - * Go into promiscuous mode if either of the PROMISC or ALLMULTI flags are - * set, or if we have been asked to deal with more than 9 multicast - * addresses. To do this: set MPE and PME in SWC1 - */ - XE_SELECT_PAGE(0x42); - XE_OUTB(XE_SWC1, 0x06); + /* Handle ALLMULTI flag */ + if (ifp->if_flags & IFF_ALLMULTI) { + XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) | XE_SWC1_ALLMULTI); + return; } - else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { - /* - * Program the filters for up to 9 addresses - */ + else + XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI); + + /* Iterate over multicast address list */ + count = 0; +#if __FreeBSD_version < 500000 + LIST_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { +#else + TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { +#endif + if (maddr->ifma_addr->sa_family != AF_LINK) + continue; + + count++; + + if (count < 10) + /* First 9 use Individual Addresses for exact matching */ + xe_set_addr(scp, LLADDR((struct sockaddr_dl *)maddr->ifma_addr), count); + else + if (scp->mohawk) + /* Use hash filter on Mohawk and Dingo */ + xe_set_hash(scp, LLADDR((struct sockaddr_dl *)maddr->ifma_addr)); + else + /* Nowhere else to put them on CE2 */ + break; + } + +#if XE_DEBUG > 1 + device_printf(scp->dev, "set_multicast: count = %u\n", count); +#endif + + /* Now do some cleanup and enable multicast handling as needed */ + if (count == 0) { + /* Disable all multicast handling */ XE_SELECT_PAGE(0x42); - XE_OUTB(XE_SWC1, 0x01); - XE_SELECT_PAGE(0x40); - XE_OUTB(XE_CMD0, XE_CMD0_OFFLINE); - /*xe_reg_dump(scp);*/ - xe_setaddrs(scp); - /*xe_reg_dump(scp);*/ - XE_SELECT_PAGE(0x40); - XE_OUTB(XE_CMD0, XE_CMD0_RX_ENABLE|XE_CMD0_ONLINE); + XE_OUTB(XE_SWC1, XE_INB(XE_SWC1) & ~(XE_SWC1_IA_ENABLE|XE_SWC1_ALLMULTI)); + if (scp->mohawk) { + XE_SELECT_PAGE(0x02); + XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); + } } - else { - /* - * No multicast operation (default) - */ + else if (count < 10) { + /* Full in any unused Individual Addresses with our MAC address */ + for (i = count + 1; i < 10; i++) + xe_set_addr(scp, (u_int8_t *)(&scp->arpcom.ac_enaddr), i); + /* Enable Individual Address matching only */ XE_SELECT_PAGE(0x42); - XE_OUTB(XE_SWC1, 0); + XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) | XE_SWC1_IA_ENABLE); + if (scp->mohawk) { + XE_SELECT_PAGE(0x02); + XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); + } + } + else { + if (scp->mohawk) { + /* Check whether hash table is full */ + XE_SELECT_PAGE(0x58); + for (i = 0x08; i < 0x10; i++) + if (XE_INB(i) != 0xff) + break; + if (i == 0x10) { + /* Hash table full - enable promiscuous multicast matching */ + XE_SELECT_PAGE(0x42); + XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_IA_ENABLE) | XE_SWC1_ALLMULTI); + XE_SELECT_PAGE(0x02); + XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~XE_MSR_HASH_TABLE); + } + else { + /* Enable hash table and Individual Address matching */ + XE_SELECT_PAGE(0x42); + XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_ALLMULTI) | XE_SWC1_IA_ENABLE); + XE_SELECT_PAGE(0x02); + XE_OUTB(XE_MSR, XE_INB(XE_MSR) | XE_MSR_HASH_TABLE); + } + } + else { + /* Enable promiscuous multicast matching */ + XE_SELECT_PAGE(0x42); + XE_OUTB(XE_SWC1, (XE_INB(XE_SWC1) & ~XE_SWC1_IA_ENABLE) | XE_SWC1_ALLMULTI); + } } + XE_SELECT_PAGE(0); } /* - * Set up all on-chip addresses (for multicast). AFAICS, there are 10 - * of these things; the first is our MAC address, the other 9 are mcast - * addresses, padded with the MAC address if there aren't enough. - * XXX - This doesn't work right, but I'm not sure why yet. We seem to be - * XXX - doing much the same as the Linux code, which is weird enough that - * XXX - it's probably right (despite my earlier comments to the contrary). + * Copy the Ethernet multicast address in addr to the on-chip registers for + * Individual Address idx. Assumes that addr is really a multicast address + * and that idx > 0 (slot 0 is always used for the card MAC address). */ static void -xe_setaddrs(struct xe_softc *scp) { - struct ifmultiaddr *maddr; - u_int8_t *addr; - u_int8_t page, slot, byte, i; - - maddr = TAILQ_FIRST(&scp->arpcom.ac_if.if_multiaddrs); +xe_set_addr(struct xe_softc *scp, u_int8_t* addr, unsigned idx) { + u_int8_t page, reg; + unsigned i; - XE_SELECT_PAGE(page = 0x50); + /* + * Individual Addresses are stored in registers 8-F of pages 0x50-0x57. IA1 + * therefore starts at register 0xE on page 0x50. The expressions below + * compute the starting page and register for any IA index > 0. + */ + --idx; + page = 0x50 + idx%4 + idx/4*3; + reg = 0x0e - 2 * (idx%4); - for (slot = 0, byte = 8; slot < 10; slot++) { +#if XE_DEBUG > 1 + device_printf(scp->dev, "set_addr: idx = %u, page = 0x%02x, reg = 0x%02x\n", + idx+1, page, reg); +#endif - if (slot == 0) - addr = (u_int8_t *)(&scp->arpcom.ac_enaddr); - else { - while (maddr != NULL && maddr->ifma_addr->sa_family != AF_LINK) - maddr = TAILQ_NEXT(maddr, ifma_link); - if (maddr != NULL) - addr = LLADDR((struct sockaddr_dl *)maddr->ifma_addr); - else - addr = (u_int8_t *)(&scp->arpcom.ac_enaddr); + /* + * Copy the IA bytes. Note that the byte order is reversed for Mohawk and + * Dingo wrt. CE2 hardware. + */ + XE_SELECT_PAGE(page); + for (i = 0; i < 6; i++) { +#if XE_DEBUG > 1 + if (i > 0) + printf(":%02x", addr[i]); + else + device_printf(scp->dev, "set_addr: %02x", addr[0]); +#endif + XE_OUTB(reg, addr[scp->mohawk ? 5 - i : i]); + if (++reg == 0x10) { + reg = 0x08; + XE_SELECT_PAGE(++page); } - - for (i = 0; i < 6; i++, byte++) { -#if XE_DEBUG > 2 - if (i) - printf(":%x", addr[i]); - else - device_printf(scp->dev, "individual addresses %d: %x", slot, addr[0]); + } +#if XE_DEBUG > 1 + printf("\n"); #endif +} - if (byte > 15) { - page++; - byte = 8; - XE_SELECT_PAGE(page); - } - if (scp->mohawk) - XE_OUTB(byte, addr[5 - i]); +/* + * Set the appropriate bit in the multicast hash table for the supplied + * Ethernet multicast address addr. Assumes that addr is really a multicast + * address. + */ +static void +xe_set_hash(struct xe_softc* scp, u_int8_t* addr) { + u_int32_t crc = 0xffffffff; + u_int8_t bit, byte, crc31, idx; + unsigned i, j; + + /* Compute CRC of the address -- standard Ethernet CRC function */ + for (i = 0; i < 6; i++) { + byte = addr[i]; + for (j = 1; j <= 8; j++) { + if (crc & 0x80000000) + crc31 = 0x01; else - XE_OUTB(byte, addr[i]); + crc31 = 0; + bit = crc31 ^ (byte & 0x01); + crc <<= 1; + byte >>= 1; + if (bit) + crc = (crc ^ XE_CRC_POLY)|1; } -#if XE_DEBUG > 2 - printf("\n"); + } + +#if XE_DEBUG > 1 + device_printf(scp->dev, "set_hash: CRC = 0x%08x\n", crc); #endif + + /* Hash table index = 6 msbs of CRC, reversed */ + for (i = 0, idx = 0; i < 6; i++) { + idx >>= 1; + if (crc & 0x80000000) { + idx |= 0x20; + } + crc <<= 1; } - XE_SELECT_PAGE(0); + /* Top 3 bits of idx give register - 8, bottom 3 give bit within register */ + byte = idx >> 3 | 0x08; + bit = 0x01 << (idx & 0x07); + +#if XE_DEBUG > 1 + device_printf(scp->dev, "set_hash: idx = 0x%02x, byte = 0x%02x, bit = 0x%02x\n", idx, byte, bit); +#endif + + XE_SELECT_PAGE(0x58); + XE_OUTB(byte, XE_INB(byte) | bit); } @@ -1395,27 +1518,34 @@ xe_setaddrs(struct xe_softc *scp) { */ static int xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) { - struct mbuf *mbp2; - u_int16_t len, pad, free; + unsigned len, pad; + unsigned char wantbyte; u_int8_t *data; - u_int8_t savebyte[2], wantbyte; + u_int8_t savebyte[2]; /* Get total packet length */ - for (len = 0, mbp2 = mbp; mbp2 != NULL; len += mbp2->m_len, mbp2 = mbp2->m_next); + if (mbp->m_flags & M_PKTHDR) + len = mbp->m_pkthdr.len; + else { + struct mbuf* mbp2 = mbp; + for (len = 0; mbp2 != NULL; len += mbp2->m_len, mbp2 = mbp2->m_next); + } + +#if XE_DEBUG > 2 + device_printf(scp->dev, "pio_write_packet: len = %u\n", len); +#endif /* Packets < minimum length may need to be padded out */ pad = 0; - if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) { - pad = (ETHER_MIN_LEN - ETHER_CRC_LEN - len + 1) >> 1; - len = ETHER_MIN_LEN - ETHER_CRC_LEN; + if (len < scp->tx_min) { + pad = scp->tx_min - len; + len = scp->tx_min; } /* Check transmit buffer space */ XE_SELECT_PAGE(0); - XE_OUTW(XE_TRS, len+2); - free = XE_INW(XE_TSO); - free &= 0x7fff; - if (free <= len + 2) + XE_OUTW(XE_TRS, len+2); /* Only effective on rev. 1 CE2 cards */ + if ((XE_INW(XE_TSO) & 0x7fff) <= len + 2) return 1; /* Send packet length to card */ @@ -1438,7 +1568,7 @@ xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) { } if (len > 1) { /* Output contiguous words */ bus_space_write_multi_2(scp->bst, scp->bsh, XE_EDP, (u_int16_t *) data, - len >> 1); + len >> 1); data += len & ~1; len &= 1; } @@ -1449,76 +1579,32 @@ xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) { } mbp = mbp->m_next; } - if (wantbyte) /* Last byte for odd-length packets */ - XE_OUTW(XE_EDP, *(u_short *)savebyte); /* - * For CE3 cards, just tell 'em to send -- apparently the card will pad out - * short packets with random cruft. Otherwise, write nonsense words to fill - * out the packet. I guess it is then sent automatically (?) + * Send last byte of odd-length packets + */ + if (wantbyte) + XE_OUTB(XE_EDP, savebyte[0]); + + /* + * Can just tell CE3 cards to send; short packets will be padded out with + * random cruft automatically. For CE2, manually pad the packet with + * garbage; it will be sent when the required number or bytes have been + * delivered to the card. */ if (scp->mohawk) - XE_OUTB(XE_CR, XE_CR_TX_PACKET|XE_CR_ENABLE_INTR); - else + XE_OUTB(XE_CR, XE_CR_TX_PACKET | XE_CR_RESTART_TX | XE_CR_ENABLE_INTR); + else if (pad > 0) { + if (pad & 0x01) + XE_OUTB(XE_EDP, 0xaa); + pad >>= 1; while (pad > 0) { XE_OUTW(XE_EDP, 0xdead); pad--; } - - return 0; -} - -/* - * Compute the 32-bit Ethernet CRC for the given buffer. - */ -static u_int32_t -xe_compute_crc(u_int8_t *data, int len) { - u_int32_t crc = 0xffffffff; - u_int32_t poly = 0x04c11db6; - u_int8_t current, crc31, bit; - int i, k; - - for (i = 0; i < len; i++) { - current = data[i]; - for (k = 1; k <= 8; k++) { - if (crc & 0x80000000) { - crc31 = 0x01; - } - else { - crc31 = 0; - } - bit = crc31 ^ (current & 0x01); - crc <<= 1; - current >>= 1; - if (bit) { - crc = (crc ^ poly)|1; - } - } } - return crc; -} - -/* - * Convert a CRC into an index into the multicast hash table. What we do is - * take the most-significant 6 bits of the CRC, reverse them, and use that as - * the bit number in the hash table. Bits 5:3 of the result give the byte - * within the table (0-7); bits 2:0 give the bit number within that byte (also - * 0-7), ie. the number of shifts needed to get it into the lsb position. - */ -static int -xe_compute_hashbit(u_int32_t crc) { - u_int8_t hashbit = 0; - int i; - - for (i = 0; i < 6; i++) { - hashbit >>= 1; - if (crc & 0x80000000) { - hashbit &= 0x80; - } - crc <<= 1; - } - return (hashbit >> 2); + return 0; } @@ -1788,7 +1874,7 @@ xe_phy_writereg(struct xe_softc *scp, u_int16_t reg, u_int16_t data) { } -#ifdef XE_DEBUG +#if XE_DEBUG > 2 /* * A bit of debugging code. */ @@ -1853,13 +1939,18 @@ int xe_activate(device_t dev) { struct xe_softc *sc = device_get_softc(dev); - int start, err; + int start, err, i; - if (!sc->dingo) { +#if XE_DEBUG > 1 + device_printf(dev, "activate\n"); +#endif + + if (!sc->modem) { sc->port_rid = 0; /* 0 is managed by pccard */ sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port_rid, 0, ~0, 16, RF_ACTIVE); - } else { + } + else if (sc->dingo) { /* * Find a 16 byte aligned ioport for the card. */ @@ -1880,12 +1971,59 @@ xe_activate(device_t dev) sc->port_res); start = (rman_get_start(sc->port_res) + 15) & ~0xf; } while (1); +#if XE_DEBUG > 1 + device_printf(dev, "RealPort port 0x%0lx, size 0x%0lx\n", + bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), + bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid)); +#endif /* XE_DEBUG */ + } + else if (sc->ce2) { + /* + * Find contiguous I/O port for the Ethernet function on CEM2 and + * CEM3 cards. We allocate window 0 wherever pccard has decided + * it should be, then find an available window adjacent to it for + * the second function. Not sure that both windows are actually + * needed. + */ +#if XE_DEBUG > 0 + device_printf(dev, "Finding I/O port for CEM2/CEM3\n"); +#endif + sc->ce2_port_rid = 0; /* 0 is managed by pccard */ + sc->ce2_port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sc->ce2_port_rid, 0, ~0, + 8, RF_ACTIVE); + if (!sc->ce2_port_res) { +#if XE_DEBUG > 0 + device_printf(dev, "Cannot allocate I/O port for modem\n"); +#endif + return ENOMEM; + } + + sc->port_rid = 1; + start = bus_get_resource_start(dev, SYS_RES_IOPORT, + sc->ce2_port_rid); + for (i = 0; i < 2; i++) { + start += (i == 0 ? 8 : -24); + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, + &sc->port_rid, start, + start + 18, 18, RF_ACTIVE); + if (sc->port_res == 0) + continue; /* Failed, try again if possible */ + if (bus_get_resource_start(dev, SYS_RES_IOPORT, + sc->port_rid) == start) + break; /* Success! */ + + bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, + sc->port_res); + sc->port_res = 0; + } #if XE_DEBUG > 2 - device_printf(dev, "port 0x%0lx, size 0x%0lx\n", + device_printf(dev, "CEM2/CEM3 port 0x%0lx, size 0x%0lx\n", bus_get_resource_start(dev, SYS_RES_IOPORT, sc->port_rid), bus_get_resource_count(dev, SYS_RES_IOPORT, sc->port_rid)); #endif /* XE_DEBUG */ } + if (!sc->port_res) { #if XE_DEBUG > 0 device_printf(dev, "Cannot allocate ioport\n"); @@ -1919,6 +2057,11 @@ xe_deactivate(device_t dev) { struct xe_softc *sc = device_get_softc(dev); +#if XE_DEBUG > 1 + device_printf(dev, "deactivate\n"); +#endif + xe_disable_intr(sc); + if (sc->intrhand) bus_teardown_intr(dev, sc->irq_res, sc->intrhand); sc->intrhand = 0; @@ -1926,6 +2069,10 @@ xe_deactivate(device_t dev) bus_release_resource(dev, SYS_RES_IOPORT, sc->port_rid, sc->port_res); sc->port_res = 0; + if (sc->ce2_port_res) + bus_release_resource(dev, SYS_RES_IOPORT, sc->ce2_port_rid, + sc->ce2_port_res); + sc->ce2_port_res = 0; if (sc->irq_res) bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq_res); |