#include <sys/cdefs.h>
#ifdef HAVE_KERNEL_OPTION_HEADERS
#include "opt_device_polling.h"
#endif
#include <sys/param.h>
#include <sys/endian.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_vlan_var.h>
#include <net/bpf.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <dev/mii/mii.h>
#include <dev/mii/mii_bitbang.h>
#include <dev/mii/miivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#define DC_USEIOSPACE
#include <dev/dc/if_dcreg.h>
MODULE_DEPEND(dc, pci, 1, 1, 1);
MODULE_DEPEND(dc, ether, 1, 1, 1);
MODULE_DEPEND(dc, miibus, 1, 1, 1);
#include "miibus_if.h"
static const struct dc_type dc_devs[] = {
{ DC_DEVID(DC_VENDORID_DEC, DC_DEVICEID_21143), 0,
"Intel 21143 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009), 0,
"Davicom DM9009 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100), 0,
"Davicom DM9100 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102), DC_REVISION_DM9102A,
"Davicom DM9102A 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9102), 0,
"Davicom DM9102 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_AL981), 0,
"ADMtek AL981 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_AN983), 0,
"ADMtek AN983 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_AN985), 0,
"ADMtek AN985 CardBus 10/100BaseTX or clone" },
{ DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9511), 0,
"ADMtek ADM9511 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9513), 0,
"ADMtek ADM9513 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ASIX, DC_DEVICEID_AX88140A), DC_REVISION_88141,
"ASIX AX88141 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ASIX, DC_DEVICEID_AX88140A), 0,
"ASIX AX88140A 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_98713), DC_REVISION_98713A,
"Macronix 98713A 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_98713), 0,
"Macronix 98713 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_CP, DC_DEVICEID_98713_CP), DC_REVISION_98713A,
"Compex RL100-TX 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_CP, DC_DEVICEID_98713_CP), 0,
"Compex RL100-TX 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_987x5), DC_REVISION_98725,
"Macronix 98725 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_987x5), DC_REVISION_98715AEC_C,
"Macronix 98715AEC-C 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_987x5), 0,
"Macronix 98715/98715A 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_98727), 0,
"Macronix 98727/98732 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_LO, DC_DEVICEID_82C115), 0,
"LC82C115 PNIC II 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_LO, DC_DEVICEID_82C168), DC_REVISION_82C169,
"82c169 PNIC 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_LO, DC_DEVICEID_82C168), 0,
"82c168 PNIC 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ACCTON, DC_DEVICEID_EN1217), 0,
"Accton EN1217 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ACCTON, DC_DEVICEID_EN2242), 0,
"Accton EN2242 MiniPCI 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_XIRCOM, DC_DEVICEID_X3201), 0,
"Xircom X3201 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_DLINK, DC_DEVICEID_DRP32TXD), 0,
"Neteasy DRP-32TXD Cardbus 10/100" },
{ DC_DEVID(DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500), 0,
"Abocom FE2500 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500MX), 0,
"Abocom FE2500MX 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_CONEXANT, DC_DEVICEID_RS7112), 0,
"Conexant LANfinity MiniPCI 10/100BaseTX" },
{ DC_DEVID(DC_VENDORID_HAWKING, DC_DEVICEID_HAWKING_PN672TX), 0,
"Hawking CB102 CardBus 10/100" },
{ DC_DEVID(DC_VENDORID_PLANEX, DC_DEVICEID_FNW3602T), 0,
"PlaneX FNW-3602-T CardBus 10/100" },
{ DC_DEVID(DC_VENDORID_3COM, DC_DEVICEID_3CSOHOB), 0,
"3Com OfficeConnect 10/100B" },
{ DC_DEVID(DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN120), 0,
"Microsoft MN-120 CardBus 10/100" },
{ DC_DEVID(DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130), 0,
"Microsoft MN-130 10/100" },
{ DC_DEVID(DC_VENDORID_LINKSYS, DC_DEVICEID_PCMPC200_AB08), 0,
"Linksys PCMPC200 CardBus 10/100" },
{ DC_DEVID(DC_VENDORID_LINKSYS, DC_DEVICEID_PCMPC200_AB09), 0,
"Linksys PCMPC200 CardBus 10/100" },
{ DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261), 0,
"ULi M5261 FastEthernet" },
{ DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5263), 0,
"ULi M5263 FastEthernet" },
{ 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);
static const struct dc_type *dc_devtype(device_t);
static void dc_discard_rxbuf(struct dc_softc *, int);
static int dc_newbuf(struct dc_softc *, 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 int 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(if_t);
static void dc_start_locked(if_t);
static int dc_ioctl(if_t, u_long, caddr_t);
static void dc_init(void *);
static void dc_init_locked(struct dc_softc *);
static void dc_stop(struct dc_softc *);
static void dc_watchdog(void *);
static int dc_shutdown(device_t);
static int dc_ifmedia_upd(if_t);
static int dc_ifmedia_upd_locked(struct dc_softc *);
static void dc_ifmedia_sts(if_t, struct ifmediareq *);
static int dc_dma_alloc(struct dc_softc *);
static void dc_dma_free(struct dc_softc *);
static void dc_dma_map_addr(void *, bus_dma_segment_t *, int, int);
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, uint16_t *);
static void dc_eeprom_getword_pnic(struct dc_softc *, int, uint16_t *);
static void dc_eeprom_getword_xircom(struct dc_softc *, int, uint16_t *);
static void dc_eeprom_width(struct dc_softc *);
static void dc_read_eeprom(struct dc_softc *, caddr_t, int, int, int);
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 void dc_netcfg_wait(struct dc_softc *);
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_uli(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 int dc_read_srom(struct dc_softc *, int);
static int dc_parse_21143_srom(struct dc_softc *);
static int dc_decode_leaf_sia(struct dc_softc *, struct dc_eblock_sia *);
static int dc_decode_leaf_mii(struct dc_softc *, struct dc_eblock_mii *);
static int dc_decode_leaf_sym(struct dc_softc *, struct dc_eblock_sym *);
static void dc_apply_fixup(struct dc_softc *, int);
static int dc_check_multiport(struct dc_softc *);
static uint32_t dc_mii_bitbang_read(device_t);
static void dc_mii_bitbang_write(device_t, uint32_t);
static const struct mii_bitbang_ops dc_mii_bitbang_ops = {
dc_mii_bitbang_read,
dc_mii_bitbang_write,
{
DC_SIO_MII_DATAOUT,
DC_SIO_MII_DATAIN,
DC_SIO_MII_CLK,
0,
DC_SIO_MII_DIR,
}
};
#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[] = {
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),
DEVMETHOD(miibus_readreg, dc_miibus_readreg),
DEVMETHOD(miibus_writereg, dc_miibus_writereg),
DEVMETHOD(miibus_statchg, dc_miibus_statchg),
DEVMETHOD(miibus_mediainit, dc_miibus_mediainit),
DEVMETHOD_END
};
static driver_t dc_driver = {
"dc",
dc_methods,
sizeof(struct dc_softc)
};
DRIVER_MODULE_ORDERED(dc, pci, dc_driver, NULL, NULL, SI_ORDER_ANY);
MODULE_PNP_INFO("W32:vendor/device;U8:revision;D:#", pci, dc, dc_devs,
nitems(dc_devs) - 1);
DRIVER_MODULE(miibus, dc, miibus_driver, NULL, NULL);
#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))
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;
dc_eeprom_idle(sc);
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);
}
dc_eeprom_idle(sc);
if (i < 4 || i > 12)
sc->dc_romwidth = 6;
else
sc->dc_romwidth = 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);
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);
}
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);
}
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);
}
}
static void
dc_eeprom_getword_pnic(struct dc_softc *sc, int addr, uint16_t *dest)
{
int i;
uint32_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 = (uint16_t)(r & 0xFFFF);
return;
}
}
}
static void
dc_eeprom_getword_xircom(struct dc_softc *sc, int addr, uint16_t *dest)
{
SIO_SET(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
addr *= 2;
CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
*dest = (uint16_t)CSR_READ_4(sc, DC_SIO) & 0xff;
addr += 1;
CSR_WRITE_4(sc, DC_ROM, addr | 0x160);
*dest |= ((uint16_t)CSR_READ_4(sc, DC_SIO) & 0xff) << 8;
SIO_CLR(DC_SIO_ROMSEL | DC_SIO_ROMCTL_READ);
}
static void
dc_eeprom_getword(struct dc_softc *sc, int addr, uint16_t *dest)
{
int i;
uint16_t word = 0;
dc_eeprom_idle(sc);
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);
dc_eeprom_putbyte(sc, addr);
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);
}
dc_eeprom_idle(sc);
*dest = word;
}
static void
dc_read_eeprom(struct dc_softc *sc, caddr_t dest, int off, int cnt, int be)
{
int i;
uint16_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 = (uint16_t *)(dest + (i * 2));
if (be)
*ptr = be16toh(word);
else
*ptr = le16toh(word);
}
}
static void
dc_mii_bitbang_write(device_t dev, uint32_t val)
{
struct dc_softc *sc;
sc = device_get_softc(dev);
CSR_WRITE_4(sc, DC_SIO, val);
CSR_BARRIER_4(sc, DC_SIO,
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
}
static uint32_t
dc_mii_bitbang_read(device_t dev)
{
struct dc_softc *sc;
uint32_t val;
sc = device_get_softc(dev);
val = CSR_READ_4(sc, DC_SIO);
CSR_BARRIER_4(sc, DC_SIO,
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
return (val);
}
static int
dc_miibus_readreg(device_t dev, int phy, int reg)
{
struct dc_softc *sc;
int i, rval, phy_reg = 0;
sc = device_get_softc(dev);
if (sc->dc_pmode != DC_PMODE_MII) {
if (phy == (MII_NPHY - 1)) {
switch (reg) {
case MII_BMSR:
return (BMSR_MEDIAMASK);
case MII_PHYIDR1:
if (DC_IS_PNIC(sc))
return (DC_VENDORID_LO);
return (DC_VENDORID_DEC);
case MII_PHYIDR2:
if (DC_IS_PNIC(sc))
return (DC_DEVICEID_82C168);
return (DC_DEVICEID_21143);
default:
return (0);
}
} 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 (sc->dc_type == DC_TYPE_ULI_M5263) {
CSR_WRITE_4(sc, DC_ROM,
((phy << DC_ULI_PHY_ADDR_SHIFT) & DC_ULI_PHY_ADDR_MASK) |
((reg << DC_ULI_PHY_REG_SHIFT) & DC_ULI_PHY_REG_MASK) |
DC_ULI_PHY_OP_READ);
for (i = 0; i < DC_TIMEOUT; i++) {
DELAY(1);
rval = CSR_READ_4(sc, DC_ROM);
if ((rval & DC_ULI_PHY_OP_DONE) != 0) {
return (rval & DC_ULI_PHY_DATA_MASK);
}
}
if (i == DC_TIMEOUT)
device_printf(dev, "phy read timed out\n");
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:
device_printf(dev, "phy_read: bad phy register %x\n",
reg);
return (0);
}
rval = CSR_READ_4(sc, phy_reg) & 0x0000FFFF;
if (rval == 0xFFFF)
return (0);
return (rval);
}
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);
}
rval = mii_bitbang_readreg(dev, &dc_mii_bitbang_ops, phy, reg);
if (sc->dc_type == DC_TYPE_98713)
CSR_WRITE_4(sc, DC_NETCFG, phy_reg);
return (rval);
}
static int
dc_miibus_writereg(device_t dev, int phy, int reg, int data)
{
struct dc_softc *sc;
int i, phy_reg = 0;
sc = device_get_softc(dev);
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 (sc->dc_type == DC_TYPE_ULI_M5263) {
CSR_WRITE_4(sc, DC_ROM,
((phy << DC_ULI_PHY_ADDR_SHIFT) & DC_ULI_PHY_ADDR_MASK) |
((reg << DC_ULI_PHY_REG_SHIFT) & DC_ULI_PHY_REG_MASK) |
((data << DC_ULI_PHY_DATA_SHIFT) & DC_ULI_PHY_DATA_MASK) |
DC_ULI_PHY_OP_WRITE);
DELAY(1);
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:
device_printf(dev, "phy_write: bad phy register %x\n",
reg);
return (0);
break;
}
CSR_WRITE_4(sc, phy_reg, data);
return (0);
}
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);
}
mii_bitbang_writereg(dev, &dc_mii_bitbang_ops, phy, reg, data);
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;
if_t ifp;
struct mii_data *mii;
struct ifmedia *ifm;
sc = device_get_softc(dev);
mii = device_get_softc(sc->dc_miibus);
ifp = sc->dc_ifp;
if (mii == NULL || ifp == NULL ||
(if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
return;
ifm = &mii->mii_media;
if (DC_IS_DAVICOM(sc) && IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1) {
dc_setcfg(sc, ifm->ifm_media);
return;
} else if (!DC_IS_ADMTEK(sc))
dc_setcfg(sc, mii->mii_media_active);
sc->dc_link = 0;
if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
(IFM_ACTIVE | IFM_AVALID)) {
switch (IFM_SUBTYPE(mii->mii_media_active)) {
case IFM_10_T:
case IFM_100_TX:
sc->dc_link = 1;
break;
}
}
}
static void
dc_miibus_mediainit(device_t dev)
{
struct dc_softc *sc;
struct mii_data *mii;
struct ifmedia *ifm;
int rev;
rev = pci_get_revid(dev);
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;
crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
if (sc->dc_flags & DC_128BIT_HASH)
return (crc & ((1 << DC_BITS_128) - 1));
if (sc->dc_flags & DC_64BIT_HASH)
return (crc & ((1 << DC_BITS_64) - 1));
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));
}
static uint32_t
dc_mchash_be(const uint8_t *addr)
{
uint32_t crc;
crc = ether_crc32_be(addr, ETHER_ADDR_LEN);
return ((crc >> 26) & 0x0000003F);
}
static u_int
dc_hash_maddr_21143(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct dc_softc *sc = arg;
uint32_t h;
h = dc_mchash_le(sc, LLADDR(sdl));
sc->dc_cdata.dc_sbuf[h >> 4] |= htole32(1 << (h & 0xF));
return (1);
}
static void
dc_setfilt_21143(struct dc_softc *sc)
{
uint16_t eaddr[(ETHER_ADDR_LEN+1)/2];
struct dc_desc *sframe;
uint32_t h, *sp;
if_t ifp;
int i;
ifp = sc->dc_ifp;
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(DC_ADDR_LO(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 (if_getflags(ifp) & IFF_PROMISC)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
if (if_getflags(ifp) & IFF_ALLMULTI)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
if_foreach_llmaddr(ifp, dc_hash_maddr_21143, sc);
if (if_getflags(ifp) & IFF_BROADCAST) {
h = dc_mchash_le(sc, if_getbroadcastaddr(ifp));
sp[h >> 4] |= htole32(1 << (h & 0xF));
}
bcopy(if_getlladdr(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
sp[39] = DC_SP_MAC(eaddr[0]);
sp[40] = DC_SP_MAC(eaddr[1]);
sp[41] = DC_SP_MAC(eaddr[2]);
sframe->dc_status = htole32(DC_TXSTAT_OWN);
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap, BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->dc_stag, sc->dc_smap, BUS_DMASYNC_PREWRITE);
CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
DELAY(10000);
sc->dc_wdog_timer = 5;
}
static u_int
dc_hash_maddr_admtek_be(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
uint32_t *hashes = arg;
int h = 0;
h = dc_mchash_be(LLADDR(sdl));
if (h < 32)
hashes[0] |= (1 << h);
else
hashes[1] |= (1 << (h - 32));
return (1);
}
struct dc_hash_maddr_admtek_le_ctx {
struct dc_softc *sc;
uint32_t hashes[2];
};
static u_int
dc_hash_maddr_admtek_le(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct dc_hash_maddr_admtek_le_ctx *ctx = arg;
int h = 0;
h = dc_mchash_le(ctx->sc, LLADDR(sdl));
if (h < 32)
ctx->hashes[0] |= (1 << h);
else
ctx->hashes[1] |= (1 << (h - 32));
return (1);
}
static void
dc_setfilt_admtek(struct dc_softc *sc)
{
uint8_t eaddr[ETHER_ADDR_LEN];
if_t ifp;
struct dc_hash_maddr_admtek_le_ctx ctx = { sc, { 0, 0 }};
ifp = sc->dc_ifp;
bcopy(if_getlladdr(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
CSR_WRITE_4(sc, DC_AL_PAR0, eaddr[3] << 24 | eaddr[2] << 16 |
eaddr[1] << 8 | eaddr[0]);
CSR_WRITE_4(sc, DC_AL_PAR1, eaddr[5] << 8 | eaddr[4]);
if (if_getflags(ifp) & IFF_PROMISC)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
if (if_getflags(ifp) & IFF_ALLMULTI)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
CSR_WRITE_4(sc, DC_AL_MAR0, 0);
CSR_WRITE_4(sc, DC_AL_MAR1, 0);
if (if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI))
return;
if (DC_IS_CENTAUR(sc))
if_foreach_llmaddr(ifp, dc_hash_maddr_admtek_le, &ctx);
else
if_foreach_llmaddr(ifp, dc_hash_maddr_admtek_be, &ctx.hashes);
CSR_WRITE_4(sc, DC_AL_MAR0, ctx.hashes[0]);
CSR_WRITE_4(sc, DC_AL_MAR1, ctx.hashes[1]);
}
static void
dc_setfilt_asix(struct dc_softc *sc)
{
uint32_t eaddr[(ETHER_ADDR_LEN+3)/4];
if_t ifp;
uint32_t hashes[2] = { 0, 0 };
ifp = sc->dc_ifp;
bcopy(if_getlladdr(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR0);
CSR_WRITE_4(sc, DC_AX_FILTDATA, eaddr[0]);
CSR_WRITE_4(sc, DC_AX_FILTIDX, DC_AX_FILTIDX_PAR1);
CSR_WRITE_4(sc, DC_AX_FILTDATA, eaddr[1]);
if (if_getflags(ifp) & IFF_PROMISC)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
if (if_getflags(ifp) & IFF_ALLMULTI)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
if (if_getflags(ifp) & IFF_BROADCAST)
DC_SETBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
else
DC_CLRBIT(sc, DC_NETCFG, DC_AX_NETCFG_RX_BROAD);
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 (if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI))
return;
if_foreach_llmaddr(ifp, dc_hash_maddr_admtek_be, hashes);
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 u_int
dc_hash_maddr_uli(void *arg, struct sockaddr_dl *sdl, u_int mcnt)
{
uint32_t **sp = arg;
uint8_t *ma;
if (mcnt == DC_ULI_FILTER_NPERF)
return (0);
ma = LLADDR(sdl);
*(*sp)++ = DC_SP_MAC(ma[1] << 8 | ma[0]);
*(*sp)++ = DC_SP_MAC(ma[3] << 8 | ma[2]);
*(*sp)++ = DC_SP_MAC(ma[5] << 8 | ma[4]);
return (1);
}
static void
dc_setfilt_uli(struct dc_softc *sc)
{
uint8_t eaddr[ETHER_ADDR_LEN];
if_t ifp;
struct dc_desc *sframe;
uint32_t filter, *sp;
int i, mcnt;
ifp = sc->dc_ifp;
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(DC_ADDR_LO(sc->dc_saddr));
sframe->dc_ctl = htole32(DC_SFRAME_LEN | DC_TXCTL_SETUP |
DC_TXCTL_TLINK | DC_FILTER_PERFECT | DC_TXCTL_FINT);
sc->dc_cdata.dc_tx_chain[i] = (struct mbuf *)sc->dc_cdata.dc_sbuf;
bcopy(if_getlladdr(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
*sp++ = DC_SP_MAC(eaddr[1] << 8 | eaddr[0]);
*sp++ = DC_SP_MAC(eaddr[3] << 8 | eaddr[2]);
*sp++ = DC_SP_MAC(eaddr[5] << 8 | eaddr[4]);
*sp++ = DC_SP_MAC(0xFFFF);
*sp++ = DC_SP_MAC(0xFFFF);
*sp++ = DC_SP_MAC(0xFFFF);
filter = CSR_READ_4(sc, DC_NETCFG);
filter &= ~(DC_NETCFG_RX_PROMISC | DC_NETCFG_RX_ALLMULTI);
mcnt = if_foreach_llmaddr(ifp, dc_hash_maddr_uli, &sp);
if (mcnt == DC_ULI_FILTER_NPERF)
filter |= DC_NETCFG_RX_ALLMULTI;
else
for (; mcnt < DC_ULI_FILTER_NPERF; mcnt++) {
*sp++ = DC_SP_MAC(0xFFFF);
*sp++ = DC_SP_MAC(0xFFFF);
*sp++ = DC_SP_MAC(0xFFFF);
}
if (filter & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON))
CSR_WRITE_4(sc, DC_NETCFG,
filter & ~(DC_NETCFG_TX_ON | DC_NETCFG_RX_ON));
if (if_getflags(ifp) & IFF_PROMISC)
filter |= DC_NETCFG_RX_PROMISC | DC_NETCFG_RX_ALLMULTI;
if (if_getflags(ifp) & IFF_ALLMULTI)
filter |= DC_NETCFG_RX_ALLMULTI;
CSR_WRITE_4(sc, DC_NETCFG,
filter & ~(DC_NETCFG_TX_ON | DC_NETCFG_RX_ON));
if (filter & (DC_NETCFG_TX_ON | DC_NETCFG_RX_ON))
CSR_WRITE_4(sc, DC_NETCFG, filter);
sframe->dc_status = htole32(DC_TXSTAT_OWN);
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap, BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->dc_stag, sc->dc_smap, BUS_DMASYNC_PREWRITE);
CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
DELAY(1000);
sc->dc_wdog_timer = 5;
}
static u_int
dc_hash_maddr_xircom(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct dc_softc *sc = arg;
uint32_t h;
h = dc_mchash_le(sc, LLADDR(sdl));
sc->dc_cdata.dc_sbuf[h >> 4] |= htole32(1 << (h & 0xF));
return (1);
}
static void
dc_setfilt_xircom(struct dc_softc *sc)
{
uint16_t eaddr[(ETHER_ADDR_LEN+1)/2];
if_t ifp;
struct dc_desc *sframe;
uint32_t h, *sp;
int i;
ifp = sc->dc_ifp;
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(DC_ADDR_LO(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 (if_getflags(ifp) & IFF_PROMISC)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_PROMISC);
if (if_getflags(ifp) & IFF_ALLMULTI)
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
else
DC_CLRBIT(sc, DC_NETCFG, DC_NETCFG_RX_ALLMULTI);
if_foreach_llmaddr(ifp, dc_hash_maddr_xircom, &sp);
if (if_getflags(ifp) & IFF_BROADCAST) {
h = dc_mchash_le(sc, if_getbroadcastaddr(ifp));
sp[h >> 4] |= htole32(1 << (h & 0xF));
}
bcopy(if_getlladdr(sc->dc_ifp), eaddr, ETHER_ADDR_LEN);
sp[0] = DC_SP_MAC(eaddr[0]);
sp[1] = DC_SP_MAC(eaddr[1]);
sp[2] = DC_SP_MAC(eaddr[2]);
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
sframe->dc_status = htole32(DC_TXSTAT_OWN);
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap, BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
bus_dmamap_sync(sc->dc_stag, sc->dc_smap, BUS_DMASYNC_PREWRITE);
CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
DELAY(1000);
sc->dc_wdog_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_ULI(sc))
dc_setfilt_uli(sc);
if (DC_IS_XIRCOM(sc))
dc_setfilt_xircom(sc);
}
static void
dc_netcfg_wait(struct dc_softc *sc)
{
uint32_t isr;
int i;
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 && bus_child_present(sc->dc_dev)) {
if (!(isr & DC_ISR_TX_IDLE) && !DC_IS_ASIX(sc))
device_printf(sc->dc_dev,
"%s: failed to force tx to idle state\n", __func__);
if (!((isr & DC_ISR_RX_STATE) == DC_RXSTATE_STOPPED ||
(isr & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT) &&
!DC_HAS_BROKEN_RXSTATE(sc))
device_printf(sc->dc_dev,
"%s: failed to force rx to idle state\n", __func__);
}
}
static void
dc_setcfg(struct dc_softc *sc, int media)
{
int restart = 0, watchdogreg;
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));
dc_netcfg_wait(sc);
}
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)) {
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);
} 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 (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) {
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);
} 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);
DELAY(20000);
}
}
}
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) || DC_IS_ULI(sc)) {
DELAY(10000);
DC_CLRBIT(sc, DC_BUSCTL, DC_BUSCTL_RESET);
i = 0;
}
if (i == DC_TIMEOUT)
device_printf(sc->dc_dev, "reset never completed!\n");
DELAY(1000);
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
CSR_WRITE_4(sc, DC_BUSCTL, 0x00000000);
CSR_WRITE_4(sc, DC_NETCFG, 0x00000000);
if (DC_IS_INTEL(sc)) {
DC_SETBIT(sc, DC_SIARESET, DC_SIA_RESET);
CSR_WRITE_4(sc, DC_10BTCTRL, 0xFFFFFFFF);
CSR_WRITE_4(sc, DC_WATCHDOG, 0);
}
}
static const struct dc_type *
dc_devtype(device_t dev)
{
const struct dc_type *t;
uint32_t devid;
uint8_t rev;
t = dc_devs;
devid = pci_get_devid(dev);
rev = pci_get_revid(dev);
while (t->dc_name != NULL) {
if (devid == t->dc_devid && rev >= t->dc_minrev)
return (t);
t++;
}
return (NULL);
}
static int
dc_probe(device_t dev)
{
const struct dc_type *t;
t = dc_devtype(dev);
if (t != NULL) {
device_set_desc(dev, t->dc_name);
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
}
static void
dc_apply_fixup(struct dc_softc *sc, int media)
{
struct dc_mediainfo *m;
uint8_t *p;
int i;
uint32_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 int
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);
if (m == NULL) {
device_printf(sc->dc_dev, "Could not allocate mediainfo\n");
return (ENOMEM);
}
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;
}
if (l->dc_sia_code & DC_SIA_CODE_EXT) {
m->dc_gp_len = 2;
m->dc_gp_ptr =
(uint8_t *)&l->dc_un.dc_sia_ext.dc_sia_gpio_ctl;
} else {
m->dc_gp_len = 2;
m->dc_gp_ptr =
(uint8_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;
return (0);
}
static int
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 (m == NULL) {
device_printf(sc->dc_dev, "Could not allocate mediainfo\n");
return (ENOMEM);
}
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 = (uint8_t *)&l->dc_sym_gpio_ctl;
m->dc_next = sc->dc_mi;
sc->dc_mi = m;
sc->dc_pmode = DC_PMODE_SYM;
return (0);
}
static int
dc_decode_leaf_mii(struct dc_softc *sc, struct dc_eblock_mii *l)
{
struct dc_mediainfo *m;
uint8_t *p;
m = malloc(sizeof(struct dc_mediainfo), M_DEVBUF, M_NOWAIT | M_ZERO);
if (m == NULL) {
device_printf(sc->dc_dev, "Could not allocate mediainfo\n");
return (ENOMEM);
}
m->dc_media = IFM_AUTO;
m->dc_gp_len = l->dc_gpr_len;
p = (uint8_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;
return (0);
}
static int
dc_read_srom(struct dc_softc *sc, int bits)
{
int size;
size = DC_ROM_SIZE(bits);
sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc->dc_srom == NULL) {
device_printf(sc->dc_dev, "Could not allocate SROM buffer\n");
return (ENOMEM);
}
dc_read_eeprom(sc, (caddr_t)sc->dc_srom, 0, (size / 2), 0);
return (0);
}
static int
dc_parse_21143_srom(struct dc_softc *sc)
{
struct dc_leaf_hdr *lhdr;
struct dc_eblock_hdr *hdr;
int error, 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;
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++;
}
ptr = (char *)lhdr;
ptr += sizeof(struct dc_leaf_hdr) - 1;
error = 0;
for (i = 0; i < lhdr->dc_mcnt; i++) {
hdr = (struct dc_eblock_hdr *)ptr;
switch (hdr->dc_type) {
case DC_EBLOCK_MII:
error = dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr);
break;
case DC_EBLOCK_SIA:
if (! have_mii)
error = dc_decode_leaf_sia(sc,
(struct dc_eblock_sia *)hdr);
break;
case DC_EBLOCK_SYM:
if (! have_mii)
error = dc_decode_leaf_sym(sc,
(struct dc_eblock_sym *)hdr);
break;
default:
break;
}
ptr += (hdr->dc_len & 0x7F);
ptr++;
}
return (error);
}
static void
dc_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
bus_addr_t *paddr;
KASSERT(nseg == 1,
("%s: wrong number of segments (%d)", __func__, nseg));
paddr = arg;
*paddr = segs->ds_addr;
}
static int
dc_dma_alloc(struct dc_softc *sc)
{
int error, i;
error = bus_dma_tag_create(bus_get_dma_tag(sc->dc_dev), 1, 0,
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
BUS_SPACE_MAXSIZE_32BIT, 0, BUS_SPACE_MAXSIZE_32BIT, 0,
NULL, NULL, &sc->dc_ptag);
if (error) {
device_printf(sc->dc_dev,
"failed to allocate parent DMA tag\n");
goto fail;
}
error = bus_dma_tag_create(sc->dc_ptag, DC_LIST_ALIGN, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, DC_RX_LIST_SZ, 1,
DC_RX_LIST_SZ, 0, NULL, NULL, &sc->dc_rx_ltag);
if (error) {
device_printf(sc->dc_dev, "failed to create RX list DMA tag\n");
goto fail;
}
error = bus_dma_tag_create(sc->dc_ptag, DC_LIST_ALIGN, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, DC_TX_LIST_SZ, 1,
DC_TX_LIST_SZ, 0, NULL, NULL, &sc->dc_tx_ltag);
if (error) {
device_printf(sc->dc_dev, "failed to create TX list DMA tag\n");
goto fail;
}
error = bus_dmamem_alloc(sc->dc_rx_ltag,
(void **)&sc->dc_ldata.dc_rx_list, BUS_DMA_NOWAIT |
BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dc_rx_lmap);
if (error) {
device_printf(sc->dc_dev,
"failed to allocate DMA'able memory for RX list\n");
goto fail;
}
error = bus_dmamap_load(sc->dc_rx_ltag, sc->dc_rx_lmap,
sc->dc_ldata.dc_rx_list, DC_RX_LIST_SZ, dc_dma_map_addr,
&sc->dc_ldata.dc_rx_list_paddr, BUS_DMA_NOWAIT);
if (error) {
device_printf(sc->dc_dev,
"failed to load DMA'able memory for RX list\n");
goto fail;
}
error = bus_dmamem_alloc(sc->dc_tx_ltag,
(void **)&sc->dc_ldata.dc_tx_list, BUS_DMA_NOWAIT |
BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->dc_tx_lmap);
if (error) {
device_printf(sc->dc_dev,
"failed to allocate DMA'able memory for TX list\n");
goto fail;
}
error = bus_dmamap_load(sc->dc_tx_ltag, sc->dc_tx_lmap,
sc->dc_ldata.dc_tx_list, DC_TX_LIST_SZ, dc_dma_map_addr,
&sc->dc_ldata.dc_tx_list_paddr, BUS_DMA_NOWAIT);
if (error) {
device_printf(sc->dc_dev,
"cannot load DMA'able memory for TX list\n");
goto fail;
}
error = bus_dma_tag_create(sc->dc_ptag, DC_LIST_ALIGN, 0,
BUS_SPACE_MAXADDR, 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) {
device_printf(sc->dc_dev,
"failed to create DMA tag for setup frame\n");
goto fail;
}
error = bus_dmamem_alloc(sc->dc_stag, (void **)&sc->dc_cdata.dc_sbuf,
BUS_DMA_NOWAIT, &sc->dc_smap);
if (error) {
device_printf(sc->dc_dev,
"failed to allocate DMA'able memory for setup frame\n");
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) {
device_printf(sc->dc_dev,
"cannot load DMA'able memory for setup frame\n");
goto fail;
}
error = bus_dma_tag_create(sc->dc_ptag, DC_RXBUF_ALIGN, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
MCLBYTES, 1, MCLBYTES, 0, NULL, NULL, &sc->dc_rx_mtag);
if (error) {
device_printf(sc->dc_dev, "failed to create RX mbuf tag\n");
goto fail;
}
error = bus_dma_tag_create(sc->dc_ptag, 1, 0,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
MCLBYTES * DC_MAXFRAGS, DC_MAXFRAGS, MCLBYTES,
0, NULL, NULL, &sc->dc_tx_mtag);
if (error) {
device_printf(sc->dc_dev, "failed to create TX mbuf tag\n");
goto fail;
}
for (i = 0; i < DC_TX_LIST_CNT; i++) {
error = bus_dmamap_create(sc->dc_tx_mtag, 0,
&sc->dc_cdata.dc_tx_map[i]);
if (error) {
device_printf(sc->dc_dev,
"failed to create TX mbuf dmamap\n");
goto fail;
}
}
for (i = 0; i < DC_RX_LIST_CNT; i++) {
error = bus_dmamap_create(sc->dc_rx_mtag, 0,
&sc->dc_cdata.dc_rx_map[i]);
if (error) {
device_printf(sc->dc_dev,
"failed to create RX mbuf dmamap\n");
goto fail;
}
}
error = bus_dmamap_create(sc->dc_rx_mtag, 0, &sc->dc_sparemap);
if (error) {
device_printf(sc->dc_dev,
"failed to create spare RX mbuf dmamap\n");
goto fail;
}
fail:
return (error);
}
static void
dc_dma_free(struct dc_softc *sc)
{
int i;
if (sc->dc_rx_mtag != NULL) {
for (i = 0; i < DC_RX_LIST_CNT; i++) {
if (sc->dc_cdata.dc_rx_map[i] != NULL)
bus_dmamap_destroy(sc->dc_rx_mtag,
sc->dc_cdata.dc_rx_map[i]);
}
if (sc->dc_sparemap != NULL)
bus_dmamap_destroy(sc->dc_rx_mtag, sc->dc_sparemap);
bus_dma_tag_destroy(sc->dc_rx_mtag);
}
if (sc->dc_rx_mtag != NULL) {
for (i = 0; i < DC_TX_LIST_CNT; i++) {
if (sc->dc_cdata.dc_tx_map[i] != NULL)
bus_dmamap_destroy(sc->dc_tx_mtag,
sc->dc_cdata.dc_tx_map[i]);
}
bus_dma_tag_destroy(sc->dc_tx_mtag);
}
if (sc->dc_rx_ltag) {
if (sc->dc_ldata.dc_rx_list_paddr != 0)
bus_dmamap_unload(sc->dc_rx_ltag, sc->dc_rx_lmap);
if (sc->dc_ldata.dc_rx_list != NULL)
bus_dmamem_free(sc->dc_rx_ltag, sc->dc_ldata.dc_rx_list,
sc->dc_rx_lmap);
bus_dma_tag_destroy(sc->dc_rx_ltag);
}
if (sc->dc_tx_ltag) {
if (sc->dc_ldata.dc_tx_list_paddr != 0)
bus_dmamap_unload(sc->dc_tx_ltag, sc->dc_tx_lmap);
if (sc->dc_ldata.dc_tx_list != NULL)
bus_dmamem_free(sc->dc_tx_ltag, sc->dc_ldata.dc_tx_list,
sc->dc_tx_lmap);
bus_dma_tag_destroy(sc->dc_tx_ltag);
}
if (sc->dc_stag) {
if (sc->dc_saddr != 0)
bus_dmamap_unload(sc->dc_stag, sc->dc_smap);
if (sc->dc_cdata.dc_sbuf != NULL)
bus_dmamem_free(sc->dc_stag, sc->dc_cdata.dc_sbuf,
sc->dc_smap);
bus_dma_tag_destroy(sc->dc_stag);
}
}
static int
dc_attach(device_t dev)
{
uint32_t eaddr[(ETHER_ADDR_LEN+3)/4];
uint32_t command;
struct dc_softc *sc;
if_t ifp;
struct dc_mediainfo *m;
uint32_t reg, revision;
uint16_t *srom;
int error, mac_offset, n, phy, rid, tmp;
uint8_t *mac;
sc = device_get_softc(dev);
sc->dc_dev = dev;
mtx_init(&sc->dc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
MTX_DEF);
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) {
device_printf(dev, "couldn't map ports/memory\n");
error = ENXIO;
goto fail;
}
sc->dc_btag = rman_get_bustag(sc->dc_res);
sc->dc_bhandle = rman_get_bushandle(sc->dc_res);
rid = 0;
sc->dc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->dc_irq == NULL) {
device_printf(dev, "couldn't map interrupt\n");
error = ENXIO;
goto fail;
}
sc->dc_info = dc_devtype(dev);
revision = pci_get_revid(dev);
error = 0;
if (sc->dc_info->dc_devid !=
DC_DEVID(DC_VENDORID_LO, DC_DEVICEID_82C168) &&
sc->dc_info->dc_devid !=
DC_DEVID(DC_VENDORID_XIRCOM, DC_DEVICEID_X3201))
dc_eeprom_width(sc);
switch (sc->dc_info->dc_devid) {
case DC_DEVID(DC_VENDORID_DEC, 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;
error = dc_read_srom(sc, sc->dc_romwidth);
if (error != 0)
goto fail;
break;
case DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9009):
case DC_DEVID(DC_VENDORID_DAVICOM, DC_DEVICEID_DM9100):
case DC_DEVID(DC_VENDORID_DAVICOM, 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;
pci_write_config(dev, PCIR_LATTIMER, 0x80, 1);
break;
case DC_DEVID(DC_VENDORID_ADMTEK, 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;
error = dc_read_srom(sc, sc->dc_romwidth);
if (error != 0)
goto fail;
break;
case DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_AN983):
case DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_AN985):
case DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9511):
case DC_DEVID(DC_VENDORID_ADMTEK, DC_DEVICEID_ADM9513):
case DC_DEVID(DC_VENDORID_DLINK, DC_DEVICEID_DRP32TXD):
case DC_DEVID(DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500):
case DC_DEVID(DC_VENDORID_ABOCOM, DC_DEVICEID_FE2500MX):
case DC_DEVID(DC_VENDORID_ACCTON, DC_DEVICEID_EN2242):
case DC_DEVID(DC_VENDORID_HAWKING, DC_DEVICEID_HAWKING_PN672TX):
case DC_DEVID(DC_VENDORID_PLANEX, DC_DEVICEID_FNW3602T):
case DC_DEVID(DC_VENDORID_3COM, DC_DEVICEID_3CSOHOB):
case DC_DEVID(DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN120):
case DC_DEVID(DC_VENDORID_MICROSOFT, DC_DEVICEID_MSMN130):
case DC_DEVID(DC_VENDORID_LINKSYS, DC_DEVICEID_PCMPC200_AB08):
case DC_DEVID(DC_VENDORID_LINKSYS, DC_DEVICEID_PCMPC200_AB09):
sc->dc_type = DC_TYPE_AN983;
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;
break;
case DC_DEVID(DC_VENDORID_MX, DC_DEVICEID_98713):
case DC_DEVID(DC_VENDORID_CP, 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_DEVID(DC_VENDORID_MX, DC_DEVICEID_987x5):
case DC_DEVID(DC_VENDORID_ACCTON, DC_DEVICEID_EN1217):
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_DEVID(DC_VENDORID_MX, 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_DEVID(DC_VENDORID_LO, 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_DEVID(DC_VENDORID_LO, 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 (sc->dc_pnic_rx_buf == NULL) {
device_printf(sc->dc_dev,
"Could not allocate PNIC RX buffer\n");
error = ENOMEM;
goto fail;
}
if (revision < DC_REVISION_82C169)
sc->dc_pmode = DC_PMODE_SYM;
break;
case DC_DEVID(DC_VENDORID_ASIX, 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_DEVID(DC_VENDORID_XIRCOM, DC_DEVICEID_X3201):
sc->dc_type = DC_TYPE_XIRCOM;
sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE |
DC_TX_ALIGN;
sc->dc_pmode = DC_PMODE_MII;
break;
case DC_DEVID(DC_VENDORID_CONEXANT, 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;
error = dc_read_srom(sc, sc->dc_romwidth);
if (error != 0)
goto fail;
break;
case DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261):
case DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5263):
if (sc->dc_info->dc_devid ==
DC_DEVID(DC_VENDORID_ULI, DC_DEVICEID_M5261))
sc->dc_type = DC_TYPE_ULI_M5261;
else
sc->dc_type = DC_TYPE_ULI_M5263;
sc->dc_flags |= DC_TX_INTR_ALWAYS | DC_TX_COALESCE |
DC_TX_ALIGN;
sc->dc_pmode = DC_PMODE_MII;
error = dc_read_srom(sc, sc->dc_romwidth);
if (error != 0)
goto fail;
break;
default:
device_printf(dev, "unknown device: %x\n",
sc->dc_info->dc_devid);
break;
}
if (DC_IS_DAVICOM(sc))
sc->dc_cachesize = 0;
else
sc->dc_cachesize = pci_get_cachelnsz(dev);
dc_reset(sc);
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);
}
if (DC_IS_INTEL(sc)) {
error = dc_parse_21143_srom(sc);
if (error != 0)
goto fail;
} 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;
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);
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_AN983:
reg = CSR_READ_4(sc, DC_AL_PAR0);
mac = (uint8_t *)&eaddr[0];
mac[0] = (reg >> 0) & 0xff;
mac[1] = (reg >> 8) & 0xff;
mac[2] = (reg >> 16) & 0xff;
mac[3] = (reg >> 24) & 0xff;
reg = CSR_READ_4(sc, DC_AL_PAR1);
mac[4] = (reg >> 0) & 0xff;
mac[5] = (reg >> 8) & 0xff;
break;
case DC_TYPE_CONEXANT:
bcopy(sc->dc_srom + DC_CONEXANT_EE_NODEADDR, &eaddr,
ETHER_ADDR_LEN);
break;
case DC_TYPE_XIRCOM:
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;
case DC_TYPE_ULI_M5261:
case DC_TYPE_ULI_M5263:
srom = (uint16_t *)sc->dc_srom;
if (srom == NULL || *srom == 0xFFFF || *srom == 0) {
device_printf(dev,
"Reading station address from ID Table.\n");
CSR_WRITE_4(sc, DC_BUSCTL, 0x10000);
CSR_WRITE_4(sc, DC_SIARESET, 0x01C0);
CSR_WRITE_4(sc, DC_10BTCTRL, 0x0000);
CSR_WRITE_4(sc, DC_10BTCTRL, 0x0010);
CSR_WRITE_4(sc, DC_10BTCTRL, 0x0000);
CSR_WRITE_4(sc, DC_SIARESET, 0x0000);
CSR_WRITE_4(sc, DC_SIARESET, 0x01B0);
mac = (uint8_t *)eaddr;
for (n = 0; n < ETHER_ADDR_LEN; n++)
mac[n] = (uint8_t)CSR_READ_4(sc, DC_10BTCTRL);
CSR_WRITE_4(sc, DC_SIARESET, 0x0000);
CSR_WRITE_4(sc, DC_BUSCTL, 0x0000);
DELAY(10);
} else
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3,
0);
break;
default:
dc_read_eeprom(sc, (caddr_t)&eaddr, DC_EE_NODEADDR, 3, 0);
break;
}
bcopy(eaddr, sc->dc_eaddr, sizeof(eaddr));
if ((sc->dc_eaddr[0] == 0 && (sc->dc_eaddr[1] & ~0xffff) == 0) ||
(sc->dc_eaddr[0] == 0xffffffff &&
(sc->dc_eaddr[1] & 0xffff) == 0xffff)) {
error = dc_check_multiport(sc);
if (error == 0) {
bcopy(sc->dc_eaddr, eaddr, sizeof(eaddr));
if (DC_IS_INTEL(sc) && sc->dc_srom != NULL) {
while (sc->dc_mi != NULL) {
m = sc->dc_mi->dc_next;
free(sc->dc_mi, M_DEVBUF);
sc->dc_mi = m;
}
error = dc_parse_21143_srom(sc);
if (error != 0)
goto fail;
}
} else if (error == ENOMEM)
goto fail;
else
error = 0;
}
if ((error = dc_dma_alloc(sc)) != 0)
goto fail;
ifp = sc->dc_ifp = if_alloc(IFT_ETHER);
if_setsoftc(ifp, sc);
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
if_setioctlfn(ifp, dc_ioctl);
if_setstartfn(ifp, dc_start);
if_setinitfn(ifp, dc_init);
if_setsendqlen(ifp, DC_TX_LIST_CNT - 1);
if_setsendqready(ifp);
tmp = 0;
if (DC_IS_INTEL(sc)) {
dc_apply_fixup(sc, IFM_AUTO);
tmp = sc->dc_pmode;
sc->dc_pmode = DC_PMODE_MII;
}
if (DC_IS_XIRCOM(sc)) {
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);
}
phy = MII_PHY_ANY;
if (DC_IS_ADMTEK(sc))
phy = DC_ADMTEK_PHYADDR;
if (DC_IS_CONEXANT(sc))
phy = DC_CONEXANT_PHYADDR;
error = mii_attach(dev, &sc->dc_miibus, ifp, dc_ifmedia_upd,
dc_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
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;
if (!(pci_get_subvendor(dev) == 0x1033 &&
pci_get_subdevice(dev) == 0x8028))
sc->dc_flags |= DC_TULIP_LEDS;
error = mii_attach(dev, &sc->dc_miibus, ifp, dc_ifmedia_upd,
dc_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY,
MII_OFFSET_ANY, 0);
}
if (error) {
device_printf(dev, "attaching PHYs failed\n");
goto fail;
}
if (DC_IS_ADMTEK(sc)) {
DC_SETBIT(sc, DC_AL_CR, DC_AL_CR_ATUR);
}
if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0);
if_setcapenable(ifp, if_getcapabilities(ifp));
#ifdef DEVICE_POLLING
if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
#endif
callout_init_mtx(&sc->dc_stat_ch, &sc->dc_mtx, 0);
callout_init_mtx(&sc->dc_wdog_ch, &sc->dc_mtx, 0);
ether_ifattach(ifp, (caddr_t)eaddr);
error = bus_setup_intr(dev, sc->dc_irq, INTR_TYPE_NET | INTR_MPSAFE,
NULL, dc_intr, sc, &sc->dc_intrhand);
if (error) {
device_printf(dev, "couldn't set up irq\n");
ether_ifdetach(ifp);
goto fail;
}
fail:
if (error)
dc_detach(dev);
return (error);
}
static int
dc_detach(device_t dev)
{
struct dc_softc *sc;
if_t ifp;
struct dc_mediainfo *m;
sc = device_get_softc(dev);
KASSERT(mtx_initialized(&sc->dc_mtx), ("dc mutex not initialized"));
ifp = sc->dc_ifp;
#ifdef DEVICE_POLLING
if (ifp != NULL && if_getcapenable(ifp) & IFCAP_POLLING)
ether_poll_deregister(ifp);
#endif
if (device_is_attached(dev)) {
DC_LOCK(sc);
dc_stop(sc);
DC_UNLOCK(sc);
callout_drain(&sc->dc_stat_ch);
callout_drain(&sc->dc_wdog_ch);
ether_ifdetach(ifp);
}
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 (ifp != NULL)
if_free(ifp);
dc_dma_free(sc);
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);
mtx_destroy(&sc->dc_mtx);
return (0);
}
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_status = 0;
ld->dc_tx_list[i].dc_ctl = 0;
ld->dc_tx_list[i].dc_data = 0;
ld->dc_tx_list[i].dc_next = htole32(DC_TXDESC(sc, nexti));
cd->dc_tx_chain[i] = NULL;
}
cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0;
cd->dc_tx_pkts = 0;
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap,
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
return (0);
}
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) != 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_rx_ltag, sc->dc_rx_lmap,
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
return (0);
}
static int
dc_newbuf(struct dc_softc *sc, int i)
{
struct mbuf *m;
bus_dmamap_t map;
bus_dma_segment_t segs[1];
int error, nseg;
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m == NULL)
return (ENOBUFS);
m->m_len = m->m_pkthdr.len = MCLBYTES;
m_adj(m, sizeof(u_int64_t));
if (sc->dc_flags & DC_PNIC_RX_BUG_WAR)
bzero(mtod(m, char *), m->m_len);
error = bus_dmamap_load_mbuf_sg(sc->dc_rx_mtag, sc->dc_sparemap,
m, segs, &nseg, 0);
if (error) {
m_freem(m);
return (error);
}
KASSERT(nseg == 1, ("%s: wrong number of segments (%d)", __func__,
nseg));
if (sc->dc_cdata.dc_rx_chain[i] != NULL)
bus_dmamap_unload(sc->dc_rx_mtag, sc->dc_cdata.dc_rx_map[i]);
map = sc->dc_cdata.dc_rx_map[i];
sc->dc_cdata.dc_rx_map[i] = sc->dc_sparemap;
sc->dc_sparemap = map;
sc->dc_cdata.dc_rx_chain[i] = m;
bus_dmamap_sync(sc->dc_rx_mtag, sc->dc_cdata.dc_rx_map[i],
BUS_DMASYNC_PREREAD);
sc->dc_ldata.dc_rx_list[i].dc_ctl = htole32(DC_RXCTL_RLINK | DC_RXLEN);
sc->dc_ldata.dc_rx_list[i].dc_data =
htole32(DC_ADDR_LO(segs[0].ds_addr));
sc->dc_ldata.dc_rx_list[i].dc_status = htole32(DC_RXSTAT_OWN);
bus_dmamap_sync(sc->dc_rx_ltag, sc->dc_rx_lmap,
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
return (0);
}
#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;
uint32_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);
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 (i == idx || rxstat & DC_RXSTAT_LASTFRAG)
break;
dc_discard_rxbuf(sc, i);
DC_INC(i, DC_RX_LIST_CNT);
}
total_len = DC_RXBYTES(rxstat);
while (*ptr == 0x00)
ptr--;
if ((uintptr_t)(ptr) & 0x3)
ptr -= 1;
ptr -= total_len;
if (ptr < sc->dc_pnic_rx_buf)
ptr = sc->dc_pnic_rx_buf;
bcopy(ptr, mtod(m, char *), total_len);
cur_rx->dc_status = htole32(rxstat | DC_RXSTAT_FIRSTFRAG);
}
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 (i == DC_RX_LIST_CNT)
return (0);
sc->dc_cdata.dc_rx_prod = pos;
return (EAGAIN);
}
static void
dc_discard_rxbuf(struct dc_softc *sc, int i)
{
struct mbuf *m;
if (sc->dc_flags & DC_PNIC_RX_BUG_WAR) {
m = sc->dc_cdata.dc_rx_chain[i];
bzero(mtod(m, char *), m->m_len);
}
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_rx_ltag, sc->dc_rx_lmap, BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
}
static int
dc_rxeof(struct dc_softc *sc)
{
struct mbuf *m;
if_t ifp;
struct dc_desc *cur_rx;
int i, total_len, rx_npkts;
uint32_t rxstat;
DC_LOCK_ASSERT(sc);
ifp = sc->dc_ifp;
rx_npkts = 0;
bus_dmamap_sync(sc->dc_rx_ltag, sc->dc_rx_lmap, BUS_DMASYNC_POSTREAD |
BUS_DMASYNC_POSTWRITE);
for (i = sc->dc_cdata.dc_rx_prod;
(if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0;
DC_INC(i, DC_RX_LIST_CNT)) {
#ifdef DEVICE_POLLING
if (if_getcapenable(ifp) & IFCAP_POLLING) {
if (sc->rxcycles <= 0)
break;
sc->rxcycles--;
}
#endif
cur_rx = &sc->dc_ldata.dc_rx_list[i];
rxstat = le32toh(cur_rx->dc_status);
if ((rxstat & DC_RXSTAT_OWN) != 0)
break;
m = sc->dc_cdata.dc_rx_chain[i];
bus_dmamap_sync(sc->dc_rx_mtag, sc->dc_cdata.dc_rx_map[i],
BUS_DMASYNC_POSTREAD);
total_len = DC_RXBYTES(rxstat);
rx_npkts++;
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)
continue;
dc_pnic_rx_bug_war(sc, i);
rxstat = le32toh(cur_rx->dc_status);
total_len = DC_RXBYTES(rxstat);
}
}
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))) {
if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
if (rxstat & DC_RXSTAT_COLLSEEN)
if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
dc_discard_rxbuf(sc, i);
if (rxstat & DC_RXSTAT_CRCERR)
continue;
else {
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
return (rx_npkts);
}
}
}
total_len -= ETHER_CRC_LEN;
#ifdef __NO_STRICT_ALIGNMENT
if (dc_newbuf(sc, i) != 0) {
dc_discard_rxbuf(sc, i);
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
continue;
}
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = m->m_len = total_len;
#else
{
struct mbuf *m0;
m0 = m_devget(mtod(m, char *), total_len,
ETHER_ALIGN, ifp, NULL);
dc_discard_rxbuf(sc, i);
if (m0 == NULL) {
if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
continue;
}
m = m0;
}
#endif
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
DC_UNLOCK(sc);
if_input(ifp, m);
DC_LOCK(sc);
}
sc->dc_cdata.dc_rx_prod = i;
return (rx_npkts);
}
static void
dc_txeof(struct dc_softc *sc)
{
struct dc_desc *cur_tx;
if_t ifp;
int idx, setup;
uint32_t ctl, txstat;
if (sc->dc_cdata.dc_tx_cnt == 0)
return;
ifp = sc->dc_ifp;
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap, BUS_DMASYNC_POSTREAD |
BUS_DMASYNC_POSTWRITE);
setup = 0;
for (idx = sc->dc_cdata.dc_tx_cons; idx != sc->dc_cdata.dc_tx_prod;
DC_INC(idx, DC_TX_LIST_CNT), sc->dc_cdata.dc_tx_cnt--) {
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 (sc->dc_cdata.dc_tx_chain[idx] == NULL)
continue;
if (ctl & DC_TXCTL_SETUP) {
cur_tx->dc_ctl = htole32(ctl & ~DC_TXCTL_SETUP);
setup++;
bus_dmamap_sync(sc->dc_stag, sc->dc_smap,
BUS_DMASYNC_POSTWRITE);
if (DC_IS_PNIC(sc)) {
if (txstat & DC_TXSTAT_ERRSUM)
dc_setfilt(sc);
}
sc->dc_cdata.dc_tx_chain[idx] = NULL;
continue;
}
if (DC_IS_XIRCOM(sc) || DC_IS_CONEXANT(sc)) {
if (
sc->dc_pmode == DC_PMODE_MII &&
((txstat & 0xFFFF) & ~(DC_TXSTAT_ERRSUM |
DC_TXSTAT_NOCARRIER)))
txstat &= ~DC_TXSTAT_ERRSUM;
} else {
if (
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) {
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
if (txstat & DC_TXSTAT_EXCESSCOLL)
if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
if (txstat & DC_TXSTAT_LATECOLL)
if_inc_counter(ifp, IFCOUNTER_COLLISIONS, 1);
if (!(txstat & DC_TXSTAT_UNDERRUN)) {
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
return;
}
} else
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
if_inc_counter(ifp, IFCOUNTER_COLLISIONS, (txstat & DC_TXSTAT_COLLCNT) >> 3);
bus_dmamap_sync(sc->dc_tx_mtag, sc->dc_cdata.dc_tx_map[idx],
BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->dc_tx_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_cons = idx;
if (sc->dc_cdata.dc_tx_cnt <= DC_TX_LIST_CNT - DC_TX_LIST_RSVD) {
if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
if (sc->dc_cdata.dc_tx_cnt == 0)
sc->dc_wdog_timer = 0;
}
if (setup > 0)
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
static void
dc_tick(void *xsc)
{
struct dc_softc *sc;
struct mii_data *mii;
if_t ifp;
uint32_t r;
sc = xsc;
DC_LOCK_ASSERT(sc);
ifp = sc->dc_ifp;
mii = device_get_softc(sc->dc_miibus);
if (sc->dc_flags & DC_TX_USE_TX_INTR)
dc_txeof(sc);
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 {
if ((DC_HAS_BROKEN_RXSTATE(sc) || (CSR_READ_4(sc,
DC_ISR) & DC_ISR_RX_STATE) == DC_RXSTATE_WAIT) &&
sc->dc_cdata.dc_tx_cnt == 0)
mii_tick(mii);
}
} else
mii_tick(mii);
if (sc->dc_link != 0 && !if_sendq_empty(ifp))
dc_start_locked(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);
}
static void
dc_tx_underrun(struct dc_softc *sc)
{
uint32_t netcfg, isr;
int i, reinit;
reinit = 0;
netcfg = CSR_READ_4(sc, DC_NETCFG);
device_printf(sc->dc_dev, "TX underrun -- ");
if ((sc->dc_flags & DC_TX_STORENFWD) == 0) {
if (sc->dc_txthresh + DC_TXTHRESH_INC > DC_TXTHRESH_MAX) {
printf("using store and forward mode\n");
netcfg |= DC_NETCFG_STORENFWD;
} else {
printf("increasing TX threshold\n");
sc->dc_txthresh += DC_TXTHRESH_INC;
netcfg &= ~DC_NETCFG_TX_THRESH;
netcfg |= sc->dc_txthresh;
}
if (DC_IS_INTEL(sc)) {
CSR_WRITE_4(sc, DC_NETCFG, 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) {
device_printf(sc->dc_dev,
"%s: failed to force tx to idle state\n",
__func__);
reinit++;
}
}
} else {
printf("resetting\n");
reinit++;
}
if (reinit == 0) {
CSR_WRITE_4(sc, DC_NETCFG, netcfg);
if (DC_IS_INTEL(sc))
CSR_WRITE_4(sc, DC_NETCFG, netcfg | DC_NETCFG_TX_ON);
} else {
if_setdrvflagbits(sc->dc_ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
}
}
#ifdef DEVICE_POLLING
static poll_handler_t dc_poll;
static int
dc_poll(if_t ifp, enum poll_cmd cmd, int count)
{
struct dc_softc *sc = if_getsoftc(ifp);
int rx_npkts = 0;
DC_LOCK(sc);
if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) {
DC_UNLOCK(sc);
return (rx_npkts);
}
sc->rxcycles = count;
rx_npkts = dc_rxeof(sc);
dc_txeof(sc);
if (!if_sendq_empty(ifp) &&
!(if_getdrvflags(ifp) & IFF_DRV_OACTIVE))
dc_start_locked(ifp);
if (cmd == POLL_AND_CHECK_STATUS) {
uint32_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 (rx_npkts);
}
CSR_WRITE_4(sc, DC_ISR, status);
if (status & (DC_ISR_RX_WATDOGTIMEO | DC_ISR_RX_NOBUF)) {
uint32_t r = CSR_READ_4(sc, DC_FRAMESDISCARDED);
if_inc_counter(ifp, IFCOUNTER_IERRORS, (r & 0xffff) + ((r >> 17) & 0x7ff));
if (dc_rx_resync(sc))
dc_rxeof(sc);
}
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) {
if_printf(ifp, "%s: bus error\n", __func__);
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
}
}
DC_UNLOCK(sc);
return (rx_npkts);
}
#endif
static void
dc_intr(void *arg)
{
struct dc_softc *sc;
if_t ifp;
uint32_t r, status;
int n;
sc = arg;
if (sc->suspended)
return;
DC_LOCK(sc);
status = CSR_READ_4(sc, DC_ISR);
if (status == 0xFFFFFFFF || (status & DC_INTRS) == 0) {
DC_UNLOCK(sc);
return;
}
ifp = sc->dc_ifp;
#ifdef DEVICE_POLLING
if (if_getcapenable(ifp) & IFCAP_POLLING) {
DC_UNLOCK(sc);
return;
}
#endif
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
for (n = 16; n > 0; n--) {
if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
break;
CSR_WRITE_4(sc, DC_ISR, status);
if (status & DC_ISR_RX_OK) {
if (dc_rxeof(sc) == 0) {
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)) {
r = CSR_READ_4(sc, DC_FRAMESDISCARDED);
if_inc_counter(ifp, IFCOUNTER_IERRORS, (r & 0xffff) + ((r >> 17) & 0x7ff));
if (dc_rxeof(sc) == 0) {
while (dc_rx_resync(sc))
dc_rxeof(sc);
}
}
if (!if_sendq_empty(ifp))
dc_start_locked(ifp);
if (status & DC_ISR_BUS_ERR) {
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
DC_UNLOCK(sc);
return;
}
status = CSR_READ_4(sc, DC_ISR);
if (status == 0xFFFFFFFF || (status & DC_INTRS) == 0)
break;
}
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
DC_UNLOCK(sc);
}
static int
dc_encap(struct dc_softc *sc, struct mbuf **m_head)
{
bus_dma_segment_t segs[DC_MAXFRAGS];
bus_dmamap_t map;
struct dc_desc *f;
struct mbuf *m;
int cur, defragged, error, first, frag, i, idx, nseg;
m = NULL;
defragged = 0;
if (sc->dc_flags & DC_TX_COALESCE &&
((*m_head)->m_next != NULL || sc->dc_flags & DC_TX_ALIGN)) {
m = m_defrag(*m_head, M_NOWAIT);
defragged = 1;
} else {
i = 0;
for (m = *m_head; m != NULL; m = m->m_next)
i++;
if (i > DC_TX_LIST_CNT / 4 ||
DC_TX_LIST_CNT - i + sc->dc_cdata.dc_tx_cnt <=
DC_TX_LIST_RSVD) {
m = m_collapse(*m_head, M_NOWAIT, DC_MAXFRAGS);
defragged = 1;
}
}
if (defragged != 0) {
if (m == NULL) {
m_freem(*m_head);
*m_head = NULL;
return (ENOBUFS);
}
*m_head = m;
}
idx = sc->dc_cdata.dc_tx_prod;
error = bus_dmamap_load_mbuf_sg(sc->dc_tx_mtag,
sc->dc_cdata.dc_tx_map[idx], *m_head, segs, &nseg, 0);
if (error == EFBIG) {
if (defragged != 0 || (m = m_collapse(*m_head, M_NOWAIT,
DC_MAXFRAGS)) == NULL) {
m_freem(*m_head);
*m_head = NULL;
return (defragged != 0 ? error : ENOBUFS);
}
*m_head = m;
error = bus_dmamap_load_mbuf_sg(sc->dc_tx_mtag,
sc->dc_cdata.dc_tx_map[idx], *m_head, segs, &nseg, 0);
if (error != 0) {
m_freem(*m_head);
*m_head = NULL;
return (error);
}
} else if (error != 0)
return (error);
KASSERT(nseg <= DC_MAXFRAGS,
("%s: wrong number of segments (%d)", __func__, nseg));
if (nseg == 0) {
m_freem(*m_head);
*m_head = NULL;
return (EIO);
}
if (sc->dc_cdata.dc_tx_cnt + nseg > DC_TX_LIST_CNT - DC_TX_LIST_RSVD) {
bus_dmamap_unload(sc->dc_tx_mtag, sc->dc_cdata.dc_tx_map[idx]);
return (ENOBUFS);
}
bus_dmamap_sync(sc->dc_tx_mtag, sc->dc_cdata.dc_tx_map[idx],
BUS_DMASYNC_PREWRITE);
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_tx_mtag,
sc->dc_cdata.dc_tx_map[first]);
m_freem(*m_head);
*m_head = NULL;
return (ENOBUFS);
}
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(DC_ADDR_LO(segs[i].ds_addr));
cur = frag;
DC_INC(frag, DC_TX_LIST_CNT);
}
sc->dc_cdata.dc_tx_prod = frag;
sc->dc_cdata.dc_tx_cnt += nseg;
sc->dc_cdata.dc_tx_chain[cur] = *m_head;
sc->dc_ldata.dc_tx_list[cur].dc_ctl |= htole32(DC_TXCTL_LASTFRAG);
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_pkts >= 8) {
sc->dc_cdata.dc_tx_pkts = 0;
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);
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
map = sc->dc_cdata.dc_tx_map[cur];
sc->dc_cdata.dc_tx_map[cur] = sc->dc_cdata.dc_tx_map[first];
sc->dc_cdata.dc_tx_map[first] = map;
return (0);
}
static void
dc_start(if_t ifp)
{
struct dc_softc *sc;
sc = if_getsoftc(ifp);
DC_LOCK(sc);
dc_start_locked(ifp);
DC_UNLOCK(sc);
}
static void
dc_start_locked(if_t ifp)
{
struct dc_softc *sc;
struct mbuf *m_head;
int queued;
sc = if_getsoftc(ifp);
DC_LOCK_ASSERT(sc);
if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING || sc->dc_link == 0)
return;
sc->dc_cdata.dc_tx_first = sc->dc_cdata.dc_tx_prod;
for (queued = 0; !if_sendq_empty(ifp); ) {
if (sc->dc_cdata.dc_tx_cnt > DC_TX_LIST_CNT - DC_TX_LIST_RSVD) {
if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
break;
}
m_head = if_dequeue(ifp);
if (m_head == NULL)
break;
if (dc_encap(sc, &m_head)) {
if (m_head == NULL)
break;
if_sendq_prepend(ifp, m_head);
if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
break;
}
queued++;
BPF_MTAP(ifp, m_head);
}
if (queued > 0) {
if (!(sc->dc_flags & DC_TX_POLL))
CSR_WRITE_4(sc, DC_TXSTART, 0xFFFFFFFF);
sc->dc_wdog_timer = 5;
}
}
static void
dc_init(void *xsc)
{
struct dc_softc *sc = xsc;
DC_LOCK(sc);
dc_init_locked(sc);
DC_UNLOCK(sc);
}
static void
dc_init_locked(struct dc_softc *sc)
{
if_t ifp = sc->dc_ifp;
struct mii_data *mii;
struct ifmedia *ifm;
DC_LOCK_ASSERT(sc);
if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
return;
mii = device_get_softc(sc->dc_miibus);
dc_stop(sc);
dc_reset(sc);
if (DC_IS_INTEL(sc)) {
ifm = &mii->mii_media;
dc_apply_fixup(sc, ifm->ifm_media);
}
if (DC_IS_ASIX(sc) || DC_IS_DAVICOM(sc) || DC_IS_ULI(sc))
CSR_WRITE_4(sc, DC_BUSCTL, 0);
else
CSR_WRITE_4(sc, DC_BUSCTL, DC_BUSCTL_MRME | DC_BUSCTL_MRLE);
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)) {
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)) {
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);
if (dc_list_rx_init(sc) == ENOBUFS) {
device_printf(sc->dc_dev,
"initialization failed: no memory for rx buffers\n");
dc_stop(sc);
return;
}
dc_list_tx_init(sc);
CSR_WRITE_4(sc, DC_RXADDR, DC_RXDESC(sc, 0));
CSR_WRITE_4(sc, DC_TXADDR, DC_TXDESC(sc, 0));
#ifdef DEVICE_POLLING
if (if_getcapenable(ifp) & IFCAP_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);
if (DC_IS_ULI(sc))
CSR_WRITE_4(sc, DC_WATCHDOG, DC_WDOG_JABBERCLK |
DC_WDOG_HOSTUNJAB);
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_TX_ON);
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);
}
dc_setfilt(sc);
DC_SETBIT(sc, DC_NETCFG, DC_NETCFG_RX_ON);
CSR_WRITE_4(sc, DC_RXSTART, 0xFFFFFFFF);
if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
dc_ifmedia_upd_locked(sc);
CSR_READ_4(sc, DC_FRAMESDISCARDED);
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);
}
sc->dc_wdog_timer = 0;
callout_reset(&sc->dc_wdog_ch, hz, dc_watchdog, sc);
}
static int
dc_ifmedia_upd(if_t ifp)
{
struct dc_softc *sc;
int error;
sc = if_getsoftc(ifp);
DC_LOCK(sc);
error = dc_ifmedia_upd_locked(sc);
DC_UNLOCK(sc);
return (error);
}
static int
dc_ifmedia_upd_locked(struct dc_softc *sc)
{
struct mii_data *mii;
struct ifmedia *ifm;
int error;
DC_LOCK_ASSERT(sc);
sc->dc_link = 0;
mii = device_get_softc(sc->dc_miibus);
error = mii_mediachg(mii);
if (error == 0) {
ifm = &mii->mii_media;
if (DC_IS_INTEL(sc))
dc_setcfg(sc, ifm->ifm_media);
else if (DC_IS_DAVICOM(sc) &&
IFM_SUBTYPE(ifm->ifm_media) == IFM_HPNA_1)
dc_setcfg(sc, ifm->ifm_media);
}
return (error);
}
static void
dc_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
{
struct dc_softc *sc;
struct mii_data *mii;
struct ifmedia *ifm;
sc = if_getsoftc(ifp);
mii = device_get_softc(sc->dc_miibus);
DC_LOCK(sc);
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;
DC_UNLOCK(sc);
return;
}
}
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
DC_UNLOCK(sc);
}
static int
dc_ioctl(if_t ifp, u_long command, caddr_t data)
{
struct dc_softc *sc = if_getsoftc(ifp);
struct ifreq *ifr = (struct ifreq *)data;
struct mii_data *mii;
int error = 0;
switch (command) {
case SIOCSIFFLAGS:
DC_LOCK(sc);
if (if_getflags(ifp) & IFF_UP) {
int need_setfilt = (if_getflags(ifp) ^ sc->dc_if_flags) &
(IFF_PROMISC | IFF_ALLMULTI);
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
if (need_setfilt)
dc_setfilt(sc);
} else {
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
}
} else {
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
dc_stop(sc);
}
sc->dc_if_flags = if_getflags(ifp);
DC_UNLOCK(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
DC_LOCK(sc);
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
dc_setfilt(sc);
DC_UNLOCK(sc);
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
mii = device_get_softc(sc->dc_miibus);
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
break;
case SIOCSIFCAP:
#ifdef DEVICE_POLLING
if (ifr->ifr_reqcap & IFCAP_POLLING &&
!(if_getcapenable(ifp) & IFCAP_POLLING)) {
error = ether_poll_register(dc_poll, ifp);
if (error)
return(error);
DC_LOCK(sc);
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
if_setcapenablebit(ifp, IFCAP_POLLING, 0);
DC_UNLOCK(sc);
return (error);
}
if (!(ifr->ifr_reqcap & IFCAP_POLLING) &&
if_getcapenable(ifp) & IFCAP_POLLING) {
error = ether_poll_deregister(ifp);
DC_LOCK(sc);
CSR_WRITE_4(sc, DC_IMR, DC_INTRS);
if_setcapenablebit(ifp, 0, IFCAP_POLLING);
DC_UNLOCK(sc);
return (error);
}
#endif
break;
default:
error = ether_ioctl(ifp, command, data);
break;
}
return (error);
}
static void
dc_watchdog(void *xsc)
{
struct dc_softc *sc = xsc;
if_t ifp;
DC_LOCK_ASSERT(sc);
if (sc->dc_wdog_timer == 0 || --sc->dc_wdog_timer != 0) {
callout_reset(&sc->dc_wdog_ch, hz, dc_watchdog, sc);
return;
}
ifp = sc->dc_ifp;
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
device_printf(sc->dc_dev, "watchdog timeout\n");
if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
dc_init_locked(sc);
if (!if_sendq_empty(ifp))
dc_start_locked(ifp);
}
static void
dc_stop(struct dc_softc *sc)
{
if_t ifp;
struct dc_list_data *ld;
struct dc_chain_data *cd;
int i;
uint32_t ctl, netcfg;
DC_LOCK_ASSERT(sc);
ifp = sc->dc_ifp;
ld = &sc->dc_ldata;
cd = &sc->dc_cdata;
callout_stop(&sc->dc_stat_ch);
callout_stop(&sc->dc_wdog_ch);
sc->dc_wdog_timer = 0;
sc->dc_link = 0;
if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
netcfg = CSR_READ_4(sc, DC_NETCFG);
if (netcfg & (DC_NETCFG_RX_ON | DC_NETCFG_TX_ON))
CSR_WRITE_4(sc, DC_NETCFG,
netcfg & ~(DC_NETCFG_RX_ON | DC_NETCFG_TX_ON));
CSR_WRITE_4(sc, DC_IMR, 0x00000000);
if (netcfg & (DC_NETCFG_RX_ON | DC_NETCFG_TX_ON))
dc_netcfg_wait(sc);
CSR_WRITE_4(sc, DC_TXADDR, 0x00000000);
CSR_WRITE_4(sc, DC_RXADDR, 0x00000000);
for (i = 0; i < DC_RX_LIST_CNT; i++) {
if (cd->dc_rx_chain[i] != NULL) {
bus_dmamap_sync(sc->dc_rx_mtag,
cd->dc_rx_map[i], BUS_DMASYNC_POSTREAD);
bus_dmamap_unload(sc->dc_rx_mtag,
cd->dc_rx_map[i]);
m_freem(cd->dc_rx_chain[i]);
cd->dc_rx_chain[i] = NULL;
}
}
bzero(ld->dc_rx_list, DC_RX_LIST_SZ);
bus_dmamap_sync(sc->dc_rx_ltag, sc->dc_rx_lmap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
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) {
bus_dmamap_sync(sc->dc_stag, sc->dc_smap,
BUS_DMASYNC_POSTWRITE);
} else {
bus_dmamap_sync(sc->dc_tx_mtag,
cd->dc_tx_map[i], BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->dc_tx_mtag,
cd->dc_tx_map[i]);
m_freem(cd->dc_tx_chain[i]);
}
cd->dc_tx_chain[i] = NULL;
}
}
bzero(ld->dc_tx_list, DC_TX_LIST_SZ);
bus_dmamap_sync(sc->dc_tx_ltag, sc->dc_tx_lmap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
static int
dc_suspend(device_t dev)
{
struct dc_softc *sc;
sc = device_get_softc(dev);
DC_LOCK(sc);
dc_stop(sc);
sc->suspended = 1;
DC_UNLOCK(sc);
return (0);
}
static int
dc_resume(device_t dev)
{
struct dc_softc *sc;
if_t ifp;
sc = device_get_softc(dev);
ifp = sc->dc_ifp;
DC_LOCK(sc);
if (if_getflags(ifp) & IFF_UP)
dc_init_locked(sc);
sc->suspended = 0;
DC_UNLOCK(sc);
return (0);
}
static int
dc_shutdown(device_t dev)
{
struct dc_softc *sc;
sc = device_get_softc(dev);
DC_LOCK(sc);
dc_stop(sc);
DC_UNLOCK(sc);
return (0);
}
static int
dc_check_multiport(struct dc_softc *sc)
{
struct dc_softc *dsc;
devclass_t dc;
device_t child;
uint8_t *eaddr;
int unit;
dc = devclass_find("dc");
for (unit = 0; unit < devclass_get_maxunit(dc); unit++) {
child = devclass_get_device(dc, unit);
if (child == NULL)
continue;
if (child == sc->dc_dev)
continue;
if (device_get_parent(child) != device_get_parent(sc->dc_dev))
continue;
if (unit > device_get_unit(sc->dc_dev))
continue;
if (device_is_attached(child) == 0)
continue;
dsc = device_get_softc(child);
device_printf(sc->dc_dev,
"Using station address of %s as base\n",
device_get_nameunit(child));
bcopy(dsc->dc_eaddr, sc->dc_eaddr, ETHER_ADDR_LEN);
eaddr = (uint8_t *)sc->dc_eaddr;
eaddr[5]++;
if (DC_IS_INTEL(sc) && dsc->dc_srom != NULL &&
sc->dc_romwidth != 0) {
free(sc->dc_srom, M_DEVBUF);
sc->dc_romwidth = dsc->dc_romwidth;
sc->dc_srom = malloc(DC_ROM_SIZE(sc->dc_romwidth),
M_DEVBUF, M_NOWAIT);
if (sc->dc_srom == NULL) {
device_printf(sc->dc_dev,
"Could not allocate SROM buffer\n");
return (ENOMEM);
}
bcopy(dsc->dc_srom, sc->dc_srom,
DC_ROM_SIZE(sc->dc_romwidth));
}
return (0);
}
return (ENOENT);
}