summaryrefslogtreecommitdiffstats
path: root/sys/dev/dc/if_dc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/dc/if_dc.c')
-rw-r--r--sys/dev/dc/if_dc.c212
1 files changed, 152 insertions, 60 deletions
diff --git a/sys/dev/dc/if_dc.c b/sys/dev/dc/if_dc.c
index 8569081..c65eba0 100644
--- a/sys/dev/dc/if_dc.c
+++ b/sys/dev/dc/if_dc.c
@@ -287,11 +287,11 @@ 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 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 *);
@@ -944,23 +944,45 @@ static void
dc_miibus_statchg(device_t dev)
{
struct dc_softc *sc;
+ struct ifnet *ifp;
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);
+ ifp = sc->dc_ifp;
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & 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);
sc->dc_if_media = ifm->ifm_media;
- } else {
- dc_setcfg(sc, mii->mii_media_active);
- sc->dc_if_media = mii->mii_media_active;
+ return;
}
+
+ 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;
+ default:
+ break;
+ }
+ }
+ if (sc->dc_link == 0)
+ return;
+
+ sc->dc_if_media = mii->mii_media_active;
+ if (DC_IS_ADMTEK(sc))
+ return;
+ dc_setcfg(sc, mii->mii_media_active);
}
/*
@@ -1404,8 +1426,6 @@ dc_setcfg(struct dc_softc *sc, int media)
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);
@@ -1415,10 +1435,6 @@ dc_setcfg(struct dc_softc *sc, int media)
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);
}
}
@@ -1442,8 +1458,6 @@ dc_setcfg(struct dc_softc *sc, int media)
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);
@@ -1463,9 +1477,6 @@ dc_setcfg(struct dc_softc *sc, int media)
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);
}
}
@@ -1537,7 +1548,7 @@ dc_reset(struct dc_softc *sc)
*/
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_10BTCTRL, 0xFFFFFFFF);
CSR_WRITE_4(sc, DC_WATCHDOG, 0);
}
}
@@ -1616,12 +1627,16 @@ dc_apply_fixup(struct dc_softc *sc, int media)
}
}
-static void
+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;
@@ -1658,14 +1673,19 @@ dc_decode_leaf_sia(struct dc_softc *sc, struct dc_eblock_sia *l)
sc->dc_mi = m;
sc->dc_pmode = DC_PMODE_SIA;
+ return (0);
}
-static void
+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;
@@ -1679,15 +1699,20 @@ dc_decode_leaf_sym(struct dc_softc *sc, struct dc_eblock_sym *l)
sc->dc_mi = m;
sc->dc_pmode = DC_PMODE_SYM;
+ return (0);
}
-static void
+static int
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);
+ if (m == NULL) {
+ device_printf(sc->dc_dev, "Could not allocate mediainfo\n");
+ return (ENOMEM);
+ }
/* We abuse IFM_AUTO to represent MII. */
m->dc_media = IFM_AUTO;
m->dc_gp_len = l->dc_gpr_len;
@@ -1702,24 +1727,30 @@ dc_decode_leaf_mii(struct dc_softc *sc, struct dc_eblock_mii *l)
m->dc_next = sc->dc_mi;
sc->dc_mi = m;
+ return (0);
}
-static void
+static int
dc_read_srom(struct dc_softc *sc, int bits)
{
int size;
- size = 2 << bits;
+ size = DC_ROM_SIZE(bits);
sc->dc_srom = malloc(size, M_DEVBUF, M_NOWAIT);
+ 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 void
+static int
dc_parse_21143_srom(struct dc_softc *sc)
{
struct dc_leaf_hdr *lhdr;
struct dc_eblock_hdr *hdr;
- int have_mii, i, loff;
+ int error, have_mii, i, loff;
char *ptr;
have_mii = 0;
@@ -1746,20 +1777,21 @@ dc_parse_21143_srom(struct dc_softc *sc)
*/
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:
- dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr);
+ error = dc_decode_leaf_mii(sc, (struct dc_eblock_mii *)hdr);
break;
case DC_EBLOCK_SIA:
if (! have_mii)
- dc_decode_leaf_sia(sc,
+ error = dc_decode_leaf_sia(sc,
(struct dc_eblock_sia *)hdr);
break;
case DC_EBLOCK_SYM:
if (! have_mii)
- dc_decode_leaf_sym(sc,
+ error = dc_decode_leaf_sym(sc,
(struct dc_eblock_sym *)hdr);
break;
default:
@@ -1769,6 +1801,7 @@ dc_parse_21143_srom(struct dc_softc *sc)
ptr += (hdr->dc_len & 0x7F);
ptr++;
}
+ return (error);
}
static void
@@ -1793,6 +1826,7 @@ dc_attach(device_t dev)
u_int32_t command;
struct dc_softc *sc;
struct ifnet *ifp;
+ struct dc_mediainfo *m;
u_int32_t reg, revision;
int error, i, mac_offset, phy, rid, tmp;
u_int8_t *mac;
@@ -1835,6 +1869,7 @@ dc_attach(device_t dev)
sc->dc_info = dc_devtype(dev);
revision = pci_get_revid(dev);
+ error = 0;
/* Get the eeprom width, but PNIC and XIRCOM have diff eeprom */
if (sc->dc_info->dc_devid !=
DC_DEVID(DC_VENDORID_LO, DC_DEVICEID_82C168) &&
@@ -1848,7 +1883,9 @@ dc_attach(device_t dev)
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);
+ 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):
@@ -1867,7 +1904,9 @@ dc_attach(device_t dev)
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);
+ 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):
@@ -1934,6 +1973,12 @@ dc_attach(device_t dev)
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;
@@ -1959,7 +2004,9 @@ dc_attach(device_t dev)
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);
+ error = dc_read_srom(sc, sc->dc_romwidth);
+ if (error != 0)
+ goto fail;
break;
default:
device_printf(dev, "unknown device: %x\n",
@@ -1990,9 +2037,11 @@ dc_attach(device_t dev)
* 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 (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
@@ -2071,8 +2120,24 @@ dc_attach(device_t dev)
if ((sc->dc_eaddr[0] == 0 && (sc->dc_eaddr[1] & ~0xffff) == 0) ||
(sc->dc_eaddr[0] == 0xffffffff &&
(sc->dc_eaddr[1] & 0xffff) == 0xffff)) {
- if (dc_check_multiport(sc) == 0)
+ error = dc_check_multiport(sc);
+ if (error == 0) {
bcopy(sc->dc_eaddr, eaddr, sizeof(eaddr));
+ /* Extract media information. */
+ 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;
}
/* Allocate a busdma tag and DMA safe memory for TX/RX descriptors. */
@@ -2408,6 +2473,7 @@ dc_list_tx_init(struct dc_softc *sc)
}
cd->dc_tx_prod = cd->dc_tx_cons = cd->dc_tx_cnt = 0;
+ cd->dc_tx_pkts = 0;
bus_dmamap_sync(sc->dc_ltag, sc->dc_lmap,
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
return (0);
@@ -2776,6 +2842,9 @@ dc_txeof(struct dc_softc *sc)
int idx;
u_int32_t ctl, txstat;
+ if (sc->dc_cdata.dc_tx_cnt == 0)
+ return;
+
ifp = sc->dc_ifp;
/*
@@ -2887,6 +2956,13 @@ dc_tick(void *xsc)
ifp = sc->dc_ifp;
mii = device_get_softc(sc->dc_miibus);
+ /*
+ * Reclaim transmitted frames for controllers that do
+ * not generate TX completion interrupt for every frame.
+ */
+ 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);
@@ -2909,11 +2985,8 @@ dc_tick(void *xsc)
*/
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) {
+ 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);
@@ -2937,12 +3010,8 @@ dc_tick(void *xsc)
* 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 (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
- dc_start_locked(ifp);
- }
+ if (sc->dc_link != 0 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
+ 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);
@@ -3264,8 +3333,11 @@ dc_encap(struct dc_softc *sc, struct mbuf **m_head)
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)
+ 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_mtag, sc->dc_cdata.dc_tx_map[idx],
@@ -3333,11 +3405,6 @@ dc_start_locked(struct ifnet *ifp)
* to him.
*/
BPF_MTAP(ifp, m_head);
-
- if (sc->dc_flags & DC_TX_ONE) {
- ifp->if_drv_flags |= IFF_DRV_OACTIVE;
- break;
- }
}
if (queued > 0) {
@@ -3367,6 +3434,7 @@ dc_init_locked(struct dc_softc *sc)
{
struct ifnet *ifp = sc->dc_ifp;
struct mii_data *mii;
+ struct ifmedia *ifm;
DC_LOCK_ASSERT(sc);
@@ -3377,6 +3445,10 @@ dc_init_locked(struct dc_softc *sc)
*/
dc_stop(sc);
dc_reset(sc);
+ if (DC_IS_INTEL(sc)) {
+ ifm = &mii->mii_media;
+ dc_apply_fixup(sc, ifm->ifm_media);
+ }
/*
* Set cache alignment and burst length.
@@ -3520,12 +3592,12 @@ dc_init_locked(struct dc_softc *sc)
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_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ mii_mediachg(mii);
+ dc_setcfg(sc, sc->dc_if_media);
+
/* 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;
@@ -3556,7 +3628,9 @@ dc_ifmedia_upd(struct ifnet *ifp)
mii_mediachg(mii);
ifm = &mii->mii_media;
- if (DC_IS_DAVICOM(sc) &&
+ 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);
else
@@ -3838,12 +3912,30 @@ dc_check_multiport(struct dc_softc *sc)
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",
+ 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]++;
+ /* Prepare SROM to parse again. */
+ 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);
OpenPOWER on IntegriCloud