Index: head/sys/dev/dc/if_dc.c =================================================================== --- head/sys/dev/dc/if_dc.c (revision 131252) +++ head/sys/dev/dc/if_dc.c (revision 131253) @@ -1,3875 +1,3815 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143 * series chips and several workalikes including the following: * * Macronix 98713/98715/98725/98727/98732 PMAC (www.macronix.com) * Macronix/Lite-On 82c115 PNIC II (www.macronix.com) * Lite-On 82c168/82c169 PNIC (www.litecom.com) * ASIX Electronics AX88140A (www.asix.com.tw) * ASIX Electronics AX88141 (www.asix.com.tw) * ADMtek AL981 (www.admtek.com.tw) * ADMtek AN985 (www.admtek.com.tw) * Netgear FA511 (www.netgear.com) Appears to be rebadged ADMTek AN985 * Davicom DM9100, DM9102, DM9102A (www.davicom8.com) * Accton EN1217 (www.accton.com) * Xircom X3201 (www.xircom.com) * Abocom FE2500 * Conexant LANfinity (www.conexant.com) * 3Com OfficeConnect 10/100B 3CSOHO100B (www.3com.com) * * Datasheets for the 21143 are available at developer.intel.com. * Datasheets for the clone parts can be found at their respective sites. * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.) * The PNIC II is essentially a Macronix 98715A chip; the only difference * worth noting is that its multicast hash table is only 128 bits wide * instead of 512. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The Intel 21143 is the successor to the DEC 21140. It is basically * the same as the 21140 but with a few new features. The 21143 supports * three kinds of media attachments: * * o MII port, for 10Mbps and 100Mbps support and NWAY * autonegotiation provided by an external PHY. * o SYM port, for symbol mode 100Mbps support. * o 10baseT port. * o AUI/BNC port. * * The 100Mbps SYM port and 10baseT port can be used together in * combination with the internal NWAY support to create a 10/100 * autosensing configuration. * * Note that not all tulip workalikes are handled in this driver: we only * deal with those which are relatively well behaved. The Winbond is * handled separately due to its different register offsets and the * special handling needed for its various bugs. The PNIC is handled * here, but I'm not thrilled about it. * * All of the workalike chips use some form of MII transceiver support * with the exception of the Macronix chips, which also have a SYM port. * The ASIX AX88140A is also documented to have a SYM port, but all * the cards I've seen use an MII transceiver, probably because the * AX88140A doesn't support internal NWAY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DC_USEIOSPACE #ifdef __alpha__ #define SRM_MEDIA #endif #include #ifdef __sparc64__ #include #include #endif MODULE_DEPEND(dc, pci, 1, 1, 1); MODULE_DEPEND(dc, ether, 1, 1, 1); MODULE_DEPEND(dc, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Various supported device vendors/types and their names. */ static struct dc_type dc_devs[] = { { DC_VENDORID_DEC, DC_DEVICEID_21143, "Intel 21143 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009, "Davicom DM9009 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100, "Davicom DM9100 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, "Davicom DM9102 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, "Davicom DM9102A 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981, "ADMtek AL981 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985, "ADMtek AN985 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9511, "ADMtek ADM9511 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9513, "ADMtek ADM9513 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_FA511, "Netgear FA511 10/100BaseTX" }, { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, "ASIX AX88140A 10/100BaseTX" }, { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, "ASIX AX88141 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98713, "Macronix 98713 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98713, "Macronix 98713A 10/100BaseTX" }, { DC_VENDORID_CP, DC_DEVICEID_98713_CP, "Compex RL100-TX 10/100BaseTX" }, { DC_VENDORID_CP, DC_DEVICEID_98713_CP, "Compex RL100-TX 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98715/98715A 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98715AEC-C 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98725 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98727, "Macronix 98727/98732 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C115, "LC82C115 PNIC II 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C168, "82c168 PNIC 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C168, "82c169 PNIC 10/100BaseTX" }, { DC_VENDORID_ACCTON, DC_DEVICEID_EN1217, "Accton EN1217 10/100BaseTX" }, { DC_VENDORID_ACCTON, DC_DEVICEID_EN2242, "Accton EN2242 MiniPCI 10/100BaseTX" }, { DC_VENDORID_XIRCOM, DC_DEVICEID_X3201, "Xircom X3201 10/100BaseTX" }, { DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500, "Abocom FE2500 10/100BaseTX" }, { DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500MX, "Abocom FE2500MX 10/100BaseTX" }, { DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112, "Conexant LANfinity MiniPCI 10/100BaseTX" }, { DC_VENDORID_HAWKING, DC_DEVICEID_HAWKING_PN672TX, "Hawking CB102 CardBus 10/100" }, { DC_VENDORID_PLANEX, DC_DEVICEID_FNW3602T, "PlaneX FNW-3602-T CardBus 10/100" }, { DC_VENDORID_3COM, DC_DEVICEID_3CSOHOB, "3Com OfficeConnect 10/100B" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN120, "Microsoft MN-120 CardBus 10/100" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130, "Microsoft MN-130 10/100" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130_FAKE, "Microsoft MN-130 10/100" }, { 0, 0, NULL } }; static int dc_probe (device_t); static int dc_attach (device_t); static int dc_detach (device_t); static int dc_suspend (device_t); static int dc_resume (device_t); -#ifndef BURN_BRIDGES -static void dc_acpi (device_t); -#endif static struct dc_type *dc_devtype (device_t); static int dc_newbuf (struct dc_softc *, int, int); static int dc_encap (struct dc_softc *, struct mbuf **); static void dc_pnic_rx_bug_war (struct dc_softc *, int); static int dc_rx_resync (struct dc_softc *); static void dc_rxeof (struct dc_softc *); static void dc_txeof (struct dc_softc *); static void dc_tick (void *); static void dc_tx_underrun (struct dc_softc *); static void dc_intr (void *); static void dc_start (struct ifnet *); static int dc_ioctl (struct ifnet *, u_long, caddr_t); static void dc_init (void *); static void dc_stop (struct dc_softc *); static void dc_watchdog (struct ifnet *); static void dc_shutdown (device_t); static int dc_ifmedia_upd (struct ifnet *); static void dc_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void dc_delay (struct dc_softc *); static void dc_eeprom_idle (struct dc_softc *); static void dc_eeprom_putbyte (struct dc_softc *, int); static void dc_eeprom_getword (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_getword_pnic (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_getword_xircom (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_width (struct dc_softc *); static void dc_read_eeprom (struct dc_softc *, caddr_t, int, int, int); static void dc_mii_writebit (struct dc_softc *, int); static int dc_mii_readbit (struct dc_softc *); static void dc_mii_sync (struct dc_softc *); static void dc_mii_send (struct dc_softc *, u_int32_t, int); static int dc_mii_readreg (struct dc_softc *, struct dc_mii_frame *); static int dc_mii_writereg (struct dc_softc *, struct dc_mii_frame *); static int dc_miibus_readreg (device_t, int, int); static int dc_miibus_writereg (device_t, int, int, int); static void dc_miibus_statchg (device_t); static void dc_miibus_mediainit (device_t); static void dc_setcfg (struct dc_softc *, int); static uint32_t dc_mchash_le (struct dc_softc *, const uint8_t *); static uint32_t dc_mchash_be (const uint8_t *); static void dc_setfilt_21143 (struct dc_softc *); static void dc_setfilt_asix (struct dc_softc *); static void dc_setfilt_admtek (struct dc_softc *); static void dc_setfilt_xircom (struct dc_softc *); static void dc_setfilt (struct dc_softc *); static void dc_reset (struct dc_softc *); static int dc_list_rx_init (struct dc_softc *); static int dc_list_tx_init (struct dc_softc *); static void dc_read_srom (struct dc_softc *, int); static void dc_parse_21143_srom (struct dc_softc *); static void dc_decode_leaf_sia (struct dc_softc *, struct dc_eblock_sia *); static void dc_decode_leaf_mii (struct dc_softc *, struct dc_eblock_mii *); static void dc_decode_leaf_sym (struct dc_softc *, struct dc_eblock_sym *); static void dc_apply_fixup (struct dc_softc *, int); static void dc_dma_map_txbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); static void dc_dma_map_rxbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); #ifdef DC_USEIOSPACE #define DC_RES SYS_RES_IOPORT #define DC_RID DC_PCI_CFBIO #else #define DC_RES SYS_RES_MEMORY #define DC_RID DC_PCI_CFBMA #endif static device_method_t dc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dc_probe), DEVMETHOD(device_attach, dc_attach), DEVMETHOD(device_detach, dc_detach), DEVMETHOD(device_suspend, dc_suspend), DEVMETHOD(device_resume, dc_resume), DEVMETHOD(device_shutdown, dc_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, dc_miibus_readreg), DEVMETHOD(miibus_writereg, dc_miibus_writereg), DEVMETHOD(miibus_statchg, dc_miibus_statchg), DEVMETHOD(miibus_mediainit, dc_miibus_mediainit), { 0, 0 } }; static driver_t dc_driver = { "dc", dc_methods, sizeof(struct dc_softc) }; static devclass_t dc_devclass; #ifdef __i386__ static int dc_quick = 1; SYSCTL_INT(_hw, OID_AUTO, dc_quick, CTLFLAG_RW, &dc_quick, 0, "do not m_devget() in dc driver"); #endif DRIVER_MODULE(dc, cardbus, dc_driver, dc_devclass, 0, 0); DRIVER_MODULE(dc, pci, dc_driver, dc_devclass, 0, 0); DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0); #define DC_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) #define DC_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x)) #define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x)) #define IS_MPSAFE 0 static void dc_delay(struct dc_softc *sc) { int idx; for (idx = (300 / 33) + 1; idx > 0; idx--) CSR_READ_4(sc, DC_BUSCTL); } static void dc_eeprom_width(struct dc_softc *sc) { int i; /* Force EEPROM to idle state. */ dc_eeprom_idle(sc); /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); for (i = 3; i--;) { if (6 & (1 << i)) DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); else DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } for (i = 1; i <= 12; i++) { DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); if (!(CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)) { DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); break; } DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); if (i < 4 || i > 12) sc->dc_romwidth = 6; else sc->dc_romwidth = i; /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); } static void dc_eeprom_idle(struct dc_softc *sc) { int i; CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); for (i = 0; i < 25; i++) { DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); CSR_WRITE_4(sc, DC_SIO, 0x00000000); } /* * Send a read command and address to the EEPROM, check for ACK. */ static void dc_eeprom_putbyte(struct dc_softc *sc, int addr) { int d, i; d = DC_EECMD_READ >> 6; for (i = 3; i--; ) { if (d & (1 << i)) DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); else DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } /* * Feed in each bit and strobe the clock. */ for (i = sc->dc_romwidth; i--;) { if (addr & (1 << i)) { SIO_SET(DC_SIO_EE_DATAIN); } else { SIO_CLR(DC_SIO_EE_DATAIN); } dc_delay(sc); SIO_SET(DC_SIO_EE_CLK); dc_delay(sc); SIO_CLR(DC_SIO_EE_CLK); dc_delay(sc); } } /* * Read a word of data stored in the EEPROM at address 'addr.' * The PNIC 82c168/82c169 has its own non-standard way to read * the EEPROM. */ static void dc_eeprom_getword_pnic(struct dc_softc *sc, int addr, u_int16_t *dest) { int i; u_int32_t r; CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ | addr); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(1); r = CSR_READ_4(sc, DC_SIO); if (!(r & DC_PN_SIOCTL_BUSY)) { *dest = (u_int16_t)(r & 0xFFFF); return; } } } /* * Read a word of data stored in the EEPROM at address 'addr.' * The Xircom X3201 has its own non-standard way to read * the EEPROM, too. */ static void dc_eeprom_getword_xircom(struct dc_softc *sc, int addr, u_int16_t *dest) { SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); addr *= 2; CSR_WRITE_4(sc, DC_ROM, addr | 0x160); *dest = (u_int16_t)CSR_READ_4(sc, DC_SIO) & 0xff; addr += 1; CSR_WRITE_4(sc, DC_ROM, addr | 0x160); *dest |= ((u_int16_t)CSR_READ_4(sc, DC_SIO) & 0xff) << 8; SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void dc_eeprom_getword(struct dc_softc *sc, int addr, u_int16_t *dest) { int i; u_int16_t word = 0; /* Force EEPROM to idle state. */ dc_eeprom_idle(sc); /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); /* * Send address of word we want to read. */ dc_eeprom_putbyte(sc, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { SIO_SET(DC_SIO_EE_CLK); dc_delay(sc); if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT) word |= i; dc_delay(sc); SIO_CLR(DC_SIO_EE_CLK); dc_delay(sc); } /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void dc_read_eeprom(struct dc_softc *sc, caddr_t dest, int off, int cnt, int swap) { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { if (DC_IS_PNIC(sc)) dc_eeprom_getword_pnic(sc, off + i, &word); else if (DC_IS_XIRCOM(sc)) dc_eeprom_getword_xircom(sc, off + i, &word); else dc_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } } /* * The following two routines are taken from the Macronix 98713 * Application Notes pp.19-21. */ /* * Write a bit to the MII bus. */ static void dc_mii_writebit(struct dc_softc *sc, int bit) { if (bit) CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE | DC_SIO_MII_DATAOUT); else CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); } /* * Read a bit from the MII bus. */ static int dc_mii_readbit(struct dc_softc *sc) { CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ | DC_SIO_MII_DIR); CSR_READ_4(sc, DC_SIO); DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN) return (1); return (0); } /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void dc_mii_sync(struct dc_softc *sc) { int i; CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); for (i = 0; i < 32; i++) dc_mii_writebit(sc, 1); } /* * Clock a series of bits through the MII. */ static void dc_mii_send(struct dc_softc *sc, u_int32_t bits, int cnt) { int i; for (i = (0x1 << (cnt - 1)); i; i >>= 1) dc_mii_writebit(sc, bits & i); } /* * Read an PHY register through the MII. */ static int dc_mii_readreg(struct dc_softc *sc, struct dc_mii_frame *frame) { int i, ack; DC_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = DC_MII_STARTDELIM; frame->mii_opcode = DC_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Sync the PHYs. */ dc_mii_sync(sc); /* * Send command/address info. */ dc_mii_send(sc, frame->mii_stdelim, 2); dc_mii_send(sc, frame->mii_opcode, 2); dc_mii_send(sc, frame->mii_phyaddr, 5); dc_mii_send(sc, frame->mii_regaddr, 5); #ifdef notdef /* Idle bit */ dc_mii_writebit(sc, 1); dc_mii_writebit(sc, 0); #endif /* Check for ack. */ ack = dc_mii_readbit(sc); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for (i = 0; i < 16; i++) dc_mii_readbit(sc); goto fail; } for (i = 0x8000; i; i >>= 1) { if (!ack) { if (dc_mii_readbit(sc)) frame->mii_data |= i; } } fail: dc_mii_writebit(sc, 0); dc_mii_writebit(sc, 0); DC_UNLOCK(sc); if (ack) return (1); return (0); } /* * Write to a PHY register through the MII. */ static int dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame) { DC_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = DC_MII_STARTDELIM; frame->mii_opcode = DC_MII_WRITEOP; frame->mii_turnaround = DC_MII_TURNAROUND; /* * Sync the PHYs. */ dc_mii_sync(sc); dc_mii_send(sc, frame->mii_stdelim, 2); dc_mii_send(sc, frame->mii_opcode, 2); dc_mii_send(sc, frame->mii_phyaddr, 5); dc_mii_send(sc, frame->mii_regaddr, 5); dc_mii_send(sc, frame->mii_turnaround, 2); dc_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ dc_mii_writebit(sc, 0); dc_mii_writebit(sc, 0); DC_UNLOCK(sc); return (0); } static int dc_miibus_readreg(device_t dev, int phy, int reg) { struct dc_mii_frame frame; struct dc_softc *sc; int i, rval, phy_reg = 0; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); /* * Note: both the AL981 and AN985 have internal PHYs, * however the AL981 provides direct access to the PHY * registers while the AN985 uses a serial MII interface. * The AN985's MII interface is also buggy in that you * can read from any MII address (0 to 31), but only address 1 * behaves normally. To deal with both cases, we pretend * that the PHY is at MII address 1. */ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return (0); /* * Note: the ukphy probes of the RS7112 report a PHY at * MII address 0 (possibly HomePNA?) and 1 (ethernet) * so we only respond to correct one. */ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) return (0); if (sc->dc_pmode != DC_PMODE_MII) { if (phy == (MII_NPHY - 1)) { switch (reg) { case MII_BMSR: /* * Fake something to make the probe * code think there's a PHY here. */ return (BMSR_MEDIAMASK); break; case MII_PHYIDR1: if (DC_IS_PNIC(sc)) return (DC_VENDORID_LO); return (DC_VENDORID_DEC); break; case MII_PHYIDR2: if (DC_IS_PNIC(sc)) return (DC_DEVICEID_82C168); return (DC_DEVICEID_21143); break; default: return (0); break; } } else return (0); } if (DC_IS_PNIC(sc)) { CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ | (phy << 23) | (reg << 18)); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(1); rval = CSR_READ_4(sc, DC_PN_MII); if (!(rval & DC_PN_MII_BUSY)) { rval &= 0xFFFF; return (rval == 0xFFFF ? 0 : rval); } } return (0); } if (DC_IS_COMET(sc)) { switch (reg) { case MII_BMCR: phy_reg = DC_AL_BMCR; break; case MII_BMSR: phy_reg = DC_AL_BMSR; break; case MII_PHYIDR1: phy_reg = DC_AL_VENID; break; case MII_PHYIDR2: phy_reg = DC_AL_DEVID; break; case MII_ANAR: phy_reg = DC_AL_ANAR; break; case MII_ANLPAR: phy_reg = DC_AL_LPAR; break; case MII_ANER: phy_reg = DC_AL_ANER; break; default: printf("dc%d: phy_read: bad phy register %x\n", sc->dc_unit, reg); return (0); break; } rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; if (rval == 0xFFFF) return (0); return (rval); } frame.mii_phyaddr = phy; frame.mii_regaddr = reg; if (sc->dc_type == DC_TYPE_98713) { phy_reg = CSR_READ_4(sc, DC_NETCFG); CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); } dc_mii_readreg(sc, &frame); if (sc->dc_type == DC_TYPE_98713) CSR_WRITE_4(sc, DC_NETCFG, phy_reg); return (frame.mii_data); } static int dc_miibus_writereg(device_t dev, int phy, int reg, int data) { struct dc_softc *sc; struct dc_mii_frame frame; int i, phy_reg = 0; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return (0); if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) return (0); if (DC_IS_PNIC(sc)) { CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE | (phy << 23) | (reg << 10) | data); for (i = 0; i < DC_TIMEOUT; i++) { if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY)) break; } return (0); } if (DC_IS_COMET(sc)) { switch (reg) { case MII_BMCR: phy_reg = DC_AL_BMCR; break; case MII_BMSR: phy_reg = DC_AL_BMSR; break; case MII_PHYIDR1: phy_reg = DC_AL_VENID; break; case MII_PHYIDR2: phy_reg = DC_AL_DEVID; break; case MII_ANAR: phy_reg = DC_AL_ANAR; break; case MII_ANLPAR: phy_reg = DC_AL_LPAR; break; case MII_ANER: phy_reg = DC_AL_ANER; break; default: printf("dc%d: phy_write: bad phy register %x\n", sc->dc_unit, reg); return (0); break; } CSR_WRITE_4(sc, phy_reg, data); return (0); } frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; if (sc->dc_type == DC_TYPE_98713) { phy_reg = CSR_READ_4(sc, DC_NETCFG); CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); } dc_mii_writereg(sc, &frame); if (sc->dc_type == DC_TYPE_98713) CSR_WRITE_4(sc, DC_NETCFG, phy_reg); return (0); } static void dc_miibus_statchg(device_t dev) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = device_get_softc(dev); if (DC_IS_ADMTEK(sc)) return; mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { dc_setcfg(sc, ifm->ifm_media); sc->dc_if_media = ifm->ifm_media; } else { dc_setcfg(sc, mii->mii_media_active); sc->dc_if_media = mii->mii_media_active; } } /* * Special support for DM9102A cards with HomePNA PHYs. Note: * with the Davicom DM9102A/DM9801 eval board that I have, it seems * to be impossible to talk to the management interface of the DM9801 * PHY (its MDIO pin is not connected to anything). Consequently, * the driver has to just 'know' about the additional mode and deal * with it itself. *sigh* */ static void dc_miibus_mediainit(device_t dev) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; int rev; rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; sc = device_get_softc(dev); mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && rev >= DC_REVISION_DM9102A) ifmedia_add(ifm, IFM_ETHER | IFM_HPNA_1, 0, NULL); } #define DC_BITS_512 9 #define DC_BITS_128 7 #define DC_BITS_64 6 static uint32_t dc_mchash_le(struct dc_softc *sc, const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); /* * The hash table on the PNIC II and the MX98715AEC-C/D/E * chips is only 128 bits wide. */ if (sc->dc_flags & DC_128BIT_HASH) return (crc & ((1 << DC_BITS_128) - 1)); /* The hash table on the MX98715BEC is only 64 bits wide. */ if (sc->dc_flags & DC_64BIT_HASH) return (crc & ((1 << DC_BITS_64) - 1)); /* Xircom's hash filtering table is different (read: weird) */ /* Xircom uses the LEAST significant bits */ if (DC_IS_XIRCOM(sc)) { if ((crc & 0x180) == 0x180) return ((crc & 0x0F) + (crc & 0x70) * 3 + (14 << 4)); else return ((crc & 0x1F) + ((crc >> 1) & 0xF0) * 3 + (12 << 4)); } return (crc & ((1 << DC_BITS_512) - 1)); } /* * Calculate CRC of a multicast group address, return the lower 6 bits. */ static uint32_t dc_mchash_be(const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_be(addr, ETHER_ADDR_LEN); /* Return the filter bit position. */ return ((crc >> 26) & 0x0000003F); } /* * 21143-style RX filter setup routine. Filter programming is done by * downloading a special setup frame into the TX engine. 21143, Macronix, * PNIC, PNIC II and Davicom chips are programmed this way. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */ static void dc_setfilt_21143(struct dc_softc *sc) { struct dc_desc *sframe; u_int32_t h, *sp; struct ifmultiaddr *ifma; struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; i = sc->dc_cdata.dc_tx_prod; DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); sc->dc_cdata.dc_tx_cnt++; sframe = &sc->dc_ldata->dc_tx_list[i]; sp = sc->dc_cdata.dc_sbuf; bzero(sp, DC_SFRAME_LEN); sframe->dc_data = htole32(sc->dc_saddr); sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | DC_FILTER_HASHPERF | DC_TXCTL_FINT); sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= htole32(1 << (h & 0xF)); } if (ifp->if_flags & IFF_BROADCAST) { h = dc_mchash_le(sc, ifp->if_broadcastaddr); sp[h >> 4] |= htole32(1 << (h & 0xF)); } /* Set our MAC address */ sp[39] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[0]); sp[40] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[1]); sp[41] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[2]); sframe->dc_status = htole32(DC_TXSTAT_OWN); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * The PNIC takes an exceedingly long time to process its * setup frame; wait 10ms after posting the setup frame * before proceeding, just so it has time to swallow its * medicine. */ DELAY(10000); ifp->if_timer = 5; } static void dc_setfilt_admtek(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int h = 0; u_int32_t hashes[2] = { 0, 0 }; ifp = &sc->arpcom.ac_if; /* Init our MAC address. */ CSR_WRITE_4(sc, DC_AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); CSR_WRITE_4(sc, DC_AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); /* First, zot all the existing hash bits. */ CSR_WRITE_4(sc, DC_AL_MAR0, 0); CSR_WRITE_4(sc, DC_AL_MAR1, 0); /* * If we're already in promisc or allmulti mode, we * don't have to bother programming the multicast filter. */ if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) return; /* Now program new ones. */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (DC_IS_CENTAUR(sc)) h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); else h = dc_mchash_be( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]); CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]); } static void dc_setfilt_asix(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int h = 0; u_int32_t hashes[2] = { 0, 0 }; ifp = &sc->arpcom.ac_if; /* Init our MAC address */ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); /* * The ASIX chip has a special bit to enable reception * of broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); else DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); /* * If we're already in promisc or allmulti mode, we * don't have to bother programming the multicast filter. */ if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) return; /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); } static void dc_setfilt_xircom(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; struct dc_desc *sframe; u_int32_t h, *sp; int i; ifp = &sc->arpcom.ac_if; DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)); i = sc->dc_cdata.dc_tx_prod; DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); sc->dc_cdata.dc_tx_cnt++; sframe = &sc->dc_ldata->dc_tx_list[i]; sp = sc->dc_cdata.dc_sbuf; bzero(sp, DC_SFRAME_LEN); sframe->dc_data = htole32(sc->dc_saddr); sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | DC_FILTER_HASHPERF | DC_TXCTL_FINT); sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= htole32(1 << (h & 0xF)); } if (ifp->if_flags & IFF_BROADCAST) { h = dc_mchash_le(sc, ifp->if_broadcastaddr); sp[h >> 4] |= htole32(1 << (h & 0xF)); } /* Set our MAC address */ sp[0] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[0]); sp[1] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[1]); sp[2] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[2]); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); ifp->if_flags |= IFF_RUNNING; sframe->dc_status = htole32(DC_TXSTAT_OWN); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * Wait some time... */ DELAY(1000); ifp->if_timer = 5; } static void dc_setfilt(struct dc_softc *sc) { if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) || DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc) || DC_IS_CONEXANT(sc)) dc_setfilt_21143(sc); if (DC_IS_ASIX(sc)) dc_setfilt_asix(sc); if (DC_IS_ADMTEK(sc)) dc_setfilt_admtek(sc); if (DC_IS_XIRCOM(sc)) dc_setfilt_xircom(sc); } /* * In order to fiddle with the 'full-duplex' and '100Mbps' bits in * the netconfig register, we first have to put the transmit and/or * receive logic in the idle state. */ static void dc_setcfg(struct dc_softc *sc, int media) { int i, restart = 0, watchdogreg; u_int32_t isr; if (IFM_SUBTYPE(media) == IFM_NONE) return; if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)) { restart = 1; DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)); for (i = 0; i < DC_TIMEOUT; i++) { isr = CSR_READ_4(sc, DC_ISR); if (isr & DC_ISR_TX_IDLE && ((isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED || (isr & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT)) break; DELAY(10); } if (i == DC_TIMEOUT) printf("dc%d: failed to force tx and " "rx to idle state\n", sc->dc_unit); } if (IFM_SUBTYPE(media) == IFM_100_TX) { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); if (sc->dc_pmode == DC_PMODE_MII) { if (DC_IS_INTEL(sc)) { /* There's a write enable bit here that reads as 1. */ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); watchdogreg &= ~DC_WDOG_CTLWREN; watchdogreg |= DC_WDOG_JABBERDIS; CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); } else { DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); } DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_PORTSEL | DC_NETCFG_SCRAMBLER)); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_SCRAMBLER)); if (!DC_IS_DAVICOM(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, IFM_AUTO); } else { if (DC_IS_PNIC(sc)) { DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL); DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); } DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, (media & IFM_GMASK) == IFM_FDX ? IFM_100_TX | IFM_FDX : IFM_100_TX); } } if (IFM_SUBTYPE(media) == IFM_10_T) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); if (sc->dc_pmode == DC_PMODE_MII) { /* There's a write enable bit here that reads as 1. */ if (DC_IS_INTEL(sc)) { watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); watchdogreg &= ~DC_WDOG_CTLWREN; watchdogreg |= DC_WDOG_JABBERDIS; CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); } else { DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); } DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_PORTSEL | DC_NETCFG_SCRAMBLER)); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); if (!DC_IS_DAVICOM(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, IFM_AUTO); } else { if (DC_IS_PNIC(sc)) { DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL); DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); } DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); if (DC_IS_INTEL(sc)) { DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if ((media & IFM_GMASK) == IFM_FDX) DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D); else DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F); DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); DC_CLRBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); dc_apply_fixup(sc, (media & IFM_GMASK) == IFM_FDX ? IFM_10_T | IFM_FDX : IFM_10_T); DELAY(20000); } } } /* * If this is a Davicom DM9102A card with a DM9801 HomePNA * PHY and we want HomePNA mode, set the portsel bit to turn * on the external MII port. */ if (DC_IS_DAVICOM(sc)) { if (IFM_SUBTYPE(media) == IFM_HPNA_1) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); sc->dc_link = 1; } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); } } if ((media & IFM_GMASK) == IFM_FDX) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); } if (restart) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON | DC_NETCFG_RX_ON); } static void dc_reset(struct dc_softc *sc) { int i; DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET)) break; } if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc) || DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) { DELAY(10000); DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); i = 0; } if (i == DC_TIMEOUT) printf("dc%d: reset never completed!\n", sc->dc_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); CSR_WRITE_4(sc, DC_IMR, 0x00000000); CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000); CSR_WRITE_4(sc, DC_NETCFG, 0x00000000); /* * Bring the SIA out of reset. In some cases, it looks * like failing to unreset the SIA soon enough gets it * into a state where it will never come out of reset * until we reset the whole chip again. */ if (DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); CSR_WRITE_4(sc, DC_10BTCTRL, 0); CSR_WRITE_4(sc, DC_WATCHDOG, 0); } } static struct dc_type * dc_devtype(device_t dev) { struct dc_type *t; u_int32_t rev; t = dc_devs; while (t->dc_name != NULL) { if ((pci_get_vendor(dev) == t->dc_vid) && (pci_get_device(dev) == t->dc_did)) { /* Check the PCI revision */ rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; if (t->dc_did == DC_DEVICEID_98713 && rev >= DC_REVISION_98713A) t++; if (t->dc_did == DC_DEVICEID_98713_CP && rev >= DC_REVISION_98713A) t++; if (t->dc_did == DC_DEVICEID_987x5 && rev >= DC_REVISION_98715AEC_C) t++; if (t->dc_did == DC_DEVICEID_987x5 && rev >= DC_REVISION_98725) t++; if (t->dc_did == DC_DEVICEID_AX88140A && rev >= DC_REVISION_88141) t++; if (t->dc_did == DC_DEVICEID_82C168 && rev >= DC_REVISION_82C169) t++; if (t->dc_did == DC_DEVICEID_DM9102 && rev >= DC_REVISION_DM9102A) t++; /* * The Microsoft MN-130 has a device ID of 0x0002, * which happens to be the same as the PNIC 82c168. * To keep dc_attach() from getting confused, we * pretend its ID is something different. * XXX: ideally, dc_attach() should be checking * vendorid+deviceid together to avoid such * collisions. */ if (t->dc_vid == DC_VENDORID_MICROSOFT && t->dc_did == DC_DEVICEID_MSMN130) t++; return (t); } t++; } return (NULL); } /* * Probe for a 21143 or clone chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We do a little bit of extra work to identify the exact type of * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, * but different revision IDs. The same is true for 98715/98715A * chips and the 98725, as well as the ASIX and ADMtek chips. In some * cases, the exact chip revision affects driver behavior. */ static int dc_probe(device_t dev) { struct dc_type *t; t = dc_devtype(dev); if (t != NULL) { device_set_desc(dev, t->dc_name); return (0); } return (ENXIO); } -#ifndef BURN_BRIDGES static void -dc_acpi(device_t dev) -{ - int unit; - u_int32_t iobase, membase, irq; - - unit = device_get_unit(dev); - - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - /* Save important PCI config data. */ - iobase = pci_read_config(dev, DC_PCI_CFBIO, 4); - membase = pci_read_config(dev, DC_PCI_CFBMA, 4); - irq = pci_read_config(dev, DC_PCI_CFIT, 4); - - /* Reset the power state. */ - printf("dc%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, DC_PCI_CFBIO, iobase, 4); - pci_write_config(dev, DC_PCI_CFBMA, membase, 4); - pci_write_config(dev, DC_PCI_CFIT, irq, 4); - } -} -#endif - -static void dc_apply_fixup(struct dc_softc *sc, int media) { struct dc_mediainfo *m; u_int8_t *p; int i; u_int32_t reg; m = sc->dc_mi; while (m != NULL) { if (m->dc_media == media) break; m = m->dc_next; } if (m == NULL) return; for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) { reg = (p[0] | (p[1] << 8)) << 16; CSR_WRITE_4(sc, DC_WATCHDOG, reg); } for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) { reg = (p[0] | (p[1] << 8)) << 16; CSR_WRITE_4(sc, DC_WATCHDOG, reg); } } static void dc_decode_leaf_sia(struct dc_softc *sc, struct dc_eblock_sia *l) { struct dc_mediainfo *m; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); switch (l->dc_sia_code & ~DC_SIA_CODE_EXT) { case DC_SIA_CODE_10BT: m->dc_media = IFM_10_T; break; case DC_SIA_CODE_10BT_FDX: m->dc_media = IFM_10_T | IFM_FDX; break; case DC_SIA_CODE_10B2: m->dc_media = IFM_10_2; break; case DC_SIA_CODE_10B5: m->dc_media = IFM_10_5; break; default: break; } /* * We need to ignore CSR13, CSR14, CSR15 for SIA mode. * Things apparently already work for cards that do * supply Media Specific Data. */ if (l->dc_sia_code & DC_SIA_CODE_EXT) { m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_un.dc_sia_ext.dc_sia_gpio_ctl; } else { m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_un.dc_sia_noext.dc_sia_gpio_ctl; } m->dc_next = sc->dc_mi; sc->dc_mi = m; sc->dc_pmode = DC_PMODE_SIA; } static void dc_decode_leaf_sym(struct dc_softc *sc, struct dc_eblock_sym *l) { struct dc_mediainfo *m; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (l->dc_sym_code == DC_SYM_CODE_100BT) m->dc_media = IFM_100_TX; if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX) m->dc_media = IFM_100_TX | IFM_FDX; m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl; m->dc_next = sc->dc_mi; sc->dc_mi = m; sc->dc_pmode = DC_PMODE_SYM; } static void dc_decode_leaf_mii(struct dc_softc *sc, struct dc_eblock_mii *l) { struct dc_mediainfo *m; u_int8_t *p; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); /* We abuse IFM_AUTO to represent MII. */ m->dc_media = IFM_AUTO; m->dc_gp_len = l->dc_gpr_len; p = (u_int8_t *)l; p += sizeof(struct dc_eblock_mii); m->dc_gp_ptr = p; p += 2 * l->dc_gpr_len; m->dc_reset_len = *p; p++; m->dc_reset_ptr = p; m->dc_next = sc->dc_mi; sc->dc_mi = m; } static void dc_read_srom(struct dc_softc *sc, int bits) { int size; size = 2 << bits; sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT); dc_read_eeprom(sc, (caddr_t)sc->dc_srom, 0, (size / 2), 0); } static void dc_parse_21143_srom(struct dc_softc *sc) { struct dc_leaf_hdr *lhdr; struct dc_eblock_hdr *hdr; int have_mii, i, loff; char *ptr; have_mii = 0; loff = sc->dc_srom[27]; lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]); ptr = (char *)lhdr; ptr += sizeof(struct dc_leaf_hdr) - 1; /* * Look if we got a MII media block. */ for (i = 0; i < lhdr->dc_mcnt; i++) { hdr = (struct dc_eblock_hdr *)ptr; if (hdr->dc_type == DC_EBLOCK_MII) have_mii++; ptr += (hdr->dc_len & 0x7F); ptr++; } /* * Do the same thing again. Only use SIA and SYM media * blocks if no MII media block is available. */ ptr = (char *)lhdr; ptr += sizeof(struct dc_leaf_hdr) - 1; for (i = 0; i < lhdr->dc_mcnt; i++) { hdr = (struct dc_eblock_hdr *)ptr; switch (hdr->dc_type) { case DC_EBLOCK_MII: dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr); break; case DC_EBLOCK_SIA: if (! have_mii) dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr); break; case DC_EBLOCK_SYM: if (! have_mii) dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr); break; default: /* Don't care. Yet. */ break; } ptr += (hdr->dc_len & 0x7F); ptr++; } } static void dc_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { u_int32_t *paddr; KASSERT(nseg == 1, ("wrong number of segments, should be 1")); paddr = arg; *paddr = segs->ds_addr; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int dc_attach(device_t dev) { int tmp = 0; u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct dc_softc *sc; struct ifnet *ifp; u_int32_t revision; int unit, error = 0, rid, mac_offset; int i; u_int8_t *mac; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES + /* - * Handle power management nonsense. - */ - dc_acpi(dev); -#endif - /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = DC_RID; sc->dc_res = bus_alloc_resource_any(dev, DC_RES, &rid, RF_ACTIVE); if (sc->dc_res == NULL) { printf("dc%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->dc_btag = rman_get_bustag(sc->dc_res); sc->dc_bhandle = rman_get_bushandle(sc->dc_res); /* Allocate interrupt. */ rid = 0; sc->dc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->dc_irq == NULL) { printf("dc%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Need this info to decide on a chip type. */ sc->dc_info = dc_devtype(dev); revision = pci_read_config(dev, DC_PCI_CFRV, 4) & 0x000000FF; /* Get the eeprom width, but PNIC and XIRCOM have diff eeprom */ if (sc->dc_info->dc_did != DC_DEVICEID_82C168 && sc->dc_info->dc_did != DC_DEVICEID_X3201) dc_eeprom_width(sc); switch (sc->dc_info->dc_did) { case DC_DEVICEID_21143: sc->dc_type = DC_TYPE_21143; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL; /* Save EEPROM contents so we can parse them later. */ dc_read_srom(sc, sc->dc_romwidth); break; case DC_DEVICEID_DM9009: case DC_DEVICEID_DM9100: case DC_DEVICEID_DM9102: sc->dc_type = DC_TYPE_DM9102; sc->dc_flags |= DC_TX_COALESCE | DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_TX_STORENFWD; sc->dc_flags |= DC_TX_ALIGN; sc->dc_pmode = DC_PMODE_MII; /* Increase the latency timer value. */ command = pci_read_config(dev, DC_PCI_CFLT, 4); command &= 0xFFFF00FF; command |= 0x00008000; pci_write_config(dev, DC_PCI_CFLT, command, 4); break; case DC_DEVICEID_AL981: sc->dc_type = DC_TYPE_AL981; sc->dc_flags |= DC_TX_USE_TX_INTR; sc->dc_flags |= DC_TX_ADMTEK_WAR; sc->dc_pmode = DC_PMODE_MII; dc_read_srom(sc, sc->dc_romwidth); break; case DC_DEVICEID_AN985: case DC_DEVICEID_ADM9511: case DC_DEVICEID_ADM9513: case DC_DEVICEID_FA511: case DC_DEVICEID_FE2500: case DC_DEVICEID_EN2242: case DC_DEVICEID_HAWKING_PN672TX: case DC_DEVICEID_3CSOHOB: case DC_DEVICEID_MSMN120: case DC_DEVICEID_MSMN130_FAKE: /* XXX avoid collision with PNIC*/ sc->dc_type = DC_TYPE_AN985; sc->dc_flags |= DC_64BIT_HASH; sc->dc_flags |= DC_TX_USE_TX_INTR; sc->dc_flags |= DC_TX_ADMTEK_WAR; sc->dc_pmode = DC_PMODE_MII; /* Don't read SROM for - auto-loaded on reset */ break; case DC_DEVICEID_98713: case DC_DEVICEID_98713_CP: if (revision < DC_REVISION_98713A) { sc->dc_type = DC_TYPE_98713; } if (revision >= DC_REVISION_98713A) { sc->dc_type = DC_TYPE_98713A; sc->dc_flags |= DC_21143_NWAY; } sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; break; case DC_DEVICEID_987x5: case DC_DEVICEID_EN1217: /* * Macronix MX98715AEC-C/D/E parts have only a * 128-bit hash table. We need to deal with these * in the same manner as the PNIC II so that we * get the right number of bits out of the * CRC routine. */ if (revision >= DC_REVISION_98715AEC_C && revision < DC_REVISION_98725) sc->dc_flags |= DC_128BIT_HASH; sc->dc_type = DC_TYPE_987x5; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_98727: sc->dc_type = DC_TYPE_987x5; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_82C115: sc->dc_type = DC_TYPE_PNICII; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR | DC_128BIT_HASH; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_82C168: sc->dc_type = DC_TYPE_PNIC; sc->dc_flags |= DC_TX_STORENFWD | DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_PNIC_RX_BUG_WAR; sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT); if (revision < DC_REVISION_82C169) sc->dc_pmode = DC_PMODE_SYM; break; case DC_DEVICEID_AX88140A: sc->dc_type = DC_TYPE_ASIX; sc->dc_flags |= DC_TX_USE_TX_INTR | DC_TX_INTR_FIRSTFRAG; sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_X3201: sc->dc_type = DC_TYPE_XIRCOM; sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE | DC_TX_ALIGN; /* * We don't actually need to coalesce, but we're doing * it to obtain a double word aligned buffer. * The DC_TX_COALESCE flag is required. */ sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_RS7112: sc->dc_type = DC_TYPE_CONEXANT; sc->dc_flags |= DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_pmode = DC_PMODE_MII; dc_read_srom(sc, sc->dc_romwidth); break; default: printf("dc%d: unknown device: %x\n", sc->dc_unit, sc->dc_info->dc_did); break; } /* Save the cache line size. */ if (DC_IS_DAVICOM(sc)) sc->dc_cachesize = 0; else sc->dc_cachesize = pci_read_config(dev, DC_PCI_CFLT, 4) & 0xFF; /* Reset the adapter. */ dc_reset(sc); /* Take 21143 out of snooze mode */ if (DC_IS_INTEL(sc) || DC_IS_XIRCOM(sc)) { command = pci_read_config(dev, DC_PCI_CFDD, 4); command &= ~(DC_CFDD_SNOOZE_MODE | DC_CFDD_SLEEP_MODE); pci_write_config(dev, DC_PCI_CFDD, command, 4); } /* * Try to learn something about the supported media. * We know that ASIX and ADMtek and Davicom devices * will *always* be using MII media, so that's a no-brainer. * The tricky ones are the Macronix/PNIC II and the * Intel 21143. */ if (DC_IS_INTEL(sc)) dc_parse_21143_srom(sc); else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { if (sc->dc_type == DC_TYPE_98713) sc->dc_pmode = DC_PMODE_MII; else sc->dc_pmode = DC_PMODE_SYM; } else if (!sc->dc_pmode) sc->dc_pmode = DC_PMODE_MII; /* * Get station address from the EEPROM. */ switch(sc->dc_type) { case DC_TYPE_98713: case DC_TYPE_98713A: case DC_TYPE_987x5: case DC_TYPE_PNICII: dc_read_eeprom(sc, (caddr_t)&mac_offset, (DC_EE_NODEADDR_OFFSET / 2), 1, 0); dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); break; case DC_TYPE_PNIC: dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); break; case DC_TYPE_DM9102: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); #ifdef __sparc64__ /* * If this is an onboard dc(4) the station address read from * the EEPROM is all zero and we have to get it from the fcode. */ for (i = 0; i < ETHER_ADDR_LEN; i++) if (eaddr[i] != 0x00) break; if (i >= ETHER_ADDR_LEN && OF_getetheraddr2(dev, eaddr) == -1) OF_getetheraddr(dev, eaddr); #endif break; case DC_TYPE_21143: case DC_TYPE_ASIX: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); break; case DC_TYPE_AL981: case DC_TYPE_AN985: *(u_int32_t *)(&eaddr[0]) = CSR_READ_4(sc, DC_AL_PAR0); *(u_int16_t *)(&eaddr[4]) = CSR_READ_4(sc, DC_AL_PAR1); break; case DC_TYPE_CONEXANT: bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr, ETHER_ADDR_LEN); break; case DC_TYPE_XIRCOM: /* The MAC comes from the CIS. */ mac = pci_get_ether(dev); if (!mac) { device_printf(dev, "No station address in CIS!\n"); error = ENXIO; goto fail; } bcopy(mac, eaddr, ETHER_ADDR_LEN); break; default: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); break; } sc->dc_unit = unit; bcopy(eaddr, &sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct dc_list_data), 1, sizeof(struct dc_list_data), 0, NULL, NULL, &sc->dc_ltag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } error = bus_dmamem_alloc(sc->dc_ltag, (void **)&sc->dc_ldata, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->dc_lmap); if (error) { printf("dc%d: failed to allocate DMA safe memory\n", unit); error = ENXIO; goto fail; } error = bus_dmamap_load(sc->dc_ltag, sc->dc_lmap, sc->dc_ldata, sizeof(struct dc_list_data), dc_dma_map_addr, &sc->dc_laddr, BUS_DMA_NOWAIT); if (error) { printf("dc%d: cannot get address of the descriptors\n", unit); error = ENXIO; goto fail; } /* * Allocate a busdma tag and DMA safe memory for the multicast * setup frame. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, DC_SFRAME_LEN + DC_MIN_FRAMELEN, 1, DC_SFRAME_LEN + DC_MIN_FRAMELEN, 0, NULL, NULL, &sc->dc_stag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } error = bus_dmamem_alloc(sc->dc_stag, (void **)&sc->dc_cdata.dc_sbuf, BUS_DMA_NOWAIT, &sc->dc_smap); if (error) { printf("dc%d: failed to allocate DMA safe memory\n", unit); error = ENXIO; goto fail; } error = bus_dmamap_load(sc->dc_stag, sc->dc_smap, sc->dc_cdata.dc_sbuf, DC_SFRAME_LEN, dc_dma_map_addr, &sc->dc_saddr, BUS_DMA_NOWAIT); if (error) { printf("dc%d: cannot get address of the descriptors\n", unit); error = ENXIO; goto fail; } /* Allocate a busdma tag for mbufs. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * DC_TX_LIST_CNT, DC_TX_LIST_CNT, MCLBYTES, 0, NULL, NULL, &sc->dc_mtag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } /* Create the TX/RX busdma maps. */ for (i = 0; i < DC_TX_LIST_CNT; i++) { error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_cdata.dc_tx_map[i]); if (error) { printf("dc%d: failed to init TX ring\n", unit); error = ENXIO; goto fail; } } for (i = 0; i < DC_RX_LIST_CNT; i++) { error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_cdata.dc_rx_map[i]); if (error) { printf("dc%d: failed to init RX ring\n", unit); error = ENXIO; goto fail; } } error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_sparemap); if (error) { printf("dc%d: failed to init RX ring\n", unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); /* XXX: bleah, MTU gets overwritten in ether_ifattach() */ ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = dc_ioctl; ifp->if_start = dc_start; ifp->if_watchdog = dc_watchdog; ifp->if_init = dc_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; /* * Do MII setup. If this is a 21143, check for a PHY on the * MII bus after applying any necessary fixups to twiddle the * GPIO bits. If we don't end up finding a PHY, restore the * old selection (SIA only or SIA/SYM) and attach the dcphy * driver instead. */ if (DC_IS_INTEL(sc)) { dc_apply_fixup(sc, IFM_AUTO); tmp = sc->dc_pmode; sc->dc_pmode = DC_PMODE_MII; } error = mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); if (error && DC_IS_INTEL(sc)) { sc->dc_pmode = tmp; if (sc->dc_pmode != DC_PMODE_SIA) sc->dc_pmode = DC_PMODE_SYM; sc->dc_flags |= DC_21143_NWAY; mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); /* * For non-MII cards, we need to have the 21143 * drive the LEDs. Except there are some systems * like the NEC VersaPro NoteBook PC which have no * LEDs, and twiddling these bits has adverse effects * on them. (I.e. you suddenly can't get a link.) */ if (pci_read_config(dev, DC_PCI_CSID, 4) != 0x80281033) sc->dc_flags |= DC_TULIP_LEDS; error = 0; } if (error) { printf("dc%d: MII without any PHY!\n", sc->dc_unit); goto fail; } if (DC_IS_XIRCOM(sc)) { /* * setup General Purpose Port mode and data so the tulip * can talk to the MII. */ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); } if (DC_IS_ADMTEK(sc)) { /* * Set automatic TX underrun recovery for the ADMtek chips */ DC_SETBIT(sc, DC_AL_CR, DC_AL_CR_ATUR); } /* * Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; callout_init(&sc->dc_stat_ch, IS_MPSAFE ? CALLOUT_MPSAFE : 0); #ifdef SRM_MEDIA sc->dc_srm_media = 0; /* Remember the SRM console media setting */ if (DC_IS_INTEL(sc)) { command = pci_read_config(dev, DC_PCI_CFDD, 4); command &= ~(DC_CFDD_SNOOZE_MODE | DC_CFDD_SLEEP_MODE); switch ((command >> 8) & 0xff) { case 3: sc->dc_srm_media = IFM_10_T; break; case 4: sc->dc_srm_media = IFM_10_T | IFM_FDX; break; case 5: sc->dc_srm_media = IFM_100_TX; break; case 6: sc->dc_srm_media = IFM_100_TX | IFM_FDX; break; } if (sc->dc_srm_media) sc->dc_srm_media |= IFM_ACTIVE | IFM_ETHER; } #endif /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | (IS_MPSAFE ? INTR_MPSAFE : 0), dc_intr, sc, &sc->dc_intrhand); if (error) { printf("dc%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) dc_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int dc_detach(device_t dev) { struct dc_softc *sc; struct ifnet *ifp; struct dc_mediainfo *m; int i; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->dc_mtx), ("dc mutex not initialized")); DC_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { dc_stop(sc); ether_ifdetach(ifp); } if (sc->dc_miibus) device_delete_child(dev, sc->dc_miibus); bus_generic_detach(dev); if (sc->dc_intrhand) bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); if (sc->dc_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); if (sc->dc_res) bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); if (sc->dc_cdata.dc_sbuf != NULL) bus_dmamem_free(sc->dc_stag, sc->dc_cdata.dc_sbuf, sc->dc_smap); if (sc->dc_ldata != NULL) bus_dmamem_free(sc->dc_ltag, sc->dc_ldata, sc->dc_lmap); for (i = 0; i < DC_TX_LIST_CNT; i++) bus_dmamap_destroy(sc->dc_mtag, sc->dc_cdata.dc_tx_map[i]); for (i = 0; i < DC_RX_LIST_CNT; i++) bus_dmamap_destroy(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i]); bus_dmamap_destroy(sc->dc_mtag, sc->dc_sparemap); if (sc->dc_stag) bus_dma_tag_destroy(sc->dc_stag); if (sc->dc_mtag) bus_dma_tag_destroy(sc->dc_mtag); if (sc->dc_ltag) bus_dma_tag_destroy(sc->dc_ltag); free(sc->dc_pnic_rx_buf, M_DEVBUF); while (sc->dc_mi != NULL) { m = sc->dc_mi->dc_next; free(sc->dc_mi, M_DEVBUF); sc->dc_mi = m; } free(sc->dc_srom, M_DEVBUF); DC_UNLOCK(sc); mtx_destroy(&sc->dc_mtx); return (0); } /* * Initialize the transmit descriptors. */ static int dc_list_tx_init(struct dc_softc *sc) { struct dc_chain_data *cd; struct dc_list_data *ld; int i, nexti; cd = &sc->dc_cdata; ld = sc->dc_ldata; for (i = 0; i < DC_TX_LIST_CNT; i++) { if (i == DC_TX_LIST_CNT - 1) nexti = 0; else nexti = i + 1; ld->dc_tx_list[i].dc_next = htole32(DC_TXDESC(sc, nexti)); cd->dc_tx_chain[i] = NULL; ld->dc_tx_list[i].dc_data = 0; ld->dc_tx_list[i].dc_ctl = 0; } cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int dc_list_rx_init(struct dc_softc *sc) { struct dc_chain_data *cd; struct dc_list_data *ld; int i, nexti; cd = &sc->dc_cdata; ld = sc->dc_ldata; for (i = 0; i < DC_RX_LIST_CNT; i++) { if (dc_newbuf(sc, i, 1) != 0) return (ENOBUFS); if (i == DC_RX_LIST_CNT - 1) nexti = 0; else nexti = i + 1; ld->dc_rx_list[i].dc_next = htole32(DC_RXDESC(sc, nexti)); } cd->dc_rx_prod = 0; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } static void dc_dma_map_rxbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct dc_softc *sc; struct dc_desc *c; sc = arg; c = &sc->dc_ldata->dc_rx_list[sc->dc_cdata.dc_rx_cur]; if (error) { sc->dc_cdata.dc_rx_err = error; return; } KASSERT(nseg == 1, ("wrong number of segments, should be 1")); sc->dc_cdata.dc_rx_err = 0; c->dc_data = htole32(segs->ds_addr); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int dc_newbuf(struct dc_softc *sc, int i, int alloc) { struct mbuf *m_new; bus_dmamap_t tmp; int error; if (alloc) { m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m_new == NULL) return (ENOBUFS); } else { m_new = sc->dc_cdata.dc_rx_chain[i]; m_new->m_data = m_new->m_ext.ext_buf; } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_adj(m_new, sizeof(u_int64_t)); /* * If this is a PNIC chip, zero the buffer. This is part * of the workaround for the receive bug in the 82c168 and * 82c169 chips. */ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) bzero(mtod(m_new, char *), m_new->m_len); /* No need to remap the mbuf if we're reusing it. */ if (alloc) { sc->dc_cdata.dc_rx_cur = i; error = bus_dmamap_load_mbuf(sc->dc_mtag, sc->dc_sparemap, m_new, dc_dma_map_rxbuf, sc, 0); if (error) { m_freem(m_new); return (error); } if (sc->dc_cdata.dc_rx_err != 0) { m_freem(m_new); return (sc->dc_cdata.dc_rx_err); } bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i]); tmp = sc->dc_cdata.dc_rx_map[i]; sc->dc_cdata.dc_rx_map[i] = sc->dc_sparemap; sc->dc_sparemap = tmp; sc->dc_cdata.dc_rx_chain[i] = m_new; } sc->dc_ldata->dc_rx_list[i].dc_ctl = htole32(DC_RXCTL_RLINK | DC_RXLEN); sc->dc_ldata->dc_rx_list[i].dc_status = htole32(DC_RXSTAT_OWN); bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i], BUS_DMASYNC_PREREAD); bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Grrrrr. * The PNIC chip has a terrible bug in it that manifests itself during * periods of heavy activity. The exact mode of failure if difficult to * pinpoint: sometimes it only happens in promiscuous mode, sometimes it * will happen on slow machines. The bug is that sometimes instead of * uploading one complete frame during reception, it uploads what looks * like the entire contents of its FIFO memory. The frame we want is at * the end of the whole mess, but we never know exactly how much data has * been uploaded, so salvaging the frame is hard. * * There is only one way to do it reliably, and it's disgusting. * Here's what we know: * * - We know there will always be somewhere between one and three extra * descriptors uploaded. * * - We know the desired received frame will always be at the end of the * total data upload. * * - We know the size of the desired received frame because it will be * provided in the length field of the status word in the last descriptor. * * Here's what we do: * * - When we allocate buffers for the receive ring, we bzero() them. * This means that we know that the buffer contents should be all * zeros, except for data uploaded by the chip. * * - We also force the PNIC chip to upload frames that include the * ethernet CRC at the end. * * - We gather all of the bogus frame data into a single buffer. * * - We then position a pointer at the end of this buffer and scan * backwards until we encounter the first non-zero byte of data. * This is the end of the received frame. We know we will encounter * some data at the end of the frame because the CRC will always be * there, so even if the sender transmits a packet of all zeros, * we won't be fooled. * * - We know the size of the actual received frame, so we subtract * that value from the current pointer location. This brings us * to the start of the actual received packet. * * - We copy this into an mbuf and pass it on, along with the actual * frame length. * * The performance hit is tremendous, but it beats dropping frames all * the time. */ #define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG | DC_RXSTAT_LASTFRAG) static void dc_pnic_rx_bug_war(struct dc_softc *sc, int idx) { struct dc_desc *cur_rx; struct dc_desc *c = NULL; struct mbuf *m = NULL; unsigned char *ptr; int i, total_len; u_int32_t rxstat = 0; i = sc->dc_pnic_rx_bug_save; cur_rx = &sc->dc_ldata->dc_rx_list[idx]; ptr = sc->dc_pnic_rx_buf; bzero(ptr, DC_RXLEN * 5); /* Copy all the bytes from the bogus buffers. */ while (1) { c = &sc->dc_ldata->dc_rx_list[i]; rxstat = le32toh(c->dc_status); m = sc->dc_cdata.dc_rx_chain[i]; bcopy(mtod(m, char *), ptr, DC_RXLEN); ptr += DC_RXLEN; /* If this is the last buffer, break out. */ if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) break; dc_newbuf(sc, i, 0); DC_INC(i, DC_RX_LIST_CNT); } /* Find the length of the actual receive frame. */ total_len = DC_RXBYTES(rxstat); /* Scan backwards until we hit a non-zero byte. */ while (*ptr == 0x00) ptr--; /* Round off. */ if ((uintptr_t)(ptr) & 0x3) ptr -= 1; /* Now find the start of the frame. */ ptr -= total_len; if (ptr < sc->dc_pnic_rx_buf) ptr = sc->dc_pnic_rx_buf; /* * Now copy the salvaged frame to the last mbuf and fake up * the status word to make it look like a successful * frame reception. */ dc_newbuf(sc, i, 0); bcopy(ptr, mtod(m, char *), total_len); cur_rx->dc_status = htole32(rxstat | DC_RXSTAT_FIRSTFRAG); } /* * This routine searches the RX ring for dirty descriptors in the * event that the rxeof routine falls out of sync with the chip's * current descriptor pointer. This may happen sometimes as a result * of a "no RX buffer available" condition that happens when the chip * consumes all of the RX buffers before the driver has a chance to * process the RX ring. This routine may need to be called more than * once to bring the driver back in sync with the chip, however we * should still be getting RX DONE interrupts to drive the search * for new packets in the RX ring, so we should catch up eventually. */ static int dc_rx_resync(struct dc_softc *sc) { struct dc_desc *cur_rx; int i, pos; pos = sc->dc_cdata.dc_rx_prod; for (i = 0; i < DC_RX_LIST_CNT; i++) { cur_rx = &sc->dc_ldata->dc_rx_list[pos]; if (!(le32toh(cur_rx->dc_status) & DC_RXSTAT_OWN)) break; DC_INC(pos, DC_RX_LIST_CNT); } /* If the ring really is empty, then just return. */ if (i == DC_RX_LIST_CNT) return (0); /* We've fallen behing the chip: catch it. */ sc->dc_cdata.dc_rx_prod = pos; return (EAGAIN); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void dc_rxeof(struct dc_softc *sc) { struct mbuf *m; struct ifnet *ifp; struct dc_desc *cur_rx; int i, total_len = 0; u_int32_t rxstat; DC_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; i = sc->dc_cdata.dc_rx_prod; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_POSTREAD); while (!(le32toh(sc->dc_ldata->dc_rx_list[i].dc_status) & DC_RXSTAT_OWN)) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif cur_rx = &sc->dc_ldata->dc_rx_list[i]; rxstat = le32toh(cur_rx->dc_status); m = sc->dc_cdata.dc_rx_chain[i]; bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i], BUS_DMASYNC_POSTREAD); total_len = DC_RXBYTES(rxstat); if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) { if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) { if (rxstat & DC_RXSTAT_FIRSTFRAG) sc->dc_pnic_rx_bug_save = i; if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) { DC_INC(i, DC_RX_LIST_CNT); continue; } dc_pnic_rx_bug_war(sc, i); rxstat = le32toh(cur_rx->dc_status); total_len = DC_RXBYTES(rxstat); } } /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. However, don't report long * frames as errors since they could be vlans. */ if ((rxstat & DC_RXSTAT_RXERR)) { if (!(rxstat & DC_RXSTAT_GIANT) || (rxstat & (DC_RXSTAT_CRCERR | DC_RXSTAT_DRIBBLE | DC_RXSTAT_MIIERE | DC_RXSTAT_COLLSEEN | DC_RXSTAT_RUNT | DC_RXSTAT_DE))) { ifp->if_ierrors++; if (rxstat & DC_RXSTAT_COLLSEEN) ifp->if_collisions++; dc_newbuf(sc, i, 0); if (rxstat & DC_RXSTAT_CRCERR) { DC_INC(i, DC_RX_LIST_CNT); continue; } else { dc_init(sc); return; } } } /* No errors; receive the packet. */ total_len -= ETHER_CRC_LEN; #ifdef __i386__ /* * On the x86 we do not have alignment problems, so try to * allocate a new buffer for the receive ring, and pass up * the one where the packet is already, saving the expensive * copy done in m_devget(). * If we are on an architecture with alignment problems, or * if the allocation fails, then use m_devget and leave the * existing buffer in the receive ring. */ if (dc_quick && dc_newbuf(sc, i, 1) == 0) { m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; DC_INC(i, DC_RX_LIST_CNT); } else #endif { struct mbuf *m0; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); dc_newbuf(sc, i, 0); DC_INC(i, DC_RX_LIST_CNT); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; } ifp->if_ipackets++; DC_UNLOCK(sc); (*ifp->if_input)(ifp, m); DC_LOCK(sc); } sc->dc_cdata.dc_rx_prod = i; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void dc_txeof(struct dc_softc *sc) { struct dc_desc *cur_tx = NULL; struct ifnet *ifp; int idx; u_int32_t ctl, txstat; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_POSTREAD); idx = sc->dc_cdata.dc_tx_cons; while (idx != sc->dc_cdata.dc_tx_prod) { cur_tx = &sc->dc_ldata->dc_tx_list[idx]; txstat = le32toh(cur_tx->dc_status); ctl = le32toh(cur_tx->dc_ctl); if (txstat & DC_TXSTAT_OWN) break; if (!(ctl & DC_TXCTL_LASTFRAG) || ctl & DC_TXCTL_SETUP) { if (ctl & DC_TXCTL_SETUP) { /* * Yes, the PNIC is so brain damaged * that it will sometimes generate a TX * underrun error while DMAing the RX * filter setup frame. If we detect this, * we have to send the setup frame again, * or else the filter won't be programmed * correctly. */ if (DC_IS_PNIC(sc)) { if (txstat & DC_TXSTAT_ERRSUM) dc_setfilt(sc); } sc->dc_cdata.dc_tx_chain[idx] = NULL; } sc->dc_cdata.dc_tx_cnt--; DC_INC(idx, DC_TX_LIST_CNT); continue; } if (DC_IS_XIRCOM(sc) || DC_IS_CONEXANT(sc)) { /* * XXX: Why does my Xircom taunt me so? * For some reason it likes setting the CARRLOST flag * even when the carrier is there. wtf?!? * Who knows, but Conexant chips have the * same problem. Maybe they took lessons * from Xircom. */ if (/*sc->dc_type == DC_TYPE_21143 &&*/ sc->dc_pmode == DC_PMODE_MII && ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM | DC_TXSTAT_NOCARRIER))) txstat &= ~DC_TXSTAT_ERRSUM; } else { if (/*sc->dc_type == DC_TYPE_21143 &&*/ sc->dc_pmode == DC_PMODE_MII && ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM | DC_TXSTAT_NOCARRIER | DC_TXSTAT_CARRLOST))) txstat &= ~DC_TXSTAT_ERRSUM; } if (txstat & DC_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & DC_TXSTAT_EXCESSCOLL) ifp->if_collisions++; if (txstat & DC_TXSTAT_LATECOLL) ifp->if_collisions++; if (!(txstat & DC_TXSTAT_UNDERRUN)) { dc_init(sc); return; } } ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; if (sc->dc_cdata.dc_tx_chain[idx] != NULL) { bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx]); m_freem(sc->dc_cdata.dc_tx_chain[idx]); sc->dc_cdata.dc_tx_chain[idx] = NULL; } sc->dc_cdata.dc_tx_cnt--; DC_INC(idx, DC_TX_LIST_CNT); } if (idx != sc->dc_cdata.dc_tx_cons) { /* Some buffers have been freed. */ sc->dc_cdata.dc_tx_cons = idx; ifp->if_flags &= ~IFF_OACTIVE; } ifp->if_timer = (sc->dc_cdata.dc_tx_cnt == 0) ? 0 : 5; } static void dc_tick(void *xsc) { struct dc_softc *sc; struct mii_data *mii; struct ifnet *ifp; u_int32_t r; sc = xsc; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->dc_miibus); if (sc->dc_flags & DC_REDUCED_MII_POLL) { if (sc->dc_flags & DC_21143_NWAY) { r = CSR_READ_4(sc, DC_10BTSTAT); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX && (r & DC_TSTAT_LS100)) { sc->dc_link = 0; mii_mediachg(mii); } if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && (r & DC_TSTAT_LS10)) { sc->dc_link = 0; mii_mediachg(mii); } if (sc->dc_link == 0) mii_tick(mii); } else { r = CSR_READ_4(sc, DC_ISR); if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT && sc->dc_cdata.dc_tx_cnt == 0) { mii_tick(mii); if (!(mii->mii_media_status & IFM_ACTIVE)) sc->dc_link = 0; } } } else mii_tick(mii); /* * When the init routine completes, we expect to be able to send * packets right away, and in fact the network code will send a * gratuitous ARP the moment the init routine marks the interface * as running. However, even though the MAC may have been initialized, * there may be a delay of a few seconds before the PHY completes * autonegotiation and the link is brought up. Any transmissions * made during that delay will be lost. Dealing with this is tricky: * we can't just pause in the init routine while waiting for the * PHY to come ready since that would bring the whole system to * a screeching halt for several seconds. * * What we do here is prevent the TX start routine from sending * any packets until a link has been established. After the * interface has been initialized, the tick routine will poll * the state of the PHY until the IFM_ACTIVE flag is set. Until * that time, packets will stay in the send queue, and once the * link comes up, they will be flushed out to the wire. */ if (!sc->dc_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->dc_link++; if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); } if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link) callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); else callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); DC_UNLOCK(sc); } /* * A transmit underrun has occurred. Back off the transmit threshold, * or switch to store and forward mode if we have to. */ static void dc_tx_underrun(struct dc_softc *sc) { u_int32_t isr; int i; if (DC_IS_DAVICOM(sc)) dc_init(sc); if (DC_IS_INTEL(sc)) { /* * The real 21143 requires that the transmitter be idle * in order to change the transmit threshold or store * and forward state. */ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); for (i = 0; i < DC_TIMEOUT; i++) { isr = CSR_READ_4(sc, DC_ISR); if (isr & DC_ISR_TX_IDLE) break; DELAY(10); } if (i == DC_TIMEOUT) { printf("dc%d: failed to force tx to idle state\n", sc->dc_unit); dc_init(sc); } } printf("dc%d: TX underrun -- ", sc->dc_unit); sc->dc_txthresh += DC_TXTHRESH_INC; if (sc->dc_txthresh > DC_TXTHRESH_MAX) { printf("using store and forward mode\n"); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); } else { printf("increasing TX threshold\n"); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); } if (DC_IS_INTEL(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); } #ifdef DEVICE_POLLING static poll_handler_t dc_poll; static void dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct dc_softc *sc = ifp->if_softc; if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ /* Re-enable interrupts. */ CSR_WRITE_4(sc, DC_IMR, DC_INTRS); return; } DC_LOCK(sc); sc->rxcycles = count; dc_rxeof(sc); dc_txeof(sc); if (ifp->if_snd.ifq_head != NULL && !(ifp->if_flags & IFF_OACTIVE)) dc_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int32_t status; status = CSR_READ_4(sc, DC_ISR); status &= (DC_ISR_RX_WATDOGTIMEO | DC_ISR_RX_NOBUF | DC_ISR_TX_NOBUF | DC_ISR_TX_IDLE | DC_ISR_TX_UNDERRUN | DC_ISR_BUS_ERR); if (!status) { DC_UNLOCK(sc); return; } /* ack what we have */ CSR_WRITE_4(sc, DC_ISR, status); if (status & (DC_ISR_RX_WATDOGTIMEO | DC_ISR_RX_NOBUF)) { u_int32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED); ifp->if_ierrors += (r & 0xffff) + ((r >> 17) & 0x7ff); if (dc_rx_resync(sc)) dc_rxeof(sc); } /* restart transmit unit if necessary */ if (status & DC_ISR_TX_IDLE && sc->dc_cdata.dc_tx_cnt) CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); if (status & DC_ISR_TX_UNDERRUN) dc_tx_underrun(sc); if (status & DC_ISR_BUS_ERR) { printf("dc_poll: dc%d bus error\n", sc->dc_unit); dc_reset(sc); dc_init(sc); } } DC_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void dc_intr(void *arg) { struct dc_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; if (sc->suspended) return; if ((CSR_READ_4(sc, DC_ISR) & DC_INTRS) == 0) return; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(dc_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_4(sc, DC_IMR, 0x00000000); goto done; } #endif /* Suppress unwanted interrupts */ if (!(ifp->if_flags & IFF_UP)) { if (CSR_READ_4(sc, DC_ISR) & DC_INTRS) dc_stop(sc); DC_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_4(sc, DC_IMR, 0x00000000); while (((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) && status != 0xFFFFFFFF) { CSR_WRITE_4(sc, DC_ISR, status); if (status & DC_ISR_RX_OK) { int curpkts; curpkts = ifp->if_ipackets; dc_rxeof(sc); if (curpkts == ifp->if_ipackets) { while (dc_rx_resync(sc)) dc_rxeof(sc); } } if (status & (DC_ISR_TX_OK | DC_ISR_TX_NOBUF)) dc_txeof(sc); if (status & DC_ISR_TX_IDLE) { dc_txeof(sc); if (sc->dc_cdata.dc_tx_cnt) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); } } if (status & DC_ISR_TX_UNDERRUN) dc_tx_underrun(sc); if ((status & DC_ISR_RX_WATDOGTIMEO) || (status & DC_ISR_RX_NOBUF)) { int curpkts; curpkts = ifp->if_ipackets; dc_rxeof(sc); if (curpkts == ifp->if_ipackets) { while (dc_rx_resync(sc)) dc_rxeof(sc); } } if (status & DC_ISR_BUS_ERR) { dc_reset(sc); dc_init(sc); } } /* Re-enable interrupts. */ CSR_WRITE_4(sc, DC_IMR, DC_INTRS); if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); #ifdef DEVICE_POLLING done: #endif DC_UNLOCK(sc); } static void dc_dma_map_txbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct dc_softc *sc; struct dc_desc *f; int cur, first, frag, i; sc = arg; if (error) { sc->dc_cdata.dc_tx_err = error; return; } first = cur = frag = sc->dc_cdata.dc_tx_prod; for (i = 0; i < nseg; i++) { if ((sc->dc_flags & DC_TX_ADMTEK_WAR) && (frag == (DC_TX_LIST_CNT - 1)) && (first != sc->dc_cdata.dc_tx_first)) { bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_tx_map[first]); sc->dc_cdata.dc_tx_err = ENOBUFS; return; } f = &sc->dc_ldata->dc_tx_list[frag]; f->dc_ctl = htole32(DC_TXCTL_TLINK | segs[i].ds_len); if (i == 0) { f->dc_status = 0; f->dc_ctl |= htole32(DC_TXCTL_FIRSTFRAG); } else f->dc_status = htole32(DC_TXSTAT_OWN); f->dc_data = htole32(segs[i].ds_addr); cur = frag; DC_INC(frag, DC_TX_LIST_CNT); } sc->dc_cdata.dc_tx_err = 0; sc->dc_cdata.dc_tx_prod = frag; sc->dc_cdata.dc_tx_cnt += nseg; sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_LASTFRAG); sc->dc_cdata.dc_tx_chain[cur] = sc->dc_cdata.dc_tx_mapping; if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG) sc->dc_ldata->dc_tx_list[first].dc_ctl |= htole32(DC_TXCTL_FINT); if (sc->dc_flags & DC_TX_INTR_ALWAYS) sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_FINT); if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64) sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_FINT); sc->dc_ldata->dc_tx_list[first].dc_status = htole32(DC_TXSTAT_OWN); } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int dc_encap(struct dc_softc *sc, struct mbuf **m_head) { struct mbuf *m; int error, idx, chainlen = 0; /* * If there's no way we can send any packets, return now. */ if (DC_TX_LIST_CNT - sc->dc_cdata.dc_tx_cnt < 6) return (ENOBUFS); /* * Count the number of frags in this chain to see if * we need to m_defrag. Since the descriptor list is shared * by all packets, we'll m_defrag long chains so that they * do not use up the entire list, even if they would fit. */ for (m = *m_head; m != NULL; m = m->m_next) chainlen++; if ((chainlen > DC_TX_LIST_CNT / 4) || ((DC_TX_LIST_CNT - (chainlen + sc->dc_cdata.dc_tx_cnt)) < 6)) { m = m_defrag(*m_head, M_DONTWAIT); if (m == NULL) return (ENOBUFS); *m_head = m; } /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ idx = sc->dc_cdata.dc_tx_prod; sc->dc_cdata.dc_tx_mapping = *m_head; error = bus_dmamap_load_mbuf(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], *m_head, dc_dma_map_txbuf, sc, 0); if (error) return (error); if (sc->dc_cdata.dc_tx_err != 0) return (sc->dc_cdata.dc_tx_err); bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void dc_start(struct ifnet *ifp) { struct dc_softc *sc; struct mbuf *m_head = NULL, *m; int idx; sc = ifp->if_softc; DC_LOCK(sc); if (!sc->dc_link && ifp->if_snd.ifq_len < 10) { DC_UNLOCK(sc); return; } if (ifp->if_flags & IFF_OACTIVE) { DC_UNLOCK(sc); return; } idx = sc->dc_cdata.dc_tx_first = sc->dc_cdata.dc_tx_prod; while (sc->dc_cdata.dc_tx_chain[idx] == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (sc->dc_flags & DC_TX_COALESCE && (m_head->m_next != NULL || sc->dc_flags & DC_TX_ALIGN)) { m = m_defrag(m_head, M_DONTWAIT); if (m == NULL) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } else { m_head = m; } } if (dc_encap(sc, &m_head)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } idx = sc->dc_cdata.dc_tx_prod; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); if (sc->dc_flags & DC_TX_ONE) { ifp->if_flags |= IFF_OACTIVE; break; } } /* Transmit */ if (!(sc->dc_flags & DC_TX_POLL)) CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; DC_UNLOCK(sc); } static void dc_init(void *xsc) { struct dc_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; DC_LOCK(sc); mii = device_get_softc(sc->dc_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ dc_stop(sc); dc_reset(sc); /* * Set cache alignment and burst length. */ if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc)) CSR_WRITE_4(sc, DC_BUSCTL, 0); else CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME | DC_BUSCTL_MRLE); /* * Evenly share the bus between receive and transmit process. */ if (DC_IS_INTEL(sc)) DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_ARBITRATION); if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA); } else { DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG); } if (sc->dc_flags & DC_TX_POLL) DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1); switch(sc->dc_cachesize) { case 32: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG); break; case 16: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG); break; case 8: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG); break; case 0: default: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE); break; } if (sc->dc_flags & DC_TX_STORENFWD) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); else { if (sc->dc_txthresh > DC_TXTHRESH_MAX) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); } } DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF); if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { /* * The app notes for the 98713 and 98715A say that * in order to have the chips operate properly, a magic * number must be written to CSR16. Macronix does not * document the meaning of these bits so there's no way * to know exactly what they do. The 98713 has a magic * number all its own; the rest all use a different one. */ DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713); else DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715); } if (DC_IS_XIRCOM(sc)) { /* * setup General Purpose Port mode and data so the tulip * can talk to the MII. */ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); } DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_MIN); /* Init circular RX list. */ if (dc_list_rx_init(sc) == ENOBUFS) { printf("dc%d: initialization failed: no " "memory for rx buffers\n", sc->dc_unit); dc_stop(sc); DC_UNLOCK(sc); return; } /* * Init TX descriptors. */ dc_list_tx_init(sc); /* * Load the address of the RX list. */ CSR_WRITE_4(sc, DC_RXADDR, DC_RXDESC(sc, 0)); CSR_WRITE_4(sc, DC_TXADDR, DC_TXDESC(sc, 0)); /* * Enable interrupts. */ #ifdef DEVICE_POLLING /* * ... but only if we are not polling, and make sure they are off in * the case of polling. Some cards (e.g. fxp) turn interrupts on * after a reset. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_4(sc, DC_IMR, 0x00000000); else #endif CSR_WRITE_4(sc, DC_IMR, DC_INTRS); CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF); /* Enable transmitter. */ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); /* * If this is an Intel 21143 and we're not using the * MII port, program the LED control pins so we get * link and activity indications. */ if (sc->dc_flags & DC_TULIP_LEDS) { CSR_WRITE_4(sc, DC_WATCHDOG, DC_WDOG_CTLWREN | DC_WDOG_LINK | DC_WDOG_ACTIVITY); CSR_WRITE_4(sc, DC_WATCHDOG, 0); } /* * Load the RX/multicast filter. We do this sort of late * because the filter programming scheme on the 21143 and * some clones requires DMAing a setup frame via the TX * engine, and we need the transmitter enabled for that. */ dc_setfilt(sc); /* Enable receiver. */ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF); mii_mediachg(mii); dc_setcfg(sc, sc->dc_if_media); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* Don't start the ticker if this is a homePNA link. */ if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_HPNA_1) sc->dc_link = 1; else { if (sc->dc_flags & DC_21143_NWAY) callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); else callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); } #ifdef SRM_MEDIA if(sc->dc_srm_media) { struct ifreq ifr; ifr.ifr_media = sc->dc_srm_media; ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA); sc->dc_srm_media = 0; } #endif DC_UNLOCK(sc); } /* * Set media options. */ static int dc_ifmedia_upd(struct ifnet *ifp) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = ifp->if_softc; mii = device_get_softc(sc->dc_miibus); mii_mediachg(mii); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) dc_setcfg(sc, ifm->ifm_media); else sc->dc_link = 0; return (0); } /* * Report current media status. */ static void dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = ifp->if_softc; mii = device_get_softc(sc->dc_miibus); mii_pollstat(mii); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc)) { if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { ifmr->ifm_active = ifm->ifm_media; ifmr->ifm_status = 0; return; } } ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct dc_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; int error = 0; DC_LOCK(sc); switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) & (IFF_PROMISC | IFF_ALLMULTI); if (ifp->if_flags & IFF_RUNNING) { if (need_setfilt) dc_setfilt(sc); } else { sc->dc_txthresh = 0; dc_init(sc); } } else { if (ifp->if_flags & IFF_RUNNING) dc_stop(sc); } sc->dc_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: dc_setfilt(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->dc_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); #ifdef SRM_MEDIA if (sc->dc_srm_media) sc->dc_srm_media = 0; #endif break; case SIOCSIFCAP: ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING; break; default: error = ether_ioctl(ifp, command, data); break; } DC_UNLOCK(sc); return (error); } static void dc_watchdog(struct ifnet *ifp) { struct dc_softc *sc; sc = ifp->if_softc; DC_LOCK(sc); ifp->if_oerrors++; printf("dc%d: watchdog timeout\n", sc->dc_unit); dc_stop(sc); dc_reset(sc); dc_init(sc); if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); DC_UNLOCK(sc); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void dc_stop(struct dc_softc *sc) { struct ifnet *ifp; struct dc_list_data *ld; struct dc_chain_data *cd; int i; u_int32_t ctl; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; ld = sc->dc_ldata; cd = &sc->dc_cdata; callout_stop(&sc->dc_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON | DC_NETCFG_TX_ON)); CSR_WRITE_4(sc, DC_IMR, 0x00000000); CSR_WRITE_4(sc, DC_TXADDR, 0x00000000); CSR_WRITE_4(sc, DC_RXADDR, 0x00000000); sc->dc_link = 0; /* * Free data in the RX lists. */ for (i = 0; i < DC_RX_LIST_CNT; i++) { if (cd->dc_rx_chain[i] != NULL) { m_freem(cd->dc_rx_chain[i]); cd->dc_rx_chain[i] = NULL; } } bzero(&ld->dc_rx_list, sizeof(ld->dc_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < DC_TX_LIST_CNT; i++) { if (cd->dc_tx_chain[i] != NULL) { ctl = le32toh(ld->dc_tx_list[i].dc_ctl); if ((ctl & DC_TXCTL_SETUP) || !(ctl & DC_TXCTL_LASTFRAG)) { cd->dc_tx_chain[i] = NULL; continue; } bus_dmamap_unload(sc->dc_mtag, cd->dc_tx_map[i]); m_freem(cd->dc_tx_chain[i]); cd->dc_tx_chain[i] = NULL; } } bzero(&ld->dc_tx_list, sizeof(ld->dc_tx_list)); DC_UNLOCK(sc); } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int dc_suspend(device_t dev) { struct dc_softc *sc; - int i, s; + int s; s = splimp(); sc = device_get_softc(dev); - dc_stop(sc); - - for (i = 0; i < 5; i++) - sc->saved_maps[i] = pci_read_config(dev, PCIR_BAR(i), 4); - sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); - sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); - sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); - sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); - sc->suspended = 1; splx(s); return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int dc_resume(device_t dev) { struct dc_softc *sc; struct ifnet *ifp; - int i, s; + int s; s = splimp(); sc = device_get_softc(dev); ifp = &sc->arpcom.ac_if; -#ifndef BURN_BRIDGES - dc_acpi(dev); -#endif - /* better way to do this? */ - for (i = 0; i < 5; i++) - pci_write_config(dev, PCIR_BAR(i), sc->saved_maps[i], 4); - pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); - pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); - pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); - pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); - - /* reenable busmastering */ - pci_enable_busmaster(dev); - pci_enable_io(dev, DC_RES); /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) dc_init(sc); sc->suspended = 0; splx(s); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void dc_shutdown(device_t dev) { struct dc_softc *sc; sc = device_get_softc(dev); dc_stop(sc); } Index: head/sys/dev/sf/if_sf.c =================================================================== --- head/sys/dev/sf/if_sf.c (revision 131252) +++ head/sys/dev/sf/if_sf.c (revision 131253) @@ -1,1506 +1,1482 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Adaptec AIC-6915 "Starfire" PCI fast ethernet driver for FreeBSD. * Programming manual is available from: * ftp.adaptec.com:/pub/BBS/userguides/aic6915_pg.pdf. * * Written by Bill Paul * Department of Electical Engineering * Columbia University, New York City */ /* * The Adaptec AIC-6915 "Starfire" is a 64-bit 10/100 PCI ethernet * controller designed with flexibility and reducing CPU load in mind. * The Starfire offers high and low priority buffer queues, a * producer/consumer index mechanism and several different buffer * queue and completion queue descriptor types. Any one of a number * of different driver designs can be used, depending on system and * OS requirements. This driver makes use of type0 transmit frame * descriptors (since BSD fragments packets across an mbuf chain) * and two RX buffer queues prioritized on size (one queue for small * frames that will fit into a single mbuf, another with full size * mbuf clusters for everything else). The producer/consumer indexes * and completion queues are also used. * * One downside to the Starfire has to do with alignment: buffer * queues must be aligned on 256-byte boundaries, and receive buffers * must be aligned on longword boundaries. The receive buffer alignment * causes problems on the Alpha platform, where the packet payload * should be longword aligned. There is no simple way around this. * * For receive filtering, the Starfire offers 16 perfect filter slots * and a 512-bit hash table. * * The Starfire has no internal transceiver, relying instead on an * external MII-based transceiver. Accessing registers on external * PHYs is done through a special register map rather than with the * usual bitbang MDIO method. * * Acesssing the registers on the Starfire is a little tricky. The * Starfire has a 512K internal register space. When programmed for * PCI memory mapped mode, the entire register space can be accessed * directly. However in I/O space mode, only 256 bytes are directly * mapped into PCI I/O space. The other registers can be accessed * indirectly using the SF_INDIRECTIO_ADDR and SF_INDIRECTIO_DATA * registers inside the 256-byte I/O window. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include #include #define SF_USEIOSPACE #include MODULE_DEPEND(sf, pci, 1, 1, 1); MODULE_DEPEND(sf, ether, 1, 1, 1); MODULE_DEPEND(sf, miibus, 1, 1, 1); static struct sf_type sf_devs[] = { { AD_VENDORID, AD_DEVICEID_STARFIRE, "Adaptec AIC-6915 10/100BaseTX" }, { 0, 0, NULL } }; static int sf_probe (device_t); static int sf_attach (device_t); static int sf_detach (device_t); static void sf_intr (void *); static void sf_stats_update (void *); static void sf_rxeof (struct sf_softc *); static void sf_txeof (struct sf_softc *); static int sf_encap (struct sf_softc *, struct sf_tx_bufdesc_type0 *, struct mbuf *); static void sf_start (struct ifnet *); static int sf_ioctl (struct ifnet *, u_long, caddr_t); static void sf_init (void *); static void sf_stop (struct sf_softc *); static void sf_watchdog (struct ifnet *); static void sf_shutdown (device_t); static int sf_ifmedia_upd (struct ifnet *); static void sf_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void sf_reset (struct sf_softc *); static int sf_init_rx_ring (struct sf_softc *); static void sf_init_tx_ring (struct sf_softc *); static int sf_newbuf (struct sf_softc *, struct sf_rx_bufdesc_type0 *, struct mbuf *); static void sf_setmulti (struct sf_softc *); static int sf_setperf (struct sf_softc *, int, caddr_t); static int sf_sethash (struct sf_softc *, caddr_t, int); #ifdef notdef static int sf_setvlan (struct sf_softc *, int, u_int32_t); #endif static u_int8_t sf_read_eeprom (struct sf_softc *, int); static int sf_miibus_readreg (device_t, int, int); static int sf_miibus_writereg (device_t, int, int, int); static void sf_miibus_statchg (device_t); static u_int32_t csr_read_4 (struct sf_softc *, int); static void csr_write_4 (struct sf_softc *, int, u_int32_t); static void sf_txthresh_adjust (struct sf_softc *); #ifdef SF_USEIOSPACE #define SF_RES SYS_RES_IOPORT #define SF_RID SF_PCI_LOIO #else #define SF_RES SYS_RES_MEMORY #define SF_RID SF_PCI_LOMEM #endif static device_method_t sf_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sf_probe), DEVMETHOD(device_attach, sf_attach), DEVMETHOD(device_detach, sf_detach), DEVMETHOD(device_shutdown, sf_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sf_miibus_readreg), DEVMETHOD(miibus_writereg, sf_miibus_writereg), DEVMETHOD(miibus_statchg, sf_miibus_statchg), { 0, 0 } }; static driver_t sf_driver = { "sf", sf_methods, sizeof(struct sf_softc), }; static devclass_t sf_devclass; DRIVER_MODULE(sf, pci, sf_driver, sf_devclass, 0, 0); DRIVER_MODULE(miibus, sf, miibus_driver, miibus_devclass, 0, 0); #define SF_SETBIT(sc, reg, x) \ csr_write_4(sc, reg, csr_read_4(sc, reg) | (x)) #define SF_CLRBIT(sc, reg, x) \ csr_write_4(sc, reg, csr_read_4(sc, reg) & ~(x)) static u_int32_t csr_read_4(sc, reg) struct sf_softc *sc; int reg; { u_int32_t val; #ifdef SF_USEIOSPACE CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); val = CSR_READ_4(sc, SF_INDIRECTIO_DATA); #else val = CSR_READ_4(sc, (reg + SF_RMAP_INTREG_BASE)); #endif return(val); } static u_int8_t sf_read_eeprom(sc, reg) struct sf_softc *sc; int reg; { u_int8_t val; val = (csr_read_4(sc, SF_EEADDR_BASE + (reg & 0xFFFFFFFC)) >> (8 * (reg & 3))) & 0xFF; return(val); } static void csr_write_4(sc, reg, val) struct sf_softc *sc; int reg; u_int32_t val; { #ifdef SF_USEIOSPACE CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); CSR_WRITE_4(sc, SF_INDIRECTIO_DATA, val); #else CSR_WRITE_4(sc, (reg + SF_RMAP_INTREG_BASE), val); #endif return; } /* * Copy the address 'mac' into the perfect RX filter entry at * offset 'idx.' The perfect filter only has 16 entries so do * some sanity tests. */ static int sf_setperf(sc, idx, mac) struct sf_softc *sc; int idx; caddr_t mac; { u_int16_t *p; if (idx < 0 || idx > SF_RXFILT_PERFECT_CNT) return(EINVAL); if (mac == NULL) return(EINVAL); p = (u_int16_t *)mac; csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP), htons(p[2])); csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP) + 4, htons(p[1])); csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP) + 8, htons(p[0])); return(0); } /* * Set the bit in the 512-bit hash table that corresponds to the * specified mac address 'mac.' If 'prio' is nonzero, update the * priority hash table instead of the filter hash table. */ static int sf_sethash(sc, mac, prio) struct sf_softc *sc; caddr_t mac; int prio; { u_int32_t h; if (mac == NULL) return(EINVAL); h = ether_crc32_be(mac, ETHER_ADDR_LEN) >> 23; if (prio) { SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_PRIOOFF + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); } else { SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_ADDROFF + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); } return(0); } #ifdef notdef /* * Set a VLAN tag in the receive filter. */ static int sf_setvlan(sc, idx, vlan) struct sf_softc *sc; int idx; u_int32_t vlan; { if (idx < 0 || idx >> SF_RXFILT_HASH_CNT) return(EINVAL); csr_write_4(sc, SF_RXFILT_HASH_BASE + (idx * SF_RXFILT_HASH_SKIP) + SF_RXFILT_HASH_VLANOFF, vlan); return(0); } #endif static int sf_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct sf_softc *sc; int i; u_int32_t val = 0; sc = device_get_softc(dev); for (i = 0; i < SF_TIMEOUT; i++) { val = csr_read_4(sc, SF_PHY_REG(phy, reg)); if (val & SF_MII_DATAVALID) break; } if (i == SF_TIMEOUT) return(0); if ((val & 0x0000FFFF) == 0xFFFF) return(0); return(val & 0x0000FFFF); } static int sf_miibus_writereg(dev, phy, reg, val) device_t dev; int phy, reg, val; { struct sf_softc *sc; int i; int busy; sc = device_get_softc(dev); csr_write_4(sc, SF_PHY_REG(phy, reg), val); for (i = 0; i < SF_TIMEOUT; i++) { busy = csr_read_4(sc, SF_PHY_REG(phy, reg)); if (!(busy & SF_MII_BUSY)) break; } return(0); } static void sf_miibus_statchg(dev) device_t dev; { struct sf_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->sf_miibus); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_FDX); } else { SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_HDX); } return; } static void sf_setmulti(sc) struct sf_softc *sc; { struct ifnet *ifp; int i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0, 0 }; ifp = &sc->arpcom.ac_if; /* First zot all the existing filters. */ for (i = 1; i < SF_RXFILT_PERFECT_CNT; i++) sf_setperf(sc, i, (char *)&dummy); for (i = SF_RXFILT_HASH_BASE; i < (SF_RXFILT_HASH_MAX + 1); i += 4) csr_write_4(sc, i, 0); SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); /* Now program new ones. */ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); } else { i = 1; TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Program the first 15 multicast groups * into the perfect filter. For all others, * use the hash table. */ if (i < SF_RXFILT_PERFECT_CNT) { sf_setperf(sc, i, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); i++; continue; } sf_sethash(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 0); } } return; } /* * Set media options. */ static int sf_ifmedia_upd(ifp) struct ifnet *ifp; { struct sf_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sf_miibus); sc->sf_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return(0); } /* * Report current media status. */ static void sf_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sf_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sf_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int sf_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sf_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; SF_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->sf_if_flags & IFF_PROMISC)) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else if (ifp->if_flags & IFF_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->sf_if_flags & IFF_PROMISC) { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else if (!(ifp->if_flags & IFF_RUNNING)) sf_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) sf_stop(sc); } sc->sf_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: sf_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->sf_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } SF_UNLOCK(sc); return(error); } static void sf_reset(sc) struct sf_softc *sc; { register int i; csr_write_4(sc, SF_GEN_ETH_CTL, 0); SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); DELAY(1000); SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_RESET); for (i = 0; i < SF_TIMEOUT; i++) { DELAY(10); if (!(csr_read_4(sc, SF_PCI_DEVCFG) & SF_PCIDEVCFG_RESET)) break; } if (i == SF_TIMEOUT) printf("sf%d: reset never completed!\n", sc->sf_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for an Adaptec AIC-6915 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We also check the subsystem ID so that we can identify exactly which * NIC has been found, if possible. */ static int sf_probe(dev) device_t dev; { struct sf_type *t; t = sf_devs; while(t->sf_name != NULL) { if ((pci_get_vendor(dev) == t->sf_vid) && (pci_get_device(dev) == t->sf_did)) { switch((pci_read_config(dev, SF_PCI_SUBVEN_ID, 4) >> 16) & 0xFFFF) { case AD_SUBSYSID_62011_REV0: case AD_SUBSYSID_62011_REV1: device_set_desc(dev, "Adaptec ANA-62011 10/100BaseTX"); return(0); case AD_SUBSYSID_62022: device_set_desc(dev, "Adaptec ANA-62022 10/100BaseTX"); return(0); case AD_SUBSYSID_62044_REV0: case AD_SUBSYSID_62044_REV1: device_set_desc(dev, "Adaptec ANA-62044 10/100BaseTX"); return(0); case AD_SUBSYSID_62020: device_set_desc(dev, "Adaptec ANA-62020 10/100BaseFX"); return(0); case AD_SUBSYSID_69011: device_set_desc(dev, "Adaptec ANA-69011 10/100BaseTX"); return(0); default: device_set_desc(dev, t->sf_name); return(0); break; } } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int sf_attach(dev) device_t dev; { int i; struct sf_softc *sc; struct ifnet *ifp; int unit, rid, error = 0; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->sf_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, SF_PCI_LOIO, 4); - membase = pci_read_config(dev, SF_PCI_LOMEM, 4); - irq = pci_read_config(dev, SF_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("sf%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, SF_PCI_LOIO, iobase, 4); - pci_write_config(dev, SF_PCI_LOMEM, membase, 4); - pci_write_config(dev, SF_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = SF_RID; sc->sf_res = bus_alloc_resource_any(dev, SF_RES, &rid, RF_ACTIVE); if (sc->sf_res == NULL) { printf ("sf%d: couldn't map ports\n", unit); error = ENXIO; goto fail; } sc->sf_btag = rman_get_bustag(sc->sf_res); sc->sf_bhandle = rman_get_bushandle(sc->sf_res); /* Allocate interrupt */ rid = 0; sc->sf_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sf_irq == NULL) { printf("sf%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } callout_handle_init(&sc->sf_stat_ch); /* Reset the adapter. */ sf_reset(sc); /* * Get station address from the EEPROM. */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = sf_read_eeprom(sc, SF_EE_NODEADDR + ETHER_ADDR_LEN - i); sc->sf_unit = unit; /* Allocate the descriptor queues. */ sc->sf_ldata = contigmalloc(sizeof(struct sf_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->sf_ldata == NULL) { printf("sf%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->sf_ldata, sizeof(struct sf_list_data)); /* Do MII setup. */ if (mii_phy_probe(dev, &sc->sf_miibus, sf_ifmedia_upd, sf_ifmedia_sts)) { printf("sf%d: MII without any phy!\n", sc->sf_unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sf_ioctl; ifp->if_start = sf_start; ifp->if_watchdog = sf_watchdog; ifp->if_init = sf_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = SF_TX_DLIST_CNT - 1; /* * Call MI attach routine. */ ether_ifattach(ifp, sc->arpcom.ac_enaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->sf_irq, INTR_TYPE_NET, sf_intr, sc, &sc->sf_intrhand); if (error) { printf("sf%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) sf_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int sf_detach(dev) device_t dev; { struct sf_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sf_mtx), ("sf mutex not initialized")); SF_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { sf_stop(sc); ether_ifdetach(ifp); } if (sc->sf_miibus) device_delete_child(dev, sc->sf_miibus); bus_generic_detach(dev); if (sc->sf_intrhand) bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand); if (sc->sf_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq); if (sc->sf_res) bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); if (sc->sf_ldata) contigfree(sc->sf_ldata, sizeof(struct sf_list_data), M_DEVBUF); SF_UNLOCK(sc); mtx_destroy(&sc->sf_mtx); return(0); } static int sf_init_rx_ring(sc) struct sf_softc *sc; { struct sf_list_data *ld; int i; ld = sc->sf_ldata; bzero((char *)ld->sf_rx_dlist_big, sizeof(struct sf_rx_bufdesc_type0) * SF_RX_DLIST_CNT); bzero((char *)ld->sf_rx_clist, sizeof(struct sf_rx_cmpdesc_type3) * SF_RX_CLIST_CNT); for (i = 0; i < SF_RX_DLIST_CNT; i++) { if (sf_newbuf(sc, &ld->sf_rx_dlist_big[i], NULL) == ENOBUFS) return(ENOBUFS); } return(0); } static void sf_init_tx_ring(sc) struct sf_softc *sc; { struct sf_list_data *ld; int i; ld = sc->sf_ldata; bzero((char *)ld->sf_tx_dlist, sizeof(struct sf_tx_bufdesc_type0) * SF_TX_DLIST_CNT); bzero((char *)ld->sf_tx_clist, sizeof(struct sf_tx_cmpdesc_type0) * SF_TX_CLIST_CNT); for (i = 0; i < SF_TX_DLIST_CNT; i++) ld->sf_tx_dlist[i].sf_id = SF_TX_BUFDESC_ID; for (i = 0; i < SF_TX_CLIST_CNT; i++) ld->sf_tx_clist[i].sf_type = SF_TXCMPTYPE_TX; ld->sf_tx_dlist[SF_TX_DLIST_CNT - 1].sf_end = 1; sc->sf_tx_cnt = 0; return; } static int sf_newbuf(sc, c, m) struct sf_softc *sc; struct sf_rx_bufdesc_type0 *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, sizeof(u_int64_t)); c->sf_mbuf = m_new; c->sf_addrlo = SF_RX_HOSTADDR(vtophys(mtod(m_new, caddr_t))); c->sf_valid = 1; return(0); } /* * The starfire is programmed to use 'normal' mode for packet reception, * which means we use the consumer/producer model for both the buffer * descriptor queue and the completion descriptor queue. The only problem * with this is that it involves a lot of register accesses: we have to * read the RX completion consumer and producer indexes and the RX buffer * producer index, plus the RX completion consumer and RX buffer producer * indexes have to be updated. It would have been easier if Adaptec had * put each index in a separate register, especially given that the damn * NIC has a 512K register space. * * In spite of all the lovely features that Adaptec crammed into the 6915, * it is marred by one truly stupid design flaw, which is that receive * buffer addresses must be aligned on a longword boundary. This forces * the packet payload to be unaligned, which is suboptimal on the x86 and * completely unuseable on the Alpha. Our only recourse is to copy received * packets into properly aligned buffers before handing them off. */ static void sf_rxeof(sc) struct sf_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct sf_rx_bufdesc_type0 *desc; struct sf_rx_cmpdesc_type3 *cur_rx; u_int32_t rxcons, rxprod; int cmpprodidx, cmpconsidx, bufprodidx; SF_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; rxcons = csr_read_4(sc, SF_CQ_CONSIDX); rxprod = csr_read_4(sc, SF_RXDQ_PTR_Q1); cmpprodidx = SF_IDX_LO(csr_read_4(sc, SF_CQ_PRODIDX)); cmpconsidx = SF_IDX_LO(rxcons); bufprodidx = SF_IDX_LO(rxprod); while (cmpconsidx != cmpprodidx) { struct mbuf *m0; cur_rx = &sc->sf_ldata->sf_rx_clist[cmpconsidx]; desc = &sc->sf_ldata->sf_rx_dlist_big[cur_rx->sf_endidx]; m = desc->sf_mbuf; SF_INC(cmpconsidx, SF_RX_CLIST_CNT); SF_INC(bufprodidx, SF_RX_DLIST_CNT); if (!(cur_rx->sf_status1 & SF_RXSTAT1_OK)) { ifp->if_ierrors++; sf_newbuf(sc, desc, m); continue; } m0 = m_devget(mtod(m, char *), cur_rx->sf_len, ETHER_ALIGN, ifp, NULL); sf_newbuf(sc, desc, m); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; ifp->if_ipackets++; SF_UNLOCK(sc); (*ifp->if_input)(ifp, m); SF_LOCK(sc); } csr_write_4(sc, SF_CQ_CONSIDX, (rxcons & ~SF_CQ_CONSIDX_RXQ1) | cmpconsidx); csr_write_4(sc, SF_RXDQ_PTR_Q1, (rxprod & ~SF_RXDQ_PRODIDX) | bufprodidx); return; } /* * Read the transmit status from the completion queue and release * mbufs. Note that the buffer descriptor index in the completion * descriptor is an offset from the start of the transmit buffer * descriptor list in bytes. This is important because the manual * gives the impression that it should match the producer/consumer * index, which is the offset in 8 byte blocks. */ static void sf_txeof(sc) struct sf_softc *sc; { int txcons, cmpprodidx, cmpconsidx; struct sf_tx_cmpdesc_type1 *cur_cmp; struct sf_tx_bufdesc_type0 *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; txcons = csr_read_4(sc, SF_CQ_CONSIDX); cmpprodidx = SF_IDX_HI(csr_read_4(sc, SF_CQ_PRODIDX)); cmpconsidx = SF_IDX_HI(txcons); while (cmpconsidx != cmpprodidx) { cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx]; cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7]; if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK) ifp->if_opackets++; else { if (cur_cmp->sf_txstat & SF_TXSTAT_TX_UNDERRUN) sf_txthresh_adjust(sc); ifp->if_oerrors++; } sc->sf_tx_cnt--; if (cur_tx->sf_mbuf != NULL) { m_freem(cur_tx->sf_mbuf); cur_tx->sf_mbuf = NULL; } else break; SF_INC(cmpconsidx, SF_TX_CLIST_CNT); } ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; csr_write_4(sc, SF_CQ_CONSIDX, (txcons & ~SF_CQ_CONSIDX_TXQ) | ((cmpconsidx << 16) & 0xFFFF0000)); return; } static void sf_txthresh_adjust(sc) struct sf_softc *sc; { u_int32_t txfctl; u_int8_t txthresh; txfctl = csr_read_4(sc, SF_TX_FRAMCTL); txthresh = txfctl & SF_TXFRMCTL_TXTHRESH; if (txthresh < 0xFF) { txthresh++; txfctl &= ~SF_TXFRMCTL_TXTHRESH; txfctl |= txthresh; #ifdef DIAGNOSTIC printf("sf%d: tx underrun, increasing " "tx threshold to %d bytes\n", sc->sf_unit, txthresh * 4); #endif csr_write_4(sc, SF_TX_FRAMCTL, txfctl); } return; } static void sf_intr(arg) void *arg; { struct sf_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; if (!(csr_read_4(sc, SF_ISR_SHADOW) & SF_ISR_PCIINT_ASSERTED)) { SF_UNLOCK(sc); return; } /* Disable interrupts. */ csr_write_4(sc, SF_IMR, 0x00000000); for (;;) { status = csr_read_4(sc, SF_ISR); if (status) csr_write_4(sc, SF_ISR, status); if (!(status & SF_INTRS)) break; if (status & SF_ISR_RXDQ1_DMADONE) sf_rxeof(sc); if (status & SF_ISR_TX_TXDONE || status & SF_ISR_TX_DMADONE || status & SF_ISR_TX_QUEUEDONE) sf_txeof(sc); if (status & SF_ISR_TX_LOFIFO) sf_txthresh_adjust(sc); if (status & SF_ISR_ABNORMALINTR) { if (status & SF_ISR_STATSOFLOW) { untimeout(sf_stats_update, sc, sc->sf_stat_ch); sf_stats_update(sc); } else sf_init(sc); } } /* Re-enable interrupts. */ csr_write_4(sc, SF_IMR, SF_INTRS); if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); SF_UNLOCK(sc); return; } static void sf_init(xsc) void *xsc; { struct sf_softc *sc; struct ifnet *ifp; struct mii_data *mii; int i; sc = xsc; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->sf_miibus); sf_stop(sc); sf_reset(sc); /* Init all the receive filter registers */ for (i = SF_RXFILT_PERFECT_BASE; i < (SF_RXFILT_HASH_MAX + 1); i += 4) csr_write_4(sc, i, 0); /* Empty stats counter registers. */ for (i = 0; i < sizeof(struct sf_stats)/sizeof(u_int32_t); i++) csr_write_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t)), 0); /* Init our MAC address */ csr_write_4(sc, SF_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); csr_write_4(sc, SF_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); sf_setperf(sc, 0, (caddr_t)&sc->arpcom.ac_enaddr); if (sf_init_rx_ring(sc) == ENOBUFS) { printf("sf%d: initialization failed: no " "memory for rx buffers\n", sc->sf_unit); SF_UNLOCK(sc); return; } sf_init_tx_ring(sc); csr_write_4(sc, SF_RXFILT, SF_PERFMODE_NORMAL|SF_HASHMODE_WITHVLAN); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } if (ifp->if_flags & IFF_BROADCAST) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); } else { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); } /* * Load the multicast filter. */ sf_setmulti(sc); /* Init the completion queue indexes */ csr_write_4(sc, SF_CQ_CONSIDX, 0); csr_write_4(sc, SF_CQ_PRODIDX, 0); /* Init the RX completion queue */ csr_write_4(sc, SF_RXCQ_CTL_1, vtophys(sc->sf_ldata->sf_rx_clist) & SF_RXCQ_ADDR); SF_SETBIT(sc, SF_RXCQ_CTL_1, SF_RXCQTYPE_3); /* Init RX DMA control. */ SF_SETBIT(sc, SF_RXDMA_CTL, SF_RXDMA_REPORTBADPKTS); /* Init the RX buffer descriptor queue. */ csr_write_4(sc, SF_RXDQ_ADDR_Q1, vtophys(sc->sf_ldata->sf_rx_dlist_big)); csr_write_4(sc, SF_RXDQ_CTL_1, (MCLBYTES << 16) | SF_DESCSPACE_16BYTES); csr_write_4(sc, SF_RXDQ_PTR_Q1, SF_RX_DLIST_CNT - 1); /* Init the TX completion queue */ csr_write_4(sc, SF_TXCQ_CTL, vtophys(sc->sf_ldata->sf_tx_clist) & SF_RXCQ_ADDR); /* Init the TX buffer descriptor queue. */ csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, vtophys(sc->sf_ldata->sf_tx_dlist)); SF_SETBIT(sc, SF_TX_FRAMCTL, SF_TXFRMCTL_CPLAFTERTX); csr_write_4(sc, SF_TXDQ_CTL, SF_TXBUFDESC_TYPE0|SF_TXMINSPACE_128BYTES|SF_TXSKIPLEN_8BYTES); SF_SETBIT(sc, SF_TXDQ_CTL, SF_TXDQCTL_NODMACMP); /* Enable autopadding of short TX frames. */ SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_AUTOPAD); /* Enable interrupts. */ csr_write_4(sc, SF_IMR, SF_INTRS); SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_INTR_ENB); /* Enable the RX and TX engines. */ SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_RX_ENB|SF_ETHCTL_RXDMA_ENB); SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_TX_ENB|SF_ETHCTL_TXDMA_ENB); /*mii_mediachg(mii);*/ sf_ifmedia_upd(ifp); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); SF_UNLOCK(sc); return; } static int sf_encap(sc, c, m_head) struct sf_softc *sc; struct sf_tx_bufdesc_type0 *c; struct mbuf *m_head; { int frag = 0; struct sf_frag *f = NULL; struct mbuf *m; m = m_head; for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == SF_MAXFRAGS) break; f = &c->sf_frags[frag]; if (frag == 0) f->sf_pktlen = m_head->m_pkthdr.len; f->sf_fraglen = m->m_len; f->sf_addr = vtophys(mtod(m, vm_offset_t)); frag++; } } if (m != NULL) { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("sf%d: no memory for tx list\n", sc->sf_unit); return(1); } if (m_head->m_pkthdr.len > MHLEN) { MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); printf("sf%d: no memory for tx list\n", sc->sf_unit); return(1); } } m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; m_freem(m_head); m_head = m_new; f = &c->sf_frags[0]; f->sf_fraglen = f->sf_pktlen = m_head->m_pkthdr.len; f->sf_addr = vtophys(mtod(m_head, caddr_t)); frag = 1; } c->sf_mbuf = m_head; c->sf_id = SF_TX_BUFDESC_ID; c->sf_fragcnt = frag; c->sf_intr = 1; c->sf_caltcp = 0; c->sf_crcen = 1; return(0); } static void sf_start(ifp) struct ifnet *ifp; { struct sf_softc *sc; struct sf_tx_bufdesc_type0 *cur_tx = NULL; struct mbuf *m_head = NULL; int i, txprod; sc = ifp->if_softc; SF_LOCK(sc); if (!sc->sf_link && ifp->if_snd.ifq_len < 10) { SF_UNLOCK(sc); return; } if (ifp->if_flags & IFF_OACTIVE) { SF_UNLOCK(sc); return; } txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); i = SF_IDX_HI(txprod) >> 4; if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { printf("sf%d: TX ring full, resetting\n", sc->sf_unit); sf_init(sc); txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); i = SF_IDX_HI(txprod) >> 4; } while(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf == NULL) { if (sc->sf_tx_cnt >= (SF_TX_DLIST_CNT - 5)) { ifp->if_flags |= IFF_OACTIVE; cur_tx = NULL; break; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; cur_tx = &sc->sf_ldata->sf_tx_dlist[i]; if (sf_encap(sc, cur_tx, m_head)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; cur_tx = NULL; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); SF_INC(i, SF_TX_DLIST_CNT); sc->sf_tx_cnt++; /* * Don't get the TX DMA queue get too full. */ if (sc->sf_tx_cnt > 64) break; } if (cur_tx == NULL) { SF_UNLOCK(sc); return; } /* Transmit */ csr_write_4(sc, SF_TXDQ_PRODIDX, (txprod & ~SF_TXDQ_PRODIDX_HIPRIO) | ((i << 20) & 0xFFFF0000)); ifp->if_timer = 5; SF_UNLOCK(sc); return; } static void sf_stop(sc) struct sf_softc *sc; { int i; struct ifnet *ifp; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; untimeout(sf_stats_update, sc, sc->sf_stat_ch); csr_write_4(sc, SF_GEN_ETH_CTL, 0); csr_write_4(sc, SF_CQ_CONSIDX, 0); csr_write_4(sc, SF_CQ_PRODIDX, 0); csr_write_4(sc, SF_RXDQ_ADDR_Q1, 0); csr_write_4(sc, SF_RXDQ_CTL_1, 0); csr_write_4(sc, SF_RXDQ_PTR_Q1, 0); csr_write_4(sc, SF_TXCQ_CTL, 0); csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, 0); csr_write_4(sc, SF_TXDQ_CTL, 0); sf_reset(sc); sc->sf_link = 0; for (i = 0; i < SF_RX_DLIST_CNT; i++) { if (sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf != NULL) { m_freem(sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf); sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf = NULL; } } for (i = 0; i < SF_TX_DLIST_CNT; i++) { if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { m_freem(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf); sc->sf_ldata->sf_tx_dlist[i].sf_mbuf = NULL; } } ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); SF_UNLOCK(sc); return; } /* * Note: it is important that this function not be interrupted. We * use a two-stage register access scheme: if we are interrupted in * between setting the indirect address register and reading from the * indirect data register, the contents of the address register could * be changed out from under us. */ static void sf_stats_update(xsc) void *xsc; { struct sf_softc *sc; struct ifnet *ifp; struct mii_data *mii; struct sf_stats stats; u_int32_t *ptr; int i; sc = xsc; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->sf_miibus); ptr = (u_int32_t *)&stats; for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) ptr[i] = csr_read_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t))); for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) csr_write_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t)), 0); ifp->if_collisions += stats.sf_tx_single_colls + stats.sf_tx_multi_colls + stats.sf_tx_excess_colls; mii_tick(mii); if (!sc->sf_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sf_link++; if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); } sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); SF_UNLOCK(sc); return; } static void sf_watchdog(ifp) struct ifnet *ifp; { struct sf_softc *sc; sc = ifp->if_softc; SF_LOCK(sc); ifp->if_oerrors++; printf("sf%d: watchdog timeout\n", sc->sf_unit); sf_stop(sc); sf_reset(sc); sf_init(sc); if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); SF_UNLOCK(sc); return; } static void sf_shutdown(dev) device_t dev; { struct sf_softc *sc; sc = device_get_softc(dev); sf_stop(sc); return; } Index: head/sys/dev/sk/if_sk.c =================================================================== --- head/sys/dev/sk/if_sk.c (revision 131252) +++ head/sys/dev/sk/if_sk.c (revision 131253) @@ -1,2695 +1,2671 @@ /* $OpenBSD: if_sk.c,v 2.33 2003/08/12 05:23:06 nate Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2003 Nathan L. Binkert * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * SysKonnect SK-NET gigabit ethernet driver for FreeBSD. Supports * the SK-984x series adapters, both single port and dual port. * References: * The XaQti XMAC II datasheet, * http://www.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf * The SysKonnect GEnesis manual, http://www.syskonnect.com * * Note: XaQti has been aquired by Vitesse, and Vitesse does not have the * XMAC II datasheet online. I have put my copy at people.freebsd.org as a * convenience to others until Vitesse corrects this problem: * * http://people.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf * * Written by Bill Paul * Department of Electrical Engineering * Columbia University, New York City */ /* * The SysKonnect gigabit ethernet adapters consist of two main * components: the SysKonnect GEnesis controller chip and the XaQti Corp. * XMAC II gigabit ethernet MAC. The XMAC provides all of the MAC * components and a PHY while the GEnesis controller provides a PCI * interface with DMA support. Each card may have between 512K and * 2MB of SRAM on board depending on the configuration. * * The SysKonnect GEnesis controller can have either one or two XMAC * chips connected to it, allowing single or dual port NIC configurations. * SysKonnect has the distinction of being the only vendor on the market * with a dual port gigabit ethernet NIC. The GEnesis provides dual FIFOs, * dual DMA queues, packet/MAC/transmit arbiters and direct access to the * XMAC registers. This driver takes advantage of these features to allow * both XMACs to operate as independent interfaces. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include #include #if 0 #define SK_USEIOSPACE #endif #include #include #include MODULE_DEPEND(sk, pci, 1, 1, 1); MODULE_DEPEND(sk, ether, 1, 1, 1); MODULE_DEPEND(sk, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif static struct sk_type sk_devs[] = { { VENDORID_SK, DEVICEID_SK_V1, "SysKonnect Gigabit Ethernet (V1.0)" }, { VENDORID_SK, DEVICEID_SK_V2, "SysKonnect Gigabit Ethernet (V2.0)" }, { VENDORID_MARVELL, DEVICEID_SK_V2, "Marvell Gigabit Ethernet" }, { VENDORID_3COM, DEVICEID_3COM_3C940, "3Com 3C940 Gigabit Ethernet" }, { VENDORID_LINKSYS, DEVICEID_LINKSYS_EG1032, "Linksys EG1032 Gigabit Ethernet" }, { VENDORID_DLINK, DEVICEID_DLINK_DGE530T, "D-Link DGE-530T Gigabit Ethernet" }, { 0, 0, NULL } }; static int skc_probe (device_t); static int skc_attach (device_t); static int skc_detach (device_t); static void skc_shutdown (device_t); static int sk_detach (device_t); static int sk_probe (device_t); static int sk_attach (device_t); static void sk_tick (void *); static void sk_intr (void *); static void sk_intr_xmac (struct sk_if_softc *); static void sk_intr_bcom (struct sk_if_softc *); static void sk_intr_yukon (struct sk_if_softc *); static void sk_rxeof (struct sk_if_softc *); static void sk_txeof (struct sk_if_softc *); static int sk_encap (struct sk_if_softc *, struct mbuf *, u_int32_t *); static void sk_start (struct ifnet *); static int sk_ioctl (struct ifnet *, u_long, caddr_t); static void sk_init (void *); static void sk_init_xmac (struct sk_if_softc *); static void sk_init_yukon (struct sk_if_softc *); static void sk_stop (struct sk_if_softc *); static void sk_watchdog (struct ifnet *); static int sk_ifmedia_upd (struct ifnet *); static void sk_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void sk_reset (struct sk_softc *); static int sk_newbuf (struct sk_if_softc *, struct sk_chain *, struct mbuf *); static int sk_alloc_jumbo_mem (struct sk_if_softc *); static void *sk_jalloc (struct sk_if_softc *); static void sk_jfree (void *, void *); static int sk_init_rx_ring (struct sk_if_softc *); static void sk_init_tx_ring (struct sk_if_softc *); static u_int32_t sk_win_read_4 (struct sk_softc *, int); static u_int16_t sk_win_read_2 (struct sk_softc *, int); static u_int8_t sk_win_read_1 (struct sk_softc *, int); static void sk_win_write_4 (struct sk_softc *, int, u_int32_t); static void sk_win_write_2 (struct sk_softc *, int, u_int32_t); static void sk_win_write_1 (struct sk_softc *, int, u_int32_t); static u_int8_t sk_vpd_readbyte (struct sk_softc *, int); static void sk_vpd_read_res (struct sk_softc *, struct vpd_res *, int); static void sk_vpd_read (struct sk_softc *); static int sk_miibus_readreg (device_t, int, int); static int sk_miibus_writereg (device_t, int, int, int); static void sk_miibus_statchg (device_t); static int sk_xmac_miibus_readreg (struct sk_if_softc *, int, int); static int sk_xmac_miibus_writereg (struct sk_if_softc *, int, int, int); static void sk_xmac_miibus_statchg (struct sk_if_softc *); static int sk_marv_miibus_readreg (struct sk_if_softc *, int, int); static int sk_marv_miibus_writereg (struct sk_if_softc *, int, int, int); static void sk_marv_miibus_statchg (struct sk_if_softc *); static uint32_t sk_xmchash (const uint8_t *); static uint32_t sk_gmchash (const uint8_t *); static void sk_setfilt (struct sk_if_softc *, caddr_t, int); static void sk_setmulti (struct sk_if_softc *); static void sk_setpromisc (struct sk_if_softc *); #ifdef SK_USEIOSPACE #define SK_RES SYS_RES_IOPORT #define SK_RID SK_PCI_LOIO #else #define SK_RES SYS_RES_MEMORY #define SK_RID SK_PCI_LOMEM #endif /* * Note that we have newbus methods for both the GEnesis controller * itself and the XMAC(s). The XMACs are children of the GEnesis, and * the miibus code is a child of the XMACs. We need to do it this way * so that the miibus drivers can access the PHY registers on the * right PHY. It's not quite what I had in mind, but it's the only * design that achieves the desired effect. */ static device_method_t skc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, skc_probe), DEVMETHOD(device_attach, skc_attach), DEVMETHOD(device_detach, skc_detach), DEVMETHOD(device_shutdown, skc_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), { 0, 0 } }; static driver_t skc_driver = { "skc", skc_methods, sizeof(struct sk_softc) }; static devclass_t skc_devclass; static device_method_t sk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sk_probe), DEVMETHOD(device_attach, sk_attach), DEVMETHOD(device_detach, sk_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sk_miibus_readreg), DEVMETHOD(miibus_writereg, sk_miibus_writereg), DEVMETHOD(miibus_statchg, sk_miibus_statchg), { 0, 0 } }; static driver_t sk_driver = { "sk", sk_methods, sizeof(struct sk_if_softc) }; static devclass_t sk_devclass; DRIVER_MODULE(sk, pci, skc_driver, skc_devclass, 0, 0); DRIVER_MODULE(sk, skc, sk_driver, sk_devclass, 0, 0); DRIVER_MODULE(miibus, sk, miibus_driver, miibus_devclass, 0, 0); #define SK_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x) #define SK_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~x) #define SK_WIN_SETBIT_4(sc, reg, x) \ sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) | x) #define SK_WIN_CLRBIT_4(sc, reg, x) \ sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) & ~x) #define SK_WIN_SETBIT_2(sc, reg, x) \ sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) | x) #define SK_WIN_CLRBIT_2(sc, reg, x) \ sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) & ~x) static u_int32_t sk_win_read_4(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_4(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_4(sc, reg)); #endif } static u_int16_t sk_win_read_2(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_2(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_2(sc, reg)); #endif } static u_int8_t sk_win_read_1(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_1(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_1(sc, reg)); #endif } static void sk_win_write_4(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_4(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_4(sc, reg, val); #endif return; } static void sk_win_write_2(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_2(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_2(sc, reg, val); #endif return; } static void sk_win_write_1(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_1(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_1(sc, reg, val); #endif return; } /* * The VPD EEPROM contains Vital Product Data, as suggested in * the PCI 2.1 specification. The VPD data is separared into areas * denoted by resource IDs. The SysKonnect VPD contains an ID string * resource (the name of the adapter), a read-only area resource * containing various key/data fields and a read/write area which * can be used to store asset management information or log messages. * We read the ID string and read-only into buffers attached to * the controller softc structure for later use. At the moment, * we only use the ID string during skc_attach(). */ static u_int8_t sk_vpd_readbyte(sc, addr) struct sk_softc *sc; int addr; { int i; sk_win_write_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR), addr); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (sk_win_read_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR)) & SK_VPD_FLAG) break; } if (i == SK_TIMEOUT) return(0); return(sk_win_read_1(sc, SK_PCI_REG(SK_PCI_VPD_DATA))); } static void sk_vpd_read_res(sc, res, addr) struct sk_softc *sc; struct vpd_res *res; int addr; { int i; u_int8_t *ptr; ptr = (u_int8_t *)res; for (i = 0; i < sizeof(struct vpd_res); i++) ptr[i] = sk_vpd_readbyte(sc, i + addr); return; } static void sk_vpd_read(sc) struct sk_softc *sc; { int pos = 0, i; struct vpd_res res; if (sc->sk_vpd_prodname != NULL) free(sc->sk_vpd_prodname, M_DEVBUF); if (sc->sk_vpd_readonly != NULL) free(sc->sk_vpd_readonly, M_DEVBUF); sc->sk_vpd_prodname = NULL; sc->sk_vpd_readonly = NULL; sk_vpd_read_res(sc, &res, pos); /* * Bail out quietly if the eeprom appears to be missing or empty. */ if (res.vr_id == 0xff && res.vr_len == 0xff && res.vr_pad == 0xff) return; if (res.vr_id != VPD_RES_ID) { printf("skc%d: bad VPD resource id: expected %x got %x\n", sc->sk_unit, VPD_RES_ID, res.vr_id); return; } pos += sizeof(res); sc->sk_vpd_prodname = malloc(res.vr_len + 1, M_DEVBUF, M_NOWAIT); for (i = 0; i < res.vr_len; i++) sc->sk_vpd_prodname[i] = sk_vpd_readbyte(sc, i + pos); sc->sk_vpd_prodname[i] = '\0'; pos += i; sk_vpd_read_res(sc, &res, pos); if (res.vr_id != VPD_RES_READ) { printf("skc%d: bad VPD resource id: expected %x got %x\n", sc->sk_unit, VPD_RES_READ, res.vr_id); return; } pos += sizeof(res); sc->sk_vpd_readonly = malloc(res.vr_len, M_DEVBUF, M_NOWAIT); for (i = 0; i < res.vr_len + 1; i++) sc->sk_vpd_readonly[i] = sk_vpd_readbyte(sc, i + pos); return; } static int sk_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: return(sk_xmac_miibus_readreg(sc_if, phy, reg)); case SK_YUKON: return(sk_marv_miibus_readreg(sc_if, phy, reg)); } return(0); } static int sk_miibus_writereg(dev, phy, reg, val) device_t dev; int phy, reg, val; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: return(sk_xmac_miibus_writereg(sc_if, phy, reg, val)); case SK_YUKON: return(sk_marv_miibus_writereg(sc_if, phy, reg, val)); } return(0); } static void sk_miibus_statchg(dev) device_t dev; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: sk_xmac_miibus_statchg(sc_if); break; case SK_YUKON: sk_marv_miibus_statchg(sc_if); break; } return; } static int sk_xmac_miibus_readreg(sc_if, phy, reg) struct sk_if_softc *sc_if; int phy, reg; { int i; if (sc_if->sk_phytype == SK_PHYTYPE_XMAC && phy != 0) return(0); SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); SK_XM_READ_2(sc_if, XM_PHY_DATA); if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYDATARDY) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(0); } } DELAY(1); i = SK_XM_READ_2(sc_if, XM_PHY_DATA); SK_IF_UNLOCK(sc_if); return(i); } static int sk_xmac_miibus_writereg(sc_if, phy, reg, val) struct sk_if_softc *sc_if; int phy, reg, val; { int i; SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); for (i = 0; i < SK_TIMEOUT; i++) { if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(ETIMEDOUT); } SK_XM_WRITE_2(sc_if, XM_PHY_DATA, val); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) break; } SK_IF_UNLOCK(sc_if); if (i == SK_TIMEOUT) printf("sk%d: phy write timed out\n", sc_if->sk_unit); return(0); } static void sk_xmac_miibus_statchg(sc_if) struct sk_if_softc *sc_if; { struct mii_data *mii; mii = device_get_softc(sc_if->sk_miibus); SK_IF_LOCK(sc_if); /* * If this is a GMII PHY, manually set the XMAC's * duplex mode accordingly. */ if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); } else { SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); } } SK_IF_UNLOCK(sc_if); return; } static int sk_marv_miibus_readreg(sc_if, phy, reg) struct sk_if_softc *sc_if; int phy, reg; { u_int16_t val; int i; if (phy != 0 || (sc_if->sk_phytype != SK_PHYTYPE_MARV_COPPER && sc_if->sk_phytype != SK_PHYTYPE_MARV_FIBER)) { return(0); } SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_READ); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); val = SK_YU_READ_2(sc_if, YUKON_SMICR); if (val & YU_SMICR_READ_VALID) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(0); } val = SK_YU_READ_2(sc_if, YUKON_SMIDR); SK_IF_UNLOCK(sc_if); return(val); } static int sk_marv_miibus_writereg(sc_if, phy, reg, val) struct sk_if_softc *sc_if; int phy, reg, val; { int i; SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMIDR, val); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_WRITE); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (SK_YU_READ_2(sc_if, YUKON_SMICR) & YU_SMICR_BUSY) break; } SK_IF_UNLOCK(sc_if); return(0); } static void sk_marv_miibus_statchg(sc_if) struct sk_if_softc *sc_if; { return; } #define HASH_BITS 6 static u_int32_t sk_xmchash(addr) const uint8_t *addr; { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); return (~crc & ((1 << HASH_BITS) - 1)); } /* gmchash is just a big endian crc */ static u_int32_t sk_gmchash(addr) const uint8_t *addr; { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_be(addr, ETHER_ADDR_LEN); return (crc & ((1 << HASH_BITS) - 1)); } static void sk_setfilt(sc_if, addr, slot) struct sk_if_softc *sc_if; caddr_t addr; int slot; { int base; base = XM_RXFILT_ENTRY(slot); SK_XM_WRITE_2(sc_if, base, *(u_int16_t *)(&addr[0])); SK_XM_WRITE_2(sc_if, base + 2, *(u_int16_t *)(&addr[2])); SK_XM_WRITE_2(sc_if, base + 4, *(u_int16_t *)(&addr[4])); return; } static void sk_setmulti(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc = sc_if->sk_softc; struct ifnet *ifp = &sc_if->arpcom.ac_if; u_int32_t hashes[2] = { 0, 0 }; int h = 0, i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; /* First, zot all the existing filters. */ switch(sc->sk_type) { case SK_GENESIS: for (i = 1; i < XM_RXFILT_MAX; i++) sk_setfilt(sc_if, (caddr_t)&dummy, i); SK_XM_WRITE_4(sc_if, XM_MAR0, 0); SK_XM_WRITE_4(sc_if, XM_MAR2, 0); break; case SK_YUKON: SK_YU_WRITE_2(sc_if, YUKON_MCAH1, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH2, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH3, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH4, 0); break; } /* Now program new ones. */ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { hashes[0] = 0xFFFFFFFF; hashes[1] = 0xFFFFFFFF; } else { i = 1; TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Program the first XM_RXFILT_MAX multicast groups * into the perfect filter. For all others, * use the hash table. */ if (sc->sk_type == SK_GENESIS && i < XM_RXFILT_MAX) { sk_setfilt(sc_if, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); i++; continue; } switch(sc->sk_type) { case SK_GENESIS: h = sk_xmchash( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); break; case SK_YUKON: h = sk_gmchash( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); break; } if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } } switch(sc->sk_type) { case SK_GENESIS: SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_HASH| XM_MODE_RX_USE_PERFECT); SK_XM_WRITE_4(sc_if, XM_MAR0, hashes[0]); SK_XM_WRITE_4(sc_if, XM_MAR2, hashes[1]); break; case SK_YUKON: SK_YU_WRITE_2(sc_if, YUKON_MCAH1, hashes[0] & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH2, (hashes[0] >> 16) & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH3, hashes[1] & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH4, (hashes[1] >> 16) & 0xffff); break; } return; } static void sk_setpromisc(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc = sc_if->sk_softc; struct ifnet *ifp = &sc_if->arpcom.ac_if; switch(sc->sk_type) { case SK_GENESIS: if (ifp->if_flags & IFF_PROMISC) { SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); } else { SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); } break; case SK_YUKON: if (ifp->if_flags & IFF_PROMISC) { SK_YU_CLRBIT_2(sc_if, YUKON_RCR, YU_RCR_UFLEN | YU_RCR_MUFLEN); } else { SK_YU_SETBIT_2(sc_if, YUKON_RCR, YU_RCR_UFLEN | YU_RCR_MUFLEN); } break; } return; } static int sk_init_rx_ring(sc_if) struct sk_if_softc *sc_if; { struct sk_chain_data *cd = &sc_if->sk_cdata; struct sk_ring_data *rd = sc_if->sk_rdata; int i; bzero((char *)rd->sk_rx_ring, sizeof(struct sk_rx_desc) * SK_RX_RING_CNT); for (i = 0; i < SK_RX_RING_CNT; i++) { cd->sk_rx_chain[i].sk_desc = &rd->sk_rx_ring[i]; if (sk_newbuf(sc_if, &cd->sk_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (SK_RX_RING_CNT - 1)) { cd->sk_rx_chain[i].sk_next = &cd->sk_rx_chain[0]; rd->sk_rx_ring[i].sk_next = vtophys(&rd->sk_rx_ring[0]); } else { cd->sk_rx_chain[i].sk_next = &cd->sk_rx_chain[i + 1]; rd->sk_rx_ring[i].sk_next = vtophys(&rd->sk_rx_ring[i + 1]); } } sc_if->sk_cdata.sk_rx_prod = 0; sc_if->sk_cdata.sk_rx_cons = 0; return(0); } static void sk_init_tx_ring(sc_if) struct sk_if_softc *sc_if; { struct sk_chain_data *cd = &sc_if->sk_cdata; struct sk_ring_data *rd = sc_if->sk_rdata; int i; bzero((char *)sc_if->sk_rdata->sk_tx_ring, sizeof(struct sk_tx_desc) * SK_TX_RING_CNT); for (i = 0; i < SK_TX_RING_CNT; i++) { cd->sk_tx_chain[i].sk_desc = &rd->sk_tx_ring[i]; if (i == (SK_TX_RING_CNT - 1)) { cd->sk_tx_chain[i].sk_next = &cd->sk_tx_chain[0]; rd->sk_tx_ring[i].sk_next = vtophys(&rd->sk_tx_ring[0]); } else { cd->sk_tx_chain[i].sk_next = &cd->sk_tx_chain[i + 1]; rd->sk_tx_ring[i].sk_next = vtophys(&rd->sk_tx_ring[i + 1]); } } sc_if->sk_cdata.sk_tx_prod = 0; sc_if->sk_cdata.sk_tx_cons = 0; sc_if->sk_cdata.sk_tx_cnt = 0; return; } static int sk_newbuf(sc_if, c, m) struct sk_if_softc *sc_if; struct sk_chain *c; struct mbuf *m; { struct mbuf *m_new = NULL; struct sk_rx_desc *r; if (m == NULL) { caddr_t *buf = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); /* Allocate the jumbo buffer */ buf = sk_jalloc(sc_if); if (buf == NULL) { m_freem(m_new); #ifdef SK_VERBOSE printf("sk%d: jumbo allocation failed " "-- packet dropped!\n", sc_if->sk_unit); #endif return(ENOBUFS); } /* Attach the buffer to the mbuf */ MEXTADD(m_new, buf, SK_JLEN, sk_jfree, (struct sk_if_softc *)sc_if, 0, EXT_NET_DRV); m_new->m_data = (void *)buf; m_new->m_pkthdr.len = m_new->m_len = SK_JLEN; } else { /* * We're re-using a previously allocated mbuf; * be sure to re-init pointers and lengths to * default values. */ m_new = m; m_new->m_len = m_new->m_pkthdr.len = SK_JLEN; m_new->m_data = m_new->m_ext.ext_buf; } /* * Adjust alignment so packet payload begins on a * longword boundary. Mandatory for Alpha, useful on * x86 too. */ m_adj(m_new, ETHER_ALIGN); r = c->sk_desc; c->sk_mbuf = m_new; r->sk_data_lo = vtophys(mtod(m_new, caddr_t)); r->sk_ctl = m_new->m_len | SK_RXSTAT; return(0); } /* * Allocate jumbo buffer storage. The SysKonnect adapters support * "jumbograms" (9K frames), although SysKonnect doesn't currently * use them in their drivers. In order for us to use them, we need * large 9K receive buffers, however standard mbuf clusters are only * 2048 bytes in size. Consequently, we need to allocate and manage * our own jumbo buffer pool. Fortunately, this does not require an * excessive amount of additional code. */ static int sk_alloc_jumbo_mem(sc_if) struct sk_if_softc *sc_if; { caddr_t ptr; register int i; struct sk_jpool_entry *entry; /* Grab a big chunk o' storage. */ sc_if->sk_cdata.sk_jumbo_buf = contigmalloc(SK_JMEM, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc_if->sk_cdata.sk_jumbo_buf == NULL) { printf("sk%d: no memory for jumbo buffers!\n", sc_if->sk_unit); return(ENOBUFS); } SLIST_INIT(&sc_if->sk_jfree_listhead); SLIST_INIT(&sc_if->sk_jinuse_listhead); /* * Now divide it up into 9K pieces and save the addresses * in an array. */ ptr = sc_if->sk_cdata.sk_jumbo_buf; for (i = 0; i < SK_JSLOTS; i++) { sc_if->sk_cdata.sk_jslots[i] = ptr; ptr += SK_JLEN; entry = malloc(sizeof(struct sk_jpool_entry), M_DEVBUF, M_NOWAIT); if (entry == NULL) { free(sc_if->sk_cdata.sk_jumbo_buf, M_DEVBUF); sc_if->sk_cdata.sk_jumbo_buf = NULL; printf("sk%d: no memory for jumbo " "buffer queue!\n", sc_if->sk_unit); return(ENOBUFS); } entry->slot = i; SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); } return(0); } /* * Allocate a jumbo buffer. */ static void * sk_jalloc(sc_if) struct sk_if_softc *sc_if; { struct sk_jpool_entry *entry; entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); if (entry == NULL) { #ifdef SK_VERBOSE printf("sk%d: no free jumbo buffers\n", sc_if->sk_unit); #endif return(NULL); } SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc_if->sk_jinuse_listhead, entry, jpool_entries); return(sc_if->sk_cdata.sk_jslots[entry->slot]); } /* * Release a jumbo buffer. */ static void sk_jfree(buf, args) void *buf; void *args; { struct sk_if_softc *sc_if; int i; struct sk_jpool_entry *entry; /* Extract the softc struct pointer. */ sc_if = (struct sk_if_softc *)args; if (sc_if == NULL) panic("sk_jfree: didn't get softc pointer!"); /* calculate the slot this buffer belongs to */ i = ((vm_offset_t)buf - (vm_offset_t)sc_if->sk_cdata.sk_jumbo_buf) / SK_JLEN; if ((i < 0) || (i >= SK_JSLOTS)) panic("sk_jfree: asked to free buffer that we don't manage!"); entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead); if (entry == NULL) panic("sk_jfree: buffer not in use!"); entry->slot = i; SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); return; } /* * Set media options. */ static int sk_ifmedia_upd(ifp) struct ifnet *ifp; { struct sk_if_softc *sc_if = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc_if->sk_miibus); sk_init(sc_if); mii_mediachg(mii); return(0); } /* * Report current media status. */ static void sk_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sk_if_softc *sc_if; struct mii_data *mii; sc_if = ifp->if_softc; mii = device_get_softc(sc_if->sk_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int sk_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sk_if_softc *sc_if = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int error = 0; struct mii_data *mii; SK_IF_LOCK(sc_if); switch(command) { case SIOCSIFMTU: if (ifr->ifr_mtu > SK_JUMBO_MTU) error = EINVAL; else { ifp->if_mtu = ifr->ifr_mtu; sk_init(sc_if); } break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) { if ((ifp->if_flags ^ sc_if->sk_if_flags) & IFF_PROMISC) { sk_setpromisc(sc_if); sk_setmulti(sc_if); } } else sk_init(sc_if); } else { if (ifp->if_flags & IFF_RUNNING) sk_stop(sc_if); } sc_if->sk_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: sk_setmulti(sc_if); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc_if->sk_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } SK_IF_UNLOCK(sc_if); return(error); } /* * Probe for a SysKonnect GEnesis chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int skc_probe(dev) device_t dev; { struct sk_softc *sc; struct sk_type *t = sk_devs; sc = device_get_softc(dev); while(t->sk_name != NULL) { if ((pci_get_vendor(dev) == t->sk_vid) && (pci_get_device(dev) == t->sk_did)) { device_set_desc(dev, t->sk_name); return(0); } t++; } return(ENXIO); } /* * Force the GEnesis into reset, then bring it out of reset. */ static void sk_reset(sc) struct sk_softc *sc; { CSR_WRITE_2(sc, SK_CSR, SK_CSR_SW_RESET); CSR_WRITE_2(sc, SK_CSR, SK_CSR_MASTER_RESET); if (sc->sk_type == SK_YUKON) CSR_WRITE_2(sc, SK_LINK_CTRL, SK_LINK_RESET_SET); DELAY(1000); CSR_WRITE_2(sc, SK_CSR, SK_CSR_SW_UNRESET); DELAY(2); CSR_WRITE_2(sc, SK_CSR, SK_CSR_MASTER_UNRESET); if (sc->sk_type == SK_YUKON) CSR_WRITE_2(sc, SK_LINK_CTRL, SK_LINK_RESET_CLEAR); if (sc->sk_type == SK_GENESIS) { /* Configure packet arbiter */ sk_win_write_2(sc, SK_PKTARB_CTL, SK_PKTARBCTL_UNRESET); sk_win_write_2(sc, SK_RXPA1_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_TXPA1_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_RXPA2_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_TXPA2_TINIT, SK_PKTARB_TIMEOUT); } /* Enable RAM interface */ sk_win_write_4(sc, SK_RAMCTL, SK_RAMCTL_UNRESET); /* * Configure interrupt moderation. The moderation timer * defers interrupts specified in the interrupt moderation * timer mask based on the timeout specified in the interrupt * moderation timer init register. Each bit in the timer * register represents 18.825ns, so to specify a timeout in * microseconds, we have to multiply by 54. */ sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(200)); sk_win_write_4(sc, SK_IMMR, SK_ISR_TX1_S_EOF|SK_ISR_TX2_S_EOF| SK_ISR_RX1_EOF|SK_ISR_RX2_EOF); sk_win_write_1(sc, SK_IMTIMERCTL, SK_IMCTL_START); return; } static int sk_probe(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(device_get_parent(dev)); /* * Not much to do here. We always know there will be * at least one XMAC present, and if there are two, * skc_attach() will create a second device instance * for us. */ switch (sc->sk_type) { case SK_GENESIS: device_set_desc(dev, "XaQti Corp. XMAC II"); break; case SK_YUKON: device_set_desc(dev, "Marvell Semiconductor, Inc. Yukon"); break; } return(0); } /* * Each XMAC chip is attached as a separate logical IP interface. * Single port cards will have only one logical interface of course. */ static int sk_attach(dev) device_t dev; { struct sk_softc *sc; struct sk_if_softc *sc_if; struct ifnet *ifp; int i, port, error; if (dev == NULL) return(EINVAL); error = 0; sc_if = device_get_softc(dev); sc = device_get_softc(device_get_parent(dev)); SK_LOCK(sc); port = *(int *)device_get_ivars(dev); free(device_get_ivars(dev), M_DEVBUF); device_set_ivars(dev, NULL); sc_if->sk_dev = dev; sc_if->sk_unit = device_get_unit(dev); sc_if->sk_port = port; sc_if->sk_softc = sc; sc->sk_if[port] = sc_if; if (port == SK_PORT_A) sc_if->sk_tx_bmu = SK_BMU_TXS_CSR0; if (port == SK_PORT_B) sc_if->sk_tx_bmu = SK_BMU_TXS_CSR1; /* * Get station address for this interface. Note that * dual port cards actually come with three station * addresses: one for each port, plus an extra. The * extra one is used by the SysKonnect driver software * as a 'virtual' station address for when both ports * are operating in failover mode. Currently we don't * use this extra address. */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc_if->arpcom.ac_enaddr[i] = sk_win_read_1(sc, SK_MAC0_0 + (port * 8) + i); /* * Set up RAM buffer addresses. The NIC will have a certain * amount of SRAM on it, somewhere between 512K and 2MB. We * need to divide this up a) between the transmitter and * receiver and b) between the two XMACs, if this is a * dual port NIC. Our algotithm is to divide up the memory * evenly so that everyone gets a fair share. */ if (sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC) { u_int32_t chunk, val; chunk = sc->sk_ramsize / 2; val = sc->sk_rboff / sizeof(u_int64_t); sc_if->sk_rx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_rx_ramend = val - 1; sc_if->sk_tx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_tx_ramend = val - 1; } else { u_int32_t chunk, val; chunk = sc->sk_ramsize / 4; val = (sc->sk_rboff + (chunk * 2 * sc_if->sk_port)) / sizeof(u_int64_t); sc_if->sk_rx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_rx_ramend = val - 1; sc_if->sk_tx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_tx_ramend = val - 1; } /* Read and save PHY type and set PHY address */ sc_if->sk_phytype = sk_win_read_1(sc, SK_EPROM1) & 0xF; switch(sc_if->sk_phytype) { case SK_PHYTYPE_XMAC: sc_if->sk_phyaddr = SK_PHYADDR_XMAC; break; case SK_PHYTYPE_BCOM: sc_if->sk_phyaddr = SK_PHYADDR_BCOM; break; case SK_PHYTYPE_MARV_COPPER: sc_if->sk_phyaddr = SK_PHYADDR_MARV; break; default: printf("skc%d: unsupported PHY type: %d\n", sc->sk_unit, sc_if->sk_phytype); error = ENODEV; goto fail; } /* Allocate the descriptor queues. */ sc_if->sk_rdata = contigmalloc(sizeof(struct sk_ring_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc_if->sk_rdata == NULL) { printf("sk%d: no memory for list buffers!\n", sc_if->sk_unit); error = ENOMEM; goto fail; } bzero(sc_if->sk_rdata, sizeof(struct sk_ring_data)); /* Try to allocate memory for jumbo buffers. */ if (sk_alloc_jumbo_mem(sc_if)) { printf("sk%d: jumbo buffer allocation failed\n", sc_if->sk_unit); error = ENOMEM; goto fail; } ifp = &sc_if->arpcom.ac_if; ifp->if_softc = sc_if; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sk_ioctl; ifp->if_start = sk_start; ifp->if_watchdog = sk_watchdog; ifp->if_init = sk_init; ifp->if_baudrate = 1000000000; ifp->if_snd.ifq_maxlen = SK_TX_RING_CNT - 1; callout_handle_init(&sc_if->sk_tick_ch); /* * Call MI attach routine. */ ether_ifattach(ifp, sc_if->arpcom.ac_enaddr); /* * Do miibus setup. */ switch (sc->sk_type) { case SK_GENESIS: sk_init_xmac(sc_if); break; case SK_YUKON: sk_init_yukon(sc_if); break; } if (mii_phy_probe(dev, &sc_if->sk_miibus, sk_ifmedia_upd, sk_ifmedia_sts)) { printf("skc%d: no PHY found!\n", sc_if->sk_unit); ether_ifdetach(ifp); error = ENXIO; goto fail; } fail: SK_UNLOCK(sc); if (error) { /* Access should be ok even though lock has been dropped */ sc->sk_if[port] = NULL; sk_detach(dev); } return(error); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int skc_attach(dev) device_t dev; { struct sk_softc *sc; int unit, error = 0, rid, *port; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->sk_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, SK_PCI_LOIO, 4); - membase = pci_read_config(dev, SK_PCI_LOMEM, 4); - irq = pci_read_config(dev, SK_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("skc%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, SK_PCI_LOIO, iobase, 4); - pci_write_config(dev, SK_PCI_LOMEM, membase, 4); - pci_write_config(dev, SK_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = SK_RID; sc->sk_res = bus_alloc_resource_any(dev, SK_RES, &rid, RF_ACTIVE); if (sc->sk_res == NULL) { printf("sk%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->sk_btag = rman_get_bustag(sc->sk_res); sc->sk_bhandle = rman_get_bushandle(sc->sk_res); /* Allocate interrupt */ rid = 0; sc->sk_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sk_irq == NULL) { printf("skc%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Set adapter type */ switch (pci_get_device(dev)) { case DEVICEID_SK_V1: sc->sk_type = SK_GENESIS; break; case DEVICEID_SK_V2: case DEVICEID_3COM_3C940: case DEVICEID_LINKSYS_EG1032: case DEVICEID_DLINK_DGE530T: sc->sk_type = SK_YUKON; break; default: printf("skc%d: unknown device!\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ sk_reset(sc); sc->sk_unit = unit; /* Read and save vital product data from EEPROM. */ sk_vpd_read(sc); if (sc->sk_type == SK_GENESIS) { /* Read and save RAM size and RAMbuffer offset */ switch(sk_win_read_1(sc, SK_EPROM0)) { case SK_RAMSIZE_512K_64: sc->sk_ramsize = 0x80000; sc->sk_rboff = SK_RBOFF_0; break; case SK_RAMSIZE_1024K_64: sc->sk_ramsize = 0x100000; sc->sk_rboff = SK_RBOFF_80000; break; case SK_RAMSIZE_1024K_128: sc->sk_ramsize = 0x100000; sc->sk_rboff = SK_RBOFF_0; break; case SK_RAMSIZE_2048K_128: sc->sk_ramsize = 0x200000; sc->sk_rboff = SK_RBOFF_0; break; default: printf("skc%d: unknown ram size: %d\n", sc->sk_unit, sk_win_read_1(sc, SK_EPROM0)); error = ENXIO; goto fail; } } else { sc->sk_ramsize = 0x20000; sc->sk_rboff = SK_RBOFF_0; } /* Read and save physical media type */ switch(sk_win_read_1(sc, SK_PMDTYPE)) { case SK_PMD_1000BASESX: sc->sk_pmd = IFM_1000_SX; break; case SK_PMD_1000BASELX: sc->sk_pmd = IFM_1000_LX; break; case SK_PMD_1000BASECX: sc->sk_pmd = IFM_1000_CX; break; case SK_PMD_1000BASETX: sc->sk_pmd = IFM_1000_T; break; default: printf("skc%d: unknown media type: 0x%x\n", sc->sk_unit, sk_win_read_1(sc, SK_PMDTYPE)); error = ENXIO; goto fail; } /* Announce the product name. */ if (sc->sk_vpd_prodname != NULL) printf("skc%d: %s\n", sc->sk_unit, sc->sk_vpd_prodname); sc->sk_devs[SK_PORT_A] = device_add_child(dev, "sk", -1); port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); *port = SK_PORT_A; device_set_ivars(sc->sk_devs[SK_PORT_A], port); if (!(sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC)) { sc->sk_devs[SK_PORT_B] = device_add_child(dev, "sk", -1); port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); *port = SK_PORT_B; device_set_ivars(sc->sk_devs[SK_PORT_B], port); } /* Turn on the 'driver is loaded' LED. */ CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_ON); bus_generic_attach(dev); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->sk_irq, INTR_TYPE_NET, sk_intr, sc, &sc->sk_intrhand); if (error) { printf("skc%d: couldn't set up irq\n", unit); goto fail; } fail: if (error) skc_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int sk_detach(dev) device_t dev; { struct sk_if_softc *sc_if; struct ifnet *ifp; sc_if = device_get_softc(dev); KASSERT(mtx_initialized(&sc_if->sk_softc->sk_mtx), ("sk mutex not initialized in sk_detach")); SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; /* These should only be active if attach_xmac succeeded */ if (device_is_attached(dev)) { sk_stop(sc_if); ether_ifdetach(ifp); } if (sc_if->sk_miibus) device_delete_child(dev, sc_if->sk_miibus); bus_generic_detach(dev); if (sc_if->sk_cdata.sk_jumbo_buf) contigfree(sc_if->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF); if (sc_if->sk_rdata) { contigfree(sc_if->sk_rdata, sizeof(struct sk_ring_data), M_DEVBUF); } SK_IF_UNLOCK(sc_if); return(0); } static int skc_detach(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sk_mtx), ("sk mutex not initialized")); SK_LOCK(sc); if (device_is_alive(dev)) { if (sc->sk_devs[SK_PORT_A] != NULL) device_delete_child(dev, sc->sk_devs[SK_PORT_A]); if (sc->sk_devs[SK_PORT_B] != NULL) device_delete_child(dev, sc->sk_devs[SK_PORT_B]); bus_generic_detach(dev); } if (sc->sk_intrhand) bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand); if (sc->sk_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); if (sc->sk_res) bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); SK_UNLOCK(sc); mtx_destroy(&sc->sk_mtx); return(0); } static int sk_encap(sc_if, m_head, txidx) struct sk_if_softc *sc_if; struct mbuf *m_head; u_int32_t *txidx; { struct sk_tx_desc *f = NULL; struct mbuf *m; u_int32_t frag, cur, cnt = 0; m = m_head; cur = frag = *txidx; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ for (m = m_head; m != NULL; m = m->m_next) { if (m->m_len != 0) { if ((SK_TX_RING_CNT - (sc_if->sk_cdata.sk_tx_cnt + cnt)) < 2) return(ENOBUFS); f = &sc_if->sk_rdata->sk_tx_ring[frag]; f->sk_data_lo = vtophys(mtod(m, vm_offset_t)); f->sk_ctl = m->m_len | SK_OPCODE_DEFAULT; if (cnt == 0) f->sk_ctl |= SK_TXCTL_FIRSTFRAG; else f->sk_ctl |= SK_TXCTL_OWN; cur = frag; SK_INC(frag, SK_TX_RING_CNT); cnt++; } } if (m != NULL) return(ENOBUFS); sc_if->sk_rdata->sk_tx_ring[cur].sk_ctl |= SK_TXCTL_LASTFRAG|SK_TXCTL_EOF_INTR; sc_if->sk_cdata.sk_tx_chain[cur].sk_mbuf = m_head; sc_if->sk_rdata->sk_tx_ring[*txidx].sk_ctl |= SK_TXCTL_OWN; sc_if->sk_cdata.sk_tx_cnt += cnt; *txidx = frag; return(0); } static void sk_start(ifp) struct ifnet *ifp; { struct sk_softc *sc; struct sk_if_softc *sc_if; struct mbuf *m_head = NULL; u_int32_t idx; sc_if = ifp->if_softc; sc = sc_if->sk_softc; SK_IF_LOCK(sc_if); idx = sc_if->sk_cdata.sk_tx_prod; while(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ if (sk_encap(sc_if, m_head, &idx)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); } /* Transmit */ sc_if->sk_cdata.sk_tx_prod = idx; CSR_WRITE_4(sc, sc_if->sk_tx_bmu, SK_TXBMU_TX_START); /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; SK_IF_UNLOCK(sc_if); return; } static void sk_watchdog(ifp) struct ifnet *ifp; { struct sk_if_softc *sc_if; sc_if = ifp->if_softc; printf("sk%d: watchdog timeout\n", sc_if->sk_unit); sk_init(sc_if); return; } static void skc_shutdown(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(dev); SK_LOCK(sc); /* Turn off the 'driver is loaded' LED. */ CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_OFF); /* * Reset the GEnesis controller. Doing this should also * assert the resets on the attached XMAC(s). */ sk_reset(sc); SK_UNLOCK(sc); return; } static void sk_rxeof(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; struct mbuf *m; struct ifnet *ifp; struct sk_chain *cur_rx; int total_len = 0; int i; u_int32_t rxstat; sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; i = sc_if->sk_cdata.sk_rx_prod; cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; SK_LOCK_ASSERT(sc); while(!(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl & SK_RXCTL_OWN)) { cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; rxstat = sc_if->sk_rdata->sk_rx_ring[i].sk_xmac_rxstat; m = cur_rx->sk_mbuf; cur_rx->sk_mbuf = NULL; total_len = SK_RXBYTES(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl); SK_INC(i, SK_RX_RING_CNT); if (rxstat & XM_RXSTAT_ERRFRAME) { ifp->if_ierrors++; sk_newbuf(sc_if, cur_rx, m); continue; } /* * Try to allocate a new jumbo buffer. If that * fails, copy the packet to mbufs and put the * jumbo buffer back in the ring so it can be * re-used. If allocating mbufs fails, then we * have to drop the packet. */ if (sk_newbuf(sc_if, cur_rx, NULL) == ENOBUFS) { struct mbuf *m0; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); sk_newbuf(sc_if, cur_rx, m); if (m0 == NULL) { printf("sk%d: no receive buffers " "available -- packet dropped!\n", sc_if->sk_unit); ifp->if_ierrors++; continue; } m = m0; } else { m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; } ifp->if_ipackets++; SK_UNLOCK(sc); (*ifp->if_input)(ifp, m); SK_LOCK(sc); } sc_if->sk_cdata.sk_rx_prod = i; return; } static void sk_txeof(sc_if) struct sk_if_softc *sc_if; { struct sk_tx_desc *cur_tx = NULL; struct ifnet *ifp; u_int32_t idx; ifp = &sc_if->arpcom.ac_if; /* * Go through our tx ring and free mbufs for those * frames that have been sent. */ idx = sc_if->sk_cdata.sk_tx_cons; while(idx != sc_if->sk_cdata.sk_tx_prod) { cur_tx = &sc_if->sk_rdata->sk_tx_ring[idx]; if (cur_tx->sk_ctl & SK_TXCTL_OWN) break; if (cur_tx->sk_ctl & SK_TXCTL_LASTFRAG) ifp->if_opackets++; if (sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf); sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf = NULL; } sc_if->sk_cdata.sk_tx_cnt--; SK_INC(idx, SK_TX_RING_CNT); ifp->if_timer = 0; } sc_if->sk_cdata.sk_tx_cons = idx; if (cur_tx != NULL) ifp->if_flags &= ~IFF_OACTIVE; return; } static void sk_tick(xsc_if) void *xsc_if; { struct sk_if_softc *sc_if; struct mii_data *mii; struct ifnet *ifp; int i; sc_if = xsc_if; SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; mii = device_get_softc(sc_if->sk_miibus); if (!(ifp->if_flags & IFF_UP)) { SK_IF_UNLOCK(sc_if); return; } if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { sk_intr_bcom(sc_if); SK_IF_UNLOCK(sc_if); return; } /* * According to SysKonnect, the correct way to verify that * the link has come back up is to poll bit 0 of the GPIO * register three times. This pin has the signal from the * link_sync pin connected to it; if we read the same link * state 3 times in a row, we know the link is up. */ for (i = 0; i < 3; i++) { if (SK_XM_READ_2(sc_if, XM_GPIO) & XM_GPIO_GP0_SET) break; } if (i != 3) { sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); SK_IF_UNLOCK(sc_if); return; } /* Turn the GP0 interrupt back on. */ SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); SK_XM_READ_2(sc_if, XM_ISR); mii_tick(mii); untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); SK_IF_UNLOCK(sc_if); return; } static void sk_intr_bcom(sc_if) struct sk_if_softc *sc_if; { struct mii_data *mii; struct ifnet *ifp; int status; mii = device_get_softc(sc_if->sk_miibus); ifp = &sc_if->arpcom.ac_if; SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); /* * Read the PHY interrupt register to make sure * we clear any pending interrupts. */ status = sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_ISR); if (!(ifp->if_flags & IFF_RUNNING)) { sk_init_xmac(sc_if); return; } if (status & (BRGPHY_ISR_LNK_CHG|BRGPHY_ISR_AN_PR)) { int lstat; lstat = sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_AUXSTS); if (!(lstat & BRGPHY_AUXSTS_LINK) && sc_if->sk_link) { mii_mediachg(mii); /* Turn off the link LED. */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF); sc_if->sk_link = 0; } else if (status & BRGPHY_ISR_LNK_CHG) { sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_IMR, 0xFF00); mii_tick(mii); sc_if->sk_link = 1; /* Turn on the link LED. */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_ON|SK_LINKLED_LINKSYNC_OFF| SK_LINKLED_BLINK_OFF); } else { mii_tick(mii); sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } } SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); return; } static void sk_intr_xmac(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; u_int16_t status; sc = sc_if->sk_softc; status = SK_XM_READ_2(sc_if, XM_ISR); /* * Link has gone down. Start MII tick timeout to * watch for link resync. */ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) { if (status & XM_ISR_GP0_SET) { SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } if (status & XM_ISR_AUTONEG_DONE) { sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } } if (status & XM_IMR_TX_UNDERRUN) SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_TXFIFO); if (status & XM_IMR_RX_OVERRUN) SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_RXFIFO); status = SK_XM_READ_2(sc_if, XM_ISR); return; } static void sk_intr_yukon(sc_if) struct sk_if_softc *sc_if; { int status; status = SK_IF_READ_2(sc_if, 0, SK_GMAC_ISR); return; } static void sk_intr(xsc) void *xsc; { struct sk_softc *sc = xsc; struct sk_if_softc *sc_if0 = NULL, *sc_if1 = NULL; struct ifnet *ifp0 = NULL, *ifp1 = NULL; u_int32_t status; SK_LOCK(sc); sc_if0 = sc->sk_if[SK_PORT_A]; sc_if1 = sc->sk_if[SK_PORT_B]; if (sc_if0 != NULL) ifp0 = &sc_if0->arpcom.ac_if; if (sc_if1 != NULL) ifp1 = &sc_if1->arpcom.ac_if; for (;;) { status = CSR_READ_4(sc, SK_ISSR); if (!(status & sc->sk_intrmask)) break; /* Handle receive interrupts first. */ if (status & SK_ISR_RX1_EOF) { sk_rxeof(sc_if0); CSR_WRITE_4(sc, SK_BMU_RX_CSR0, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } if (status & SK_ISR_RX2_EOF) { sk_rxeof(sc_if1); CSR_WRITE_4(sc, SK_BMU_RX_CSR1, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } /* Then transmit interrupts. */ if (status & SK_ISR_TX1_S_EOF) { sk_txeof(sc_if0); CSR_WRITE_4(sc, SK_BMU_TXS_CSR0, SK_TXBMU_CLR_IRQ_EOF); } if (status & SK_ISR_TX2_S_EOF) { sk_txeof(sc_if1); CSR_WRITE_4(sc, SK_BMU_TXS_CSR1, SK_TXBMU_CLR_IRQ_EOF); } /* Then MAC interrupts. */ if (status & SK_ISR_MAC1 && ifp0->if_flags & IFF_RUNNING) { if (sc->sk_type == SK_GENESIS) sk_intr_xmac(sc_if0); else sk_intr_yukon(sc_if0); } if (status & SK_ISR_MAC2 && ifp1->if_flags & IFF_RUNNING) { if (sc->sk_type == SK_GENESIS) sk_intr_xmac(sc_if1); else sk_intr_yukon(sc_if1); } if (status & SK_ISR_EXTERNAL_REG) { if (ifp0 != NULL && sc_if0->sk_phytype == SK_PHYTYPE_BCOM) sk_intr_bcom(sc_if0); if (ifp1 != NULL && sc_if1->sk_phytype == SK_PHYTYPE_BCOM) sk_intr_bcom(sc_if1); } } CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); if (ifp0 != NULL && ifp0->if_snd.ifq_head != NULL) sk_start(ifp0); if (ifp1 != NULL && ifp1->if_snd.ifq_head != NULL) sk_start(ifp1); SK_UNLOCK(sc); return; } static void sk_init_xmac(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; struct ifnet *ifp; struct sk_bcom_hack bhack[] = { { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 }, { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 }, { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, { 0, 0 } }; sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; /* Unreset the XMAC. */ SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_UNRESET); DELAY(1000); /* Reset the XMAC's internal state. */ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); /* Save the XMAC II revision */ sc_if->sk_xmac_rev = XM_XMAC_REV(SK_XM_READ_4(sc_if, XM_DEVID)); /* * Perform additional initialization for external PHYs, * namely for the 1000baseTX cards that use the XMAC's * GMII mode. */ if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { int i = 0; u_int32_t val; /* Take PHY out of reset. */ val = sk_win_read_4(sc, SK_GPIO); if (sc_if->sk_port == SK_PORT_A) val |= SK_GPIO_DIR0|SK_GPIO_DAT0; else val |= SK_GPIO_DIR2|SK_GPIO_DAT2; sk_win_write_4(sc, SK_GPIO, val); /* Enable GMII mode on the XMAC. */ SK_XM_SETBIT_2(sc_if, XM_HWCFG, XM_HWCFG_GMIIMODE); sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET); DELAY(10000); sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_IMR, 0xFFF0); /* * Early versions of the BCM5400 apparently have * a bug that requires them to have their reserved * registers initialized to some magic values. I don't * know what the numbers do, I'm just the messenger. */ if (sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, 0x03) == 0x6041) { while(bhack[i].reg) { sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, bhack[i].reg, bhack[i].val); i++; } } } /* Set station address */ SK_XM_WRITE_2(sc_if, XM_PAR0, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[0])); SK_XM_WRITE_2(sc_if, XM_PAR1, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[2])); SK_XM_WRITE_2(sc_if, XM_PAR2, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[4])); SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_STATION); if (ifp->if_flags & IFF_BROADCAST) { SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); } else { SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); } /* We don't need the FCS appended to the packet. */ SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_STRIPFCS); /* We want short frames padded to 60 bytes. */ SK_XM_SETBIT_2(sc_if, XM_TXCMD, XM_TXCMD_AUTOPAD); /* * Enable the reception of all error frames. This is is * a necessary evil due to the design of the XMAC. The * XMAC's receive FIFO is only 8K in size, however jumbo * frames can be up to 9000 bytes in length. When bad * frame filtering is enabled, the XMAC's RX FIFO operates * in 'store and forward' mode. For this to work, the * entire frame has to fit into the FIFO, but that means * that jumbo frames larger than 8192 bytes will be * truncated. Disabling all bad frame filtering causes * the RX FIFO to operate in streaming mode, in which * case the XMAC will start transfering frames out of the * RX FIFO as soon as the FIFO threshold is reached. */ SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_BADFRAMES| XM_MODE_RX_GIANTS|XM_MODE_RX_RUNTS|XM_MODE_RX_CRCERRS| XM_MODE_RX_INRANGELEN); if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); else SK_XM_CLRBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); /* * Bump up the transmit threshold. This helps hold off transmit * underruns when we're blasting traffic from both ports at once. */ SK_XM_WRITE_2(sc_if, XM_TX_REQTHRESH, SK_XM_TX_FIFOTHRESH); /* Set promiscuous mode */ sk_setpromisc(sc_if); /* Set multicast filter */ sk_setmulti(sc_if); /* Clear and enable interrupts */ SK_XM_READ_2(sc_if, XM_ISR); if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) SK_XM_WRITE_2(sc_if, XM_IMR, XM_INTRS); else SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); /* Configure MAC arbiter */ switch(sc_if->sk_xmac_rev) { case XM_XMAC_REV_B2: sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); break; case XM_XMAC_REV_C1: sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); break; default: break; } sk_win_write_2(sc, SK_MACARB_CTL, SK_MACARBCTL_UNRESET|SK_MACARBCTL_FASTOE_OFF); sc_if->sk_link = 1; return; } static void sk_init_yukon(sc_if) struct sk_if_softc *sc_if; { u_int32_t phy; u_int16_t reg; int i; /* GMAC and GPHY Reset */ SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, SK_GPHY_RESET_SET); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_SET); DELAY(1000); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_SET); DELAY(1000); phy = SK_GPHY_INT_POL_HI | SK_GPHY_DIS_FC | SK_GPHY_DIS_SLEEP | SK_GPHY_ENA_XC | SK_GPHY_ANEG_ALL | SK_GPHY_ENA_PAUSE; switch(sc_if->sk_softc->sk_pmd) { case IFM_1000_SX: case IFM_1000_LX: phy |= SK_GPHY_FIBER; break; case IFM_1000_CX: case IFM_1000_T: phy |= SK_GPHY_COPPER; break; } SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, phy | SK_GPHY_RESET_SET); DELAY(1000); SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, phy | SK_GPHY_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_LOOP_OFF | SK_GMAC_PAUSE_ON | SK_GMAC_RESET_CLEAR); /* unused read of the interrupt source register */ SK_IF_READ_2(sc_if, 0, SK_GMAC_ISR); reg = SK_YU_READ_2(sc_if, YUKON_PAR); /* MIB Counter Clear Mode set */ reg |= YU_PAR_MIB_CLR; SK_YU_WRITE_2(sc_if, YUKON_PAR, reg); /* MIB Counter Clear Mode clear */ reg &= ~YU_PAR_MIB_CLR; SK_YU_WRITE_2(sc_if, YUKON_PAR, reg); /* receive control reg */ SK_YU_WRITE_2(sc_if, YUKON_RCR, YU_RCR_CRCR); /* transmit parameter register */ SK_YU_WRITE_2(sc_if, YUKON_TPR, YU_TPR_JAM_LEN(0x3) | YU_TPR_JAM_IPG(0xb) | YU_TPR_JAM2DATA_IPG(0x1a) ); /* serial mode register */ SK_YU_WRITE_2(sc_if, YUKON_SMR, YU_SMR_DATA_BLIND(0x1c) | YU_SMR_MFL_VLAN | YU_SMR_IPG_DATA(0x1e)); /* Setup Yukon's address */ for (i = 0; i < 3; i++) { /* Write Source Address 1 (unicast filter) */ SK_YU_WRITE_2(sc_if, YUKON_SAL1 + i * 4, sc_if->arpcom.ac_enaddr[i * 2] | sc_if->arpcom.ac_enaddr[i * 2 + 1] << 8); } for (i = 0; i < 3; i++) { reg = sk_win_read_2(sc_if->sk_softc, SK_MAC1_0 + i * 2 + sc_if->sk_port * 8); SK_YU_WRITE_2(sc_if, YUKON_SAL2 + i * 4, reg); } /* Set promiscuous mode */ sk_setpromisc(sc_if); /* Set multicast filter */ sk_setmulti(sc_if); /* enable interrupt mask for counter overflows */ SK_YU_WRITE_2(sc_if, YUKON_TIMR, 0); SK_YU_WRITE_2(sc_if, YUKON_RIMR, 0); SK_YU_WRITE_2(sc_if, YUKON_TRIMR, 0); /* Configure RX MAC FIFO */ SK_IF_WRITE_1(sc_if, 0, SK_RXMF1_CTRL_TEST, SK_RFCTL_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_RXMF1_CTRL_TEST, SK_RFCTL_OPERATION_ON); /* Configure TX MAC FIFO */ SK_IF_WRITE_1(sc_if, 0, SK_TXMF1_CTRL_TEST, SK_TFCTL_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_TXMF1_CTRL_TEST, SK_TFCTL_OPERATION_ON); } /* * Note that to properly initialize any part of the GEnesis chip, * you first have to take it out of reset mode. */ static void sk_init(xsc) void *xsc; { struct sk_if_softc *sc_if = xsc; struct sk_softc *sc; struct ifnet *ifp; struct mii_data *mii; u_int16_t reg; SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; sc = sc_if->sk_softc; mii = device_get_softc(sc_if->sk_miibus); /* Cancel pending I/O and free all RX/TX buffers. */ sk_stop(sc_if); if (sc->sk_type == SK_GENESIS) { /* Configure LINK_SYNC LED */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_ON); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_ON); /* Configure RX LED */ SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_START); /* Configure TX LED */ SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_TXLEDCTL_COUNTER_START); } /* Configure I2C registers */ /* Configure XMAC(s) */ switch (sc->sk_type) { case SK_GENESIS: sk_init_xmac(sc_if); break; case SK_YUKON: sk_init_yukon(sc_if); break; } mii_mediachg(mii); if (sc->sk_type == SK_GENESIS) { /* Configure MAC FIFOs */ SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_END, SK_FIFO_END); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_ON); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_END, SK_FIFO_END); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_ON); } /* Configure transmit arbiter(s) */ SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, SK_TXARCTL_ON|SK_TXARCTL_FSYNC_ON); /* Configure RAMbuffers */ SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_START, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_WR_PTR, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_RD_PTR, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_END, sc_if->sk_rx_ramend); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_ON); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_UNRESET); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_STORENFWD_ON); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_START, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_WR_PTR, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_RD_PTR, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_END, sc_if->sk_tx_ramend); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_ON); /* Configure BMUs */ SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_ONLINE); SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, vtophys(&sc_if->sk_rdata->sk_rx_ring[0])); SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, 0); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_ONLINE); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_LO, vtophys(&sc_if->sk_rdata->sk_tx_ring[0])); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_HI, 0); /* Init descriptors */ if (sk_init_rx_ring(sc_if) == ENOBUFS) { printf("sk%d: initialization failed: no " "memory for rx buffers\n", sc_if->sk_unit); sk_stop(sc_if); SK_IF_UNLOCK(sc_if); return; } sk_init_tx_ring(sc_if); /* Configure interrupt handling */ CSR_READ_4(sc, SK_ISSR); if (sc_if->sk_port == SK_PORT_A) sc->sk_intrmask |= SK_INTRS1; else sc->sk_intrmask |= SK_INTRS2; sc->sk_intrmask |= SK_ISR_EXTERNAL_REG; CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); /* Start BMUs. */ SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_RX_START); switch(sc->sk_type) { case SK_GENESIS: /* Enable XMACs TX and RX state machines */ SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_IGNPAUSE); SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); break; case SK_YUKON: reg = SK_YU_READ_2(sc_if, YUKON_GPCR); reg |= YU_GPCR_TXEN | YU_GPCR_RXEN; reg &= ~(YU_GPCR_SPEED_EN | YU_GPCR_DPLX_EN); SK_YU_WRITE_2(sc_if, YUKON_GPCR, reg); } ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; SK_IF_UNLOCK(sc_if); return; } static void sk_stop(sc_if) struct sk_if_softc *sc_if; { int i; struct sk_softc *sc; struct ifnet *ifp; SK_IF_LOCK(sc_if); sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { u_int32_t val; /* Put PHY back into reset. */ val = sk_win_read_4(sc, SK_GPIO); if (sc_if->sk_port == SK_PORT_A) { val |= SK_GPIO_DIR0; val &= ~SK_GPIO_DAT0; } else { val |= SK_GPIO_DIR2; val &= ~SK_GPIO_DAT2; } sk_win_write_4(sc, SK_GPIO, val); } /* Turn off various components of this interface. */ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); switch (sc->sk_type) { case SK_GENESIS: SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_RESET); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_RESET); break; case SK_YUKON: SK_IF_WRITE_1(sc_if,0, SK_RXMF1_CTRL_TEST, SK_RFCTL_RESET_SET); SK_IF_WRITE_1(sc_if,0, SK_TXMF1_CTRL_TEST, SK_TFCTL_RESET_SET); break; } SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_OFFLINE); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_OFFLINE); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, SK_TXARCTL_OFF); SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_OFF); /* Disable interrupts */ if (sc_if->sk_port == SK_PORT_A) sc->sk_intrmask &= ~SK_INTRS1; else sc->sk_intrmask &= ~SK_INTRS2; CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); SK_XM_READ_2(sc_if, XM_ISR); SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); /* Free RX and TX mbufs still in the queues. */ for (i = 0; i < SK_RX_RING_CNT; i++) { if (sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf); sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf = NULL; } } for (i = 0; i < SK_TX_RING_CNT; i++) { if (sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf); sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf = NULL; } } ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); SK_IF_UNLOCK(sc_if); return; } Index: head/sys/dev/vr/if_vr.c =================================================================== --- head/sys/dev/vr/if_vr.c (revision 131252) +++ head/sys/dev/vr/if_vr.c (revision 131253) @@ -1,1817 +1,1793 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * VIA Rhine fast ethernet PCI NIC driver * * Supports various network adapters based on the VIA Rhine * and Rhine II PCI controllers, including the D-Link DFE530TX. * Datasheets are available at http://www.via.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The VIA Rhine controllers are similar in some respects to the * the DEC tulip chips, except less complicated. The controller * uses an MII bus and an external physical layer interface. The * receiver has a one entry perfect filter and a 64-bit hash table * multicast filter. Transmit and receive descriptors are similar * to the tulip. * * The Rhine has a serious flaw in its transmit DMA mechanism: * transmit buffers must be longword aligned. Unfortunately, * FreeBSD doesn't guarantee that mbufs will be filled in starting * at longword boundaries, so we have to do a buffer copy before * transmission. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include #define VR_USEIOSPACE #include MODULE_DEPEND(vr, pci, 1, 1, 1); MODULE_DEPEND(vr, ether, 1, 1, 1); MODULE_DEPEND(vr, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #undef VR_USESWSHIFT /* * Various supported device vendors/types and their names. */ static struct vr_type vr_devs[] = { { VIA_VENDORID, VIA_DEVICEID_RHINE, "VIA VT3043 Rhine I 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_II, "VIA VT86C100A Rhine II 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_II_2, "VIA VT6102 Rhine II 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_III, "VIA VT6105 Rhine III 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_III_M, "VIA VT6105M Rhine III 10/100BaseTX" }, { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, "Delta Electronics Rhine II 10/100BaseTX" }, { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, "Addtron Technology Rhine II 10/100BaseTX" }, { 0, 0, NULL } }; static int vr_probe (device_t); static int vr_attach (device_t); static int vr_detach (device_t); static int vr_newbuf (struct vr_softc *, struct vr_chain_onefrag *, struct mbuf *); static int vr_encap (struct vr_softc *, struct vr_chain *, struct mbuf * ); static void vr_rxeof (struct vr_softc *); static void vr_rxeoc (struct vr_softc *); static void vr_txeof (struct vr_softc *); static void vr_tick (void *); static void vr_intr (void *); static void vr_start (struct ifnet *); static int vr_ioctl (struct ifnet *, u_long, caddr_t); static void vr_init (void *); static void vr_stop (struct vr_softc *); static void vr_watchdog (struct ifnet *); static void vr_shutdown (device_t); static int vr_ifmedia_upd (struct ifnet *); static void vr_ifmedia_sts (struct ifnet *, struct ifmediareq *); #ifdef VR_USESWSHIFT static void vr_mii_sync (struct vr_softc *); static void vr_mii_send (struct vr_softc *, u_int32_t, int); #endif static int vr_mii_readreg (struct vr_softc *, struct vr_mii_frame *); static int vr_mii_writereg (struct vr_softc *, struct vr_mii_frame *); static int vr_miibus_readreg (device_t, int, int); static int vr_miibus_writereg (device_t, int, int, int); static void vr_miibus_statchg (device_t); static void vr_setcfg (struct vr_softc *, int); static void vr_setmulti (struct vr_softc *); static void vr_reset (struct vr_softc *); static int vr_list_rx_init (struct vr_softc *); static int vr_list_tx_init (struct vr_softc *); #ifdef VR_USEIOSPACE #define VR_RES SYS_RES_IOPORT #define VR_RID VR_PCI_LOIO #else #define VR_RES SYS_RES_MEMORY #define VR_RID VR_PCI_LOMEM #endif static device_method_t vr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vr_probe), DEVMETHOD(device_attach, vr_attach), DEVMETHOD(device_detach, vr_detach), DEVMETHOD(device_shutdown, vr_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, vr_miibus_readreg), DEVMETHOD(miibus_writereg, vr_miibus_writereg), DEVMETHOD(miibus_statchg, vr_miibus_statchg), { 0, 0 } }; static driver_t vr_driver = { "vr", vr_methods, sizeof(struct vr_softc) }; static devclass_t vr_devclass; DRIVER_MODULE(vr, pci, vr_driver, vr_devclass, 0, 0); DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); #define VR_SETBIT(sc, reg, x) \ CSR_WRITE_1(sc, reg, \ CSR_READ_1(sc, reg) | (x)) #define VR_CLRBIT(sc, reg, x) \ CSR_WRITE_1(sc, reg, \ CSR_READ_1(sc, reg) & ~(x)) #define VR_SETBIT16(sc, reg, x) \ CSR_WRITE_2(sc, reg, \ CSR_READ_2(sc, reg) | (x)) #define VR_CLRBIT16(sc, reg, x) \ CSR_WRITE_2(sc, reg, \ CSR_READ_2(sc, reg) & ~(x)) #define VR_SETBIT32(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | (x)) #define VR_CLRBIT32(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) \ CSR_WRITE_1(sc, VR_MIICMD, \ CSR_READ_1(sc, VR_MIICMD) | (x)) #define SIO_CLR(x) \ CSR_WRITE_1(sc, VR_MIICMD, \ CSR_READ_1(sc, VR_MIICMD) & ~(x)) #ifdef VR_USESWSHIFT /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void vr_mii_sync(sc) struct vr_softc *sc; { register int i; SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); for (i = 0; i < 32; i++) { SIO_SET(VR_MIICMD_CLK); DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void vr_mii_send(sc, bits, cnt) struct vr_softc *sc; u_int32_t bits; int cnt; { int i; SIO_CLR(VR_MIICMD_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { SIO_SET(VR_MIICMD_DATAIN); } else { SIO_CLR(VR_MIICMD_DATAIN); } DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); } } #endif /* * Read an PHY register through the MII. */ static int vr_mii_readreg(sc, frame) struct vr_softc *sc; struct vr_mii_frame *frame; #ifdef VR_USESWSHIFT { int i, ack; VR_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = VR_MII_STARTDELIM; frame->mii_opcode = VR_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_1(sc, VR_MIICMD, 0); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); /* * Turn on data xmit. */ SIO_SET(VR_MIICMD_DIR); vr_mii_sync(sc); /* * Send command/address info. */ vr_mii_send(sc, frame->mii_stdelim, 2); vr_mii_send(sc, frame->mii_opcode, 2); vr_mii_send(sc, frame->mii_phyaddr, 5); vr_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); /* Turn off xmit. */ SIO_CLR(VR_MIICMD_DIR); /* Check for ack */ SIO_CLR(VR_MIICMD_CLK); DELAY(1); ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; SIO_SET(VR_MIICMD_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { SIO_CLR(VR_MIICMD_CLK); DELAY(1); if (!ack) { if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) frame->mii_data |= i; DELAY(1); } SIO_SET(VR_MIICMD_CLK); DELAY(1); } fail: SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); VR_UNLOCK(sc); if (ack) return(1); return(0); } #else { int s, i; s = splimp(); /* Set the PHY-adress */ CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| frame->mii_phyaddr); /* Set the register-adress */ CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_READ_ENB); for (i = 0; i < 10000; i++) { if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_READ_ENB) == 0) break; DELAY(1); } frame->mii_data = CSR_READ_2(sc, VR_MIIDATA); (void)splx(s); return(0); } #endif /* * Write to a PHY register through the MII. */ static int vr_mii_writereg(sc, frame) struct vr_softc *sc; struct vr_mii_frame *frame; #ifdef VR_USESWSHIFT { VR_LOCK(sc); CSR_WRITE_1(sc, VR_MIICMD, 0); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); /* * Set up frame for TX. */ frame->mii_stdelim = VR_MII_STARTDELIM; frame->mii_opcode = VR_MII_WRITEOP; frame->mii_turnaround = VR_MII_TURNAROUND; /* * Turn on data output. */ SIO_SET(VR_MIICMD_DIR); vr_mii_sync(sc); vr_mii_send(sc, frame->mii_stdelim, 2); vr_mii_send(sc, frame->mii_opcode, 2); vr_mii_send(sc, frame->mii_phyaddr, 5); vr_mii_send(sc, frame->mii_regaddr, 5); vr_mii_send(sc, frame->mii_turnaround, 2); vr_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ SIO_SET(VR_MIICMD_CLK); DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); /* * Turn off xmit. */ SIO_CLR(VR_MIICMD_DIR); VR_UNLOCK(sc); return(0); } #else { int s, i; s = splimp(); /* Set the PHY-adress */ CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| frame->mii_phyaddr); /* Set the register-adress and data to write */ CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); CSR_WRITE_2(sc, VR_MIIDATA, frame->mii_data); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_WRITE_ENB); for (i = 0; i < 10000; i++) { if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_WRITE_ENB) == 0) break; DELAY(1); } (void)splx(s); return(0); } #endif static int vr_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct vr_softc *sc; struct vr_mii_frame frame; sc = device_get_softc(dev); switch (sc->vr_revid) { case REV_ID_VT6102_APOLLO: if (phy != 1) return 0; default: break; } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; vr_mii_readreg(sc, &frame); return(frame.mii_data); } static int vr_miibus_writereg(dev, phy, reg, data) device_t dev; u_int16_t phy, reg, data; { struct vr_softc *sc; struct vr_mii_frame frame; sc = device_get_softc(dev); switch (sc->vr_revid) { case REV_ID_VT6102_APOLLO: if (phy != 1) return 0; default: break; } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; vr_mii_writereg(sc, &frame); return(0); } static void vr_miibus_statchg(dev) device_t dev; { struct vr_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); VR_LOCK(sc); mii = device_get_softc(sc->vr_miibus); vr_setcfg(sc, mii->mii_media_active); VR_UNLOCK(sc); return; } /* * Program the 64-bit multicast hash filter. */ static void vr_setmulti(sc) struct vr_softc *sc; { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; rxfilt = CSR_READ_1(sc, VR_RXCFG); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxfilt |= VR_RXCFG_RX_MULTI; CSR_WRITE_1(sc, VR_RXCFG, rxfilt); CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, VR_MAR0, 0); CSR_WRITE_4(sc, VR_MAR1, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if (mcnt) rxfilt |= VR_RXCFG_RX_MULTI; else rxfilt &= ~VR_RXCFG_RX_MULTI; CSR_WRITE_4(sc, VR_MAR0, hashes[0]); CSR_WRITE_4(sc, VR_MAR1, hashes[1]); CSR_WRITE_1(sc, VR_RXCFG, rxfilt); return; } /* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */ static void vr_setcfg(sc, media) struct vr_softc *sc; int media; { int restart = 0; if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { restart = 1; VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); } if ((media & IFM_GMASK) == IFM_FDX) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); else VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); if (restart) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); return; } static void vr_reset(sc) struct vr_softc *sc; { register int i; VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); for (i = 0; i < VR_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) break; } if (i == VR_TIMEOUT) { if (sc->vr_revid < REV_ID_VT3065_A) printf("vr%d: reset never completed!\n", sc->vr_unit); else { /* Use newer force reset command */ printf("vr%d: Using force reset command.\n", sc->vr_unit); VR_SETBIT(sc, VR_MISC_CR1, VR_MISCCR1_FORSRST); } } /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a VIA Rhine chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int vr_probe(dev) device_t dev; { struct vr_type *t; t = vr_devs; while(t->vr_name != NULL) { if ((pci_get_vendor(dev) == t->vr_vid) && (pci_get_device(dev) == t->vr_did)) { device_set_desc(dev, t->vr_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int vr_attach(dev) device_t dev; { int i; u_char eaddr[ETHER_ADDR_LEN]; struct vr_softc *sc; struct ifnet *ifp; int unit, error = 0, rid; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->vr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, VR_PCI_LOIO, 4); - membase = pci_read_config(dev, VR_PCI_LOMEM, 4); - irq = pci_read_config(dev, VR_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("vr%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, VR_PCI_LOIO, iobase, 4); - pci_write_config(dev, VR_PCI_LOMEM, membase, 4); - pci_write_config(dev, VR_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); sc->vr_revid = pci_read_config(dev, VR_PCI_REVID, 4) & 0x000000FF; rid = VR_RID; sc->vr_res = bus_alloc_resource_any(dev, VR_RES, &rid, RF_ACTIVE); if (sc->vr_res == NULL) { printf("vr%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->vr_btag = rman_get_bustag(sc->vr_res); sc->vr_bhandle = rman_get_bushandle(sc->vr_res); /* Allocate interrupt */ rid = 0; sc->vr_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->vr_irq == NULL) { printf("vr%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* * Windows may put the chip in suspend mode when it * shuts down. Be sure to kick it in the head to wake it * up again. */ VR_CLRBIT(sc, VR_STICKHW, (VR_STICKHW_DS0|VR_STICKHW_DS1)); /* Reset the adapter. */ vr_reset(sc); /* * Turn on bit2 (MIION) in PCI configuration register 0x53 during * initialization and disable AUTOPOLL. */ pci_write_config(dev, VR_PCI_MODE, pci_read_config(dev, VR_PCI_MODE, 4) | (VR_MODE3_MIION << 24), 4); VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL); /* * Get station address. The way the Rhine chips work, * you're not allowed to directly access the EEPROM once * they've been programmed a special way. Consequently, * we need to read the node address from the PAR0 and PAR1 * registers. */ VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); DELAY(200); for (i = 0; i < ETHER_ADDR_LEN; i++) eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); sc->vr_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->vr_ldata == NULL) { printf("vr%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->vr_ldata, sizeof(struct vr_list_data)); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = vr_ioctl; ifp->if_start = vr_start; ifp->if_watchdog = vr_watchdog; ifp->if_init = vr_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; /* * Do MII setup. */ if (mii_phy_probe(dev, &sc->vr_miibus, vr_ifmedia_upd, vr_ifmedia_sts)) { printf("vr%d: MII without any phy!\n", sc->vr_unit); error = ENXIO; goto fail; } callout_handle_init(&sc->vr_stat_ch); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, vr_intr, sc, &sc->vr_intrhand); if (error) { printf("vr%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) vr_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int vr_detach(dev) device_t dev; { struct vr_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->vr_mtx), ("vr mutex not initialized")); VR_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { vr_stop(sc); ether_ifdetach(ifp); } if (sc->vr_miibus) device_delete_child(dev, sc->vr_miibus); bus_generic_detach(dev); if (sc->vr_intrhand) bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); if (sc->vr_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); if (sc->vr_res) bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); if (sc->vr_ldata) contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); VR_UNLOCK(sc); mtx_destroy(&sc->vr_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int vr_list_tx_init(sc) struct vr_softc *sc; { struct vr_chain_data *cd; struct vr_list_data *ld; int i; cd = &sc->vr_cdata; ld = sc->vr_ldata; for (i = 0; i < VR_TX_LIST_CNT; i++) { cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; if (i == (VR_TX_LIST_CNT - 1)) cd->vr_tx_chain[i].vr_nextdesc = &cd->vr_tx_chain[0]; else cd->vr_tx_chain[i].vr_nextdesc = &cd->vr_tx_chain[i + 1]; } cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0]; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int vr_list_rx_init(sc) struct vr_softc *sc; { struct vr_chain_data *cd; struct vr_list_data *ld; int i; cd = &sc->vr_cdata; ld = sc->vr_ldata; for (i = 0; i < VR_RX_LIST_CNT; i++) { cd->vr_rx_chain[i].vr_ptr = (struct vr_desc *)&ld->vr_rx_list[i]; if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (VR_RX_LIST_CNT - 1)) { cd->vr_rx_chain[i].vr_nextdesc = &cd->vr_rx_chain[0]; ld->vr_rx_list[i].vr_next = vtophys(&ld->vr_rx_list[0]); } else { cd->vr_rx_chain[i].vr_nextdesc = &cd->vr_rx_chain[i + 1]; ld->vr_rx_list[i].vr_next = vtophys(&ld->vr_rx_list[i + 1]); } } cd->vr_rx_head = &cd->vr_rx_chain[0]; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because * MCLBYTES is 2048, so we have to subtract one otherwise we'll * overflow the field and make a mess. */ static int vr_newbuf(sc, c, m) struct vr_softc *sc; struct vr_chain_onefrag *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, sizeof(u_int64_t)); c->vr_mbuf = m_new; c->vr_ptr->vr_status = VR_RXSTAT; c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void vr_rxeof(sc) struct vr_softc *sc; { struct mbuf *m, *m0; struct ifnet *ifp; struct vr_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; VR_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & VR_RXSTAT_OWN)) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif /* DEVICE_POLLING */ m0 = NULL; cur_rx = sc->vr_cdata.vr_rx_head; sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; m = cur_rx->vr_mbuf; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & VR_RXSTAT_RXERR) { ifp->if_ierrors++; printf("vr%d: rx error (%02x):", sc->vr_unit, rxstat & 0x000000ff); if (rxstat & VR_RXSTAT_CRCERR) printf(" crc error"); if (rxstat & VR_RXSTAT_FRAMEALIGNERR) printf(" frame alignment error\n"); if (rxstat & VR_RXSTAT_FIFOOFLOW) printf(" FIFO overflow"); if (rxstat & VR_RXSTAT_GIANT) printf(" received giant packet"); if (rxstat & VR_RXSTAT_RUNT) printf(" received runt packet"); if (rxstat & VR_RXSTAT_BUSERR) printf(" system bus error"); if (rxstat & VR_RXSTAT_BUFFERR) printf("rx buffer error"); printf("\n"); vr_newbuf(sc, cur_rx, m); continue; } /* No errors; receive the packet. */ total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); /* * XXX The VIA Rhine chip includes the CRC with every * received frame, and there's no way to turn this * behavior off (at least, I can't find anything in * the manual that explains how to do it) so we have * to trim off the CRC manually. */ total_len -= ETHER_CRC_LEN; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); vr_newbuf(sc, cur_rx, m); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; ifp->if_ipackets++; VR_UNLOCK(sc); (*ifp->if_input)(ifp, m); VR_LOCK(sc); } return; } static void vr_rxeoc(sc) struct vr_softc *sc; { struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; ifp->if_ierrors++; VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); DELAY(10000); for (i = 0x400; i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RX_ON); i--) ; /* Wait for receiver to stop */ if (!i) { printf("vr%d: rx shutdown error!\n", sc->vr_unit); sc->vr_flags |= VR_F_RESTART; return; } vr_rxeof(sc); CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void vr_txeof(sc) struct vr_softc *sc; { struct vr_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ cur_tx = sc->vr_cdata.vr_tx_cons; while (cur_tx->vr_mbuf != NULL) { u_int32_t txstat; int i; txstat = cur_tx->vr_ptr->vr_status; if ((txstat & VR_TXSTAT_ABRT) || (txstat & VR_TXSTAT_UDF)) { for (i = 0x400; i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_TX_ON); i--) ; /* Wait for chip to shutdown */ if (!i) { printf("vr%d: tx shutdown timeout\n", sc->vr_unit); sc->vr_flags |= VR_F_RESTART; break; } VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; CSR_WRITE_4(sc, VR_TXADDR, vtophys(cur_tx->vr_ptr)); break; } if (txstat & VR_TXSTAT_OWN) break; if (txstat & VR_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & VR_TXSTAT_DEFER) ifp->if_collisions++; if (txstat & VR_TXSTAT_LATECOLL) ifp->if_collisions++; } ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; m_freem(cur_tx->vr_mbuf); cur_tx->vr_mbuf = NULL; ifp->if_flags &= ~IFF_OACTIVE; cur_tx = cur_tx->vr_nextdesc; } sc->vr_cdata.vr_tx_cons = cur_tx; if (cur_tx->vr_mbuf == NULL) ifp->if_timer = 0; } static void vr_tick(xsc) void *xsc; { struct vr_softc *sc; struct mii_data *mii; sc = xsc; VR_LOCK(sc); if (sc->vr_flags & VR_F_RESTART) { printf("vr%d: restarting\n", sc->vr_unit); vr_stop(sc); vr_reset(sc); vr_init(sc); sc->vr_flags &= ~VR_F_RESTART; } mii = device_get_softc(sc->vr_miibus); mii_tick(mii); sc->vr_stat_ch = timeout(vr_tick, sc, hz); VR_UNLOCK(sc); return; } #ifdef DEVICE_POLLING static poll_handler_t vr_poll; static void vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct vr_softc *sc = ifp->if_softc; VR_LOCK(sc); if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); goto done; } sc->rxcycles = count; vr_rxeof(sc); vr_txeof(sc); if (ifp->if_snd.ifq_head != NULL) vr_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int16_t status; status = CSR_READ_2(sc, VR_ISR); if (status) CSR_WRITE_2(sc, VR_ISR, status); if ((status & VR_INTRS) == 0) goto done; if (status & VR_ISR_RX_DROPPED) { printf("vr%d: rx packet lost\n", sc->vr_unit); ifp->if_ierrors++; } if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { printf("vr%d: receive error (%04x)", sc->vr_unit, status); if (status & VR_ISR_RX_NOBUF) printf(" no buffers"); if (status & VR_ISR_RX_OFLOW) printf(" overflow"); if (status & VR_ISR_RX_DROPPED) printf(" packet lost"); printf("\n"); vr_rxeoc(sc); } if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { vr_reset(sc); vr_init(sc); goto done; } if ((status & VR_ISR_UDFI) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_TX_ABRT)) { ifp->if_oerrors++; if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); } } } done: VR_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void vr_intr(arg) void *arg; { struct vr_softc *sc; struct ifnet *ifp; u_int16_t status; sc = arg; VR_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(vr_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_2(sc, VR_IMR, 0x0000); vr_poll(ifp, 0, 1); goto done; } #endif /* DEVICE_POLLING */ /* Supress unwanted interrupts. */ if (!(ifp->if_flags & IFF_UP)) { vr_stop(sc); VR_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_2(sc, VR_IMR, 0x0000); for (;;) { status = CSR_READ_2(sc, VR_ISR); if (status) CSR_WRITE_2(sc, VR_ISR, status); if ((status & VR_INTRS) == 0) break; if (status & VR_ISR_RX_OK) vr_rxeof(sc); if (status & VR_ISR_RX_DROPPED) { printf("vr%d: rx packet lost\n", sc->vr_unit); ifp->if_ierrors++; } if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { printf("vr%d: receive error (%04x)", sc->vr_unit, status); if (status & VR_ISR_RX_NOBUF) printf(" no buffers"); if (status & VR_ISR_RX_OFLOW) printf(" overflow"); if (status & VR_ISR_RX_DROPPED) printf(" packet lost"); printf("\n"); vr_rxeoc(sc); } if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { vr_reset(sc); vr_init(sc); break; } if ((status & VR_ISR_TX_OK) || (status & VR_ISR_TX_ABRT) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_UDFI)) { vr_txeof(sc); if ((status & VR_ISR_UDFI) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_TX_ABRT)) { ifp->if_oerrors++; if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); } } } } /* Re-enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); if (ifp->if_snd.ifq_head != NULL) { vr_start(ifp); } #ifdef DEVICE_POLLING done: #endif /* DEVICE_POLLING */ VR_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int vr_encap(sc, c, m_head) struct vr_softc *sc; struct vr_chain *c; struct mbuf *m_head; { struct vr_desc *f = NULL; struct mbuf *m; /* * The VIA Rhine wants packet buffers to be longword * aligned, but very often our mbufs aren't. Rather than * waste time trying to decide when to copy and when not * to copy, just do it all the time. */ m = m_defrag(m_head, M_DONTWAIT); if (m == NULL) { return(1); } /* * The Rhine chip doesn't auto-pad, so we have to make * sure to pad short frames out to the minimum frame length * ourselves. */ if (m->m_len < VR_MIN_FRAMELEN) { m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len; m->m_len = m->m_pkthdr.len; } c->vr_mbuf = m; f = c->vr_ptr; f->vr_data = vtophys(mtod(m, caddr_t)); f->vr_ctl = m->m_len; f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; f->vr_status = 0; f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; f->vr_next = vtophys(c->vr_nextdesc->vr_ptr); return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void vr_start(ifp) struct ifnet *ifp; { struct vr_softc *sc; struct mbuf *m_head; struct vr_chain *cur_tx; if (ifp->if_flags & IFF_OACTIVE) return; sc = ifp->if_softc; VR_LOCK(sc); cur_tx = sc->vr_cdata.vr_tx_prod; while (cur_tx->vr_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pack the data into the descriptor. */ if (vr_encap(sc, cur_tx, m_head)) { /* Rollback, send what we were able to encap. */ IF_PREPEND(&ifp->if_snd, m_head); break; } VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->vr_mbuf); cur_tx = cur_tx->vr_nextdesc; } if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) { sc->vr_cdata.vr_tx_prod = cur_tx; /* Tell the chip to start transmitting. */ VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; if (cur_tx->vr_mbuf != NULL) ifp->if_flags |= IFF_OACTIVE; } VR_UNLOCK(sc); return; } static void vr_init(xsc) void *xsc; { struct vr_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; int i; VR_LOCK(sc); mii = device_get_softc(sc->vr_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ vr_stop(sc); vr_reset(sc); /* * Set our station address. */ for (i = 0; i < ETHER_ADDR_LEN; i++) CSR_WRITE_1(sc, VR_PAR0 + i, sc->arpcom.ac_enaddr[i]); /* Set DMA size */ VR_CLRBIT(sc, VR_BCR0, VR_BCR0_DMA_LENGTH); VR_SETBIT(sc, VR_BCR0, VR_BCR0_DMA_STORENFWD); /* * BCR0 and BCR1 can override the RXCFG and TXCFG registers, * so we must set both. */ VR_CLRBIT(sc, VR_BCR0, VR_BCR0_RX_THRESH); VR_SETBIT(sc, VR_BCR0, VR_BCR0_RXTHRESH128BYTES); VR_CLRBIT(sc, VR_BCR1, VR_BCR1_TX_THRESH); VR_SETBIT(sc, VR_BCR1, VR_BCR1_TXTHRESHSTORENFWD); VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_128BYTES); VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); /* Init circular RX list. */ if (vr_list_rx_init(sc) == ENOBUFS) { printf("vr%d: initialization failed: no " "memory for rx buffers\n", sc->vr_unit); vr_stop(sc); VR_UNLOCK(sc); return; } /* * Init tx descriptors. */ vr_list_tx_init(sc); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); else VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); /* Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); else VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); /* * Program the multicast filter, if necessary. */ vr_setmulti(sc); /* * Load the address of the RX list. */ CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| VR_CMD_TX_ON|VR_CMD_RX_ON| VR_CMD_RX_GO); CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); CSR_WRITE_2(sc, VR_ISR, 0xFFFF); #ifdef DEVICE_POLLING /* * Disable interrupts if we are polling. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_2(sc, VR_IMR, 0); else #endif /* DEVICE_POLLING */ /* * Enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); mii_mediachg(mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->vr_stat_ch = timeout(vr_tick, sc, hz); VR_UNLOCK(sc); return; } /* * Set media options. */ static int vr_ifmedia_upd(ifp) struct ifnet *ifp; { struct vr_softc *sc; sc = ifp->if_softc; if (ifp->if_flags & IFF_UP) vr_init(sc); return(0); } /* * Report current media status. */ static void vr_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct vr_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->vr_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int vr_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct vr_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; VR_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { vr_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) vr_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: vr_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->vr_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; break; default: error = ether_ioctl(ifp, command, data); break; } VR_UNLOCK(sc); return(error); } static void vr_watchdog(ifp) struct ifnet *ifp; { struct vr_softc *sc; sc = ifp->if_softc; VR_LOCK(sc); ifp->if_oerrors++; printf("vr%d: watchdog timeout\n", sc->vr_unit); vr_stop(sc); vr_reset(sc); vr_init(sc); if (ifp->if_snd.ifq_head != NULL) vr_start(ifp); VR_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void vr_stop(sc) struct vr_softc *sc; { register int i; struct ifnet *ifp; VR_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; untimeout(vr_tick, sc, sc->vr_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif /* DEVICE_POLLING */ VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); CSR_WRITE_2(sc, VR_IMR, 0x0000); CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); /* * Free data in the RX lists. */ for (i = 0; i < VR_RX_LIST_CNT; i++) { if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; } } bzero((char *)&sc->vr_ldata->vr_rx_list, sizeof(sc->vr_ldata->vr_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < VR_TX_LIST_CNT; i++) { if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; } } bzero((char *)&sc->vr_ldata->vr_tx_list, sizeof(sc->vr_ldata->vr_tx_list)); VR_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void vr_shutdown(dev) device_t dev; { struct vr_softc *sc; sc = device_get_softc(dev); vr_stop(sc); return; } Index: head/sys/pci/if_dc.c =================================================================== --- head/sys/pci/if_dc.c (revision 131252) +++ head/sys/pci/if_dc.c (revision 131253) @@ -1,3875 +1,3815 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * DEC "tulip" clone ethernet driver. Supports the DEC/Intel 21143 * series chips and several workalikes including the following: * * Macronix 98713/98715/98725/98727/98732 PMAC (www.macronix.com) * Macronix/Lite-On 82c115 PNIC II (www.macronix.com) * Lite-On 82c168/82c169 PNIC (www.litecom.com) * ASIX Electronics AX88140A (www.asix.com.tw) * ASIX Electronics AX88141 (www.asix.com.tw) * ADMtek AL981 (www.admtek.com.tw) * ADMtek AN985 (www.admtek.com.tw) * Netgear FA511 (www.netgear.com) Appears to be rebadged ADMTek AN985 * Davicom DM9100, DM9102, DM9102A (www.davicom8.com) * Accton EN1217 (www.accton.com) * Xircom X3201 (www.xircom.com) * Abocom FE2500 * Conexant LANfinity (www.conexant.com) * 3Com OfficeConnect 10/100B 3CSOHO100B (www.3com.com) * * Datasheets for the 21143 are available at developer.intel.com. * Datasheets for the clone parts can be found at their respective sites. * (Except for the PNIC; see www.freebsd.org/~wpaul/PNIC/pnic.ps.gz.) * The PNIC II is essentially a Macronix 98715A chip; the only difference * worth noting is that its multicast hash table is only 128 bits wide * instead of 512. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The Intel 21143 is the successor to the DEC 21140. It is basically * the same as the 21140 but with a few new features. The 21143 supports * three kinds of media attachments: * * o MII port, for 10Mbps and 100Mbps support and NWAY * autonegotiation provided by an external PHY. * o SYM port, for symbol mode 100Mbps support. * o 10baseT port. * o AUI/BNC port. * * The 100Mbps SYM port and 10baseT port can be used together in * combination with the internal NWAY support to create a 10/100 * autosensing configuration. * * Note that not all tulip workalikes are handled in this driver: we only * deal with those which are relatively well behaved. The Winbond is * handled separately due to its different register offsets and the * special handling needed for its various bugs. The PNIC is handled * here, but I'm not thrilled about it. * * All of the workalike chips use some form of MII transceiver support * with the exception of the Macronix chips, which also have a SYM port. * The ASIX AX88140A is also documented to have a SYM port, but all * the cards I've seen use an MII transceiver, probably because the * AX88140A doesn't support internal NWAY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DC_USEIOSPACE #ifdef __alpha__ #define SRM_MEDIA #endif #include #ifdef __sparc64__ #include #include #endif MODULE_DEPEND(dc, pci, 1, 1, 1); MODULE_DEPEND(dc, ether, 1, 1, 1); MODULE_DEPEND(dc, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Various supported device vendors/types and their names. */ static struct dc_type dc_devs[] = { { DC_VENDORID_DEC, DC_DEVICEID_21143, "Intel 21143 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009, "Davicom DM9009 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100, "Davicom DM9100 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, "Davicom DM9102 10/100BaseTX" }, { DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102, "Davicom DM9102A 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_AL981, "ADMtek AL981 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_AN985, "ADMtek AN985 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9511, "ADMtek ADM9511 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9513, "ADMtek ADM9513 10/100BaseTX" }, { DC_VENDORID_ADMTEK, DC_DEVICEID_FA511, "Netgear FA511 10/100BaseTX" }, { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, "ASIX AX88140A 10/100BaseTX" }, { DC_VENDORID_ASIX, DC_DEVICEID_AX88140A, "ASIX AX88141 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98713, "Macronix 98713 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98713, "Macronix 98713A 10/100BaseTX" }, { DC_VENDORID_CP, DC_DEVICEID_98713_CP, "Compex RL100-TX 10/100BaseTX" }, { DC_VENDORID_CP, DC_DEVICEID_98713_CP, "Compex RL100-TX 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98715/98715A 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98715AEC-C 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_987x5, "Macronix 98725 10/100BaseTX" }, { DC_VENDORID_MX, DC_DEVICEID_98727, "Macronix 98727/98732 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C115, "LC82C115 PNIC II 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C168, "82c168 PNIC 10/100BaseTX" }, { DC_VENDORID_LO, DC_DEVICEID_82C168, "82c169 PNIC 10/100BaseTX" }, { DC_VENDORID_ACCTON, DC_DEVICEID_EN1217, "Accton EN1217 10/100BaseTX" }, { DC_VENDORID_ACCTON, DC_DEVICEID_EN2242, "Accton EN2242 MiniPCI 10/100BaseTX" }, { DC_VENDORID_XIRCOM, DC_DEVICEID_X3201, "Xircom X3201 10/100BaseTX" }, { DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500, "Abocom FE2500 10/100BaseTX" }, { DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500MX, "Abocom FE2500MX 10/100BaseTX" }, { DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112, "Conexant LANfinity MiniPCI 10/100BaseTX" }, { DC_VENDORID_HAWKING, DC_DEVICEID_HAWKING_PN672TX, "Hawking CB102 CardBus 10/100" }, { DC_VENDORID_PLANEX, DC_DEVICEID_FNW3602T, "PlaneX FNW-3602-T CardBus 10/100" }, { DC_VENDORID_3COM, DC_DEVICEID_3CSOHOB, "3Com OfficeConnect 10/100B" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN120, "Microsoft MN-120 CardBus 10/100" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130, "Microsoft MN-130 10/100" }, { DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130_FAKE, "Microsoft MN-130 10/100" }, { 0, 0, NULL } }; static int dc_probe (device_t); static int dc_attach (device_t); static int dc_detach (device_t); static int dc_suspend (device_t); static int dc_resume (device_t); -#ifndef BURN_BRIDGES -static void dc_acpi (device_t); -#endif static struct dc_type *dc_devtype (device_t); static int dc_newbuf (struct dc_softc *, int, int); static int dc_encap (struct dc_softc *, struct mbuf **); static void dc_pnic_rx_bug_war (struct dc_softc *, int); static int dc_rx_resync (struct dc_softc *); static void dc_rxeof (struct dc_softc *); static void dc_txeof (struct dc_softc *); static void dc_tick (void *); static void dc_tx_underrun (struct dc_softc *); static void dc_intr (void *); static void dc_start (struct ifnet *); static int dc_ioctl (struct ifnet *, u_long, caddr_t); static void dc_init (void *); static void dc_stop (struct dc_softc *); static void dc_watchdog (struct ifnet *); static void dc_shutdown (device_t); static int dc_ifmedia_upd (struct ifnet *); static void dc_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void dc_delay (struct dc_softc *); static void dc_eeprom_idle (struct dc_softc *); static void dc_eeprom_putbyte (struct dc_softc *, int); static void dc_eeprom_getword (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_getword_pnic (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_getword_xircom (struct dc_softc *, int, u_int16_t *); static void dc_eeprom_width (struct dc_softc *); static void dc_read_eeprom (struct dc_softc *, caddr_t, int, int, int); static void dc_mii_writebit (struct dc_softc *, int); static int dc_mii_readbit (struct dc_softc *); static void dc_mii_sync (struct dc_softc *); static void dc_mii_send (struct dc_softc *, u_int32_t, int); static int dc_mii_readreg (struct dc_softc *, struct dc_mii_frame *); static int dc_mii_writereg (struct dc_softc *, struct dc_mii_frame *); static int dc_miibus_readreg (device_t, int, int); static int dc_miibus_writereg (device_t, int, int, int); static void dc_miibus_statchg (device_t); static void dc_miibus_mediainit (device_t); static void dc_setcfg (struct dc_softc *, int); static uint32_t dc_mchash_le (struct dc_softc *, const uint8_t *); static uint32_t dc_mchash_be (const uint8_t *); static void dc_setfilt_21143 (struct dc_softc *); static void dc_setfilt_asix (struct dc_softc *); static void dc_setfilt_admtek (struct dc_softc *); static void dc_setfilt_xircom (struct dc_softc *); static void dc_setfilt (struct dc_softc *); static void dc_reset (struct dc_softc *); static int dc_list_rx_init (struct dc_softc *); static int dc_list_tx_init (struct dc_softc *); static void dc_read_srom (struct dc_softc *, int); static void dc_parse_21143_srom (struct dc_softc *); static void dc_decode_leaf_sia (struct dc_softc *, struct dc_eblock_sia *); static void dc_decode_leaf_mii (struct dc_softc *, struct dc_eblock_mii *); static void dc_decode_leaf_sym (struct dc_softc *, struct dc_eblock_sym *); static void dc_apply_fixup (struct dc_softc *, int); static void dc_dma_map_txbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); static void dc_dma_map_rxbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); #ifdef DC_USEIOSPACE #define DC_RES SYS_RES_IOPORT #define DC_RID DC_PCI_CFBIO #else #define DC_RES SYS_RES_MEMORY #define DC_RID DC_PCI_CFBMA #endif static device_method_t dc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dc_probe), DEVMETHOD(device_attach, dc_attach), DEVMETHOD(device_detach, dc_detach), DEVMETHOD(device_suspend, dc_suspend), DEVMETHOD(device_resume, dc_resume), DEVMETHOD(device_shutdown, dc_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, dc_miibus_readreg), DEVMETHOD(miibus_writereg, dc_miibus_writereg), DEVMETHOD(miibus_statchg, dc_miibus_statchg), DEVMETHOD(miibus_mediainit, dc_miibus_mediainit), { 0, 0 } }; static driver_t dc_driver = { "dc", dc_methods, sizeof(struct dc_softc) }; static devclass_t dc_devclass; #ifdef __i386__ static int dc_quick = 1; SYSCTL_INT(_hw, OID_AUTO, dc_quick, CTLFLAG_RW, &dc_quick, 0, "do not m_devget() in dc driver"); #endif DRIVER_MODULE(dc, cardbus, dc_driver, dc_devclass, 0, 0); DRIVER_MODULE(dc, pci, dc_driver, dc_devclass, 0, 0); DRIVER_MODULE(miibus, dc, miibus_driver, miibus_devclass, 0, 0); #define DC_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) #define DC_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) DC_SETBIT(sc, DC_SIO, (x)) #define SIO_CLR(x) DC_CLRBIT(sc, DC_SIO, (x)) #define IS_MPSAFE 0 static void dc_delay(struct dc_softc *sc) { int idx; for (idx = (300 / 33) + 1; idx > 0; idx--) CSR_READ_4(sc, DC_BUSCTL); } static void dc_eeprom_width(struct dc_softc *sc) { int i; /* Force EEPROM to idle state. */ dc_eeprom_idle(sc); /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); for (i = 3; i--;) { if (6 & (1 << i)) DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); else DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } for (i = 1; i <= 12; i++) { DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); if (!(CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT)) { DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); break; } DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); if (i < 4 || i > 12) sc->dc_romwidth = 6; else sc->dc_romwidth = i; /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); } static void dc_eeprom_idle(struct dc_softc *sc) { int i; CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); for (i = 0; i < 25; i++) { DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); CSR_WRITE_4(sc, DC_SIO, 0x00000000); } /* * Send a read command and address to the EEPROM, check for ACK. */ static void dc_eeprom_putbyte(struct dc_softc *sc, int addr) { int d, i; d = DC_EECMD_READ >> 6; for (i = 3; i--; ) { if (d & (1 << i)) DC_SETBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); else DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_DATAIN); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); } /* * Feed in each bit and strobe the clock. */ for (i = sc->dc_romwidth; i--;) { if (addr & (1 << i)) { SIO_SET(DC_SIO_EE_DATAIN); } else { SIO_CLR(DC_SIO_EE_DATAIN); } dc_delay(sc); SIO_SET(DC_SIO_EE_CLK); dc_delay(sc); SIO_CLR(DC_SIO_EE_CLK); dc_delay(sc); } } /* * Read a word of data stored in the EEPROM at address 'addr.' * The PNIC 82c168/82c169 has its own non-standard way to read * the EEPROM. */ static void dc_eeprom_getword_pnic(struct dc_softc *sc, int addr, u_int16_t *dest) { int i; u_int32_t r; CSR_WRITE_4(sc, DC_PN_SIOCTL, DC_PN_EEOPCODE_READ | addr); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(1); r = CSR_READ_4(sc, DC_SIO); if (!(r & DC_PN_SIOCTL_BUSY)) { *dest = (u_int16_t)(r & 0xFFFF); return; } } } /* * Read a word of data stored in the EEPROM at address 'addr.' * The Xircom X3201 has its own non-standard way to read * the EEPROM, too. */ static void dc_eeprom_getword_xircom(struct dc_softc *sc, int addr, u_int16_t *dest) { SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); addr *= 2; CSR_WRITE_4(sc, DC_ROM, addr | 0x160); *dest = (u_int16_t)CSR_READ_4(sc, DC_SIO) & 0xff; addr += 1; CSR_WRITE_4(sc, DC_ROM, addr | 0x160); *dest |= ((u_int16_t)CSR_READ_4(sc, DC_SIO) & 0xff) << 8; SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ); } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void dc_eeprom_getword(struct dc_softc *sc, int addr, u_int16_t *dest) { int i; u_int16_t word = 0; /* Force EEPROM to idle state. */ dc_eeprom_idle(sc); /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, DC_SIO, DC_SIO_EESEL); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_ROMCTL_READ); dc_delay(sc); DC_CLRBIT(sc, DC_SIO, DC_SIO_EE_CLK); dc_delay(sc); DC_SETBIT(sc, DC_SIO, DC_SIO_EE_CS); dc_delay(sc); /* * Send address of word we want to read. */ dc_eeprom_putbyte(sc, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { SIO_SET(DC_SIO_EE_CLK); dc_delay(sc); if (CSR_READ_4(sc, DC_SIO) & DC_SIO_EE_DATAOUT) word |= i; dc_delay(sc); SIO_CLR(DC_SIO_EE_CLK); dc_delay(sc); } /* Turn off EEPROM access mode. */ dc_eeprom_idle(sc); *dest = word; } /* * Read a sequence of words from the EEPROM. */ static void dc_read_eeprom(struct dc_softc *sc, caddr_t dest, int off, int cnt, int swap) { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { if (DC_IS_PNIC(sc)) dc_eeprom_getword_pnic(sc, off + i, &word); else if (DC_IS_XIRCOM(sc)) dc_eeprom_getword_xircom(sc, off + i, &word); else dc_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } } /* * The following two routines are taken from the Macronix 98713 * Application Notes pp.19-21. */ /* * Write a bit to the MII bus. */ static void dc_mii_writebit(struct dc_softc *sc, int bit) { if (bit) CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE | DC_SIO_MII_DATAOUT); else CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); } /* * Read a bit from the MII bus. */ static int dc_mii_readbit(struct dc_softc *sc) { CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_READ | DC_SIO_MII_DIR); CSR_READ_4(sc, DC_SIO); DC_SETBIT(sc, DC_SIO, DC_SIO_MII_CLK); DC_CLRBIT(sc, DC_SIO, DC_SIO_MII_CLK); if (CSR_READ_4(sc, DC_SIO) & DC_SIO_MII_DATAIN) return (1); return (0); } /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void dc_mii_sync(struct dc_softc *sc) { int i; CSR_WRITE_4(sc, DC_SIO, DC_SIO_ROMCTL_WRITE); for (i = 0; i < 32; i++) dc_mii_writebit(sc, 1); } /* * Clock a series of bits through the MII. */ static void dc_mii_send(struct dc_softc *sc, u_int32_t bits, int cnt) { int i; for (i = (0x1 << (cnt - 1)); i; i >>= 1) dc_mii_writebit(sc, bits & i); } /* * Read an PHY register through the MII. */ static int dc_mii_readreg(struct dc_softc *sc, struct dc_mii_frame *frame) { int i, ack; DC_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = DC_MII_STARTDELIM; frame->mii_opcode = DC_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Sync the PHYs. */ dc_mii_sync(sc); /* * Send command/address info. */ dc_mii_send(sc, frame->mii_stdelim, 2); dc_mii_send(sc, frame->mii_opcode, 2); dc_mii_send(sc, frame->mii_phyaddr, 5); dc_mii_send(sc, frame->mii_regaddr, 5); #ifdef notdef /* Idle bit */ dc_mii_writebit(sc, 1); dc_mii_writebit(sc, 0); #endif /* Check for ack. */ ack = dc_mii_readbit(sc); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for (i = 0; i < 16; i++) dc_mii_readbit(sc); goto fail; } for (i = 0x8000; i; i >>= 1) { if (!ack) { if (dc_mii_readbit(sc)) frame->mii_data |= i; } } fail: dc_mii_writebit(sc, 0); dc_mii_writebit(sc, 0); DC_UNLOCK(sc); if (ack) return (1); return (0); } /* * Write to a PHY register through the MII. */ static int dc_mii_writereg(struct dc_softc *sc, struct dc_mii_frame *frame) { DC_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = DC_MII_STARTDELIM; frame->mii_opcode = DC_MII_WRITEOP; frame->mii_turnaround = DC_MII_TURNAROUND; /* * Sync the PHYs. */ dc_mii_sync(sc); dc_mii_send(sc, frame->mii_stdelim, 2); dc_mii_send(sc, frame->mii_opcode, 2); dc_mii_send(sc, frame->mii_phyaddr, 5); dc_mii_send(sc, frame->mii_regaddr, 5); dc_mii_send(sc, frame->mii_turnaround, 2); dc_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ dc_mii_writebit(sc, 0); dc_mii_writebit(sc, 0); DC_UNLOCK(sc); return (0); } static int dc_miibus_readreg(device_t dev, int phy, int reg) { struct dc_mii_frame frame; struct dc_softc *sc; int i, rval, phy_reg = 0; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); /* * Note: both the AL981 and AN985 have internal PHYs, * however the AL981 provides direct access to the PHY * registers while the AN985 uses a serial MII interface. * The AN985's MII interface is also buggy in that you * can read from any MII address (0 to 31), but only address 1 * behaves normally. To deal with both cases, we pretend * that the PHY is at MII address 1. */ if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return (0); /* * Note: the ukphy probes of the RS7112 report a PHY at * MII address 0 (possibly HomePNA?) and 1 (ethernet) * so we only respond to correct one. */ if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) return (0); if (sc->dc_pmode != DC_PMODE_MII) { if (phy == (MII_NPHY - 1)) { switch (reg) { case MII_BMSR: /* * Fake something to make the probe * code think there's a PHY here. */ return (BMSR_MEDIAMASK); break; case MII_PHYIDR1: if (DC_IS_PNIC(sc)) return (DC_VENDORID_LO); return (DC_VENDORID_DEC); break; case MII_PHYIDR2: if (DC_IS_PNIC(sc)) return (DC_DEVICEID_82C168); return (DC_DEVICEID_21143); break; default: return (0); break; } } else return (0); } if (DC_IS_PNIC(sc)) { CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_READ | (phy << 23) | (reg << 18)); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(1); rval = CSR_READ_4(sc, DC_PN_MII); if (!(rval & DC_PN_MII_BUSY)) { rval &= 0xFFFF; return (rval == 0xFFFF ? 0 : rval); } } return (0); } if (DC_IS_COMET(sc)) { switch (reg) { case MII_BMCR: phy_reg = DC_AL_BMCR; break; case MII_BMSR: phy_reg = DC_AL_BMSR; break; case MII_PHYIDR1: phy_reg = DC_AL_VENID; break; case MII_PHYIDR2: phy_reg = DC_AL_DEVID; break; case MII_ANAR: phy_reg = DC_AL_ANAR; break; case MII_ANLPAR: phy_reg = DC_AL_LPAR; break; case MII_ANER: phy_reg = DC_AL_ANER; break; default: printf("dc%d: phy_read: bad phy register %x\n", sc->dc_unit, reg); return (0); break; } rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF; if (rval == 0xFFFF) return (0); return (rval); } frame.mii_phyaddr = phy; frame.mii_regaddr = reg; if (sc->dc_type == DC_TYPE_98713) { phy_reg = CSR_READ_4(sc, DC_NETCFG); CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); } dc_mii_readreg(sc, &frame); if (sc->dc_type == DC_TYPE_98713) CSR_WRITE_4(sc, DC_NETCFG, phy_reg); return (frame.mii_data); } static int dc_miibus_writereg(device_t dev, int phy, int reg, int data) { struct dc_softc *sc; struct dc_mii_frame frame; int i, phy_reg = 0; sc = device_get_softc(dev); bzero(&frame, sizeof(frame)); if (DC_IS_ADMTEK(sc) && phy != DC_ADMTEK_PHYADDR) return (0); if (DC_IS_CONEXANT(sc) && phy != DC_CONEXANT_PHYADDR) return (0); if (DC_IS_PNIC(sc)) { CSR_WRITE_4(sc, DC_PN_MII, DC_PN_MIIOPCODE_WRITE | (phy << 23) | (reg << 10) | data); for (i = 0; i < DC_TIMEOUT; i++) { if (!(CSR_READ_4(sc, DC_PN_MII) & DC_PN_MII_BUSY)) break; } return (0); } if (DC_IS_COMET(sc)) { switch (reg) { case MII_BMCR: phy_reg = DC_AL_BMCR; break; case MII_BMSR: phy_reg = DC_AL_BMSR; break; case MII_PHYIDR1: phy_reg = DC_AL_VENID; break; case MII_PHYIDR2: phy_reg = DC_AL_DEVID; break; case MII_ANAR: phy_reg = DC_AL_ANAR; break; case MII_ANLPAR: phy_reg = DC_AL_LPAR; break; case MII_ANER: phy_reg = DC_AL_ANER; break; default: printf("dc%d: phy_write: bad phy register %x\n", sc->dc_unit, reg); return (0); break; } CSR_WRITE_4(sc, phy_reg, data); return (0); } frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; if (sc->dc_type == DC_TYPE_98713) { phy_reg = CSR_READ_4(sc, DC_NETCFG); CSR_WRITE_4(sc, DC_NETCFG, phy_reg & ~DC_NETCFG_PORTSEL); } dc_mii_writereg(sc, &frame); if (sc->dc_type == DC_TYPE_98713) CSR_WRITE_4(sc, DC_NETCFG, phy_reg); return (0); } static void dc_miibus_statchg(device_t dev) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = device_get_softc(dev); if (DC_IS_ADMTEK(sc)) return; mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { dc_setcfg(sc, ifm->ifm_media); sc->dc_if_media = ifm->ifm_media; } else { dc_setcfg(sc, mii->mii_media_active); sc->dc_if_media = mii->mii_media_active; } } /* * Special support for DM9102A cards with HomePNA PHYs. Note: * with the Davicom DM9102A/DM9801 eval board that I have, it seems * to be impossible to talk to the management interface of the DM9801 * PHY (its MDIO pin is not connected to anything). Consequently, * the driver has to just 'know' about the additional mode and deal * with it itself. *sigh* */ static void dc_miibus_mediainit(device_t dev) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; int rev; rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; sc = device_get_softc(dev); mii = device_get_softc(sc->dc_miibus); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && rev >= DC_REVISION_DM9102A) ifmedia_add(ifm, IFM_ETHER | IFM_HPNA_1, 0, NULL); } #define DC_BITS_512 9 #define DC_BITS_128 7 #define DC_BITS_64 6 static uint32_t dc_mchash_le(struct dc_softc *sc, const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); /* * The hash table on the PNIC II and the MX98715AEC-C/D/E * chips is only 128 bits wide. */ if (sc->dc_flags & DC_128BIT_HASH) return (crc & ((1 << DC_BITS_128) - 1)); /* The hash table on the MX98715BEC is only 64 bits wide. */ if (sc->dc_flags & DC_64BIT_HASH) return (crc & ((1 << DC_BITS_64) - 1)); /* Xircom's hash filtering table is different (read: weird) */ /* Xircom uses the LEAST significant bits */ if (DC_IS_XIRCOM(sc)) { if ((crc & 0x180) == 0x180) return ((crc & 0x0F) + (crc & 0x70) * 3 + (14 << 4)); else return ((crc & 0x1F) + ((crc >> 1) & 0xF0) * 3 + (12 << 4)); } return (crc & ((1 << DC_BITS_512) - 1)); } /* * Calculate CRC of a multicast group address, return the lower 6 bits. */ static uint32_t dc_mchash_be(const uint8_t *addr) { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_be(addr, ETHER_ADDR_LEN); /* Return the filter bit position. */ return ((crc >> 26) & 0x0000003F); } /* * 21143-style RX filter setup routine. Filter programming is done by * downloading a special setup frame into the TX engine. 21143, Macronix, * PNIC, PNIC II and Davicom chips are programmed this way. * * We always program the chip using 'hash perfect' mode, i.e. one perfect * address (our node address) and a 512-bit hash filter for multicast * frames. We also sneak the broadcast address into the hash filter since * we need that too. */ static void dc_setfilt_21143(struct dc_softc *sc) { struct dc_desc *sframe; u_int32_t h, *sp; struct ifmultiaddr *ifma; struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; i = sc->dc_cdata.dc_tx_prod; DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); sc->dc_cdata.dc_tx_cnt++; sframe = &sc->dc_ldata->dc_tx_list[i]; sp = sc->dc_cdata.dc_sbuf; bzero(sp, DC_SFRAME_LEN); sframe->dc_data = htole32(sc->dc_saddr); sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | DC_FILTER_HASHPERF | DC_TXCTL_FINT); sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= htole32(1 << (h & 0xF)); } if (ifp->if_flags & IFF_BROADCAST) { h = dc_mchash_le(sc, ifp->if_broadcastaddr); sp[h >> 4] |= htole32(1 << (h & 0xF)); } /* Set our MAC address */ sp[39] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[0]); sp[40] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[1]); sp[41] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[2]); sframe->dc_status = htole32(DC_TXSTAT_OWN); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * The PNIC takes an exceedingly long time to process its * setup frame; wait 10ms after posting the setup frame * before proceeding, just so it has time to swallow its * medicine. */ DELAY(10000); ifp->if_timer = 5; } static void dc_setfilt_admtek(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int h = 0; u_int32_t hashes[2] = { 0, 0 }; ifp = &sc->arpcom.ac_if; /* Init our MAC address. */ CSR_WRITE_4(sc, DC_AL_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); CSR_WRITE_4(sc, DC_AL_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); /* First, zot all the existing hash bits. */ CSR_WRITE_4(sc, DC_AL_MAR0, 0); CSR_WRITE_4(sc, DC_AL_MAR1, 0); /* * If we're already in promisc or allmulti mode, we * don't have to bother programming the multicast filter. */ if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) return; /* Now program new ones. */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; if (DC_IS_CENTAUR(sc)) h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); else h = dc_mchash_be( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } CSR_WRITE_4(sc, DC_AL_MAR0, hashes[0]); CSR_WRITE_4(sc, DC_AL_MAR1, hashes[1]); } static void dc_setfilt_asix(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; int h = 0; u_int32_t hashes[2] = { 0, 0 }; ifp = &sc->arpcom.ac_if; /* Init our MAC address */ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); /* * The ASIX chip has a special bit to enable reception * of broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); else DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD); /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, 0); /* * If we're already in promisc or allmulti mode, we * don't have to bother programming the multicast filter. */ if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) return; /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_be(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR0); CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[0]); CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_MAR1); CSR_WRITE_4(sc, DC_AX_FILTDATA, hashes[1]); } static void dc_setfilt_xircom(struct dc_softc *sc) { struct ifnet *ifp; struct ifmultiaddr *ifma; struct dc_desc *sframe; u_int32_t h, *sp; int i; ifp = &sc->arpcom.ac_if; DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)); i = sc->dc_cdata.dc_tx_prod; DC_INC(sc->dc_cdata.dc_tx_prod, DC_TX_LIST_CNT); sc->dc_cdata.dc_tx_cnt++; sframe = &sc->dc_ldata->dc_tx_list[i]; sp = sc->dc_cdata.dc_sbuf; bzero(sp, DC_SFRAME_LEN); sframe->dc_data = htole32(sc->dc_saddr); sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP | DC_TXCTL_TLINK | DC_FILTER_HASHPERF | DC_TXCTL_FINT); sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC); if (ifp->if_flags & IFF_ALLMULTI) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); else DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = dc_mchash_le(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); sp[h >> 4] |= htole32(1 << (h & 0xF)); } if (ifp->if_flags & IFF_BROADCAST) { h = dc_mchash_le(sc, ifp->if_broadcastaddr); sp[h >> 4] |= htole32(1 << (h & 0xF)); } /* Set our MAC address */ sp[0] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[0]); sp[1] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[1]); sp[2] = DC_SP_MAC(((u_int16_t *)sc->arpcom.ac_enaddr)[2]); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); ifp->if_flags |= IFF_RUNNING; sframe->dc_status = htole32(DC_TXSTAT_OWN); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * Wait some time... */ DELAY(1000); ifp->if_timer = 5; } static void dc_setfilt(struct dc_softc *sc) { if (DC_IS_INTEL(sc) || DC_IS_MACRONIX(sc) || DC_IS_PNIC(sc) || DC_IS_PNICII(sc) || DC_IS_DAVICOM(sc) || DC_IS_CONEXANT(sc)) dc_setfilt_21143(sc); if (DC_IS_ASIX(sc)) dc_setfilt_asix(sc); if (DC_IS_ADMTEK(sc)) dc_setfilt_admtek(sc); if (DC_IS_XIRCOM(sc)) dc_setfilt_xircom(sc); } /* * In order to fiddle with the 'full-duplex' and '100Mbps' bits in * the netconfig register, we first have to put the transmit and/or * receive logic in the idle state. */ static void dc_setcfg(struct dc_softc *sc, int media) { int i, restart = 0, watchdogreg; u_int32_t isr; if (IFM_SUBTYPE(media) == IFM_NONE) return; if (CSR_READ_4(sc, DC_NETCFG) & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)) { restart = 1; DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON)); for (i = 0; i < DC_TIMEOUT; i++) { isr = CSR_READ_4(sc, DC_ISR); if (isr & DC_ISR_TX_IDLE && ((isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED || (isr & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT)) break; DELAY(10); } if (i == DC_TIMEOUT) printf("dc%d: failed to force tx and " "rx to idle state\n", sc->dc_unit); } if (IFM_SUBTYPE(media) == IFM_100_TX) { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); if (sc->dc_pmode == DC_PMODE_MII) { if (DC_IS_INTEL(sc)) { /* There's a write enable bit here that reads as 1. */ watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); watchdogreg &= ~DC_WDOG_CTLWREN; watchdogreg |= DC_WDOG_JABBERDIS; CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); } else { DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); } DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_PORTSEL | DC_NETCFG_SCRAMBLER)); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_SCRAMBLER)); if (!DC_IS_DAVICOM(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, IFM_AUTO); } else { if (DC_IS_PNIC(sc)) { DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_SPEEDSEL); DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); } DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, (media & IFM_GMASK) == IFM_FDX ? IFM_100_TX | IFM_FDX : IFM_100_TX); } } if (IFM_SUBTYPE(media) == IFM_10_T) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_SPEEDSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_HEARTBEAT); if (sc->dc_pmode == DC_PMODE_MII) { /* There's a write enable bit here that reads as 1. */ if (DC_IS_INTEL(sc)) { watchdogreg = CSR_READ_4(sc, DC_WATCHDOG); watchdogreg &= ~DC_WDOG_CTLWREN; watchdogreg |= DC_WDOG_JABBERDIS; CSR_WRITE_4(sc, DC_WATCHDOG, watchdogreg); } else { DC_SETBIT(sc, DC_WATCHDOG, DC_WDOG_JABBERDIS); } DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_PCS | DC_NETCFG_PORTSEL | DC_NETCFG_SCRAMBLER)); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PCS); if (!DC_IS_DAVICOM(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if (DC_IS_INTEL(sc)) dc_apply_fixup(sc, IFM_AUTO); } else { if (DC_IS_PNIC(sc)) { DC_PN_GPIO_CLRBIT(sc, DC_PN_GPIO_SPEEDSEL); DC_PN_GPIO_SETBIT(sc, DC_PN_GPIO_100TX_LOOP); DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_SPEEDSEL); } DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PCS); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_SCRAMBLER); if (DC_IS_INTEL(sc)) { DC_CLRBIT(sc, DC_SIARESET, DC_SIA_RESET); DC_CLRBIT(sc, DC_10BTCTRL, 0xFFFF); if ((media & IFM_GMASK) == IFM_FDX) DC_SETBIT(sc, DC_10BTCTRL, 0x7F3D); else DC_SETBIT(sc, DC_10BTCTRL, 0x7F3F); DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); DC_CLRBIT(sc, DC_10BTCTRL, DC_TCTL_AUTONEGENBL); dc_apply_fixup(sc, (media & IFM_GMASK) == IFM_FDX ? IFM_10_T | IFM_FDX : IFM_10_T); DELAY(20000); } } } /* * If this is a Davicom DM9102A card with a DM9801 HomePNA * PHY and we want HomePNA mode, set the portsel bit to turn * on the external MII port. */ if (DC_IS_DAVICOM(sc)) { if (IFM_SUBTYPE(media) == IFM_HPNA_1) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); sc->dc_link = 1; } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_PORTSEL); } } if ((media & IFM_GMASK) == IFM_FDX) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) DC_SETBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_FULLDUPLEX); if (sc->dc_pmode == DC_PMODE_SYM && DC_IS_PNIC(sc)) DC_CLRBIT(sc, DC_PN_NWAY, DC_PN_NWAY_DUPLEX); } if (restart) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON | DC_NETCFG_RX_ON); } static void dc_reset(struct dc_softc *sc) { int i; DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); for (i = 0; i < DC_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, DC_BUSCTL) & DC_BUSCTL_RESET)) break; } if (DC_IS_ASIX(sc) || DC_IS_ADMTEK(sc) || DC_IS_CONEXANT(sc) || DC_IS_XIRCOM(sc) || DC_IS_INTEL(sc)) { DELAY(10000); DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET); i = 0; } if (i == DC_TIMEOUT) printf("dc%d: reset never completed!\n", sc->dc_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); CSR_WRITE_4(sc, DC_IMR, 0x00000000); CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000); CSR_WRITE_4(sc, DC_NETCFG, 0x00000000); /* * Bring the SIA out of reset. In some cases, it looks * like failing to unreset the SIA soon enough gets it * into a state where it will never come out of reset * until we reset the whole chip again. */ if (DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET); CSR_WRITE_4(sc, DC_10BTCTRL, 0); CSR_WRITE_4(sc, DC_WATCHDOG, 0); } } static struct dc_type * dc_devtype(device_t dev) { struct dc_type *t; u_int32_t rev; t = dc_devs; while (t->dc_name != NULL) { if ((pci_get_vendor(dev) == t->dc_vid) && (pci_get_device(dev) == t->dc_did)) { /* Check the PCI revision */ rev = pci_read_config(dev, DC_PCI_CFRV, 4) & 0xFF; if (t->dc_did == DC_DEVICEID_98713 && rev >= DC_REVISION_98713A) t++; if (t->dc_did == DC_DEVICEID_98713_CP && rev >= DC_REVISION_98713A) t++; if (t->dc_did == DC_DEVICEID_987x5 && rev >= DC_REVISION_98715AEC_C) t++; if (t->dc_did == DC_DEVICEID_987x5 && rev >= DC_REVISION_98725) t++; if (t->dc_did == DC_DEVICEID_AX88140A && rev >= DC_REVISION_88141) t++; if (t->dc_did == DC_DEVICEID_82C168 && rev >= DC_REVISION_82C169) t++; if (t->dc_did == DC_DEVICEID_DM9102 && rev >= DC_REVISION_DM9102A) t++; /* * The Microsoft MN-130 has a device ID of 0x0002, * which happens to be the same as the PNIC 82c168. * To keep dc_attach() from getting confused, we * pretend its ID is something different. * XXX: ideally, dc_attach() should be checking * vendorid+deviceid together to avoid such * collisions. */ if (t->dc_vid == DC_VENDORID_MICROSOFT && t->dc_did == DC_DEVICEID_MSMN130) t++; return (t); } t++; } return (NULL); } /* * Probe for a 21143 or clone chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We do a little bit of extra work to identify the exact type of * chip. The MX98713 and MX98713A have the same PCI vendor/device ID, * but different revision IDs. The same is true for 98715/98715A * chips and the 98725, as well as the ASIX and ADMtek chips. In some * cases, the exact chip revision affects driver behavior. */ static int dc_probe(device_t dev) { struct dc_type *t; t = dc_devtype(dev); if (t != NULL) { device_set_desc(dev, t->dc_name); return (0); } return (ENXIO); } -#ifndef BURN_BRIDGES static void -dc_acpi(device_t dev) -{ - int unit; - u_int32_t iobase, membase, irq; - - unit = device_get_unit(dev); - - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - /* Save important PCI config data. */ - iobase = pci_read_config(dev, DC_PCI_CFBIO, 4); - membase = pci_read_config(dev, DC_PCI_CFBMA, 4); - irq = pci_read_config(dev, DC_PCI_CFIT, 4); - - /* Reset the power state. */ - printf("dc%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, DC_PCI_CFBIO, iobase, 4); - pci_write_config(dev, DC_PCI_CFBMA, membase, 4); - pci_write_config(dev, DC_PCI_CFIT, irq, 4); - } -} -#endif - -static void dc_apply_fixup(struct dc_softc *sc, int media) { struct dc_mediainfo *m; u_int8_t *p; int i; u_int32_t reg; m = sc->dc_mi; while (m != NULL) { if (m->dc_media == media) break; m = m->dc_next; } if (m == NULL) return; for (i = 0, p = m->dc_reset_ptr; i < m->dc_reset_len; i++, p += 2) { reg = (p[0] | (p[1] << 8)) << 16; CSR_WRITE_4(sc, DC_WATCHDOG, reg); } for (i = 0, p = m->dc_gp_ptr; i < m->dc_gp_len; i++, p += 2) { reg = (p[0] | (p[1] << 8)) << 16; CSR_WRITE_4(sc, DC_WATCHDOG, reg); } } static void dc_decode_leaf_sia(struct dc_softc *sc, struct dc_eblock_sia *l) { struct dc_mediainfo *m; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); switch (l->dc_sia_code & ~DC_SIA_CODE_EXT) { case DC_SIA_CODE_10BT: m->dc_media = IFM_10_T; break; case DC_SIA_CODE_10BT_FDX: m->dc_media = IFM_10_T | IFM_FDX; break; case DC_SIA_CODE_10B2: m->dc_media = IFM_10_2; break; case DC_SIA_CODE_10B5: m->dc_media = IFM_10_5; break; default: break; } /* * We need to ignore CSR13, CSR14, CSR15 for SIA mode. * Things apparently already work for cards that do * supply Media Specific Data. */ if (l->dc_sia_code & DC_SIA_CODE_EXT) { m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_un.dc_sia_ext.dc_sia_gpio_ctl; } else { m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_un.dc_sia_noext.dc_sia_gpio_ctl; } m->dc_next = sc->dc_mi; sc->dc_mi = m; sc->dc_pmode = DC_PMODE_SIA; } static void dc_decode_leaf_sym(struct dc_softc *sc, struct dc_eblock_sym *l) { struct dc_mediainfo *m; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); if (l->dc_sym_code == DC_SYM_CODE_100BT) m->dc_media = IFM_100_TX; if (l->dc_sym_code == DC_SYM_CODE_100BT_FDX) m->dc_media = IFM_100_TX | IFM_FDX; m->dc_gp_len = 2; m->dc_gp_ptr = (u_int8_t *)&l->dc_sym_gpio_ctl; m->dc_next = sc->dc_mi; sc->dc_mi = m; sc->dc_pmode = DC_PMODE_SYM; } static void dc_decode_leaf_mii(struct dc_softc *sc, struct dc_eblock_mii *l) { struct dc_mediainfo *m; u_int8_t *p; m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO); /* We abuse IFM_AUTO to represent MII. */ m->dc_media = IFM_AUTO; m->dc_gp_len = l->dc_gpr_len; p = (u_int8_t *)l; p += sizeof(struct dc_eblock_mii); m->dc_gp_ptr = p; p += 2 * l->dc_gpr_len; m->dc_reset_len = *p; p++; m->dc_reset_ptr = p; m->dc_next = sc->dc_mi; sc->dc_mi = m; } static void dc_read_srom(struct dc_softc *sc, int bits) { int size; size = 2 << bits; sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT); dc_read_eeprom(sc, (caddr_t)sc->dc_srom, 0, (size / 2), 0); } static void dc_parse_21143_srom(struct dc_softc *sc) { struct dc_leaf_hdr *lhdr; struct dc_eblock_hdr *hdr; int have_mii, i, loff; char *ptr; have_mii = 0; loff = sc->dc_srom[27]; lhdr = (struct dc_leaf_hdr *)&(sc->dc_srom[loff]); ptr = (char *)lhdr; ptr += sizeof(struct dc_leaf_hdr) - 1; /* * Look if we got a MII media block. */ for (i = 0; i < lhdr->dc_mcnt; i++) { hdr = (struct dc_eblock_hdr *)ptr; if (hdr->dc_type == DC_EBLOCK_MII) have_mii++; ptr += (hdr->dc_len & 0x7F); ptr++; } /* * Do the same thing again. Only use SIA and SYM media * blocks if no MII media block is available. */ ptr = (char *)lhdr; ptr += sizeof(struct dc_leaf_hdr) - 1; for (i = 0; i < lhdr->dc_mcnt; i++) { hdr = (struct dc_eblock_hdr *)ptr; switch (hdr->dc_type) { case DC_EBLOCK_MII: dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr); break; case DC_EBLOCK_SIA: if (! have_mii) dc_decode_leaf_sia(sc, (struct dc_eblock_sia *)hdr); break; case DC_EBLOCK_SYM: if (! have_mii) dc_decode_leaf_sym(sc, (struct dc_eblock_sym *)hdr); break; default: /* Don't care. Yet. */ break; } ptr += (hdr->dc_len & 0x7F); ptr++; } } static void dc_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) { u_int32_t *paddr; KASSERT(nseg == 1, ("wrong number of segments, should be 1")); paddr = arg; *paddr = segs->ds_addr; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int dc_attach(device_t dev) { int tmp = 0; u_char eaddr[ETHER_ADDR_LEN]; u_int32_t command; struct dc_softc *sc; struct ifnet *ifp; u_int32_t revision; int unit, error = 0, rid, mac_offset; int i; u_int8_t *mac; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES + /* - * Handle power management nonsense. - */ - dc_acpi(dev); -#endif - /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = DC_RID; sc->dc_res = bus_alloc_resource_any(dev, DC_RES, &rid, RF_ACTIVE); if (sc->dc_res == NULL) { printf("dc%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->dc_btag = rman_get_bustag(sc->dc_res); sc->dc_bhandle = rman_get_bushandle(sc->dc_res); /* Allocate interrupt. */ rid = 0; sc->dc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->dc_irq == NULL) { printf("dc%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Need this info to decide on a chip type. */ sc->dc_info = dc_devtype(dev); revision = pci_read_config(dev, DC_PCI_CFRV, 4) & 0x000000FF; /* Get the eeprom width, but PNIC and XIRCOM have diff eeprom */ if (sc->dc_info->dc_did != DC_DEVICEID_82C168 && sc->dc_info->dc_did != DC_DEVICEID_X3201) dc_eeprom_width(sc); switch (sc->dc_info->dc_did) { case DC_DEVICEID_21143: sc->dc_type = DC_TYPE_21143; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL; /* Save EEPROM contents so we can parse them later. */ dc_read_srom(sc, sc->dc_romwidth); break; case DC_DEVICEID_DM9009: case DC_DEVICEID_DM9100: case DC_DEVICEID_DM9102: sc->dc_type = DC_TYPE_DM9102; sc->dc_flags |= DC_TX_COALESCE | DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_TX_STORENFWD; sc->dc_flags |= DC_TX_ALIGN; sc->dc_pmode = DC_PMODE_MII; /* Increase the latency timer value. */ command = pci_read_config(dev, DC_PCI_CFLT, 4); command &= 0xFFFF00FF; command |= 0x00008000; pci_write_config(dev, DC_PCI_CFLT, command, 4); break; case DC_DEVICEID_AL981: sc->dc_type = DC_TYPE_AL981; sc->dc_flags |= DC_TX_USE_TX_INTR; sc->dc_flags |= DC_TX_ADMTEK_WAR; sc->dc_pmode = DC_PMODE_MII; dc_read_srom(sc, sc->dc_romwidth); break; case DC_DEVICEID_AN985: case DC_DEVICEID_ADM9511: case DC_DEVICEID_ADM9513: case DC_DEVICEID_FA511: case DC_DEVICEID_FE2500: case DC_DEVICEID_EN2242: case DC_DEVICEID_HAWKING_PN672TX: case DC_DEVICEID_3CSOHOB: case DC_DEVICEID_MSMN120: case DC_DEVICEID_MSMN130_FAKE: /* XXX avoid collision with PNIC*/ sc->dc_type = DC_TYPE_AN985; sc->dc_flags |= DC_64BIT_HASH; sc->dc_flags |= DC_TX_USE_TX_INTR; sc->dc_flags |= DC_TX_ADMTEK_WAR; sc->dc_pmode = DC_PMODE_MII; /* Don't read SROM for - auto-loaded on reset */ break; case DC_DEVICEID_98713: case DC_DEVICEID_98713_CP: if (revision < DC_REVISION_98713A) { sc->dc_type = DC_TYPE_98713; } if (revision >= DC_REVISION_98713A) { sc->dc_type = DC_TYPE_98713A; sc->dc_flags |= DC_21143_NWAY; } sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; break; case DC_DEVICEID_987x5: case DC_DEVICEID_EN1217: /* * Macronix MX98715AEC-C/D/E parts have only a * 128-bit hash table. We need to deal with these * in the same manner as the PNIC II so that we * get the right number of bits out of the * CRC routine. */ if (revision >= DC_REVISION_98715AEC_C && revision < DC_REVISION_98725) sc->dc_flags |= DC_128BIT_HASH; sc->dc_type = DC_TYPE_987x5; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_98727: sc->dc_type = DC_TYPE_987x5; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_82C115: sc->dc_type = DC_TYPE_PNICII; sc->dc_flags |= DC_TX_POLL | DC_TX_USE_TX_INTR | DC_128BIT_HASH; sc->dc_flags |= DC_REDUCED_MII_POLL | DC_21143_NWAY; break; case DC_DEVICEID_82C168: sc->dc_type = DC_TYPE_PNIC; sc->dc_flags |= DC_TX_STORENFWD | DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_PNIC_RX_BUG_WAR; sc->dc_pnic_rx_buf = malloc(DC_RXLEN * 5, M_DEVBUF, M_NOWAIT); if (revision < DC_REVISION_82C169) sc->dc_pmode = DC_PMODE_SYM; break; case DC_DEVICEID_AX88140A: sc->dc_type = DC_TYPE_ASIX; sc->dc_flags |= DC_TX_USE_TX_INTR | DC_TX_INTR_FIRSTFRAG; sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_X3201: sc->dc_type = DC_TYPE_XIRCOM; sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE | DC_TX_ALIGN; /* * We don't actually need to coalesce, but we're doing * it to obtain a double word aligned buffer. * The DC_TX_COALESCE flag is required. */ sc->dc_pmode = DC_PMODE_MII; break; case DC_DEVICEID_RS7112: sc->dc_type = DC_TYPE_CONEXANT; sc->dc_flags |= DC_TX_INTR_ALWAYS; sc->dc_flags |= DC_REDUCED_MII_POLL; sc->dc_pmode = DC_PMODE_MII; dc_read_srom(sc, sc->dc_romwidth); break; default: printf("dc%d: unknown device: %x\n", sc->dc_unit, sc->dc_info->dc_did); break; } /* Save the cache line size. */ if (DC_IS_DAVICOM(sc)) sc->dc_cachesize = 0; else sc->dc_cachesize = pci_read_config(dev, DC_PCI_CFLT, 4) & 0xFF; /* Reset the adapter. */ dc_reset(sc); /* Take 21143 out of snooze mode */ if (DC_IS_INTEL(sc) || DC_IS_XIRCOM(sc)) { command = pci_read_config(dev, DC_PCI_CFDD, 4); command &= ~(DC_CFDD_SNOOZE_MODE | DC_CFDD_SLEEP_MODE); pci_write_config(dev, DC_PCI_CFDD, command, 4); } /* * Try to learn something about the supported media. * We know that ASIX and ADMtek and Davicom devices * will *always* be using MII media, so that's a no-brainer. * The tricky ones are the Macronix/PNIC II and the * Intel 21143. */ if (DC_IS_INTEL(sc)) dc_parse_21143_srom(sc); else if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { if (sc->dc_type == DC_TYPE_98713) sc->dc_pmode = DC_PMODE_MII; else sc->dc_pmode = DC_PMODE_SYM; } else if (!sc->dc_pmode) sc->dc_pmode = DC_PMODE_MII; /* * Get station address from the EEPROM. */ switch(sc->dc_type) { case DC_TYPE_98713: case DC_TYPE_98713A: case DC_TYPE_987x5: case DC_TYPE_PNICII: dc_read_eeprom(sc, (caddr_t)&mac_offset, (DC_EE_NODEADDR_OFFSET / 2), 1, 0); dc_read_eeprom(sc, (caddr_t)&eaddr, (mac_offset / 2), 3, 0); break; case DC_TYPE_PNIC: dc_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 1); break; case DC_TYPE_DM9102: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); #ifdef __sparc64__ /* * If this is an onboard dc(4) the station address read from * the EEPROM is all zero and we have to get it from the fcode. */ for (i = 0; i < ETHER_ADDR_LEN; i++) if (eaddr[i] != 0x00) break; if (i >= ETHER_ADDR_LEN && OF_getetheraddr2(dev, eaddr) == -1) OF_getetheraddr(dev, eaddr); #endif break; case DC_TYPE_21143: case DC_TYPE_ASIX: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); break; case DC_TYPE_AL981: case DC_TYPE_AN985: *(u_int32_t *)(&eaddr[0]) = CSR_READ_4(sc, DC_AL_PAR0); *(u_int16_t *)(&eaddr[4]) = CSR_READ_4(sc, DC_AL_PAR1); break; case DC_TYPE_CONEXANT: bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr, ETHER_ADDR_LEN); break; case DC_TYPE_XIRCOM: /* The MAC comes from the CIS. */ mac = pci_get_ether(dev); if (!mac) { device_printf(dev, "No station address in CIS!\n"); error = ENXIO; goto fail; } bcopy(mac, eaddr, ETHER_ADDR_LEN); break; default: dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0); break; } sc->dc_unit = unit; bcopy(eaddr, &sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct dc_list_data), 1, sizeof(struct dc_list_data), 0, NULL, NULL, &sc->dc_ltag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } error = bus_dmamem_alloc(sc->dc_ltag, (void **)&sc->dc_ldata, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->dc_lmap); if (error) { printf("dc%d: failed to allocate DMA safe memory\n", unit); error = ENXIO; goto fail; } error = bus_dmamap_load(sc->dc_ltag, sc->dc_lmap, sc->dc_ldata, sizeof(struct dc_list_data), dc_dma_map_addr, &sc->dc_laddr, BUS_DMA_NOWAIT); if (error) { printf("dc%d: cannot get address of the descriptors\n", unit); error = ENXIO; goto fail; } /* * Allocate a busdma tag and DMA safe memory for the multicast * setup frame. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, DC_SFRAME_LEN + DC_MIN_FRAMELEN, 1, DC_SFRAME_LEN + DC_MIN_FRAMELEN, 0, NULL, NULL, &sc->dc_stag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } error = bus_dmamem_alloc(sc->dc_stag, (void **)&sc->dc_cdata.dc_sbuf, BUS_DMA_NOWAIT, &sc->dc_smap); if (error) { printf("dc%d: failed to allocate DMA safe memory\n", unit); error = ENXIO; goto fail; } error = bus_dmamap_load(sc->dc_stag, sc->dc_smap, sc->dc_cdata.dc_sbuf, DC_SFRAME_LEN, dc_dma_map_addr, &sc->dc_saddr, BUS_DMA_NOWAIT); if (error) { printf("dc%d: cannot get address of the descriptors\n", unit); error = ENXIO; goto fail; } /* Allocate a busdma tag for mbufs. */ error = bus_dma_tag_create(NULL, PAGE_SIZE, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * DC_TX_LIST_CNT, DC_TX_LIST_CNT, MCLBYTES, 0, NULL, NULL, &sc->dc_mtag); if (error) { printf("dc%d: failed to allocate busdma tag\n", unit); error = ENXIO; goto fail; } /* Create the TX/RX busdma maps. */ for (i = 0; i < DC_TX_LIST_CNT; i++) { error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_cdata.dc_tx_map[i]); if (error) { printf("dc%d: failed to init TX ring\n", unit); error = ENXIO; goto fail; } } for (i = 0; i < DC_RX_LIST_CNT; i++) { error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_cdata.dc_rx_map[i]); if (error) { printf("dc%d: failed to init RX ring\n", unit); error = ENXIO; goto fail; } } error = bus_dmamap_create(sc->dc_mtag, 0, &sc->dc_sparemap); if (error) { printf("dc%d: failed to init RX ring\n", unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); /* XXX: bleah, MTU gets overwritten in ether_ifattach() */ ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = dc_ioctl; ifp->if_start = dc_start; ifp->if_watchdog = dc_watchdog; ifp->if_init = dc_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = DC_TX_LIST_CNT - 1; /* * Do MII setup. If this is a 21143, check for a PHY on the * MII bus after applying any necessary fixups to twiddle the * GPIO bits. If we don't end up finding a PHY, restore the * old selection (SIA only or SIA/SYM) and attach the dcphy * driver instead. */ if (DC_IS_INTEL(sc)) { dc_apply_fixup(sc, IFM_AUTO); tmp = sc->dc_pmode; sc->dc_pmode = DC_PMODE_MII; } error = mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); if (error && DC_IS_INTEL(sc)) { sc->dc_pmode = tmp; if (sc->dc_pmode != DC_PMODE_SIA) sc->dc_pmode = DC_PMODE_SYM; sc->dc_flags |= DC_21143_NWAY; mii_phy_probe(dev, &sc->dc_miibus, dc_ifmedia_upd, dc_ifmedia_sts); /* * For non-MII cards, we need to have the 21143 * drive the LEDs. Except there are some systems * like the NEC VersaPro NoteBook PC which have no * LEDs, and twiddling these bits has adverse effects * on them. (I.e. you suddenly can't get a link.) */ if (pci_read_config(dev, DC_PCI_CSID, 4) != 0x80281033) sc->dc_flags |= DC_TULIP_LEDS; error = 0; } if (error) { printf("dc%d: MII without any PHY!\n", sc->dc_unit); goto fail; } if (DC_IS_XIRCOM(sc)) { /* * setup General Purpose Port mode and data so the tulip * can talk to the MII. */ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); } if (DC_IS_ADMTEK(sc)) { /* * Set automatic TX underrun recovery for the ADMtek chips */ DC_SETBIT(sc, DC_AL_CR, DC_AL_CR_ATUR); } /* * Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; callout_init(&sc->dc_stat_ch, IS_MPSAFE ? CALLOUT_MPSAFE : 0); #ifdef SRM_MEDIA sc->dc_srm_media = 0; /* Remember the SRM console media setting */ if (DC_IS_INTEL(sc)) { command = pci_read_config(dev, DC_PCI_CFDD, 4); command &= ~(DC_CFDD_SNOOZE_MODE | DC_CFDD_SLEEP_MODE); switch ((command >> 8) & 0xff) { case 3: sc->dc_srm_media = IFM_10_T; break; case 4: sc->dc_srm_media = IFM_10_T | IFM_FDX; break; case 5: sc->dc_srm_media = IFM_100_TX; break; case 6: sc->dc_srm_media = IFM_100_TX | IFM_FDX; break; } if (sc->dc_srm_media) sc->dc_srm_media |= IFM_ACTIVE | IFM_ETHER; } #endif /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | (IS_MPSAFE ? INTR_MPSAFE : 0), dc_intr, sc, &sc->dc_intrhand); if (error) { printf("dc%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) dc_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int dc_detach(device_t dev) { struct dc_softc *sc; struct ifnet *ifp; struct dc_mediainfo *m; int i; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->dc_mtx), ("dc mutex not initialized")); DC_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { dc_stop(sc); ether_ifdetach(ifp); } if (sc->dc_miibus) device_delete_child(dev, sc->dc_miibus); bus_generic_detach(dev); if (sc->dc_intrhand) bus_teardown_intr(dev, sc->dc_irq, sc->dc_intrhand); if (sc->dc_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->dc_irq); if (sc->dc_res) bus_release_resource(dev, DC_RES, DC_RID, sc->dc_res); if (sc->dc_cdata.dc_sbuf != NULL) bus_dmamem_free(sc->dc_stag, sc->dc_cdata.dc_sbuf, sc->dc_smap); if (sc->dc_ldata != NULL) bus_dmamem_free(sc->dc_ltag, sc->dc_ldata, sc->dc_lmap); for (i = 0; i < DC_TX_LIST_CNT; i++) bus_dmamap_destroy(sc->dc_mtag, sc->dc_cdata.dc_tx_map[i]); for (i = 0; i < DC_RX_LIST_CNT; i++) bus_dmamap_destroy(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i]); bus_dmamap_destroy(sc->dc_mtag, sc->dc_sparemap); if (sc->dc_stag) bus_dma_tag_destroy(sc->dc_stag); if (sc->dc_mtag) bus_dma_tag_destroy(sc->dc_mtag); if (sc->dc_ltag) bus_dma_tag_destroy(sc->dc_ltag); free(sc->dc_pnic_rx_buf, M_DEVBUF); while (sc->dc_mi != NULL) { m = sc->dc_mi->dc_next; free(sc->dc_mi, M_DEVBUF); sc->dc_mi = m; } free(sc->dc_srom, M_DEVBUF); DC_UNLOCK(sc); mtx_destroy(&sc->dc_mtx); return (0); } /* * Initialize the transmit descriptors. */ static int dc_list_tx_init(struct dc_softc *sc) { struct dc_chain_data *cd; struct dc_list_data *ld; int i, nexti; cd = &sc->dc_cdata; ld = sc->dc_ldata; for (i = 0; i < DC_TX_LIST_CNT; i++) { if (i == DC_TX_LIST_CNT - 1) nexti = 0; else nexti = i + 1; ld->dc_tx_list[i].dc_next = htole32(DC_TXDESC(sc, nexti)); cd->dc_tx_chain[i] = NULL; ld->dc_tx_list[i].dc_data = 0; ld->dc_tx_list[i].dc_ctl = 0; } cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int dc_list_rx_init(struct dc_softc *sc) { struct dc_chain_data *cd; struct dc_list_data *ld; int i, nexti; cd = &sc->dc_cdata; ld = sc->dc_ldata; for (i = 0; i < DC_RX_LIST_CNT; i++) { if (dc_newbuf(sc, i, 1) != 0) return (ENOBUFS); if (i == DC_RX_LIST_CNT - 1) nexti = 0; else nexti = i + 1; ld->dc_rx_list[i].dc_next = htole32(DC_RXDESC(sc, nexti)); } cd->dc_rx_prod = 0; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } static void dc_dma_map_rxbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct dc_softc *sc; struct dc_desc *c; sc = arg; c = &sc->dc_ldata->dc_rx_list[sc->dc_cdata.dc_rx_cur]; if (error) { sc->dc_cdata.dc_rx_err = error; return; } KASSERT(nseg == 1, ("wrong number of segments, should be 1")); sc->dc_cdata.dc_rx_err = 0; c->dc_data = htole32(segs->ds_addr); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int dc_newbuf(struct dc_softc *sc, int i, int alloc) { struct mbuf *m_new; bus_dmamap_t tmp; int error; if (alloc) { m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m_new == NULL) return (ENOBUFS); } else { m_new = sc->dc_cdata.dc_rx_chain[i]; m_new->m_data = m_new->m_ext.ext_buf; } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_adj(m_new, sizeof(u_int64_t)); /* * If this is a PNIC chip, zero the buffer. This is part * of the workaround for the receive bug in the 82c168 and * 82c169 chips. */ if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) bzero(mtod(m_new, char *), m_new->m_len); /* No need to remap the mbuf if we're reusing it. */ if (alloc) { sc->dc_cdata.dc_rx_cur = i; error = bus_dmamap_load_mbuf(sc->dc_mtag, sc->dc_sparemap, m_new, dc_dma_map_rxbuf, sc, 0); if (error) { m_freem(m_new); return (error); } if (sc->dc_cdata.dc_rx_err != 0) { m_freem(m_new); return (sc->dc_cdata.dc_rx_err); } bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i]); tmp = sc->dc_cdata.dc_rx_map[i]; sc->dc_cdata.dc_rx_map[i] = sc->dc_sparemap; sc->dc_sparemap = tmp; sc->dc_cdata.dc_rx_chain[i] = m_new; } sc->dc_ldata->dc_rx_list[i].dc_ctl = htole32(DC_RXCTL_RLINK | DC_RXLEN); sc->dc_ldata->dc_rx_list[i].dc_status = htole32(DC_RXSTAT_OWN); bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i], BUS_DMASYNC_PREREAD); bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Grrrrr. * The PNIC chip has a terrible bug in it that manifests itself during * periods of heavy activity. The exact mode of failure if difficult to * pinpoint: sometimes it only happens in promiscuous mode, sometimes it * will happen on slow machines. The bug is that sometimes instead of * uploading one complete frame during reception, it uploads what looks * like the entire contents of its FIFO memory. The frame we want is at * the end of the whole mess, but we never know exactly how much data has * been uploaded, so salvaging the frame is hard. * * There is only one way to do it reliably, and it's disgusting. * Here's what we know: * * - We know there will always be somewhere between one and three extra * descriptors uploaded. * * - We know the desired received frame will always be at the end of the * total data upload. * * - We know the size of the desired received frame because it will be * provided in the length field of the status word in the last descriptor. * * Here's what we do: * * - When we allocate buffers for the receive ring, we bzero() them. * This means that we know that the buffer contents should be all * zeros, except for data uploaded by the chip. * * - We also force the PNIC chip to upload frames that include the * ethernet CRC at the end. * * - We gather all of the bogus frame data into a single buffer. * * - We then position a pointer at the end of this buffer and scan * backwards until we encounter the first non-zero byte of data. * This is the end of the received frame. We know we will encounter * some data at the end of the frame because the CRC will always be * there, so even if the sender transmits a packet of all zeros, * we won't be fooled. * * - We know the size of the actual received frame, so we subtract * that value from the current pointer location. This brings us * to the start of the actual received packet. * * - We copy this into an mbuf and pass it on, along with the actual * frame length. * * The performance hit is tremendous, but it beats dropping frames all * the time. */ #define DC_WHOLEFRAME (DC_RXSTAT_FIRSTFRAG | DC_RXSTAT_LASTFRAG) static void dc_pnic_rx_bug_war(struct dc_softc *sc, int idx) { struct dc_desc *cur_rx; struct dc_desc *c = NULL; struct mbuf *m = NULL; unsigned char *ptr; int i, total_len; u_int32_t rxstat = 0; i = sc->dc_pnic_rx_bug_save; cur_rx = &sc->dc_ldata->dc_rx_list[idx]; ptr = sc->dc_pnic_rx_buf; bzero(ptr, DC_RXLEN * 5); /* Copy all the bytes from the bogus buffers. */ while (1) { c = &sc->dc_ldata->dc_rx_list[i]; rxstat = le32toh(c->dc_status); m = sc->dc_cdata.dc_rx_chain[i]; bcopy(mtod(m, char *), ptr, DC_RXLEN); ptr += DC_RXLEN; /* If this is the last buffer, break out. */ if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) break; dc_newbuf(sc, i, 0); DC_INC(i, DC_RX_LIST_CNT); } /* Find the length of the actual receive frame. */ total_len = DC_RXBYTES(rxstat); /* Scan backwards until we hit a non-zero byte. */ while (*ptr == 0x00) ptr--; /* Round off. */ if ((uintptr_t)(ptr) & 0x3) ptr -= 1; /* Now find the start of the frame. */ ptr -= total_len; if (ptr < sc->dc_pnic_rx_buf) ptr = sc->dc_pnic_rx_buf; /* * Now copy the salvaged frame to the last mbuf and fake up * the status word to make it look like a successful * frame reception. */ dc_newbuf(sc, i, 0); bcopy(ptr, mtod(m, char *), total_len); cur_rx->dc_status = htole32(rxstat | DC_RXSTAT_FIRSTFRAG); } /* * This routine searches the RX ring for dirty descriptors in the * event that the rxeof routine falls out of sync with the chip's * current descriptor pointer. This may happen sometimes as a result * of a "no RX buffer available" condition that happens when the chip * consumes all of the RX buffers before the driver has a chance to * process the RX ring. This routine may need to be called more than * once to bring the driver back in sync with the chip, however we * should still be getting RX DONE interrupts to drive the search * for new packets in the RX ring, so we should catch up eventually. */ static int dc_rx_resync(struct dc_softc *sc) { struct dc_desc *cur_rx; int i, pos; pos = sc->dc_cdata.dc_rx_prod; for (i = 0; i < DC_RX_LIST_CNT; i++) { cur_rx = &sc->dc_ldata->dc_rx_list[pos]; if (!(le32toh(cur_rx->dc_status) & DC_RXSTAT_OWN)) break; DC_INC(pos, DC_RX_LIST_CNT); } /* If the ring really is empty, then just return. */ if (i == DC_RX_LIST_CNT) return (0); /* We've fallen behing the chip: catch it. */ sc->dc_cdata.dc_rx_prod = pos; return (EAGAIN); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void dc_rxeof(struct dc_softc *sc) { struct mbuf *m; struct ifnet *ifp; struct dc_desc *cur_rx; int i, total_len = 0; u_int32_t rxstat; DC_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; i = sc->dc_cdata.dc_rx_prod; bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_POSTREAD); while (!(le32toh(sc->dc_ldata->dc_rx_list[i].dc_status) & DC_RXSTAT_OWN)) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif cur_rx = &sc->dc_ldata->dc_rx_list[i]; rxstat = le32toh(cur_rx->dc_status); m = sc->dc_cdata.dc_rx_chain[i]; bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_rx_map[i], BUS_DMASYNC_POSTREAD); total_len = DC_RXBYTES(rxstat); if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) { if ((rxstat & DC_WHOLEFRAME) != DC_WHOLEFRAME) { if (rxstat & DC_RXSTAT_FIRSTFRAG) sc->dc_pnic_rx_bug_save = i; if ((rxstat & DC_RXSTAT_LASTFRAG) == 0) { DC_INC(i, DC_RX_LIST_CNT); continue; } dc_pnic_rx_bug_war(sc, i); rxstat = le32toh(cur_rx->dc_status); total_len = DC_RXBYTES(rxstat); } } /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. However, don't report long * frames as errors since they could be vlans. */ if ((rxstat & DC_RXSTAT_RXERR)) { if (!(rxstat & DC_RXSTAT_GIANT) || (rxstat & (DC_RXSTAT_CRCERR | DC_RXSTAT_DRIBBLE | DC_RXSTAT_MIIERE | DC_RXSTAT_COLLSEEN | DC_RXSTAT_RUNT | DC_RXSTAT_DE))) { ifp->if_ierrors++; if (rxstat & DC_RXSTAT_COLLSEEN) ifp->if_collisions++; dc_newbuf(sc, i, 0); if (rxstat & DC_RXSTAT_CRCERR) { DC_INC(i, DC_RX_LIST_CNT); continue; } else { dc_init(sc); return; } } } /* No errors; receive the packet. */ total_len -= ETHER_CRC_LEN; #ifdef __i386__ /* * On the x86 we do not have alignment problems, so try to * allocate a new buffer for the receive ring, and pass up * the one where the packet is already, saving the expensive * copy done in m_devget(). * If we are on an architecture with alignment problems, or * if the allocation fails, then use m_devget and leave the * existing buffer in the receive ring. */ if (dc_quick && dc_newbuf(sc, i, 1) == 0) { m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; DC_INC(i, DC_RX_LIST_CNT); } else #endif { struct mbuf *m0; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); dc_newbuf(sc, i, 0); DC_INC(i, DC_RX_LIST_CNT); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; } ifp->if_ipackets++; DC_UNLOCK(sc); (*ifp->if_input)(ifp, m); DC_LOCK(sc); } sc->dc_cdata.dc_rx_prod = i; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void dc_txeof(struct dc_softc *sc) { struct dc_desc *cur_tx = NULL; struct ifnet *ifp; int idx; u_int32_t ctl, txstat; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_POSTREAD); idx = sc->dc_cdata.dc_tx_cons; while (idx != sc->dc_cdata.dc_tx_prod) { cur_tx = &sc->dc_ldata->dc_tx_list[idx]; txstat = le32toh(cur_tx->dc_status); ctl = le32toh(cur_tx->dc_ctl); if (txstat & DC_TXSTAT_OWN) break; if (!(ctl & DC_TXCTL_LASTFRAG) || ctl & DC_TXCTL_SETUP) { if (ctl & DC_TXCTL_SETUP) { /* * Yes, the PNIC is so brain damaged * that it will sometimes generate a TX * underrun error while DMAing the RX * filter setup frame. If we detect this, * we have to send the setup frame again, * or else the filter won't be programmed * correctly. */ if (DC_IS_PNIC(sc)) { if (txstat & DC_TXSTAT_ERRSUM) dc_setfilt(sc); } sc->dc_cdata.dc_tx_chain[idx] = NULL; } sc->dc_cdata.dc_tx_cnt--; DC_INC(idx, DC_TX_LIST_CNT); continue; } if (DC_IS_XIRCOM(sc) || DC_IS_CONEXANT(sc)) { /* * XXX: Why does my Xircom taunt me so? * For some reason it likes setting the CARRLOST flag * even when the carrier is there. wtf?!? * Who knows, but Conexant chips have the * same problem. Maybe they took lessons * from Xircom. */ if (/*sc->dc_type == DC_TYPE_21143 &&*/ sc->dc_pmode == DC_PMODE_MII && ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM | DC_TXSTAT_NOCARRIER))) txstat &= ~DC_TXSTAT_ERRSUM; } else { if (/*sc->dc_type == DC_TYPE_21143 &&*/ sc->dc_pmode == DC_PMODE_MII && ((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM | DC_TXSTAT_NOCARRIER | DC_TXSTAT_CARRLOST))) txstat &= ~DC_TXSTAT_ERRSUM; } if (txstat & DC_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & DC_TXSTAT_EXCESSCOLL) ifp->if_collisions++; if (txstat & DC_TXSTAT_LATECOLL) ifp->if_collisions++; if (!(txstat & DC_TXSTAT_UNDERRUN)) { dc_init(sc); return; } } ifp->if_collisions += (txstat & DC_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; if (sc->dc_cdata.dc_tx_chain[idx] != NULL) { bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx]); m_freem(sc->dc_cdata.dc_tx_chain[idx]); sc->dc_cdata.dc_tx_chain[idx] = NULL; } sc->dc_cdata.dc_tx_cnt--; DC_INC(idx, DC_TX_LIST_CNT); } if (idx != sc->dc_cdata.dc_tx_cons) { /* Some buffers have been freed. */ sc->dc_cdata.dc_tx_cons = idx; ifp->if_flags &= ~IFF_OACTIVE; } ifp->if_timer = (sc->dc_cdata.dc_tx_cnt == 0) ? 0 : 5; } static void dc_tick(void *xsc) { struct dc_softc *sc; struct mii_data *mii; struct ifnet *ifp; u_int32_t r; sc = xsc; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->dc_miibus); if (sc->dc_flags & DC_REDUCED_MII_POLL) { if (sc->dc_flags & DC_21143_NWAY) { r = CSR_READ_4(sc, DC_10BTSTAT); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX && (r & DC_TSTAT_LS100)) { sc->dc_link = 0; mii_mediachg(mii); } if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T && (r & DC_TSTAT_LS10)) { sc->dc_link = 0; mii_mediachg(mii); } if (sc->dc_link == 0) mii_tick(mii); } else { r = CSR_READ_4(sc, DC_ISR); if ((r & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT && sc->dc_cdata.dc_tx_cnt == 0) { mii_tick(mii); if (!(mii->mii_media_status & IFM_ACTIVE)) sc->dc_link = 0; } } } else mii_tick(mii); /* * When the init routine completes, we expect to be able to send * packets right away, and in fact the network code will send a * gratuitous ARP the moment the init routine marks the interface * as running. However, even though the MAC may have been initialized, * there may be a delay of a few seconds before the PHY completes * autonegotiation and the link is brought up. Any transmissions * made during that delay will be lost. Dealing with this is tricky: * we can't just pause in the init routine while waiting for the * PHY to come ready since that would bring the whole system to * a screeching halt for several seconds. * * What we do here is prevent the TX start routine from sending * any packets until a link has been established. After the * interface has been initialized, the tick routine will poll * the state of the PHY until the IFM_ACTIVE flag is set. Until * that time, packets will stay in the send queue, and once the * link comes up, they will be flushed out to the wire. */ if (!sc->dc_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->dc_link++; if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); } if (sc->dc_flags & DC_21143_NWAY && !sc->dc_link) callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); else callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); DC_UNLOCK(sc); } /* * A transmit underrun has occurred. Back off the transmit threshold, * or switch to store and forward mode if we have to. */ static void dc_tx_underrun(struct dc_softc *sc) { u_int32_t isr; int i; if (DC_IS_DAVICOM(sc)) dc_init(sc); if (DC_IS_INTEL(sc)) { /* * The real 21143 requires that the transmitter be idle * in order to change the transmit threshold or store * and forward state. */ DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); for (i = 0; i < DC_TIMEOUT; i++) { isr = CSR_READ_4(sc, DC_ISR); if (isr & DC_ISR_TX_IDLE) break; DELAY(10); } if (i == DC_TIMEOUT) { printf("dc%d: failed to force tx to idle state\n", sc->dc_unit); dc_init(sc); } } printf("dc%d: TX underrun -- ", sc->dc_unit); sc->dc_txthresh += DC_TXTHRESH_INC; if (sc->dc_txthresh > DC_TXTHRESH_MAX) { printf("using store and forward mode\n"); DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); } else { printf("increasing TX threshold\n"); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); } if (DC_IS_INTEL(sc)) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); } #ifdef DEVICE_POLLING static poll_handler_t dc_poll; static void dc_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct dc_softc *sc = ifp->if_softc; if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ /* Re-enable interrupts. */ CSR_WRITE_4(sc, DC_IMR, DC_INTRS); return; } DC_LOCK(sc); sc->rxcycles = count; dc_rxeof(sc); dc_txeof(sc); if (ifp->if_snd.ifq_head != NULL && !(ifp->if_flags & IFF_OACTIVE)) dc_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int32_t status; status = CSR_READ_4(sc, DC_ISR); status &= (DC_ISR_RX_WATDOGTIMEO | DC_ISR_RX_NOBUF | DC_ISR_TX_NOBUF | DC_ISR_TX_IDLE | DC_ISR_TX_UNDERRUN | DC_ISR_BUS_ERR); if (!status) { DC_UNLOCK(sc); return; } /* ack what we have */ CSR_WRITE_4(sc, DC_ISR, status); if (status & (DC_ISR_RX_WATDOGTIMEO | DC_ISR_RX_NOBUF)) { u_int32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED); ifp->if_ierrors += (r & 0xffff) + ((r >> 17) & 0x7ff); if (dc_rx_resync(sc)) dc_rxeof(sc); } /* restart transmit unit if necessary */ if (status & DC_ISR_TX_IDLE && sc->dc_cdata.dc_tx_cnt) CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); if (status & DC_ISR_TX_UNDERRUN) dc_tx_underrun(sc); if (status & DC_ISR_BUS_ERR) { printf("dc_poll: dc%d bus error\n", sc->dc_unit); dc_reset(sc); dc_init(sc); } } DC_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void dc_intr(void *arg) { struct dc_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; if (sc->suspended) return; if ((CSR_READ_4(sc, DC_ISR) & DC_INTRS) == 0) return; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(dc_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_4(sc, DC_IMR, 0x00000000); goto done; } #endif /* Suppress unwanted interrupts */ if (!(ifp->if_flags & IFF_UP)) { if (CSR_READ_4(sc, DC_ISR) & DC_INTRS) dc_stop(sc); DC_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_4(sc, DC_IMR, 0x00000000); while (((status = CSR_READ_4(sc, DC_ISR)) & DC_INTRS) && status != 0xFFFFFFFF) { CSR_WRITE_4(sc, DC_ISR, status); if (status & DC_ISR_RX_OK) { int curpkts; curpkts = ifp->if_ipackets; dc_rxeof(sc); if (curpkts == ifp->if_ipackets) { while (dc_rx_resync(sc)) dc_rxeof(sc); } } if (status & (DC_ISR_TX_OK | DC_ISR_TX_NOBUF)) dc_txeof(sc); if (status & DC_ISR_TX_IDLE) { dc_txeof(sc); if (sc->dc_cdata.dc_tx_cnt) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); } } if (status & DC_ISR_TX_UNDERRUN) dc_tx_underrun(sc); if ((status & DC_ISR_RX_WATDOGTIMEO) || (status & DC_ISR_RX_NOBUF)) { int curpkts; curpkts = ifp->if_ipackets; dc_rxeof(sc); if (curpkts == ifp->if_ipackets) { while (dc_rx_resync(sc)) dc_rxeof(sc); } } if (status & DC_ISR_BUS_ERR) { dc_reset(sc); dc_init(sc); } } /* Re-enable interrupts. */ CSR_WRITE_4(sc, DC_IMR, DC_INTRS); if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); #ifdef DEVICE_POLLING done: #endif DC_UNLOCK(sc); } static void dc_dma_map_txbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct dc_softc *sc; struct dc_desc *f; int cur, first, frag, i; sc = arg; if (error) { sc->dc_cdata.dc_tx_err = error; return; } first = cur = frag = sc->dc_cdata.dc_tx_prod; for (i = 0; i < nseg; i++) { if ((sc->dc_flags & DC_TX_ADMTEK_WAR) && (frag == (DC_TX_LIST_CNT - 1)) && (first != sc->dc_cdata.dc_tx_first)) { bus_dmamap_unload(sc->dc_mtag, sc->dc_cdata.dc_tx_map[first]); sc->dc_cdata.dc_tx_err = ENOBUFS; return; } f = &sc->dc_ldata->dc_tx_list[frag]; f->dc_ctl = htole32(DC_TXCTL_TLINK | segs[i].ds_len); if (i == 0) { f->dc_status = 0; f->dc_ctl |= htole32(DC_TXCTL_FIRSTFRAG); } else f->dc_status = htole32(DC_TXSTAT_OWN); f->dc_data = htole32(segs[i].ds_addr); cur = frag; DC_INC(frag, DC_TX_LIST_CNT); } sc->dc_cdata.dc_tx_err = 0; sc->dc_cdata.dc_tx_prod = frag; sc->dc_cdata.dc_tx_cnt += nseg; sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_LASTFRAG); sc->dc_cdata.dc_tx_chain[cur] = sc->dc_cdata.dc_tx_mapping; if (sc->dc_flags & DC_TX_INTR_FIRSTFRAG) sc->dc_ldata->dc_tx_list[first].dc_ctl |= htole32(DC_TXCTL_FINT); if (sc->dc_flags & DC_TX_INTR_ALWAYS) sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_FINT); if (sc->dc_flags & DC_TX_USE_TX_INTR && sc->dc_cdata.dc_tx_cnt > 64) sc->dc_ldata->dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_FINT); sc->dc_ldata->dc_tx_list[first].dc_status = htole32(DC_TXSTAT_OWN); } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int dc_encap(struct dc_softc *sc, struct mbuf **m_head) { struct mbuf *m; int error, idx, chainlen = 0; /* * If there's no way we can send any packets, return now. */ if (DC_TX_LIST_CNT - sc->dc_cdata.dc_tx_cnt < 6) return (ENOBUFS); /* * Count the number of frags in this chain to see if * we need to m_defrag. Since the descriptor list is shared * by all packets, we'll m_defrag long chains so that they * do not use up the entire list, even if they would fit. */ for (m = *m_head; m != NULL; m = m->m_next) chainlen++; if ((chainlen > DC_TX_LIST_CNT / 4) || ((DC_TX_LIST_CNT - (chainlen + sc->dc_cdata.dc_tx_cnt)) < 6)) { m = m_defrag(*m_head, M_DONTWAIT); if (m == NULL) return (ENOBUFS); *m_head = m; } /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ idx = sc->dc_cdata.dc_tx_prod; sc->dc_cdata.dc_tx_mapping = *m_head; error = bus_dmamap_load_mbuf(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], *m_head, dc_dma_map_txbuf, sc, 0); if (error) return (error); if (sc->dc_cdata.dc_tx_err != 0) return (sc->dc_cdata.dc_tx_err); bus_dmamap_sync(sc->dc_mtag, sc->dc_cdata.dc_tx_map[idx], BUS_DMASYNC_PREWRITE); bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap, BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); return (0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void dc_start(struct ifnet *ifp) { struct dc_softc *sc; struct mbuf *m_head = NULL, *m; int idx; sc = ifp->if_softc; DC_LOCK(sc); if (!sc->dc_link && ifp->if_snd.ifq_len < 10) { DC_UNLOCK(sc); return; } if (ifp->if_flags & IFF_OACTIVE) { DC_UNLOCK(sc); return; } idx = sc->dc_cdata.dc_tx_first = sc->dc_cdata.dc_tx_prod; while (sc->dc_cdata.dc_tx_chain[idx] == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (sc->dc_flags & DC_TX_COALESCE && (m_head->m_next != NULL || sc->dc_flags & DC_TX_ALIGN)) { m = m_defrag(m_head, M_DONTWAIT); if (m == NULL) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } else { m_head = m; } } if (dc_encap(sc, &m_head)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } idx = sc->dc_cdata.dc_tx_prod; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); if (sc->dc_flags & DC_TX_ONE) { ifp->if_flags |= IFF_OACTIVE; break; } } /* Transmit */ if (!(sc->dc_flags & DC_TX_POLL)) CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; DC_UNLOCK(sc); } static void dc_init(void *xsc) { struct dc_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; DC_LOCK(sc); mii = device_get_softc(sc->dc_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ dc_stop(sc); dc_reset(sc); /* * Set cache alignment and burst length. */ if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc)) CSR_WRITE_4(sc, DC_BUSCTL, 0); else CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME | DC_BUSCTL_MRLE); /* * Evenly share the bus between receive and transmit process. */ if (DC_IS_INTEL(sc)) DC_SETBIT(sc, DC_BUSCTL, DC_BUSCTL_ARBITRATION); if (DC_IS_DAVICOM(sc) || DC_IS_INTEL(sc)) { DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_USECA); } else { DC_SETBIT(sc, DC_BUSCTL, DC_BURSTLEN_16LONG); } if (sc->dc_flags & DC_TX_POLL) DC_SETBIT(sc, DC_BUSCTL, DC_TXPOLL_1); switch(sc->dc_cachesize) { case 32: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_32LONG); break; case 16: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_16LONG); break; case 8: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_8LONG); break; case 0: default: DC_SETBIT(sc, DC_BUSCTL, DC_CACHEALIGN_NONE); break; } if (sc->dc_flags & DC_TX_STORENFWD) DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); else { if (sc->dc_txthresh > DC_TXTHRESH_MAX) { DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); } else { DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_STORENFWD); DC_SETBIT(sc, DC_NETCFG, sc->dc_txthresh); } } DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_NO_RXCRC); DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_BACKOFF); if (DC_IS_MACRONIX(sc) || DC_IS_PNICII(sc)) { /* * The app notes for the 98713 and 98715A say that * in order to have the chips operate properly, a magic * number must be written to CSR16. Macronix does not * document the meaning of these bits so there's no way * to know exactly what they do. The 98713 has a magic * number all its own; the rest all use a different one. */ DC_CLRBIT(sc, DC_MX_MAGICPACKET, 0xFFFF0000); if (sc->dc_type == DC_TYPE_98713) DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98713); else DC_SETBIT(sc, DC_MX_MAGICPACKET, DC_MX_MAGIC_98715); } if (DC_IS_XIRCOM(sc)) { /* * setup General Purpose Port mode and data so the tulip * can talk to the MII. */ CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_WRITE_EN | DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); CSR_WRITE_4(sc, DC_SIAGP, DC_SIAGP_INT1_EN | DC_SIAGP_MD_GP2_OUTPUT | DC_SIAGP_MD_GP0_OUTPUT); DELAY(10); } DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_TX_THRESH); DC_SETBIT(sc, DC_NETCFG, DC_TXTHRESH_MIN); /* Init circular RX list. */ if (dc_list_rx_init(sc) == ENOBUFS) { printf("dc%d: initialization failed: no " "memory for rx buffers\n", sc->dc_unit); dc_stop(sc); DC_UNLOCK(sc); return; } /* * Init TX descriptors. */ dc_list_tx_init(sc); /* * Load the address of the RX list. */ CSR_WRITE_4(sc, DC_RXADDR, DC_RXDESC(sc, 0)); CSR_WRITE_4(sc, DC_TXADDR, DC_TXDESC(sc, 0)); /* * Enable interrupts. */ #ifdef DEVICE_POLLING /* * ... but only if we are not polling, and make sure they are off in * the case of polling. Some cards (e.g. fxp) turn interrupts on * after a reset. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_4(sc, DC_IMR, 0x00000000); else #endif CSR_WRITE_4(sc, DC_IMR, DC_INTRS); CSR_WRITE_4(sc, DC_ISR, 0xFFFFFFFF); /* Enable transmitter. */ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON); /* * If this is an Intel 21143 and we're not using the * MII port, program the LED control pins so we get * link and activity indications. */ if (sc->dc_flags & DC_TULIP_LEDS) { CSR_WRITE_4(sc, DC_WATCHDOG, DC_WDOG_CTLWREN | DC_WDOG_LINK | DC_WDOG_ACTIVITY); CSR_WRITE_4(sc, DC_WATCHDOG, 0); } /* * Load the RX/multicast filter. We do this sort of late * because the filter programming scheme on the 21143 and * some clones requires DMAing a setup frame via the TX * engine, and we need the transmitter enabled for that. */ dc_setfilt(sc); /* Enable receiver. */ DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON); CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF); mii_mediachg(mii); dc_setcfg(sc, sc->dc_if_media); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; /* Don't start the ticker if this is a homePNA link. */ if (IFM_SUBTYPE(mii->mii_media.ifm_media) == IFM_HPNA_1) sc->dc_link = 1; else { if (sc->dc_flags & DC_21143_NWAY) callout_reset(&sc->dc_stat_ch, hz/10, dc_tick, sc); else callout_reset(&sc->dc_stat_ch, hz, dc_tick, sc); } #ifdef SRM_MEDIA if(sc->dc_srm_media) { struct ifreq ifr; ifr.ifr_media = sc->dc_srm_media; ifmedia_ioctl(ifp, &ifr, &mii->mii_media, SIOCSIFMEDIA); sc->dc_srm_media = 0; } #endif DC_UNLOCK(sc); } /* * Set media options. */ static int dc_ifmedia_upd(struct ifnet *ifp) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = ifp->if_softc; mii = device_get_softc(sc->dc_miibus); mii_mediachg(mii); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc) && IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) dc_setcfg(sc, ifm->ifm_media); else sc->dc_link = 0; return (0); } /* * Report current media status. */ static void dc_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) { struct dc_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = ifp->if_softc; mii = device_get_softc(sc->dc_miibus); mii_pollstat(mii); ifm = &mii->mii_media; if (DC_IS_DAVICOM(sc)) { if (IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) { ifmr->ifm_active = ifm->ifm_media; ifmr->ifm_status = 0; return; } } ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } static int dc_ioctl(struct ifnet *ifp, u_long command, caddr_t data) { struct dc_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct mii_data *mii; int error = 0; DC_LOCK(sc); switch (command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { int need_setfilt = (ifp->if_flags ^ sc->dc_if_flags) & (IFF_PROMISC | IFF_ALLMULTI); if (ifp->if_flags & IFF_RUNNING) { if (need_setfilt) dc_setfilt(sc); } else { sc->dc_txthresh = 0; dc_init(sc); } } else { if (ifp->if_flags & IFF_RUNNING) dc_stop(sc); } sc->dc_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: dc_setfilt(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->dc_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); #ifdef SRM_MEDIA if (sc->dc_srm_media) sc->dc_srm_media = 0; #endif break; case SIOCSIFCAP: ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING; break; default: error = ether_ioctl(ifp, command, data); break; } DC_UNLOCK(sc); return (error); } static void dc_watchdog(struct ifnet *ifp) { struct dc_softc *sc; sc = ifp->if_softc; DC_LOCK(sc); ifp->if_oerrors++; printf("dc%d: watchdog timeout\n", sc->dc_unit); dc_stop(sc); dc_reset(sc); dc_init(sc); if (ifp->if_snd.ifq_head != NULL) dc_start(ifp); DC_UNLOCK(sc); } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void dc_stop(struct dc_softc *sc) { struct ifnet *ifp; struct dc_list_data *ld; struct dc_chain_data *cd; int i; u_int32_t ctl; DC_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; ld = sc->dc_ldata; cd = &sc->dc_cdata; callout_stop(&sc->dc_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif DC_CLRBIT(sc, DC_NETCFG, (DC_NETCFG_RX_ON | DC_NETCFG_TX_ON)); CSR_WRITE_4(sc, DC_IMR, 0x00000000); CSR_WRITE_4(sc, DC_TXADDR, 0x00000000); CSR_WRITE_4(sc, DC_RXADDR, 0x00000000); sc->dc_link = 0; /* * Free data in the RX lists. */ for (i = 0; i < DC_RX_LIST_CNT; i++) { if (cd->dc_rx_chain[i] != NULL) { m_freem(cd->dc_rx_chain[i]); cd->dc_rx_chain[i] = NULL; } } bzero(&ld->dc_rx_list, sizeof(ld->dc_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < DC_TX_LIST_CNT; i++) { if (cd->dc_tx_chain[i] != NULL) { ctl = le32toh(ld->dc_tx_list[i].dc_ctl); if ((ctl & DC_TXCTL_SETUP) || !(ctl & DC_TXCTL_LASTFRAG)) { cd->dc_tx_chain[i] = NULL; continue; } bus_dmamap_unload(sc->dc_mtag, cd->dc_tx_map[i]); m_freem(cd->dc_tx_chain[i]); cd->dc_tx_chain[i] = NULL; } } bzero(&ld->dc_tx_list, sizeof(ld->dc_tx_list)); DC_UNLOCK(sc); } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int dc_suspend(device_t dev) { struct dc_softc *sc; - int i, s; + int s; s = splimp(); sc = device_get_softc(dev); - dc_stop(sc); - - for (i = 0; i < 5; i++) - sc->saved_maps[i] = pci_read_config(dev, PCIR_BAR(i), 4); - sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); - sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); - sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); - sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); - sc->suspended = 1; splx(s); return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int dc_resume(device_t dev) { struct dc_softc *sc; struct ifnet *ifp; - int i, s; + int s; s = splimp(); sc = device_get_softc(dev); ifp = &sc->arpcom.ac_if; -#ifndef BURN_BRIDGES - dc_acpi(dev); -#endif - /* better way to do this? */ - for (i = 0; i < 5; i++) - pci_write_config(dev, PCIR_BAR(i), sc->saved_maps[i], 4); - pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); - pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); - pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); - pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); - - /* reenable busmastering */ - pci_enable_busmaster(dev); - pci_enable_io(dev, DC_RES); /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) dc_init(sc); sc->suspended = 0; splx(s); return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void dc_shutdown(device_t dev) { struct dc_softc *sc; sc = device_get_softc(dev); dc_stop(sc); } Index: head/sys/pci/if_pcn.c =================================================================== --- head/sys/pci/if_pcn.c (revision 131252) +++ head/sys/pci/if_pcn.c (revision 131253) @@ -1,1417 +1,1393 @@ /* * Copyright (c) 2000 Berkeley Software Design, Inc. * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * AMD Am79c972 fast ethernet PCI NIC driver. Datatheets are available * from http://www.amd.com. * * The AMD PCnet/PCI controllers are more advanced and functional * versions of the venerable 7990 LANCE. The PCnet/PCI chips retain * backwards compatibility with the LANCE and thus can be made * to work with older LANCE drivers. This is in fact how the * PCnet/PCI chips were supported in FreeBSD originally. The trouble * is that the PCnet/PCI devices offer several performance enhancements * which can't be exploited in LANCE compatibility mode. Chief among * these enhancements is the ability to perform PCI DMA operations * using 32-bit addressing (which eliminates the need for ISA * bounce-buffering), and special receive buffer alignment (which * allows the receive handler to pass packets to the upper protocol * layers without copying on both the x86 and alpha platforms). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include #define PCN_USEIOSPACE #include MODULE_DEPEND(pcn, pci, 1, 1, 1); MODULE_DEPEND(pcn, ether, 1, 1, 1); MODULE_DEPEND(pcn, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Various supported device vendors/types and their names. */ static struct pcn_type pcn_devs[] = { { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" }, { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" }, { 0, 0, NULL } }; static u_int32_t pcn_csr_read (struct pcn_softc *, int); static u_int16_t pcn_csr_read16 (struct pcn_softc *, int); static u_int16_t pcn_bcr_read16 (struct pcn_softc *, int); static void pcn_csr_write (struct pcn_softc *, int, int); static u_int32_t pcn_bcr_read (struct pcn_softc *, int); static void pcn_bcr_write (struct pcn_softc *, int, int); static int pcn_probe (device_t); static int pcn_attach (device_t); static int pcn_detach (device_t); static int pcn_newbuf (struct pcn_softc *, int, struct mbuf *); static int pcn_encap (struct pcn_softc *, struct mbuf *, u_int32_t *); static void pcn_rxeof (struct pcn_softc *); static void pcn_txeof (struct pcn_softc *); static void pcn_intr (void *); static void pcn_tick (void *); static void pcn_start (struct ifnet *); static int pcn_ioctl (struct ifnet *, u_long, caddr_t); static void pcn_init (void *); static void pcn_stop (struct pcn_softc *); static void pcn_watchdog (struct ifnet *); static void pcn_shutdown (device_t); static int pcn_ifmedia_upd (struct ifnet *); static void pcn_ifmedia_sts (struct ifnet *, struct ifmediareq *); static int pcn_miibus_readreg (device_t, int, int); static int pcn_miibus_writereg (device_t, int, int, int); static void pcn_miibus_statchg (device_t); static void pcn_setfilt (struct ifnet *); static void pcn_setmulti (struct pcn_softc *); static void pcn_reset (struct pcn_softc *); static int pcn_list_rx_init (struct pcn_softc *); static int pcn_list_tx_init (struct pcn_softc *); #ifdef PCN_USEIOSPACE #define PCN_RES SYS_RES_IOPORT #define PCN_RID PCN_PCI_LOIO #else #define PCN_RES SYS_RES_MEMORY #define PCN_RID PCN_PCI_LOMEM #endif static device_method_t pcn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pcn_probe), DEVMETHOD(device_attach, pcn_attach), DEVMETHOD(device_detach, pcn_detach), DEVMETHOD(device_shutdown, pcn_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, pcn_miibus_readreg), DEVMETHOD(miibus_writereg, pcn_miibus_writereg), DEVMETHOD(miibus_statchg, pcn_miibus_statchg), { 0, 0 } }; static driver_t pcn_driver = { "pcn", pcn_methods, sizeof(struct pcn_softc) }; static devclass_t pcn_devclass; DRIVER_MODULE(pcn, pci, pcn_driver, pcn_devclass, 0, 0); DRIVER_MODULE(miibus, pcn, miibus_driver, miibus_devclass, 0, 0); #define PCN_CSR_SETBIT(sc, reg, x) \ pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) | (x)) #define PCN_CSR_CLRBIT(sc, reg, x) \ pcn_csr_write(sc, reg, pcn_csr_read(sc, reg) & ~(x)) #define PCN_BCR_SETBIT(sc, reg, x) \ pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) | (x)) #define PCN_BCR_CLRBIT(sc, reg, x) \ pcn_bcr_write(sc, reg, pcn_bcr_read(sc, reg) & ~(x)) static u_int32_t pcn_csr_read(sc, reg) struct pcn_softc *sc; int reg; { CSR_WRITE_4(sc, PCN_IO32_RAP, reg); return(CSR_READ_4(sc, PCN_IO32_RDP)); } static u_int16_t pcn_csr_read16(sc, reg) struct pcn_softc *sc; int reg; { CSR_WRITE_2(sc, PCN_IO16_RAP, reg); return(CSR_READ_2(sc, PCN_IO16_RDP)); } static void pcn_csr_write(sc, reg, val) struct pcn_softc *sc; int reg; int val; { CSR_WRITE_4(sc, PCN_IO32_RAP, reg); CSR_WRITE_4(sc, PCN_IO32_RDP, val); return; } static u_int32_t pcn_bcr_read(sc, reg) struct pcn_softc *sc; int reg; { CSR_WRITE_4(sc, PCN_IO32_RAP, reg); return(CSR_READ_4(sc, PCN_IO32_BDP)); } static u_int16_t pcn_bcr_read16(sc, reg) struct pcn_softc *sc; int reg; { CSR_WRITE_2(sc, PCN_IO16_RAP, reg); return(CSR_READ_2(sc, PCN_IO16_BDP)); } static void pcn_bcr_write(sc, reg, val) struct pcn_softc *sc; int reg; int val; { CSR_WRITE_4(sc, PCN_IO32_RAP, reg); CSR_WRITE_4(sc, PCN_IO32_BDP, val); return; } static int pcn_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct pcn_softc *sc; int val; sc = device_get_softc(dev); if (sc->pcn_phyaddr && phy > sc->pcn_phyaddr) return(0); pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); val = pcn_bcr_read(sc, PCN_BCR_MIIDATA) & 0xFFFF; if (val == 0xFFFF) return(0); sc->pcn_phyaddr = phy; return(val); } static int pcn_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct pcn_softc *sc; sc = device_get_softc(dev); pcn_bcr_write(sc, PCN_BCR_MIIADDR, reg | (phy << 5)); pcn_bcr_write(sc, PCN_BCR_MIIDATA, data); return(0); } static void pcn_miibus_statchg(dev) device_t dev; { struct pcn_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->pcn_miibus); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { PCN_BCR_SETBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); } else { PCN_BCR_CLRBIT(sc, PCN_BCR_DUPLEX, PCN_DUPLEX_FDEN); } return; } static void pcn_setmulti(sc) struct pcn_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int32_t h, i; u_int16_t hashes[4] = { 0, 0, 0, 0 }; ifp = &sc->arpcom.ac_if; PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { for (i = 0; i < 4; i++) pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0xFFFF); PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); return; } /* first, zot all the existing hash bits */ for (i = 0; i < 4; i++) pcn_csr_write(sc, PCN_CSR_MAR0 + i, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_le(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; hashes[h >> 4] |= 1 << (h & 0xF); } for (i = 0; i < 4; i++) pcn_csr_write(sc, PCN_CSR_MAR0 + i, hashes[i]); PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); return; } static void pcn_reset(sc) struct pcn_softc *sc; { /* * Issue a reset by reading from the RESET register. * Note that we don't know if the chip is operating in * 16-bit or 32-bit mode at this point, so we attempt * to reset the chip both ways. If one fails, the other * will succeed. */ CSR_READ_2(sc, PCN_IO16_RESET); CSR_READ_4(sc, PCN_IO32_RESET); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); /* Select 32-bit (DWIO) mode */ CSR_WRITE_4(sc, PCN_IO32_RDP, 0); /* Select software style 3. */ pcn_bcr_write(sc, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI_BURST); return; } /* * Probe for an AMD chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int pcn_probe(dev) device_t dev; { struct pcn_type *t; struct pcn_softc *sc; int rid; u_int32_t chip_id; t = pcn_devs; sc = device_get_softc(dev); while(t->pcn_name != NULL) { if ((pci_get_vendor(dev) == t->pcn_vid) && (pci_get_device(dev) == t->pcn_did)) { /* * Temporarily map the I/O space * so we can read the chip ID register. */ rid = PCN_RID; sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); if (sc->pcn_res == NULL) { device_printf(dev, "couldn't map ports/memory\n"); return(ENXIO); } sc->pcn_btag = rman_get_bustag(sc->pcn_res); sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); PCN_LOCK(sc); /* * Note: we can *NOT* put the chip into * 32-bit mode yet. The lnc driver will only * work in 16-bit mode, and once the chip * goes into 32-bit mode, the only way to * get it out again is with a hardware reset. * So if pcn_probe() is called before the * lnc driver's probe routine, the chip will * be locked into 32-bit operation and the lnc * driver will be unable to attach to it. * Note II: if the chip happens to already * be in 32-bit mode, we still need to check * the chip ID, but first we have to detect * 32-bit mode using only 16-bit operations. * The safest way to do this is to read the * PCI subsystem ID from BCR23/24 and compare * that with the value read from PCI config * space. */ chip_id = pcn_bcr_read16(sc, PCN_BCR_PCISUBSYSID); chip_id <<= 16; chip_id |= pcn_bcr_read16(sc, PCN_BCR_PCISUBVENID); /* * Note III: the test for 0x10001000 is a hack to * pacify VMware, who's pseudo-PCnet interface is * broken. Reading the subsystem register from PCI * config space yeilds 0x00000000 while reading the * same value from I/O space yeilds 0x10001000. It's * not supposed to be that way. */ if (chip_id == pci_read_config(dev, PCIR_SUBVEND_0, 4) || chip_id == 0x10001000) { /* We're in 16-bit mode. */ chip_id = pcn_csr_read16(sc, PCN_CSR_CHIPID1); chip_id <<= 16; chip_id |= pcn_csr_read16(sc, PCN_CSR_CHIPID0); } else { /* We're in 32-bit mode. */ chip_id = pcn_csr_read(sc, PCN_CSR_CHIPID1); chip_id <<= 16; chip_id |= pcn_csr_read(sc, PCN_CSR_CHIPID0); } bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); PCN_UNLOCK(sc); mtx_destroy(&sc->pcn_mtx); chip_id >>= 12; sc->pcn_type = chip_id & PART_MASK; switch(sc->pcn_type) { case Am79C971: case Am79C972: case Am79C973: case Am79C975: case Am79C976: case Am79C978: break; default: return(ENXIO); } device_set_desc(dev, t->pcn_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int pcn_attach(dev) device_t dev; { u_int32_t eaddr[2]; struct pcn_softc *sc; struct ifnet *ifp; int unit, error = 0, rid; sc = device_get_softc(dev); unit = device_get_unit(dev); /* Initialize our mutex. */ mtx_init(&sc->pcn_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, PCN_PCI_LOIO, 4); - membase = pci_read_config(dev, PCN_PCI_LOMEM, 4); - irq = pci_read_config(dev, PCN_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("pcn%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, PCN_PCI_LOIO, iobase, 4); - pci_write_config(dev, PCN_PCI_LOMEM, membase, 4); - pci_write_config(dev, PCN_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = PCN_RID; sc->pcn_res = bus_alloc_resource_any(dev, PCN_RES, &rid, RF_ACTIVE); if (sc->pcn_res == NULL) { printf("pcn%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->pcn_btag = rman_get_bustag(sc->pcn_res); sc->pcn_bhandle = rman_get_bushandle(sc->pcn_res); /* Allocate interrupt */ rid = 0; sc->pcn_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->pcn_irq == NULL) { printf("pcn%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ pcn_reset(sc); /* * Get station address from the EEPROM. */ eaddr[0] = CSR_READ_4(sc, PCN_IO32_APROM00); eaddr[1] = CSR_READ_4(sc, PCN_IO32_APROM01); bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->pcn_unit = unit; callout_handle_init(&sc->pcn_stat_ch); sc->pcn_ldata = contigmalloc(sizeof(struct pcn_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->pcn_ldata == NULL) { printf("pcn%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->pcn_ldata, sizeof(struct pcn_list_data)); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = pcn_ioctl; ifp->if_start = pcn_start; ifp->if_watchdog = pcn_watchdog; ifp->if_init = pcn_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = PCN_TX_LIST_CNT - 1; /* * Do MII setup. */ if (mii_phy_probe(dev, &sc->pcn_miibus, pcn_ifmedia_upd, pcn_ifmedia_sts)) { printf("pcn%d: MII without any PHY!\n", sc->pcn_unit); error = ENXIO; goto fail; } /* * Call MI attach routine. */ ether_ifattach(ifp, (u_int8_t *) eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->pcn_irq, INTR_TYPE_NET, pcn_intr, sc, &sc->pcn_intrhand); if (error) { printf("pcn%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) pcn_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int pcn_detach(dev) device_t dev; { struct pcn_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = &sc->arpcom.ac_if; KASSERT(mtx_initialized(&sc->pcn_mtx), ("pcn mutex not initialized")); PCN_LOCK(sc); /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { pcn_reset(sc); pcn_stop(sc); ether_ifdetach(ifp); } if (sc->pcn_miibus) device_delete_child(dev, sc->pcn_miibus); bus_generic_detach(dev); if (sc->pcn_intrhand) bus_teardown_intr(dev, sc->pcn_irq, sc->pcn_intrhand); if (sc->pcn_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->pcn_irq); if (sc->pcn_res) bus_release_resource(dev, PCN_RES, PCN_RID, sc->pcn_res); if (sc->pcn_ldata) { contigfree(sc->pcn_ldata, sizeof(struct pcn_list_data), M_DEVBUF); } PCN_UNLOCK(sc); mtx_destroy(&sc->pcn_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int pcn_list_tx_init(sc) struct pcn_softc *sc; { struct pcn_list_data *ld; struct pcn_ring_data *cd; int i; cd = &sc->pcn_cdata; ld = sc->pcn_ldata; for (i = 0; i < PCN_TX_LIST_CNT; i++) { cd->pcn_tx_chain[i] = NULL; ld->pcn_tx_list[i].pcn_tbaddr = 0; ld->pcn_tx_list[i].pcn_txctl = 0; ld->pcn_tx_list[i].pcn_txstat = 0; } cd->pcn_tx_prod = cd->pcn_tx_cons = cd->pcn_tx_cnt = 0; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. */ static int pcn_list_rx_init(sc) struct pcn_softc *sc; { struct pcn_ring_data *cd; int i; cd = &sc->pcn_cdata; for (i = 0; i < PCN_RX_LIST_CNT; i++) { if (pcn_newbuf(sc, i, NULL) == ENOBUFS) return(ENOBUFS); } cd->pcn_rx_prod = 0; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int pcn_newbuf(sc, idx, m) struct pcn_softc *sc; int idx; struct mbuf *m; { struct mbuf *m_new = NULL; struct pcn_rx_desc *c; c = &sc->pcn_ldata->pcn_rx_list[idx]; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, ETHER_ALIGN); sc->pcn_cdata.pcn_rx_chain[idx] = m_new; c->pcn_rbaddr = vtophys(mtod(m_new, caddr_t)); c->pcn_bufsz = (~(PCN_RXLEN) + 1) & PCN_RXLEN_BUFSZ; c->pcn_bufsz |= PCN_RXLEN_MBO; c->pcn_rxstat = PCN_RXSTAT_STP|PCN_RXSTAT_ENP|PCN_RXSTAT_OWN; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void pcn_rxeof(sc) struct pcn_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct pcn_rx_desc *cur_rx; int i; PCN_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; i = sc->pcn_cdata.pcn_rx_prod; while(PCN_OWN_RXDESC(&sc->pcn_ldata->pcn_rx_list[i])) { cur_rx = &sc->pcn_ldata->pcn_rx_list[i]; m = sc->pcn_cdata.pcn_rx_chain[i]; sc->pcn_cdata.pcn_rx_chain[i] = NULL; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (cur_rx->pcn_rxstat & PCN_RXSTAT_ERR) { ifp->if_ierrors++; pcn_newbuf(sc, i, m); PCN_INC(i, PCN_RX_LIST_CNT); continue; } if (pcn_newbuf(sc, i, NULL)) { /* Ran out of mbufs; recycle this one. */ pcn_newbuf(sc, i, m); ifp->if_ierrors++; PCN_INC(i, PCN_RX_LIST_CNT); continue; } PCN_INC(i, PCN_RX_LIST_CNT); /* No errors; receive the packet. */ ifp->if_ipackets++; m->m_len = m->m_pkthdr.len = cur_rx->pcn_rxlen - ETHER_CRC_LEN; m->m_pkthdr.rcvif = ifp; PCN_UNLOCK(sc); (*ifp->if_input)(ifp, m); PCN_LOCK(sc); } sc->pcn_cdata.pcn_rx_prod = i; return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void pcn_txeof(sc) struct pcn_softc *sc; { struct pcn_tx_desc *cur_tx = NULL; struct ifnet *ifp; u_int32_t idx; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ idx = sc->pcn_cdata.pcn_tx_cons; while (idx != sc->pcn_cdata.pcn_tx_prod) { cur_tx = &sc->pcn_ldata->pcn_tx_list[idx]; if (!PCN_OWN_TXDESC(cur_tx)) break; if (!(cur_tx->pcn_txctl & PCN_TXCTL_ENP)) { sc->pcn_cdata.pcn_tx_cnt--; PCN_INC(idx, PCN_TX_LIST_CNT); continue; } if (cur_tx->pcn_txctl & PCN_TXCTL_ERR) { ifp->if_oerrors++; if (cur_tx->pcn_txstat & PCN_TXSTAT_EXDEF) ifp->if_collisions++; if (cur_tx->pcn_txstat & PCN_TXSTAT_RTRY) ifp->if_collisions++; } ifp->if_collisions += cur_tx->pcn_txstat & PCN_TXSTAT_TRC; ifp->if_opackets++; if (sc->pcn_cdata.pcn_tx_chain[idx] != NULL) { m_freem(sc->pcn_cdata.pcn_tx_chain[idx]); sc->pcn_cdata.pcn_tx_chain[idx] = NULL; } sc->pcn_cdata.pcn_tx_cnt--; PCN_INC(idx, PCN_TX_LIST_CNT); } if (idx != sc->pcn_cdata.pcn_tx_cons) { /* Some buffers have been freed. */ sc->pcn_cdata.pcn_tx_cons = idx; ifp->if_flags &= ~IFF_OACTIVE; } ifp->if_timer = (sc->pcn_cdata.pcn_tx_cnt == 0) ? 0 : 5; return; } static void pcn_tick(xsc) void *xsc; { struct pcn_softc *sc; struct mii_data *mii; struct ifnet *ifp; sc = xsc; ifp = &sc->arpcom.ac_if; PCN_LOCK(sc); mii = device_get_softc(sc->pcn_miibus); mii_tick(mii); /* link just died */ if (sc->pcn_link & !(mii->mii_media_status & IFM_ACTIVE)) sc->pcn_link = 0; /* link just came up, restart */ if (!sc->pcn_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->pcn_link++; if (ifp->if_snd.ifq_head != NULL) pcn_start(ifp); } sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); PCN_UNLOCK(sc); return; } static void pcn_intr(arg) void *arg; { struct pcn_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; ifp = &sc->arpcom.ac_if; /* Supress unwanted interrupts */ if (!(ifp->if_flags & IFF_UP)) { pcn_stop(sc); return; } PCN_LOCK(sc); CSR_WRITE_4(sc, PCN_IO32_RAP, PCN_CSR_CSR); while ((status = CSR_READ_4(sc, PCN_IO32_RDP)) & PCN_CSR_INTR) { CSR_WRITE_4(sc, PCN_IO32_RDP, status); if (status & PCN_CSR_RINT) pcn_rxeof(sc); if (status & PCN_CSR_TINT) pcn_txeof(sc); if (status & PCN_CSR_ERR) { pcn_init(sc); break; } } if (ifp->if_snd.ifq_head != NULL) pcn_start(ifp); PCN_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int pcn_encap(sc, m_head, txidx) struct pcn_softc *sc; struct mbuf *m_head; u_int32_t *txidx; { struct pcn_tx_desc *f = NULL; struct mbuf *m; int frag, cur, cnt = 0; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ m = m_head; cur = frag = *txidx; for (m = m_head; m != NULL; m = m->m_next) { if (m->m_len != 0) { if ((PCN_TX_LIST_CNT - (sc->pcn_cdata.pcn_tx_cnt + cnt)) < 2) return(ENOBUFS); f = &sc->pcn_ldata->pcn_tx_list[frag]; f->pcn_txctl = (~(m->m_len) + 1) & PCN_TXCTL_BUFSZ; f->pcn_txctl |= PCN_TXCTL_MBO; f->pcn_tbaddr = vtophys(mtod(m, vm_offset_t)); if (cnt == 0) f->pcn_txctl |= PCN_TXCTL_STP; else f->pcn_txctl |= PCN_TXCTL_OWN; cur = frag; PCN_INC(frag, PCN_TX_LIST_CNT); cnt++; } } if (m != NULL) return(ENOBUFS); sc->pcn_cdata.pcn_tx_chain[cur] = m_head; sc->pcn_ldata->pcn_tx_list[cur].pcn_txctl |= PCN_TXCTL_ENP|PCN_TXCTL_ADD_FCS|PCN_TXCTL_MORE_LTINT; sc->pcn_ldata->pcn_tx_list[*txidx].pcn_txctl |= PCN_TXCTL_OWN; sc->pcn_cdata.pcn_tx_cnt += cnt; *txidx = frag; return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void pcn_start(ifp) struct ifnet *ifp; { struct pcn_softc *sc; struct mbuf *m_head = NULL; u_int32_t idx; sc = ifp->if_softc; PCN_LOCK(sc); if (!sc->pcn_link) { PCN_UNLOCK(sc); return; } idx = sc->pcn_cdata.pcn_tx_prod; if (ifp->if_flags & IFF_OACTIVE) { PCN_UNLOCK(sc); return; } while(sc->pcn_cdata.pcn_tx_chain[idx] == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (pcn_encap(sc, m_head, &idx)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); } /* Transmit */ sc->pcn_cdata.pcn_tx_prod = idx; pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; PCN_UNLOCK(sc); return; } static void pcn_setfilt(ifp) struct ifnet *ifp; { struct pcn_softc *sc; sc = ifp->if_softc; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); } else { PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_PROMISC); } /* Set the capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { PCN_CSR_CLRBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); } else { PCN_CSR_SETBIT(sc, PCN_CSR_MODE, PCN_MODE_RXNOBROAD); } return; } static void pcn_init(xsc) void *xsc; { struct pcn_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii = NULL; PCN_LOCK(sc); /* * Cancel pending I/O and free all RX/TX buffers. */ pcn_stop(sc); pcn_reset(sc); mii = device_get_softc(sc->pcn_miibus); /* Set MAC address */ pcn_csr_write(sc, PCN_CSR_PAR0, ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); pcn_csr_write(sc, PCN_CSR_PAR1, ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); pcn_csr_write(sc, PCN_CSR_PAR2, ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); /* Init circular RX list. */ if (pcn_list_rx_init(sc) == ENOBUFS) { printf("pcn%d: initialization failed: no " "memory for rx buffers\n", sc->pcn_unit); pcn_stop(sc); PCN_UNLOCK(sc); return; } /* * Init tx descriptors. */ pcn_list_tx_init(sc); /* Set up the mode register. */ pcn_csr_write(sc, PCN_CSR_MODE, PCN_PORT_MII); /* Set up RX filter. */ pcn_setfilt(ifp); /* * Load the multicast filter. */ pcn_setmulti(sc); /* * Load the addresses of the RX and TX lists. */ pcn_csr_write(sc, PCN_CSR_RXADDR0, vtophys(&sc->pcn_ldata->pcn_rx_list[0]) & 0xFFFF); pcn_csr_write(sc, PCN_CSR_RXADDR1, (vtophys(&sc->pcn_ldata->pcn_rx_list[0]) >> 16) & 0xFFFF); pcn_csr_write(sc, PCN_CSR_TXADDR0, vtophys(&sc->pcn_ldata->pcn_tx_list[0]) & 0xFFFF); pcn_csr_write(sc, PCN_CSR_TXADDR1, (vtophys(&sc->pcn_ldata->pcn_tx_list[0]) >> 16) & 0xFFFF); /* Set the RX and TX ring sizes. */ pcn_csr_write(sc, PCN_CSR_RXRINGLEN, (~PCN_RX_LIST_CNT) + 1); pcn_csr_write(sc, PCN_CSR_TXRINGLEN, (~PCN_TX_LIST_CNT) + 1); /* We're not using the initialization block. */ pcn_csr_write(sc, PCN_CSR_IAB1, 0); /* Enable fast suspend mode. */ PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE); /* * Enable burst read and write. Also set the no underflow * bit. This will avoid transmit underruns in certain * conditions while still providing decent performance. */ PCN_BCR_SETBIT(sc, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW| PCN_BUSCTL_BREAD|PCN_BUSCTL_BWRITE); /* Enable graceful recovery from underflow. */ PCN_CSR_SETBIT(sc, PCN_CSR_IMR, PCN_IMR_DXSUFLO); /* Enable auto-padding of short TX frames. */ PCN_CSR_SETBIT(sc, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX); /* Disable MII autoneg (we handle this ourselves). */ PCN_BCR_SETBIT(sc, PCN_BCR_MIICTL, PCN_MIICTL_DANAS); if (sc->pcn_type == Am79C978) pcn_bcr_write(sc, PCN_BCR_PHYSEL, PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA); /* Enable interrupts and start the controller running. */ pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); mii_mediachg(mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->pcn_stat_ch = timeout(pcn_tick, sc, hz); PCN_UNLOCK(sc); return; } /* * Set media options. */ static int pcn_ifmedia_upd(ifp) struct ifnet *ifp; { struct pcn_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->pcn_miibus); sc->pcn_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return(0); } /* * Report current media status. */ static void pcn_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct pcn_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->pcn_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int pcn_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct pcn_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii = NULL; int error = 0; PCN_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->pcn_if_flags & IFF_PROMISC)) { PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); pcn_setfilt(ifp); PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); } else if (ifp->if_flags & IFF_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->pcn_if_flags & IFF_PROMISC) { PCN_CSR_SETBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); pcn_setfilt(ifp); PCN_CSR_CLRBIT(sc, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND); pcn_csr_write(sc, PCN_CSR_CSR, PCN_CSR_INTEN|PCN_CSR_START); } else if (!(ifp->if_flags & IFF_RUNNING)) pcn_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) pcn_stop(sc); } sc->pcn_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: pcn_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->pcn_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } PCN_UNLOCK(sc); return(error); } static void pcn_watchdog(ifp) struct ifnet *ifp; { struct pcn_softc *sc; sc = ifp->if_softc; PCN_LOCK(sc); ifp->if_oerrors++; printf("pcn%d: watchdog timeout\n", sc->pcn_unit); pcn_stop(sc); pcn_reset(sc); pcn_init(sc); if (ifp->if_snd.ifq_head != NULL) pcn_start(ifp); PCN_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void pcn_stop(sc) struct pcn_softc *sc; { register int i; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; PCN_LOCK(sc); ifp->if_timer = 0; untimeout(pcn_tick, sc, sc->pcn_stat_ch); /* Turn off interrupts */ PCN_CSR_CLRBIT(sc, PCN_CSR_CSR, PCN_CSR_INTEN); /* Stop adapter */ PCN_CSR_SETBIT(sc, PCN_CSR_CSR, PCN_CSR_STOP); sc->pcn_link = 0; /* * Free data in the RX lists. */ for (i = 0; i < PCN_RX_LIST_CNT; i++) { if (sc->pcn_cdata.pcn_rx_chain[i] != NULL) { m_freem(sc->pcn_cdata.pcn_rx_chain[i]); sc->pcn_cdata.pcn_rx_chain[i] = NULL; } } bzero((char *)&sc->pcn_ldata->pcn_rx_list, sizeof(sc->pcn_ldata->pcn_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < PCN_TX_LIST_CNT; i++) { if (sc->pcn_cdata.pcn_tx_chain[i] != NULL) { m_freem(sc->pcn_cdata.pcn_tx_chain[i]); sc->pcn_cdata.pcn_tx_chain[i] = NULL; } } bzero((char *)&sc->pcn_ldata->pcn_tx_list, sizeof(sc->pcn_ldata->pcn_tx_list)); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); PCN_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void pcn_shutdown(dev) device_t dev; { struct pcn_softc *sc; sc = device_get_softc(dev); PCN_LOCK(sc); pcn_reset(sc); pcn_stop(sc); PCN_UNLOCK(sc); return; } Index: head/sys/pci/if_rl.c =================================================================== --- head/sys/pci/if_rl.c (revision 131252) +++ head/sys/pci/if_rl.c (revision 131253) @@ -1,1972 +1,1915 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * RealTek 8129/8139 PCI NIC driver * * Supports several extremely cheap PCI 10/100 adapters based on * the RealTek chipset. Datasheets can be obtained from * www.realtek.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' This is * probably the worst PCI ethernet controller ever made, with the possible * exception of the FEAST chip made by SMC. The 8139 supports bus-master * DMA, but it has a terrible interface that nullifies any performance * gains that bus-master DMA usually offers. * * For transmission, the chip offers a series of four TX descriptor * registers. Each transmit frame must be in a contiguous buffer, aligned * on a longword (32-bit) boundary. This means we almost always have to * do mbuf copies in order to transmit a frame, except in the unlikely * case where a) the packet fits into a single mbuf, and b) the packet * is 32-bit aligned within the mbuf's data area. The presence of only * four descriptor registers means that we can never have more than four * packets queued for transmission at any one time. * * Reception is not much better. The driver has to allocate a single large * buffer area (up to 64K in size) into which the chip will DMA received * frames. Because we don't know where within this region received packets * will begin or end, we have no choice but to copy data from the buffer * area into mbufs in order to pass the packets up to the higher protocol * levels. * * It's impossible given this rotten design to really achieve decent * performance at 100Mbps, unless you happen to have a 400Mhz PII or * some equally overmuscled CPU to drive it. * * On the bright side, the 8139 does have a built-in PHY, although * rather than using an MDIO serial interface like most other NICs, the * PHY registers are directly accessible through the 8139's register * space. The 8139 supports autonegotiation, as well as a 64-bit multicast * filter. * * The 8129 chip is an older version of the 8139 that uses an external PHY * chip. The 8129 has a serial MDIO interface for accessing the MII where * the 8139 lets you directly access the on-board PHY registers. We need * to select which interface to use depending on the chip type. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(rl, pci, 1, 1, 1); MODULE_DEPEND(rl, ether, 1, 1, 1); MODULE_DEPEND(rl, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Default to using PIO access for this driver. On SMP systems, * there appear to be problems with memory mapped mode: it looks like * doing too many memory mapped access back to back in rapid succession * can hang the bus. I'm inclined to blame this on crummy design/construction * on the part of RealTek. Memory mapped mode does appear to work on * uniprocessor systems though. */ #define RL_USEIOSPACE #include /* * Various supported device vendors/types and their names. */ static struct rl_type rl_devs[] = { { RT_VENDORID, RT_DEVICEID_8129, RL_8129, "RealTek 8129 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8139, RL_8139, "RealTek 8139 10/100BaseTX" }, { RT_VENDORID, RT_DEVICEID_8138, RL_8139, "RealTek 8139 10/100BaseTX CardBus" }, { RT_VENDORID, RT_DEVICEID_8100, RL_8139, "RealTek 8100 10/100BaseTX" }, { ACCTON_VENDORID, ACCTON_DEVICEID_5030, RL_8139, "Accton MPX 5030/5038 10/100BaseTX" }, { DELTA_VENDORID, DELTA_DEVICEID_8139, RL_8139, "Delta Electronics 8139 10/100BaseTX" }, { ADDTRON_VENDORID, ADDTRON_DEVICEID_8139, RL_8139, "Addtron Technolgy 8139 10/100BaseTX" }, { DLINK_VENDORID, DLINK_DEVICEID_530TXPLUS, RL_8139, "D-Link DFE-530TX+ 10/100BaseTX" }, { DLINK_VENDORID, DLINK_DEVICEID_690TXD, RL_8139, "D-Link DFE-690TXD 10/100BaseTX" }, { NORTEL_VENDORID, ACCTON_DEVICEID_5030, RL_8139, "Nortel Networks 10/100BaseTX" }, { COREGA_VENDORID, COREGA_DEVICEID_FETHERCBTXD, RL_8139, "Corega FEther CB-TXD" }, { COREGA_VENDORID, COREGA_DEVICEID_FETHERIICBTXD, RL_8139, "Corega FEtherII CB-TXD" }, { PEPPERCON_VENDORID, PEPPERCON_DEVICEID_ROLF, RL_8139, "Peppercon AG ROL-F" }, { PLANEX_VENDORID, PLANEX_DEVICEID_FNW3800TX, RL_8139, "Planex FNW-3800-TX" }, { CP_VENDORID, RT_DEVICEID_8139, RL_8139, "Compaq HNE-300" }, { LEVEL1_VENDORID, LEVEL1_DEVICEID_FPC0106TX, RL_8139, "LevelOne FPC-0106TX" }, { EDIMAX_VENDORID, EDIMAX_DEVICEID_EP4103DL, RL_8139, "Edimax EP-4103DL CardBus" }, { 0, 0, 0, NULL } }; static int rl_probe (device_t); static int rl_attach (device_t); static int rl_detach (device_t); static int rl_encap (struct rl_softc *, struct mbuf * ); static void rl_rxeof (struct rl_softc *); static void rl_txeof (struct rl_softc *); static void rl_intr (void *); static void rl_tick (void *); static void rl_start (struct ifnet *); static int rl_ioctl (struct ifnet *, u_long, caddr_t); static void rl_init (void *); static void rl_stop (struct rl_softc *); static void rl_watchdog (struct ifnet *); static int rl_suspend (device_t); static int rl_resume (device_t); static void rl_shutdown (device_t); static int rl_ifmedia_upd (struct ifnet *); static void rl_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void rl_eeprom_putbyte (struct rl_softc *, int); static void rl_eeprom_getword (struct rl_softc *, int, u_int16_t *); static void rl_read_eeprom (struct rl_softc *, caddr_t, int, int, int); static void rl_mii_sync (struct rl_softc *); static void rl_mii_send (struct rl_softc *, u_int32_t, int); static int rl_mii_readreg (struct rl_softc *, struct rl_mii_frame *); static int rl_mii_writereg (struct rl_softc *, struct rl_mii_frame *); static int rl_miibus_readreg (device_t, int, int); static int rl_miibus_writereg (device_t, int, int, int); static void rl_miibus_statchg (device_t); static void rl_setmulti (struct rl_softc *); static void rl_reset (struct rl_softc *); static int rl_list_tx_init (struct rl_softc *); static void rl_dma_map_rxbuf (void *, bus_dma_segment_t *, int, int); static void rl_dma_map_txbuf (void *, bus_dma_segment_t *, int, int); #ifdef RL_USEIOSPACE #define RL_RES SYS_RES_IOPORT #define RL_RID RL_PCI_LOIO #else #define RL_RES SYS_RES_MEMORY #define RL_RID RL_PCI_LOMEM #endif static device_method_t rl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rl_probe), DEVMETHOD(device_attach, rl_attach), DEVMETHOD(device_detach, rl_detach), DEVMETHOD(device_suspend, rl_suspend), DEVMETHOD(device_resume, rl_resume), DEVMETHOD(device_shutdown, rl_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, rl_miibus_readreg), DEVMETHOD(miibus_writereg, rl_miibus_writereg), DEVMETHOD(miibus_statchg, rl_miibus_statchg), { 0, 0 } }; static driver_t rl_driver = { "rl", rl_methods, sizeof(struct rl_softc) }; static devclass_t rl_devclass; DRIVER_MODULE(rl, pci, rl_driver, rl_devclass, 0, 0); DRIVER_MODULE(rl, cardbus, rl_driver, rl_devclass, 0, 0); DRIVER_MODULE(miibus, rl, miibus_driver, miibus_devclass, 0, 0); #define EE_SET(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) | x) #define EE_CLR(x) \ CSR_WRITE_1(sc, RL_EECMD, \ CSR_READ_1(sc, RL_EECMD) & ~x) static void rl_dma_map_rxbuf(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { struct rl_softc *sc; sc = arg; CSR_WRITE_4(sc, RL_RXADDR, segs->ds_addr & 0xFFFFFFFF); return; } static void rl_dma_map_txbuf(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { struct rl_softc *sc; sc = arg; CSR_WRITE_4(sc, RL_CUR_TXADDR(sc), segs->ds_addr & 0xFFFFFFFF); return; } /* * Send a read command and address to the EEPROM, check for ACK. */ static void rl_eeprom_putbyte(sc, addr) struct rl_softc *sc; int addr; { register int d, i; d = addr | sc->rl_eecmd_read; /* * Feed in each bit and strobe the clock. */ for (i = 0x400; i; i >>= 1) { if (d & i) { EE_SET(RL_EE_DATAIN); } else { EE_CLR(RL_EE_DATAIN); } DELAY(100); EE_SET(RL_EE_CLK); DELAY(150); EE_CLR(RL_EE_CLK); DELAY(100); } return; } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void rl_eeprom_getword(sc, addr, dest) struct rl_softc *sc; int addr; u_int16_t *dest; { register int i; u_int16_t word = 0; /* Enter EEPROM access mode. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); /* * Send address of word we want to read. */ rl_eeprom_putbyte(sc, addr); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { EE_SET(RL_EE_CLK); DELAY(100); if (CSR_READ_1(sc, RL_EECMD) & RL_EE_DATAOUT) word |= i; EE_CLR(RL_EE_CLK); DELAY(100); } /* Turn off EEPROM access mode. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); *dest = word; return; } /* * Read a sequence of words from the EEPROM. */ static void rl_read_eeprom(sc, dest, off, cnt, swap) struct rl_softc *sc; caddr_t dest; int off; int cnt; int swap; { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { rl_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return; } /* * MII access routines are provided for the 8129, which * doesn't have a built-in PHY. For the 8139, we fake things * up by diverting rl_phy_readreg()/rl_phy_writereg() to the * direct access PHY registers. */ #define MII_SET(x) \ CSR_WRITE_1(sc, RL_MII, \ CSR_READ_1(sc, RL_MII) | (x)) #define MII_CLR(x) \ CSR_WRITE_1(sc, RL_MII, \ CSR_READ_1(sc, RL_MII) & ~(x)) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void rl_mii_sync(sc) struct rl_softc *sc; { register int i; MII_SET(RL_MII_DIR|RL_MII_DATAOUT); for (i = 0; i < 32; i++) { MII_SET(RL_MII_CLK); DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void rl_mii_send(sc, bits, cnt) struct rl_softc *sc; u_int32_t bits; int cnt; { int i; MII_CLR(RL_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(RL_MII_DATAOUT); } else { MII_CLR(RL_MII_DATAOUT); } DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); } } /* * Read an PHY register through the MII. */ static int rl_mii_readreg(sc, frame) struct rl_softc *sc; struct rl_mii_frame *frame; { int i, ack; RL_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = RL_MII_STARTDELIM; frame->mii_opcode = RL_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_2(sc, RL_MII, 0); /* * Turn on data xmit. */ MII_SET(RL_MII_DIR); rl_mii_sync(sc); /* * Send command/address info. */ rl_mii_send(sc, frame->mii_stdelim, 2); rl_mii_send(sc, frame->mii_opcode, 2); rl_mii_send(sc, frame->mii_phyaddr, 5); rl_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ MII_CLR((RL_MII_CLK|RL_MII_DATAOUT)); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); /* Turn off xmit. */ MII_CLR(RL_MII_DIR); /* Check for ack */ MII_CLR(RL_MII_CLK); DELAY(1); ack = CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN; MII_SET(RL_MII_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(RL_MII_CLK); DELAY(1); if (!ack) { if (CSR_READ_2(sc, RL_MII) & RL_MII_DATAIN) frame->mii_data |= i; DELAY(1); } MII_SET(RL_MII_CLK); DELAY(1); } fail: MII_CLR(RL_MII_CLK); DELAY(1); MII_SET(RL_MII_CLK); DELAY(1); RL_UNLOCK(sc); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int rl_mii_writereg(sc, frame) struct rl_softc *sc; struct rl_mii_frame *frame; { RL_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = RL_MII_STARTDELIM; frame->mii_opcode = RL_MII_WRITEOP; frame->mii_turnaround = RL_MII_TURNAROUND; /* * Turn on data output. */ MII_SET(RL_MII_DIR); rl_mii_sync(sc); rl_mii_send(sc, frame->mii_stdelim, 2); rl_mii_send(sc, frame->mii_opcode, 2); rl_mii_send(sc, frame->mii_phyaddr, 5); rl_mii_send(sc, frame->mii_regaddr, 5); rl_mii_send(sc, frame->mii_turnaround, 2); rl_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(RL_MII_CLK); DELAY(1); MII_CLR(RL_MII_CLK); DELAY(1); /* * Turn off xmit. */ MII_CLR(RL_MII_DIR); RL_UNLOCK(sc); return(0); } static int rl_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct rl_softc *sc; struct rl_mii_frame frame; u_int16_t rval = 0; u_int16_t rl8139_reg = 0; sc = device_get_softc(dev); RL_LOCK(sc); if (sc->rl_type == RL_8139) { /* Pretend the internal PHY is only at address 0 */ if (phy) { RL_UNLOCK(sc); return(0); } switch(reg) { case MII_BMCR: rl8139_reg = RL_BMCR; break; case MII_BMSR: rl8139_reg = RL_BMSR; break; case MII_ANAR: rl8139_reg = RL_ANAR; break; case MII_ANER: rl8139_reg = RL_ANER; break; case MII_ANLPAR: rl8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: RL_UNLOCK(sc); return(0); /* * Allow the rlphy driver to read the media status * register. If we have a link partner which does not * support NWAY, this is the register which will tell * us the results of parallel detection. */ case RL_MEDIASTAT: rval = CSR_READ_1(sc, RL_MEDIASTAT); RL_UNLOCK(sc); return(rval); default: printf("rl%d: bad phy register\n", sc->rl_unit); RL_UNLOCK(sc); return(0); } rval = CSR_READ_2(sc, rl8139_reg); RL_UNLOCK(sc); return(rval); } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; rl_mii_readreg(sc, &frame); RL_UNLOCK(sc); return(frame.mii_data); } static int rl_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct rl_softc *sc; struct rl_mii_frame frame; u_int16_t rl8139_reg = 0; sc = device_get_softc(dev); RL_LOCK(sc); if (sc->rl_type == RL_8139) { /* Pretend the internal PHY is only at address 0 */ if (phy) { RL_UNLOCK(sc); return(0); } switch(reg) { case MII_BMCR: rl8139_reg = RL_BMCR; break; case MII_BMSR: rl8139_reg = RL_BMSR; break; case MII_ANAR: rl8139_reg = RL_ANAR; break; case MII_ANER: rl8139_reg = RL_ANER; break; case MII_ANLPAR: rl8139_reg = RL_LPAR; break; case MII_PHYIDR1: case MII_PHYIDR2: RL_UNLOCK(sc); return(0); break; default: printf("rl%d: bad phy register\n", sc->rl_unit); RL_UNLOCK(sc); return(0); } CSR_WRITE_2(sc, rl8139_reg, data); RL_UNLOCK(sc); return(0); } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; rl_mii_writereg(sc, &frame); RL_UNLOCK(sc); return(0); } static void rl_miibus_statchg(dev) device_t dev; { return; } /* * Program the 64-bit multicast hash filter. */ static void rl_setmulti(sc) struct rl_softc *sc; { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int32_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; rxfilt = CSR_READ_4(sc, RL_RXCFG); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxfilt |= RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, RL_MAR4, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, RL_MAR0, 0); CSR_WRITE_4(sc, RL_MAR4, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if (mcnt) rxfilt |= RL_RXCFG_RX_MULTI; else rxfilt &= ~RL_RXCFG_RX_MULTI; CSR_WRITE_4(sc, RL_RXCFG, rxfilt); CSR_WRITE_4(sc, RL_MAR0, hashes[0]); CSR_WRITE_4(sc, RL_MAR4, hashes[1]); return; } static void rl_reset(sc) struct rl_softc *sc; { register int i; CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_RESET); for (i = 0; i < RL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_1(sc, RL_COMMAND) & RL_CMD_RESET)) break; } if (i == RL_TIMEOUT) printf("rl%d: reset never completed!\n", sc->rl_unit); return; } /* * Probe for a RealTek 8129/8139 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int rl_probe(dev) device_t dev; { struct rl_type *t; struct rl_softc *sc; int rid; u_int32_t hwrev; t = rl_devs; sc = device_get_softc(dev); while(t->rl_name != NULL) { if ((pci_get_vendor(dev) == t->rl_vid) && (pci_get_device(dev) == t->rl_did)) { /* * Temporarily map the I/O space * so we can read the chip ID register. */ rid = RL_RID; sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, RF_ACTIVE); if (sc->rl_res == NULL) { device_printf(dev, "couldn't map ports/memory\n"); return(ENXIO); } sc->rl_btag = rman_get_bustag(sc->rl_res); sc->rl_bhandle = rman_get_bushandle(sc->rl_res); mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); RL_LOCK(sc); hwrev = CSR_READ_4(sc, RL_TXCFG) & RL_TXCFG_HWREV; bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); RL_UNLOCK(sc); mtx_destroy(&sc->rl_mtx); /* Don't attach to 8139C+ or 8169/8110 chips. */ if (hwrev == RL_HWREV_8139CPLUS || (hwrev == RL_HWREV_8169 && t->rl_did == RT_DEVICEID_8169) || hwrev == RL_HWREV_8169S || hwrev == RL_HWREV_8110S) { t++; continue; } device_set_desc(dev, t->rl_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int rl_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; u_int16_t as[3]; struct rl_softc *sc; struct ifnet *ifp; u_int16_t rl_did = 0; struct rl_type *t; int unit, error = 0, rid, i; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->rl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES /* - * Handle power management nonsense. - */ - - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, RL_PCI_LOIO, 4); - membase = pci_read_config(dev, RL_PCI_LOMEM, 4); - irq = pci_read_config(dev, RL_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("rl%d: chip is is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, RL_PCI_LOIO, iobase, 4); - pci_write_config(dev, RL_PCI_LOMEM, membase, 4); - pci_write_config(dev, RL_PCI_INTLINE, irq, 4); - } -#endif - /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = RL_RID; sc->rl_res = bus_alloc_resource_any(dev, RL_RES, &rid, RF_ACTIVE); if (sc->rl_res == NULL) { printf ("rl%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } #ifdef notdef /* Detect the Realtek 8139B. For some reason, this chip is very * unstable when left to autoselect the media * The best workaround is to set the device to the required * media type or to set it to the 10 Meg speed. */ if ((rman_get_end(sc->rl_res)-rman_get_start(sc->rl_res))==0xff) { printf("rl%d: Realtek 8139B detected. Warning, " "this may be unstable in autoselect mode\n", unit); } #endif sc->rl_btag = rman_get_bustag(sc->rl_res); sc->rl_bhandle = rman_get_bushandle(sc->rl_res); /* Allocate interrupt */ rid = 0; sc->rl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->rl_irq == NULL) { printf("rl%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ rl_reset(sc); sc->rl_eecmd_read = RL_EECMD_READ_6BIT; rl_read_eeprom(sc, (caddr_t)&rl_did, 0, 1, 0); if (rl_did != 0x8129) sc->rl_eecmd_read = RL_EECMD_READ_8BIT; /* * Get station address from the EEPROM. */ rl_read_eeprom(sc, (caddr_t)as, RL_EE_EADDR, 3, 0); for (i = 0; i < 3; i++) { eaddr[(i * 2) + 0] = as[i] & 0xff; eaddr[(i * 2) + 1] = as[i] >> 8; } sc->rl_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* * Now read the exact device type from the EEPROM to find * out if it's an 8129 or 8139. */ rl_read_eeprom(sc, (caddr_t)&rl_did, RL_EE_PCI_DID, 1, 0); t = rl_devs; sc->rl_type = 0; while(t->rl_name != NULL) { if (rl_did == t->rl_did) { sc->rl_type = t->rl_basetype; break; } t++; } if (sc->rl_type == 0) { printf("rl%d: unknown device ID: %x\n", unit, rl_did); error = ENXIO; goto fail; } /* * Allocate the parent bus DMA tag appropriate for PCI. */ #define RL_NSEG_NEW 32 error = bus_dma_tag_create(NULL, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, RL_NSEG_NEW, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rl_parent_tag); if (error) goto fail; /* * Now allocate a tag for the DMA descriptor lists. * All of our lists are allocated as a contiguous block * of memory. */ error = bus_dma_tag_create(sc->rl_parent_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ RL_RXBUFLEN + 1518, 1, /* maxsize,nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->rl_tag); if (error) goto fail; /* * Now allocate a chunk of DMA-able memory based on the * tag we just created. */ error = bus_dmamem_alloc(sc->rl_tag, (void **)&sc->rl_cdata.rl_rx_buf, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->rl_cdata.rl_rx_dmamap); if (error) { printf("rl%d: no memory for list buffers!\n", unit); bus_dma_tag_destroy(sc->rl_tag); sc->rl_tag = NULL; goto fail; } /* Leave a few bytes before the start of the RX ring buffer. */ sc->rl_cdata.rl_rx_buf_ptr = sc->rl_cdata.rl_rx_buf; sc->rl_cdata.rl_rx_buf += sizeof(u_int64_t); /* Do MII setup */ if (mii_phy_probe(dev, &sc->rl_miibus, rl_ifmedia_upd, rl_ifmedia_sts)) { printf("rl%d: MII without any phy!\n", sc->rl_unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = rl_ioctl; ifp->if_start = rl_start; ifp->if_watchdog = rl_watchdog; ifp->if_init = rl_init; ifp->if_baudrate = 10000000; ifp->if_capabilities = IFCAP_VLAN_MTU; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; callout_handle_init(&sc->rl_stat_ch); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->rl_irq, INTR_TYPE_NET, rl_intr, sc, &sc->rl_intrhand); if (error) { printf("rl%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) rl_detach(dev); return (error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int rl_detach(dev) device_t dev; { struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->rl_mtx), ("rl mutex not initialized")); RL_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { rl_stop(sc); ether_ifdetach(ifp); } if (sc->rl_miibus) device_delete_child(dev, sc->rl_miibus); bus_generic_detach(dev); if (sc->rl_intrhand) bus_teardown_intr(dev, sc->rl_irq, sc->rl_intrhand); if (sc->rl_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->rl_irq); if (sc->rl_res) bus_release_resource(dev, RL_RES, RL_RID, sc->rl_res); if (sc->rl_tag) { bus_dmamap_unload(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap); bus_dmamem_free(sc->rl_tag, sc->rl_cdata.rl_rx_buf, sc->rl_cdata.rl_rx_dmamap); bus_dma_tag_destroy(sc->rl_tag); } if (sc->rl_parent_tag) bus_dma_tag_destroy(sc->rl_parent_tag); RL_UNLOCK(sc); mtx_destroy(&sc->rl_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int rl_list_tx_init(sc) struct rl_softc *sc; { struct rl_chain_data *cd; int i; cd = &sc->rl_cdata; for (i = 0; i < RL_TX_LIST_CNT; i++) { cd->rl_tx_chain[i] = NULL; CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(u_int32_t)), 0x0000000); } sc->rl_cdata.cur_tx = 0; sc->rl_cdata.last_tx = 0; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. * * You know there's something wrong with a PCI bus-master chip design * when you have to use m_devget(). * * The receive operation is badly documented in the datasheet, so I'll * attempt to document it here. The driver provides a buffer area and * places its base address in the RX buffer start address register. * The chip then begins copying frames into the RX buffer. Each frame * is preceded by a 32-bit RX status word which specifies the length * of the frame and certain other status bits. Each frame (starting with * the status word) is also 32-bit aligned. The frame length is in the * first 16 bits of the status word; the lower 15 bits correspond with * the 'rx status register' mentioned in the datasheet. * * Note: to make the Alpha happy, the frame payload needs to be aligned * on a 32-bit boundary. To achieve this, we pass RL_ETHER_ALIGN (2 bytes) * as the offset argument to m_devget(). */ static void rl_rxeof(sc) struct rl_softc *sc; { struct mbuf *m; struct ifnet *ifp; int total_len = 0; u_int32_t rxstat; caddr_t rxbufpos; int wrap = 0; u_int16_t cur_rx; u_int16_t limit; u_int16_t rx_bytes = 0, max_bytes; RL_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; bus_dmamap_sync(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, BUS_DMASYNC_POSTREAD); cur_rx = (CSR_READ_2(sc, RL_CURRXADDR) + 16) % RL_RXBUFLEN; /* Do not try to read past this point. */ limit = CSR_READ_2(sc, RL_CURRXBUF) % RL_RXBUFLEN; if (limit < cur_rx) max_bytes = (RL_RXBUFLEN - cur_rx) + limit; else max_bytes = limit - cur_rx; while((CSR_READ_1(sc, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif /* DEVICE_POLLING */ rxbufpos = sc->rl_cdata.rl_rx_buf + cur_rx; rxstat = le32toh(*(u_int32_t *)rxbufpos); /* * Here's a totally undocumented fact for you. When the * RealTek chip is in the process of copying a packet into * RAM for you, the length will be 0xfff0. If you spot a * packet header with this value, you need to stop. The * datasheet makes absolutely no mention of this and * RealTek should be shot for this. */ if ((u_int16_t)(rxstat >> 16) == RL_RXSTAT_UNFINISHED) break; if (!(rxstat & RL_RXSTAT_RXOK)) { ifp->if_ierrors++; rl_init(sc); return; } /* No errors; receive the packet. */ total_len = rxstat >> 16; rx_bytes += total_len + 4; /* * XXX The RealTek chip includes the CRC with every * received frame, and there's no way to turn this * behavior off (at least, I can't find anything in * the manual that explains how to do it) so we have * to trim off the CRC manually. */ total_len -= ETHER_CRC_LEN; /* * Avoid trying to read more bytes than we know * the chip has prepared for us. */ if (rx_bytes > max_bytes) break; rxbufpos = sc->rl_cdata.rl_rx_buf + ((cur_rx + sizeof(u_int32_t)) % RL_RXBUFLEN); if (rxbufpos == (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN)) rxbufpos = sc->rl_cdata.rl_rx_buf; wrap = (sc->rl_cdata.rl_rx_buf + RL_RXBUFLEN) - rxbufpos; if (total_len > wrap) { m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, NULL); if (m == NULL) { ifp->if_ierrors++; } else { m_copyback(m, wrap, total_len - wrap, sc->rl_cdata.rl_rx_buf); } cur_rx = (total_len - wrap + ETHER_CRC_LEN); } else { m = m_devget(rxbufpos, total_len, RL_ETHER_ALIGN, ifp, NULL); if (m == NULL) { ifp->if_ierrors++; } cur_rx += total_len + 4 + ETHER_CRC_LEN; } /* * Round up to 32-bit boundary. */ cur_rx = (cur_rx + 3) & ~3; CSR_WRITE_2(sc, RL_CURRXADDR, cur_rx - 16); if (m == NULL) continue; ifp->if_ipackets++; RL_UNLOCK(sc); (*ifp->if_input)(ifp, m); RL_LOCK(sc); } return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void rl_txeof(sc) struct rl_softc *sc; { struct ifnet *ifp; u_int32_t txstat; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been uploaded. */ do { if (RL_LAST_TXMBUF(sc) == NULL) break; txstat = CSR_READ_4(sc, RL_LAST_TXSTAT(sc)); if (!(txstat & (RL_TXSTAT_TX_OK| RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) break; ifp->if_collisions += (txstat & RL_TXSTAT_COLLCNT) >> 24; bus_dmamap_unload(sc->rl_tag, RL_LAST_DMAMAP(sc)); bus_dmamap_destroy(sc->rl_tag, RL_LAST_DMAMAP(sc)); m_freem(RL_LAST_TXMBUF(sc)); RL_LAST_TXMBUF(sc) = NULL; if (txstat & RL_TXSTAT_TX_OK) ifp->if_opackets++; else { int oldthresh; ifp->if_oerrors++; if ((txstat & RL_TXSTAT_TXABRT) || (txstat & RL_TXSTAT_OUTOFWIN)) CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); oldthresh = sc->rl_txthresh; /* error recovery */ rl_reset(sc); rl_init(sc); /* * If there was a transmit underrun, * bump the TX threshold. */ if (txstat & RL_TXSTAT_TX_UNDERRUN) sc->rl_txthresh = oldthresh + 32; return; } RL_INC(sc->rl_cdata.last_tx); ifp->if_flags &= ~IFF_OACTIVE; } while (sc->rl_cdata.last_tx != sc->rl_cdata.cur_tx); if (RL_LAST_TXMBUF(sc) == NULL) ifp->if_timer = 0; else if (ifp->if_timer == 0) ifp->if_timer = 5; return; } static void rl_tick(xsc) void *xsc; { struct rl_softc *sc; struct mii_data *mii; sc = xsc; RL_LOCK(sc); mii = device_get_softc(sc->rl_miibus); mii_tick(mii); sc->rl_stat_ch = timeout(rl_tick, sc, hz); RL_UNLOCK(sc); return; } #ifdef DEVICE_POLLING static void rl_poll (struct ifnet *ifp, enum poll_cmd cmd, int count) { struct rl_softc *sc = ifp->if_softc; RL_LOCK(sc); if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ CSR_WRITE_2(sc, RL_IMR, RL_INTRS); goto done; } sc->rxcycles = count; rl_rxeof(sc); rl_txeof(sc); if (ifp->if_snd.ifq_head != NULL) rl_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int16_t status; status = CSR_READ_2(sc, RL_ISR); if (status == 0xffff) goto done; if (status) CSR_WRITE_2(sc, RL_ISR, status); /* * XXX check behaviour on receiver stalls. */ if (status & RL_ISR_SYSTEM_ERR) { rl_reset(sc); rl_init(sc); } } done: RL_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void rl_intr(arg) void *arg; { struct rl_softc *sc; struct ifnet *ifp; u_int16_t status; sc = arg; if (sc->suspended) { return; } RL_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(rl_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_2(sc, RL_IMR, 0x0000); rl_poll(ifp, 0, 1); goto done; } #endif /* DEVICE_POLLING */ for (;;) { status = CSR_READ_2(sc, RL_ISR); /* If the card has gone away the read returns 0xffff. */ if (status == 0xffff) break; if (status) CSR_WRITE_2(sc, RL_ISR, status); if ((status & RL_INTRS) == 0) break; if (status & RL_ISR_RX_OK) rl_rxeof(sc); if (status & RL_ISR_RX_ERR) rl_rxeof(sc); if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) rl_txeof(sc); if (status & RL_ISR_SYSTEM_ERR) { rl_reset(sc); rl_init(sc); } } if (ifp->if_snd.ifq_head != NULL) rl_start(ifp); #ifdef DEVICE_POLLING done: #endif RL_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int rl_encap(sc, m_head) struct rl_softc *sc; struct mbuf *m_head; { struct mbuf *m_new = NULL; /* * The RealTek is brain damaged and wants longword-aligned * TX buffers, plus we can only have one fragment buffer * per packet. We have to copy pretty much all the time. */ m_new = m_defrag(m_head, M_DONTWAIT); if (m_new == NULL) { m_freem(m_head); return(1); } m_head = m_new; /* Pad frames to at least 60 bytes. */ if (m_head->m_pkthdr.len < RL_MIN_FRAMELEN) { /* * Make security concious people happy: zero out the * bytes in the pad area, since we don't know what * this mbuf cluster buffer's previous user might * have left in it. */ bzero(mtod(m_head, char *) + m_head->m_pkthdr.len, RL_MIN_FRAMELEN - m_head->m_pkthdr.len); m_head->m_pkthdr.len += (RL_MIN_FRAMELEN - m_head->m_pkthdr.len); m_head->m_len = m_head->m_pkthdr.len; } RL_CUR_TXMBUF(sc) = m_head; return(0); } /* * Main transmit routine. */ static void rl_start(ifp) struct ifnet *ifp; { struct rl_softc *sc; struct mbuf *m_head = NULL; sc = ifp->if_softc; RL_LOCK(sc); while(RL_CUR_TXMBUF(sc) == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (rl_encap(sc, m_head)) { break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, RL_CUR_TXMBUF(sc)); /* * Transmit the frame. */ bus_dmamap_create(sc->rl_tag, 0, &RL_CUR_DMAMAP(sc)); bus_dmamap_load(sc->rl_tag, RL_CUR_DMAMAP(sc), mtod(RL_CUR_TXMBUF(sc), void *), RL_CUR_TXMBUF(sc)->m_pkthdr.len, rl_dma_map_txbuf, sc, 0); bus_dmamap_sync(sc->rl_tag, RL_CUR_DMAMAP(sc), BUS_DMASYNC_PREREAD); CSR_WRITE_4(sc, RL_CUR_TXSTAT(sc), RL_TXTHRESH(sc->rl_txthresh) | RL_CUR_TXMBUF(sc)->m_pkthdr.len); RL_INC(sc->rl_cdata.cur_tx); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; } /* * We broke out of the loop because all our TX slots are * full. Mark the NIC as busy until it drains some of the * packets from the queue. */ if (RL_CUR_TXMBUF(sc) != NULL) ifp->if_flags |= IFF_OACTIVE; RL_UNLOCK(sc); return; } static void rl_init(xsc) void *xsc; { struct rl_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; u_int32_t rxcfg = 0; RL_LOCK(sc); mii = device_get_softc(sc->rl_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ rl_stop(sc); /* * Init our MAC address. Even though the chipset * documentation doesn't mention it, we need to enter "Config * register write enable" mode to modify the ID registers. */ CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_WRITECFG); CSR_WRITE_STREAM_4(sc, RL_IDR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); CSR_WRITE_STREAM_4(sc, RL_IDR4, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); /* Init the RX buffer pointer register. */ bus_dmamap_load(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, sc->rl_cdata.rl_rx_buf, RL_RXBUFLEN, rl_dma_map_rxbuf, sc, 0); bus_dmamap_sync(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap, BUS_DMASYNC_PREWRITE); /* Init TX descriptors. */ rl_list_tx_init(sc); /* * Enable transmit and receive. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); /* * Set the initial TX and RX configuration. */ CSR_WRITE_4(sc, RL_TXCFG, RL_TXCFG_CONFIG); CSR_WRITE_4(sc, RL_RXCFG, RL_RXCFG_CONFIG); /* Set the individual bit to receive frames for this host only. */ rxcfg = CSR_READ_4(sc, RL_RXCFG); rxcfg |= RL_RXCFG_RX_INDIV; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { rxcfg |= RL_RXCFG_RX_ALLPHYS; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } else { rxcfg &= ~RL_RXCFG_RX_ALLPHYS; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { rxcfg |= RL_RXCFG_RX_BROAD; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } else { rxcfg &= ~RL_RXCFG_RX_BROAD; CSR_WRITE_4(sc, RL_RXCFG, rxcfg); } /* * Program the multicast filter, if necessary. */ rl_setmulti(sc); #ifdef DEVICE_POLLING /* * Disable interrupts if we are polling. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_2(sc, RL_IMR, 0); else /* otherwise ... */ #endif /* DEVICE_POLLING */ /* * Enable interrupts. */ CSR_WRITE_2(sc, RL_IMR, RL_INTRS); /* Set initial TX threshold */ sc->rl_txthresh = RL_TX_THRESH_INIT; /* Start RX/TX process. */ CSR_WRITE_4(sc, RL_MISSEDPKT, 0); /* Enable receiver and transmitter. */ CSR_WRITE_1(sc, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB); mii_mediachg(mii); CSR_WRITE_1(sc, RL_CFG1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->rl_stat_ch = timeout(rl_tick, sc, hz); RL_UNLOCK(sc); return; } /* * Set media options. */ static int rl_ifmedia_upd(ifp) struct ifnet *ifp; { struct rl_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); mii_mediachg(mii); return(0); } /* * Report current media status. */ static void rl_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct rl_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->rl_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int rl_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct rl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; RL_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { rl_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) rl_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: rl_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->rl_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING; break; default: error = ether_ioctl(ifp, command, data); break; } RL_UNLOCK(sc); return(error); } static void rl_watchdog(ifp) struct ifnet *ifp; { struct rl_softc *sc; sc = ifp->if_softc; RL_LOCK(sc); printf("rl%d: watchdog timeout\n", sc->rl_unit); ifp->if_oerrors++; rl_txeof(sc); rl_rxeof(sc); rl_init(sc); RL_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void rl_stop(sc) struct rl_softc *sc; { register int i; struct ifnet *ifp; RL_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; untimeout(rl_tick, sc, sc->rl_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif /* DEVICE_POLLING */ CSR_WRITE_1(sc, RL_COMMAND, 0x00); CSR_WRITE_2(sc, RL_IMR, 0x0000); bus_dmamap_unload(sc->rl_tag, sc->rl_cdata.rl_rx_dmamap); /* * Free the TX list buffers. */ for (i = 0; i < RL_TX_LIST_CNT; i++) { if (sc->rl_cdata.rl_tx_chain[i] != NULL) { bus_dmamap_unload(sc->rl_tag, sc->rl_cdata.rl_tx_dmamap[i]); bus_dmamap_destroy(sc->rl_tag, sc->rl_cdata.rl_tx_dmamap[i]); m_freem(sc->rl_cdata.rl_tx_chain[i]); sc->rl_cdata.rl_tx_chain[i] = NULL; CSR_WRITE_4(sc, RL_TXADDR0 + (i * sizeof(u_int32_t)), 0x0000000); } } RL_UNLOCK(sc); return; } /* * Device suspend routine. Stop the interface and save some PCI * settings in case the BIOS doesn't restore them properly on * resume. */ static int rl_suspend(dev) device_t dev; { -#ifndef BURN_BRIDGES - register int i; -#endif struct rl_softc *sc; sc = device_get_softc(dev); - rl_stop(sc); - -#ifndef BURN_BRIDGES - for (i = 0; i < 5; i++) - sc->saved_maps[i] = pci_read_config(dev, PCIR_MAPS + i * 4, 4); - sc->saved_biosaddr = pci_read_config(dev, PCIR_BIOS, 4); - sc->saved_intline = pci_read_config(dev, PCIR_INTLINE, 1); - sc->saved_cachelnsz = pci_read_config(dev, PCIR_CACHELNSZ, 1); - sc->saved_lattimer = pci_read_config(dev, PCIR_LATTIMER, 1); -#endif - sc->suspended = 1; return (0); } /* * Device resume routine. Restore some PCI settings in case the BIOS * doesn't, re-enable busmastering, and restart the interface if * appropriate. */ static int rl_resume(dev) device_t dev; { -#ifndef BURN_BRIDGES - register int i; -#endif struct rl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); ifp = &sc->arpcom.ac_if; - -#ifndef BURN_BRIDGES - /* better way to do this? */ - for (i = 0; i < 5; i++) - pci_write_config(dev, PCIR_MAPS + i * 4, sc->saved_maps[i], 4); - pci_write_config(dev, PCIR_BIOS, sc->saved_biosaddr, 4); - pci_write_config(dev, PCIR_INTLINE, sc->saved_intline, 1); - pci_write_config(dev, PCIR_CACHELNSZ, sc->saved_cachelnsz, 1); - pci_write_config(dev, PCIR_LATTIMER, sc->saved_lattimer, 1); - - /* reenable busmastering */ - pci_enable_busmaster(dev); - pci_enable_io(dev, RL_RES); -#endif /* reinitialize interface if necessary */ if (ifp->if_flags & IFF_UP) rl_init(sc); sc->suspended = 0; return (0); } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void rl_shutdown(dev) device_t dev; { struct rl_softc *sc; sc = device_get_softc(dev); rl_stop(sc); return; } Index: head/sys/pci/if_rlreg.h =================================================================== --- head/sys/pci/if_rlreg.h (revision 131252) +++ head/sys/pci/if_rlreg.h (revision 131253) @@ -1,881 +1,873 @@ /* * Copyright (c) 1997, 1998-2003 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * RealTek 8129/8139 register offsets */ #define RL_IDR0 0x0000 /* ID register 0 (station addr) */ #define RL_IDR1 0x0001 /* Must use 32-bit accesses (?) */ #define RL_IDR2 0x0002 #define RL_IDR3 0x0003 #define RL_IDR4 0x0004 #define RL_IDR5 0x0005 /* 0006-0007 reserved */ #define RL_MAR0 0x0008 /* Multicast hash table */ #define RL_MAR1 0x0009 #define RL_MAR2 0x000A #define RL_MAR3 0x000B #define RL_MAR4 0x000C #define RL_MAR5 0x000D #define RL_MAR6 0x000E #define RL_MAR7 0x000F #define RL_TXSTAT0 0x0010 /* status of TX descriptor 0 */ #define RL_TXSTAT1 0x0014 /* status of TX descriptor 1 */ #define RL_TXSTAT2 0x0018 /* status of TX descriptor 2 */ #define RL_TXSTAT3 0x001C /* status of TX descriptor 3 */ #define RL_TXADDR0 0x0020 /* address of TX descriptor 0 */ #define RL_TXADDR1 0x0024 /* address of TX descriptor 1 */ #define RL_TXADDR2 0x0028 /* address of TX descriptor 2 */ #define RL_TXADDR3 0x002C /* address of TX descriptor 3 */ #define RL_RXADDR 0x0030 /* RX ring start address */ #define RL_RX_EARLY_BYTES 0x0034 /* RX early byte count */ #define RL_RX_EARLY_STAT 0x0036 /* RX early status */ #define RL_COMMAND 0x0037 /* command register */ #define RL_CURRXADDR 0x0038 /* current address of packet read */ #define RL_CURRXBUF 0x003A /* current RX buffer address */ #define RL_IMR 0x003C /* interrupt mask register */ #define RL_ISR 0x003E /* interrupt status register */ #define RL_TXCFG 0x0040 /* transmit config */ #define RL_RXCFG 0x0044 /* receive config */ #define RL_TIMERCNT 0x0048 /* timer count register */ #define RL_MISSEDPKT 0x004C /* missed packet counter */ #define RL_EECMD 0x0050 /* EEPROM command register */ #define RL_CFG0 0x0051 /* config register #0 */ #define RL_CFG1 0x0052 /* config register #1 */ /* 0053-0057 reserved */ #define RL_MEDIASTAT 0x0058 /* media status register (8139) */ /* 0059-005A reserved */ #define RL_MII 0x005A /* 8129 chip only */ #define RL_HALTCLK 0x005B #define RL_MULTIINTR 0x005C /* multiple interrupt */ #define RL_PCIREV 0x005E /* PCI revision value */ /* 005F reserved */ #define RL_TXSTAT_ALL 0x0060 /* TX status of all descriptors */ /* Direct PHY access registers only available on 8139 */ #define RL_BMCR 0x0062 /* PHY basic mode control */ #define RL_BMSR 0x0064 /* PHY basic mode status */ #define RL_ANAR 0x0066 /* PHY autoneg advert */ #define RL_LPAR 0x0068 /* PHY link partner ability */ #define RL_ANER 0x006A /* PHY autoneg expansion */ #define RL_DISCCNT 0x006C /* disconnect counter */ #define RL_FALSECAR 0x006E /* false carrier counter */ #define RL_NWAYTST 0x0070 /* NWAY test register */ #define RL_RX_ER 0x0072 /* RX_ER counter */ #define RL_CSCFG 0x0074 /* CS configuration register */ /* * When operating in special C+ mode, some of the registers in an * 8139C+ chip have different definitions. These are also used for * the 8169 gigE chip. */ #define RL_DUMPSTATS_LO 0x0010 /* counter dump command register */ #define RL_DUMPSTATS_HI 0x0014 /* counter dump command register */ #define RL_TXLIST_ADDR_LO 0x0020 /* 64 bits, 256 byte alignment */ #define RL_TXLIST_ADDR_HI 0x0024 /* 64 bits, 256 byte alignment */ #define RL_TXLIST_ADDR_HPRIO_LO 0x0028 /* 64 bits, 256 byte alignment */ #define RL_TXLIST_ADDR_HPRIO_HI 0x002C /* 64 bits, 256 byte alignment */ #define RL_CFG2 0x0053 #define RL_TIMERINT 0x0054 /* interrupt on timer expire */ #define RL_TXSTART 0x00D9 /* 8 bits */ #define RL_CPLUS_CMD 0x00E0 /* 16 bits */ #define RL_RXLIST_ADDR_LO 0x00E4 /* 64 bits, 256 byte alignment */ #define RL_RXLIST_ADDR_HI 0x00E8 /* 64 bits, 256 byte alignment */ #define RL_EARLY_TX_THRESH 0x00EC /* 8 bits */ /* * Registers specific to the 8169 gigE chip */ #define RL_TIMERINT_8169 0x0058 /* different offset than 8139 */ #define RL_PHYAR 0x0060 #define RL_TBICSR 0x0064 #define RL_TBI_ANAR 0x0068 #define RL_TBI_LPAR 0x006A #define RL_GMEDIASTAT 0x006C /* 8 bits */ #define RL_MAXRXPKTLEN 0x00DA /* 16 bits, chip multiplies by 8 */ #define RL_GTXSTART 0x0038 /* 16 bits */ /* * TX config register bits */ #define RL_TXCFG_CLRABRT 0x00000001 /* retransmit aborted pkt */ #define RL_TXCFG_MAXDMA 0x00000700 /* max DMA burst size */ #define RL_TXCFG_CRCAPPEND 0x00010000 /* CRC append (0 = yes) */ #define RL_TXCFG_LOOPBKTST 0x00060000 /* loopback test */ #define RL_TXCFG_IFG2 0x00080000 /* 8169 only */ #define RL_TXCFG_IFG 0x03000000 /* interframe gap */ #define RL_TXCFG_HWREV 0x7CC00000 #define RL_LOOPTEST_OFF 0x00000000 #define RL_LOOPTEST_ON 0x00020000 #define RL_LOOPTEST_ON_CPLUS 0x00060000 #define RL_HWREV_8169 0x00000000 #define RL_HWREV_8169S 0x04000000 #define RL_HWREV_8110S 0x00800000 #define RL_HWREV_8139 0x60000000 #define RL_HWREV_8139A 0x70000000 #define RL_HWREV_8139AG 0x70800000 #define RL_HWREV_8139B 0x78000000 #define RL_HWREV_8130 0x7C000000 #define RL_HWREV_8139C 0x74000000 #define RL_HWREV_8139D 0x74400000 #define RL_HWREV_8139CPLUS 0x74800000 #define RL_HWREV_8101 0x74c00000 #define RL_HWREV_8100 0x78800000 #define RL_TXDMA_16BYTES 0x00000000 #define RL_TXDMA_32BYTES 0x00000100 #define RL_TXDMA_64BYTES 0x00000200 #define RL_TXDMA_128BYTES 0x00000300 #define RL_TXDMA_256BYTES 0x00000400 #define RL_TXDMA_512BYTES 0x00000500 #define RL_TXDMA_1024BYTES 0x00000600 #define RL_TXDMA_2048BYTES 0x00000700 /* * Transmit descriptor status register bits. */ #define RL_TXSTAT_LENMASK 0x00001FFF #define RL_TXSTAT_OWN 0x00002000 #define RL_TXSTAT_TX_UNDERRUN 0x00004000 #define RL_TXSTAT_TX_OK 0x00008000 #define RL_TXSTAT_EARLY_THRESH 0x003F0000 #define RL_TXSTAT_COLLCNT 0x0F000000 #define RL_TXSTAT_CARR_HBEAT 0x10000000 #define RL_TXSTAT_OUTOFWIN 0x20000000 #define RL_TXSTAT_TXABRT 0x40000000 #define RL_TXSTAT_CARRLOSS 0x80000000 /* * Interrupt status register bits. */ #define RL_ISR_RX_OK 0x0001 #define RL_ISR_RX_ERR 0x0002 #define RL_ISR_TX_OK 0x0004 #define RL_ISR_TX_ERR 0x0008 #define RL_ISR_RX_OVERRUN 0x0010 #define RL_ISR_PKT_UNDERRUN 0x0020 #define RL_ISR_LINKCHG 0x0020 /* 8169 only */ #define RL_ISR_FIFO_OFLOW 0x0040 /* 8139 only */ #define RL_ISR_TX_DESC_UNAVAIL 0x0080 /* C+ only */ #define RL_ISR_SWI 0x0100 /* C+ only */ #define RL_ISR_CABLE_LEN_CHGD 0x2000 #define RL_ISR_PCS_TIMEOUT 0x4000 /* 8129 only */ #define RL_ISR_TIMEOUT_EXPIRED 0x4000 #define RL_ISR_SYSTEM_ERR 0x8000 #define RL_INTRS \ (RL_ISR_TX_OK|RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR) #define RL_INTRS_CPLUS \ (RL_ISR_RX_OK|RL_ISR_RX_ERR|RL_ISR_TX_ERR| \ RL_ISR_RX_OVERRUN|RL_ISR_PKT_UNDERRUN|RL_ISR_FIFO_OFLOW| \ RL_ISR_PCS_TIMEOUT|RL_ISR_SYSTEM_ERR|RL_ISR_TIMEOUT_EXPIRED) /* * Media status register. (8139 only) */ #define RL_MEDIASTAT_RXPAUSE 0x01 #define RL_MEDIASTAT_TXPAUSE 0x02 #define RL_MEDIASTAT_LINK 0x04 #define RL_MEDIASTAT_SPEED10 0x08 #define RL_MEDIASTAT_RXFLOWCTL 0x40 /* duplex mode */ #define RL_MEDIASTAT_TXFLOWCTL 0x80 /* duplex mode */ /* * Receive config register. */ #define RL_RXCFG_RX_ALLPHYS 0x00000001 /* accept all nodes */ #define RL_RXCFG_RX_INDIV 0x00000002 /* match filter */ #define RL_RXCFG_RX_MULTI 0x00000004 /* accept all multicast */ #define RL_RXCFG_RX_BROAD 0x00000008 /* accept all broadcast */ #define RL_RXCFG_RX_RUNT 0x00000010 #define RL_RXCFG_RX_ERRPKT 0x00000020 #define RL_RXCFG_WRAP 0x00000080 #define RL_RXCFG_MAXDMA 0x00000700 #define RL_RXCFG_BUFSZ 0x00001800 #define RL_RXCFG_FIFOTHRESH 0x0000E000 #define RL_RXCFG_EARLYTHRESH 0x07000000 #define RL_RXDMA_16BYTES 0x00000000 #define RL_RXDMA_32BYTES 0x00000100 #define RL_RXDMA_64BYTES 0x00000200 #define RL_RXDMA_128BYTES 0x00000300 #define RL_RXDMA_256BYTES 0x00000400 #define RL_RXDMA_512BYTES 0x00000500 #define RL_RXDMA_1024BYTES 0x00000600 #define RL_RXDMA_UNLIMITED 0x00000700 #define RL_RXBUF_8 0x00000000 #define RL_RXBUF_16 0x00000800 #define RL_RXBUF_32 0x00001000 #define RL_RXBUF_64 0x00001800 #define RL_RXFIFO_16BYTES 0x00000000 #define RL_RXFIFO_32BYTES 0x00002000 #define RL_RXFIFO_64BYTES 0x00004000 #define RL_RXFIFO_128BYTES 0x00006000 #define RL_RXFIFO_256BYTES 0x00008000 #define RL_RXFIFO_512BYTES 0x0000A000 #define RL_RXFIFO_1024BYTES 0x0000C000 #define RL_RXFIFO_NOTHRESH 0x0000E000 /* * Bits in RX status header (included with RX'ed packet * in ring buffer). */ #define RL_RXSTAT_RXOK 0x00000001 #define RL_RXSTAT_ALIGNERR 0x00000002 #define RL_RXSTAT_CRCERR 0x00000004 #define RL_RXSTAT_GIANT 0x00000008 #define RL_RXSTAT_RUNT 0x00000010 #define RL_RXSTAT_BADSYM 0x00000020 #define RL_RXSTAT_BROAD 0x00002000 #define RL_RXSTAT_INDIV 0x00004000 #define RL_RXSTAT_MULTI 0x00008000 #define RL_RXSTAT_LENMASK 0xFFFF0000 #define RL_RXSTAT_UNFINISHED 0xFFF0 /* DMA still in progress */ /* * Command register. */ #define RL_CMD_EMPTY_RXBUF 0x0001 #define RL_CMD_TX_ENB 0x0004 #define RL_CMD_RX_ENB 0x0008 #define RL_CMD_RESET 0x0010 /* * EEPROM control register */ #define RL_EE_DATAOUT 0x01 /* Data out */ #define RL_EE_DATAIN 0x02 /* Data in */ #define RL_EE_CLK 0x04 /* clock */ #define RL_EE_SEL 0x08 /* chip select */ #define RL_EE_MODE (0x40|0x80) #define RL_EEMODE_OFF 0x00 #define RL_EEMODE_AUTOLOAD 0x40 #define RL_EEMODE_PROGRAM 0x80 #define RL_EEMODE_WRITECFG (0x80|0x40) /* 9346 EEPROM commands */ #define RL_EECMD_WRITE 0x140 #define RL_EECMD_READ_6BIT 0x180 #define RL_EECMD_READ_8BIT 0x600 #define RL_EECMD_ERASE 0x1c0 #define RL_EE_ID 0x00 #define RL_EE_PCI_VID 0x01 #define RL_EE_PCI_DID 0x02 /* Location of station address inside EEPROM */ #define RL_EE_EADDR 0x07 /* * MII register (8129 only) */ #define RL_MII_CLK 0x01 #define RL_MII_DATAIN 0x02 #define RL_MII_DATAOUT 0x04 #define RL_MII_DIR 0x80 /* 0 == input, 1 == output */ /* * Config 0 register */ #define RL_CFG0_ROM0 0x01 #define RL_CFG0_ROM1 0x02 #define RL_CFG0_ROM2 0x04 #define RL_CFG0_PL0 0x08 #define RL_CFG0_PL1 0x10 #define RL_CFG0_10MBPS 0x20 /* 10 Mbps internal mode */ #define RL_CFG0_PCS 0x40 #define RL_CFG0_SCR 0x80 /* * Config 1 register */ #define RL_CFG1_PWRDWN 0x01 #define RL_CFG1_SLEEP 0x02 #define RL_CFG1_IOMAP 0x04 #define RL_CFG1_MEMMAP 0x08 #define RL_CFG1_RSVD 0x10 #define RL_CFG1_DRVLOAD 0x20 #define RL_CFG1_LED0 0x40 #define RL_CFG1_FULLDUPLEX 0x40 /* 8129 only */ #define RL_CFG1_LED1 0x80 /* * 8139C+ register definitions */ /* RL_DUMPSTATS_LO register */ #define RL_DUMPSTATS_START 0x00000008 /* Transmit start register */ #define RL_TXSTART_SWI 0x01 /* generate TX interrupt */ #define RL_TXSTART_START 0x40 /* start normal queue transmit */ #define RL_TXSTART_HPRIO_START 0x80 /* start hi prio queue transmit */ /* * Config 2 register, 8139C+/8169/8169S/8110S only */ #define RL_CFG2_BUSFREQ 0x07 #define RL_CFG2_BUSWIDTH 0x08 #define RL_CFG2_AUXPWRSTS 0x10 #define RL_BUSFREQ_33MHZ 0x00 #define RL_BUSFREQ_66MHZ 0x01 #define RL_BUSWIDTH_32BITS 0x00 #define RL_BUSWIDTH_64BITS 0x08 /* C+ mode command register */ #define RL_CPLUSCMD_TXENB 0x0001 /* enable C+ transmit mode */ #define RL_CPLUSCMD_RXENB 0x0002 /* enable C+ receive mode */ #define RL_CPLUSCMD_PCI_MRW 0x0008 /* enable PCI multi-read/write */ #define RL_CPLUSCMD_PCI_DAC 0x0010 /* PCI dual-address cycle only */ #define RL_CPLUSCMD_RXCSUM_ENB 0x0020 /* enable RX checksum offload */ #define RL_CPLUSCMD_VLANSTRIP 0x0040 /* enable VLAN tag stripping */ /* C+ early transmit threshold */ #define RL_EARLYTXTHRESH_CNT 0x003F /* byte count times 8 */ /* * Gigabit PHY access register (8169 only) */ #define RL_PHYAR_PHYDATA 0x0000FFFF #define RL_PHYAR_PHYREG 0x001F0000 #define RL_PHYAR_BUSY 0x80000000 /* * Gigabit media status (8169 only) */ #define RL_GMEDIASTAT_FDX 0x01 /* full duplex */ #define RL_GMEDIASTAT_LINK 0x02 /* link up */ #define RL_GMEDIASTAT_10MBPS 0x04 /* 10mps link */ #define RL_GMEDIASTAT_100MBPS 0x08 /* 100mbps link */ #define RL_GMEDIASTAT_1000MBPS 0x10 /* gigE link */ #define RL_GMEDIASTAT_RXFLOW 0x20 /* RX flow control on */ #define RL_GMEDIASTAT_TXFLOW 0x40 /* TX flow control on */ #define RL_GMEDIASTAT_TBI 0x80 /* TBI enabled */ /* * The RealTek doesn't use a fragment-based descriptor mechanism. * Instead, there are only four register sets, each or which represents * one 'descriptor.' Basically, each TX descriptor is just a contiguous * packet buffer (32-bit aligned!) and we place the buffer addresses in * the registers so the chip knows where they are. * * We can sort of kludge together the same kind of buffer management * used in previous drivers, but we have to do buffer copies almost all * the time, so it doesn't really buy us much. * * For reception, there's just one large buffer where the chip stores * all received packets. */ #define RL_RX_BUF_SZ RL_RXBUF_64 #define RL_RXBUFLEN (1 << ((RL_RX_BUF_SZ >> 11) + 13)) #define RL_TX_LIST_CNT 4 #define RL_MIN_FRAMELEN 60 #define RL_TXTHRESH(x) ((x) << 11) #define RL_TX_THRESH_INIT 96 #define RL_RX_FIFOTHRESH RL_RXFIFO_NOTHRESH #define RL_RX_MAXDMA RL_RXDMA_UNLIMITED #define RL_TX_MAXDMA RL_TXDMA_2048BYTES #define RL_RXCFG_CONFIG (RL_RX_FIFOTHRESH|RL_RX_MAXDMA|RL_RX_BUF_SZ) #define RL_TXCFG_CONFIG (RL_TXCFG_IFG|RL_TX_MAXDMA) #define RL_ETHER_ALIGN 2 struct rl_chain_data { u_int16_t cur_rx; caddr_t rl_rx_buf; caddr_t rl_rx_buf_ptr; bus_dmamap_t rl_rx_dmamap; struct mbuf *rl_tx_chain[RL_TX_LIST_CNT]; bus_dmamap_t rl_tx_dmamap[RL_TX_LIST_CNT]; u_int8_t last_tx; u_int8_t cur_tx; }; #define RL_INC(x) (x = (x + 1) % RL_TX_LIST_CNT) #define RL_CUR_TXADDR(x) ((x->rl_cdata.cur_tx * 4) + RL_TXADDR0) #define RL_CUR_TXSTAT(x) ((x->rl_cdata.cur_tx * 4) + RL_TXSTAT0) #define RL_CUR_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.cur_tx]) #define RL_CUR_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.cur_tx]) #define RL_LAST_TXADDR(x) ((x->rl_cdata.last_tx * 4) + RL_TXADDR0) #define RL_LAST_TXSTAT(x) ((x->rl_cdata.last_tx * 4) + RL_TXSTAT0) #define RL_LAST_TXMBUF(x) (x->rl_cdata.rl_tx_chain[x->rl_cdata.last_tx]) #define RL_LAST_DMAMAP(x) (x->rl_cdata.rl_tx_dmamap[x->rl_cdata.last_tx]) struct rl_type { u_int16_t rl_vid; u_int16_t rl_did; int rl_basetype; char *rl_name; }; struct rl_hwrev { u_int32_t rl_rev; int rl_type; char *rl_desc; }; struct rl_mii_frame { u_int8_t mii_stdelim; u_int8_t mii_opcode; u_int8_t mii_phyaddr; u_int8_t mii_regaddr; u_int8_t mii_turnaround; u_int16_t mii_data; }; /* * MII constants */ #define RL_MII_STARTDELIM 0x01 #define RL_MII_READOP 0x02 #define RL_MII_WRITEOP 0x01 #define RL_MII_TURNAROUND 0x02 #define RL_8129 1 #define RL_8139 2 #define RL_8139CPLUS 3 #define RL_8169 4 #define RL_ISCPLUS(x) ((x)->rl_type == RL_8139CPLUS || \ (x)->rl_type == RL_8169) /* * The 8139C+ and 8160 gigE chips support descriptor-based TX * and RX. In fact, they even support TCP large send. Descriptors * must be allocated in contiguous blocks that are aligned on a * 256-byte boundary. The rings can hold a maximum of 64 descriptors. */ /* * RX/TX descriptor definition. When large send mode is enabled, the * lower 11 bits of the TX rl_cmd word are used to hold the MSS, and * the checksum offload bits are disabled. The structure layout is * the same for RX and TX descriptors */ struct rl_desc { u_int32_t rl_cmdstat; u_int32_t rl_vlanctl; u_int32_t rl_bufaddr_lo; u_int32_t rl_bufaddr_hi; }; #define RL_TDESC_CMD_FRAGLEN 0x0000FFFF #define RL_TDESC_CMD_TCPCSUM 0x00010000 /* TCP checksum enable */ #define RL_TDESC_CMD_UDPCSUM 0x00020000 /* UDP checksum enable */ #define RL_TDESC_CMD_IPCSUM 0x00040000 /* IP header checksum enable */ #define RL_TDESC_CMD_MSSVAL 0x07FF0000 /* Large send MSS value */ #define RL_TDESC_CMD_LGSEND 0x08000000 /* TCP large send enb */ #define RL_TDESC_CMD_EOF 0x10000000 /* end of frame marker */ #define RL_TDESC_CMD_SOF 0x20000000 /* start of frame marker */ #define RL_TDESC_CMD_EOR 0x40000000 /* end of ring marker */ #define RL_TDESC_CMD_OWN 0x80000000 /* chip owns descriptor */ #define RL_TDESC_VLANCTL_TAG 0x00020000 /* Insert VLAN tag */ #define RL_TDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ /* * Error bits are valid only on the last descriptor of a frame * (i.e. RL_TDESC_CMD_EOF == 1) */ #define RL_TDESC_STAT_COLCNT 0x000F0000 /* collision count */ #define RL_TDESC_STAT_EXCESSCOL 0x00100000 /* excessive collisions */ #define RL_TDESC_STAT_LINKFAIL 0x00200000 /* link faulure */ #define RL_TDESC_STAT_OWINCOL 0x00400000 /* out-of-window collision */ #define RL_TDESC_STAT_TXERRSUM 0x00800000 /* transmit error summary */ #define RL_TDESC_STAT_UNDERRUN 0x02000000 /* TX underrun occured */ #define RL_TDESC_STAT_OWN 0x80000000 /* * RX descriptor cmd/vlan definitions */ #define RL_RDESC_CMD_EOR 0x40000000 #define RL_RDESC_CMD_OWN 0x80000000 #define RL_RDESC_CMD_BUFLEN 0x00001FFF #define RL_RDESC_STAT_OWN 0x80000000 #define RL_RDESC_STAT_EOR 0x40000000 #define RL_RDESC_STAT_SOF 0x20000000 #define RL_RDESC_STAT_EOF 0x10000000 #define RL_RDESC_STAT_FRALIGN 0x08000000 /* frame alignment error */ #define RL_RDESC_STAT_MCAST 0x04000000 /* multicast pkt received */ #define RL_RDESC_STAT_UCAST 0x02000000 /* unicast pkt received */ #define RL_RDESC_STAT_BCAST 0x01000000 /* broadcast pkt received */ #define RL_RDESC_STAT_BUFOFLOW 0x00800000 /* out of buffer space */ #define RL_RDESC_STAT_FIFOOFLOW 0x00400000 /* FIFO overrun */ #define RL_RDESC_STAT_GIANT 0x00200000 /* pkt > 4096 bytes */ #define RL_RDESC_STAT_RXERRSUM 0x00100000 /* RX error summary */ #define RL_RDESC_STAT_RUNT 0x00080000 /* runt packet received */ #define RL_RDESC_STAT_CRCERR 0x00040000 /* CRC error */ #define RL_RDESC_STAT_PROTOID 0x00030000 /* Protocol type */ #define RL_RDESC_STAT_IPSUMBAD 0x00008000 /* IP header checksum bad */ #define RL_RDESC_STAT_UDPSUMBAD 0x00004000 /* UDP checksum bad */ #define RL_RDESC_STAT_TCPSUMBAD 0x00002000 /* TCP checksum bad */ #define RL_RDESC_STAT_FRAGLEN 0x00001FFF /* RX'ed frame/frag len */ #define RL_RDESC_STAT_GFRAGLEN 0x00003FFF /* RX'ed frame/frag len */ #define RL_RDESC_VLANCTL_TAG 0x00010000 /* VLAN tag available (rl_vlandata valid)*/ #define RL_RDESC_VLANCTL_DATA 0x0000FFFF /* TAG data */ #define RL_PROTOID_NONIP 0x00000000 #define RL_PROTOID_TCPIP 0x00010000 #define RL_PROTOID_UDPIP 0x00020000 #define RL_PROTOID_IP 0x00030000 #define RL_TCPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ RL_PROTOID_TCPIP) #define RL_UDPPKT(x) (((x) & RL_RDESC_STAT_PROTOID) == \ RL_PROTOID_UDPIP) /* * Statistics counter structure (8139C+ and 8169 only) */ struct rl_stats { u_int32_t rl_tx_pkts_lo; u_int32_t rl_tx_pkts_hi; u_int32_t rl_tx_errs_lo; u_int32_t rl_tx_errs_hi; u_int32_t rl_tx_errs; u_int16_t rl_missed_pkts; u_int16_t rl_rx_framealign_errs; u_int32_t rl_tx_onecoll; u_int32_t rl_tx_multicolls; u_int32_t rl_rx_ucasts_hi; u_int32_t rl_rx_ucasts_lo; u_int32_t rl_rx_bcasts_lo; u_int32_t rl_rx_bcasts_hi; u_int32_t rl_rx_mcasts; u_int16_t rl_tx_aborts; u_int16_t rl_rx_underruns; }; #define RL_RX_DESC_CNT 64 #define RL_TX_DESC_CNT 64 #define RL_RX_LIST_SZ (RL_RX_DESC_CNT * sizeof(struct rl_desc)) #define RL_TX_LIST_SZ (RL_TX_DESC_CNT * sizeof(struct rl_desc)) #define RL_RING_ALIGN 256 #define RL_IFQ_MAXLEN 512 #define RL_DESC_INC(x) (x = (x + 1) % RL_TX_DESC_CNT) #define RL_OWN(x) (le32toh((x)->rl_cmdstat) & RL_RDESC_STAT_OWN) #define RL_RXBYTES(x) (le32toh((x)->rl_cmdstat) & sc->rl_rxlenmask) #define RL_PKTSZ(x) ((x)/* >> 3*/) #define RL_ADDR_LO(y) ((u_int64_t) (y) & 0xFFFFFFFF) #define RL_ADDR_HI(y) ((u_int64_t) (y) >> 32) #define RL_JUMBO_FRAMELEN 9018 #define RL_JUMBO_MTU (RL_JUMBO_FRAMELEN-ETHER_HDR_LEN-ETHER_CRC_LEN) struct rl_softc; struct rl_dmaload_arg { struct rl_softc *sc; int rl_idx; int rl_maxsegs; u_int32_t rl_flags; struct rl_desc *rl_ring; }; struct rl_list_data { struct mbuf *rl_tx_mbuf[RL_TX_DESC_CNT]; struct mbuf *rl_rx_mbuf[RL_TX_DESC_CNT]; int rl_tx_prodidx; int rl_rx_prodidx; int rl_tx_considx; int rl_tx_free; bus_dmamap_t rl_tx_dmamap[RL_TX_DESC_CNT]; bus_dmamap_t rl_rx_dmamap[RL_RX_DESC_CNT]; bus_dma_tag_t rl_mtag; /* mbuf mapping tag */ bus_dma_tag_t rl_stag; /* stats mapping tag */ bus_dmamap_t rl_smap; /* stats map */ struct rl_stats *rl_stats; bus_addr_t rl_stats_addr; bus_dma_tag_t rl_rx_list_tag; bus_dmamap_t rl_rx_list_map; struct rl_desc *rl_rx_list; bus_addr_t rl_rx_list_addr; bus_dma_tag_t rl_tx_list_tag; bus_dmamap_t rl_tx_list_map; struct rl_desc *rl_tx_list; bus_addr_t rl_tx_list_addr; }; struct rl_softc { struct arpcom arpcom; /* interface info */ bus_space_handle_t rl_bhandle; /* bus space handle */ bus_space_tag_t rl_btag; /* bus space tag */ struct resource *rl_res; struct resource *rl_irq; void *rl_intrhand; device_t rl_miibus; bus_dma_tag_t rl_parent_tag; bus_dma_tag_t rl_tag; u_int8_t rl_unit; /* interface number */ u_int8_t rl_type; int rl_eecmd_read; u_int8_t rl_stats_no_timeout; int rl_txthresh; struct rl_chain_data rl_cdata; struct rl_list_data rl_ldata; struct callout_handle rl_stat_ch; struct mtx rl_mtx; struct mbuf *rl_head; struct mbuf *rl_tail; u_int32_t rl_hwrev; u_int32_t rl_rxlenmask; int rl_testmode; int suspended; /* 0 = normal 1 = suspended */ #ifdef DEVICE_POLLING int rxcycles; #endif - -#ifndef BURN_BRIDGES - u_int32_t saved_maps[5]; /* pci data */ - u_int32_t saved_biosaddr; - u_int8_t saved_intline; - u_int8_t saved_cachelnsz; - u_int8_t saved_lattimer; -#endif }; #define RL_LOCK(_sc) mtx_lock(&(_sc)->rl_mtx) #define RL_UNLOCK(_sc) mtx_unlock(&(_sc)->rl_mtx) #define RL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rl_mtx, MA_OWNED) /* * register space access macros */ #define CSR_WRITE_STREAM_4(sc, reg, val) \ bus_space_write_stream_4(sc->rl_btag, sc->rl_bhandle, reg, val) #define CSR_WRITE_4(sc, reg, val) \ bus_space_write_4(sc->rl_btag, sc->rl_bhandle, reg, val) #define CSR_WRITE_2(sc, reg, val) \ bus_space_write_2(sc->rl_btag, sc->rl_bhandle, reg, val) #define CSR_WRITE_1(sc, reg, val) \ bus_space_write_1(sc->rl_btag, sc->rl_bhandle, reg, val) #define CSR_READ_4(sc, reg) \ bus_space_read_4(sc->rl_btag, sc->rl_bhandle, reg) #define CSR_READ_2(sc, reg) \ bus_space_read_2(sc->rl_btag, sc->rl_bhandle, reg) #define CSR_READ_1(sc, reg) \ bus_space_read_1(sc->rl_btag, sc->rl_bhandle, reg) #define RL_TIMEOUT 1000 /* * General constants that are fun to know. * * RealTek PCI vendor ID */ #define RT_VENDORID 0x10EC /* * RealTek chip device IDs. */ #define RT_DEVICEID_8129 0x8129 #define RT_DEVICEID_8138 0x8138 #define RT_DEVICEID_8139 0x8139 #define RT_DEVICEID_8169 0x8169 #define RT_DEVICEID_8100 0x8100 #define RT_REVID_8139CPLUS 0x20 /* * Accton PCI vendor ID */ #define ACCTON_VENDORID 0x1113 /* * Accton MPX 5030/5038 device ID. */ #define ACCTON_DEVICEID_5030 0x1211 /* * Nortel PCI vendor ID */ #define NORTEL_VENDORID 0x126C /* * Delta Electronics Vendor ID. */ #define DELTA_VENDORID 0x1500 /* * Delta device IDs. */ #define DELTA_DEVICEID_8139 0x1360 /* * Addtron vendor ID. */ #define ADDTRON_VENDORID 0x4033 /* * Addtron device IDs. */ #define ADDTRON_DEVICEID_8139 0x1360 /* * D-Link vendor ID. */ #define DLINK_VENDORID 0x1186 /* * D-Link DFE-530TX+ device ID */ #define DLINK_DEVICEID_530TXPLUS 0x1300 /* * D-Link DFE-690TXD device ID */ #define DLINK_DEVICEID_690TXD 0x1340 /* * Corega K.K vendor ID */ #define COREGA_VENDORID 0x1259 /* * Corega FEther CB-TXD device ID */ #define COREGA_DEVICEID_FETHERCBTXD 0xa117 /* * Corega FEtherII CB-TXD device ID */ #define COREGA_DEVICEID_FETHERIICBTXD 0xa11e /* * Peppercon vendor ID */ #define PEPPERCON_VENDORID 0x1743 /* * Peppercon ROL-F device ID */ #define PEPPERCON_DEVICEID_ROLF 0x8139 /* * Planex Communications, Inc. vendor ID */ #define PLANEX_VENDORID 0x14ea /* * Planex FNW-3800-TX device ID */ #define PLANEX_DEVICEID_FNW3800TX 0xab07 /* * LevelOne vendor ID */ #define LEVEL1_VENDORID 0x018A /* * LevelOne FPC-0106TX devide ID */ #define LEVEL1_DEVICEID_FPC0106TX 0x0106 /* * Compaq vendor ID */ #define CP_VENDORID 0x021B /* * Edimax vendor ID */ #define EDIMAX_VENDORID 0x13D1 /* * Edimax EP-4103DL cardbus device ID */ #define EDIMAX_DEVICEID_EP4103DL 0xAB06 /* * PCI low memory base and low I/O base register, and * other PCI registers. */ #define RL_PCI_VENDOR_ID 0x00 #define RL_PCI_DEVICE_ID 0x02 #define RL_PCI_COMMAND 0x04 #define RL_PCI_STATUS 0x06 #define RL_PCI_CLASSCODE 0x09 #define RL_PCI_LATENCY_TIMER 0x0D #define RL_PCI_HEADER_TYPE 0x0E #define RL_PCI_LOIO 0x10 #define RL_PCI_LOMEM 0x14 #define RL_PCI_BIOSROM 0x30 #define RL_PCI_INTLINE 0x3C #define RL_PCI_INTPIN 0x3D #define RL_PCI_MINGNT 0x3E #define RL_PCI_MINLAT 0x0F #define RL_PCI_RESETOPT 0x48 #define RL_PCI_EEPROM_DATA 0x4C #define RL_PCI_CAPID 0x50 /* 8 bits */ #define RL_PCI_NEXTPTR 0x51 /* 8 bits */ #define RL_PCI_PWRMGMTCAP 0x52 /* 16 bits */ #define RL_PCI_PWRMGMTCTRL 0x54 /* 16 bits */ #define RL_PSTATE_MASK 0x0003 #define RL_PSTATE_D0 0x0000 #define RL_PSTATE_D1 0x0002 #define RL_PSTATE_D2 0x0002 #define RL_PSTATE_D3 0x0003 #define RL_PME_EN 0x0010 #define RL_PME_STATUS 0x8000 Index: head/sys/pci/if_sf.c =================================================================== --- head/sys/pci/if_sf.c (revision 131252) +++ head/sys/pci/if_sf.c (revision 131253) @@ -1,1506 +1,1482 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Adaptec AIC-6915 "Starfire" PCI fast ethernet driver for FreeBSD. * Programming manual is available from: * ftp.adaptec.com:/pub/BBS/userguides/aic6915_pg.pdf. * * Written by Bill Paul * Department of Electical Engineering * Columbia University, New York City */ /* * The Adaptec AIC-6915 "Starfire" is a 64-bit 10/100 PCI ethernet * controller designed with flexibility and reducing CPU load in mind. * The Starfire offers high and low priority buffer queues, a * producer/consumer index mechanism and several different buffer * queue and completion queue descriptor types. Any one of a number * of different driver designs can be used, depending on system and * OS requirements. This driver makes use of type0 transmit frame * descriptors (since BSD fragments packets across an mbuf chain) * and two RX buffer queues prioritized on size (one queue for small * frames that will fit into a single mbuf, another with full size * mbuf clusters for everything else). The producer/consumer indexes * and completion queues are also used. * * One downside to the Starfire has to do with alignment: buffer * queues must be aligned on 256-byte boundaries, and receive buffers * must be aligned on longword boundaries. The receive buffer alignment * causes problems on the Alpha platform, where the packet payload * should be longword aligned. There is no simple way around this. * * For receive filtering, the Starfire offers 16 perfect filter slots * and a 512-bit hash table. * * The Starfire has no internal transceiver, relying instead on an * external MII-based transceiver. Accessing registers on external * PHYs is done through a special register map rather than with the * usual bitbang MDIO method. * * Acesssing the registers on the Starfire is a little tricky. The * Starfire has a 512K internal register space. When programmed for * PCI memory mapped mode, the entire register space can be accessed * directly. However in I/O space mode, only 256 bytes are directly * mapped into PCI I/O space. The other registers can be accessed * indirectly using the SF_INDIRECTIO_ADDR and SF_INDIRECTIO_DATA * registers inside the 256-byte I/O window. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include #include #define SF_USEIOSPACE #include MODULE_DEPEND(sf, pci, 1, 1, 1); MODULE_DEPEND(sf, ether, 1, 1, 1); MODULE_DEPEND(sf, miibus, 1, 1, 1); static struct sf_type sf_devs[] = { { AD_VENDORID, AD_DEVICEID_STARFIRE, "Adaptec AIC-6915 10/100BaseTX" }, { 0, 0, NULL } }; static int sf_probe (device_t); static int sf_attach (device_t); static int sf_detach (device_t); static void sf_intr (void *); static void sf_stats_update (void *); static void sf_rxeof (struct sf_softc *); static void sf_txeof (struct sf_softc *); static int sf_encap (struct sf_softc *, struct sf_tx_bufdesc_type0 *, struct mbuf *); static void sf_start (struct ifnet *); static int sf_ioctl (struct ifnet *, u_long, caddr_t); static void sf_init (void *); static void sf_stop (struct sf_softc *); static void sf_watchdog (struct ifnet *); static void sf_shutdown (device_t); static int sf_ifmedia_upd (struct ifnet *); static void sf_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void sf_reset (struct sf_softc *); static int sf_init_rx_ring (struct sf_softc *); static void sf_init_tx_ring (struct sf_softc *); static int sf_newbuf (struct sf_softc *, struct sf_rx_bufdesc_type0 *, struct mbuf *); static void sf_setmulti (struct sf_softc *); static int sf_setperf (struct sf_softc *, int, caddr_t); static int sf_sethash (struct sf_softc *, caddr_t, int); #ifdef notdef static int sf_setvlan (struct sf_softc *, int, u_int32_t); #endif static u_int8_t sf_read_eeprom (struct sf_softc *, int); static int sf_miibus_readreg (device_t, int, int); static int sf_miibus_writereg (device_t, int, int, int); static void sf_miibus_statchg (device_t); static u_int32_t csr_read_4 (struct sf_softc *, int); static void csr_write_4 (struct sf_softc *, int, u_int32_t); static void sf_txthresh_adjust (struct sf_softc *); #ifdef SF_USEIOSPACE #define SF_RES SYS_RES_IOPORT #define SF_RID SF_PCI_LOIO #else #define SF_RES SYS_RES_MEMORY #define SF_RID SF_PCI_LOMEM #endif static device_method_t sf_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sf_probe), DEVMETHOD(device_attach, sf_attach), DEVMETHOD(device_detach, sf_detach), DEVMETHOD(device_shutdown, sf_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sf_miibus_readreg), DEVMETHOD(miibus_writereg, sf_miibus_writereg), DEVMETHOD(miibus_statchg, sf_miibus_statchg), { 0, 0 } }; static driver_t sf_driver = { "sf", sf_methods, sizeof(struct sf_softc), }; static devclass_t sf_devclass; DRIVER_MODULE(sf, pci, sf_driver, sf_devclass, 0, 0); DRIVER_MODULE(miibus, sf, miibus_driver, miibus_devclass, 0, 0); #define SF_SETBIT(sc, reg, x) \ csr_write_4(sc, reg, csr_read_4(sc, reg) | (x)) #define SF_CLRBIT(sc, reg, x) \ csr_write_4(sc, reg, csr_read_4(sc, reg) & ~(x)) static u_int32_t csr_read_4(sc, reg) struct sf_softc *sc; int reg; { u_int32_t val; #ifdef SF_USEIOSPACE CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); val = CSR_READ_4(sc, SF_INDIRECTIO_DATA); #else val = CSR_READ_4(sc, (reg + SF_RMAP_INTREG_BASE)); #endif return(val); } static u_int8_t sf_read_eeprom(sc, reg) struct sf_softc *sc; int reg; { u_int8_t val; val = (csr_read_4(sc, SF_EEADDR_BASE + (reg & 0xFFFFFFFC)) >> (8 * (reg & 3))) & 0xFF; return(val); } static void csr_write_4(sc, reg, val) struct sf_softc *sc; int reg; u_int32_t val; { #ifdef SF_USEIOSPACE CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE); CSR_WRITE_4(sc, SF_INDIRECTIO_DATA, val); #else CSR_WRITE_4(sc, (reg + SF_RMAP_INTREG_BASE), val); #endif return; } /* * Copy the address 'mac' into the perfect RX filter entry at * offset 'idx.' The perfect filter only has 16 entries so do * some sanity tests. */ static int sf_setperf(sc, idx, mac) struct sf_softc *sc; int idx; caddr_t mac; { u_int16_t *p; if (idx < 0 || idx > SF_RXFILT_PERFECT_CNT) return(EINVAL); if (mac == NULL) return(EINVAL); p = (u_int16_t *)mac; csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP), htons(p[2])); csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP) + 4, htons(p[1])); csr_write_4(sc, SF_RXFILT_PERFECT_BASE + (idx * SF_RXFILT_PERFECT_SKIP) + 8, htons(p[0])); return(0); } /* * Set the bit in the 512-bit hash table that corresponds to the * specified mac address 'mac.' If 'prio' is nonzero, update the * priority hash table instead of the filter hash table. */ static int sf_sethash(sc, mac, prio) struct sf_softc *sc; caddr_t mac; int prio; { u_int32_t h; if (mac == NULL) return(EINVAL); h = ether_crc32_be(mac, ETHER_ADDR_LEN) >> 23; if (prio) { SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_PRIOOFF + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); } else { SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_ADDROFF + (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF))); } return(0); } #ifdef notdef /* * Set a VLAN tag in the receive filter. */ static int sf_setvlan(sc, idx, vlan) struct sf_softc *sc; int idx; u_int32_t vlan; { if (idx < 0 || idx >> SF_RXFILT_HASH_CNT) return(EINVAL); csr_write_4(sc, SF_RXFILT_HASH_BASE + (idx * SF_RXFILT_HASH_SKIP) + SF_RXFILT_HASH_VLANOFF, vlan); return(0); } #endif static int sf_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct sf_softc *sc; int i; u_int32_t val = 0; sc = device_get_softc(dev); for (i = 0; i < SF_TIMEOUT; i++) { val = csr_read_4(sc, SF_PHY_REG(phy, reg)); if (val & SF_MII_DATAVALID) break; } if (i == SF_TIMEOUT) return(0); if ((val & 0x0000FFFF) == 0xFFFF) return(0); return(val & 0x0000FFFF); } static int sf_miibus_writereg(dev, phy, reg, val) device_t dev; int phy, reg, val; { struct sf_softc *sc; int i; int busy; sc = device_get_softc(dev); csr_write_4(sc, SF_PHY_REG(phy, reg), val); for (i = 0; i < SF_TIMEOUT; i++) { busy = csr_read_4(sc, SF_PHY_REG(phy, reg)); if (!(busy & SF_MII_BUSY)) break; } return(0); } static void sf_miibus_statchg(dev) device_t dev; { struct sf_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->sf_miibus); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_FDX); } else { SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX); csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_HDX); } return; } static void sf_setmulti(sc) struct sf_softc *sc; { struct ifnet *ifp; int i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0, 0 }; ifp = &sc->arpcom.ac_if; /* First zot all the existing filters. */ for (i = 1; i < SF_RXFILT_PERFECT_CNT; i++) sf_setperf(sc, i, (char *)&dummy); for (i = SF_RXFILT_HASH_BASE; i < (SF_RXFILT_HASH_MAX + 1); i += 4) csr_write_4(sc, i, 0); SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); /* Now program new ones. */ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI); } else { i = 1; TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Program the first 15 multicast groups * into the perfect filter. For all others, * use the hash table. */ if (i < SF_RXFILT_PERFECT_CNT) { sf_setperf(sc, i, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); i++; continue; } sf_sethash(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 0); } } return; } /* * Set media options. */ static int sf_ifmedia_upd(ifp) struct ifnet *ifp; { struct sf_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sf_miibus); sc->sf_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return(0); } /* * Report current media status. */ static void sf_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sf_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sf_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int sf_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sf_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; SF_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->sf_if_flags & IFF_PROMISC)) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else if (ifp->if_flags & IFF_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->sf_if_flags & IFF_PROMISC) { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else if (!(ifp->if_flags & IFF_RUNNING)) sf_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) sf_stop(sc); } sc->sf_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: sf_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->sf_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } SF_UNLOCK(sc); return(error); } static void sf_reset(sc) struct sf_softc *sc; { register int i; csr_write_4(sc, SF_GEN_ETH_CTL, 0); SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); DELAY(1000); SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET); SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_RESET); for (i = 0; i < SF_TIMEOUT; i++) { DELAY(10); if (!(csr_read_4(sc, SF_PCI_DEVCFG) & SF_PCIDEVCFG_RESET)) break; } if (i == SF_TIMEOUT) printf("sf%d: reset never completed!\n", sc->sf_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for an Adaptec AIC-6915 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. * We also check the subsystem ID so that we can identify exactly which * NIC has been found, if possible. */ static int sf_probe(dev) device_t dev; { struct sf_type *t; t = sf_devs; while(t->sf_name != NULL) { if ((pci_get_vendor(dev) == t->sf_vid) && (pci_get_device(dev) == t->sf_did)) { switch((pci_read_config(dev, SF_PCI_SUBVEN_ID, 4) >> 16) & 0xFFFF) { case AD_SUBSYSID_62011_REV0: case AD_SUBSYSID_62011_REV1: device_set_desc(dev, "Adaptec ANA-62011 10/100BaseTX"); return(0); case AD_SUBSYSID_62022: device_set_desc(dev, "Adaptec ANA-62022 10/100BaseTX"); return(0); case AD_SUBSYSID_62044_REV0: case AD_SUBSYSID_62044_REV1: device_set_desc(dev, "Adaptec ANA-62044 10/100BaseTX"); return(0); case AD_SUBSYSID_62020: device_set_desc(dev, "Adaptec ANA-62020 10/100BaseFX"); return(0); case AD_SUBSYSID_69011: device_set_desc(dev, "Adaptec ANA-69011 10/100BaseTX"); return(0); default: device_set_desc(dev, t->sf_name); return(0); break; } } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int sf_attach(dev) device_t dev; { int i; struct sf_softc *sc; struct ifnet *ifp; int unit, rid, error = 0; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->sf_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, SF_PCI_LOIO, 4); - membase = pci_read_config(dev, SF_PCI_LOMEM, 4); - irq = pci_read_config(dev, SF_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("sf%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, SF_PCI_LOIO, iobase, 4); - pci_write_config(dev, SF_PCI_LOMEM, membase, 4); - pci_write_config(dev, SF_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = SF_RID; sc->sf_res = bus_alloc_resource_any(dev, SF_RES, &rid, RF_ACTIVE); if (sc->sf_res == NULL) { printf ("sf%d: couldn't map ports\n", unit); error = ENXIO; goto fail; } sc->sf_btag = rman_get_bustag(sc->sf_res); sc->sf_bhandle = rman_get_bushandle(sc->sf_res); /* Allocate interrupt */ rid = 0; sc->sf_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sf_irq == NULL) { printf("sf%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } callout_handle_init(&sc->sf_stat_ch); /* Reset the adapter. */ sf_reset(sc); /* * Get station address from the EEPROM. */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc->arpcom.ac_enaddr[i] = sf_read_eeprom(sc, SF_EE_NODEADDR + ETHER_ADDR_LEN - i); sc->sf_unit = unit; /* Allocate the descriptor queues. */ sc->sf_ldata = contigmalloc(sizeof(struct sf_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->sf_ldata == NULL) { printf("sf%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->sf_ldata, sizeof(struct sf_list_data)); /* Do MII setup. */ if (mii_phy_probe(dev, &sc->sf_miibus, sf_ifmedia_upd, sf_ifmedia_sts)) { printf("sf%d: MII without any phy!\n", sc->sf_unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sf_ioctl; ifp->if_start = sf_start; ifp->if_watchdog = sf_watchdog; ifp->if_init = sf_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = SF_TX_DLIST_CNT - 1; /* * Call MI attach routine. */ ether_ifattach(ifp, sc->arpcom.ac_enaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->sf_irq, INTR_TYPE_NET, sf_intr, sc, &sc->sf_intrhand); if (error) { printf("sf%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) sf_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int sf_detach(dev) device_t dev; { struct sf_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sf_mtx), ("sf mutex not initialized")); SF_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { sf_stop(sc); ether_ifdetach(ifp); } if (sc->sf_miibus) device_delete_child(dev, sc->sf_miibus); bus_generic_detach(dev); if (sc->sf_intrhand) bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand); if (sc->sf_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq); if (sc->sf_res) bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res); if (sc->sf_ldata) contigfree(sc->sf_ldata, sizeof(struct sf_list_data), M_DEVBUF); SF_UNLOCK(sc); mtx_destroy(&sc->sf_mtx); return(0); } static int sf_init_rx_ring(sc) struct sf_softc *sc; { struct sf_list_data *ld; int i; ld = sc->sf_ldata; bzero((char *)ld->sf_rx_dlist_big, sizeof(struct sf_rx_bufdesc_type0) * SF_RX_DLIST_CNT); bzero((char *)ld->sf_rx_clist, sizeof(struct sf_rx_cmpdesc_type3) * SF_RX_CLIST_CNT); for (i = 0; i < SF_RX_DLIST_CNT; i++) { if (sf_newbuf(sc, &ld->sf_rx_dlist_big[i], NULL) == ENOBUFS) return(ENOBUFS); } return(0); } static void sf_init_tx_ring(sc) struct sf_softc *sc; { struct sf_list_data *ld; int i; ld = sc->sf_ldata; bzero((char *)ld->sf_tx_dlist, sizeof(struct sf_tx_bufdesc_type0) * SF_TX_DLIST_CNT); bzero((char *)ld->sf_tx_clist, sizeof(struct sf_tx_cmpdesc_type0) * SF_TX_CLIST_CNT); for (i = 0; i < SF_TX_DLIST_CNT; i++) ld->sf_tx_dlist[i].sf_id = SF_TX_BUFDESC_ID; for (i = 0; i < SF_TX_CLIST_CNT; i++) ld->sf_tx_clist[i].sf_type = SF_TXCMPTYPE_TX; ld->sf_tx_dlist[SF_TX_DLIST_CNT - 1].sf_end = 1; sc->sf_tx_cnt = 0; return; } static int sf_newbuf(sc, c, m) struct sf_softc *sc; struct sf_rx_bufdesc_type0 *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, sizeof(u_int64_t)); c->sf_mbuf = m_new; c->sf_addrlo = SF_RX_HOSTADDR(vtophys(mtod(m_new, caddr_t))); c->sf_valid = 1; return(0); } /* * The starfire is programmed to use 'normal' mode for packet reception, * which means we use the consumer/producer model for both the buffer * descriptor queue and the completion descriptor queue. The only problem * with this is that it involves a lot of register accesses: we have to * read the RX completion consumer and producer indexes and the RX buffer * producer index, plus the RX completion consumer and RX buffer producer * indexes have to be updated. It would have been easier if Adaptec had * put each index in a separate register, especially given that the damn * NIC has a 512K register space. * * In spite of all the lovely features that Adaptec crammed into the 6915, * it is marred by one truly stupid design flaw, which is that receive * buffer addresses must be aligned on a longword boundary. This forces * the packet payload to be unaligned, which is suboptimal on the x86 and * completely unuseable on the Alpha. Our only recourse is to copy received * packets into properly aligned buffers before handing them off. */ static void sf_rxeof(sc) struct sf_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct sf_rx_bufdesc_type0 *desc; struct sf_rx_cmpdesc_type3 *cur_rx; u_int32_t rxcons, rxprod; int cmpprodidx, cmpconsidx, bufprodidx; SF_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; rxcons = csr_read_4(sc, SF_CQ_CONSIDX); rxprod = csr_read_4(sc, SF_RXDQ_PTR_Q1); cmpprodidx = SF_IDX_LO(csr_read_4(sc, SF_CQ_PRODIDX)); cmpconsidx = SF_IDX_LO(rxcons); bufprodidx = SF_IDX_LO(rxprod); while (cmpconsidx != cmpprodidx) { struct mbuf *m0; cur_rx = &sc->sf_ldata->sf_rx_clist[cmpconsidx]; desc = &sc->sf_ldata->sf_rx_dlist_big[cur_rx->sf_endidx]; m = desc->sf_mbuf; SF_INC(cmpconsidx, SF_RX_CLIST_CNT); SF_INC(bufprodidx, SF_RX_DLIST_CNT); if (!(cur_rx->sf_status1 & SF_RXSTAT1_OK)) { ifp->if_ierrors++; sf_newbuf(sc, desc, m); continue; } m0 = m_devget(mtod(m, char *), cur_rx->sf_len, ETHER_ALIGN, ifp, NULL); sf_newbuf(sc, desc, m); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; ifp->if_ipackets++; SF_UNLOCK(sc); (*ifp->if_input)(ifp, m); SF_LOCK(sc); } csr_write_4(sc, SF_CQ_CONSIDX, (rxcons & ~SF_CQ_CONSIDX_RXQ1) | cmpconsidx); csr_write_4(sc, SF_RXDQ_PTR_Q1, (rxprod & ~SF_RXDQ_PRODIDX) | bufprodidx); return; } /* * Read the transmit status from the completion queue and release * mbufs. Note that the buffer descriptor index in the completion * descriptor is an offset from the start of the transmit buffer * descriptor list in bytes. This is important because the manual * gives the impression that it should match the producer/consumer * index, which is the offset in 8 byte blocks. */ static void sf_txeof(sc) struct sf_softc *sc; { int txcons, cmpprodidx, cmpconsidx; struct sf_tx_cmpdesc_type1 *cur_cmp; struct sf_tx_bufdesc_type0 *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; txcons = csr_read_4(sc, SF_CQ_CONSIDX); cmpprodidx = SF_IDX_HI(csr_read_4(sc, SF_CQ_PRODIDX)); cmpconsidx = SF_IDX_HI(txcons); while (cmpconsidx != cmpprodidx) { cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx]; cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7]; if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK) ifp->if_opackets++; else { if (cur_cmp->sf_txstat & SF_TXSTAT_TX_UNDERRUN) sf_txthresh_adjust(sc); ifp->if_oerrors++; } sc->sf_tx_cnt--; if (cur_tx->sf_mbuf != NULL) { m_freem(cur_tx->sf_mbuf); cur_tx->sf_mbuf = NULL; } else break; SF_INC(cmpconsidx, SF_TX_CLIST_CNT); } ifp->if_timer = 0; ifp->if_flags &= ~IFF_OACTIVE; csr_write_4(sc, SF_CQ_CONSIDX, (txcons & ~SF_CQ_CONSIDX_TXQ) | ((cmpconsidx << 16) & 0xFFFF0000)); return; } static void sf_txthresh_adjust(sc) struct sf_softc *sc; { u_int32_t txfctl; u_int8_t txthresh; txfctl = csr_read_4(sc, SF_TX_FRAMCTL); txthresh = txfctl & SF_TXFRMCTL_TXTHRESH; if (txthresh < 0xFF) { txthresh++; txfctl &= ~SF_TXFRMCTL_TXTHRESH; txfctl |= txthresh; #ifdef DIAGNOSTIC printf("sf%d: tx underrun, increasing " "tx threshold to %d bytes\n", sc->sf_unit, txthresh * 4); #endif csr_write_4(sc, SF_TX_FRAMCTL, txfctl); } return; } static void sf_intr(arg) void *arg; { struct sf_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; if (!(csr_read_4(sc, SF_ISR_SHADOW) & SF_ISR_PCIINT_ASSERTED)) { SF_UNLOCK(sc); return; } /* Disable interrupts. */ csr_write_4(sc, SF_IMR, 0x00000000); for (;;) { status = csr_read_4(sc, SF_ISR); if (status) csr_write_4(sc, SF_ISR, status); if (!(status & SF_INTRS)) break; if (status & SF_ISR_RXDQ1_DMADONE) sf_rxeof(sc); if (status & SF_ISR_TX_TXDONE || status & SF_ISR_TX_DMADONE || status & SF_ISR_TX_QUEUEDONE) sf_txeof(sc); if (status & SF_ISR_TX_LOFIFO) sf_txthresh_adjust(sc); if (status & SF_ISR_ABNORMALINTR) { if (status & SF_ISR_STATSOFLOW) { untimeout(sf_stats_update, sc, sc->sf_stat_ch); sf_stats_update(sc); } else sf_init(sc); } } /* Re-enable interrupts. */ csr_write_4(sc, SF_IMR, SF_INTRS); if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); SF_UNLOCK(sc); return; } static void sf_init(xsc) void *xsc; { struct sf_softc *sc; struct ifnet *ifp; struct mii_data *mii; int i; sc = xsc; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->sf_miibus); sf_stop(sc); sf_reset(sc); /* Init all the receive filter registers */ for (i = SF_RXFILT_PERFECT_BASE; i < (SF_RXFILT_HASH_MAX + 1); i += 4) csr_write_4(sc, i, 0); /* Empty stats counter registers. */ for (i = 0; i < sizeof(struct sf_stats)/sizeof(u_int32_t); i++) csr_write_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t)), 0); /* Init our MAC address */ csr_write_4(sc, SF_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0])); csr_write_4(sc, SF_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4])); sf_setperf(sc, 0, (caddr_t)&sc->arpcom.ac_enaddr); if (sf_init_rx_ring(sc) == ENOBUFS) { printf("sf%d: initialization failed: no " "memory for rx buffers\n", sc->sf_unit); SF_UNLOCK(sc); return; } sf_init_tx_ring(sc); csr_write_4(sc, SF_RXFILT, SF_PERFMODE_NORMAL|SF_HASHMODE_WITHVLAN); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } else { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC); } if (ifp->if_flags & IFF_BROADCAST) { SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); } else { SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_BROAD); } /* * Load the multicast filter. */ sf_setmulti(sc); /* Init the completion queue indexes */ csr_write_4(sc, SF_CQ_CONSIDX, 0); csr_write_4(sc, SF_CQ_PRODIDX, 0); /* Init the RX completion queue */ csr_write_4(sc, SF_RXCQ_CTL_1, vtophys(sc->sf_ldata->sf_rx_clist) & SF_RXCQ_ADDR); SF_SETBIT(sc, SF_RXCQ_CTL_1, SF_RXCQTYPE_3); /* Init RX DMA control. */ SF_SETBIT(sc, SF_RXDMA_CTL, SF_RXDMA_REPORTBADPKTS); /* Init the RX buffer descriptor queue. */ csr_write_4(sc, SF_RXDQ_ADDR_Q1, vtophys(sc->sf_ldata->sf_rx_dlist_big)); csr_write_4(sc, SF_RXDQ_CTL_1, (MCLBYTES << 16) | SF_DESCSPACE_16BYTES); csr_write_4(sc, SF_RXDQ_PTR_Q1, SF_RX_DLIST_CNT - 1); /* Init the TX completion queue */ csr_write_4(sc, SF_TXCQ_CTL, vtophys(sc->sf_ldata->sf_tx_clist) & SF_RXCQ_ADDR); /* Init the TX buffer descriptor queue. */ csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, vtophys(sc->sf_ldata->sf_tx_dlist)); SF_SETBIT(sc, SF_TX_FRAMCTL, SF_TXFRMCTL_CPLAFTERTX); csr_write_4(sc, SF_TXDQ_CTL, SF_TXBUFDESC_TYPE0|SF_TXMINSPACE_128BYTES|SF_TXSKIPLEN_8BYTES); SF_SETBIT(sc, SF_TXDQ_CTL, SF_TXDQCTL_NODMACMP); /* Enable autopadding of short TX frames. */ SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_AUTOPAD); /* Enable interrupts. */ csr_write_4(sc, SF_IMR, SF_INTRS); SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_INTR_ENB); /* Enable the RX and TX engines. */ SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_RX_ENB|SF_ETHCTL_RXDMA_ENB); SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_TX_ENB|SF_ETHCTL_TXDMA_ENB); /*mii_mediachg(mii);*/ sf_ifmedia_upd(ifp); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); SF_UNLOCK(sc); return; } static int sf_encap(sc, c, m_head) struct sf_softc *sc; struct sf_tx_bufdesc_type0 *c; struct mbuf *m_head; { int frag = 0; struct sf_frag *f = NULL; struct mbuf *m; m = m_head; for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == SF_MAXFRAGS) break; f = &c->sf_frags[frag]; if (frag == 0) f->sf_pktlen = m_head->m_pkthdr.len; f->sf_fraglen = m->m_len; f->sf_addr = vtophys(mtod(m, vm_offset_t)); frag++; } } if (m != NULL) { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) { printf("sf%d: no memory for tx list\n", sc->sf_unit); return(1); } if (m_head->m_pkthdr.len > MHLEN) { MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); printf("sf%d: no memory for tx list\n", sc->sf_unit); return(1); } } m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; m_freem(m_head); m_head = m_new; f = &c->sf_frags[0]; f->sf_fraglen = f->sf_pktlen = m_head->m_pkthdr.len; f->sf_addr = vtophys(mtod(m_head, caddr_t)); frag = 1; } c->sf_mbuf = m_head; c->sf_id = SF_TX_BUFDESC_ID; c->sf_fragcnt = frag; c->sf_intr = 1; c->sf_caltcp = 0; c->sf_crcen = 1; return(0); } static void sf_start(ifp) struct ifnet *ifp; { struct sf_softc *sc; struct sf_tx_bufdesc_type0 *cur_tx = NULL; struct mbuf *m_head = NULL; int i, txprod; sc = ifp->if_softc; SF_LOCK(sc); if (!sc->sf_link && ifp->if_snd.ifq_len < 10) { SF_UNLOCK(sc); return; } if (ifp->if_flags & IFF_OACTIVE) { SF_UNLOCK(sc); return; } txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); i = SF_IDX_HI(txprod) >> 4; if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { printf("sf%d: TX ring full, resetting\n", sc->sf_unit); sf_init(sc); txprod = csr_read_4(sc, SF_TXDQ_PRODIDX); i = SF_IDX_HI(txprod) >> 4; } while(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf == NULL) { if (sc->sf_tx_cnt >= (SF_TX_DLIST_CNT - 5)) { ifp->if_flags |= IFF_OACTIVE; cur_tx = NULL; break; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; cur_tx = &sc->sf_ldata->sf_tx_dlist[i]; if (sf_encap(sc, cur_tx, m_head)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; cur_tx = NULL; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); SF_INC(i, SF_TX_DLIST_CNT); sc->sf_tx_cnt++; /* * Don't get the TX DMA queue get too full. */ if (sc->sf_tx_cnt > 64) break; } if (cur_tx == NULL) { SF_UNLOCK(sc); return; } /* Transmit */ csr_write_4(sc, SF_TXDQ_PRODIDX, (txprod & ~SF_TXDQ_PRODIDX_HIPRIO) | ((i << 20) & 0xFFFF0000)); ifp->if_timer = 5; SF_UNLOCK(sc); return; } static void sf_stop(sc) struct sf_softc *sc; { int i; struct ifnet *ifp; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; untimeout(sf_stats_update, sc, sc->sf_stat_ch); csr_write_4(sc, SF_GEN_ETH_CTL, 0); csr_write_4(sc, SF_CQ_CONSIDX, 0); csr_write_4(sc, SF_CQ_PRODIDX, 0); csr_write_4(sc, SF_RXDQ_ADDR_Q1, 0); csr_write_4(sc, SF_RXDQ_CTL_1, 0); csr_write_4(sc, SF_RXDQ_PTR_Q1, 0); csr_write_4(sc, SF_TXCQ_CTL, 0); csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, 0); csr_write_4(sc, SF_TXDQ_CTL, 0); sf_reset(sc); sc->sf_link = 0; for (i = 0; i < SF_RX_DLIST_CNT; i++) { if (sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf != NULL) { m_freem(sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf); sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf = NULL; } } for (i = 0; i < SF_TX_DLIST_CNT; i++) { if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) { m_freem(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf); sc->sf_ldata->sf_tx_dlist[i].sf_mbuf = NULL; } } ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); SF_UNLOCK(sc); return; } /* * Note: it is important that this function not be interrupted. We * use a two-stage register access scheme: if we are interrupted in * between setting the indirect address register and reading from the * indirect data register, the contents of the address register could * be changed out from under us. */ static void sf_stats_update(xsc) void *xsc; { struct sf_softc *sc; struct ifnet *ifp; struct mii_data *mii; struct sf_stats stats; u_int32_t *ptr; int i; sc = xsc; SF_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->sf_miibus); ptr = (u_int32_t *)&stats; for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) ptr[i] = csr_read_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t))); for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++) csr_write_4(sc, SF_STATS_BASE + (i + sizeof(u_int32_t)), 0); ifp->if_collisions += stats.sf_tx_single_colls + stats.sf_tx_multi_colls + stats.sf_tx_excess_colls; mii_tick(mii); if (!sc->sf_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sf_link++; if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); } sc->sf_stat_ch = timeout(sf_stats_update, sc, hz); SF_UNLOCK(sc); return; } static void sf_watchdog(ifp) struct ifnet *ifp; { struct sf_softc *sc; sc = ifp->if_softc; SF_LOCK(sc); ifp->if_oerrors++; printf("sf%d: watchdog timeout\n", sc->sf_unit); sf_stop(sc); sf_reset(sc); sf_init(sc); if (ifp->if_snd.ifq_head != NULL) sf_start(ifp); SF_UNLOCK(sc); return; } static void sf_shutdown(dev) device_t dev; { struct sf_softc *sc; sc = device_get_softc(dev); sf_stop(sc); return; } Index: head/sys/pci/if_sis.c =================================================================== --- head/sys/pci/if_sis.c (revision 131252) +++ head/sys/pci/if_sis.c (revision 131253) @@ -1,2460 +1,2436 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * SiS 900/SiS 7016 fast ethernet PCI NIC driver. Datasheets are * available from http://www.sis.com.tw. * * This driver also supports the NatSemi DP83815. Datasheets are * available from http://www.national.com. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The SiS 900 is a fairly simple chip. It uses bus master DMA with * simple TX and RX descriptors of 3 longwords in size. The receiver * has a single perfect filter entry for the station address and a * 128-bit multicast hash table. The SiS 900 has a built-in MII-based * transceiver while the 7016 requires an external transceiver chip. * Both chips offer the standard bit-bang MII interface as well as * an enchanced PHY interface which simplifies accessing MII registers. * * The only downside to this chipset is that RX descriptors must be * longword aligned. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SIS_USEIOSPACE #include MODULE_DEPEND(sis, pci, 1, 1, 1); MODULE_DEPEND(sis, ether, 1, 1, 1); MODULE_DEPEND(sis, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" /* * Various supported device vendors/types and their names. */ static struct sis_type sis_devs[] = { { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP8381[56] 10/100BaseTX" }, { 0, 0, NULL } }; static int sis_probe (device_t); static int sis_attach (device_t); static int sis_detach (device_t); static int sis_newbuf (struct sis_softc *, struct sis_desc *, struct mbuf *); static int sis_encap (struct sis_softc *, struct mbuf **, u_int32_t *); static void sis_rxeof (struct sis_softc *); static void sis_rxeoc (struct sis_softc *); static void sis_txeof (struct sis_softc *); static void sis_intr (void *); static void sis_tick (void *); static void sis_start (struct ifnet *); static int sis_ioctl (struct ifnet *, u_long, caddr_t); static void sis_init (void *); static void sis_stop (struct sis_softc *); static void sis_watchdog (struct ifnet *); static void sis_shutdown (device_t); static int sis_ifmedia_upd (struct ifnet *); static void sis_ifmedia_sts (struct ifnet *, struct ifmediareq *); static u_int16_t sis_reverse (u_int16_t); static void sis_delay (struct sis_softc *); static void sis_eeprom_idle (struct sis_softc *); static void sis_eeprom_putbyte (struct sis_softc *, int); static void sis_eeprom_getword (struct sis_softc *, int, u_int16_t *); static void sis_read_eeprom (struct sis_softc *, caddr_t, int, int, int); #ifdef __i386__ static void sis_read_cmos (struct sis_softc *, device_t, caddr_t, int, int); static void sis_read_mac (struct sis_softc *, device_t, caddr_t); static device_t sis_find_bridge (device_t); #endif static void sis_mii_sync (struct sis_softc *); static void sis_mii_send (struct sis_softc *, u_int32_t, int); static int sis_mii_readreg (struct sis_softc *, struct sis_mii_frame *); static int sis_mii_writereg (struct sis_softc *, struct sis_mii_frame *); static int sis_miibus_readreg (device_t, int, int); static int sis_miibus_writereg (device_t, int, int, int); static void sis_miibus_statchg (device_t); static void sis_setmulti_sis (struct sis_softc *); static void sis_setmulti_ns (struct sis_softc *); static uint32_t sis_mchash (struct sis_softc *, const uint8_t *); static void sis_reset (struct sis_softc *); static int sis_list_rx_init (struct sis_softc *); static int sis_list_tx_init (struct sis_softc *); static void sis_dma_map_desc_ptr (void *, bus_dma_segment_t *, int, int); static void sis_dma_map_desc_next (void *, bus_dma_segment_t *, int, int); static void sis_dma_map_ring (void *, bus_dma_segment_t *, int, int); #ifdef SIS_USEIOSPACE #define SIS_RES SYS_RES_IOPORT #define SIS_RID SIS_PCI_LOIO #else #define SIS_RES SYS_RES_MEMORY #define SIS_RID SIS_PCI_LOMEM #endif static device_method_t sis_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sis_probe), DEVMETHOD(device_attach, sis_attach), DEVMETHOD(device_detach, sis_detach), DEVMETHOD(device_shutdown, sis_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sis_miibus_readreg), DEVMETHOD(miibus_writereg, sis_miibus_writereg), DEVMETHOD(miibus_statchg, sis_miibus_statchg), { 0, 0 } }; static driver_t sis_driver = { "sis", sis_methods, sizeof(struct sis_softc) }; static devclass_t sis_devclass; DRIVER_MODULE(sis, pci, sis_driver, sis_devclass, 0, 0); DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); #define SIS_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | (x)) #define SIS_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) \ CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) #define SIO_CLR(x) \ CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) static void sis_dma_map_desc_next(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { struct sis_desc *r; r = arg; r->sis_next = segs->ds_addr; return; } static void sis_dma_map_desc_ptr(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { struct sis_desc *r; r = arg; r->sis_ptr = segs->ds_addr; return; } static void sis_dma_map_ring(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { u_int32_t *p; p = arg; *p = segs->ds_addr; return; } /* * Routine to reverse the bits in a word. Stolen almost * verbatim from /usr/games/fortune. */ static u_int16_t sis_reverse(n) u_int16_t n; { n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); return(n); } static void sis_delay(sc) struct sis_softc *sc; { int idx; for (idx = (300 / 33) + 1; idx > 0; idx--) CSR_READ_4(sc, SIS_CSR); return; } static void sis_eeprom_idle(sc) struct sis_softc *sc; { register int i; SIO_SET(SIS_EECTL_CSEL); sis_delay(sc); SIO_SET(SIS_EECTL_CLK); sis_delay(sc); for (i = 0; i < 25; i++) { SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); SIO_SET(SIS_EECTL_CLK); sis_delay(sc); } SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); SIO_CLR(SIS_EECTL_CSEL); sis_delay(sc); CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); return; } /* * Send a read command and address to the EEPROM, check for ACK. */ static void sis_eeprom_putbyte(sc, addr) struct sis_softc *sc; int addr; { register int d, i; d = addr | SIS_EECMD_READ; /* * Feed in each bit and stobe the clock. */ for (i = 0x400; i; i >>= 1) { if (d & i) { SIO_SET(SIS_EECTL_DIN); } else { SIO_CLR(SIS_EECTL_DIN); } sis_delay(sc); SIO_SET(SIS_EECTL_CLK); sis_delay(sc); SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); } return; } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void sis_eeprom_getword(sc, addr, dest) struct sis_softc *sc; int addr; u_int16_t *dest; { register int i; u_int16_t word = 0; /* Force EEPROM to idle state. */ sis_eeprom_idle(sc); /* Enter EEPROM access mode. */ sis_delay(sc); SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); SIO_SET(SIS_EECTL_CSEL); sis_delay(sc); /* * Send address of word we want to read. */ sis_eeprom_putbyte(sc, addr); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { SIO_SET(SIS_EECTL_CLK); sis_delay(sc); if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) word |= i; sis_delay(sc); SIO_CLR(SIS_EECTL_CLK); sis_delay(sc); } /* Turn off EEPROM access mode. */ sis_eeprom_idle(sc); *dest = word; return; } /* * Read a sequence of words from the EEPROM. */ static void sis_read_eeprom(sc, dest, off, cnt, swap) struct sis_softc *sc; caddr_t dest; int off; int cnt; int swap; { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { sis_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return; } #ifdef __i386__ static device_t sis_find_bridge(dev) device_t dev; { devclass_t pci_devclass; device_t *pci_devices; int pci_count = 0; device_t *pci_children; int pci_childcount = 0; device_t *busp, *childp; device_t child = NULL; int i, j; if ((pci_devclass = devclass_find("pci")) == NULL) return(NULL); devclass_get_devices(pci_devclass, &pci_devices, &pci_count); for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) { pci_childcount = 0; device_get_children(*busp, &pci_children, &pci_childcount); for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) { if (pci_get_vendor(*childp) == SIS_VENDORID && pci_get_device(*childp) == 0x0008) { child = *childp; goto done; } } } done: free(pci_devices, M_TEMP); free(pci_children, M_TEMP); return(child); } static void sis_read_cmos(sc, dev, dest, off, cnt) struct sis_softc *sc; device_t dev; caddr_t dest; int off; int cnt; { device_t bridge; u_int8_t reg; int i; bus_space_tag_t btag; bridge = sis_find_bridge(dev); if (bridge == NULL) return; reg = pci_read_config(bridge, 0x48, 1); pci_write_config(bridge, 0x48, reg|0x40, 1); /* XXX */ btag = I386_BUS_SPACE_IO; for (i = 0; i < cnt; i++) { bus_space_write_1(btag, 0x0, 0x70, i + off); *(dest + i) = bus_space_read_1(btag, 0x0, 0x71); } pci_write_config(bridge, 0x48, reg & ~0x40, 1); return; } static void sis_read_mac(sc, dev, dest) struct sis_softc *sc; device_t dev; caddr_t dest; { u_int32_t filtsave, csrsave; filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); csrsave = CSR_READ_4(sc, SIS_CSR); CSR_WRITE_4(sc, SIS_CSR, SIS_CSR_RELOAD | filtsave); CSR_WRITE_4(sc, SIS_CSR, 0); CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave & ~SIS_RXFILTCTL_ENABLE); CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); ((u_int16_t *)dest)[0] = CSR_READ_2(sc, SIS_RXFILT_DATA); CSR_WRITE_4(sc, SIS_RXFILT_CTL,SIS_FILTADDR_PAR1); ((u_int16_t *)dest)[1] = CSR_READ_2(sc, SIS_RXFILT_DATA); CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); ((u_int16_t *)dest)[2] = CSR_READ_2(sc, SIS_RXFILT_DATA); CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); CSR_WRITE_4(sc, SIS_CSR, csrsave); return; } #endif /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void sis_mii_sync(sc) struct sis_softc *sc; { register int i; SIO_SET(SIS_MII_DIR|SIS_MII_DATA); for (i = 0; i < 32; i++) { SIO_SET(SIS_MII_CLK); DELAY(1); SIO_CLR(SIS_MII_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void sis_mii_send(sc, bits, cnt) struct sis_softc *sc; u_int32_t bits; int cnt; { int i; SIO_CLR(SIS_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { SIO_SET(SIS_MII_DATA); } else { SIO_CLR(SIS_MII_DATA); } DELAY(1); SIO_CLR(SIS_MII_CLK); DELAY(1); SIO_SET(SIS_MII_CLK); } } /* * Read an PHY register through the MII. */ static int sis_mii_readreg(sc, frame) struct sis_softc *sc; struct sis_mii_frame *frame; { int i, ack, s; s = splimp(); /* * Set up frame for RX. */ frame->mii_stdelim = SIS_MII_STARTDELIM; frame->mii_opcode = SIS_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Turn on data xmit. */ SIO_SET(SIS_MII_DIR); sis_mii_sync(sc); /* * Send command/address info. */ sis_mii_send(sc, frame->mii_stdelim, 2); sis_mii_send(sc, frame->mii_opcode, 2); sis_mii_send(sc, frame->mii_phyaddr, 5); sis_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ SIO_CLR((SIS_MII_CLK|SIS_MII_DATA)); DELAY(1); SIO_SET(SIS_MII_CLK); DELAY(1); /* Turn off xmit. */ SIO_CLR(SIS_MII_DIR); /* Check for ack */ SIO_CLR(SIS_MII_CLK); DELAY(1); ack = CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA; SIO_SET(SIS_MII_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { SIO_CLR(SIS_MII_CLK); DELAY(1); SIO_SET(SIS_MII_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { SIO_CLR(SIS_MII_CLK); DELAY(1); if (!ack) { if (CSR_READ_4(sc, SIS_EECTL) & SIS_MII_DATA) frame->mii_data |= i; DELAY(1); } SIO_SET(SIS_MII_CLK); DELAY(1); } fail: SIO_CLR(SIS_MII_CLK); DELAY(1); SIO_SET(SIS_MII_CLK); DELAY(1); splx(s); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int sis_mii_writereg(sc, frame) struct sis_softc *sc; struct sis_mii_frame *frame; { int s; s = splimp(); /* * Set up frame for TX. */ frame->mii_stdelim = SIS_MII_STARTDELIM; frame->mii_opcode = SIS_MII_WRITEOP; frame->mii_turnaround = SIS_MII_TURNAROUND; /* * Turn on data output. */ SIO_SET(SIS_MII_DIR); sis_mii_sync(sc); sis_mii_send(sc, frame->mii_stdelim, 2); sis_mii_send(sc, frame->mii_opcode, 2); sis_mii_send(sc, frame->mii_phyaddr, 5); sis_mii_send(sc, frame->mii_regaddr, 5); sis_mii_send(sc, frame->mii_turnaround, 2); sis_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ SIO_SET(SIS_MII_CLK); DELAY(1); SIO_CLR(SIS_MII_CLK); DELAY(1); /* * Turn off xmit. */ SIO_CLR(SIS_MII_DIR); splx(s); return(0); } static int sis_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct sis_softc *sc; struct sis_mii_frame frame; sc = device_get_softc(dev); if (sc->sis_type == SIS_TYPE_83815) { if (phy != 0) return(0); /* * The NatSemi chip can take a while after * a reset to come ready, during which the BMSR * returns a value of 0. This is *never* supposed * to happen: some of the BMSR bits are meant to * be hardwired in the on position, and this can * confuse the miibus code a bit during the probe * and attach phase. So we make an effort to check * for this condition and wait for it to clear. */ if (!CSR_READ_4(sc, NS_BMSR)) DELAY(1000); return CSR_READ_4(sc, NS_BMCR + (reg * 4)); } /* * Chipsets < SIS_635 seem not to be able to read/write * through mdio. Use the enhanced PHY access register * again for them. */ if (sc->sis_type == SIS_TYPE_900 && sc->sis_rev < SIS_REV_635) { int i, val = 0; if (phy != 0) return(0); CSR_WRITE_4(sc, SIS_PHYCTL, (phy << 11) | (reg << 6) | SIS_PHYOP_READ); SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); for (i = 0; i < SIS_TIMEOUT; i++) { if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) break; } if (i == SIS_TIMEOUT) { printf("sis%d: PHY failed to come ready\n", sc->sis_unit); return(0); } val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; if (val == 0xFFFF) return(0); return(val); } else { bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; sis_mii_readreg(sc, &frame); return(frame.mii_data); } } static int sis_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct sis_softc *sc; struct sis_mii_frame frame; sc = device_get_softc(dev); if (sc->sis_type == SIS_TYPE_83815) { if (phy != 0) return(0); CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); return(0); } /* * Chipsets < SIS_635 seem not to be able to read/write * through mdio. Use the enhanced PHY access register * again for them. */ if (sc->sis_type == SIS_TYPE_900 && sc->sis_rev < SIS_REV_635) { int i; if (phy != 0) return(0); CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | (reg << 6) | SIS_PHYOP_WRITE); SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); for (i = 0; i < SIS_TIMEOUT; i++) { if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) break; } if (i == SIS_TIMEOUT) printf("sis%d: PHY failed to come ready\n", sc->sis_unit); } else { bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; sis_mii_writereg(sc, &frame); } return(0); } static void sis_miibus_statchg(dev) device_t dev; { struct sis_softc *sc; sc = device_get_softc(dev); sis_init(sc); return; } static u_int32_t sis_mchash(sc, addr) struct sis_softc *sc; const uint8_t *addr; { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_be(addr, ETHER_ADDR_LEN); /* * return the filter bit position * * The NatSemi chip has a 512-bit filter, which is * different than the SiS, so we special-case it. */ if (sc->sis_type == SIS_TYPE_83815) return (crc >> 23); else if (sc->sis_rev >= SIS_REV_635 || sc->sis_rev == SIS_REV_900B) return (crc >> 24); else return (crc >> 25); } static void sis_setmulti_ns(sc) struct sis_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int32_t h = 0, i, filtsave; int bit, index; ifp = &sc->arpcom.ac_if; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); return; } /* * We have to explicitly enable the multicast hash table * on the NatSemi chip if we want to use it, which we do. */ SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); /* first, zot all the existing hash bits */ for (i = 0; i < 32; i++) { CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); } TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = sis_mchash(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); index = h >> 3; bit = h & 0x1F; CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); if (bit > 0xF) bit -= 0x10; SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); } CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); return; } static void sis_setmulti_sis(sc) struct sis_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int32_t h, i, n, ctl; u_int16_t hashes[16]; ifp = &sc->arpcom.ac_if; /* hash table size */ if (sc->sis_rev >= SIS_REV_635 || sc->sis_rev == SIS_REV_900B) n = 16; else n = 8; ctl = CSR_READ_4(sc, SIS_RXFILT_CTL) & SIS_RXFILTCTL_ENABLE; if (ifp->if_flags & IFF_BROADCAST) ctl |= SIS_RXFILTCTL_BROAD; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { ctl |= SIS_RXFILTCTL_ALLMULTI; if (ifp->if_flags & IFF_PROMISC) ctl |= SIS_RXFILTCTL_BROAD|SIS_RXFILTCTL_ALLPHYS; for (i = 0; i < n; i++) hashes[i] = ~0; } else { for (i = 0; i < n; i++) hashes[i] = 0; i = 0; TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = sis_mchash(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); hashes[h >> 4] |= 1 << (h & 0xf); i++; } if (i > n) { ctl |= SIS_RXFILTCTL_ALLMULTI; for (i = 0; i < n; i++) hashes[i] = ~0; } } for (i = 0; i < n; i++) { CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + i) << 16); CSR_WRITE_4(sc, SIS_RXFILT_DATA, hashes[i]); } CSR_WRITE_4(sc, SIS_RXFILT_CTL, ctl); } static void sis_reset(sc) struct sis_softc *sc; { register int i; SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); for (i = 0; i < SIS_TIMEOUT; i++) { if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) break; } if (i == SIS_TIMEOUT) printf("sis%d: reset never completed\n", sc->sis_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); /* * If this is a NetSemi chip, make sure to clear * PME mode. */ if (sc->sis_type == SIS_TYPE_83815) { CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS); CSR_WRITE_4(sc, NS_CLKRUN, 0); } return; } /* * Probe for an SiS chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int sis_probe(dev) device_t dev; { struct sis_type *t; t = sis_devs; while(t->sis_name != NULL) { if ((pci_get_vendor(dev) == t->sis_vid) && (pci_get_device(dev) == t->sis_did)) { device_set_desc(dev, t->sis_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int sis_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; struct sis_softc *sc; struct ifnet *ifp; int unit, error = 0, rid, waittime = 0; waittime = 0; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->sis_self = dev; mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); if (pci_get_device(dev) == SIS_DEVICEID_900) sc->sis_type = SIS_TYPE_900; if (pci_get_device(dev) == SIS_DEVICEID_7016) sc->sis_type = SIS_TYPE_7016; if (pci_get_vendor(dev) == NS_VENDORID) sc->sis_type = SIS_TYPE_83815; sc->sis_rev = pci_read_config(dev, PCIR_REVID, 1); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, SIS_PCI_LOIO, 4); - membase = pci_read_config(dev, SIS_PCI_LOMEM, 4); - irq = pci_read_config(dev, SIS_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("sis%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, SIS_PCI_LOIO, iobase, 4); - pci_write_config(dev, SIS_PCI_LOMEM, membase, 4); - pci_write_config(dev, SIS_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = SIS_RID; sc->sis_res = bus_alloc_resource_any(dev, SIS_RES, &rid, RF_ACTIVE); if (sc->sis_res == NULL) { printf("sis%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->sis_btag = rman_get_bustag(sc->sis_res); sc->sis_bhandle = rman_get_bushandle(sc->sis_res); /* Allocate interrupt */ rid = 0; sc->sis_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sis_irq == NULL) { printf("sis%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ sis_reset(sc); if (sc->sis_type == SIS_TYPE_900 && (sc->sis_rev == SIS_REV_635 || sc->sis_rev == SIS_REV_900B)) { SIO_SET(SIS_CFG_RND_CNT); SIO_SET(SIS_CFG_PERR_DETECT); } /* * Get station address from the EEPROM. */ switch (pci_get_vendor(dev)) { case NS_VENDORID: sc->sis_srr = CSR_READ_4(sc, NS_SRR); /* We can't update the device description, so spew */ if (sc->sis_srr == NS_SRR_15C) device_printf(dev, "Silicon Revision: DP83815C\n"); else if (sc->sis_srr == NS_SRR_15D) device_printf(dev, "Silicon Revision: DP83815D\n"); else if (sc->sis_srr == NS_SRR_16A) device_printf(dev, "Silicon Revision: DP83816A\n"); else device_printf(dev, "Silicon Revision %x\n", sc->sis_srr); /* * Reading the MAC address out of the EEPROM on * the NatSemi chip takes a bit more work than * you'd expect. The address spans 4 16-bit words, * with the first word containing only a single bit. * You have to shift everything over one bit to * get it aligned properly. Also, the bits are * stored backwards (the LSB is really the MSB, * and so on) so you have to reverse them in order * to get the MAC address into the form we want. * Why? Who the hell knows. */ { u_int16_t tmp[4]; sis_read_eeprom(sc, (caddr_t)&tmp, NS_EE_NODEADDR, 4, 0); /* Shift everything over one bit. */ tmp[3] = tmp[3] >> 1; tmp[3] |= tmp[2] << 15; tmp[2] = tmp[2] >> 1; tmp[2] |= tmp[1] << 15; tmp[1] = tmp[1] >> 1; tmp[1] |= tmp[0] << 15; /* Now reverse all the bits. */ tmp[3] = sis_reverse(tmp[3]); tmp[2] = sis_reverse(tmp[2]); tmp[1] = sis_reverse(tmp[1]); bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN); } break; case SIS_VENDORID: default: #ifdef __i386__ /* * If this is a SiS 630E chipset with an embedded * SiS 900 controller, we have to read the MAC address * from the APC CMOS RAM. Our method for doing this * is very ugly since we have to reach out and grab * ahold of hardware for which we cannot properly * allocate resources. This code is only compiled on * the i386 architecture since the SiS 630E chipset * is for x86 motherboards only. Note that there are * a lot of magic numbers in this hack. These are * taken from SiS's Linux driver. I'd like to replace * them with proper symbolic definitions, but that * requires some datasheets that I don't have access * to at the moment. */ if (sc->sis_rev == SIS_REV_630S || sc->sis_rev == SIS_REV_630E || sc->sis_rev == SIS_REV_630EA1) sis_read_cmos(sc, dev, (caddr_t)&eaddr, 0x9, 6); else if (sc->sis_rev == SIS_REV_635 || sc->sis_rev == SIS_REV_630ET) sis_read_mac(sc, dev, (caddr_t)&eaddr); else if (sc->sis_rev == SIS_REV_96x) { /* Allow to read EEPROM from LAN. It is shared * between a 1394 controller and the NIC and each * time we access it, we need to set SIS_EECMD_REQ. */ SIO_SET(SIS_EECMD_REQ); for (waittime = 0; waittime < SIS_TIMEOUT; waittime++) { /* Force EEPROM to idle state. */ sis_eeprom_idle(sc); if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECMD_GNT) { sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0); break; } DELAY(1); } /* * Set SIS_EECTL_CLK to high, so a other master * can operate on the i2c bus. */ SIO_SET(SIS_EECTL_CLK); /* Refuse EEPROM access by LAN */ SIO_SET(SIS_EECMD_DONE); } else #endif sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0); break; } sc->sis_unit = unit; if (debug_mpsafenet) callout_init(&sc->sis_stat_ch, CALLOUT_MPSAFE); else callout_init(&sc->sis_stat_ch, 0); bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* * Allocate the parent bus DMA tag appropriate for PCI. */ #define SIS_NSEG_NEW 32 error = bus_dma_tag_create(NULL, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->sis_parent_tag); if (error) goto fail; /* * Now allocate a tag for the DMA descriptor lists and a chunk * of DMA-able memory based on the tag. Also obtain the physical * addresses of the RX and TX ring, which we'll need later. * All of our lists are allocated as a contiguous block * of memory. */ error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ SIS_RX_LIST_SZ, 1, /* maxsize,nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &Giant, /* lockarg */ &sc->sis_ldata.sis_rx_tag); if (error) goto fail; error = bus_dmamem_alloc(sc->sis_ldata.sis_rx_tag, (void **)&sc->sis_ldata.sis_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->sis_ldata.sis_rx_dmamap); if (error) { printf("sis%d: no memory for rx list buffers!\n", unit); bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); sc->sis_ldata.sis_rx_tag = NULL; goto fail; } error = bus_dmamap_load(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap, &(sc->sis_ldata.sis_rx_list[0]), sizeof(struct sis_desc), sis_dma_map_ring, &sc->sis_cdata.sis_rx_paddr, 0); if (error) { printf("sis%d: cannot get address of the rx ring!\n", unit); bus_dmamem_free(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); sc->sis_ldata.sis_rx_tag = NULL; goto fail; } error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &Giant, /* lockarg */ &sc->sis_ldata.sis_tx_tag); if (error) goto fail; error = bus_dmamem_alloc(sc->sis_ldata.sis_tx_tag, (void **)&sc->sis_ldata.sis_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->sis_ldata.sis_tx_dmamap); if (error) { printf("sis%d: no memory for tx list buffers!\n", unit); bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); sc->sis_ldata.sis_tx_tag = NULL; goto fail; } error = bus_dmamap_load(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_dmamap, &(sc->sis_ldata.sis_tx_list[0]), sizeof(struct sis_desc), sis_dma_map_ring, &sc->sis_cdata.sis_tx_paddr, 0); if (error) { printf("sis%d: cannot get address of the tx ring!\n", unit); bus_dmamem_free(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); sc->sis_ldata.sis_tx_tag = NULL; goto fail; } error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 1, 0, /* alignment, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MCLBYTES, 1, /* maxsize,nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ &Giant, /* lockarg */ &sc->sis_tag); if (error) goto fail; /* * Obtain the physical addresses of the RX and TX * rings which we'll need later in the init routine. */ ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sis_ioctl; ifp->if_start = sis_start; ifp->if_watchdog = sis_watchdog; ifp->if_init = sis_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = SIS_TX_LIST_CNT - 1; /* * Do MII setup. */ if (mii_phy_probe(dev, &sc->sis_miibus, sis_ifmedia_upd, sis_ifmedia_sts)) { printf("sis%d: MII without any PHY!\n", sc->sis_unit); error = ENXIO; goto fail; } /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* * Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->sis_irq, INTR_TYPE_NET | INTR_MPSAFE, sis_intr, sc, &sc->sis_intrhand); if (error) { printf("sis%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) sis_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int sis_detach(dev) device_t dev; { struct sis_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sis_mtx), ("sis mutex not initialized")); SIS_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded. */ if (device_is_attached(dev)) { sis_reset(sc); sis_stop(sc); ether_ifdetach(ifp); } if (sc->sis_miibus) device_delete_child(dev, sc->sis_miibus); bus_generic_detach(dev); if (sc->sis_intrhand) bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); if (sc->sis_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); if (sc->sis_res) bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); if (sc->sis_ldata.sis_rx_tag) { bus_dmamap_unload(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap); bus_dmamem_free(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); } if (sc->sis_ldata.sis_tx_tag) { bus_dmamap_unload(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_dmamap); bus_dmamem_free(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); } if (sc->sis_parent_tag) bus_dma_tag_destroy(sc->sis_parent_tag); if (sc->sis_tag) bus_dma_tag_destroy(sc->sis_tag); SIS_UNLOCK(sc); mtx_destroy(&sc->sis_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int sis_list_tx_init(sc) struct sis_softc *sc; { struct sis_list_data *ld; struct sis_ring_data *cd; int i, nexti; cd = &sc->sis_cdata; ld = &sc->sis_ldata; for (i = 0; i < SIS_TX_LIST_CNT; i++) { nexti = (i == (SIS_TX_LIST_CNT - 1)) ? 0 : i+1; ld->sis_tx_list[i].sis_nextdesc = &ld->sis_tx_list[nexti]; bus_dmamap_load(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_dmamap, &ld->sis_tx_list[nexti], sizeof(struct sis_desc), sis_dma_map_desc_next, &ld->sis_tx_list[i], 0); ld->sis_tx_list[i].sis_mbuf = NULL; ld->sis_tx_list[i].sis_ptr = 0; ld->sis_tx_list[i].sis_ctl = 0; } cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; bus_dmamap_sync(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_rx_dmamap, BUS_DMASYNC_PREWRITE); return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int sis_list_rx_init(sc) struct sis_softc *sc; { struct sis_list_data *ld; struct sis_ring_data *cd; int i,nexti; ld = &sc->sis_ldata; cd = &sc->sis_cdata; for (i = 0; i < SIS_RX_LIST_CNT; i++) { if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS) return(ENOBUFS); nexti = (i == (SIS_RX_LIST_CNT - 1)) ? 0 : i+1; ld->sis_rx_list[i].sis_nextdesc = &ld->sis_rx_list[nexti]; bus_dmamap_load(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap, &ld->sis_rx_list[nexti], sizeof(struct sis_desc), sis_dma_map_desc_next, &ld->sis_rx_list[i], 0); } bus_dmamap_sync(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap, BUS_DMASYNC_PREWRITE); cd->sis_rx_prod = 0; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int sis_newbuf(sc, c, m) struct sis_softc *sc; struct sis_desc *c; struct mbuf *m; { if (c == NULL) return(EINVAL); if (m == NULL) { m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m == NULL) return(ENOBUFS); } else m->m_data = m->m_ext.ext_buf; c->sis_mbuf = m; c->sis_ctl = SIS_RXLEN; bus_dmamap_create(sc->sis_tag, 0, &c->sis_map); bus_dmamap_load(sc->sis_tag, c->sis_map, mtod(m, void *), MCLBYTES, sis_dma_map_desc_ptr, c, 0); bus_dmamap_sync(sc->sis_tag, c->sis_map, BUS_DMASYNC_PREWRITE); return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void sis_rxeof(sc) struct sis_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct sis_desc *cur_rx; int i, total_len = 0; u_int32_t rxstat; SIS_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; i = sc->sis_cdata.sis_rx_prod; while(SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif /* DEVICE_POLLING */ cur_rx = &sc->sis_ldata.sis_rx_list[i]; rxstat = cur_rx->sis_rxstat; bus_dmamap_sync(sc->sis_tag, cur_rx->sis_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->sis_tag, cur_rx->sis_map); bus_dmamap_destroy(sc->sis_tag, cur_rx->sis_map); m = cur_rx->sis_mbuf; cur_rx->sis_mbuf = NULL; total_len = SIS_RXBYTES(cur_rx); SIS_INC(i, SIS_RX_LIST_CNT); /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (!(rxstat & SIS_CMDSTS_PKT_OK)) { ifp->if_ierrors++; if (rxstat & SIS_RXSTAT_COLL) ifp->if_collisions++; sis_newbuf(sc, cur_rx, m); continue; } /* No errors; receive the packet. */ #ifdef __i386__ /* * On the x86 we do not have alignment problems, so try to * allocate a new buffer for the receive ring, and pass up * the one where the packet is already, saving the expensive * copy done in m_devget(). * If we are on an architecture with alignment problems, or * if the allocation fails, then use m_devget and leave the * existing buffer in the receive ring. */ if (sis_newbuf(sc, cur_rx, NULL) == 0) m->m_pkthdr.len = m->m_len = total_len; else #endif { struct mbuf *m0; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); sis_newbuf(sc, cur_rx, m); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; } ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; SIS_UNLOCK(sc); (*ifp->if_input)(ifp, m); SIS_LOCK(sc); } sc->sis_cdata.sis_rx_prod = i; return; } static void sis_rxeoc(sc) struct sis_softc *sc; { sis_rxeof(sc); sis_init(sc); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void sis_txeof(sc) struct sis_softc *sc; { struct ifnet *ifp; u_int32_t idx; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ for (idx = sc->sis_cdata.sis_tx_cons; sc->sis_cdata.sis_tx_cnt > 0; sc->sis_cdata.sis_tx_cnt--, SIS_INC(idx, SIS_TX_LIST_CNT) ) { struct sis_desc *cur_tx = &sc->sis_ldata.sis_tx_list[idx]; if (SIS_OWNDESC(cur_tx)) break; if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) continue; if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { ifp->if_oerrors++; if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) ifp->if_collisions++; if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) ifp->if_collisions++; } ifp->if_collisions += (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; ifp->if_opackets++; if (cur_tx->sis_mbuf != NULL) { m_freem(cur_tx->sis_mbuf); cur_tx->sis_mbuf = NULL; bus_dmamap_unload(sc->sis_tag, cur_tx->sis_map); bus_dmamap_destroy(sc->sis_tag, cur_tx->sis_map); } } if (idx != sc->sis_cdata.sis_tx_cons) { /* we freed up some buffers */ sc->sis_cdata.sis_tx_cons = idx; ifp->if_flags &= ~IFF_OACTIVE; } ifp->if_timer = (sc->sis_cdata.sis_tx_cnt == 0) ? 0 : 5; return; } static void sis_tick(xsc) void *xsc; { struct sis_softc *sc; struct mii_data *mii; struct ifnet *ifp; sc = xsc; SIS_LOCK(sc); sc->in_tick = 1; ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->sis_miibus); mii_tick(mii); if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->sis_link++; if (ifp->if_snd.ifq_head != NULL) sis_start(ifp); } callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); sc->in_tick = 0; SIS_UNLOCK(sc); return; } #ifdef DEVICE_POLLING static poll_handler_t sis_poll; static void sis_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct sis_softc *sc = ifp->if_softc; SIS_LOCK(sc); if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ CSR_WRITE_4(sc, SIS_IER, 1); goto done; } /* * On the sis, reading the status register also clears it. * So before returning to intr mode we must make sure that all * possible pending sources of interrupts have been served. * In practice this means run to completion the *eof routines, * and then call the interrupt routine */ sc->rxcycles = count; sis_rxeof(sc); sis_txeof(sc); if (ifp->if_snd.ifq_head != NULL) sis_start(ifp); if (sc->rxcycles > 0 || cmd == POLL_AND_CHECK_STATUS) { u_int32_t status; /* Reading the ISR register clears all interrupts. */ status = CSR_READ_4(sc, SIS_ISR); if (status & (SIS_ISR_RX_ERR|SIS_ISR_RX_OFLOW)) sis_rxeoc(sc); if (status & (SIS_ISR_RX_IDLE)) SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); if (status & SIS_ISR_SYSERR) { sis_reset(sc); sis_init(sc); } } done: SIS_UNLOCK(sc); return; } #endif /* DEVICE_POLLING */ static void sis_intr(arg) void *arg; { struct sis_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; ifp = &sc->arpcom.ac_if; SIS_LOCK(sc); #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(sis_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_4(sc, SIS_IER, 0); goto done; } #endif /* DEVICE_POLLING */ /* Supress unwanted interrupts */ if (!(ifp->if_flags & IFF_UP)) { sis_stop(sc); goto done; } /* Disable interrupts. */ CSR_WRITE_4(sc, SIS_IER, 0); for (;;) { /* Reading the ISR register clears all interrupts. */ status = CSR_READ_4(sc, SIS_ISR); if ((status & SIS_INTRS) == 0) break; if (status & (SIS_ISR_TX_DESC_OK | SIS_ISR_TX_ERR | SIS_ISR_TX_OK | SIS_ISR_TX_IDLE) ) sis_txeof(sc); if (status & (SIS_ISR_RX_DESC_OK|SIS_ISR_RX_OK|SIS_ISR_RX_IDLE)) sis_rxeof(sc); if (status & (SIS_ISR_RX_ERR | SIS_ISR_RX_OFLOW)) sis_rxeoc(sc); if (status & (SIS_ISR_RX_IDLE)) SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); if (status & SIS_ISR_SYSERR) { sis_reset(sc); sis_init(sc); } } /* Re-enable interrupts. */ CSR_WRITE_4(sc, SIS_IER, 1); if (ifp->if_snd.ifq_head != NULL) sis_start(ifp); done: SIS_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int sis_encap(sc, m_head, txidx) struct sis_softc *sc; struct mbuf **m_head; u_int32_t *txidx; { struct sis_desc *f = NULL; struct mbuf *m; int frag, cur, cnt = 0, chainlen = 0; /* * If there's no way we can send any packets, return now. */ if (SIS_TX_LIST_CNT - sc->sis_cdata.sis_tx_cnt < 2) return (ENOBUFS); /* * Count the number of frags in this chain to see if * we need to m_defrag. Since the descriptor list is shared * by all packets, we'll m_defrag long chains so that they * do not use up the entire list, even if they would fit. */ for (m = *m_head; m != NULL; m = m->m_next) chainlen++; if ((chainlen > SIS_TX_LIST_CNT / 4) || ((SIS_TX_LIST_CNT - (chainlen + sc->sis_cdata.sis_tx_cnt)) < 2)) { m = m_defrag(*m_head, M_DONTWAIT); if (m == NULL) return (ENOBUFS); *m_head = m; } /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ cur = frag = *txidx; for (m = *m_head; m != NULL; m = m->m_next) { if (m->m_len != 0) { if ((SIS_TX_LIST_CNT - (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) return(ENOBUFS); f = &sc->sis_ldata.sis_tx_list[frag]; f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; bus_dmamap_create(sc->sis_tag, 0, &f->sis_map); bus_dmamap_load(sc->sis_tag, f->sis_map, mtod(m, void *), m->m_len, sis_dma_map_desc_ptr, f, 0); bus_dmamap_sync(sc->sis_tag, f->sis_map, BUS_DMASYNC_PREREAD); if (cnt != 0) f->sis_ctl |= SIS_CMDSTS_OWN; cur = frag; SIS_INC(frag, SIS_TX_LIST_CNT); cnt++; } } if (m != NULL) return(ENOBUFS); sc->sis_ldata.sis_tx_list[cur].sis_mbuf = *m_head; sc->sis_ldata.sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; sc->sis_ldata.sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; sc->sis_cdata.sis_tx_cnt += cnt; *txidx = frag; return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void sis_start(ifp) struct ifnet *ifp; { struct sis_softc *sc; struct mbuf *m_head = NULL; u_int32_t idx; sc = ifp->if_softc; SIS_LOCK(sc); if (!sc->sis_link) { SIS_UNLOCK(sc); return; } idx = sc->sis_cdata.sis_tx_prod; if (ifp->if_flags & IFF_OACTIVE) { SIS_UNLOCK(sc); return; } while(sc->sis_ldata.sis_tx_list[idx].sis_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; if (sis_encap(sc, &m_head, &idx)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); } /* Transmit */ sc->sis_cdata.sis_tx_prod = idx; SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; SIS_UNLOCK(sc); return; } static void sis_init(xsc) void *xsc; { struct sis_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; SIS_LOCK(sc); /* * Cancel pending I/O and free all RX/TX buffers. */ sis_stop(sc); sc->sis_stopped = 0; #ifdef notyet if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr >= NS_SRR_16A) { /* * Configure 400usec of interrupt holdoff. This is based * on emperical tests on a Soekris 4801. */ CSR_WRITE_4(sc, NS_IHR, 0x100 | 4); } #endif mii = device_get_softc(sc->sis_miibus); /* Set MAC address */ if (sc->sis_type == SIS_TYPE_83815) { CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); } else { CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); CSR_WRITE_4(sc, SIS_RXFILT_DATA, ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); } /* Init circular RX list. */ if (sis_list_rx_init(sc) == ENOBUFS) { printf("sis%d: initialization failed: no " "memory for rx buffers\n", sc->sis_unit); sis_stop(sc); SIS_UNLOCK(sc); return; } /* * Init tx descriptors. */ sis_list_tx_init(sc); /* * Page 78 of the DP83815 data sheet (september 2002 version) * recommends the following register settings "for optimum * performance." for rev 15C. The driver from NS also sets * the PHY_CR register for later versions. */ if (sc->sis_type == SIS_TYPE_83815) { CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); /* DC speed = 01 */ CSR_WRITE_4(sc, NS_PHY_CR, 0x189C); if (sc->sis_srr == NS_SRR_15C) { /* set val for c2 */ CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000); /* load/kill c2 */ CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040); /* rais SD off, from 4 to c */ CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C); } CSR_WRITE_4(sc, NS_PHY_PAGE, 0); } /* * For the NatSemi chip, we have to explicitly enable the * reception of ARP frames, as well as turn on the 'perfect * match' filter where we store the station address, otherwise * we won't receive unicasts meant for this host. */ if (sc->sis_type == SIS_TYPE_83815) { SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); } /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); } else { SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); } /* * Set the capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); } else { SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); } /* * Load the multicast filter. */ if (sc->sis_type == SIS_TYPE_83815) sis_setmulti_ns(sc); else sis_setmulti_sis(sc); /* Turn the receive filter on */ SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); /* * Load the address of the RX and TX lists. */ CSR_WRITE_4(sc, SIS_RX_LISTPTR, sc->sis_cdata.sis_rx_paddr); CSR_WRITE_4(sc, SIS_TX_LISTPTR, sc->sis_cdata.sis_tx_paddr); /* SIS_CFG_EDB_MASTER_EN indicates the EDB bus is used instead of * the PCI bus. When this bit is set, the Max DMA Burst Size * for TX/RX DMA should be no larger than 16 double words. */ if (CSR_READ_4(sc, SIS_CFG) & SIS_CFG_EDB_MASTER_EN) { CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG64); } else { CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG256); } /* Accept Long Packets for VLAN support */ SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_JABBER); /* Set TX configuration */ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10); } else { CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); } /* Set full/half duplex mode. */ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { SIS_SETBIT(sc, SIS_TX_CFG, (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); } else { SIS_CLRBIT(sc, SIS_TX_CFG, (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); } if (sc->sis_type == SIS_TYPE_83815 && sc->sis_srr < NS_SRR_16A && IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { uint32_t reg; /* * Some DP83815s experience problems when used with short * (< 30m/100ft) Ethernet cables in 100BaseTX mode. This * sequence adjusts the DSP's signal attenuation to fix the * problem. */ CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); reg = CSR_READ_4(sc, NS_PHY_DSPCFG); /* Allow coefficient to be read */ CSR_WRITE_4(sc, NS_PHY_DSPCFG, (reg & 0xfff) | 0x1000); DELAY(100); reg = CSR_READ_4(sc, NS_PHY_TDATA); if ((reg & 0x0080) == 0 || (reg > 0xd8 && reg <= 0xff)) { device_printf(sc->sis_self, "Applying short cable fix (reg=%x)\n", reg); CSR_WRITE_4(sc, NS_PHY_TDATA, 0x00e8); /* Adjust coefficient and prevent change */ SIS_SETBIT(sc, NS_PHY_DSPCFG, 0x20); } CSR_WRITE_4(sc, NS_PHY_PAGE, 0); } /* * Enable interrupts. */ CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); #ifdef DEVICE_POLLING /* * ... only enable interrupts if we are not polling, make sure * they are off otherwise. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_4(sc, SIS_IER, 0); else #endif /* DEVICE_POLLING */ CSR_WRITE_4(sc, SIS_IER, 1); /* Enable receiver and transmitter. */ SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); #ifdef notdef mii_mediachg(mii); #endif ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; if (!sc->in_tick) callout_reset(&sc->sis_stat_ch, hz, sis_tick, sc); SIS_UNLOCK(sc); return; } /* * Set media options. */ static int sis_ifmedia_upd(ifp) struct ifnet *ifp; { struct sis_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sis_miibus); sc->sis_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return(0); } /* * Report current media status. */ static void sis_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sis_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->sis_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int sis_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sis_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { sis_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) sis_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: SIS_LOCK(sc); if (sc->sis_type == SIS_TYPE_83815) sis_setmulti_ns(sc); else sis_setmulti_sis(sc); SIS_UNLOCK(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->sis_miibus); SIS_LOCK(sc); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); SIS_UNLOCK(sc); break; case SIOCSIFCAP: ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING; break; default: error = ether_ioctl(ifp, command, data); break; } return(error); } static void sis_watchdog(ifp) struct ifnet *ifp; { struct sis_softc *sc; sc = ifp->if_softc; SIS_LOCK(sc); ifp->if_oerrors++; printf("sis%d: watchdog timeout\n", sc->sis_unit); sis_stop(sc); sis_reset(sc); sis_init(sc); if (ifp->if_snd.ifq_head != NULL) sis_start(ifp); SIS_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void sis_stop(sc) struct sis_softc *sc; { register int i; struct ifnet *ifp; if (sc->sis_stopped) return; SIS_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; callout_stop(&sc->sis_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif CSR_WRITE_4(sc, SIS_IER, 0); CSR_WRITE_4(sc, SIS_IMR, 0); SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); DELAY(1000); CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); sc->sis_link = 0; /* * Free data in the RX lists. */ for (i = 0; i < SIS_RX_LIST_CNT; i++) { if (sc->sis_ldata.sis_rx_list[i].sis_mbuf != NULL) { bus_dmamap_unload(sc->sis_tag, sc->sis_ldata.sis_rx_list[i].sis_map); bus_dmamap_destroy(sc->sis_tag, sc->sis_ldata.sis_rx_list[i].sis_map); m_freem(sc->sis_ldata.sis_rx_list[i].sis_mbuf); sc->sis_ldata.sis_rx_list[i].sis_mbuf = NULL; } } bzero(sc->sis_ldata.sis_rx_list, sizeof(sc->sis_ldata.sis_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < SIS_TX_LIST_CNT; i++) { if (sc->sis_ldata.sis_tx_list[i].sis_mbuf != NULL) { bus_dmamap_unload(sc->sis_tag, sc->sis_ldata.sis_tx_list[i].sis_map); bus_dmamap_destroy(sc->sis_tag, sc->sis_ldata.sis_tx_list[i].sis_map); m_freem(sc->sis_ldata.sis_tx_list[i].sis_mbuf); sc->sis_ldata.sis_tx_list[i].sis_mbuf = NULL; } } bzero(sc->sis_ldata.sis_tx_list, sizeof(sc->sis_ldata.sis_tx_list)); sc->sis_stopped = 1; SIS_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void sis_shutdown(dev) device_t dev; { struct sis_softc *sc; sc = device_get_softc(dev); SIS_LOCK(sc); sis_reset(sc); sis_stop(sc); SIS_UNLOCK(sc); return; } Index: head/sys/pci/if_sk.c =================================================================== --- head/sys/pci/if_sk.c (revision 131252) +++ head/sys/pci/if_sk.c (revision 131253) @@ -1,2695 +1,2671 @@ /* $OpenBSD: if_sk.c,v 2.33 2003/08/12 05:23:06 nate Exp $ */ /* * Copyright (c) 1997, 1998, 1999, 2000 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 2003 Nathan L. Binkert * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __FBSDID("$FreeBSD$"); /* * SysKonnect SK-NET gigabit ethernet driver for FreeBSD. Supports * the SK-984x series adapters, both single port and dual port. * References: * The XaQti XMAC II datasheet, * http://www.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf * The SysKonnect GEnesis manual, http://www.syskonnect.com * * Note: XaQti has been aquired by Vitesse, and Vitesse does not have the * XMAC II datasheet online. I have put my copy at people.freebsd.org as a * convenience to others until Vitesse corrects this problem: * * http://people.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf * * Written by Bill Paul * Department of Electrical Engineering * Columbia University, New York City */ /* * The SysKonnect gigabit ethernet adapters consist of two main * components: the SysKonnect GEnesis controller chip and the XaQti Corp. * XMAC II gigabit ethernet MAC. The XMAC provides all of the MAC * components and a PHY while the GEnesis controller provides a PCI * interface with DMA support. Each card may have between 512K and * 2MB of SRAM on board depending on the configuration. * * The SysKonnect GEnesis controller can have either one or two XMAC * chips connected to it, allowing single or dual port NIC configurations. * SysKonnect has the distinction of being the only vendor on the market * with a dual port gigabit ethernet NIC. The GEnesis provides dual FIFOs, * dual DMA queues, packet/MAC/transmit arbiters and direct access to the * XMAC registers. This driver takes advantage of these features to allow * both XMACs to operate as independent interfaces. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include #include #if 0 #define SK_USEIOSPACE #endif #include #include #include MODULE_DEPEND(sk, pci, 1, 1, 1); MODULE_DEPEND(sk, ether, 1, 1, 1); MODULE_DEPEND(sk, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif static struct sk_type sk_devs[] = { { VENDORID_SK, DEVICEID_SK_V1, "SysKonnect Gigabit Ethernet (V1.0)" }, { VENDORID_SK, DEVICEID_SK_V2, "SysKonnect Gigabit Ethernet (V2.0)" }, { VENDORID_MARVELL, DEVICEID_SK_V2, "Marvell Gigabit Ethernet" }, { VENDORID_3COM, DEVICEID_3COM_3C940, "3Com 3C940 Gigabit Ethernet" }, { VENDORID_LINKSYS, DEVICEID_LINKSYS_EG1032, "Linksys EG1032 Gigabit Ethernet" }, { VENDORID_DLINK, DEVICEID_DLINK_DGE530T, "D-Link DGE-530T Gigabit Ethernet" }, { 0, 0, NULL } }; static int skc_probe (device_t); static int skc_attach (device_t); static int skc_detach (device_t); static void skc_shutdown (device_t); static int sk_detach (device_t); static int sk_probe (device_t); static int sk_attach (device_t); static void sk_tick (void *); static void sk_intr (void *); static void sk_intr_xmac (struct sk_if_softc *); static void sk_intr_bcom (struct sk_if_softc *); static void sk_intr_yukon (struct sk_if_softc *); static void sk_rxeof (struct sk_if_softc *); static void sk_txeof (struct sk_if_softc *); static int sk_encap (struct sk_if_softc *, struct mbuf *, u_int32_t *); static void sk_start (struct ifnet *); static int sk_ioctl (struct ifnet *, u_long, caddr_t); static void sk_init (void *); static void sk_init_xmac (struct sk_if_softc *); static void sk_init_yukon (struct sk_if_softc *); static void sk_stop (struct sk_if_softc *); static void sk_watchdog (struct ifnet *); static int sk_ifmedia_upd (struct ifnet *); static void sk_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void sk_reset (struct sk_softc *); static int sk_newbuf (struct sk_if_softc *, struct sk_chain *, struct mbuf *); static int sk_alloc_jumbo_mem (struct sk_if_softc *); static void *sk_jalloc (struct sk_if_softc *); static void sk_jfree (void *, void *); static int sk_init_rx_ring (struct sk_if_softc *); static void sk_init_tx_ring (struct sk_if_softc *); static u_int32_t sk_win_read_4 (struct sk_softc *, int); static u_int16_t sk_win_read_2 (struct sk_softc *, int); static u_int8_t sk_win_read_1 (struct sk_softc *, int); static void sk_win_write_4 (struct sk_softc *, int, u_int32_t); static void sk_win_write_2 (struct sk_softc *, int, u_int32_t); static void sk_win_write_1 (struct sk_softc *, int, u_int32_t); static u_int8_t sk_vpd_readbyte (struct sk_softc *, int); static void sk_vpd_read_res (struct sk_softc *, struct vpd_res *, int); static void sk_vpd_read (struct sk_softc *); static int sk_miibus_readreg (device_t, int, int); static int sk_miibus_writereg (device_t, int, int, int); static void sk_miibus_statchg (device_t); static int sk_xmac_miibus_readreg (struct sk_if_softc *, int, int); static int sk_xmac_miibus_writereg (struct sk_if_softc *, int, int, int); static void sk_xmac_miibus_statchg (struct sk_if_softc *); static int sk_marv_miibus_readreg (struct sk_if_softc *, int, int); static int sk_marv_miibus_writereg (struct sk_if_softc *, int, int, int); static void sk_marv_miibus_statchg (struct sk_if_softc *); static uint32_t sk_xmchash (const uint8_t *); static uint32_t sk_gmchash (const uint8_t *); static void sk_setfilt (struct sk_if_softc *, caddr_t, int); static void sk_setmulti (struct sk_if_softc *); static void sk_setpromisc (struct sk_if_softc *); #ifdef SK_USEIOSPACE #define SK_RES SYS_RES_IOPORT #define SK_RID SK_PCI_LOIO #else #define SK_RES SYS_RES_MEMORY #define SK_RID SK_PCI_LOMEM #endif /* * Note that we have newbus methods for both the GEnesis controller * itself and the XMAC(s). The XMACs are children of the GEnesis, and * the miibus code is a child of the XMACs. We need to do it this way * so that the miibus drivers can access the PHY registers on the * right PHY. It's not quite what I had in mind, but it's the only * design that achieves the desired effect. */ static device_method_t skc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, skc_probe), DEVMETHOD(device_attach, skc_attach), DEVMETHOD(device_detach, skc_detach), DEVMETHOD(device_shutdown, skc_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), { 0, 0 } }; static driver_t skc_driver = { "skc", skc_methods, sizeof(struct sk_softc) }; static devclass_t skc_devclass; static device_method_t sk_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sk_probe), DEVMETHOD(device_attach, sk_attach), DEVMETHOD(device_detach, sk_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, sk_miibus_readreg), DEVMETHOD(miibus_writereg, sk_miibus_writereg), DEVMETHOD(miibus_statchg, sk_miibus_statchg), { 0, 0 } }; static driver_t sk_driver = { "sk", sk_methods, sizeof(struct sk_if_softc) }; static devclass_t sk_devclass; DRIVER_MODULE(sk, pci, skc_driver, skc_devclass, 0, 0); DRIVER_MODULE(sk, skc, sk_driver, sk_devclass, 0, 0); DRIVER_MODULE(miibus, sk, miibus_driver, miibus_devclass, 0, 0); #define SK_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x) #define SK_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~x) #define SK_WIN_SETBIT_4(sc, reg, x) \ sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) | x) #define SK_WIN_CLRBIT_4(sc, reg, x) \ sk_win_write_4(sc, reg, sk_win_read_4(sc, reg) & ~x) #define SK_WIN_SETBIT_2(sc, reg, x) \ sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) | x) #define SK_WIN_CLRBIT_2(sc, reg, x) \ sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) & ~x) static u_int32_t sk_win_read_4(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_4(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_4(sc, reg)); #endif } static u_int16_t sk_win_read_2(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_2(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_2(sc, reg)); #endif } static u_int8_t sk_win_read_1(sc, reg) struct sk_softc *sc; int reg; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); return(CSR_READ_1(sc, SK_WIN_BASE + SK_REG(reg))); #else return(CSR_READ_1(sc, reg)); #endif } static void sk_win_write_4(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_4(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_4(sc, reg, val); #endif return; } static void sk_win_write_2(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_2(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_2(sc, reg, val); #endif return; } static void sk_win_write_1(sc, reg, val) struct sk_softc *sc; int reg; u_int32_t val; { #ifdef SK_USEIOSPACE CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg)); CSR_WRITE_1(sc, SK_WIN_BASE + SK_REG(reg), val); #else CSR_WRITE_1(sc, reg, val); #endif return; } /* * The VPD EEPROM contains Vital Product Data, as suggested in * the PCI 2.1 specification. The VPD data is separared into areas * denoted by resource IDs. The SysKonnect VPD contains an ID string * resource (the name of the adapter), a read-only area resource * containing various key/data fields and a read/write area which * can be used to store asset management information or log messages. * We read the ID string and read-only into buffers attached to * the controller softc structure for later use. At the moment, * we only use the ID string during skc_attach(). */ static u_int8_t sk_vpd_readbyte(sc, addr) struct sk_softc *sc; int addr; { int i; sk_win_write_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR), addr); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (sk_win_read_2(sc, SK_PCI_REG(SK_PCI_VPD_ADDR)) & SK_VPD_FLAG) break; } if (i == SK_TIMEOUT) return(0); return(sk_win_read_1(sc, SK_PCI_REG(SK_PCI_VPD_DATA))); } static void sk_vpd_read_res(sc, res, addr) struct sk_softc *sc; struct vpd_res *res; int addr; { int i; u_int8_t *ptr; ptr = (u_int8_t *)res; for (i = 0; i < sizeof(struct vpd_res); i++) ptr[i] = sk_vpd_readbyte(sc, i + addr); return; } static void sk_vpd_read(sc) struct sk_softc *sc; { int pos = 0, i; struct vpd_res res; if (sc->sk_vpd_prodname != NULL) free(sc->sk_vpd_prodname, M_DEVBUF); if (sc->sk_vpd_readonly != NULL) free(sc->sk_vpd_readonly, M_DEVBUF); sc->sk_vpd_prodname = NULL; sc->sk_vpd_readonly = NULL; sk_vpd_read_res(sc, &res, pos); /* * Bail out quietly if the eeprom appears to be missing or empty. */ if (res.vr_id == 0xff && res.vr_len == 0xff && res.vr_pad == 0xff) return; if (res.vr_id != VPD_RES_ID) { printf("skc%d: bad VPD resource id: expected %x got %x\n", sc->sk_unit, VPD_RES_ID, res.vr_id); return; } pos += sizeof(res); sc->sk_vpd_prodname = malloc(res.vr_len + 1, M_DEVBUF, M_NOWAIT); for (i = 0; i < res.vr_len; i++) sc->sk_vpd_prodname[i] = sk_vpd_readbyte(sc, i + pos); sc->sk_vpd_prodname[i] = '\0'; pos += i; sk_vpd_read_res(sc, &res, pos); if (res.vr_id != VPD_RES_READ) { printf("skc%d: bad VPD resource id: expected %x got %x\n", sc->sk_unit, VPD_RES_READ, res.vr_id); return; } pos += sizeof(res); sc->sk_vpd_readonly = malloc(res.vr_len, M_DEVBUF, M_NOWAIT); for (i = 0; i < res.vr_len + 1; i++) sc->sk_vpd_readonly[i] = sk_vpd_readbyte(sc, i + pos); return; } static int sk_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: return(sk_xmac_miibus_readreg(sc_if, phy, reg)); case SK_YUKON: return(sk_marv_miibus_readreg(sc_if, phy, reg)); } return(0); } static int sk_miibus_writereg(dev, phy, reg, val) device_t dev; int phy, reg, val; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: return(sk_xmac_miibus_writereg(sc_if, phy, reg, val)); case SK_YUKON: return(sk_marv_miibus_writereg(sc_if, phy, reg, val)); } return(0); } static void sk_miibus_statchg(dev) device_t dev; { struct sk_if_softc *sc_if; sc_if = device_get_softc(dev); switch(sc_if->sk_softc->sk_type) { case SK_GENESIS: sk_xmac_miibus_statchg(sc_if); break; case SK_YUKON: sk_marv_miibus_statchg(sc_if); break; } return; } static int sk_xmac_miibus_readreg(sc_if, phy, reg) struct sk_if_softc *sc_if; int phy, reg; { int i; if (sc_if->sk_phytype == SK_PHYTYPE_XMAC && phy != 0) return(0); SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); SK_XM_READ_2(sc_if, XM_PHY_DATA); if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYDATARDY) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(0); } } DELAY(1); i = SK_XM_READ_2(sc_if, XM_PHY_DATA); SK_IF_UNLOCK(sc_if); return(i); } static int sk_xmac_miibus_writereg(sc_if, phy, reg, val) struct sk_if_softc *sc_if; int phy, reg, val; { int i; SK_IF_LOCK(sc_if); SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8)); for (i = 0; i < SK_TIMEOUT; i++) { if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(ETIMEDOUT); } SK_XM_WRITE_2(sc_if, XM_PHY_DATA, val); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY)) break; } SK_IF_UNLOCK(sc_if); if (i == SK_TIMEOUT) printf("sk%d: phy write timed out\n", sc_if->sk_unit); return(0); } static void sk_xmac_miibus_statchg(sc_if) struct sk_if_softc *sc_if; { struct mii_data *mii; mii = device_get_softc(sc_if->sk_miibus); SK_IF_LOCK(sc_if); /* * If this is a GMII PHY, manually set the XMAC's * duplex mode accordingly. */ if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) { if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); } else { SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX); } } SK_IF_UNLOCK(sc_if); return; } static int sk_marv_miibus_readreg(sc_if, phy, reg) struct sk_if_softc *sc_if; int phy, reg; { u_int16_t val; int i; if (phy != 0 || (sc_if->sk_phytype != SK_PHYTYPE_MARV_COPPER && sc_if->sk_phytype != SK_PHYTYPE_MARV_FIBER)) { return(0); } SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_READ); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); val = SK_YU_READ_2(sc_if, YUKON_SMICR); if (val & YU_SMICR_READ_VALID) break; } if (i == SK_TIMEOUT) { printf("sk%d: phy failed to come ready\n", sc_if->sk_unit); SK_IF_UNLOCK(sc_if); return(0); } val = SK_YU_READ_2(sc_if, YUKON_SMIDR); SK_IF_UNLOCK(sc_if); return(val); } static int sk_marv_miibus_writereg(sc_if, phy, reg, val) struct sk_if_softc *sc_if; int phy, reg, val; { int i; SK_IF_LOCK(sc_if); SK_YU_WRITE_2(sc_if, YUKON_SMIDR, val); SK_YU_WRITE_2(sc_if, YUKON_SMICR, YU_SMICR_PHYAD(phy) | YU_SMICR_REGAD(reg) | YU_SMICR_OP_WRITE); for (i = 0; i < SK_TIMEOUT; i++) { DELAY(1); if (SK_YU_READ_2(sc_if, YUKON_SMICR) & YU_SMICR_BUSY) break; } SK_IF_UNLOCK(sc_if); return(0); } static void sk_marv_miibus_statchg(sc_if) struct sk_if_softc *sc_if; { return; } #define HASH_BITS 6 static u_int32_t sk_xmchash(addr) const uint8_t *addr; { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_le(addr, ETHER_ADDR_LEN); return (~crc & ((1 << HASH_BITS) - 1)); } /* gmchash is just a big endian crc */ static u_int32_t sk_gmchash(addr) const uint8_t *addr; { uint32_t crc; /* Compute CRC for the address value. */ crc = ether_crc32_be(addr, ETHER_ADDR_LEN); return (crc & ((1 << HASH_BITS) - 1)); } static void sk_setfilt(sc_if, addr, slot) struct sk_if_softc *sc_if; caddr_t addr; int slot; { int base; base = XM_RXFILT_ENTRY(slot); SK_XM_WRITE_2(sc_if, base, *(u_int16_t *)(&addr[0])); SK_XM_WRITE_2(sc_if, base + 2, *(u_int16_t *)(&addr[2])); SK_XM_WRITE_2(sc_if, base + 4, *(u_int16_t *)(&addr[4])); return; } static void sk_setmulti(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc = sc_if->sk_softc; struct ifnet *ifp = &sc_if->arpcom.ac_if; u_int32_t hashes[2] = { 0, 0 }; int h = 0, i; struct ifmultiaddr *ifma; u_int8_t dummy[] = { 0, 0, 0, 0, 0 ,0 }; /* First, zot all the existing filters. */ switch(sc->sk_type) { case SK_GENESIS: for (i = 1; i < XM_RXFILT_MAX; i++) sk_setfilt(sc_if, (caddr_t)&dummy, i); SK_XM_WRITE_4(sc_if, XM_MAR0, 0); SK_XM_WRITE_4(sc_if, XM_MAR2, 0); break; case SK_YUKON: SK_YU_WRITE_2(sc_if, YUKON_MCAH1, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH2, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH3, 0); SK_YU_WRITE_2(sc_if, YUKON_MCAH4, 0); break; } /* Now program new ones. */ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { hashes[0] = 0xFFFFFFFF; hashes[1] = 0xFFFFFFFF; } else { i = 1; TAILQ_FOREACH_REVERSE(ifma, &ifp->if_multiaddrs, ifmultihead, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Program the first XM_RXFILT_MAX multicast groups * into the perfect filter. For all others, * use the hash table. */ if (sc->sk_type == SK_GENESIS && i < XM_RXFILT_MAX) { sk_setfilt(sc_if, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), i); i++; continue; } switch(sc->sk_type) { case SK_GENESIS: h = sk_xmchash( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); break; case SK_YUKON: h = sk_gmchash( LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); break; } if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } } switch(sc->sk_type) { case SK_GENESIS: SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_HASH| XM_MODE_RX_USE_PERFECT); SK_XM_WRITE_4(sc_if, XM_MAR0, hashes[0]); SK_XM_WRITE_4(sc_if, XM_MAR2, hashes[1]); break; case SK_YUKON: SK_YU_WRITE_2(sc_if, YUKON_MCAH1, hashes[0] & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH2, (hashes[0] >> 16) & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH3, hashes[1] & 0xffff); SK_YU_WRITE_2(sc_if, YUKON_MCAH4, (hashes[1] >> 16) & 0xffff); break; } return; } static void sk_setpromisc(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc = sc_if->sk_softc; struct ifnet *ifp = &sc_if->arpcom.ac_if; switch(sc->sk_type) { case SK_GENESIS: if (ifp->if_flags & IFF_PROMISC) { SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); } else { SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_PROMISC); } break; case SK_YUKON: if (ifp->if_flags & IFF_PROMISC) { SK_YU_CLRBIT_2(sc_if, YUKON_RCR, YU_RCR_UFLEN | YU_RCR_MUFLEN); } else { SK_YU_SETBIT_2(sc_if, YUKON_RCR, YU_RCR_UFLEN | YU_RCR_MUFLEN); } break; } return; } static int sk_init_rx_ring(sc_if) struct sk_if_softc *sc_if; { struct sk_chain_data *cd = &sc_if->sk_cdata; struct sk_ring_data *rd = sc_if->sk_rdata; int i; bzero((char *)rd->sk_rx_ring, sizeof(struct sk_rx_desc) * SK_RX_RING_CNT); for (i = 0; i < SK_RX_RING_CNT; i++) { cd->sk_rx_chain[i].sk_desc = &rd->sk_rx_ring[i]; if (sk_newbuf(sc_if, &cd->sk_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (SK_RX_RING_CNT - 1)) { cd->sk_rx_chain[i].sk_next = &cd->sk_rx_chain[0]; rd->sk_rx_ring[i].sk_next = vtophys(&rd->sk_rx_ring[0]); } else { cd->sk_rx_chain[i].sk_next = &cd->sk_rx_chain[i + 1]; rd->sk_rx_ring[i].sk_next = vtophys(&rd->sk_rx_ring[i + 1]); } } sc_if->sk_cdata.sk_rx_prod = 0; sc_if->sk_cdata.sk_rx_cons = 0; return(0); } static void sk_init_tx_ring(sc_if) struct sk_if_softc *sc_if; { struct sk_chain_data *cd = &sc_if->sk_cdata; struct sk_ring_data *rd = sc_if->sk_rdata; int i; bzero((char *)sc_if->sk_rdata->sk_tx_ring, sizeof(struct sk_tx_desc) * SK_TX_RING_CNT); for (i = 0; i < SK_TX_RING_CNT; i++) { cd->sk_tx_chain[i].sk_desc = &rd->sk_tx_ring[i]; if (i == (SK_TX_RING_CNT - 1)) { cd->sk_tx_chain[i].sk_next = &cd->sk_tx_chain[0]; rd->sk_tx_ring[i].sk_next = vtophys(&rd->sk_tx_ring[0]); } else { cd->sk_tx_chain[i].sk_next = &cd->sk_tx_chain[i + 1]; rd->sk_tx_ring[i].sk_next = vtophys(&rd->sk_tx_ring[i + 1]); } } sc_if->sk_cdata.sk_tx_prod = 0; sc_if->sk_cdata.sk_tx_cons = 0; sc_if->sk_cdata.sk_tx_cnt = 0; return; } static int sk_newbuf(sc_if, c, m) struct sk_if_softc *sc_if; struct sk_chain *c; struct mbuf *m; { struct mbuf *m_new = NULL; struct sk_rx_desc *r; if (m == NULL) { caddr_t *buf = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); /* Allocate the jumbo buffer */ buf = sk_jalloc(sc_if); if (buf == NULL) { m_freem(m_new); #ifdef SK_VERBOSE printf("sk%d: jumbo allocation failed " "-- packet dropped!\n", sc_if->sk_unit); #endif return(ENOBUFS); } /* Attach the buffer to the mbuf */ MEXTADD(m_new, buf, SK_JLEN, sk_jfree, (struct sk_if_softc *)sc_if, 0, EXT_NET_DRV); m_new->m_data = (void *)buf; m_new->m_pkthdr.len = m_new->m_len = SK_JLEN; } else { /* * We're re-using a previously allocated mbuf; * be sure to re-init pointers and lengths to * default values. */ m_new = m; m_new->m_len = m_new->m_pkthdr.len = SK_JLEN; m_new->m_data = m_new->m_ext.ext_buf; } /* * Adjust alignment so packet payload begins on a * longword boundary. Mandatory for Alpha, useful on * x86 too. */ m_adj(m_new, ETHER_ALIGN); r = c->sk_desc; c->sk_mbuf = m_new; r->sk_data_lo = vtophys(mtod(m_new, caddr_t)); r->sk_ctl = m_new->m_len | SK_RXSTAT; return(0); } /* * Allocate jumbo buffer storage. The SysKonnect adapters support * "jumbograms" (9K frames), although SysKonnect doesn't currently * use them in their drivers. In order for us to use them, we need * large 9K receive buffers, however standard mbuf clusters are only * 2048 bytes in size. Consequently, we need to allocate and manage * our own jumbo buffer pool. Fortunately, this does not require an * excessive amount of additional code. */ static int sk_alloc_jumbo_mem(sc_if) struct sk_if_softc *sc_if; { caddr_t ptr; register int i; struct sk_jpool_entry *entry; /* Grab a big chunk o' storage. */ sc_if->sk_cdata.sk_jumbo_buf = contigmalloc(SK_JMEM, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc_if->sk_cdata.sk_jumbo_buf == NULL) { printf("sk%d: no memory for jumbo buffers!\n", sc_if->sk_unit); return(ENOBUFS); } SLIST_INIT(&sc_if->sk_jfree_listhead); SLIST_INIT(&sc_if->sk_jinuse_listhead); /* * Now divide it up into 9K pieces and save the addresses * in an array. */ ptr = sc_if->sk_cdata.sk_jumbo_buf; for (i = 0; i < SK_JSLOTS; i++) { sc_if->sk_cdata.sk_jslots[i] = ptr; ptr += SK_JLEN; entry = malloc(sizeof(struct sk_jpool_entry), M_DEVBUF, M_NOWAIT); if (entry == NULL) { free(sc_if->sk_cdata.sk_jumbo_buf, M_DEVBUF); sc_if->sk_cdata.sk_jumbo_buf = NULL; printf("sk%d: no memory for jumbo " "buffer queue!\n", sc_if->sk_unit); return(ENOBUFS); } entry->slot = i; SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); } return(0); } /* * Allocate a jumbo buffer. */ static void * sk_jalloc(sc_if) struct sk_if_softc *sc_if; { struct sk_jpool_entry *entry; entry = SLIST_FIRST(&sc_if->sk_jfree_listhead); if (entry == NULL) { #ifdef SK_VERBOSE printf("sk%d: no free jumbo buffers\n", sc_if->sk_unit); #endif return(NULL); } SLIST_REMOVE_HEAD(&sc_if->sk_jfree_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc_if->sk_jinuse_listhead, entry, jpool_entries); return(sc_if->sk_cdata.sk_jslots[entry->slot]); } /* * Release a jumbo buffer. */ static void sk_jfree(buf, args) void *buf; void *args; { struct sk_if_softc *sc_if; int i; struct sk_jpool_entry *entry; /* Extract the softc struct pointer. */ sc_if = (struct sk_if_softc *)args; if (sc_if == NULL) panic("sk_jfree: didn't get softc pointer!"); /* calculate the slot this buffer belongs to */ i = ((vm_offset_t)buf - (vm_offset_t)sc_if->sk_cdata.sk_jumbo_buf) / SK_JLEN; if ((i < 0) || (i >= SK_JSLOTS)) panic("sk_jfree: asked to free buffer that we don't manage!"); entry = SLIST_FIRST(&sc_if->sk_jinuse_listhead); if (entry == NULL) panic("sk_jfree: buffer not in use!"); entry->slot = i; SLIST_REMOVE_HEAD(&sc_if->sk_jinuse_listhead, jpool_entries); SLIST_INSERT_HEAD(&sc_if->sk_jfree_listhead, entry, jpool_entries); return; } /* * Set media options. */ static int sk_ifmedia_upd(ifp) struct ifnet *ifp; { struct sk_if_softc *sc_if = ifp->if_softc; struct mii_data *mii; mii = device_get_softc(sc_if->sk_miibus); sk_init(sc_if); mii_mediachg(mii); return(0); } /* * Report current media status. */ static void sk_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct sk_if_softc *sc_if; struct mii_data *mii; sc_if = ifp->if_softc; mii = device_get_softc(sc_if->sk_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int sk_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct sk_if_softc *sc_if = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int error = 0; struct mii_data *mii; SK_IF_LOCK(sc_if); switch(command) { case SIOCSIFMTU: if (ifr->ifr_mtu > SK_JUMBO_MTU) error = EINVAL; else { ifp->if_mtu = ifr->ifr_mtu; sk_init(sc_if); } break; case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING) { if ((ifp->if_flags ^ sc_if->sk_if_flags) & IFF_PROMISC) { sk_setpromisc(sc_if); sk_setmulti(sc_if); } } else sk_init(sc_if); } else { if (ifp->if_flags & IFF_RUNNING) sk_stop(sc_if); } sc_if->sk_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: sk_setmulti(sc_if); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc_if->sk_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } SK_IF_UNLOCK(sc_if); return(error); } /* * Probe for a SysKonnect GEnesis chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int skc_probe(dev) device_t dev; { struct sk_softc *sc; struct sk_type *t = sk_devs; sc = device_get_softc(dev); while(t->sk_name != NULL) { if ((pci_get_vendor(dev) == t->sk_vid) && (pci_get_device(dev) == t->sk_did)) { device_set_desc(dev, t->sk_name); return(0); } t++; } return(ENXIO); } /* * Force the GEnesis into reset, then bring it out of reset. */ static void sk_reset(sc) struct sk_softc *sc; { CSR_WRITE_2(sc, SK_CSR, SK_CSR_SW_RESET); CSR_WRITE_2(sc, SK_CSR, SK_CSR_MASTER_RESET); if (sc->sk_type == SK_YUKON) CSR_WRITE_2(sc, SK_LINK_CTRL, SK_LINK_RESET_SET); DELAY(1000); CSR_WRITE_2(sc, SK_CSR, SK_CSR_SW_UNRESET); DELAY(2); CSR_WRITE_2(sc, SK_CSR, SK_CSR_MASTER_UNRESET); if (sc->sk_type == SK_YUKON) CSR_WRITE_2(sc, SK_LINK_CTRL, SK_LINK_RESET_CLEAR); if (sc->sk_type == SK_GENESIS) { /* Configure packet arbiter */ sk_win_write_2(sc, SK_PKTARB_CTL, SK_PKTARBCTL_UNRESET); sk_win_write_2(sc, SK_RXPA1_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_TXPA1_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_RXPA2_TINIT, SK_PKTARB_TIMEOUT); sk_win_write_2(sc, SK_TXPA2_TINIT, SK_PKTARB_TIMEOUT); } /* Enable RAM interface */ sk_win_write_4(sc, SK_RAMCTL, SK_RAMCTL_UNRESET); /* * Configure interrupt moderation. The moderation timer * defers interrupts specified in the interrupt moderation * timer mask based on the timeout specified in the interrupt * moderation timer init register. Each bit in the timer * register represents 18.825ns, so to specify a timeout in * microseconds, we have to multiply by 54. */ sk_win_write_4(sc, SK_IMTIMERINIT, SK_IM_USECS(200)); sk_win_write_4(sc, SK_IMMR, SK_ISR_TX1_S_EOF|SK_ISR_TX2_S_EOF| SK_ISR_RX1_EOF|SK_ISR_RX2_EOF); sk_win_write_1(sc, SK_IMTIMERCTL, SK_IMCTL_START); return; } static int sk_probe(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(device_get_parent(dev)); /* * Not much to do here. We always know there will be * at least one XMAC present, and if there are two, * skc_attach() will create a second device instance * for us. */ switch (sc->sk_type) { case SK_GENESIS: device_set_desc(dev, "XaQti Corp. XMAC II"); break; case SK_YUKON: device_set_desc(dev, "Marvell Semiconductor, Inc. Yukon"); break; } return(0); } /* * Each XMAC chip is attached as a separate logical IP interface. * Single port cards will have only one logical interface of course. */ static int sk_attach(dev) device_t dev; { struct sk_softc *sc; struct sk_if_softc *sc_if; struct ifnet *ifp; int i, port, error; if (dev == NULL) return(EINVAL); error = 0; sc_if = device_get_softc(dev); sc = device_get_softc(device_get_parent(dev)); SK_LOCK(sc); port = *(int *)device_get_ivars(dev); free(device_get_ivars(dev), M_DEVBUF); device_set_ivars(dev, NULL); sc_if->sk_dev = dev; sc_if->sk_unit = device_get_unit(dev); sc_if->sk_port = port; sc_if->sk_softc = sc; sc->sk_if[port] = sc_if; if (port == SK_PORT_A) sc_if->sk_tx_bmu = SK_BMU_TXS_CSR0; if (port == SK_PORT_B) sc_if->sk_tx_bmu = SK_BMU_TXS_CSR1; /* * Get station address for this interface. Note that * dual port cards actually come with three station * addresses: one for each port, plus an extra. The * extra one is used by the SysKonnect driver software * as a 'virtual' station address for when both ports * are operating in failover mode. Currently we don't * use this extra address. */ for (i = 0; i < ETHER_ADDR_LEN; i++) sc_if->arpcom.ac_enaddr[i] = sk_win_read_1(sc, SK_MAC0_0 + (port * 8) + i); /* * Set up RAM buffer addresses. The NIC will have a certain * amount of SRAM on it, somewhere between 512K and 2MB. We * need to divide this up a) between the transmitter and * receiver and b) between the two XMACs, if this is a * dual port NIC. Our algotithm is to divide up the memory * evenly so that everyone gets a fair share. */ if (sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC) { u_int32_t chunk, val; chunk = sc->sk_ramsize / 2; val = sc->sk_rboff / sizeof(u_int64_t); sc_if->sk_rx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_rx_ramend = val - 1; sc_if->sk_tx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_tx_ramend = val - 1; } else { u_int32_t chunk, val; chunk = sc->sk_ramsize / 4; val = (sc->sk_rboff + (chunk * 2 * sc_if->sk_port)) / sizeof(u_int64_t); sc_if->sk_rx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_rx_ramend = val - 1; sc_if->sk_tx_ramstart = val; val += (chunk / sizeof(u_int64_t)); sc_if->sk_tx_ramend = val - 1; } /* Read and save PHY type and set PHY address */ sc_if->sk_phytype = sk_win_read_1(sc, SK_EPROM1) & 0xF; switch(sc_if->sk_phytype) { case SK_PHYTYPE_XMAC: sc_if->sk_phyaddr = SK_PHYADDR_XMAC; break; case SK_PHYTYPE_BCOM: sc_if->sk_phyaddr = SK_PHYADDR_BCOM; break; case SK_PHYTYPE_MARV_COPPER: sc_if->sk_phyaddr = SK_PHYADDR_MARV; break; default: printf("skc%d: unsupported PHY type: %d\n", sc->sk_unit, sc_if->sk_phytype); error = ENODEV; goto fail; } /* Allocate the descriptor queues. */ sc_if->sk_rdata = contigmalloc(sizeof(struct sk_ring_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc_if->sk_rdata == NULL) { printf("sk%d: no memory for list buffers!\n", sc_if->sk_unit); error = ENOMEM; goto fail; } bzero(sc_if->sk_rdata, sizeof(struct sk_ring_data)); /* Try to allocate memory for jumbo buffers. */ if (sk_alloc_jumbo_mem(sc_if)) { printf("sk%d: jumbo buffer allocation failed\n", sc_if->sk_unit); error = ENOMEM; goto fail; } ifp = &sc_if->arpcom.ac_if; ifp->if_softc = sc_if; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = sk_ioctl; ifp->if_start = sk_start; ifp->if_watchdog = sk_watchdog; ifp->if_init = sk_init; ifp->if_baudrate = 1000000000; ifp->if_snd.ifq_maxlen = SK_TX_RING_CNT - 1; callout_handle_init(&sc_if->sk_tick_ch); /* * Call MI attach routine. */ ether_ifattach(ifp, sc_if->arpcom.ac_enaddr); /* * Do miibus setup. */ switch (sc->sk_type) { case SK_GENESIS: sk_init_xmac(sc_if); break; case SK_YUKON: sk_init_yukon(sc_if); break; } if (mii_phy_probe(dev, &sc_if->sk_miibus, sk_ifmedia_upd, sk_ifmedia_sts)) { printf("skc%d: no PHY found!\n", sc_if->sk_unit); ether_ifdetach(ifp); error = ENXIO; goto fail; } fail: SK_UNLOCK(sc); if (error) { /* Access should be ok even though lock has been dropped */ sc->sk_if[port] = NULL; sk_detach(dev); } return(error); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int skc_attach(dev) device_t dev; { struct sk_softc *sc; int unit, error = 0, rid, *port; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->sk_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, SK_PCI_LOIO, 4); - membase = pci_read_config(dev, SK_PCI_LOMEM, 4); - irq = pci_read_config(dev, SK_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("skc%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, SK_PCI_LOIO, iobase, 4); - pci_write_config(dev, SK_PCI_LOMEM, membase, 4); - pci_write_config(dev, SK_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = SK_RID; sc->sk_res = bus_alloc_resource_any(dev, SK_RES, &rid, RF_ACTIVE); if (sc->sk_res == NULL) { printf("sk%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->sk_btag = rman_get_bustag(sc->sk_res); sc->sk_bhandle = rman_get_bushandle(sc->sk_res); /* Allocate interrupt */ rid = 0; sc->sk_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->sk_irq == NULL) { printf("skc%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Set adapter type */ switch (pci_get_device(dev)) { case DEVICEID_SK_V1: sc->sk_type = SK_GENESIS; break; case DEVICEID_SK_V2: case DEVICEID_3COM_3C940: case DEVICEID_LINKSYS_EG1032: case DEVICEID_DLINK_DGE530T: sc->sk_type = SK_YUKON; break; default: printf("skc%d: unknown device!\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ sk_reset(sc); sc->sk_unit = unit; /* Read and save vital product data from EEPROM. */ sk_vpd_read(sc); if (sc->sk_type == SK_GENESIS) { /* Read and save RAM size and RAMbuffer offset */ switch(sk_win_read_1(sc, SK_EPROM0)) { case SK_RAMSIZE_512K_64: sc->sk_ramsize = 0x80000; sc->sk_rboff = SK_RBOFF_0; break; case SK_RAMSIZE_1024K_64: sc->sk_ramsize = 0x100000; sc->sk_rboff = SK_RBOFF_80000; break; case SK_RAMSIZE_1024K_128: sc->sk_ramsize = 0x100000; sc->sk_rboff = SK_RBOFF_0; break; case SK_RAMSIZE_2048K_128: sc->sk_ramsize = 0x200000; sc->sk_rboff = SK_RBOFF_0; break; default: printf("skc%d: unknown ram size: %d\n", sc->sk_unit, sk_win_read_1(sc, SK_EPROM0)); error = ENXIO; goto fail; } } else { sc->sk_ramsize = 0x20000; sc->sk_rboff = SK_RBOFF_0; } /* Read and save physical media type */ switch(sk_win_read_1(sc, SK_PMDTYPE)) { case SK_PMD_1000BASESX: sc->sk_pmd = IFM_1000_SX; break; case SK_PMD_1000BASELX: sc->sk_pmd = IFM_1000_LX; break; case SK_PMD_1000BASECX: sc->sk_pmd = IFM_1000_CX; break; case SK_PMD_1000BASETX: sc->sk_pmd = IFM_1000_T; break; default: printf("skc%d: unknown media type: 0x%x\n", sc->sk_unit, sk_win_read_1(sc, SK_PMDTYPE)); error = ENXIO; goto fail; } /* Announce the product name. */ if (sc->sk_vpd_prodname != NULL) printf("skc%d: %s\n", sc->sk_unit, sc->sk_vpd_prodname); sc->sk_devs[SK_PORT_A] = device_add_child(dev, "sk", -1); port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); *port = SK_PORT_A; device_set_ivars(sc->sk_devs[SK_PORT_A], port); if (!(sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC)) { sc->sk_devs[SK_PORT_B] = device_add_child(dev, "sk", -1); port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT); *port = SK_PORT_B; device_set_ivars(sc->sk_devs[SK_PORT_B], port); } /* Turn on the 'driver is loaded' LED. */ CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_ON); bus_generic_attach(dev); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->sk_irq, INTR_TYPE_NET, sk_intr, sc, &sc->sk_intrhand); if (error) { printf("skc%d: couldn't set up irq\n", unit); goto fail; } fail: if (error) skc_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int sk_detach(dev) device_t dev; { struct sk_if_softc *sc_if; struct ifnet *ifp; sc_if = device_get_softc(dev); KASSERT(mtx_initialized(&sc_if->sk_softc->sk_mtx), ("sk mutex not initialized in sk_detach")); SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; /* These should only be active if attach_xmac succeeded */ if (device_is_attached(dev)) { sk_stop(sc_if); ether_ifdetach(ifp); } if (sc_if->sk_miibus) device_delete_child(dev, sc_if->sk_miibus); bus_generic_detach(dev); if (sc_if->sk_cdata.sk_jumbo_buf) contigfree(sc_if->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF); if (sc_if->sk_rdata) { contigfree(sc_if->sk_rdata, sizeof(struct sk_ring_data), M_DEVBUF); } SK_IF_UNLOCK(sc_if); return(0); } static int skc_detach(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sk_mtx), ("sk mutex not initialized")); SK_LOCK(sc); if (device_is_alive(dev)) { if (sc->sk_devs[SK_PORT_A] != NULL) device_delete_child(dev, sc->sk_devs[SK_PORT_A]); if (sc->sk_devs[SK_PORT_B] != NULL) device_delete_child(dev, sc->sk_devs[SK_PORT_B]); bus_generic_detach(dev); } if (sc->sk_intrhand) bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand); if (sc->sk_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq); if (sc->sk_res) bus_release_resource(dev, SK_RES, SK_RID, sc->sk_res); SK_UNLOCK(sc); mtx_destroy(&sc->sk_mtx); return(0); } static int sk_encap(sc_if, m_head, txidx) struct sk_if_softc *sc_if; struct mbuf *m_head; u_int32_t *txidx; { struct sk_tx_desc *f = NULL; struct mbuf *m; u_int32_t frag, cur, cnt = 0; m = m_head; cur = frag = *txidx; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ for (m = m_head; m != NULL; m = m->m_next) { if (m->m_len != 0) { if ((SK_TX_RING_CNT - (sc_if->sk_cdata.sk_tx_cnt + cnt)) < 2) return(ENOBUFS); f = &sc_if->sk_rdata->sk_tx_ring[frag]; f->sk_data_lo = vtophys(mtod(m, vm_offset_t)); f->sk_ctl = m->m_len | SK_OPCODE_DEFAULT; if (cnt == 0) f->sk_ctl |= SK_TXCTL_FIRSTFRAG; else f->sk_ctl |= SK_TXCTL_OWN; cur = frag; SK_INC(frag, SK_TX_RING_CNT); cnt++; } } if (m != NULL) return(ENOBUFS); sc_if->sk_rdata->sk_tx_ring[cur].sk_ctl |= SK_TXCTL_LASTFRAG|SK_TXCTL_EOF_INTR; sc_if->sk_cdata.sk_tx_chain[cur].sk_mbuf = m_head; sc_if->sk_rdata->sk_tx_ring[*txidx].sk_ctl |= SK_TXCTL_OWN; sc_if->sk_cdata.sk_tx_cnt += cnt; *txidx = frag; return(0); } static void sk_start(ifp) struct ifnet *ifp; { struct sk_softc *sc; struct sk_if_softc *sc_if; struct mbuf *m_head = NULL; u_int32_t idx; sc_if = ifp->if_softc; sc = sc_if->sk_softc; SK_IF_LOCK(sc_if); idx = sc_if->sk_cdata.sk_tx_prod; while(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* * Pack the data into the transmit ring. If we * don't have room, set the OACTIVE flag and wait * for the NIC to drain the ring. */ if (sk_encap(sc_if, m_head, &idx)) { IF_PREPEND(&ifp->if_snd, m_head); ifp->if_flags |= IFF_OACTIVE; break; } /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, m_head); } /* Transmit */ sc_if->sk_cdata.sk_tx_prod = idx; CSR_WRITE_4(sc, sc_if->sk_tx_bmu, SK_TXBMU_TX_START); /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; SK_IF_UNLOCK(sc_if); return; } static void sk_watchdog(ifp) struct ifnet *ifp; { struct sk_if_softc *sc_if; sc_if = ifp->if_softc; printf("sk%d: watchdog timeout\n", sc_if->sk_unit); sk_init(sc_if); return; } static void skc_shutdown(dev) device_t dev; { struct sk_softc *sc; sc = device_get_softc(dev); SK_LOCK(sc); /* Turn off the 'driver is loaded' LED. */ CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_OFF); /* * Reset the GEnesis controller. Doing this should also * assert the resets on the attached XMAC(s). */ sk_reset(sc); SK_UNLOCK(sc); return; } static void sk_rxeof(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; struct mbuf *m; struct ifnet *ifp; struct sk_chain *cur_rx; int total_len = 0; int i; u_int32_t rxstat; sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; i = sc_if->sk_cdata.sk_rx_prod; cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; SK_LOCK_ASSERT(sc); while(!(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl & SK_RXCTL_OWN)) { cur_rx = &sc_if->sk_cdata.sk_rx_chain[i]; rxstat = sc_if->sk_rdata->sk_rx_ring[i].sk_xmac_rxstat; m = cur_rx->sk_mbuf; cur_rx->sk_mbuf = NULL; total_len = SK_RXBYTES(sc_if->sk_rdata->sk_rx_ring[i].sk_ctl); SK_INC(i, SK_RX_RING_CNT); if (rxstat & XM_RXSTAT_ERRFRAME) { ifp->if_ierrors++; sk_newbuf(sc_if, cur_rx, m); continue; } /* * Try to allocate a new jumbo buffer. If that * fails, copy the packet to mbufs and put the * jumbo buffer back in the ring so it can be * re-used. If allocating mbufs fails, then we * have to drop the packet. */ if (sk_newbuf(sc_if, cur_rx, NULL) == ENOBUFS) { struct mbuf *m0; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); sk_newbuf(sc_if, cur_rx, m); if (m0 == NULL) { printf("sk%d: no receive buffers " "available -- packet dropped!\n", sc_if->sk_unit); ifp->if_ierrors++; continue; } m = m0; } else { m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; } ifp->if_ipackets++; SK_UNLOCK(sc); (*ifp->if_input)(ifp, m); SK_LOCK(sc); } sc_if->sk_cdata.sk_rx_prod = i; return; } static void sk_txeof(sc_if) struct sk_if_softc *sc_if; { struct sk_tx_desc *cur_tx = NULL; struct ifnet *ifp; u_int32_t idx; ifp = &sc_if->arpcom.ac_if; /* * Go through our tx ring and free mbufs for those * frames that have been sent. */ idx = sc_if->sk_cdata.sk_tx_cons; while(idx != sc_if->sk_cdata.sk_tx_prod) { cur_tx = &sc_if->sk_rdata->sk_tx_ring[idx]; if (cur_tx->sk_ctl & SK_TXCTL_OWN) break; if (cur_tx->sk_ctl & SK_TXCTL_LASTFRAG) ifp->if_opackets++; if (sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf); sc_if->sk_cdata.sk_tx_chain[idx].sk_mbuf = NULL; } sc_if->sk_cdata.sk_tx_cnt--; SK_INC(idx, SK_TX_RING_CNT); ifp->if_timer = 0; } sc_if->sk_cdata.sk_tx_cons = idx; if (cur_tx != NULL) ifp->if_flags &= ~IFF_OACTIVE; return; } static void sk_tick(xsc_if) void *xsc_if; { struct sk_if_softc *sc_if; struct mii_data *mii; struct ifnet *ifp; int i; sc_if = xsc_if; SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; mii = device_get_softc(sc_if->sk_miibus); if (!(ifp->if_flags & IFF_UP)) { SK_IF_UNLOCK(sc_if); return; } if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { sk_intr_bcom(sc_if); SK_IF_UNLOCK(sc_if); return; } /* * According to SysKonnect, the correct way to verify that * the link has come back up is to poll bit 0 of the GPIO * register three times. This pin has the signal from the * link_sync pin connected to it; if we read the same link * state 3 times in a row, we know the link is up. */ for (i = 0; i < 3; i++) { if (SK_XM_READ_2(sc_if, XM_GPIO) & XM_GPIO_GP0_SET) break; } if (i != 3) { sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); SK_IF_UNLOCK(sc_if); return; } /* Turn the GP0 interrupt back on. */ SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); SK_XM_READ_2(sc_if, XM_ISR); mii_tick(mii); untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); SK_IF_UNLOCK(sc_if); return; } static void sk_intr_bcom(sc_if) struct sk_if_softc *sc_if; { struct mii_data *mii; struct ifnet *ifp; int status; mii = device_get_softc(sc_if->sk_miibus); ifp = &sc_if->arpcom.ac_if; SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); /* * Read the PHY interrupt register to make sure * we clear any pending interrupts. */ status = sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_ISR); if (!(ifp->if_flags & IFF_RUNNING)) { sk_init_xmac(sc_if); return; } if (status & (BRGPHY_ISR_LNK_CHG|BRGPHY_ISR_AN_PR)) { int lstat; lstat = sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_AUXSTS); if (!(lstat & BRGPHY_AUXSTS_LINK) && sc_if->sk_link) { mii_mediachg(mii); /* Turn off the link LED. */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF); sc_if->sk_link = 0; } else if (status & BRGPHY_ISR_LNK_CHG) { sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_IMR, 0xFF00); mii_tick(mii); sc_if->sk_link = 1; /* Turn on the link LED. */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_ON|SK_LINKLED_LINKSYNC_OFF| SK_LINKLED_BLINK_OFF); } else { mii_tick(mii); sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } } SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); return; } static void sk_intr_xmac(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; u_int16_t status; sc = sc_if->sk_softc; status = SK_XM_READ_2(sc_if, XM_ISR); /* * Link has gone down. Start MII tick timeout to * watch for link resync. */ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) { if (status & XM_ISR_GP0_SET) { SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET); sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } if (status & XM_ISR_AUTONEG_DONE) { sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz); } } if (status & XM_IMR_TX_UNDERRUN) SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_TXFIFO); if (status & XM_IMR_RX_OVERRUN) SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_RXFIFO); status = SK_XM_READ_2(sc_if, XM_ISR); return; } static void sk_intr_yukon(sc_if) struct sk_if_softc *sc_if; { int status; status = SK_IF_READ_2(sc_if, 0, SK_GMAC_ISR); return; } static void sk_intr(xsc) void *xsc; { struct sk_softc *sc = xsc; struct sk_if_softc *sc_if0 = NULL, *sc_if1 = NULL; struct ifnet *ifp0 = NULL, *ifp1 = NULL; u_int32_t status; SK_LOCK(sc); sc_if0 = sc->sk_if[SK_PORT_A]; sc_if1 = sc->sk_if[SK_PORT_B]; if (sc_if0 != NULL) ifp0 = &sc_if0->arpcom.ac_if; if (sc_if1 != NULL) ifp1 = &sc_if1->arpcom.ac_if; for (;;) { status = CSR_READ_4(sc, SK_ISSR); if (!(status & sc->sk_intrmask)) break; /* Handle receive interrupts first. */ if (status & SK_ISR_RX1_EOF) { sk_rxeof(sc_if0); CSR_WRITE_4(sc, SK_BMU_RX_CSR0, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } if (status & SK_ISR_RX2_EOF) { sk_rxeof(sc_if1); CSR_WRITE_4(sc, SK_BMU_RX_CSR1, SK_RXBMU_CLR_IRQ_EOF|SK_RXBMU_RX_START); } /* Then transmit interrupts. */ if (status & SK_ISR_TX1_S_EOF) { sk_txeof(sc_if0); CSR_WRITE_4(sc, SK_BMU_TXS_CSR0, SK_TXBMU_CLR_IRQ_EOF); } if (status & SK_ISR_TX2_S_EOF) { sk_txeof(sc_if1); CSR_WRITE_4(sc, SK_BMU_TXS_CSR1, SK_TXBMU_CLR_IRQ_EOF); } /* Then MAC interrupts. */ if (status & SK_ISR_MAC1 && ifp0->if_flags & IFF_RUNNING) { if (sc->sk_type == SK_GENESIS) sk_intr_xmac(sc_if0); else sk_intr_yukon(sc_if0); } if (status & SK_ISR_MAC2 && ifp1->if_flags & IFF_RUNNING) { if (sc->sk_type == SK_GENESIS) sk_intr_xmac(sc_if1); else sk_intr_yukon(sc_if1); } if (status & SK_ISR_EXTERNAL_REG) { if (ifp0 != NULL && sc_if0->sk_phytype == SK_PHYTYPE_BCOM) sk_intr_bcom(sc_if0); if (ifp1 != NULL && sc_if1->sk_phytype == SK_PHYTYPE_BCOM) sk_intr_bcom(sc_if1); } } CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); if (ifp0 != NULL && ifp0->if_snd.ifq_head != NULL) sk_start(ifp0); if (ifp1 != NULL && ifp1->if_snd.ifq_head != NULL) sk_start(ifp1); SK_UNLOCK(sc); return; } static void sk_init_xmac(sc_if) struct sk_if_softc *sc_if; { struct sk_softc *sc; struct ifnet *ifp; struct sk_bcom_hack bhack[] = { { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 }, { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 }, { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, { 0, 0 } }; sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; /* Unreset the XMAC. */ SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_UNRESET); DELAY(1000); /* Reset the XMAC's internal state. */ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); /* Save the XMAC II revision */ sc_if->sk_xmac_rev = XM_XMAC_REV(SK_XM_READ_4(sc_if, XM_DEVID)); /* * Perform additional initialization for external PHYs, * namely for the 1000baseTX cards that use the XMAC's * GMII mode. */ if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { int i = 0; u_int32_t val; /* Take PHY out of reset. */ val = sk_win_read_4(sc, SK_GPIO); if (sc_if->sk_port == SK_PORT_A) val |= SK_GPIO_DIR0|SK_GPIO_DAT0; else val |= SK_GPIO_DIR2|SK_GPIO_DAT2; sk_win_write_4(sc, SK_GPIO, val); /* Enable GMII mode on the XMAC. */ SK_XM_SETBIT_2(sc_if, XM_HWCFG, XM_HWCFG_GMIIMODE); sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET); DELAY(10000); sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, BRGPHY_MII_IMR, 0xFFF0); /* * Early versions of the BCM5400 apparently have * a bug that requires them to have their reserved * registers initialized to some magic values. I don't * know what the numbers do, I'm just the messenger. */ if (sk_xmac_miibus_readreg(sc_if, SK_PHYADDR_BCOM, 0x03) == 0x6041) { while(bhack[i].reg) { sk_xmac_miibus_writereg(sc_if, SK_PHYADDR_BCOM, bhack[i].reg, bhack[i].val); i++; } } } /* Set station address */ SK_XM_WRITE_2(sc_if, XM_PAR0, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[0])); SK_XM_WRITE_2(sc_if, XM_PAR1, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[2])); SK_XM_WRITE_2(sc_if, XM_PAR2, *(u_int16_t *)(&sc_if->arpcom.ac_enaddr[4])); SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_USE_STATION); if (ifp->if_flags & IFF_BROADCAST) { SK_XM_CLRBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); } else { SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_NOBROAD); } /* We don't need the FCS appended to the packet. */ SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_STRIPFCS); /* We want short frames padded to 60 bytes. */ SK_XM_SETBIT_2(sc_if, XM_TXCMD, XM_TXCMD_AUTOPAD); /* * Enable the reception of all error frames. This is is * a necessary evil due to the design of the XMAC. The * XMAC's receive FIFO is only 8K in size, however jumbo * frames can be up to 9000 bytes in length. When bad * frame filtering is enabled, the XMAC's RX FIFO operates * in 'store and forward' mode. For this to work, the * entire frame has to fit into the FIFO, but that means * that jumbo frames larger than 8192 bytes will be * truncated. Disabling all bad frame filtering causes * the RX FIFO to operate in streaming mode, in which * case the XMAC will start transfering frames out of the * RX FIFO as soon as the FIFO threshold is reached. */ SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_RX_BADFRAMES| XM_MODE_RX_GIANTS|XM_MODE_RX_RUNTS|XM_MODE_RX_CRCERRS| XM_MODE_RX_INRANGELEN); if (ifp->if_mtu > (ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN)) SK_XM_SETBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); else SK_XM_CLRBIT_2(sc_if, XM_RXCMD, XM_RXCMD_BIGPKTOK); /* * Bump up the transmit threshold. This helps hold off transmit * underruns when we're blasting traffic from both ports at once. */ SK_XM_WRITE_2(sc_if, XM_TX_REQTHRESH, SK_XM_TX_FIFOTHRESH); /* Set promiscuous mode */ sk_setpromisc(sc_if); /* Set multicast filter */ sk_setmulti(sc_if); /* Clear and enable interrupts */ SK_XM_READ_2(sc_if, XM_ISR); if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) SK_XM_WRITE_2(sc_if, XM_IMR, XM_INTRS); else SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); /* Configure MAC arbiter */ switch(sc_if->sk_xmac_rev) { case XM_XMAC_REV_B2: sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_B2); sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); break; case XM_XMAC_REV_C1: sk_win_write_1(sc, SK_RCINIT_RX1, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_TX1, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_RX2, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_RCINIT_TX2, SK_RCINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_RX1, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_TX1, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_RX2, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_MINIT_TX2, SK_MINIT_XMAC_C1); sk_win_write_1(sc, SK_RECOVERY_CTL, SK_RECOVERY_XMAC_B2); break; default: break; } sk_win_write_2(sc, SK_MACARB_CTL, SK_MACARBCTL_UNRESET|SK_MACARBCTL_FASTOE_OFF); sc_if->sk_link = 1; return; } static void sk_init_yukon(sc_if) struct sk_if_softc *sc_if; { u_int32_t phy; u_int16_t reg; int i; /* GMAC and GPHY Reset */ SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, SK_GPHY_RESET_SET); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_SET); DELAY(1000); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_RESET_SET); DELAY(1000); phy = SK_GPHY_INT_POL_HI | SK_GPHY_DIS_FC | SK_GPHY_DIS_SLEEP | SK_GPHY_ENA_XC | SK_GPHY_ANEG_ALL | SK_GPHY_ENA_PAUSE; switch(sc_if->sk_softc->sk_pmd) { case IFM_1000_SX: case IFM_1000_LX: phy |= SK_GPHY_FIBER; break; case IFM_1000_CX: case IFM_1000_T: phy |= SK_GPHY_COPPER; break; } SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, phy | SK_GPHY_RESET_SET); DELAY(1000); SK_IF_WRITE_4(sc_if, 0, SK_GPHY_CTRL, phy | SK_GPHY_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_GMAC_CTRL, SK_GMAC_LOOP_OFF | SK_GMAC_PAUSE_ON | SK_GMAC_RESET_CLEAR); /* unused read of the interrupt source register */ SK_IF_READ_2(sc_if, 0, SK_GMAC_ISR); reg = SK_YU_READ_2(sc_if, YUKON_PAR); /* MIB Counter Clear Mode set */ reg |= YU_PAR_MIB_CLR; SK_YU_WRITE_2(sc_if, YUKON_PAR, reg); /* MIB Counter Clear Mode clear */ reg &= ~YU_PAR_MIB_CLR; SK_YU_WRITE_2(sc_if, YUKON_PAR, reg); /* receive control reg */ SK_YU_WRITE_2(sc_if, YUKON_RCR, YU_RCR_CRCR); /* transmit parameter register */ SK_YU_WRITE_2(sc_if, YUKON_TPR, YU_TPR_JAM_LEN(0x3) | YU_TPR_JAM_IPG(0xb) | YU_TPR_JAM2DATA_IPG(0x1a) ); /* serial mode register */ SK_YU_WRITE_2(sc_if, YUKON_SMR, YU_SMR_DATA_BLIND(0x1c) | YU_SMR_MFL_VLAN | YU_SMR_IPG_DATA(0x1e)); /* Setup Yukon's address */ for (i = 0; i < 3; i++) { /* Write Source Address 1 (unicast filter) */ SK_YU_WRITE_2(sc_if, YUKON_SAL1 + i * 4, sc_if->arpcom.ac_enaddr[i * 2] | sc_if->arpcom.ac_enaddr[i * 2 + 1] << 8); } for (i = 0; i < 3; i++) { reg = sk_win_read_2(sc_if->sk_softc, SK_MAC1_0 + i * 2 + sc_if->sk_port * 8); SK_YU_WRITE_2(sc_if, YUKON_SAL2 + i * 4, reg); } /* Set promiscuous mode */ sk_setpromisc(sc_if); /* Set multicast filter */ sk_setmulti(sc_if); /* enable interrupt mask for counter overflows */ SK_YU_WRITE_2(sc_if, YUKON_TIMR, 0); SK_YU_WRITE_2(sc_if, YUKON_RIMR, 0); SK_YU_WRITE_2(sc_if, YUKON_TRIMR, 0); /* Configure RX MAC FIFO */ SK_IF_WRITE_1(sc_if, 0, SK_RXMF1_CTRL_TEST, SK_RFCTL_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_RXMF1_CTRL_TEST, SK_RFCTL_OPERATION_ON); /* Configure TX MAC FIFO */ SK_IF_WRITE_1(sc_if, 0, SK_TXMF1_CTRL_TEST, SK_TFCTL_RESET_CLEAR); SK_IF_WRITE_4(sc_if, 0, SK_TXMF1_CTRL_TEST, SK_TFCTL_OPERATION_ON); } /* * Note that to properly initialize any part of the GEnesis chip, * you first have to take it out of reset mode. */ static void sk_init(xsc) void *xsc; { struct sk_if_softc *sc_if = xsc; struct sk_softc *sc; struct ifnet *ifp; struct mii_data *mii; u_int16_t reg; SK_IF_LOCK(sc_if); ifp = &sc_if->arpcom.ac_if; sc = sc_if->sk_softc; mii = device_get_softc(sc_if->sk_miibus); /* Cancel pending I/O and free all RX/TX buffers. */ sk_stop(sc_if); if (sc->sk_type == SK_GENESIS) { /* Configure LINK_SYNC LED */ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_ON); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_ON); /* Configure RX LED */ SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_START); /* Configure TX LED */ SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_TXLEDCTL_COUNTER_START); } /* Configure I2C registers */ /* Configure XMAC(s) */ switch (sc->sk_type) { case SK_GENESIS: sk_init_xmac(sc_if); break; case SK_YUKON: sk_init_yukon(sc_if); break; } mii_mediachg(mii); if (sc->sk_type == SK_GENESIS) { /* Configure MAC FIFOs */ SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_END, SK_FIFO_END); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_ON); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_END, SK_FIFO_END); SK_IF_WRITE_4(sc_if, 0, SK_TXF1_CTL, SK_FIFO_ON); } /* Configure transmit arbiter(s) */ SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, SK_TXARCTL_ON|SK_TXARCTL_FSYNC_ON); /* Configure RAMbuffers */ SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_UNRESET); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_START, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_WR_PTR, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_RD_PTR, sc_if->sk_rx_ramstart); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_END, sc_if->sk_rx_ramend); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_ON); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_UNRESET); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_STORENFWD_ON); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_START, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_WR_PTR, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_RD_PTR, sc_if->sk_tx_ramstart); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_END, sc_if->sk_tx_ramend); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_ON); /* Configure BMUs */ SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_ONLINE); SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_LO, vtophys(&sc_if->sk_rdata->sk_rx_ring[0])); SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_CURADDR_HI, 0); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_ONLINE); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_LO, vtophys(&sc_if->sk_rdata->sk_tx_ring[0])); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_CURADDR_HI, 0); /* Init descriptors */ if (sk_init_rx_ring(sc_if) == ENOBUFS) { printf("sk%d: initialization failed: no " "memory for rx buffers\n", sc_if->sk_unit); sk_stop(sc_if); SK_IF_UNLOCK(sc_if); return; } sk_init_tx_ring(sc_if); /* Configure interrupt handling */ CSR_READ_4(sc, SK_ISSR); if (sc_if->sk_port == SK_PORT_A) sc->sk_intrmask |= SK_INTRS1; else sc->sk_intrmask |= SK_INTRS2; sc->sk_intrmask |= SK_ISR_EXTERNAL_REG; CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); /* Start BMUs. */ SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_RX_START); switch(sc->sk_type) { case SK_GENESIS: /* Enable XMACs TX and RX state machines */ SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_IGNPAUSE); SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB); break; case SK_YUKON: reg = SK_YU_READ_2(sc_if, YUKON_GPCR); reg |= YU_GPCR_TXEN | YU_GPCR_RXEN; reg &= ~(YU_GPCR_SPEED_EN | YU_GPCR_DPLX_EN); SK_YU_WRITE_2(sc_if, YUKON_GPCR, reg); } ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; SK_IF_UNLOCK(sc_if); return; } static void sk_stop(sc_if) struct sk_if_softc *sc_if; { int i; struct sk_softc *sc; struct ifnet *ifp; SK_IF_LOCK(sc_if); sc = sc_if->sk_softc; ifp = &sc_if->arpcom.ac_if; untimeout(sk_tick, sc_if, sc_if->sk_tick_ch); if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) { u_int32_t val; /* Put PHY back into reset. */ val = sk_win_read_4(sc, SK_GPIO); if (sc_if->sk_port == SK_PORT_A) { val |= SK_GPIO_DIR0; val &= ~SK_GPIO_DAT0; } else { val |= SK_GPIO_DIR2; val &= ~SK_GPIO_DAT2; } sk_win_write_4(sc, SK_GPIO, val); } /* Turn off various components of this interface. */ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC); switch (sc->sk_type) { case SK_GENESIS: SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_RESET); SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_RESET); break; case SK_YUKON: SK_IF_WRITE_1(sc_if,0, SK_RXMF1_CTRL_TEST, SK_RFCTL_RESET_SET); SK_IF_WRITE_1(sc_if,0, SK_TXMF1_CTRL_TEST, SK_TFCTL_RESET_SET); break; } SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_OFFLINE); SK_IF_WRITE_4(sc_if, 0, SK_RXRB1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); SK_IF_WRITE_4(sc_if, 1, SK_TXQS1_BMU_CSR, SK_TXBMU_OFFLINE); SK_IF_WRITE_4(sc_if, 1, SK_TXRBS1_CTLTST, SK_RBCTL_RESET|SK_RBCTL_OFF); SK_IF_WRITE_1(sc_if, 0, SK_TXAR1_COUNTERCTL, SK_TXARCTL_OFF); SK_IF_WRITE_1(sc_if, 0, SK_RXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); SK_IF_WRITE_1(sc_if, 0, SK_TXLED1_CTL, SK_RXLEDCTL_COUNTER_STOP); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF); SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_LINKSYNC_OFF); /* Disable interrupts */ if (sc_if->sk_port == SK_PORT_A) sc->sk_intrmask &= ~SK_INTRS1; else sc->sk_intrmask &= ~SK_INTRS2; CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask); SK_XM_READ_2(sc_if, XM_ISR); SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF); /* Free RX and TX mbufs still in the queues. */ for (i = 0; i < SK_RX_RING_CNT; i++) { if (sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf); sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf = NULL; } } for (i = 0; i < SK_TX_RING_CNT; i++) { if (sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf != NULL) { m_freem(sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf); sc_if->sk_cdata.sk_tx_chain[i].sk_mbuf = NULL; } } ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); SK_IF_UNLOCK(sc_if); return; } Index: head/sys/pci/if_ste.c =================================================================== --- head/sys/pci/if_ste.c (revision 131252) +++ head/sys/pci/if_ste.c (revision 131253) @@ -1,1716 +1,1692 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #define STE_USEIOSPACE #include MODULE_DEPEND(ste, pci, 1, 1, 1); MODULE_DEPEND(ste, ether, 1, 1, 1); MODULE_DEPEND(ste, miibus, 1, 1, 1); /* * Various supported device vendors/types and their names. */ static struct ste_type ste_devs[] = { { ST_VENDORID, ST_DEVICEID_ST201, "Sundance ST201 10/100BaseTX" }, { DL_VENDORID, DL_DEVICEID_DL10050, "D-Link DL10050 10/100BaseTX" }, { 0, 0, NULL } }; static int ste_probe (device_t); static int ste_attach (device_t); static int ste_detach (device_t); static void ste_init (void *); static void ste_intr (void *); static void ste_rxeoc (struct ste_softc *); static void ste_rxeof (struct ste_softc *); static void ste_txeoc (struct ste_softc *); static void ste_txeof (struct ste_softc *); static void ste_stats_update (void *); static void ste_stop (struct ste_softc *); static void ste_reset (struct ste_softc *); static int ste_ioctl (struct ifnet *, u_long, caddr_t); static int ste_encap (struct ste_softc *, struct ste_chain *, struct mbuf *); static void ste_start (struct ifnet *); static void ste_watchdog (struct ifnet *); static void ste_shutdown (device_t); static int ste_newbuf (struct ste_softc *, struct ste_chain_onefrag *, struct mbuf *); static int ste_ifmedia_upd (struct ifnet *); static void ste_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void ste_mii_sync (struct ste_softc *); static void ste_mii_send (struct ste_softc *, u_int32_t, int); static int ste_mii_readreg (struct ste_softc *, struct ste_mii_frame *); static int ste_mii_writereg (struct ste_softc *, struct ste_mii_frame *); static int ste_miibus_readreg (device_t, int, int); static int ste_miibus_writereg (device_t, int, int, int); static void ste_miibus_statchg (device_t); static int ste_eeprom_wait (struct ste_softc *); static int ste_read_eeprom (struct ste_softc *, caddr_t, int, int, int); static void ste_wait (struct ste_softc *); static void ste_setmulti (struct ste_softc *); static int ste_init_rx_list (struct ste_softc *); static void ste_init_tx_list (struct ste_softc *); #ifdef STE_USEIOSPACE #define STE_RES SYS_RES_IOPORT #define STE_RID STE_PCI_LOIO #else #define STE_RES SYS_RES_MEMORY #define STE_RID STE_PCI_LOMEM #endif static device_method_t ste_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ste_probe), DEVMETHOD(device_attach, ste_attach), DEVMETHOD(device_detach, ste_detach), DEVMETHOD(device_shutdown, ste_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, ste_miibus_readreg), DEVMETHOD(miibus_writereg, ste_miibus_writereg), DEVMETHOD(miibus_statchg, ste_miibus_statchg), { 0, 0 } }; static driver_t ste_driver = { "ste", ste_methods, sizeof(struct ste_softc) }; static devclass_t ste_devclass; DRIVER_MODULE(ste, pci, ste_driver, ste_devclass, 0, 0); DRIVER_MODULE(miibus, ste, miibus_driver, miibus_devclass, 0, 0); SYSCTL_NODE(_hw, OID_AUTO, ste, CTLFLAG_RD, 0, "if_ste parameters"); static int ste_rxsyncs; SYSCTL_INT(_hw_ste, OID_AUTO, rxsyncs, CTLFLAG_RW, &ste_rxsyncs, 0, ""); #define STE_SETBIT4(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | (x)) #define STE_CLRBIT4(sc, reg, x) \ CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) & ~(x)) #define STE_SETBIT2(sc, reg, x) \ CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) | (x)) #define STE_CLRBIT2(sc, reg, x) \ CSR_WRITE_2(sc, reg, CSR_READ_2(sc, reg) & ~(x)) #define STE_SETBIT1(sc, reg, x) \ CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) | (x)) #define STE_CLRBIT1(sc, reg, x) \ CSR_WRITE_1(sc, reg, CSR_READ_1(sc, reg) & ~(x)) #define MII_SET(x) STE_SETBIT1(sc, STE_PHYCTL, x) #define MII_CLR(x) STE_CLRBIT1(sc, STE_PHYCTL, x) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void ste_mii_sync(sc) struct ste_softc *sc; { register int i; MII_SET(STE_PHYCTL_MDIR|STE_PHYCTL_MDATA); for (i = 0; i < 32; i++) { MII_SET(STE_PHYCTL_MCLK); DELAY(1); MII_CLR(STE_PHYCTL_MCLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void ste_mii_send(sc, bits, cnt) struct ste_softc *sc; u_int32_t bits; int cnt; { int i; MII_CLR(STE_PHYCTL_MCLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(STE_PHYCTL_MDATA); } else { MII_CLR(STE_PHYCTL_MDATA); } DELAY(1); MII_CLR(STE_PHYCTL_MCLK); DELAY(1); MII_SET(STE_PHYCTL_MCLK); } } /* * Read an PHY register through the MII. */ static int ste_mii_readreg(sc, frame) struct ste_softc *sc; struct ste_mii_frame *frame; { int i, ack; STE_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = STE_MII_STARTDELIM; frame->mii_opcode = STE_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_2(sc, STE_PHYCTL, 0); /* * Turn on data xmit. */ MII_SET(STE_PHYCTL_MDIR); ste_mii_sync(sc); /* * Send command/address info. */ ste_mii_send(sc, frame->mii_stdelim, 2); ste_mii_send(sc, frame->mii_opcode, 2); ste_mii_send(sc, frame->mii_phyaddr, 5); ste_mii_send(sc, frame->mii_regaddr, 5); /* Turn off xmit. */ MII_CLR(STE_PHYCTL_MDIR); /* Idle bit */ MII_CLR((STE_PHYCTL_MCLK|STE_PHYCTL_MDATA)); DELAY(1); MII_SET(STE_PHYCTL_MCLK); DELAY(1); /* Check for ack */ MII_CLR(STE_PHYCTL_MCLK); DELAY(1); ack = CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA; MII_SET(STE_PHYCTL_MCLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(STE_PHYCTL_MCLK); DELAY(1); MII_SET(STE_PHYCTL_MCLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(STE_PHYCTL_MCLK); DELAY(1); if (!ack) { if (CSR_READ_2(sc, STE_PHYCTL) & STE_PHYCTL_MDATA) frame->mii_data |= i; DELAY(1); } MII_SET(STE_PHYCTL_MCLK); DELAY(1); } fail: MII_CLR(STE_PHYCTL_MCLK); DELAY(1); MII_SET(STE_PHYCTL_MCLK); DELAY(1); STE_UNLOCK(sc); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int ste_mii_writereg(sc, frame) struct ste_softc *sc; struct ste_mii_frame *frame; { STE_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = STE_MII_STARTDELIM; frame->mii_opcode = STE_MII_WRITEOP; frame->mii_turnaround = STE_MII_TURNAROUND; /* * Turn on data output. */ MII_SET(STE_PHYCTL_MDIR); ste_mii_sync(sc); ste_mii_send(sc, frame->mii_stdelim, 2); ste_mii_send(sc, frame->mii_opcode, 2); ste_mii_send(sc, frame->mii_phyaddr, 5); ste_mii_send(sc, frame->mii_regaddr, 5); ste_mii_send(sc, frame->mii_turnaround, 2); ste_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(STE_PHYCTL_MCLK); DELAY(1); MII_CLR(STE_PHYCTL_MCLK); DELAY(1); /* * Turn off xmit. */ MII_CLR(STE_PHYCTL_MDIR); STE_UNLOCK(sc); return(0); } static int ste_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct ste_softc *sc; struct ste_mii_frame frame; sc = device_get_softc(dev); if ( sc->ste_one_phy && phy != 0 ) return (0); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; ste_mii_readreg(sc, &frame); return(frame.mii_data); } static int ste_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct ste_softc *sc; struct ste_mii_frame frame; sc = device_get_softc(dev); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; ste_mii_writereg(sc, &frame); return(0); } static void ste_miibus_statchg(dev) device_t dev; { struct ste_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); STE_LOCK(sc); mii = device_get_softc(sc->ste_miibus); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { STE_SETBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); } else { STE_CLRBIT2(sc, STE_MACCTL0, STE_MACCTL0_FULLDUPLEX); } STE_UNLOCK(sc); return; } static int ste_ifmedia_upd(ifp) struct ifnet *ifp; { struct ste_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->ste_miibus); sc->ste_link = 0; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } mii_mediachg(mii); return(0); } static void ste_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct ste_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->ste_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static void ste_wait(sc) struct ste_softc *sc; { register int i; for (i = 0; i < STE_TIMEOUT; i++) { if (!(CSR_READ_4(sc, STE_DMACTL) & STE_DMACTL_DMA_HALTINPROG)) break; } if (i == STE_TIMEOUT) printf("ste%d: command never completed!\n", sc->ste_unit); return; } /* * The EEPROM is slow: give it time to come ready after issuing * it a command. */ static int ste_eeprom_wait(sc) struct ste_softc *sc; { int i; DELAY(1000); for (i = 0; i < 100; i++) { if (CSR_READ_2(sc, STE_EEPROM_CTL) & STE_EECTL_BUSY) DELAY(1000); else break; } if (i == 100) { printf("ste%d: eeprom failed to come ready\n", sc->ste_unit); return(1); } return(0); } /* * Read a sequence of words from the EEPROM. Note that ethernet address * data is stored in the EEPROM in network byte order. */ static int ste_read_eeprom(sc, dest, off, cnt, swap) struct ste_softc *sc; caddr_t dest; int off; int cnt; int swap; { int err = 0, i; u_int16_t word = 0, *ptr; if (ste_eeprom_wait(sc)) return(1); for (i = 0; i < cnt; i++) { CSR_WRITE_2(sc, STE_EEPROM_CTL, STE_EEOPCODE_READ | (off + i)); err = ste_eeprom_wait(sc); if (err) break; word = CSR_READ_2(sc, STE_EEPROM_DATA); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return(err ? 1 : 0); } static void ste_setmulti(sc) struct ste_softc *sc; { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; ifp = &sc->arpcom.ac_if; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); return; } /* first, zot all the existing hash bits */ CSR_WRITE_2(sc, STE_MAR0, 0); CSR_WRITE_2(sc, STE_MAR1, 0); CSR_WRITE_2(sc, STE_MAR2, 0); CSR_WRITE_2(sc, STE_MAR3, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) & 0x3F; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); } CSR_WRITE_2(sc, STE_MAR0, hashes[0] & 0xFFFF); CSR_WRITE_2(sc, STE_MAR1, (hashes[0] >> 16) & 0xFFFF); CSR_WRITE_2(sc, STE_MAR2, hashes[1] & 0xFFFF); CSR_WRITE_2(sc, STE_MAR3, (hashes[1] >> 16) & 0xFFFF); STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_ALLMULTI); STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_MULTIHASH); return; } #ifdef DEVICE_POLLING static poll_handler_t ste_poll; static void ste_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct ste_softc *sc = ifp->if_softc; STE_LOCK(sc); if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ CSR_WRITE_2(sc, STE_IMR, STE_INTRS); goto done; } sc->rxcycles = count; if (cmd == POLL_AND_CHECK_STATUS) ste_rxeoc(sc); ste_rxeof(sc); ste_txeof(sc); if (ifp->if_snd.ifq_head != NULL) ste_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { u_int16_t status; status = CSR_READ_2(sc, STE_ISR_ACK); if (status & STE_ISR_TX_DONE) ste_txeoc(sc); if (status & STE_ISR_STATS_OFLOW) { untimeout(ste_stats_update, sc, sc->ste_stat_ch); ste_stats_update(sc); } if (status & STE_ISR_LINKEVENT) mii_pollstat(device_get_softc(sc->ste_miibus)); if (status & STE_ISR_HOSTERR) { ste_reset(sc); ste_init(sc); } } done: STE_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void ste_intr(xsc) void *xsc; { struct ste_softc *sc; struct ifnet *ifp; u_int16_t status; sc = xsc; STE_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(ste_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_2(sc, STE_IMR, 0); ste_poll(ifp, 0, 1); goto done; } #endif /* DEVICE_POLLING */ /* See if this is really our interrupt. */ if (!(CSR_READ_2(sc, STE_ISR) & STE_ISR_INTLATCH)) { STE_UNLOCK(sc); return; } for (;;) { status = CSR_READ_2(sc, STE_ISR_ACK); if (!(status & STE_INTRS)) break; if (status & STE_ISR_RX_DMADONE) { ste_rxeoc(sc); ste_rxeof(sc); } if (status & STE_ISR_TX_DMADONE) ste_txeof(sc); if (status & STE_ISR_TX_DONE) ste_txeoc(sc); if (status & STE_ISR_STATS_OFLOW) { untimeout(ste_stats_update, sc, sc->ste_stat_ch); ste_stats_update(sc); } if (status & STE_ISR_LINKEVENT) mii_pollstat(device_get_softc(sc->ste_miibus)); if (status & STE_ISR_HOSTERR) { ste_reset(sc); ste_init(sc); } } /* Re-enable interrupts */ CSR_WRITE_2(sc, STE_IMR, STE_INTRS); if (ifp->if_snd.ifq_head != NULL) ste_start(ifp); #ifdef DEVICE_POLLING done: #endif /* DEVICE_POLLING */ STE_UNLOCK(sc); return; } static void ste_rxeoc(struct ste_softc *sc) { struct ste_chain_onefrag *cur_rx; STE_LOCK_ASSERT(sc); if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) { cur_rx = sc->ste_cdata.ste_rx_head; do { cur_rx = cur_rx->ste_next; /* If the ring is empty, just return. */ if (cur_rx == sc->ste_cdata.ste_rx_head) return; } while (cur_rx->ste_ptr->ste_status == 0); if (sc->ste_cdata.ste_rx_head->ste_ptr->ste_status == 0) { /* We've fallen behind the chip: catch it. */ sc->ste_cdata.ste_rx_head = cur_rx; ++ste_rxsyncs; } } } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void ste_rxeof(sc) struct ste_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct ste_chain_onefrag *cur_rx; int total_len = 0, count=0; u_int32_t rxstat; STE_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; while((rxstat = sc->ste_cdata.ste_rx_head->ste_ptr->ste_status) & STE_RXSTAT_DMADONE) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif /* DEVICE_POLLING */ if ((STE_RX_LIST_CNT - count) < 3) { break; } cur_rx = sc->ste_cdata.ste_rx_head; sc->ste_cdata.ste_rx_head = cur_rx->ste_next; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & STE_RXSTAT_FRAME_ERR) { ifp->if_ierrors++; cur_rx->ste_ptr->ste_status = 0; continue; } /* * If there error bit was not set, the upload complete * bit should be set which means we have a valid packet. * If not, something truly strange has happened. */ if (!(rxstat & STE_RXSTAT_DMADONE)) { printf("ste%d: bad receive status -- packet dropped\n", sc->ste_unit); ifp->if_ierrors++; cur_rx->ste_ptr->ste_status = 0; continue; } /* No errors; receive the packet. */ m = cur_rx->ste_mbuf; total_len = cur_rx->ste_ptr->ste_status & STE_RXSTAT_FRAMELEN; /* * Try to conjure up a new mbuf cluster. If that * fails, it means we have an out of memory condition and * should leave the buffer in place and continue. This will * result in a lost packet, but there's little else we * can do in this situation. */ if (ste_newbuf(sc, cur_rx, NULL) == ENOBUFS) { ifp->if_ierrors++; cur_rx->ste_ptr->ste_status = 0; continue; } m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; ifp->if_ipackets++; STE_UNLOCK(sc); (*ifp->if_input)(ifp, m); STE_LOCK(sc); cur_rx->ste_ptr->ste_status = 0; count++; } return; } static void ste_txeoc(sc) struct ste_softc *sc; { u_int8_t txstat; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; while ((txstat = CSR_READ_1(sc, STE_TX_STATUS)) & STE_TXSTATUS_TXDONE) { if (txstat & STE_TXSTATUS_UNDERRUN || txstat & STE_TXSTATUS_EXCESSCOLLS || txstat & STE_TXSTATUS_RECLAIMERR) { ifp->if_oerrors++; printf("ste%d: transmission error: %x\n", sc->ste_unit, txstat); ste_reset(sc); ste_init(sc); if (txstat & STE_TXSTATUS_UNDERRUN && sc->ste_tx_thresh < STE_PACKET_SIZE) { sc->ste_tx_thresh += STE_MIN_FRAMELEN; printf("ste%d: tx underrun, increasing tx" " start threshold to %d bytes\n", sc->ste_unit, sc->ste_tx_thresh); } CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); CSR_WRITE_2(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); } ste_init(sc); CSR_WRITE_2(sc, STE_TX_STATUS, txstat); } return; } static void ste_txeof(sc) struct ste_softc *sc; { struct ste_chain *cur_tx; struct ifnet *ifp; int idx; ifp = &sc->arpcom.ac_if; idx = sc->ste_cdata.ste_tx_cons; while(idx != sc->ste_cdata.ste_tx_prod) { cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; if (!(cur_tx->ste_ptr->ste_ctl & STE_TXCTL_DMADONE)) break; m_freem(cur_tx->ste_mbuf); cur_tx->ste_mbuf = NULL; ifp->if_flags &= ~IFF_OACTIVE; ifp->if_opackets++; STE_INC(idx, STE_TX_LIST_CNT); } sc->ste_cdata.ste_tx_cons = idx; if (idx == sc->ste_cdata.ste_tx_prod) ifp->if_timer = 0; } static void ste_stats_update(xsc) void *xsc; { struct ste_softc *sc; struct ifnet *ifp; struct mii_data *mii; sc = xsc; STE_LOCK(sc); ifp = &sc->arpcom.ac_if; mii = device_get_softc(sc->ste_miibus); ifp->if_collisions += CSR_READ_1(sc, STE_LATE_COLLS) + CSR_READ_1(sc, STE_MULTI_COLLS) + CSR_READ_1(sc, STE_SINGLE_COLLS); if (!sc->ste_link) { mii_pollstat(mii); if (mii->mii_media_status & IFM_ACTIVE && IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { sc->ste_link++; /* * we don't get a call-back on re-init so do it * otherwise we get stuck in the wrong link state */ ste_miibus_statchg(sc->ste_dev); if (ifp->if_snd.ifq_head != NULL) ste_start(ifp); } } sc->ste_stat_ch = timeout(ste_stats_update, sc, hz); STE_UNLOCK(sc); return; } /* * Probe for a Sundance ST201 chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int ste_probe(dev) device_t dev; { struct ste_type *t; t = ste_devs; while(t->ste_name != NULL) { if ((pci_get_vendor(dev) == t->ste_vid) && (pci_get_device(dev) == t->ste_did)) { device_set_desc(dev, t->ste_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int ste_attach(dev) device_t dev; { struct ste_softc *sc; struct ifnet *ifp; int unit, error = 0, rid; sc = device_get_softc(dev); unit = device_get_unit(dev); sc->ste_dev = dev; /* * Only use one PHY since this chip reports multiple * Note on the DFE-550 the PHY is at 1 on the DFE-580 * it is at 0 & 1. It is rev 0x12. */ if (pci_get_vendor(dev) == DL_VENDORID && pci_get_device(dev) == DL_DEVICEID_DL10050 && pci_get_revid(dev) == 0x12 ) sc->ste_one_phy = 1; mtx_init(&sc->ste_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, STE_PCI_LOIO, 4); - membase = pci_read_config(dev, STE_PCI_LOMEM, 4); - irq = pci_read_config(dev, STE_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("ste%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, STE_PCI_LOIO, iobase, 4); - pci_write_config(dev, STE_PCI_LOMEM, membase, 4); - pci_write_config(dev, STE_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = STE_RID; sc->ste_res = bus_alloc_resource_any(dev, STE_RES, &rid, RF_ACTIVE); if (sc->ste_res == NULL) { printf ("ste%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->ste_btag = rman_get_bustag(sc->ste_res); sc->ste_bhandle = rman_get_bushandle(sc->ste_res); /* Allocate interrupt */ rid = 0; sc->ste_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->ste_irq == NULL) { printf("ste%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } callout_handle_init(&sc->ste_stat_ch); /* Reset the adapter. */ ste_reset(sc); /* * Get station address from the EEPROM. */ if (ste_read_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr, STE_EEADDR_NODE0, 3, 0)) { printf("ste%d: failed to read station address\n", unit); error = ENXIO;; goto fail; } sc->ste_unit = unit; /* Allocate the descriptor queues. */ sc->ste_ldata = contigmalloc(sizeof(struct ste_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->ste_ldata == NULL) { printf("ste%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->ste_ldata, sizeof(struct ste_list_data)); /* Do MII setup. */ if (mii_phy_probe(dev, &sc->ste_miibus, ste_ifmedia_upd, ste_ifmedia_sts)) { printf("ste%d: MII without any phy!\n", sc->ste_unit); error = ENXIO; goto fail; } ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = ste_ioctl; ifp->if_start = ste_start; ifp->if_watchdog = ste_watchdog; ifp->if_init = ste_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = STE_TX_LIST_CNT - 1; sc->ste_tx_thresh = STE_TXSTART_THRESH; /* * Call MI attach routine. */ ether_ifattach(ifp, sc->arpcom.ac_enaddr); /* * Tell the upper layer(s) we support long frames. */ ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header); ifp->if_capabilities |= IFCAP_VLAN_MTU; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->ste_irq, INTR_TYPE_NET, ste_intr, sc, &sc->ste_intrhand); if (error) { printf("ste%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) ste_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int ste_detach(dev) device_t dev; { struct ste_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->ste_mtx), ("ste mutex not initialized")); STE_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { ste_stop(sc); ether_ifdetach(ifp); } if (sc->ste_miibus) device_delete_child(dev, sc->ste_miibus); bus_generic_detach(dev); if (sc->ste_intrhand) bus_teardown_intr(dev, sc->ste_irq, sc->ste_intrhand); if (sc->ste_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->ste_irq); if (sc->ste_res) bus_release_resource(dev, STE_RES, STE_RID, sc->ste_res); if (sc->ste_ldata) { contigfree(sc->ste_ldata, sizeof(struct ste_list_data), M_DEVBUF); } STE_UNLOCK(sc); mtx_destroy(&sc->ste_mtx); return(0); } static int ste_newbuf(sc, c, m) struct ste_softc *sc; struct ste_chain_onefrag *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, ETHER_ALIGN); c->ste_mbuf = m_new; c->ste_ptr->ste_status = 0; c->ste_ptr->ste_frag.ste_addr = vtophys(mtod(m_new, caddr_t)); c->ste_ptr->ste_frag.ste_len = (1536 + ETHER_VLAN_ENCAP_LEN) | STE_FRAG_LAST; return(0); } static int ste_init_rx_list(sc) struct ste_softc *sc; { struct ste_chain_data *cd; struct ste_list_data *ld; int i; cd = &sc->ste_cdata; ld = sc->ste_ldata; for (i = 0; i < STE_RX_LIST_CNT; i++) { cd->ste_rx_chain[i].ste_ptr = &ld->ste_rx_list[i]; if (ste_newbuf(sc, &cd->ste_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (STE_RX_LIST_CNT - 1)) { cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[0]; ld->ste_rx_list[i].ste_next = vtophys(&ld->ste_rx_list[0]); } else { cd->ste_rx_chain[i].ste_next = &cd->ste_rx_chain[i + 1]; ld->ste_rx_list[i].ste_next = vtophys(&ld->ste_rx_list[i + 1]); } ld->ste_rx_list[i].ste_status = 0; } cd->ste_rx_head = &cd->ste_rx_chain[0]; return(0); } static void ste_init_tx_list(sc) struct ste_softc *sc; { struct ste_chain_data *cd; struct ste_list_data *ld; int i; cd = &sc->ste_cdata; ld = sc->ste_ldata; for (i = 0; i < STE_TX_LIST_CNT; i++) { cd->ste_tx_chain[i].ste_ptr = &ld->ste_tx_list[i]; cd->ste_tx_chain[i].ste_ptr->ste_next = 0; cd->ste_tx_chain[i].ste_ptr->ste_ctl = 0; cd->ste_tx_chain[i].ste_phys = vtophys(&ld->ste_tx_list[i]); if (i == (STE_TX_LIST_CNT - 1)) cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[0]; else cd->ste_tx_chain[i].ste_next = &cd->ste_tx_chain[i + 1]; } cd->ste_tx_prod = 0; cd->ste_tx_cons = 0; return; } static void ste_init(xsc) void *xsc; { struct ste_softc *sc; int i; struct ifnet *ifp; sc = xsc; STE_LOCK(sc); ifp = &sc->arpcom.ac_if; ste_stop(sc); /* Init our MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { CSR_WRITE_1(sc, STE_PAR0 + i, sc->arpcom.ac_enaddr[i]); } /* Init RX list */ if (ste_init_rx_list(sc) == ENOBUFS) { printf("ste%d: initialization failed: no " "memory for RX buffers\n", sc->ste_unit); ste_stop(sc); STE_UNLOCK(sc); return; } /* Set RX polling interval */ CSR_WRITE_1(sc, STE_RX_DMAPOLL_PERIOD, 64); /* Init TX descriptors */ ste_init_tx_list(sc); /* Set the TX freethresh value */ CSR_WRITE_1(sc, STE_TX_DMABURST_THRESH, STE_PACKET_SIZE >> 8); /* Set the TX start threshold for best performance. */ CSR_WRITE_2(sc, STE_TX_STARTTHRESH, sc->ste_tx_thresh); /* Set the TX reclaim threshold. */ CSR_WRITE_1(sc, STE_TX_RECLAIM_THRESH, (STE_PACKET_SIZE >> 4)); /* Set up the RX filter. */ CSR_WRITE_1(sc, STE_RX_MODE, STE_RXMODE_UNICAST); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); } else { STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); } /* Set capture broadcast bit to accept broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); } else { STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_BROADCAST); } ste_setmulti(sc); /* Load the address of the RX list. */ STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); ste_wait(sc); CSR_WRITE_4(sc, STE_RX_DMALIST_PTR, vtophys(&sc->ste_ldata->ste_rx_list[0])); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_RXDMA_UNSTALL); /* Set TX polling interval (defer until we TX first packet */ CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 0); /* Load address of the TX list */ STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); ste_wait(sc); CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, 0); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); ste_wait(sc); sc->ste_tx_prev = NULL; /* Enable receiver and transmitter */ CSR_WRITE_2(sc, STE_MACCTL0, 0); CSR_WRITE_2(sc, STE_MACCTL1, 0); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_ENABLE); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_ENABLE); /* Enable stats counters. */ STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_ENABLE); CSR_WRITE_2(sc, STE_ISR, 0xFFFF); #ifdef DEVICE_POLLING /* Disable interrupts if we are polling. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_2(sc, STE_IMR, 0); else #endif /* DEVICE_POLLING */ /* Enable interrupts. */ CSR_WRITE_2(sc, STE_IMR, STE_INTRS); /* Accept VLAN length packets */ CSR_WRITE_2(sc, STE_MAX_FRAMELEN, ETHER_MAX_LEN + ETHER_VLAN_ENCAP_LEN); ste_ifmedia_upd(ifp); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->ste_stat_ch = timeout(ste_stats_update, sc, hz); STE_UNLOCK(sc); return; } static void ste_stop(sc) struct ste_softc *sc; { int i; struct ifnet *ifp; STE_LOCK(sc); ifp = &sc->arpcom.ac_if; untimeout(ste_stats_update, sc, sc->ste_stat_ch); ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif /* DEVICE_POLLING */ CSR_WRITE_2(sc, STE_IMR, 0); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_TX_DISABLE); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_RX_DISABLE); STE_SETBIT2(sc, STE_MACCTL1, STE_MACCTL1_STATS_DISABLE); STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); STE_SETBIT2(sc, STE_DMACTL, STE_DMACTL_RXDMA_STALL); ste_wait(sc); /* * Try really hard to stop the RX engine or under heavy RX * data chip will write into de-allocated memory. */ ste_reset(sc); sc->ste_link = 0; for (i = 0; i < STE_RX_LIST_CNT; i++) { if (sc->ste_cdata.ste_rx_chain[i].ste_mbuf != NULL) { m_freem(sc->ste_cdata.ste_rx_chain[i].ste_mbuf); sc->ste_cdata.ste_rx_chain[i].ste_mbuf = NULL; } } for (i = 0; i < STE_TX_LIST_CNT; i++) { if (sc->ste_cdata.ste_tx_chain[i].ste_mbuf != NULL) { m_freem(sc->ste_cdata.ste_tx_chain[i].ste_mbuf); sc->ste_cdata.ste_tx_chain[i].ste_mbuf = NULL; } } bzero(sc->ste_ldata, sizeof(struct ste_list_data)); STE_UNLOCK(sc); return; } static void ste_reset(sc) struct ste_softc *sc; { int i; STE_SETBIT4(sc, STE_ASICCTL, STE_ASICCTL_GLOBAL_RESET|STE_ASICCTL_RX_RESET| STE_ASICCTL_TX_RESET|STE_ASICCTL_DMA_RESET| STE_ASICCTL_FIFO_RESET|STE_ASICCTL_NETWORK_RESET| STE_ASICCTL_AUTOINIT_RESET|STE_ASICCTL_HOST_RESET| STE_ASICCTL_EXTRESET_RESET); DELAY(100000); for (i = 0; i < STE_TIMEOUT; i++) { if (!(CSR_READ_4(sc, STE_ASICCTL) & STE_ASICCTL_RESET_BUSY)) break; } if (i == STE_TIMEOUT) printf("ste%d: global reset never completed\n", sc->ste_unit); return; } static int ste_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct ste_softc *sc; struct ifreq *ifr; struct mii_data *mii; int error = 0; sc = ifp->if_softc; STE_LOCK(sc); ifr = (struct ifreq *)data; switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->ste_if_flags & IFF_PROMISC)) { STE_SETBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); } else if (ifp->if_flags & IFF_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->ste_if_flags & IFF_PROMISC) { STE_CLRBIT1(sc, STE_RX_MODE, STE_RXMODE_PROMISC); } if (ifp->if_flags & IFF_RUNNING && (ifp->if_flags ^ sc->ste_if_flags) & IFF_ALLMULTI) ste_setmulti(sc); if (!(ifp->if_flags & IFF_RUNNING)) { sc->ste_tx_thresh = STE_TXSTART_THRESH; ste_init(sc); } } else { if (ifp->if_flags & IFF_RUNNING) ste_stop(sc); } sc->ste_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: ste_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->ste_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: ifp->if_capenable &= ~IFCAP_POLLING; ifp->if_capenable |= ifr->ifr_reqcap & IFCAP_POLLING; break; default: error = ether_ioctl(ifp, command, data); break; } STE_UNLOCK(sc); return(error); } static int ste_encap(sc, c, m_head) struct ste_softc *sc; struct ste_chain *c; struct mbuf *m_head; { int frag = 0; struct ste_frag *f = NULL; struct mbuf *m; struct ste_desc *d; d = c->ste_ptr; d->ste_ctl = 0; encap_retry: for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == STE_MAXFRAGS) break; f = &d->ste_frags[frag]; f->ste_addr = vtophys(mtod(m, vm_offset_t)); f->ste_len = m->m_len; frag++; } } if (m != NULL) { struct mbuf *mn; /* * We ran out of segments. We have to recopy this * mbuf chain first. Bail out if we can't get the * new buffers. */ mn = m_defrag(m_head, M_DONTWAIT); if (mn == NULL) { m_freem(m_head); return ENOMEM; } m_head = mn; goto encap_retry; } c->ste_mbuf = m_head; d->ste_frags[frag - 1].ste_len |= STE_FRAG_LAST; d->ste_ctl = 1; return(0); } static void ste_start(ifp) struct ifnet *ifp; { struct ste_softc *sc; struct mbuf *m_head = NULL; struct ste_chain *cur_tx; int idx; sc = ifp->if_softc; STE_LOCK(sc); if (!sc->ste_link) { STE_UNLOCK(sc); return; } if (ifp->if_flags & IFF_OACTIVE) { STE_UNLOCK(sc); return; } idx = sc->ste_cdata.ste_tx_prod; while(sc->ste_cdata.ste_tx_chain[idx].ste_mbuf == NULL) { /* * We cannot re-use the last (free) descriptor; * the chip may not have read its ste_next yet. */ if (STE_NEXT(idx, STE_TX_LIST_CNT) == sc->ste_cdata.ste_tx_cons) { ifp->if_flags |= IFF_OACTIVE; break; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; cur_tx = &sc->ste_cdata.ste_tx_chain[idx]; if (ste_encap(sc, cur_tx, m_head) != 0) break; cur_tx->ste_ptr->ste_next = 0; if (sc->ste_tx_prev == NULL) { cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; /* Load address of the TX list */ STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_STALL); ste_wait(sc); CSR_WRITE_4(sc, STE_TX_DMALIST_PTR, vtophys(&sc->ste_ldata->ste_tx_list[0])); /* Set TX polling interval to start TX engine */ CSR_WRITE_1(sc, STE_TX_DMAPOLL_PERIOD, 64); STE_SETBIT4(sc, STE_DMACTL, STE_DMACTL_TXDMA_UNSTALL); ste_wait(sc); }else{ cur_tx->ste_ptr->ste_ctl = STE_TXCTL_DMAINTR | 1; sc->ste_tx_prev->ste_ptr->ste_next = cur_tx->ste_phys; } sc->ste_tx_prev = cur_tx; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->ste_mbuf); STE_INC(idx, STE_TX_LIST_CNT); ifp->if_timer = 5; } sc->ste_cdata.ste_tx_prod = idx; STE_UNLOCK(sc); return; } static void ste_watchdog(ifp) struct ifnet *ifp; { struct ste_softc *sc; sc = ifp->if_softc; STE_LOCK(sc); ifp->if_oerrors++; printf("ste%d: watchdog timeout\n", sc->ste_unit); ste_txeoc(sc); ste_txeof(sc); ste_rxeoc(sc); ste_rxeof(sc); ste_reset(sc); ste_init(sc); if (ifp->if_snd.ifq_head != NULL) ste_start(ifp); STE_UNLOCK(sc); return; } static void ste_shutdown(dev) device_t dev; { struct ste_softc *sc; sc = device_get_softc(dev); ste_stop(sc); return; } Index: head/sys/pci/if_vr.c =================================================================== --- head/sys/pci/if_vr.c (revision 131252) +++ head/sys/pci/if_vr.c (revision 131253) @@ -1,1817 +1,1793 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * VIA Rhine fast ethernet PCI NIC driver * * Supports various network adapters based on the VIA Rhine * and Rhine II PCI controllers, including the D-Link DFE530TX. * Datasheets are available at http://www.via.com.tw. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The VIA Rhine controllers are similar in some respects to the * the DEC tulip chips, except less complicated. The controller * uses an MII bus and an external physical layer interface. The * receiver has a one entry perfect filter and a 64-bit hash table * multicast filter. Transmit and receive descriptors are similar * to the tulip. * * The Rhine has a serious flaw in its transmit DMA mechanism: * transmit buffers must be longword aligned. Unfortunately, * FreeBSD doesn't guarantee that mbufs will be filled in starting * at longword boundaries, so we have to do a buffer copy before * transmission. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include #define VR_USEIOSPACE #include MODULE_DEPEND(vr, pci, 1, 1, 1); MODULE_DEPEND(vr, ether, 1, 1, 1); MODULE_DEPEND(vr, miibus, 1, 1, 1); /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #undef VR_USESWSHIFT /* * Various supported device vendors/types and their names. */ static struct vr_type vr_devs[] = { { VIA_VENDORID, VIA_DEVICEID_RHINE, "VIA VT3043 Rhine I 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_II, "VIA VT86C100A Rhine II 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_II_2, "VIA VT6102 Rhine II 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_III, "VIA VT6105 Rhine III 10/100BaseTX" }, { VIA_VENDORID, VIA_DEVICEID_RHINE_III_M, "VIA VT6105M Rhine III 10/100BaseTX" }, { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, "Delta Electronics Rhine II 10/100BaseTX" }, { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, "Addtron Technology Rhine II 10/100BaseTX" }, { 0, 0, NULL } }; static int vr_probe (device_t); static int vr_attach (device_t); static int vr_detach (device_t); static int vr_newbuf (struct vr_softc *, struct vr_chain_onefrag *, struct mbuf *); static int vr_encap (struct vr_softc *, struct vr_chain *, struct mbuf * ); static void vr_rxeof (struct vr_softc *); static void vr_rxeoc (struct vr_softc *); static void vr_txeof (struct vr_softc *); static void vr_tick (void *); static void vr_intr (void *); static void vr_start (struct ifnet *); static int vr_ioctl (struct ifnet *, u_long, caddr_t); static void vr_init (void *); static void vr_stop (struct vr_softc *); static void vr_watchdog (struct ifnet *); static void vr_shutdown (device_t); static int vr_ifmedia_upd (struct ifnet *); static void vr_ifmedia_sts (struct ifnet *, struct ifmediareq *); #ifdef VR_USESWSHIFT static void vr_mii_sync (struct vr_softc *); static void vr_mii_send (struct vr_softc *, u_int32_t, int); #endif static int vr_mii_readreg (struct vr_softc *, struct vr_mii_frame *); static int vr_mii_writereg (struct vr_softc *, struct vr_mii_frame *); static int vr_miibus_readreg (device_t, int, int); static int vr_miibus_writereg (device_t, int, int, int); static void vr_miibus_statchg (device_t); static void vr_setcfg (struct vr_softc *, int); static void vr_setmulti (struct vr_softc *); static void vr_reset (struct vr_softc *); static int vr_list_rx_init (struct vr_softc *); static int vr_list_tx_init (struct vr_softc *); #ifdef VR_USEIOSPACE #define VR_RES SYS_RES_IOPORT #define VR_RID VR_PCI_LOIO #else #define VR_RES SYS_RES_MEMORY #define VR_RID VR_PCI_LOMEM #endif static device_method_t vr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, vr_probe), DEVMETHOD(device_attach, vr_attach), DEVMETHOD(device_detach, vr_detach), DEVMETHOD(device_shutdown, vr_shutdown), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, vr_miibus_readreg), DEVMETHOD(miibus_writereg, vr_miibus_writereg), DEVMETHOD(miibus_statchg, vr_miibus_statchg), { 0, 0 } }; static driver_t vr_driver = { "vr", vr_methods, sizeof(struct vr_softc) }; static devclass_t vr_devclass; DRIVER_MODULE(vr, pci, vr_driver, vr_devclass, 0, 0); DRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); #define VR_SETBIT(sc, reg, x) \ CSR_WRITE_1(sc, reg, \ CSR_READ_1(sc, reg) | (x)) #define VR_CLRBIT(sc, reg, x) \ CSR_WRITE_1(sc, reg, \ CSR_READ_1(sc, reg) & ~(x)) #define VR_SETBIT16(sc, reg, x) \ CSR_WRITE_2(sc, reg, \ CSR_READ_2(sc, reg) | (x)) #define VR_CLRBIT16(sc, reg, x) \ CSR_WRITE_2(sc, reg, \ CSR_READ_2(sc, reg) & ~(x)) #define VR_SETBIT32(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | (x)) #define VR_CLRBIT32(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) \ CSR_WRITE_1(sc, VR_MIICMD, \ CSR_READ_1(sc, VR_MIICMD) | (x)) #define SIO_CLR(x) \ CSR_WRITE_1(sc, VR_MIICMD, \ CSR_READ_1(sc, VR_MIICMD) & ~(x)) #ifdef VR_USESWSHIFT /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void vr_mii_sync(sc) struct vr_softc *sc; { register int i; SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); for (i = 0; i < 32; i++) { SIO_SET(VR_MIICMD_CLK); DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void vr_mii_send(sc, bits, cnt) struct vr_softc *sc; u_int32_t bits; int cnt; { int i; SIO_CLR(VR_MIICMD_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { SIO_SET(VR_MIICMD_DATAIN); } else { SIO_CLR(VR_MIICMD_DATAIN); } DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); } } #endif /* * Read an PHY register through the MII. */ static int vr_mii_readreg(sc, frame) struct vr_softc *sc; struct vr_mii_frame *frame; #ifdef VR_USESWSHIFT { int i, ack; VR_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = VR_MII_STARTDELIM; frame->mii_opcode = VR_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_1(sc, VR_MIICMD, 0); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); /* * Turn on data xmit. */ SIO_SET(VR_MIICMD_DIR); vr_mii_sync(sc); /* * Send command/address info. */ vr_mii_send(sc, frame->mii_stdelim, 2); vr_mii_send(sc, frame->mii_opcode, 2); vr_mii_send(sc, frame->mii_phyaddr, 5); vr_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); /* Turn off xmit. */ SIO_CLR(VR_MIICMD_DIR); /* Check for ack */ SIO_CLR(VR_MIICMD_CLK); DELAY(1); ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; SIO_SET(VR_MIICMD_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { SIO_CLR(VR_MIICMD_CLK); DELAY(1); if (!ack) { if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) frame->mii_data |= i; DELAY(1); } SIO_SET(VR_MIICMD_CLK); DELAY(1); } fail: SIO_CLR(VR_MIICMD_CLK); DELAY(1); SIO_SET(VR_MIICMD_CLK); DELAY(1); VR_UNLOCK(sc); if (ack) return(1); return(0); } #else { int s, i; s = splimp(); /* Set the PHY-adress */ CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| frame->mii_phyaddr); /* Set the register-adress */ CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_READ_ENB); for (i = 0; i < 10000; i++) { if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_READ_ENB) == 0) break; DELAY(1); } frame->mii_data = CSR_READ_2(sc, VR_MIIDATA); (void)splx(s); return(0); } #endif /* * Write to a PHY register through the MII. */ static int vr_mii_writereg(sc, frame) struct vr_softc *sc; struct vr_mii_frame *frame; #ifdef VR_USESWSHIFT { VR_LOCK(sc); CSR_WRITE_1(sc, VR_MIICMD, 0); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); /* * Set up frame for TX. */ frame->mii_stdelim = VR_MII_STARTDELIM; frame->mii_opcode = VR_MII_WRITEOP; frame->mii_turnaround = VR_MII_TURNAROUND; /* * Turn on data output. */ SIO_SET(VR_MIICMD_DIR); vr_mii_sync(sc); vr_mii_send(sc, frame->mii_stdelim, 2); vr_mii_send(sc, frame->mii_opcode, 2); vr_mii_send(sc, frame->mii_phyaddr, 5); vr_mii_send(sc, frame->mii_regaddr, 5); vr_mii_send(sc, frame->mii_turnaround, 2); vr_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ SIO_SET(VR_MIICMD_CLK); DELAY(1); SIO_CLR(VR_MIICMD_CLK); DELAY(1); /* * Turn off xmit. */ SIO_CLR(VR_MIICMD_DIR); VR_UNLOCK(sc); return(0); } #else { int s, i; s = splimp(); /* Set the PHY-adress */ CSR_WRITE_1(sc, VR_PHYADDR, (CSR_READ_1(sc, VR_PHYADDR)& 0xe0)| frame->mii_phyaddr); /* Set the register-adress and data to write */ CSR_WRITE_1(sc, VR_MIIADDR, frame->mii_regaddr); CSR_WRITE_2(sc, VR_MIIDATA, frame->mii_data); VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_WRITE_ENB); for (i = 0; i < 10000; i++) { if ((CSR_READ_1(sc, VR_MIICMD) & VR_MIICMD_WRITE_ENB) == 0) break; DELAY(1); } (void)splx(s); return(0); } #endif static int vr_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct vr_softc *sc; struct vr_mii_frame frame; sc = device_get_softc(dev); switch (sc->vr_revid) { case REV_ID_VT6102_APOLLO: if (phy != 1) return 0; default: break; } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; vr_mii_readreg(sc, &frame); return(frame.mii_data); } static int vr_miibus_writereg(dev, phy, reg, data) device_t dev; u_int16_t phy, reg, data; { struct vr_softc *sc; struct vr_mii_frame frame; sc = device_get_softc(dev); switch (sc->vr_revid) { case REV_ID_VT6102_APOLLO: if (phy != 1) return 0; default: break; } bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; vr_mii_writereg(sc, &frame); return(0); } static void vr_miibus_statchg(dev) device_t dev; { struct vr_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); VR_LOCK(sc); mii = device_get_softc(sc->vr_miibus); vr_setcfg(sc, mii->mii_media_active); VR_UNLOCK(sc); return; } /* * Program the 64-bit multicast hash filter. */ static void vr_setmulti(sc) struct vr_softc *sc; { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; rxfilt = CSR_READ_1(sc, VR_RXCFG); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxfilt |= VR_RXCFG_RX_MULTI; CSR_WRITE_1(sc, VR_RXCFG, rxfilt); CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, VR_MAR0, 0); CSR_WRITE_4(sc, VR_MAR1, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if (mcnt) rxfilt |= VR_RXCFG_RX_MULTI; else rxfilt &= ~VR_RXCFG_RX_MULTI; CSR_WRITE_4(sc, VR_MAR0, hashes[0]); CSR_WRITE_4(sc, VR_MAR1, hashes[1]); CSR_WRITE_1(sc, VR_RXCFG, rxfilt); return; } /* * In order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */ static void vr_setcfg(sc, media) struct vr_softc *sc; int media; { int restart = 0; if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { restart = 1; VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); } if ((media & IFM_GMASK) == IFM_FDX) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); else VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); if (restart) VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); return; } static void vr_reset(sc) struct vr_softc *sc; { register int i; VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); for (i = 0; i < VR_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) break; } if (i == VR_TIMEOUT) { if (sc->vr_revid < REV_ID_VT3065_A) printf("vr%d: reset never completed!\n", sc->vr_unit); else { /* Use newer force reset command */ printf("vr%d: Using force reset command.\n", sc->vr_unit); VR_SETBIT(sc, VR_MISC_CR1, VR_MISCCR1_FORSRST); } } /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); return; } /* * Probe for a VIA Rhine chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int vr_probe(dev) device_t dev; { struct vr_type *t; t = vr_devs; while(t->vr_name != NULL) { if ((pci_get_vendor(dev) == t->vr_vid) && (pci_get_device(dev) == t->vr_did)) { device_set_desc(dev, t->vr_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int vr_attach(dev) device_t dev; { int i; u_char eaddr[ETHER_ADDR_LEN]; struct vr_softc *sc; struct ifnet *ifp; int unit, error = 0, rid; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->vr_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, VR_PCI_LOIO, 4); - membase = pci_read_config(dev, VR_PCI_LOMEM, 4); - irq = pci_read_config(dev, VR_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("vr%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, VR_PCI_LOIO, iobase, 4); - pci_write_config(dev, VR_PCI_LOMEM, membase, 4); - pci_write_config(dev, VR_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); sc->vr_revid = pci_read_config(dev, VR_PCI_REVID, 4) & 0x000000FF; rid = VR_RID; sc->vr_res = bus_alloc_resource_any(dev, VR_RES, &rid, RF_ACTIVE); if (sc->vr_res == NULL) { printf("vr%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->vr_btag = rman_get_bustag(sc->vr_res); sc->vr_bhandle = rman_get_bushandle(sc->vr_res); /* Allocate interrupt */ rid = 0; sc->vr_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->vr_irq == NULL) { printf("vr%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* * Windows may put the chip in suspend mode when it * shuts down. Be sure to kick it in the head to wake it * up again. */ VR_CLRBIT(sc, VR_STICKHW, (VR_STICKHW_DS0|VR_STICKHW_DS1)); /* Reset the adapter. */ vr_reset(sc); /* * Turn on bit2 (MIION) in PCI configuration register 0x53 during * initialization and disable AUTOPOLL. */ pci_write_config(dev, VR_PCI_MODE, pci_read_config(dev, VR_PCI_MODE, 4) | (VR_MODE3_MIION << 24), 4); VR_CLRBIT(sc, VR_MIICMD, VR_MIICMD_AUTOPOLL); /* * Get station address. The way the Rhine chips work, * you're not allowed to directly access the EEPROM once * they've been programmed a special way. Consequently, * we need to read the node address from the PAR0 and PAR1 * registers. */ VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); DELAY(200); for (i = 0; i < ETHER_ADDR_LEN; i++) eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); sc->vr_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->vr_ldata == NULL) { printf("vr%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->vr_ldata, sizeof(struct vr_list_data)); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = vr_ioctl; ifp->if_start = vr_start; ifp->if_watchdog = vr_watchdog; ifp->if_init = vr_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; #ifdef DEVICE_POLLING ifp->if_capabilities |= IFCAP_POLLING; #endif ifp->if_capenable = ifp->if_capabilities; /* * Do MII setup. */ if (mii_phy_probe(dev, &sc->vr_miibus, vr_ifmedia_upd, vr_ifmedia_sts)) { printf("vr%d: MII without any phy!\n", sc->vr_unit); error = ENXIO; goto fail; } callout_handle_init(&sc->vr_stat_ch); /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, vr_intr, sc, &sc->vr_intrhand); if (error) { printf("vr%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) vr_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int vr_detach(dev) device_t dev; { struct vr_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->vr_mtx), ("vr mutex not initialized")); VR_LOCK(sc); ifp = &sc->arpcom.ac_if; /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { vr_stop(sc); ether_ifdetach(ifp); } if (sc->vr_miibus) device_delete_child(dev, sc->vr_miibus); bus_generic_detach(dev); if (sc->vr_intrhand) bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); if (sc->vr_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); if (sc->vr_res) bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); if (sc->vr_ldata) contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); VR_UNLOCK(sc); mtx_destroy(&sc->vr_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int vr_list_tx_init(sc) struct vr_softc *sc; { struct vr_chain_data *cd; struct vr_list_data *ld; int i; cd = &sc->vr_cdata; ld = sc->vr_ldata; for (i = 0; i < VR_TX_LIST_CNT; i++) { cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; if (i == (VR_TX_LIST_CNT - 1)) cd->vr_tx_chain[i].vr_nextdesc = &cd->vr_tx_chain[0]; else cd->vr_tx_chain[i].vr_nextdesc = &cd->vr_tx_chain[i + 1]; } cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0]; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int vr_list_rx_init(sc) struct vr_softc *sc; { struct vr_chain_data *cd; struct vr_list_data *ld; int i; cd = &sc->vr_cdata; ld = sc->vr_ldata; for (i = 0; i < VR_RX_LIST_CNT; i++) { cd->vr_rx_chain[i].vr_ptr = (struct vr_desc *)&ld->vr_rx_list[i]; if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (VR_RX_LIST_CNT - 1)) { cd->vr_rx_chain[i].vr_nextdesc = &cd->vr_rx_chain[0]; ld->vr_rx_list[i].vr_next = vtophys(&ld->vr_rx_list[0]); } else { cd->vr_rx_chain[i].vr_nextdesc = &cd->vr_rx_chain[i + 1]; ld->vr_rx_list[i].vr_next = vtophys(&ld->vr_rx_list[i + 1]); } } cd->vr_rx_head = &cd->vr_rx_chain[0]; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. * Note: the length fields are only 11 bits wide, which means the * largest size we can specify is 2047. This is important because * MCLBYTES is 2048, so we have to subtract one otherwise we'll * overflow the field and make a mess. */ static int vr_newbuf(sc, c, m) struct vr_softc *sc; struct vr_chain_onefrag *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(ENOBUFS); } m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, sizeof(u_int64_t)); c->vr_mbuf = m_new; c->vr_ptr->vr_status = VR_RXSTAT; c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void vr_rxeof(sc) struct vr_softc *sc; { struct mbuf *m, *m0; struct ifnet *ifp; struct vr_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; VR_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & VR_RXSTAT_OWN)) { #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) { if (sc->rxcycles <= 0) break; sc->rxcycles--; } #endif /* DEVICE_POLLING */ m0 = NULL; cur_rx = sc->vr_cdata.vr_rx_head; sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; m = cur_rx->vr_mbuf; /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & VR_RXSTAT_RXERR) { ifp->if_ierrors++; printf("vr%d: rx error (%02x):", sc->vr_unit, rxstat & 0x000000ff); if (rxstat & VR_RXSTAT_CRCERR) printf(" crc error"); if (rxstat & VR_RXSTAT_FRAMEALIGNERR) printf(" frame alignment error\n"); if (rxstat & VR_RXSTAT_FIFOOFLOW) printf(" FIFO overflow"); if (rxstat & VR_RXSTAT_GIANT) printf(" received giant packet"); if (rxstat & VR_RXSTAT_RUNT) printf(" received runt packet"); if (rxstat & VR_RXSTAT_BUSERR) printf(" system bus error"); if (rxstat & VR_RXSTAT_BUFFERR) printf("rx buffer error"); printf("\n"); vr_newbuf(sc, cur_rx, m); continue; } /* No errors; receive the packet. */ total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); /* * XXX The VIA Rhine chip includes the CRC with every * received frame, and there's no way to turn this * behavior off (at least, I can't find anything in * the manual that explains how to do it) so we have * to trim off the CRC manually. */ total_len -= ETHER_CRC_LEN; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); vr_newbuf(sc, cur_rx, m); if (m0 == NULL) { ifp->if_ierrors++; continue; } m = m0; ifp->if_ipackets++; VR_UNLOCK(sc); (*ifp->if_input)(ifp, m); VR_LOCK(sc); } return; } static void vr_rxeoc(sc) struct vr_softc *sc; { struct ifnet *ifp; int i; ifp = &sc->arpcom.ac_if; ifp->if_ierrors++; VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); DELAY(10000); for (i = 0x400; i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RX_ON); i--) ; /* Wait for receiver to stop */ if (!i) { printf("vr%d: rx shutdown error!\n", sc->vr_unit); sc->vr_flags |= VR_F_RESTART; return; } vr_rxeof(sc); CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void vr_txeof(sc) struct vr_softc *sc; { struct vr_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ cur_tx = sc->vr_cdata.vr_tx_cons; while (cur_tx->vr_mbuf != NULL) { u_int32_t txstat; int i; txstat = cur_tx->vr_ptr->vr_status; if ((txstat & VR_TXSTAT_ABRT) || (txstat & VR_TXSTAT_UDF)) { for (i = 0x400; i && (CSR_READ_2(sc, VR_COMMAND) & VR_CMD_TX_ON); i--) ; /* Wait for chip to shutdown */ if (!i) { printf("vr%d: tx shutdown timeout\n", sc->vr_unit); sc->vr_flags |= VR_F_RESTART; break; } VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; CSR_WRITE_4(sc, VR_TXADDR, vtophys(cur_tx->vr_ptr)); break; } if (txstat & VR_TXSTAT_OWN) break; if (txstat & VR_TXSTAT_ERRSUM) { ifp->if_oerrors++; if (txstat & VR_TXSTAT_DEFER) ifp->if_collisions++; if (txstat & VR_TXSTAT_LATECOLL) ifp->if_collisions++; } ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; m_freem(cur_tx->vr_mbuf); cur_tx->vr_mbuf = NULL; ifp->if_flags &= ~IFF_OACTIVE; cur_tx = cur_tx->vr_nextdesc; } sc->vr_cdata.vr_tx_cons = cur_tx; if (cur_tx->vr_mbuf == NULL) ifp->if_timer = 0; } static void vr_tick(xsc) void *xsc; { struct vr_softc *sc; struct mii_data *mii; sc = xsc; VR_LOCK(sc); if (sc->vr_flags & VR_F_RESTART) { printf("vr%d: restarting\n", sc->vr_unit); vr_stop(sc); vr_reset(sc); vr_init(sc); sc->vr_flags &= ~VR_F_RESTART; } mii = device_get_softc(sc->vr_miibus); mii_tick(mii); sc->vr_stat_ch = timeout(vr_tick, sc, hz); VR_UNLOCK(sc); return; } #ifdef DEVICE_POLLING static poll_handler_t vr_poll; static void vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { struct vr_softc *sc = ifp->if_softc; VR_LOCK(sc); if (!(ifp->if_capenable & IFCAP_POLLING)) { ether_poll_deregister(ifp); cmd = POLL_DEREGISTER; } if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); goto done; } sc->rxcycles = count; vr_rxeof(sc); vr_txeof(sc); if (ifp->if_snd.ifq_head != NULL) vr_start(ifp); if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */ u_int16_t status; status = CSR_READ_2(sc, VR_ISR); if (status) CSR_WRITE_2(sc, VR_ISR, status); if ((status & VR_INTRS) == 0) goto done; if (status & VR_ISR_RX_DROPPED) { printf("vr%d: rx packet lost\n", sc->vr_unit); ifp->if_ierrors++; } if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { printf("vr%d: receive error (%04x)", sc->vr_unit, status); if (status & VR_ISR_RX_NOBUF) printf(" no buffers"); if (status & VR_ISR_RX_OFLOW) printf(" overflow"); if (status & VR_ISR_RX_DROPPED) printf(" packet lost"); printf("\n"); vr_rxeoc(sc); } if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { vr_reset(sc); vr_init(sc); goto done; } if ((status & VR_ISR_UDFI) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_TX_ABRT)) { ifp->if_oerrors++; if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); } } } done: VR_UNLOCK(sc); } #endif /* DEVICE_POLLING */ static void vr_intr(arg) void *arg; { struct vr_softc *sc; struct ifnet *ifp; u_int16_t status; sc = arg; VR_LOCK(sc); ifp = &sc->arpcom.ac_if; #ifdef DEVICE_POLLING if (ifp->if_flags & IFF_POLLING) goto done; if ((ifp->if_capenable & IFCAP_POLLING) && ether_poll_register(vr_poll, ifp)) { /* ok, disable interrupts */ CSR_WRITE_2(sc, VR_IMR, 0x0000); vr_poll(ifp, 0, 1); goto done; } #endif /* DEVICE_POLLING */ /* Supress unwanted interrupts. */ if (!(ifp->if_flags & IFF_UP)) { vr_stop(sc); VR_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_2(sc, VR_IMR, 0x0000); for (;;) { status = CSR_READ_2(sc, VR_ISR); if (status) CSR_WRITE_2(sc, VR_ISR, status); if ((status & VR_INTRS) == 0) break; if (status & VR_ISR_RX_OK) vr_rxeof(sc); if (status & VR_ISR_RX_DROPPED) { printf("vr%d: rx packet lost\n", sc->vr_unit); ifp->if_ierrors++; } if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) { printf("vr%d: receive error (%04x)", sc->vr_unit, status); if (status & VR_ISR_RX_NOBUF) printf(" no buffers"); if (status & VR_ISR_RX_OFLOW) printf(" overflow"); if (status & VR_ISR_RX_DROPPED) printf(" packet lost"); printf("\n"); vr_rxeoc(sc); } if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) { vr_reset(sc); vr_init(sc); break; } if ((status & VR_ISR_TX_OK) || (status & VR_ISR_TX_ABRT) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_UDFI)) { vr_txeof(sc); if ((status & VR_ISR_UDFI) || (status & VR_ISR_TX_ABRT2) || (status & VR_ISR_TX_ABRT)) { ifp->if_oerrors++; if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) { VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); } } } } /* Re-enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); if (ifp->if_snd.ifq_head != NULL) { vr_start(ifp); } #ifdef DEVICE_POLLING done: #endif /* DEVICE_POLLING */ VR_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int vr_encap(sc, c, m_head) struct vr_softc *sc; struct vr_chain *c; struct mbuf *m_head; { struct vr_desc *f = NULL; struct mbuf *m; /* * The VIA Rhine wants packet buffers to be longword * aligned, but very often our mbufs aren't. Rather than * waste time trying to decide when to copy and when not * to copy, just do it all the time. */ m = m_defrag(m_head, M_DONTWAIT); if (m == NULL) { return(1); } /* * The Rhine chip doesn't auto-pad, so we have to make * sure to pad short frames out to the minimum frame length * ourselves. */ if (m->m_len < VR_MIN_FRAMELEN) { m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len; m->m_len = m->m_pkthdr.len; } c->vr_mbuf = m; f = c->vr_ptr; f->vr_data = vtophys(mtod(m, caddr_t)); f->vr_ctl = m->m_len; f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; f->vr_status = 0; f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; f->vr_next = vtophys(c->vr_nextdesc->vr_ptr); return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void vr_start(ifp) struct ifnet *ifp; { struct vr_softc *sc; struct mbuf *m_head; struct vr_chain *cur_tx; if (ifp->if_flags & IFF_OACTIVE) return; sc = ifp->if_softc; VR_LOCK(sc); cur_tx = sc->vr_cdata.vr_tx_prod; while (cur_tx->vr_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pack the data into the descriptor. */ if (vr_encap(sc, cur_tx, m_head)) { /* Rollback, send what we were able to encap. */ IF_PREPEND(&ifp->if_snd, m_head); break; } VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->vr_mbuf); cur_tx = cur_tx->vr_nextdesc; } if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) { sc->vr_cdata.vr_tx_prod = cur_tx; /* Tell the chip to start transmitting. */ VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); /* Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; if (cur_tx->vr_mbuf != NULL) ifp->if_flags |= IFF_OACTIVE; } VR_UNLOCK(sc); return; } static void vr_init(xsc) void *xsc; { struct vr_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; struct mii_data *mii; int i; VR_LOCK(sc); mii = device_get_softc(sc->vr_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ vr_stop(sc); vr_reset(sc); /* * Set our station address. */ for (i = 0; i < ETHER_ADDR_LEN; i++) CSR_WRITE_1(sc, VR_PAR0 + i, sc->arpcom.ac_enaddr[i]); /* Set DMA size */ VR_CLRBIT(sc, VR_BCR0, VR_BCR0_DMA_LENGTH); VR_SETBIT(sc, VR_BCR0, VR_BCR0_DMA_STORENFWD); /* * BCR0 and BCR1 can override the RXCFG and TXCFG registers, * so we must set both. */ VR_CLRBIT(sc, VR_BCR0, VR_BCR0_RX_THRESH); VR_SETBIT(sc, VR_BCR0, VR_BCR0_RXTHRESH128BYTES); VR_CLRBIT(sc, VR_BCR1, VR_BCR1_TX_THRESH); VR_SETBIT(sc, VR_BCR1, VR_BCR1_TXTHRESHSTORENFWD); VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_128BYTES); VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); /* Init circular RX list. */ if (vr_list_rx_init(sc) == ENOBUFS) { printf("vr%d: initialization failed: no " "memory for rx buffers\n", sc->vr_unit); vr_stop(sc); VR_UNLOCK(sc); return; } /* * Init tx descriptors. */ vr_list_tx_init(sc); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); else VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); /* Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); else VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); /* * Program the multicast filter, if necessary. */ vr_setmulti(sc); /* * Load the address of the RX list. */ CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| VR_CMD_TX_ON|VR_CMD_RX_ON| VR_CMD_RX_GO); CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); CSR_WRITE_2(sc, VR_ISR, 0xFFFF); #ifdef DEVICE_POLLING /* * Disable interrupts if we are polling. */ if (ifp->if_flags & IFF_POLLING) CSR_WRITE_2(sc, VR_IMR, 0); else #endif /* DEVICE_POLLING */ /* * Enable interrupts. */ CSR_WRITE_2(sc, VR_IMR, VR_INTRS); mii_mediachg(mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->vr_stat_ch = timeout(vr_tick, sc, hz); VR_UNLOCK(sc); return; } /* * Set media options. */ static int vr_ifmedia_upd(ifp) struct ifnet *ifp; { struct vr_softc *sc; sc = ifp->if_softc; if (ifp->if_flags & IFF_UP) vr_init(sc); return(0); } /* * Report current media status. */ static void vr_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct vr_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->vr_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int vr_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct vr_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; struct mii_data *mii; int error = 0; VR_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { vr_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) vr_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: vr_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->vr_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; break; default: error = ether_ioctl(ifp, command, data); break; } VR_UNLOCK(sc); return(error); } static void vr_watchdog(ifp) struct ifnet *ifp; { struct vr_softc *sc; sc = ifp->if_softc; VR_LOCK(sc); ifp->if_oerrors++; printf("vr%d: watchdog timeout\n", sc->vr_unit); vr_stop(sc); vr_reset(sc); vr_init(sc); if (ifp->if_snd.ifq_head != NULL) vr_start(ifp); VR_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void vr_stop(sc) struct vr_softc *sc; { register int i; struct ifnet *ifp; VR_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; untimeout(vr_tick, sc, sc->vr_stat_ch); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); #ifdef DEVICE_POLLING ether_poll_deregister(ifp); #endif /* DEVICE_POLLING */ VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); CSR_WRITE_2(sc, VR_IMR, 0x0000); CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); /* * Free data in the RX lists. */ for (i = 0; i < VR_RX_LIST_CNT; i++) { if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; } } bzero((char *)&sc->vr_ldata->vr_rx_list, sizeof(sc->vr_ldata->vr_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < VR_TX_LIST_CNT; i++) { if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; } } bzero((char *)&sc->vr_ldata->vr_tx_list, sizeof(sc->vr_ldata->vr_tx_list)); VR_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void vr_shutdown(dev) device_t dev; { struct vr_softc *sc; sc = device_get_softc(dev); vr_stop(sc); return; } Index: head/sys/pci/if_wb.c =================================================================== --- head/sys/pci/if_wb.c (revision 131252) +++ head/sys/pci/if_wb.c (revision 131253) @@ -1,1856 +1,1831 @@ /* * Copyright (c) 1997, 1998 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Winbond fast ethernet PCI NIC driver * * Supports various cheap network adapters based on the Winbond W89C840F * fast ethernet controller chip. This includes adapters manufactured by * Winbond itself and some made by Linksys. * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The Winbond W89C840F chip is a bus master; in some ways it resembles * a DEC 'tulip' chip, only not as complicated. Unfortunately, it has * one major difference which is that while the registers do many of * the same things as a tulip adapter, the offsets are different: where * tulip registers are typically spaced 8 bytes apart, the Winbond * registers are spaced 4 bytes apart. The receiver filter is also * programmed differently. * * Like the tulip, the Winbond chip uses small descriptors containing * a status word, a control word and 32-bit areas that can either be used * to point to two external data blocks, or to point to a single block * and another descriptor in a linked list. Descriptors can be grouped * together in blocks to form fixed length rings or can be chained * together in linked lists. A single packet may be spread out over * several descriptors if necessary. * * For the receive ring, this driver uses a linked list of descriptors, * each pointing to a single mbuf cluster buffer, which us large enough * to hold an entire packet. The link list is looped back to created a * closed ring. * * For transmission, the driver creates a linked list of 'super descriptors' * which each contain several individual descriptors linked toghether. * Each 'super descriptor' contains WB_MAXFRAGS descriptors, which we * abuse as fragment pointers. This allows us to use a buffer managment * scheme very similar to that used in the ThunderLAN and Etherlink XL * drivers. * * Autonegotiation is performed using the external PHY via the MII bus. * The sample boards I have all use a Davicom PHY. * * Note: the author of the Linux driver for the Winbond chip alludes * to some sort of flaw in the chip's design that seems to mandate some * drastic workaround which signigicantly impairs transmit performance. * I have no idea what he's on about: transmit performance with all * three of my test boards seems fine. */ #include "opt_bdg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for vtophys */ #include /* for vtophys */ #include #include #include #include #include #include #include #include #include #include /* "controller miibus0" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #define WB_USEIOSPACE #include MODULE_DEPEND(wb, pci, 1, 1, 1); MODULE_DEPEND(wb, ether, 1, 1, 1); MODULE_DEPEND(wb, miibus, 1, 1, 1); /* * Various supported device vendors/types and their names. */ static struct wb_type wb_devs[] = { { WB_VENDORID, WB_DEVICEID_840F, "Winbond W89C840F 10/100BaseTX" }, { CP_VENDORID, CP_DEVICEID_RL100, "Compex RL100-ATX 10/100baseTX" }, { 0, 0, NULL } }; static int wb_probe (device_t); static int wb_attach (device_t); static int wb_detach (device_t); static void wb_bfree (void *addr, void *args); static int wb_newbuf (struct wb_softc *, struct wb_chain_onefrag *, struct mbuf *); static int wb_encap (struct wb_softc *, struct wb_chain *, struct mbuf *); static void wb_rxeof (struct wb_softc *); static void wb_rxeoc (struct wb_softc *); static void wb_txeof (struct wb_softc *); static void wb_txeoc (struct wb_softc *); static void wb_intr (void *); static void wb_tick (void *); static void wb_start (struct ifnet *); static int wb_ioctl (struct ifnet *, u_long, caddr_t); static void wb_init (void *); static void wb_stop (struct wb_softc *); static void wb_watchdog (struct ifnet *); static void wb_shutdown (device_t); static int wb_ifmedia_upd (struct ifnet *); static void wb_ifmedia_sts (struct ifnet *, struct ifmediareq *); static void wb_eeprom_putbyte (struct wb_softc *, int); static void wb_eeprom_getword (struct wb_softc *, int, u_int16_t *); static void wb_read_eeprom (struct wb_softc *, caddr_t, int, int, int); static void wb_mii_sync (struct wb_softc *); static void wb_mii_send (struct wb_softc *, u_int32_t, int); static int wb_mii_readreg (struct wb_softc *, struct wb_mii_frame *); static int wb_mii_writereg (struct wb_softc *, struct wb_mii_frame *); static void wb_setcfg (struct wb_softc *, u_int32_t); static void wb_setmulti (struct wb_softc *); static void wb_reset (struct wb_softc *); static void wb_fixmedia (struct wb_softc *); static int wb_list_rx_init (struct wb_softc *); static int wb_list_tx_init (struct wb_softc *); static int wb_miibus_readreg (device_t, int, int); static int wb_miibus_writereg (device_t, int, int, int); static void wb_miibus_statchg (device_t); #ifdef WB_USEIOSPACE #define WB_RES SYS_RES_IOPORT #define WB_RID WB_PCI_LOIO #else #define WB_RES SYS_RES_MEMORY #define WB_RID WB_PCI_LOMEM #endif static device_method_t wb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wb_probe), DEVMETHOD(device_attach, wb_attach), DEVMETHOD(device_detach, wb_detach), DEVMETHOD(device_shutdown, wb_shutdown), /* bus interface, for miibus */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, wb_miibus_readreg), DEVMETHOD(miibus_writereg, wb_miibus_writereg), DEVMETHOD(miibus_statchg, wb_miibus_statchg), { 0, 0 } }; static driver_t wb_driver = { "wb", wb_methods, sizeof(struct wb_softc) }; static devclass_t wb_devclass; DRIVER_MODULE(wb, pci, wb_driver, wb_devclass, 0, 0); DRIVER_MODULE(miibus, wb, miibus_driver, miibus_devclass, 0, 0); #define WB_SETBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) | (x)) #define WB_CLRBIT(sc, reg, x) \ CSR_WRITE_4(sc, reg, \ CSR_READ_4(sc, reg) & ~(x)) #define SIO_SET(x) \ CSR_WRITE_4(sc, WB_SIO, \ CSR_READ_4(sc, WB_SIO) | (x)) #define SIO_CLR(x) \ CSR_WRITE_4(sc, WB_SIO, \ CSR_READ_4(sc, WB_SIO) & ~(x)) /* * Send a read command and address to the EEPROM, check for ACK. */ static void wb_eeprom_putbyte(sc, addr) struct wb_softc *sc; int addr; { register int d, i; d = addr | WB_EECMD_READ; /* * Feed in each bit and stobe the clock. */ for (i = 0x400; i; i >>= 1) { if (d & i) { SIO_SET(WB_SIO_EE_DATAIN); } else { SIO_CLR(WB_SIO_EE_DATAIN); } DELAY(100); SIO_SET(WB_SIO_EE_CLK); DELAY(150); SIO_CLR(WB_SIO_EE_CLK); DELAY(100); } return; } /* * Read a word of data stored in the EEPROM at address 'addr.' */ static void wb_eeprom_getword(sc, addr, dest) struct wb_softc *sc; int addr; u_int16_t *dest; { register int i; u_int16_t word = 0; /* Enter EEPROM access mode. */ CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); /* * Send address of word we want to read. */ wb_eeprom_putbyte(sc, addr); CSR_WRITE_4(sc, WB_SIO, WB_SIO_EESEL|WB_SIO_EE_CS); /* * Start reading bits from EEPROM. */ for (i = 0x8000; i; i >>= 1) { SIO_SET(WB_SIO_EE_CLK); DELAY(100); if (CSR_READ_4(sc, WB_SIO) & WB_SIO_EE_DATAOUT) word |= i; SIO_CLR(WB_SIO_EE_CLK); DELAY(100); } /* Turn off EEPROM access mode. */ CSR_WRITE_4(sc, WB_SIO, 0); *dest = word; return; } /* * Read a sequence of words from the EEPROM. */ static void wb_read_eeprom(sc, dest, off, cnt, swap) struct wb_softc *sc; caddr_t dest; int off; int cnt; int swap; { int i; u_int16_t word = 0, *ptr; for (i = 0; i < cnt; i++) { wb_eeprom_getword(sc, off + i, &word); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return; } /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void wb_mii_sync(sc) struct wb_softc *sc; { register int i; SIO_SET(WB_SIO_MII_DIR|WB_SIO_MII_DATAIN); for (i = 0; i < 32; i++) { SIO_SET(WB_SIO_MII_CLK); DELAY(1); SIO_CLR(WB_SIO_MII_CLK); DELAY(1); } return; } /* * Clock a series of bits through the MII. */ static void wb_mii_send(sc, bits, cnt) struct wb_softc *sc; u_int32_t bits; int cnt; { int i; SIO_CLR(WB_SIO_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { SIO_SET(WB_SIO_MII_DATAIN); } else { SIO_CLR(WB_SIO_MII_DATAIN); } DELAY(1); SIO_CLR(WB_SIO_MII_CLK); DELAY(1); SIO_SET(WB_SIO_MII_CLK); } } /* * Read an PHY register through the MII. */ static int wb_mii_readreg(sc, frame) struct wb_softc *sc; struct wb_mii_frame *frame; { int i, ack; WB_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = WB_MII_STARTDELIM; frame->mii_opcode = WB_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; CSR_WRITE_4(sc, WB_SIO, 0); /* * Turn on data xmit. */ SIO_SET(WB_SIO_MII_DIR); wb_mii_sync(sc); /* * Send command/address info. */ wb_mii_send(sc, frame->mii_stdelim, 2); wb_mii_send(sc, frame->mii_opcode, 2); wb_mii_send(sc, frame->mii_phyaddr, 5); wb_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ SIO_CLR((WB_SIO_MII_CLK|WB_SIO_MII_DATAIN)); DELAY(1); SIO_SET(WB_SIO_MII_CLK); DELAY(1); /* Turn off xmit. */ SIO_CLR(WB_SIO_MII_DIR); /* Check for ack */ SIO_CLR(WB_SIO_MII_CLK); DELAY(1); ack = CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT; SIO_SET(WB_SIO_MII_CLK); DELAY(1); SIO_CLR(WB_SIO_MII_CLK); DELAY(1); SIO_SET(WB_SIO_MII_CLK); DELAY(1); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { SIO_CLR(WB_SIO_MII_CLK); DELAY(1); SIO_SET(WB_SIO_MII_CLK); DELAY(1); } goto fail; } for (i = 0x8000; i; i >>= 1) { SIO_CLR(WB_SIO_MII_CLK); DELAY(1); if (!ack) { if (CSR_READ_4(sc, WB_SIO) & WB_SIO_MII_DATAOUT) frame->mii_data |= i; DELAY(1); } SIO_SET(WB_SIO_MII_CLK); DELAY(1); } fail: SIO_CLR(WB_SIO_MII_CLK); DELAY(1); SIO_SET(WB_SIO_MII_CLK); DELAY(1); WB_UNLOCK(sc); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int wb_mii_writereg(sc, frame) struct wb_softc *sc; struct wb_mii_frame *frame; { WB_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = WB_MII_STARTDELIM; frame->mii_opcode = WB_MII_WRITEOP; frame->mii_turnaround = WB_MII_TURNAROUND; /* * Turn on data output. */ SIO_SET(WB_SIO_MII_DIR); wb_mii_sync(sc); wb_mii_send(sc, frame->mii_stdelim, 2); wb_mii_send(sc, frame->mii_opcode, 2); wb_mii_send(sc, frame->mii_phyaddr, 5); wb_mii_send(sc, frame->mii_regaddr, 5); wb_mii_send(sc, frame->mii_turnaround, 2); wb_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ SIO_SET(WB_SIO_MII_CLK); DELAY(1); SIO_CLR(WB_SIO_MII_CLK); DELAY(1); /* * Turn off xmit. */ SIO_CLR(WB_SIO_MII_DIR); WB_UNLOCK(sc); return(0); } static int wb_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct wb_softc *sc; struct wb_mii_frame frame; sc = device_get_softc(dev); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; wb_mii_readreg(sc, &frame); return(frame.mii_data); } static int wb_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct wb_softc *sc; struct wb_mii_frame frame; sc = device_get_softc(dev); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; wb_mii_writereg(sc, &frame); return(0); } static void wb_miibus_statchg(dev) device_t dev; { struct wb_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); WB_LOCK(sc); mii = device_get_softc(sc->wb_miibus); wb_setcfg(sc, mii->mii_media_active); WB_UNLOCK(sc); return; } /* * Program the 64-bit multicast hash filter. */ static void wb_setmulti(sc) struct wb_softc *sc; { struct ifnet *ifp; int h = 0; u_int32_t hashes[2] = { 0, 0 }; struct ifmultiaddr *ifma; u_int32_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; rxfilt = CSR_READ_4(sc, WB_NETCFG); if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { rxfilt |= WB_NETCFG_RX_MULTI; CSR_WRITE_4(sc, WB_NETCFG, rxfilt); CSR_WRITE_4(sc, WB_MAR0, 0xFFFFFFFF); CSR_WRITE_4(sc, WB_MAR1, 0xFFFFFFFF); return; } /* first, zot all the existing hash bits */ CSR_WRITE_4(sc, WB_MAR0, 0); CSR_WRITE_4(sc, WB_MAR1, 0); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; h = ~ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) >> 26; if (h < 32) hashes[0] |= (1 << h); else hashes[1] |= (1 << (h - 32)); mcnt++; } if (mcnt) rxfilt |= WB_NETCFG_RX_MULTI; else rxfilt &= ~WB_NETCFG_RX_MULTI; CSR_WRITE_4(sc, WB_MAR0, hashes[0]); CSR_WRITE_4(sc, WB_MAR1, hashes[1]); CSR_WRITE_4(sc, WB_NETCFG, rxfilt); return; } /* * The Winbond manual states that in order to fiddle with the * 'full-duplex' and '100Mbps' bits in the netconfig register, we * first have to put the transmit and/or receive logic in the idle state. */ static void wb_setcfg(sc, media) struct wb_softc *sc; u_int32_t media; { int i, restart = 0; if (CSR_READ_4(sc, WB_NETCFG) & (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)) { restart = 1; WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_TX_ON|WB_NETCFG_RX_ON)); for (i = 0; i < WB_TIMEOUT; i++) { DELAY(10); if ((CSR_READ_4(sc, WB_ISR) & WB_ISR_TX_IDLE) && (CSR_READ_4(sc, WB_ISR) & WB_ISR_RX_IDLE)) break; } if (i == WB_TIMEOUT) printf("wb%d: failed to force tx and " "rx to idle state\n", sc->wb_unit); } if (IFM_SUBTYPE(media) == IFM_10_T) WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); else WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_100MBPS); if ((media & IFM_GMASK) == IFM_FDX) WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); else WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_FULLDUPLEX); if (restart) WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON|WB_NETCFG_RX_ON); return; } static void wb_reset(sc) struct wb_softc *sc; { register int i; struct mii_data *mii; CSR_WRITE_4(sc, WB_NETCFG, 0); CSR_WRITE_4(sc, WB_BUSCTL, 0); CSR_WRITE_4(sc, WB_TXADDR, 0); CSR_WRITE_4(sc, WB_RXADDR, 0); WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); WB_SETBIT(sc, WB_BUSCTL, WB_BUSCTL_RESET); for (i = 0; i < WB_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_4(sc, WB_BUSCTL) & WB_BUSCTL_RESET)) break; } if (i == WB_TIMEOUT) printf("wb%d: reset never completed!\n", sc->wb_unit); /* Wait a little while for the chip to get its brains in order. */ DELAY(1000); if (sc->wb_miibus == NULL) return; mii = device_get_softc(sc->wb_miibus); if (mii == NULL) return; if (mii->mii_instance) { struct mii_softc *miisc; LIST_FOREACH(miisc, &mii->mii_phys, mii_list) mii_phy_reset(miisc); } return; } static void wb_fixmedia(sc) struct wb_softc *sc; { struct mii_data *mii = NULL; struct ifnet *ifp; u_int32_t media; if (sc->wb_miibus == NULL) return; mii = device_get_softc(sc->wb_miibus); ifp = &sc->arpcom.ac_if; mii_pollstat(mii); if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { media = mii->mii_media_active & ~IFM_10_T; media |= IFM_100_TX; } else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) { media = mii->mii_media_active & ~IFM_100_TX; media |= IFM_10_T; } else return; ifmedia_set(&mii->mii_media, media); return; } /* * Probe for a Winbond chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int wb_probe(dev) device_t dev; { struct wb_type *t; t = wb_devs; while(t->wb_name != NULL) { if ((pci_get_vendor(dev) == t->wb_vid) && (pci_get_device(dev) == t->wb_did)) { device_set_desc(dev, t->wb_name); return(0); } t++; } return(ENXIO); } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int wb_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; struct wb_softc *sc; struct ifnet *ifp; int unit, error = 0, rid; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->wb_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); -#ifndef BURN_BRIDGES - /* - * Handle power management nonsense. - */ - - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, WB_PCI_LOIO, 4); - membase = pci_read_config(dev, WB_PCI_LOMEM, 4); - irq = pci_read_config(dev, WB_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("wb%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, WB_PCI_LOIO, iobase, 4); - pci_write_config(dev, WB_PCI_LOMEM, membase, 4); - pci_write_config(dev, WB_PCI_INTLINE, irq, 4); - } -#endif /* * Map control/status registers. */ pci_enable_busmaster(dev); rid = WB_RID; sc->wb_res = bus_alloc_resource_any(dev, WB_RES, &rid, RF_ACTIVE); if (sc->wb_res == NULL) { printf("wb%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->wb_btag = rman_get_bustag(sc->wb_res); sc->wb_bhandle = rman_get_bushandle(sc->wb_res); /* Allocate interrupt */ rid = 0; sc->wb_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->wb_irq == NULL) { printf("wb%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Save the cache line size. */ sc->wb_cachesize = pci_read_config(dev, WB_PCI_CACHELEN, 4) & 0xFF; /* Reset the adapter. */ wb_reset(sc); /* * Get station address from the EEPROM. */ wb_read_eeprom(sc, (caddr_t)&eaddr, 0, 3, 0); sc->wb_unit = unit; bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); sc->wb_ldata = contigmalloc(sizeof(struct wb_list_data) + 8, M_DEVBUF, M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); if (sc->wb_ldata == NULL) { printf("wb%d: no memory for list buffers!\n", unit); error = ENXIO; goto fail; } bzero(sc->wb_ldata, sizeof(struct wb_list_data)); ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = wb_ioctl; ifp->if_start = wb_start; ifp->if_watchdog = wb_watchdog; ifp->if_init = wb_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = WB_TX_LIST_CNT - 1; /* * Do MII setup. */ if (mii_phy_probe(dev, &sc->wb_miibus, wb_ifmedia_upd, wb_ifmedia_sts)) { error = ENXIO; goto fail; } /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->wb_irq, INTR_TYPE_NET, wb_intr, sc, &sc->wb_intrhand); if (error) { printf("wb%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) wb_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int wb_detach(dev) device_t dev; { struct wb_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->wb_mtx), ("wb mutex not initialized")); WB_LOCK(sc); ifp = &sc->arpcom.ac_if; /* * Delete any miibus and phy devices attached to this interface. * This should only be done if attach succeeded. */ if (device_is_attached(dev)) { wb_stop(sc); ether_ifdetach(ifp); } if (sc->wb_miibus) device_delete_child(dev, sc->wb_miibus); bus_generic_detach(dev); if (sc->wb_intrhand) bus_teardown_intr(dev, sc->wb_irq, sc->wb_intrhand); if (sc->wb_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->wb_irq); if (sc->wb_res) bus_release_resource(dev, WB_RES, WB_RID, sc->wb_res); if (sc->wb_ldata) { contigfree(sc->wb_ldata, sizeof(struct wb_list_data) + 8, M_DEVBUF); } WB_UNLOCK(sc); mtx_destroy(&sc->wb_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int wb_list_tx_init(sc) struct wb_softc *sc; { struct wb_chain_data *cd; struct wb_list_data *ld; int i; cd = &sc->wb_cdata; ld = sc->wb_ldata; for (i = 0; i < WB_TX_LIST_CNT; i++) { cd->wb_tx_chain[i].wb_ptr = &ld->wb_tx_list[i]; if (i == (WB_TX_LIST_CNT - 1)) { cd->wb_tx_chain[i].wb_nextdesc = &cd->wb_tx_chain[0]; } else { cd->wb_tx_chain[i].wb_nextdesc = &cd->wb_tx_chain[i + 1]; } } cd->wb_tx_free = &cd->wb_tx_chain[0]; cd->wb_tx_tail = cd->wb_tx_head = NULL; return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int wb_list_rx_init(sc) struct wb_softc *sc; { struct wb_chain_data *cd; struct wb_list_data *ld; int i; cd = &sc->wb_cdata; ld = sc->wb_ldata; for (i = 0; i < WB_RX_LIST_CNT; i++) { cd->wb_rx_chain[i].wb_ptr = (struct wb_desc *)&ld->wb_rx_list[i]; cd->wb_rx_chain[i].wb_buf = (void *)&ld->wb_rxbufs[i]; if (wb_newbuf(sc, &cd->wb_rx_chain[i], NULL) == ENOBUFS) return(ENOBUFS); if (i == (WB_RX_LIST_CNT - 1)) { cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[0]; ld->wb_rx_list[i].wb_next = vtophys(&ld->wb_rx_list[0]); } else { cd->wb_rx_chain[i].wb_nextdesc = &cd->wb_rx_chain[i + 1]; ld->wb_rx_list[i].wb_next = vtophys(&ld->wb_rx_list[i + 1]); } } cd->wb_rx_head = &cd->wb_rx_chain[0]; return(0); } static void wb_bfree(buf, args) void *buf; void *args; { return; } /* * Initialize an RX descriptor and attach an MBUF cluster. */ static int wb_newbuf(sc, c, m) struct wb_softc *sc; struct wb_chain_onefrag *c; struct mbuf *m; { struct mbuf *m_new = NULL; if (m == NULL) { MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(ENOBUFS); m_new->m_data = c->wb_buf; m_new->m_pkthdr.len = m_new->m_len = WB_BUFBYTES; MEXTADD(m_new, c->wb_buf, WB_BUFBYTES, wb_bfree, NULL, 0, EXT_NET_DRV); } else { m_new = m; m_new->m_len = m_new->m_pkthdr.len = WB_BUFBYTES; m_new->m_data = m_new->m_ext.ext_buf; } m_adj(m_new, sizeof(u_int64_t)); c->wb_mbuf = m_new; c->wb_ptr->wb_data = vtophys(mtod(m_new, caddr_t)); c->wb_ptr->wb_ctl = WB_RXCTL_RLINK | 1536; c->wb_ptr->wb_status = WB_RXSTAT; return(0); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void wb_rxeof(sc) struct wb_softc *sc; { struct mbuf *m = NULL; struct ifnet *ifp; struct wb_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; WB_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; while(!((rxstat = sc->wb_cdata.wb_rx_head->wb_ptr->wb_status) & WB_RXSTAT_OWN)) { struct mbuf *m0 = NULL; cur_rx = sc->wb_cdata.wb_rx_head; sc->wb_cdata.wb_rx_head = cur_rx->wb_nextdesc; m = cur_rx->wb_mbuf; if ((rxstat & WB_RXSTAT_MIIERR) || (WB_RXBYTES(cur_rx->wb_ptr->wb_status) < WB_MIN_FRAMELEN) || (WB_RXBYTES(cur_rx->wb_ptr->wb_status) > 1536) || !(rxstat & WB_RXSTAT_LASTFRAG) || !(rxstat & WB_RXSTAT_RXCMP)) { ifp->if_ierrors++; wb_newbuf(sc, cur_rx, m); printf("wb%x: receiver babbling: possible chip " "bug, forcing reset\n", sc->wb_unit); wb_fixmedia(sc); wb_reset(sc); wb_init(sc); return; } if (rxstat & WB_RXSTAT_RXERR) { ifp->if_ierrors++; wb_newbuf(sc, cur_rx, m); break; } /* No errors; receive the packet. */ total_len = WB_RXBYTES(cur_rx->wb_ptr->wb_status); /* * XXX The Winbond chip includes the CRC with every * received frame, and there's no way to turn this * behavior off (at least, I can't find anything in * the manual that explains how to do it) so we have * to trim off the CRC manually. */ total_len -= ETHER_CRC_LEN; m0 = m_devget(mtod(m, char *), total_len, ETHER_ALIGN, ifp, NULL); wb_newbuf(sc, cur_rx, m); if (m0 == NULL) { ifp->if_ierrors++; break; } m = m0; ifp->if_ipackets++; WB_UNLOCK(sc); (*ifp->if_input)(ifp, m); WB_LOCK(sc); } } static void wb_rxeoc(sc) struct wb_softc *sc; { wb_rxeof(sc); WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); if (CSR_READ_4(sc, WB_ISR) & WB_RXSTATE_SUSPEND) CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void wb_txeof(sc) struct wb_softc *sc; { struct wb_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; if (sc->wb_cdata.wb_tx_head == NULL) return; /* * Go through our tx list and free mbufs for those * frames that have been transmitted. */ while(sc->wb_cdata.wb_tx_head->wb_mbuf != NULL) { u_int32_t txstat; cur_tx = sc->wb_cdata.wb_tx_head; txstat = WB_TXSTATUS(cur_tx); if ((txstat & WB_TXSTAT_OWN) || txstat == WB_UNSENT) break; if (txstat & WB_TXSTAT_TXERR) { ifp->if_oerrors++; if (txstat & WB_TXSTAT_ABORT) ifp->if_collisions++; if (txstat & WB_TXSTAT_LATECOLL) ifp->if_collisions++; } ifp->if_collisions += (txstat & WB_TXSTAT_COLLCNT) >> 3; ifp->if_opackets++; m_freem(cur_tx->wb_mbuf); cur_tx->wb_mbuf = NULL; if (sc->wb_cdata.wb_tx_head == sc->wb_cdata.wb_tx_tail) { sc->wb_cdata.wb_tx_head = NULL; sc->wb_cdata.wb_tx_tail = NULL; break; } sc->wb_cdata.wb_tx_head = cur_tx->wb_nextdesc; } return; } /* * TX 'end of channel' interrupt handler. */ static void wb_txeoc(sc) struct wb_softc *sc; { struct ifnet *ifp; ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; if (sc->wb_cdata.wb_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->wb_cdata.wb_tx_tail = NULL; } else { if (WB_TXOWN(sc->wb_cdata.wb_tx_head) == WB_UNSENT) { WB_TXOWN(sc->wb_cdata.wb_tx_head) = WB_TXSTAT_OWN; ifp->if_timer = 5; CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); } } return; } static void wb_intr(arg) void *arg; { struct wb_softc *sc; struct ifnet *ifp; u_int32_t status; sc = arg; WB_LOCK(sc); ifp = &sc->arpcom.ac_if; if (!(ifp->if_flags & IFF_UP)) { WB_UNLOCK(sc); return; } /* Disable interrupts. */ CSR_WRITE_4(sc, WB_IMR, 0x00000000); for (;;) { status = CSR_READ_4(sc, WB_ISR); if (status) CSR_WRITE_4(sc, WB_ISR, status); if ((status & WB_INTRS) == 0) break; if ((status & WB_ISR_RX_NOBUF) || (status & WB_ISR_RX_ERR)) { ifp->if_ierrors++; wb_reset(sc); if (status & WB_ISR_RX_ERR) wb_fixmedia(sc); wb_init(sc); continue; } if (status & WB_ISR_RX_OK) wb_rxeof(sc); if (status & WB_ISR_RX_IDLE) wb_rxeoc(sc); if (status & WB_ISR_TX_OK) wb_txeof(sc); if (status & WB_ISR_TX_NOBUF) wb_txeoc(sc); if (status & WB_ISR_TX_IDLE) { wb_txeof(sc); if (sc->wb_cdata.wb_tx_head != NULL) { WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); } } if (status & WB_ISR_TX_UNDERRUN) { ifp->if_oerrors++; wb_txeof(sc); WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); /* Jack up TX threshold */ sc->wb_txthresh += WB_TXTHRESH_CHUNK; WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); } if (status & WB_ISR_BUS_ERR) { wb_reset(sc); wb_init(sc); } } /* Re-enable interrupts. */ CSR_WRITE_4(sc, WB_IMR, WB_INTRS); if (ifp->if_snd.ifq_head != NULL) { wb_start(ifp); } WB_UNLOCK(sc); return; } static void wb_tick(xsc) void *xsc; { struct wb_softc *sc; struct mii_data *mii; sc = xsc; WB_LOCK(sc); mii = device_get_softc(sc->wb_miibus); mii_tick(mii); sc->wb_stat_ch = timeout(wb_tick, sc, hz); WB_UNLOCK(sc); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int wb_encap(sc, c, m_head) struct wb_softc *sc; struct wb_chain *c; struct mbuf *m_head; { int frag = 0; struct wb_desc *f = NULL; int total_len; struct mbuf *m; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ m = m_head; total_len = 0; for (m = m_head, frag = 0; m != NULL; m = m->m_next) { if (m->m_len != 0) { if (frag == WB_MAXFRAGS) break; total_len += m->m_len; f = &c->wb_ptr->wb_frag[frag]; f->wb_ctl = WB_TXCTL_TLINK | m->m_len; if (frag == 0) { f->wb_ctl |= WB_TXCTL_FIRSTFRAG; f->wb_status = 0; } else f->wb_status = WB_TXSTAT_OWN; f->wb_next = vtophys(&c->wb_ptr->wb_frag[frag + 1]); f->wb_data = vtophys(mtod(m, vm_offset_t)); frag++; } } /* * Handle special case: we used up all 16 fragments, * but we have more mbufs left in the chain. Copy the * data into an mbuf cluster. Note that we don't * bother clearing the values in the other fragment * pointers/counters; it wouldn't gain us anything, * and would waste cycles. */ if (m != NULL) { struct mbuf *m_new = NULL; MGETHDR(m_new, M_DONTWAIT, MT_DATA); if (m_new == NULL) return(1); if (m_head->m_pkthdr.len > MHLEN) { MCLGET(m_new, M_DONTWAIT); if (!(m_new->m_flags & M_EXT)) { m_freem(m_new); return(1); } } m_copydata(m_head, 0, m_head->m_pkthdr.len, mtod(m_new, caddr_t)); m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; m_freem(m_head); m_head = m_new; f = &c->wb_ptr->wb_frag[0]; f->wb_status = 0; f->wb_data = vtophys(mtod(m_new, caddr_t)); f->wb_ctl = total_len = m_new->m_len; f->wb_ctl |= WB_TXCTL_TLINK|WB_TXCTL_FIRSTFRAG; frag = 1; } if (total_len < WB_MIN_FRAMELEN) { f = &c->wb_ptr->wb_frag[frag]; f->wb_ctl = WB_MIN_FRAMELEN - total_len; f->wb_data = vtophys(&sc->wb_cdata.wb_pad); f->wb_ctl |= WB_TXCTL_TLINK; f->wb_status = WB_TXSTAT_OWN; frag++; } c->wb_mbuf = m_head; c->wb_lastdesc = frag - 1; WB_TXCTL(c) |= WB_TXCTL_LASTFRAG; WB_TXNEXT(c) = vtophys(&c->wb_nextdesc->wb_ptr->wb_frag[0]); return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void wb_start(ifp) struct ifnet *ifp; { struct wb_softc *sc; struct mbuf *m_head = NULL; struct wb_chain *cur_tx = NULL, *start_tx; sc = ifp->if_softc; WB_LOCK(sc); /* * Check for an available queue slot. If there are none, * punt. */ if (sc->wb_cdata.wb_tx_free->wb_mbuf != NULL) { ifp->if_flags |= IFF_OACTIVE; WB_UNLOCK(sc); return; } start_tx = sc->wb_cdata.wb_tx_free; while(sc->wb_cdata.wb_tx_free->wb_mbuf == NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pick a descriptor off the free list. */ cur_tx = sc->wb_cdata.wb_tx_free; sc->wb_cdata.wb_tx_free = cur_tx->wb_nextdesc; /* Pack the data into the descriptor. */ wb_encap(sc, cur_tx, m_head); if (cur_tx != start_tx) WB_TXOWN(cur_tx) = WB_TXSTAT_OWN; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->wb_mbuf); } /* * If there are no packets queued, bail. */ if (cur_tx == NULL) { WB_UNLOCK(sc); return; } /* * Place the request for the upload interrupt * in the last descriptor in the chain. This way, if * we're chaining several packets at once, we'll only * get an interupt once for the whole chain rather than * once for each packet. */ WB_TXCTL(cur_tx) |= WB_TXCTL_FINT; cur_tx->wb_ptr->wb_frag[0].wb_ctl |= WB_TXCTL_FINT; sc->wb_cdata.wb_tx_tail = cur_tx; if (sc->wb_cdata.wb_tx_head == NULL) { sc->wb_cdata.wb_tx_head = start_tx; WB_TXOWN(start_tx) = WB_TXSTAT_OWN; CSR_WRITE_4(sc, WB_TXSTART, 0xFFFFFFFF); } else { /* * We need to distinguish between the case where * the own bit is clear because the chip cleared it * and where the own bit is clear because we haven't * set it yet. The magic value WB_UNSET is just some * ramdomly chosen number which doesn't have the own * bit set. When we actually transmit the frame, the * status word will have _only_ the own bit set, so * the txeoc handler will be able to tell if it needs * to initiate another transmission to flush out pending * frames. */ WB_TXOWN(start_tx) = WB_UNSENT; } /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; WB_UNLOCK(sc); return; } static void wb_init(xsc) void *xsc; { struct wb_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int i; struct mii_data *mii; WB_LOCK(sc); mii = device_get_softc(sc->wb_miibus); /* * Cancel pending I/O and free all RX/TX buffers. */ wb_stop(sc); wb_reset(sc); sc->wb_txthresh = WB_TXTHRESH_INIT; /* * Set cache alignment and burst length. */ #ifdef foo CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_CONFIG); WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_THRESH); WB_SETBIT(sc, WB_NETCFG, WB_TXTHRESH(sc->wb_txthresh)); #endif CSR_WRITE_4(sc, WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION); WB_SETBIT(sc, WB_BUSCTL, WB_BURSTLEN_16LONG); switch(sc->wb_cachesize) { case 32: WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_32LONG); break; case 16: WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_16LONG); break; case 8: WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_8LONG); break; case 0: default: WB_SETBIT(sc, WB_BUSCTL, WB_CACHEALIGN_NONE); break; } /* This doesn't tend to work too well at 100Mbps. */ WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_EARLY_ON); /* Init our MAC address */ for (i = 0; i < ETHER_ADDR_LEN; i++) { CSR_WRITE_1(sc, WB_NODE0 + i, sc->arpcom.ac_enaddr[i]); } /* Init circular RX list. */ if (wb_list_rx_init(sc) == ENOBUFS) { printf("wb%d: initialization failed: no " "memory for rx buffers\n", sc->wb_unit); wb_stop(sc); WB_UNLOCK(sc); return; } /* Init TX descriptors. */ wb_list_tx_init(sc); /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); } else { WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ALLPHYS); } /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); } else { WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_BROAD); } /* * Program the multicast filter, if necessary. */ wb_setmulti(sc); /* * Load the address of the RX list. */ WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); CSR_WRITE_4(sc, WB_RXADDR, vtophys(&sc->wb_ldata->wb_rx_list[0])); /* * Enable interrupts. */ CSR_WRITE_4(sc, WB_IMR, WB_INTRS); CSR_WRITE_4(sc, WB_ISR, 0xFFFFFFFF); /* Enable receiver and transmitter. */ WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_RX_ON); CSR_WRITE_4(sc, WB_RXSTART, 0xFFFFFFFF); WB_CLRBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); CSR_WRITE_4(sc, WB_TXADDR, vtophys(&sc->wb_ldata->wb_tx_list[0])); WB_SETBIT(sc, WB_NETCFG, WB_NETCFG_TX_ON); mii_mediachg(mii); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->wb_stat_ch = timeout(wb_tick, sc, hz); WB_UNLOCK(sc); return; } /* * Set media options. */ static int wb_ifmedia_upd(ifp) struct ifnet *ifp; { struct wb_softc *sc; sc = ifp->if_softc; if (ifp->if_flags & IFF_UP) wb_init(sc); return(0); } /* * Report current media status. */ static void wb_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct wb_softc *sc; struct mii_data *mii; sc = ifp->if_softc; mii = device_get_softc(sc->wb_miibus); mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; return; } static int wb_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct wb_softc *sc = ifp->if_softc; struct mii_data *mii; struct ifreq *ifr = (struct ifreq *) data; int error = 0; WB_LOCK(sc); switch(command) { case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { wb_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) wb_stop(sc); } error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: wb_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: mii = device_get_softc(sc->wb_miibus); error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; default: error = ether_ioctl(ifp, command, data); break; } WB_UNLOCK(sc); return(error); } static void wb_watchdog(ifp) struct ifnet *ifp; { struct wb_softc *sc; sc = ifp->if_softc; WB_LOCK(sc); ifp->if_oerrors++; printf("wb%d: watchdog timeout\n", sc->wb_unit); #ifdef foo if (!(wb_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) printf("wb%d: no carrier - transceiver cable problem?\n", sc->wb_unit); #endif wb_stop(sc); wb_reset(sc); wb_init(sc); if (ifp->if_snd.ifq_head != NULL) wb_start(ifp); WB_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void wb_stop(sc) struct wb_softc *sc; { register int i; struct ifnet *ifp; WB_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; untimeout(wb_tick, sc, sc->wb_stat_ch); WB_CLRBIT(sc, WB_NETCFG, (WB_NETCFG_RX_ON|WB_NETCFG_TX_ON)); CSR_WRITE_4(sc, WB_IMR, 0x00000000); CSR_WRITE_4(sc, WB_TXADDR, 0x00000000); CSR_WRITE_4(sc, WB_RXADDR, 0x00000000); /* * Free data in the RX lists. */ for (i = 0; i < WB_RX_LIST_CNT; i++) { if (sc->wb_cdata.wb_rx_chain[i].wb_mbuf != NULL) { m_freem(sc->wb_cdata.wb_rx_chain[i].wb_mbuf); sc->wb_cdata.wb_rx_chain[i].wb_mbuf = NULL; } } bzero((char *)&sc->wb_ldata->wb_rx_list, sizeof(sc->wb_ldata->wb_rx_list)); /* * Free the TX list buffers. */ for (i = 0; i < WB_TX_LIST_CNT; i++) { if (sc->wb_cdata.wb_tx_chain[i].wb_mbuf != NULL) { m_freem(sc->wb_cdata.wb_tx_chain[i].wb_mbuf); sc->wb_cdata.wb_tx_chain[i].wb_mbuf = NULL; } } bzero((char *)&sc->wb_ldata->wb_tx_list, sizeof(sc->wb_ldata->wb_tx_list)); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); WB_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void wb_shutdown(dev) device_t dev; { struct wb_softc *sc; sc = device_get_softc(dev); wb_stop(sc); return; } Index: head/sys/pci/if_xl.c =================================================================== --- head/sys/pci/if_xl.c (revision 131252) +++ head/sys/pci/if_xl.c (revision 131253) @@ -1,3336 +1,3295 @@ /* * Copyright (c) 1997, 1998, 1999 * Bill Paul . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * 3Com 3c90x Etherlink XL PCI NIC driver * * Supports the 3Com "boomerang", "cyclone" and "hurricane" PCI * bus-master chips (3c90x cards and embedded controllers) including * the following: * * 3Com 3c900-TPO 10Mbps/RJ-45 * 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC * 3Com 3c905-TX 10/100Mbps/RJ-45 * 3Com 3c905-T4 10/100Mbps/RJ-45 * 3Com 3c900B-TPO 10Mbps/RJ-45 * 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC * 3Com 3c900B-TPC 10Mbps/RJ-45,BNC * 3Com 3c900B-FL 10Mbps/Fiber-optic * 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC * 3Com 3c905B-TX 10/100Mbps/RJ-45 * 3Com 3c905B-FL/FX 10/100Mbps/Fiber-optic * 3Com 3c905C-TX 10/100Mbps/RJ-45 (Tornado ASIC) * 3Com 3c980-TX 10/100Mbps server adapter (Hurricane ASIC) * 3Com 3c980C-TX 10/100Mbps server adapter (Tornado ASIC) * 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 (Hurricane ASIC) * 3Com 3c450-TX 10/100Mbps/RJ-45 (Tornado ASIC) * 3Com 3c555 10/100Mbps/RJ-45 (MiniPCI, Laptop Hurricane) * 3Com 3c556 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC) * 3Com 3c556B 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC) * 3Com 3c575TX 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) * 3Com 3c575B 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) * 3Com 3c575C 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) * 3Com 3cxfem656 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) * 3Com 3cxfem656b 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC) * 3Com 3cxfem656c 10/100Mbps/RJ-45 (Cardbus, Tornado ASIC) * Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45 * Dell on-board 3c920 10/100Mbps/RJ-45 * Dell Precision on-board 3c905B 10/100Mbps/RJ-45 * Dell Latitude laptop docking station embedded 3c905-TX * * Written by Bill Paul * Electrical Engineering Department * Columbia University, New York City */ /* * The 3c90x series chips use a bus-master DMA interface for transfering * packets to and from the controller chip. Some of the "vortex" cards * (3c59x) also supported a bus master mode, however for those chips * you could only DMA packets to/from a contiguous memory buffer. For * transmission this would mean copying the contents of the queued mbuf * chain into an mbuf cluster and then DMAing the cluster. This extra * copy would sort of defeat the purpose of the bus master support for * any packet that doesn't fit into a single mbuf. * * By contrast, the 3c90x cards support a fragment-based bus master * mode where mbuf chains can be encapsulated using TX descriptors. * This is similar to other PCI chips such as the Texas Instruments * ThunderLAN and the Intel 82557/82558. * * The "vortex" driver (if_vx.c) happens to work for the "boomerang" * bus master chips because they maintain the old PIO interface for * backwards compatibility, but starting with the 3c905B and the * "cyclone" chips, the compatibility interface has been dropped. * Since using bus master DMA is a big win, we use this driver to * support the PCI "boomerang" chips even though they work with the * "vortex" driver in order to obtain better performance. * * This driver is in the /sys/pci directory because it only supports * PCI-based NICs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include MODULE_DEPEND(xl, pci, 1, 1, 1); MODULE_DEPEND(xl, ether, 1, 1, 1); MODULE_DEPEND(xl, miibus, 1, 1, 1); /* "device miibus" required. See GENERIC if you get errors here. */ #include "miibus_if.h" #include /* * TX Checksumming is disabled by default for two reasons: * - TX Checksumming will occasionally produce corrupt packets * - TX Checksumming seems to reduce performance * * Only 905B/C cards were reported to have this problem, it is possible * that later chips _may_ be immune. */ #define XL905B_TXCSUM_BROKEN 1 #ifdef XL905B_TXCSUM_BROKEN #define XL905B_CSUM_FEATURES 0 #else #define XL905B_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) #endif /* * Various supported device vendors/types and their names. */ static struct xl_type xl_devs[] = { { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT, "3Com 3c900-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO, "3Com 3c900-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT, "3Com 3c905-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4, "3Com 3c905-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT, "3Com 3c900B-TPO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO, "3Com 3c900B-COMBO Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC, "3Com 3c900B-TPC Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10FL, "3Com 3c900B-FL Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT, "3Com 3c905B-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4, "3Com 3c905B-T4 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX, "3Com 3c905B-FX/SC Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO, "3Com 3c905B-COMBO Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT, "3Com 3c905C-TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B, "3Com 3c920B-EMB Integrated Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV, "3Com 3c980 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV, "3Com 3c980C Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX, "3Com 3cSOHO100-TX OfficeConnect" }, { TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT, "3Com 3c450-TX HomeConnect" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_555, "3Com 3c555 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_556, "3Com 3c556 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_556B, "3Com 3c556B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575A, "3Com 3c575TX Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575B, "3Com 3c575B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_575C, "3Com 3c575C Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_656, "3Com 3c656 Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_HURRICANE_656B, "3Com 3c656B Fast Etherlink XL" }, { TC_VENDORID, TC_DEVICEID_TORNADO_656C, "3Com 3c656C Fast Etherlink XL" }, { 0, 0, NULL } }; static int xl_probe (device_t); static int xl_attach (device_t); static int xl_detach (device_t); static int xl_newbuf (struct xl_softc *, struct xl_chain_onefrag *); static void xl_stats_update (void *); static int xl_encap (struct xl_softc *, struct xl_chain *, struct mbuf *); static void xl_rxeof (struct xl_softc *); static int xl_rx_resync (struct xl_softc *); static void xl_txeof (struct xl_softc *); static void xl_txeof_90xB (struct xl_softc *); static void xl_txeoc (struct xl_softc *); static void xl_intr (void *); static void xl_start (struct ifnet *); static void xl_start_90xB (struct ifnet *); static int xl_ioctl (struct ifnet *, u_long, caddr_t); static void xl_init (void *); static void xl_stop (struct xl_softc *); static void xl_watchdog (struct ifnet *); static void xl_shutdown (device_t); static int xl_suspend (device_t); static int xl_resume (device_t); static int xl_ifmedia_upd (struct ifnet *); static void xl_ifmedia_sts (struct ifnet *, struct ifmediareq *); static int xl_eeprom_wait (struct xl_softc *); static int xl_read_eeprom (struct xl_softc *, caddr_t, int, int, int); static void xl_mii_sync (struct xl_softc *); static void xl_mii_send (struct xl_softc *, u_int32_t, int); static int xl_mii_readreg (struct xl_softc *, struct xl_mii_frame *); static int xl_mii_writereg (struct xl_softc *, struct xl_mii_frame *); static void xl_setcfg (struct xl_softc *); static void xl_setmode (struct xl_softc *, int); static void xl_setmulti (struct xl_softc *); static void xl_setmulti_hash (struct xl_softc *); static void xl_reset (struct xl_softc *); static int xl_list_rx_init (struct xl_softc *); static int xl_list_tx_init (struct xl_softc *); static int xl_list_tx_init_90xB (struct xl_softc *); static void xl_wait (struct xl_softc *); static void xl_mediacheck (struct xl_softc *); static void xl_choose_xcvr (struct xl_softc *, int); static void xl_dma_map_addr (void *, bus_dma_segment_t *, int, int); static void xl_dma_map_rxbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); static void xl_dma_map_txbuf (void *, bus_dma_segment_t *, int, bus_size_t, int); #ifdef notdef static void xl_testpacket (struct xl_softc *); #endif static int xl_miibus_readreg (device_t, int, int); static int xl_miibus_writereg (device_t, int, int, int); static void xl_miibus_statchg (device_t); static void xl_miibus_mediainit (device_t); static device_method_t xl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xl_probe), DEVMETHOD(device_attach, xl_attach), DEVMETHOD(device_detach, xl_detach), DEVMETHOD(device_shutdown, xl_shutdown), DEVMETHOD(device_suspend, xl_suspend), DEVMETHOD(device_resume, xl_resume), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* MII interface */ DEVMETHOD(miibus_readreg, xl_miibus_readreg), DEVMETHOD(miibus_writereg, xl_miibus_writereg), DEVMETHOD(miibus_statchg, xl_miibus_statchg), DEVMETHOD(miibus_mediainit, xl_miibus_mediainit), { 0, 0 } }; static driver_t xl_driver = { "xl", xl_methods, sizeof(struct xl_softc) }; static devclass_t xl_devclass; DRIVER_MODULE(xl, cardbus, xl_driver, xl_devclass, 0, 0); DRIVER_MODULE(xl, pci, xl_driver, xl_devclass, 0, 0); DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, 0, 0); static void xl_dma_map_addr(arg, segs, nseg, error) void *arg; bus_dma_segment_t *segs; int nseg, error; { u_int32_t *paddr; paddr = arg; *paddr = segs->ds_addr; } static void xl_dma_map_rxbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { u_int32_t *paddr; if (error) return; KASSERT(nseg == 1, ("xl_dma_map_rxbuf: too many DMA segments")); paddr = arg; *paddr = segs->ds_addr; } static void xl_dma_map_txbuf(arg, segs, nseg, mapsize, error) void *arg; bus_dma_segment_t *segs; int nseg; bus_size_t mapsize; int error; { struct xl_list *l; int i, total_len; if (error) return; KASSERT(nseg <= XL_MAXFRAGS, ("too many DMA segments")); total_len = 0; l = arg; for (i = 0; i < nseg; i++) { KASSERT(segs[i].ds_len <= MCLBYTES, ("segment size too large")); l->xl_frag[i].xl_addr = htole32(segs[i].ds_addr); l->xl_frag[i].xl_len = htole32(segs[i].ds_len); total_len += segs[i].ds_len; } l->xl_frag[nseg - 1].xl_len = htole32(segs[nseg - 1].ds_len | XL_LAST_FRAG); l->xl_status = htole32(total_len); l->xl_next = 0; } /* * Murphy's law says that it's possible the chip can wedge and * the 'command in progress' bit may never clear. Hence, we wait * only a finite amount of time to avoid getting caught in an * infinite loop. Normally this delay routine would be a macro, * but it isn't called during normal operation so we can afford * to make it a function. */ static void xl_wait(sc) struct xl_softc *sc; { register int i; for (i = 0; i < XL_TIMEOUT; i++) { if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printf("xl%d: command never completed!\n", sc->xl_unit); return; } /* * MII access routines are provided for adapters with external * PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in * autoneg logic that's faked up to look like a PHY (3c905B-TX). * Note: if you don't perform the MDIO operations just right, * it's possible to end up with code that works correctly with * some chips/CPUs/processor speeds/bus speeds/etc but not * with others. */ #define MII_SET(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x)) #define MII_CLR(x) \ CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \ CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x)) /* * Sync the PHYs by setting data bit and strobing the clock 32 times. */ static void xl_mii_sync(sc) struct xl_softc *sc; { register int i; XL_SEL_WIN(4); MII_SET(XL_MII_DIR|XL_MII_DATA); for (i = 0; i < 32; i++) { MII_SET(XL_MII_CLK); MII_SET(XL_MII_DATA); MII_SET(XL_MII_DATA); MII_CLR(XL_MII_CLK); MII_SET(XL_MII_DATA); MII_SET(XL_MII_DATA); } return; } /* * Clock a series of bits through the MII. */ static void xl_mii_send(sc, bits, cnt) struct xl_softc *sc; u_int32_t bits; int cnt; { int i; XL_SEL_WIN(4); MII_CLR(XL_MII_CLK); for (i = (0x1 << (cnt - 1)); i; i >>= 1) { if (bits & i) { MII_SET(XL_MII_DATA); } else { MII_CLR(XL_MII_DATA); } MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); } } /* * Read an PHY register through the MII. */ static int xl_mii_readreg(sc, frame) struct xl_softc *sc; struct xl_mii_frame *frame; { int i, ack; XL_LOCK(sc); /* * Set up frame for RX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_READOP; frame->mii_turnaround = 0; frame->mii_data = 0; /* * Select register window 4. */ XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0); /* * Turn on data xmit. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); /* * Send command/address info. */ xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); /* Idle bit */ MII_CLR((XL_MII_CLK|XL_MII_DATA)); MII_SET(XL_MII_CLK); /* Turn off xmit. */ MII_CLR(XL_MII_DIR); /* Check for ack */ MII_CLR(XL_MII_CLK); ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA; MII_SET(XL_MII_CLK); /* * Now try reading data bits. If the ack failed, we still * need to clock through 16 cycles to keep the PHY(s) in sync. */ if (ack) { for(i = 0; i < 16; i++) { MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); } goto fail; } for (i = 0x8000; i; i >>= 1) { MII_CLR(XL_MII_CLK); if (!ack) { if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA) frame->mii_data |= i; } MII_SET(XL_MII_CLK); } fail: MII_CLR(XL_MII_CLK); MII_SET(XL_MII_CLK); XL_UNLOCK(sc); if (ack) return(1); return(0); } /* * Write to a PHY register through the MII. */ static int xl_mii_writereg(sc, frame) struct xl_softc *sc; struct xl_mii_frame *frame; { XL_LOCK(sc); /* * Set up frame for TX. */ frame->mii_stdelim = XL_MII_STARTDELIM; frame->mii_opcode = XL_MII_WRITEOP; frame->mii_turnaround = XL_MII_TURNAROUND; /* * Select the window 4. */ XL_SEL_WIN(4); /* * Turn on data output. */ MII_SET(XL_MII_DIR); xl_mii_sync(sc); xl_mii_send(sc, frame->mii_stdelim, 2); xl_mii_send(sc, frame->mii_opcode, 2); xl_mii_send(sc, frame->mii_phyaddr, 5); xl_mii_send(sc, frame->mii_regaddr, 5); xl_mii_send(sc, frame->mii_turnaround, 2); xl_mii_send(sc, frame->mii_data, 16); /* Idle bit. */ MII_SET(XL_MII_CLK); MII_CLR(XL_MII_CLK); /* * Turn off xmit. */ MII_CLR(XL_MII_DIR); XL_UNLOCK(sc); return(0); } static int xl_miibus_readreg(dev, phy, reg) device_t dev; int phy, reg; { struct xl_softc *sc; struct xl_mii_frame frame; sc = device_get_softc(dev); /* * Pretend that PHYs are only available at MII address 24. * This is to guard against problems with certain 3Com ASIC * revisions that incorrectly map the internal transceiver * control registers at all MII addresses. This can cause * the miibus code to attach the same PHY several times over. */ if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) return(0); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; xl_mii_readreg(sc, &frame); return(frame.mii_data); } static int xl_miibus_writereg(dev, phy, reg, data) device_t dev; int phy, reg, data; { struct xl_softc *sc; struct xl_mii_frame frame; sc = device_get_softc(dev); if ((!(sc->xl_flags & XL_FLAG_PHYOK)) && phy != 24) return(0); bzero((char *)&frame, sizeof(frame)); frame.mii_phyaddr = phy; frame.mii_regaddr = reg; frame.mii_data = data; xl_mii_writereg(sc, &frame); return(0); } static void xl_miibus_statchg(dev) device_t dev; { struct xl_softc *sc; struct mii_data *mii; sc = device_get_softc(dev); mii = device_get_softc(sc->xl_miibus); XL_LOCK(sc); xl_setcfg(sc); /* Set ASIC's duplex mode to match the PHY. */ XL_SEL_WIN(3); if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); else CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); XL_UNLOCK(sc); return; } /* * Special support for the 3c905B-COMBO. This card has 10/100 support * plus BNC and AUI ports. This means we will have both an miibus attached * plus some non-MII media settings. In order to allow this, we have to * add the extra media to the miibus's ifmedia struct, but we can't do * that during xl_attach() because the miibus hasn't been attached yet. * So instead, we wait until the miibus probe/attach is done, at which * point we will get a callback telling is that it's safe to add our * extra media. */ static void xl_miibus_mediainit(dev) device_t dev; { struct xl_softc *sc; struct mii_data *mii; struct ifmedia *ifm; sc = device_get_softc(dev); mii = device_get_softc(sc->xl_miibus); ifm = &mii->mii_media; XL_LOCK(sc); if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { /* * Check for a 10baseFL board in disguise. */ if (sc->xl_type == XL_TYPE_905B && sc->xl_media == XL_MEDIAOPT_10FL) { if (bootverbose) printf("xl%d: found 10baseFL\n", sc->xl_unit); ifmedia_add(ifm, IFM_ETHER|IFM_10_FL, 0, NULL); ifmedia_add(ifm, IFM_ETHER|IFM_10_FL|IFM_HDX, 0, NULL); if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) ifmedia_add(ifm, IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); } else { if (bootverbose) printf("xl%d: found AUI\n", sc->xl_unit); ifmedia_add(ifm, IFM_ETHER|IFM_10_5, 0, NULL); } } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (bootverbose) printf("xl%d: found BNC\n", sc->xl_unit); ifmedia_add(ifm, IFM_ETHER|IFM_10_2, 0, NULL); } XL_UNLOCK(sc); return; } /* * The EEPROM is slow: give it time to come ready after issuing * it a command. */ static int xl_eeprom_wait(sc) struct xl_softc *sc; { int i; for (i = 0; i < 100; i++) { if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY) DELAY(162); else break; } if (i == 100) { printf("xl%d: eeprom failed to come ready\n", sc->xl_unit); return(1); } return(0); } /* * Read a sequence of words from the EEPROM. Note that ethernet address * data is stored in the EEPROM in network byte order. */ static int xl_read_eeprom(sc, dest, off, cnt, swap) struct xl_softc *sc; caddr_t dest; int off; int cnt; int swap; { int err = 0, i; u_int16_t word = 0, *ptr; #define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F)) #define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F) /* WARNING! DANGER! * It's easy to accidentally overwrite the rom content! * Note: the 3c575 uses 8bit EEPROM offsets. */ XL_SEL_WIN(0); if (xl_eeprom_wait(sc)) return(1); if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30) off += 0x30; for (i = 0; i < cnt; i++) { if (sc->xl_flags & XL_FLAG_8BITROM) CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i)); else CSR_WRITE_2(sc, XL_W0_EE_CMD, XL_EE_READ | EEPROM_5BIT_OFFSET(off + i)); err = xl_eeprom_wait(sc); if (err) break; word = CSR_READ_2(sc, XL_W0_EE_DATA); ptr = (u_int16_t *)(dest + (i * 2)); if (swap) *ptr = ntohs(word); else *ptr = word; } return(err ? 1 : 0); } /* * NICs older than the 3c905B have only one multicast option, which * is to enable reception of all multicast frames. */ static void xl_setmulti(sc) struct xl_softc *sc; { struct ifnet *ifp; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); if (ifp->if_flags & IFF_ALLMULTI) { rxfilt |= XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) mcnt++; if (mcnt) rxfilt |= XL_RXFILTER_ALLMULTI; else rxfilt &= ~XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } /* * 3c905B adapters have a hash filter that we can program. */ static void xl_setmulti_hash(sc) struct xl_softc *sc; { struct ifnet *ifp; int h = 0, i; struct ifmultiaddr *ifma; u_int8_t rxfilt; int mcnt = 0; ifp = &sc->arpcom.ac_if; XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); if (ifp->if_flags & IFF_ALLMULTI) { rxfilt |= XL_RXFILTER_ALLMULTI; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } else rxfilt &= ~XL_RXFILTER_ALLMULTI; /* first, zot all the existing hash bits */ for (i = 0; i < XL_HASHFILT_SIZE; i++) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i); /* now program new ones */ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) continue; /* * Note: the 3c905B currently only supports a 64-bit hash * table, which means we really only need 6 bits, but the * manual indicates that future chip revisions will have a * 256-bit hash table, hence the routine is set up to * calculate 8 bits of position info in case we need it some * day. * Note II, The Sequel: _CURRENT_ versions of the 3c905B have * a 256 bit hash table. This means we have to use all 8 bits * regardless. On older cards, the upper 2 bits will be * ignored. Grrrr.... */ h = ether_crc32_be(LLADDR((struct sockaddr_dl *) ifma->ifma_addr), ETHER_ADDR_LEN) & 0xFF; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|XL_HASH_SET|h); mcnt++; } if (mcnt) rxfilt |= XL_RXFILTER_MULTIHASH; else rxfilt &= ~XL_RXFILTER_MULTIHASH; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); return; } #ifdef notdef static void xl_testpacket(sc) struct xl_softc *sc; { struct mbuf *m; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return; bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_dhost, ETHER_ADDR_LEN); bcopy(&sc->arpcom.ac_enaddr, mtod(m, struct ether_header *)->ether_shost, ETHER_ADDR_LEN); mtod(m, struct ether_header *)->ether_type = htons(3); mtod(m, unsigned char *)[14] = 0; mtod(m, unsigned char *)[15] = 0; mtod(m, unsigned char *)[16] = 0xE3; m->m_len = m->m_pkthdr.len = sizeof(struct ether_header) + 3; IF_ENQUEUE(&ifp->if_snd, m); xl_start(ifp); return; } #endif static void xl_setcfg(sc) struct xl_softc *sc; { u_int32_t icfg; XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); icfg &= ~XL_ICFG_CONNECTOR_MASK; if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BT4) icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS); if (sc->xl_media & XL_MEDIAOPT_BTX) icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); return; } static void xl_setmode(sc, media) struct xl_softc *sc; int media; { u_int32_t icfg; u_int16_t mediastat; printf("xl%d: selecting ", sc->xl_unit); XL_SEL_WIN(4); mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG); if (sc->xl_media & XL_MEDIAOPT_BT) { if (IFM_SUBTYPE(media) == IFM_10_T) { printf("10baseT transceiver, "); sc->xl_xcvr = XL_XCVR_10BT; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BFX) { if (IFM_SUBTYPE(media) == IFM_100_FX) { printf("100baseFX port, "); sc->xl_xcvr = XL_XCVR_100BFX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS); mediastat |= XL_MEDIASTAT_LINKBEAT; mediastat &= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { if (IFM_SUBTYPE(media) == IFM_10_5) { printf("AUI port, "); sc->xl_xcvr = XL_XCVR_AUI; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD); mediastat |= ~XL_MEDIASTAT_SQEENB; } if (IFM_SUBTYPE(media) == IFM_10_FL) { printf("10baseFL transceiver, "); sc->xl_xcvr = XL_XCVR_AUI; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD); mediastat |= ~XL_MEDIASTAT_SQEENB; } } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (IFM_SUBTYPE(media) == IFM_10_2) { printf("BNC port, "); sc->xl_xcvr = XL_XCVR_COAX; icfg &= ~XL_ICFG_CONNECTOR_MASK; icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS); mediastat &= ~(XL_MEDIASTAT_LINKBEAT| XL_MEDIASTAT_JABGUARD| XL_MEDIASTAT_SQEENB); } } if ((media & IFM_GMASK) == IFM_FDX || IFM_SUBTYPE(media) == IFM_100_FX) { printf("full duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX); } else { printf("half duplex\n"); XL_SEL_WIN(3); CSR_WRITE_1(sc, XL_W3_MAC_CTRL, (CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX)); } if (IFM_SUBTYPE(media) == IFM_10_2) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg); XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat); DELAY(800); XL_SEL_WIN(7); return; } static void xl_reset(sc) struct xl_softc *sc; { register int i; XL_SEL_WIN(0); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET | ((sc->xl_flags & XL_FLAG_WEIRDRESET) ? XL_RESETOPT_DISADVFD:0)); /* * If we're using memory mapped register mode, pause briefly * after issuing the reset command before trying to access any * other registers. With my 3c575C cardbus card, failing to do * this results in the system locking up while trying to poll * the command busy bit in the status register. */ if (sc->xl_flags & XL_FLAG_USE_MMIO) DELAY(100000); for (i = 0; i < XL_TIMEOUT; i++) { DELAY(10); if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY)) break; } if (i == XL_TIMEOUT) printf("xl%d: reset didn't complete\n", sc->xl_unit); /* Reset TX and RX. */ /* Note: the RX reset takes an absurd amount of time * on newer versions of the Tornado chips such as those * on the 3c905CX and newer 3c908C cards. We wait an * extra amount of time so that xl_wait() doesn't complain * and annoy the users. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); DELAY(100000); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR || sc->xl_flags & XL_FLAG_INVERT_MII_PWR) { XL_SEL_WIN(2); CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS, CSR_READ_2(sc, XL_W2_RESET_OPTIONS) | ((sc->xl_flags & XL_FLAG_INVERT_LED_PWR)?XL_RESETOPT_INVERT_LED:0) | ((sc->xl_flags & XL_FLAG_INVERT_MII_PWR)?XL_RESETOPT_INVERT_MII:0) ); } /* Wait a little while for the chip to get its brains in order. */ DELAY(100000); return; } /* * Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device * IDs against our list and return a device name if we find a match. */ static int xl_probe(dev) device_t dev; { struct xl_type *t; t = xl_devs; while(t->xl_name != NULL) { if ((pci_get_vendor(dev) == t->xl_vid) && (pci_get_device(dev) == t->xl_did)) { device_set_desc(dev, t->xl_name); return(0); } t++; } return(ENXIO); } /* * This routine is a kludge to work around possible hardware faults * or manufacturing defects that can cause the media options register * (or reset options register, as it's called for the first generation * 3c90x adapters) to return an incorrect result. I have encountered * one Dell Latitude laptop docking station with an integrated 3c905-TX * which doesn't have any of the 'mediaopt' bits set. This screws up * the attach routine pretty badly because it doesn't know what media * to look for. If we find ourselves in this predicament, this routine * will try to guess the media options values and warn the user of a * possible manufacturing defect with his adapter/system/whatever. */ static void xl_mediacheck(sc) struct xl_softc *sc; { /* * If some of the media options bits are set, assume they are * correct. If not, try to figure it out down below. * XXX I should check for 10baseFL, but I don't have an adapter * to test with. */ if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) { /* * Check the XCVR value. If it's not in the normal range * of values, we need to fake it up here. */ if (sc->xl_xcvr <= XL_XCVR_AUTO) return; else { printf("xl%d: bogus xcvr value " "in EEPROM (%x)\n", sc->xl_unit, sc->xl_xcvr); printf("xl%d: choosing new default based " "on card type\n", sc->xl_unit); } } else { if (sc->xl_type == XL_TYPE_905B && sc->xl_media & XL_MEDIAOPT_10FL) return; printf("xl%d: WARNING: no media options bits set in " "the media options register!!\n", sc->xl_unit); printf("xl%d: this could be a manufacturing defect in " "your adapter or system\n", sc->xl_unit); printf("xl%d: attempting to guess media type; you " "should probably consult your vendor\n", sc->xl_unit); } xl_choose_xcvr(sc, 1); return; } static void xl_choose_xcvr(sc, verbose) struct xl_softc *sc; int verbose; { u_int16_t devid; /* * Read the device ID from the EEPROM. * This is what's loaded into the PCI device ID register, so it has * to be correct otherwise we wouldn't have gotten this far. */ xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0); switch(devid) { case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */ case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */ sc->xl_media = XL_MEDIAOPT_BT; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printf("xl%d: guessing 10BaseT " "transceiver\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */ case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printf("xl%d: guessing COMBO " "(AUI/BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */ sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC; sc->xl_xcvr = XL_XCVR_10BT; if (verbose) printf("xl%d: guessing TPC (BNC/TP)\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */ sc->xl_media = XL_MEDIAOPT_10FL; sc->xl_xcvr = XL_XCVR_AUI; if (verbose) printf("xl%d: guessing 10baseFL\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ case TC_DEVICEID_HURRICANE_555: /* 3c555 */ case TC_DEVICEID_HURRICANE_556: /* 3c556 */ case TC_DEVICEID_HURRICANE_556B: /* 3c556B */ case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */ case TC_DEVICEID_HURRICANE_575B: /* 3c575B */ case TC_DEVICEID_HURRICANE_575C: /* 3c575C */ case TC_DEVICEID_HURRICANE_656: /* 3c656 */ case TC_DEVICEID_HURRICANE_656B: /* 3c656B */ case TC_DEVICEID_TORNADO_656C: /* 3c656C */ case TC_DEVICEID_TORNADO_10_100BT_920B: /* 3c920B-EMB */ sc->xl_media = XL_MEDIAOPT_MII; sc->xl_xcvr = XL_XCVR_MII; if (verbose) printf("xl%d: guessing MII\n", sc->xl_unit); break; case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */ case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */ sc->xl_media = XL_MEDIAOPT_BT4; sc->xl_xcvr = XL_XCVR_MII; if (verbose) printf("xl%d: guessing 100BaseT4/MII\n", sc->xl_unit); break; case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */ case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */ case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */ case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */ case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */ case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */ sc->xl_media = XL_MEDIAOPT_BTX; sc->xl_xcvr = XL_XCVR_AUTO; if (verbose) printf("xl%d: guessing 10/100 internal\n", sc->xl_unit); break; case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */ sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI; sc->xl_xcvr = XL_XCVR_AUTO; if (verbose) printf("xl%d: guessing 10/100 " "plus BNC/AUI\n", sc->xl_unit); break; default: printf("xl%d: unknown device ID: %x -- " "defaulting to 10baseT\n", sc->xl_unit, devid); sc->xl_media = XL_MEDIAOPT_BT; break; } return; } /* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ static int xl_attach(dev) device_t dev; { u_char eaddr[ETHER_ADDR_LEN]; u_int16_t xcvr[2]; struct xl_softc *sc; struct ifnet *ifp; int media = IFM_ETHER|IFM_100_TX|IFM_FDX; int unit, error = 0, rid, res; uint16_t did; sc = device_get_softc(dev); unit = device_get_unit(dev); mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts); did = pci_get_device(dev); sc->xl_flags = 0; if (did == TC_DEVICEID_HURRICANE_555) sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK; if (did == TC_DEVICEID_HURRICANE_556 || did == TC_DEVICEID_HURRICANE_556B) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET | XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR; if (did == TC_DEVICEID_HURRICANE_555 || did == TC_DEVICEID_HURRICANE_556) sc->xl_flags |= XL_FLAG_8BITROM; if (did == TC_DEVICEID_HURRICANE_556B) sc->xl_flags |= XL_FLAG_NO_XCVR_PWR; if (did == TC_DEVICEID_HURRICANE_575A || did == TC_DEVICEID_HURRICANE_575B || did == TC_DEVICEID_HURRICANE_575C || did == TC_DEVICEID_HURRICANE_656B || did == TC_DEVICEID_TORNADO_656C) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_8BITROM; if (did == TC_DEVICEID_HURRICANE_656) sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK; if (did == TC_DEVICEID_HURRICANE_575B) sc->xl_flags |= XL_FLAG_INVERT_LED_PWR; if (did == TC_DEVICEID_HURRICANE_575C) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; if (did == TC_DEVICEID_TORNADO_656C) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR; if (did == TC_DEVICEID_HURRICANE_656 || did == TC_DEVICEID_HURRICANE_656B) sc->xl_flags |= XL_FLAG_INVERT_MII_PWR | XL_FLAG_INVERT_LED_PWR; if (did == TC_DEVICEID_TORNADO_10_100BT_920B) sc->xl_flags |= XL_FLAG_PHYOK; switch (did) { case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */ case TC_DEVICEID_HURRICANE_575A: case TC_DEVICEID_HURRICANE_575B: case TC_DEVICEID_HURRICANE_575C: sc->xl_flags |= XL_FLAG_NO_MMIO; break; default: break; } -#ifndef BURN_BRIDGES - /* - * If this is a 3c905B, we have to check one extra thing. - * The 905B supports power management and may be placed in - * a low-power mode (D3 mode), typically by certain operating - * systems which shall not be named. The PCI BIOS is supposed - * to reset the NIC and bring it out of low-power mode, but - * some do not. Consequently, we have to see if this chip - * supports power management, and if so, make sure it's not - * in low-power mode. If power management is available, the - * capid byte will be 0x01. - * - * I _think_ that what actually happens is that the chip - * loses its PCI configuration during the transition from - * D3 back to D0; this means that it should be possible for - * us to save the PCI iobase, membase and IRQ, put the chip - * back in the D0 state, then restore the PCI config ourselves. - */ - - if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { - u_int32_t iobase, membase, irq; - - /* Save important PCI config data. */ - iobase = pci_read_config(dev, XL_PCI_LOIO, 4); - membase = pci_read_config(dev, XL_PCI_LOMEM, 4); - irq = pci_read_config(dev, XL_PCI_INTLINE, 4); - - /* Reset the power state. */ - printf("xl%d: chip is in D%d power mode " - "-- setting to D0\n", unit, - pci_get_powerstate(dev)); - - pci_set_powerstate(dev, PCI_POWERSTATE_D0); - - /* Restore PCI config data. */ - pci_write_config(dev, XL_PCI_LOIO, iobase, 4); - pci_write_config(dev, XL_PCI_LOMEM, membase, 4); - pci_write_config(dev, XL_PCI_INTLINE, irq, 4); - } -#endif - /* * Map control/status registers. */ pci_enable_busmaster(dev); if ((sc->xl_flags & XL_FLAG_NO_MMIO) == 0) { rid = XL_PCI_LOMEM; res = SYS_RES_MEMORY; sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE); } if (sc->xl_res != NULL) { sc->xl_flags |= XL_FLAG_USE_MMIO; if (bootverbose) printf("xl%d: using memory mapped I/O\n", unit); } else { rid = XL_PCI_LOIO; res = SYS_RES_IOPORT; sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE); if (sc->xl_res == NULL) { printf ("xl%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } if (bootverbose) printf("xl%d: using port I/O\n", unit); } sc->xl_btag = rman_get_bustag(sc->xl_res); sc->xl_bhandle = rman_get_bushandle(sc->xl_res); if (sc->xl_flags & XL_FLAG_FUNCREG) { rid = XL_PCI_FUNCMEM; sc->xl_fres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->xl_fres == NULL) { printf ("xl%d: couldn't map ports/memory\n", unit); error = ENXIO; goto fail; } sc->xl_ftag = rman_get_bustag(sc->xl_fres); sc->xl_fhandle = rman_get_bushandle(sc->xl_fres); } /* Allocate interrupt */ rid = 0; sc->xl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (sc->xl_irq == NULL) { printf("xl%d: couldn't map interrupt\n", unit); error = ENXIO; goto fail; } /* Reset the adapter. */ xl_reset(sc); /* * Get station address from the EEPROM. */ if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) { printf("xl%d: failed to read station address\n", sc->xl_unit); error = ENXIO; goto fail; } sc->xl_unit = unit; callout_handle_init(&sc->xl_stat_ch); bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); /* * Now allocate a tag for the DMA descriptor lists and a chunk * of DMA-able memory based on the tag. Also obtain the DMA * addresses of the RX and TX ring, which we'll need later. * All of our lists are allocated as a contiguous block * of memory. */ error = bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, NULL, NULL, &sc->xl_ldata.xl_rx_tag); if (error) { printf("xl%d: failed to allocate rx dma tag\n", unit); goto fail; } error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag, (void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->xl_ldata.xl_rx_dmamap); if (error) { printf("xl%d: no memory for rx list buffers!\n", unit); bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag); sc->xl_ldata.xl_rx_tag = NULL; goto fail; } error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ, xl_dma_map_addr, &sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT); if (error) { printf("xl%d: cannot get dma address of the rx ring!\n", unit); bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list, sc->xl_ldata.xl_rx_dmamap); bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag); sc->xl_ldata.xl_rx_tag = NULL; goto fail; } error = bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, NULL, NULL, &sc->xl_ldata.xl_tx_tag); if (error) { printf("xl%d: failed to allocate tx dma tag\n", unit); goto fail; } error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag, (void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->xl_ldata.xl_tx_dmamap); if (error) { printf("xl%d: no memory for list buffers!\n", unit); bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag); sc->xl_ldata.xl_tx_tag = NULL; goto fail; } error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ, xl_dma_map_addr, &sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT); if (error) { printf("xl%d: cannot get dma address of the tx ring!\n", unit); bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list, sc->xl_ldata.xl_tx_dmamap); bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag); sc->xl_ldata.xl_tx_tag = NULL; goto fail; } /* * Allocate a DMA tag for the mapping of mbufs. */ error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0, NULL, NULL, &sc->xl_mtag); if (error) { printf("xl%d: failed to allocate mbuf dma tag\n", unit); goto fail; } /* We need a spare DMA map for the RX ring. */ error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap); if (error) goto fail; /* * Figure out the card type. 3c905B adapters have the * 'supportsNoTxLength' bit set in the capabilities * word in the EEPROM. * Note: my 3c575C cardbus card lies. It returns a value * of 0x1578 for its capabilities word, which is somewhat * nonsensical. Another way to distinguish a 3c90x chip * from a 3c90xB/C chip is to check for the 'supportsLargePackets' * bit. This will only be set for 3c90x boomerage chips. */ xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0); if (sc->xl_caps & XL_CAPS_NO_TXLENGTH || !(sc->xl_caps & XL_CAPS_LARGE_PKTS)) sc->xl_type = XL_TYPE_905B; else sc->xl_type = XL_TYPE_90X; ifp = &sc->arpcom.ac_if; ifp->if_softc = sc; if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = xl_ioctl; ifp->if_capabilities = IFCAP_VLAN_MTU; if (sc->xl_type == XL_TYPE_905B) { ifp->if_start = xl_start_90xB; ifp->if_hwassist = XL905B_CSUM_FEATURES; #ifdef XL905B_TXCSUM_BROKEN ifp->if_capabilities |= IFCAP_RXCSUM; #else ifp->if_capabilities |= IFCAP_HWCSUM; #endif } else ifp->if_start = xl_start; ifp->if_watchdog = xl_watchdog; ifp->if_init = xl_init; ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = XL_TX_LIST_CNT - 1; ifp->if_capenable = ifp->if_capabilities; /* * Now we have to see what sort of media we have. * This includes probing for an MII interace and a * possible PHY. */ XL_SEL_WIN(3); sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT); if (bootverbose) printf("xl%d: media options word: %x\n", sc->xl_unit, sc->xl_media); xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0); sc->xl_xcvr = xcvr[0] | xcvr[1] << 16; sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK; sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS; xl_mediacheck(sc); if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX || sc->xl_media & XL_MEDIAOPT_BT4) { if (bootverbose) printf("xl%d: found MII/AUTO\n", sc->xl_unit); xl_setcfg(sc); if (mii_phy_probe(dev, &sc->xl_miibus, xl_ifmedia_upd, xl_ifmedia_sts)) { printf("xl%d: no PHY found!\n", sc->xl_unit); error = ENXIO; goto fail; } goto done; } /* * Sanity check. If the user has selected "auto" and this isn't * a 10/100 card of some kind, we need to force the transceiver * type to something sane. */ if (sc->xl_xcvr == XL_XCVR_AUTO) xl_choose_xcvr(sc, bootverbose); /* * Do ifmedia setup. */ if (sc->xl_media & XL_MEDIAOPT_BT) { if (bootverbose) printf("xl%d: found 10baseT\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); } if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) { /* * Check for a 10baseFL board in disguise. */ if (sc->xl_type == XL_TYPE_905B && sc->xl_media == XL_MEDIAOPT_10FL) { if (bootverbose) printf("xl%d: found 10baseFL\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX, 0, NULL); if (sc->xl_caps & XL_CAPS_FULL_DUPLEX) ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL); } else { if (bootverbose) printf("xl%d: found AUI\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); } } if (sc->xl_media & XL_MEDIAOPT_BNC) { if (bootverbose) printf("xl%d: found BNC\n", sc->xl_unit); ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); } if (sc->xl_media & XL_MEDIAOPT_BFX) { if (bootverbose) printf("xl%d: found 100baseFX\n", sc->xl_unit); ifp->if_baudrate = 100000000; ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL); } /* Choose a default media. */ switch(sc->xl_xcvr) { case XL_XCVR_10BT: media = IFM_ETHER|IFM_10_T; xl_setmode(sc, media); break; case XL_XCVR_AUI: if (sc->xl_type == XL_TYPE_905B && sc->xl_media == XL_MEDIAOPT_10FL) { media = IFM_ETHER|IFM_10_FL; xl_setmode(sc, media); } else { media = IFM_ETHER|IFM_10_5; xl_setmode(sc, media); } break; case XL_XCVR_COAX: media = IFM_ETHER|IFM_10_2; xl_setmode(sc, media); break; case XL_XCVR_AUTO: case XL_XCVR_100BTX: case XL_XCVR_MII: /* Chosen by miibus */ break; case XL_XCVR_100BFX: media = IFM_ETHER|IFM_100_FX; break; default: printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, sc->xl_xcvr); /* * This will probably be wrong, but it prevents * the ifmedia code from panicking. */ media = IFM_ETHER|IFM_10_T; break; } if (sc->xl_miibus == NULL) ifmedia_set(&sc->ifmedia, media); done: if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) { XL_SEL_WIN(0); CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS); } /* * Call MI attach routine. */ ether_ifattach(ifp, eaddr); /* Hook interrupt last to avoid having to lock softc */ error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET, xl_intr, sc, &sc->xl_intrhand); if (error) { printf("xl%d: couldn't set up irq\n", unit); ether_ifdetach(ifp); goto fail; } fail: if (error) xl_detach(dev); return(error); } /* * Shutdown hardware and free up resources. This can be called any * time after the mutex has been initialized. It is called in both * the error case in attach and the normal detach case so it needs * to be careful about only freeing resources that have actually been * allocated. */ static int xl_detach(dev) device_t dev; { struct xl_softc *sc; struct ifnet *ifp; int rid, res; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->xl_mtx), ("xl mutex not initialized")); XL_LOCK(sc); ifp = &sc->arpcom.ac_if; if (sc->xl_flags & XL_FLAG_USE_MMIO) { rid = XL_PCI_LOMEM; res = SYS_RES_MEMORY; } else { rid = XL_PCI_LOIO; res = SYS_RES_IOPORT; } /* These should only be active if attach succeeded */ if (device_is_attached(dev)) { xl_reset(sc); xl_stop(sc); ether_ifdetach(ifp); } if (sc->xl_miibus) device_delete_child(dev, sc->xl_miibus); bus_generic_detach(dev); ifmedia_removeall(&sc->ifmedia); if (sc->xl_intrhand) bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand); if (sc->xl_irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq); if (sc->xl_fres != NULL) bus_release_resource(dev, SYS_RES_MEMORY, XL_PCI_FUNCMEM, sc->xl_fres); if (sc->xl_res) bus_release_resource(dev, res, rid, sc->xl_res); if (sc->xl_mtag) { bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap); bus_dma_tag_destroy(sc->xl_mtag); } if (sc->xl_ldata.xl_rx_tag) { bus_dmamap_unload(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap); bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list, sc->xl_ldata.xl_rx_dmamap); bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag); } if (sc->xl_ldata.xl_tx_tag) { bus_dmamap_unload(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap); bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list, sc->xl_ldata.xl_tx_dmamap); bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag); } XL_UNLOCK(sc); mtx_destroy(&sc->xl_mtx); return(0); } /* * Initialize the transmit descriptors. */ static int xl_list_tx_init(sc) struct xl_softc *sc; { struct xl_chain_data *cd; struct xl_list_data *ld; int error, i; cd = &sc->xl_cdata; ld = &sc->xl_ldata; for (i = 0; i < XL_TX_LIST_CNT; i++) { cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; error = bus_dmamap_create(sc->xl_mtag, 0, &cd->xl_tx_chain[i].xl_map); if (error) return(error); cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr + i * sizeof(struct xl_list); if (i == (XL_TX_LIST_CNT - 1)) cd->xl_tx_chain[i].xl_next = NULL; else cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; } cd->xl_tx_free = &cd->xl_tx_chain[0]; cd->xl_tx_tail = cd->xl_tx_head = NULL; bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE); return(0); } /* * Initialize the transmit descriptors. */ static int xl_list_tx_init_90xB(sc) struct xl_softc *sc; { struct xl_chain_data *cd; struct xl_list_data *ld; int error, i; cd = &sc->xl_cdata; ld = &sc->xl_ldata; for (i = 0; i < XL_TX_LIST_CNT; i++) { cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i]; error = bus_dmamap_create(sc->xl_mtag, 0, &cd->xl_tx_chain[i].xl_map); if (error) return(error); cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr + i * sizeof(struct xl_list); if (i == (XL_TX_LIST_CNT - 1)) cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0]; else cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1]; if (i == 0) cd->xl_tx_chain[i].xl_prev = &cd->xl_tx_chain[XL_TX_LIST_CNT - 1]; else cd->xl_tx_chain[i].xl_prev = &cd->xl_tx_chain[i - 1]; } bzero(ld->xl_tx_list, XL_TX_LIST_SZ); ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY); cd->xl_tx_prod = 1; cd->xl_tx_cons = 1; cd->xl_tx_cnt = 0; bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE); return(0); } /* * Initialize the RX descriptors and allocate mbufs for them. Note that * we arrange the descriptors in a closed ring, so that the last descriptor * points back to the first. */ static int xl_list_rx_init(sc) struct xl_softc *sc; { struct xl_chain_data *cd; struct xl_list_data *ld; int error, i, next; u_int32_t nextptr; cd = &sc->xl_cdata; ld = &sc->xl_ldata; for (i = 0; i < XL_RX_LIST_CNT; i++) { cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i]; error = bus_dmamap_create(sc->xl_mtag, 0, &cd->xl_rx_chain[i].xl_map); if (error) return(error); error = xl_newbuf(sc, &cd->xl_rx_chain[i]); if (error) return(error); if (i == (XL_RX_LIST_CNT - 1)) next = 0; else next = i + 1; nextptr = ld->xl_rx_dmaaddr + next * sizeof(struct xl_list_onefrag); cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next]; ld->xl_rx_list[i].xl_next = htole32(nextptr); } bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE); cd->xl_rx_head = &cd->xl_rx_chain[0]; return(0); } /* * Initialize an RX descriptor and attach an MBUF cluster. * If we fail to do so, we need to leave the old mbuf and * the old DMA map untouched so that it can be reused. */ static int xl_newbuf(sc, c) struct xl_softc *sc; struct xl_chain_onefrag *c; { struct mbuf *m_new = NULL; bus_dmamap_t map; int error; u_int32_t baddr; m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); if (m_new == NULL) return(ENOBUFS); m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; /* Force longword alignment for packet payload. */ m_adj(m_new, ETHER_ALIGN); error = bus_dmamap_load_mbuf(sc->xl_mtag, sc->xl_tmpmap, m_new, xl_dma_map_rxbuf, &baddr, BUS_DMA_NOWAIT); if (error) { m_freem(m_new); printf("xl%d: can't map mbuf (error %d)\n", sc->xl_unit, error); return(error); } bus_dmamap_unload(sc->xl_mtag, c->xl_map); map = c->xl_map; c->xl_map = sc->xl_tmpmap; sc->xl_tmpmap = map; c->xl_mbuf = m_new; c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG); c->xl_ptr->xl_status = 0; c->xl_ptr->xl_frag.xl_addr = htole32(baddr); bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD); return(0); } static int xl_rx_resync(sc) struct xl_softc *sc; { struct xl_chain_onefrag *pos; int i; pos = sc->xl_cdata.xl_rx_head; for (i = 0; i < XL_RX_LIST_CNT; i++) { if (pos->xl_ptr->xl_status) break; pos = pos->xl_next; } if (i == XL_RX_LIST_CNT) return(0); sc->xl_cdata.xl_rx_head = pos; return(EAGAIN); } /* * A frame has been uploaded: pass the resulting mbuf chain up to * the higher level protocols. */ static void xl_rxeof(sc) struct xl_softc *sc; { struct mbuf *m; struct ifnet *ifp; struct xl_chain_onefrag *cur_rx; int total_len = 0; u_int32_t rxstat; XL_LOCK_ASSERT(sc); ifp = &sc->arpcom.ac_if; again: bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_POSTREAD); while((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) { cur_rx = sc->xl_cdata.xl_rx_head; sc->xl_cdata.xl_rx_head = cur_rx->xl_next; total_len = rxstat & XL_RXSTAT_LENMASK; /* * Since we have told the chip to allow large frames, * we need to trap giant frame errors in software. We allow * a little more than the normal frame size to account for * frames with VLAN tags. */ if (total_len > XL_MAX_FRAMELEN) rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE); /* * If an error occurs, update stats, clear the * status word and leave the mbuf cluster in place: * it should simply get re-used next time this descriptor * comes up in the ring. */ if (rxstat & XL_RXSTAT_UP_ERROR) { ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE); continue; } /* * If the error bit was not set, the upload complete * bit should be set which means we have a valid packet. * If not, something truly strange has happened. */ if (!(rxstat & XL_RXSTAT_UP_CMPLT)) { printf("xl%d: bad receive status -- " "packet dropped\n", sc->xl_unit); ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE); continue; } /* No errors; receive the packet. */ bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map, BUS_DMASYNC_POSTREAD); m = cur_rx->xl_mbuf; /* * Try to conjure up a new mbuf cluster. If that * fails, it means we have an out of memory condition and * should leave the buffer in place and continue. This will * result in a lost packet, but there's little else we * can do in this situation. */ if (xl_newbuf(sc, cur_rx)) { ifp->if_ierrors++; cur_rx->xl_ptr->xl_status = 0; bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE); continue; } bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE); ifp->if_ipackets++; m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = total_len; if (ifp->if_capenable & IFCAP_RXCSUM) { /* Do IP checksum checking. */ if (rxstat & XL_RXSTAT_IPCKOK) m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; if (!(rxstat & XL_RXSTAT_IPCKERR)) m->m_pkthdr.csum_flags |= CSUM_IP_VALID; if ((rxstat & XL_RXSTAT_TCPCOK && !(rxstat & XL_RXSTAT_TCPCKERR)) || (rxstat & XL_RXSTAT_UDPCKOK && !(rxstat & XL_RXSTAT_UDPCKERR))) { m->m_pkthdr.csum_flags |= CSUM_DATA_VALID|CSUM_PSEUDO_HDR; m->m_pkthdr.csum_data = 0xffff; } } XL_UNLOCK(sc); (*ifp->if_input)(ifp, m); XL_LOCK(sc); } /* * Handle the 'end of channel' condition. When the upload * engine hits the end of the RX ring, it will stall. This * is our cue to flush the RX ring, reload the uplist pointer * register and unstall the engine. * XXX This is actually a little goofy. With the ThunderLAN * chip, you get an interrupt when the receiver hits the end * of the receive ring, which tells you exactly when you * you need to reload the ring pointer. Here we have to * fake it. I'm mad at myself for not being clever enough * to avoid the use of a goto here. */ if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 || CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr); sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0]; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); goto again; } return; } /* * A frame was downloaded to the chip. It's safe for us to clean up * the list buffers. */ static void xl_txeof(sc) struct xl_softc *sc; { struct xl_chain *cur_tx; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* Clear the timeout timer. */ ifp->if_timer = 0; /* * Go through our tx list and free mbufs for those * frames that have been uploaded. Note: the 3c905B * sets a special bit in the status word to let us * know that a frame has been downloaded, but the * original 3c900/3c905 adapters don't do that. * Consequently, we have to use a different test if * xl_type != XL_TYPE_905B. */ while(sc->xl_cdata.xl_tx_head != NULL) { cur_tx = sc->xl_cdata.xl_tx_head; if (CSR_READ_4(sc, XL_DOWNLIST_PTR)) break; sc->xl_cdata.xl_tx_head = cur_tx->xl_next; bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map); m_freem(cur_tx->xl_mbuf); cur_tx->xl_mbuf = NULL; ifp->if_opackets++; cur_tx->xl_next = sc->xl_cdata.xl_tx_free; sc->xl_cdata.xl_tx_free = cur_tx; } if (sc->xl_cdata.xl_tx_head == NULL) { ifp->if_flags &= ~IFF_OACTIVE; sc->xl_cdata.xl_tx_tail = NULL; } else { if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED || !CSR_READ_4(sc, XL_DOWNLIST_PTR)) { CSR_WRITE_4(sc, XL_DOWNLIST_PTR, sc->xl_cdata.xl_tx_head->xl_phys); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } } return; } static void xl_txeof_90xB(sc) struct xl_softc *sc; { struct xl_chain *cur_tx = NULL; struct ifnet *ifp; int idx; ifp = &sc->arpcom.ac_if; bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap, BUS_DMASYNC_POSTREAD); idx = sc->xl_cdata.xl_tx_cons; while(idx != sc->xl_cdata.xl_tx_prod) { cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; if (!(le32toh(cur_tx->xl_ptr->xl_status) & XL_TXSTAT_DL_COMPLETE)) break; if (cur_tx->xl_mbuf != NULL) { bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map); m_freem(cur_tx->xl_mbuf); cur_tx->xl_mbuf = NULL; } ifp->if_opackets++; sc->xl_cdata.xl_tx_cnt--; XL_INC(idx, XL_TX_LIST_CNT); ifp->if_timer = 0; } sc->xl_cdata.xl_tx_cons = idx; if (cur_tx != NULL) ifp->if_flags &= ~IFF_OACTIVE; return; } /* * TX 'end of channel' interrupt handler. Actually, we should * only get a 'TX complete' interrupt if there's a transmit error, * so this is really TX error handler. */ static void xl_txeoc(sc) struct xl_softc *sc; { u_int8_t txstat; while((txstat = CSR_READ_1(sc, XL_TX_STATUS))) { if (txstat & XL_TXSTATUS_UNDERRUN || txstat & XL_TXSTATUS_JABBER || txstat & XL_TXSTATUS_RECLAIM) { printf("xl%d: transmission error: %x\n", sc->xl_unit, txstat); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); if (sc->xl_type == XL_TYPE_905B) { if (sc->xl_cdata.xl_tx_cnt) { int i; struct xl_chain *c; i = sc->xl_cdata.xl_tx_cons; c = &sc->xl_cdata.xl_tx_chain[i]; CSR_WRITE_4(sc, XL_DOWNLIST_PTR, c->xl_phys); CSR_WRITE_1(sc, XL_DOWN_POLL, 64); } } else { if (sc->xl_cdata.xl_tx_head != NULL) CSR_WRITE_4(sc, XL_DOWNLIST_PTR, sc->xl_cdata.xl_tx_head->xl_phys); } /* * Remember to set this for the * first generation 3c90X chips. */ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); if (txstat & XL_TXSTATUS_UNDERRUN && sc->xl_tx_thresh < XL_PACKET_SIZE) { sc->xl_tx_thresh += XL_MIN_FRAMELEN; printf("xl%d: tx underrun, increasing tx start" " threshold to %d bytes\n", sc->xl_unit, sc->xl_tx_thresh); } CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); if (sc->xl_type == XL_TYPE_905B) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); } CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } else { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); } /* * Write an arbitrary byte to the TX_STATUS register * to clear this interrupt/error and advance to the next. */ CSR_WRITE_1(sc, XL_TX_STATUS, 0x01); } return; } static void xl_intr(arg) void *arg; { struct xl_softc *sc; struct ifnet *ifp; u_int16_t status; sc = arg; XL_LOCK(sc); ifp = &sc->arpcom.ac_if; while((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS && status != 0xFFFF) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|(status & XL_INTRS)); if (status & XL_STAT_UP_COMPLETE) { int curpkts; curpkts = ifp->if_ipackets; xl_rxeof(sc); if (curpkts == ifp->if_ipackets) { while (xl_rx_resync(sc)) xl_rxeof(sc); } } if (status & XL_STAT_DOWN_COMPLETE) { if (sc->xl_type == XL_TYPE_905B) xl_txeof_90xB(sc); else xl_txeof(sc); } if (status & XL_STAT_TX_COMPLETE) { ifp->if_oerrors++; xl_txeoc(sc); } if (status & XL_STAT_ADFAIL) { xl_reset(sc); xl_init(sc); } if (status & XL_STAT_STATSOFLOW) { sc->xl_stats_no_timeout = 1; xl_stats_update(sc); sc->xl_stats_no_timeout = 0; } } if (ifp->if_snd.ifq_head != NULL) (*ifp->if_start)(ifp); XL_UNLOCK(sc); return; } static void xl_stats_update(xsc) void *xsc; { struct xl_softc *sc; struct ifnet *ifp; struct xl_stats xl_stats; u_int8_t *p; int i; struct mii_data *mii = NULL; bzero((char *)&xl_stats, sizeof(struct xl_stats)); sc = xsc; ifp = &sc->arpcom.ac_if; if (sc->xl_miibus != NULL) mii = device_get_softc(sc->xl_miibus); p = (u_int8_t *)&xl_stats; /* Read all the stats registers. */ XL_SEL_WIN(6); for (i = 0; i < 16; i++) *p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i); ifp->if_ierrors += xl_stats.xl_rx_overrun; ifp->if_collisions += xl_stats.xl_tx_multi_collision + xl_stats.xl_tx_single_collision + xl_stats.xl_tx_late_collision; /* * Boomerang and cyclone chips have an extra stats counter * in window 4 (BadSSD). We have to read this too in order * to clear out all the stats registers and avoid a statsoflow * interrupt. */ XL_SEL_WIN(4); CSR_READ_1(sc, XL_W4_BADSSD); if ((mii != NULL) && (!sc->xl_stats_no_timeout)) mii_tick(mii); XL_SEL_WIN(7); if (!sc->xl_stats_no_timeout) sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); return; } /* * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data * pointers to the fragment pointers. */ static int xl_encap(sc, c, m_head) struct xl_softc *sc; struct xl_chain *c; struct mbuf *m_head; { int error; u_int32_t status; struct ifnet *ifp; ifp = &sc->arpcom.ac_if; /* * Start packing the mbufs in this chain into * the fragment pointers. Stop when we run out * of fragments or hit the end of the mbuf chain. */ error = bus_dmamap_load_mbuf(sc->xl_mtag, c->xl_map, m_head, xl_dma_map_txbuf, c->xl_ptr, BUS_DMA_NOWAIT); if (error && error != EFBIG) { m_freem(m_head); printf("xl%d: can't map mbuf (error %d)\n", sc->xl_unit, error); return(1); } /* * Handle special case: we used up all 63 fragments, * but we have more mbufs left in the chain. Copy the * data into an mbuf cluster. Note that we don't * bother clearing the values in the other fragment * pointers/counters; it wouldn't gain us anything, * and would waste cycles. */ if (error) { struct mbuf *m_new; m_new = m_defrag(m_head, M_DONTWAIT); if (m_new == NULL) { m_freem(m_head); return(1); } else { m_head = m_new; } error = bus_dmamap_load_mbuf(sc->xl_mtag, c->xl_map, m_head, xl_dma_map_txbuf, c->xl_ptr, BUS_DMA_NOWAIT); if (error) { m_freem(m_head); printf("xl%d: can't map mbuf (error %d)\n", sc->xl_unit, error); return(1); } } if (sc->xl_type == XL_TYPE_905B) { status = XL_TXSTAT_RND_DEFEAT; #ifndef XL905B_TXCSUM_BROKEN if (m_head->m_pkthdr.csum_flags) { if (m_head->m_pkthdr.csum_flags & CSUM_IP) status |= XL_TXSTAT_IPCKSUM; if (m_head->m_pkthdr.csum_flags & CSUM_TCP) status |= XL_TXSTAT_TCPCKSUM; if (m_head->m_pkthdr.csum_flags & CSUM_UDP) status |= XL_TXSTAT_UDPCKSUM; } #endif c->xl_ptr->xl_status = htole32(status); } c->xl_mbuf = m_head; bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE); return(0); } /* * Main transmit routine. To avoid having to do mbuf copies, we put pointers * to the mbuf data regions directly in the transmit lists. We also save a * copy of the pointers since the transmit list fragment pointers are * physical addresses. */ static void xl_start(ifp) struct ifnet *ifp; { struct xl_softc *sc; struct mbuf *m_head = NULL; struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; struct xl_chain *prev_tx; u_int32_t status; int error; sc = ifp->if_softc; XL_LOCK(sc); /* * Check for an available queue slot. If there are none, * punt. */ if (sc->xl_cdata.xl_tx_free == NULL) { xl_txeoc(sc); xl_txeof(sc); if (sc->xl_cdata.xl_tx_free == NULL) { ifp->if_flags |= IFF_OACTIVE; XL_UNLOCK(sc); return; } } start_tx = sc->xl_cdata.xl_tx_free; while(sc->xl_cdata.xl_tx_free != NULL) { IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; /* Pick a descriptor off the free list. */ prev_tx = cur_tx; cur_tx = sc->xl_cdata.xl_tx_free; /* Pack the data into the descriptor. */ error = xl_encap(sc, cur_tx, m_head); if (error) { cur_tx = prev_tx; continue; } sc->xl_cdata.xl_tx_free = cur_tx->xl_next; cur_tx->xl_next = NULL; /* Chain it together. */ if (prev != NULL) { prev->xl_next = cur_tx; prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys); } prev = cur_tx; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->xl_mbuf); } /* * If there are no packets queued, bail. */ if (cur_tx == NULL) { XL_UNLOCK(sc); return; } /* * Place the request for the upload interrupt * in the last descriptor in the chain. This way, if * we're chaining several packets at once, we'll only * get an interupt once for the whole chain rather than * once for each packet. */ cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) | XL_TXSTAT_DL_INTR); bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap, BUS_DMASYNC_PREWRITE); /* * Queue the packets. If the TX channel is clear, update * the downlist pointer register. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); xl_wait(sc); if (sc->xl_cdata.xl_tx_head != NULL) { sc->xl_cdata.xl_tx_tail->xl_next = start_tx; sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next = htole32(start_tx->xl_phys); status = sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status; sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status = htole32(le32toh(status) & ~XL_TXSTAT_DL_INTR); sc->xl_cdata.xl_tx_tail = cur_tx; } else { sc->xl_cdata.xl_tx_head = start_tx; sc->xl_cdata.xl_tx_tail = cur_tx; } if (!CSR_READ_4(sc, XL_DOWNLIST_PTR)) CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); XL_SEL_WIN(7); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; /* * XXX Under certain conditions, usually on slower machines * where interrupts may be dropped, it's possible for the * adapter to chew up all the buffers in the receive ring * and stall, without us being able to do anything about it. * To guard against this, we need to make a pass over the * RX queue to make sure there aren't any packets pending. * Doing it here means we can flush the receive ring at the * same time the chip is DMAing the transmit descriptors we * just gave it. * * 3Com goes to some lengths to emphasize the Parallel Tasking (tm) * nature of their chips in all their marketing literature; * we may as well take advantage of it. :) */ xl_rxeof(sc); XL_UNLOCK(sc); return; } static void xl_start_90xB(ifp) struct ifnet *ifp; { struct xl_softc *sc; struct mbuf *m_head = NULL; struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx; struct xl_chain *prev_tx; int error, idx; sc = ifp->if_softc; XL_LOCK(sc); if (ifp->if_flags & IFF_OACTIVE) { XL_UNLOCK(sc); return; } idx = sc->xl_cdata.xl_tx_prod; start_tx = &sc->xl_cdata.xl_tx_chain[idx]; while (sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL) { if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) { ifp->if_flags |= IFF_OACTIVE; break; } IF_DEQUEUE(&ifp->if_snd, m_head); if (m_head == NULL) break; prev_tx = cur_tx; cur_tx = &sc->xl_cdata.xl_tx_chain[idx]; /* Pack the data into the descriptor. */ error = xl_encap(sc, cur_tx, m_head); if (error) { cur_tx = prev_tx; continue; } /* Chain it together. */ if (prev != NULL) prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys); prev = cur_tx; /* * If there's a BPF listener, bounce a copy of this frame * to him. */ BPF_MTAP(ifp, cur_tx->xl_mbuf); XL_INC(idx, XL_TX_LIST_CNT); sc->xl_cdata.xl_tx_cnt++; } /* * If there are no packets queued, bail. */ if (cur_tx == NULL) { XL_UNLOCK(sc); return; } /* * Place the request for the upload interrupt * in the last descriptor in the chain. This way, if * we're chaining several packets at once, we'll only * get an interupt once for the whole chain rather than * once for each packet. */ cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) | XL_TXSTAT_DL_INTR); bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap, BUS_DMASYNC_PREWRITE); /* Start transmission */ sc->xl_cdata.xl_tx_prod = idx; start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys); /* * Set a timeout in case the chip goes out to lunch. */ ifp->if_timer = 5; XL_UNLOCK(sc); return; } static void xl_init(xsc) void *xsc; { struct xl_softc *sc = xsc; struct ifnet *ifp = &sc->arpcom.ac_if; int error, i; u_int16_t rxfilt = 0; struct mii_data *mii = NULL; XL_LOCK(sc); /* * Cancel pending I/O and free all RX/TX buffers. */ xl_stop(sc); if (sc->xl_miibus == NULL) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); xl_wait(sc); } CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); DELAY(10000); if (sc->xl_miibus != NULL) mii = device_get_softc(sc->xl_miibus); /* Init our MAC address */ XL_SEL_WIN(2); for (i = 0; i < ETHER_ADDR_LEN; i++) { CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i, sc->arpcom.ac_enaddr[i]); } /* Clear the station mask. */ for (i = 0; i < 3; i++) CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0); #ifdef notdef /* Reset TX and RX. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); #endif /* Init circular RX list. */ error = xl_list_rx_init(sc); if (error) { printf("xl%d: initialization of the rx ring failed (%d)\n", sc->xl_unit, error); xl_stop(sc); XL_UNLOCK(sc); return; } /* Init TX descriptors. */ if (sc->xl_type == XL_TYPE_905B) error = xl_list_tx_init_90xB(sc); else error = xl_list_tx_init(sc); if (error) { printf("xl%d: initialization of the tx ring failed (%d)\n", sc->xl_unit, error); xl_stop(sc); XL_UNLOCK(sc); } /* * Set the TX freethresh value. * Note that this has no effect on 3c905B "cyclone" * cards but is required for 3c900/3c905 "boomerang" * cards in order to enable the download engine. */ CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8); /* Set the TX start threshold for best performance. */ sc->xl_tx_thresh = XL_MIN_FRAMELEN; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh); /* * If this is a 3c905B, also set the tx reclaim threshold. * This helps cut down on the number of tx reclaim errors * that could happen on a busy network. The chip multiplies * the register value by 16 to obtain the actual threshold * in bytes, so we divide by 16 when setting the value here. * The existing threshold value can be examined by reading * the register at offset 9 in window 5. */ if (sc->xl_type == XL_TYPE_905B) { CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4)); } /* Set RX filter bits. */ XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); /* Set the individual bit to receive frames for this host only. */ rxfilt |= XL_RXFILTER_INDIVIDUAL; /* If we want promiscuous mode, set the allframes bit. */ if (ifp->if_flags & IFF_PROMISC) { rxfilt |= XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } /* * Set capture broadcast bit to capture broadcast frames. */ if (ifp->if_flags & IFF_BROADCAST) { rxfilt |= XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } else { rxfilt &= ~XL_RXFILTER_BROADCAST; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); } /* * Program the multicast filter, if necessary. */ if (sc->xl_type == XL_TYPE_905B) xl_setmulti_hash(sc); else xl_setmulti(sc); /* * Load the address of the RX list. We have to * stall the upload engine before we can manipulate * the uplist pointer register, then unstall it when * we're finished. We also have to wait for the * stall command to complete before proceeding. * Note that we have to do this after any RX resets * have completed since the uplist register is cleared * by a reset. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL); xl_wait(sc); if (sc->xl_type == XL_TYPE_905B) { /* Set polling interval */ CSR_WRITE_1(sc, XL_DOWN_POLL, 64); /* Load the address of the TX list */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL); xl_wait(sc); CSR_WRITE_4(sc, XL_DOWNLIST_PTR, sc->xl_cdata.xl_tx_chain[0].xl_phys); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL); xl_wait(sc); } /* * If the coax transceiver is on, make sure to enable * the DC-DC converter. */ XL_SEL_WIN(3); if (sc->xl_xcvr == XL_XCVR_COAX) CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START); else CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); /* * increase packet size to allow reception of 802.1q or ISL packets. * For the 3c90x chip, set the 'allow large packets' bit in the MAC * control register. For 3c90xB/C chips, use the RX packet size * register. */ if (sc->xl_type == XL_TYPE_905B) CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE); else { u_int8_t macctl; macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL); macctl |= XL_MACCTRL_ALLOW_LARGE_PACK; CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl); } /* Clear out the stats counters. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); sc->xl_stats_no_timeout = 1; xl_stats_update(sc); sc->xl_stats_no_timeout = 0; XL_SEL_WIN(4); CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE); /* * Enable interrupts. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS); if (sc->xl_flags & XL_FLAG_FUNCREG) bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000); /* Set the RX early threshold */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2)); CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY); /* Enable receiver and transmitter. */ CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE); xl_wait(sc); if (mii != NULL) mii_mediachg(mii); /* Select window 7 for normal operations. */ XL_SEL_WIN(7); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; sc->xl_stat_ch = timeout(xl_stats_update, sc, hz); XL_UNLOCK(sc); return; } /* * Set media options. */ static int xl_ifmedia_upd(ifp) struct ifnet *ifp; { struct xl_softc *sc; struct ifmedia *ifm = NULL; struct mii_data *mii = NULL; sc = ifp->if_softc; if (sc->xl_miibus != NULL) mii = device_get_softc(sc->xl_miibus); if (mii == NULL) ifm = &sc->ifmedia; else ifm = &mii->mii_media; switch(IFM_SUBTYPE(ifm->ifm_media)) { case IFM_100_FX: case IFM_10_FL: case IFM_10_2: case IFM_10_5: xl_setmode(sc, ifm->ifm_media); return(0); break; default: break; } if (sc->xl_media & XL_MEDIAOPT_MII || sc->xl_media & XL_MEDIAOPT_BTX || sc->xl_media & XL_MEDIAOPT_BT4) { xl_init(sc); } else { xl_setmode(sc, ifm->ifm_media); } return(0); } /* * Report current media status. */ static void xl_ifmedia_sts(ifp, ifmr) struct ifnet *ifp; struct ifmediareq *ifmr; { struct xl_softc *sc; u_int32_t icfg; u_int16_t status = 0; struct mii_data *mii = NULL; sc = ifp->if_softc; if (sc->xl_miibus != NULL) mii = device_get_softc(sc->xl_miibus); XL_SEL_WIN(4); status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); XL_SEL_WIN(3); icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK; icfg >>= XL_ICFG_CONNECTOR_BITS; ifmr->ifm_active = IFM_ETHER; ifmr->ifm_status = IFM_AVALID; if ((status & XL_MEDIASTAT_CARRIER) == 0) ifmr->ifm_status |= IFM_ACTIVE; switch(icfg) { case XL_XCVR_10BT: ifmr->ifm_active = IFM_ETHER|IFM_10_T; if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; break; case XL_XCVR_AUI: if (sc->xl_type == XL_TYPE_905B && sc->xl_media == XL_MEDIAOPT_10FL) { ifmr->ifm_active = IFM_ETHER|IFM_10_FL; if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; } else ifmr->ifm_active = IFM_ETHER|IFM_10_5; break; case XL_XCVR_COAX: ifmr->ifm_active = IFM_ETHER|IFM_10_2; break; /* * XXX MII and BTX/AUTO should be separate cases. */ case XL_XCVR_100BTX: case XL_XCVR_AUTO: case XL_XCVR_MII: if (mii != NULL) { mii_pollstat(mii); ifmr->ifm_active = mii->mii_media_active; ifmr->ifm_status = mii->mii_media_status; } break; case XL_XCVR_100BFX: ifmr->ifm_active = IFM_ETHER|IFM_100_FX; break; default: printf("xl%d: unknown XCVR type: %d\n", sc->xl_unit, icfg); break; } return; } static int xl_ioctl(ifp, command, data) struct ifnet *ifp; u_long command; caddr_t data; { struct xl_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *) data; int error = 0; struct mii_data *mii = NULL; u_int8_t rxfilt; XL_LOCK(sc); switch(command) { case SIOCSIFFLAGS: XL_SEL_WIN(5); rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER); if (ifp->if_flags & IFF_UP) { if (ifp->if_flags & IFF_RUNNING && ifp->if_flags & IFF_PROMISC && !(sc->xl_if_flags & IFF_PROMISC)) { rxfilt |= XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); XL_SEL_WIN(7); } else if (ifp->if_flags & IFF_RUNNING && !(ifp->if_flags & IFF_PROMISC) && sc->xl_if_flags & IFF_PROMISC) { rxfilt &= ~XL_RXFILTER_ALLFRAMES; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt); XL_SEL_WIN(7); } else xl_init(sc); } else { if (ifp->if_flags & IFF_RUNNING) xl_stop(sc); } sc->xl_if_flags = ifp->if_flags; error = 0; break; case SIOCADDMULTI: case SIOCDELMULTI: if (sc->xl_type == XL_TYPE_905B) xl_setmulti_hash(sc); else xl_setmulti(sc); error = 0; break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: if (sc->xl_miibus != NULL) mii = device_get_softc(sc->xl_miibus); if (mii == NULL) error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); else error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); break; case SIOCSIFCAP: ifp->if_capenable = ifr->ifr_reqcap; if (ifp->if_capenable & IFCAP_TXCSUM) ifp->if_hwassist = XL905B_CSUM_FEATURES; else ifp->if_hwassist = 0; break; default: error = ether_ioctl(ifp, command, data); break; } XL_UNLOCK(sc); return(error); } static void xl_watchdog(ifp) struct ifnet *ifp; { struct xl_softc *sc; u_int16_t status = 0; sc = ifp->if_softc; XL_LOCK(sc); ifp->if_oerrors++; XL_SEL_WIN(4); status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS); printf("xl%d: watchdog timeout\n", sc->xl_unit); if (status & XL_MEDIASTAT_CARRIER) printf("xl%d: no carrier - transceiver cable problem?\n", sc->xl_unit); xl_txeoc(sc); xl_txeof(sc); xl_rxeof(sc); xl_reset(sc); xl_init(sc); if (ifp->if_snd.ifq_head != NULL) (*ifp->if_start)(ifp); XL_UNLOCK(sc); return; } /* * Stop the adapter and free any mbufs allocated to the * RX and TX lists. */ static void xl_stop(sc) struct xl_softc *sc; { register int i; struct ifnet *ifp; XL_LOCK(sc); ifp = &sc->arpcom.ac_if; ifp->if_timer = 0; CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP); DELAY(800); #ifdef foo CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET); xl_wait(sc); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET); xl_wait(sc); #endif CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0); CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0); if (sc->xl_flags & XL_FLAG_FUNCREG) bus_space_write_4 (sc->xl_ftag, sc->xl_fhandle, 4, 0x8000); /* Stop the stats updater. */ untimeout(xl_stats_update, sc, sc->xl_stat_ch); /* * Free data in the RX lists. */ for (i = 0; i < XL_RX_LIST_CNT; i++) { if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) { bus_dmamap_unload(sc->xl_mtag, sc->xl_cdata.xl_rx_chain[i].xl_map); bus_dmamap_destroy(sc->xl_mtag, sc->xl_cdata.xl_rx_chain[i].xl_map); m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf); sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL; } } bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ); /* * Free the TX list buffers. */ for (i = 0; i < XL_TX_LIST_CNT; i++) { if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) { bus_dmamap_unload(sc->xl_mtag, sc->xl_cdata.xl_tx_chain[i].xl_map); bus_dmamap_destroy(sc->xl_mtag, sc->xl_cdata.xl_tx_chain[i].xl_map); m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf); sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL; } } bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ); ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); XL_UNLOCK(sc); return; } /* * Stop all chip I/O so that the kernel's probe routines don't * get confused by errant DMAs when rebooting. */ static void xl_shutdown(dev) device_t dev; { struct xl_softc *sc; sc = device_get_softc(dev); XL_LOCK(sc); xl_reset(sc); xl_stop(sc); XL_UNLOCK(sc); return; } static int xl_suspend(dev) device_t dev; { struct xl_softc *sc; sc = device_get_softc(dev); XL_LOCK(sc); xl_stop(sc); XL_UNLOCK(sc); return(0); } static int xl_resume(dev) device_t dev; { struct xl_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); XL_LOCK(sc); ifp = &sc->arpcom.ac_if; xl_reset(sc); if (ifp->if_flags & IFF_UP) xl_init(sc); XL_UNLOCK(sc); return(0); }