Add support for SX cards using TBI such as Netgear GA621.

Sponsored by:	Vernier Networks.
MFC after:	1 week
This commit is contained in:
ambrisko 2002-08-08 18:33:28 +00:00
parent 59f57c52dc
commit 761cf38833
2 changed files with 249 additions and 63 deletions

View File

@ -620,30 +620,56 @@ static void
nge_miibus_statchg(dev)
device_t dev;
{
int status;
struct nge_softc *sc;
struct mii_data *mii;
sc = device_get_softc(dev);
mii = device_get_softc(sc->nge_miibus);
if (sc->nge_tbi) {
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media)
== IFM_AUTO) {
status = CSR_READ_4(sc, NGE_TBI_ANLPAR);
if (status == 0 || status & NGE_TBIANAR_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else if ((sc->nge_ifmedia.ifm_cur->ifm_media & IFM_GMASK)
!= IFM_FDX) {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
mii = device_get_softc(sc->nge_miibus);
/* If we have a 1000Mbps link, set the mode_1000 bit. */
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) {
NGE_SETBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
} else {
NGE_CLRBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
}
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
/* If we have a 1000Mbps link, set the mode_1000 bit. */
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX) {
NGE_SETBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
} else {
NGE_CLRBIT(sc, NGE_CFG, NGE_CFG_MODE_1000);
}
}
return;
}
@ -802,6 +828,7 @@ nge_attach(dev)
struct nge_softc *sc;
struct ifnet *ifp;
int unit, error = 0, rid;
const char *sep = "";
s = splimp();
@ -957,14 +984,48 @@ nge_attach(dev)
* Do MII setup.
*/
if (mii_phy_probe(dev, &sc->nge_miibus,
nge_ifmedia_upd, nge_ifmedia_sts)) {
printf("nge%d: MII without any PHY!\n", sc->nge_unit);
nge_free_jumbo_mem(sc);
bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq);
bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res);
error = ENXIO;
goto fail;
nge_ifmedia_upd, nge_ifmedia_sts)) {
if (CSR_READ_4(sc, NGE_CFG) & NGE_CFG_TBI_EN) {
sc->nge_tbi = 1;
device_printf(dev, "Using TBI\n");
sc->nge_miibus = dev;
ifmedia_init(&sc->nge_ifmedia, 0, nge_ifmedia_upd,
nge_ifmedia_sts);
#define ADD(m, c) ifmedia_add(&sc->nge_ifmedia, (m), (c), NULL)
#define PRINT(s) printf("%s%s", sep, s); sep = ", "
ADD(IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, 0), 0);
device_printf(dev, " ");
ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, 0, 0), 0);
PRINT("1000baseSX");
ADD(IFM_MAKEWORD(IFM_ETHER, IFM_1000_SX, IFM_FDX, 0),0);
PRINT("1000baseSX-FDX");
ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0), 0);
PRINT("auto");
printf("\n");
#undef ADD
#undef PRINT
ifmedia_set(&sc->nge_ifmedia,
IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, 0));
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
| NGE_GPIO_GP4_OUT
| NGE_GPIO_GP1_OUTENB | NGE_GPIO_GP2_OUTENB
| NGE_GPIO_GP3_OUTENB
| NGE_GPIO_GP3_IN | NGE_GPIO_GP4_IN);
} else {
printf("nge%d: MII without any PHY!\n", sc->nge_unit);
nge_free_jumbo_mem(sc);
bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq);
bus_release_resource(dev, NGE_RES, NGE_RID,
sc->nge_res);
error = ENXIO;
goto fail;
}
}
/*
@ -974,6 +1035,7 @@ nge_attach(dev)
callout_handle_init(&sc->nge_stat_ch);
fail:
splx(s);
mtx_destroy(&sc->nge_mtx);
return(error);
@ -997,8 +1059,9 @@ nge_detach(dev)
ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
bus_generic_detach(dev);
device_delete_child(dev, sc->nge_miibus);
if (!sc->nge_tbi) {
device_delete_child(dev, sc->nge_miibus);
}
bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq);
bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res);
@ -1284,7 +1347,6 @@ nge_rxeof(sc)
cur_rx->nge_mbuf = NULL;
total_len = NGE_RXBYTES(cur_rx);
NGE_INC(i, NGE_RX_LIST_CNT);
/*
* If an error occurs, update stats, clear the
* status word and leave the mbuf cluster in place:
@ -1297,7 +1359,6 @@ nge_rxeof(sc)
continue;
}
/*
* Ok. NatSemi really screwed up here. This is the
* only gigE chip I know of with alignment constraints
@ -1448,18 +1509,33 @@ nge_tick(xsc)
sc = xsc;
ifp = &sc->arpcom.ac_if;
mii = device_get_softc(sc->nge_miibus);
mii_tick(mii);
if (!sc->nge_link) {
if (mii->mii_media_status & IFM_ACTIVE &&
IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
sc->nge_link++;
if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T)
if (sc->nge_tbi) {
if (!sc->nge_link) {
if (CSR_READ_4(sc, NGE_TBI_BMSR)
& NGE_TBIBMSR_ANEG_DONE) {
printf("nge%d: gigabit link up\n",
sc->nge_unit);
if (ifp->if_snd.ifq_head != NULL)
nge_start(ifp);
nge_miibus_statchg(sc->nge_miibus);
sc->nge_link++;
if (ifp->if_snd.ifq_head != NULL)
nge_start(ifp);
}
}
} else {
mii = device_get_softc(sc->nge_miibus);
mii_tick(mii);
if (!sc->nge_link) {
if (mii->mii_media_status & IFM_ACTIVE &&
IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
sc->nge_link++;
if (IFM_SUBTYPE(mii->mii_media_active)
== IFM_1000_T)
printf("nge%d: gigabit link up\n",
sc->nge_unit);
if (ifp->if_snd.ifq_head != NULL)
nge_start(ifp);
}
}
}
sc->nge_stat_ch = timeout(nge_tick, sc, hz);
@ -1489,6 +1565,11 @@ nge_intr(arg)
/* Disable interrupts. */
CSR_WRITE_4(sc, NGE_IER, 0);
/* Data LED on for TBI mode */
if(sc->nge_tbi)
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
| NGE_GPIO_GP3_OUT);
for (;;) {
/* Reading the ISR register clears all interrupts. */
status = CSR_READ_4(sc, NGE_ISR);
@ -1538,6 +1619,12 @@ nge_intr(arg)
if (ifp->if_snd.ifq_head != NULL)
nge_start(ifp);
/* Data LED off for TBI mode */
if(sc->nge_tbi)
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
& ~NGE_GPIO_GP3_OUT);
return;
}
@ -1691,7 +1778,11 @@ nge_init(xsc)
*/
nge_stop(sc);
mii = device_get_softc(sc->nge_miibus);
if (sc->nge_tbi) {
mii = NULL;
} else {
mii = device_get_softc(sc->nge_miibus);
}
/* Set MAC address */
CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR0);
@ -1790,14 +1881,27 @@ nge_init(xsc)
NGE_SETBIT(sc, NGE_VLAN_IP_TXCTL, NGE_VIPTXCTL_TAG_PER_PKT);
/* Set full/half duplex mode. */
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
if (sc->nge_tbi) {
if ((sc->nge_ifmedia.ifm_cur->ifm_media & IFM_GMASK)
== IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
}
}
nge_tick(sc);
@ -1851,15 +1955,46 @@ nge_ifmedia_upd(ifp)
sc = ifp->if_softc;
mii = device_get_softc(sc->nge_miibus);
sc->nge_link = 0;
if (mii->mii_instance) {
struct mii_softc *miisc;
for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
miisc = LIST_NEXT(miisc, mii_list))
mii_phy_reset(miisc);
if (sc->nge_tbi) {
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media)
== IFM_AUTO) {
CSR_WRITE_4(sc, NGE_TBI_ANAR,
CSR_READ_4(sc, NGE_TBI_ANAR)
| NGE_TBIANAR_HDX | NGE_TBIANAR_FDX
| NGE_TBIANAR_PS1 | NGE_TBIANAR_PS2);
CSR_WRITE_4(sc, NGE_TBI_BMCR, NGE_TBIBMCR_ENABLE_ANEG
| NGE_TBIBMCR_RESTART_ANEG);
CSR_WRITE_4(sc, NGE_TBI_BMCR, NGE_TBIBMCR_ENABLE_ANEG);
} else if ((sc->nge_ifmedia.ifm_cur->ifm_media
& IFM_GMASK) == IFM_FDX) {
NGE_SETBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
CSR_WRITE_4(sc, NGE_TBI_ANAR, 0);
CSR_WRITE_4(sc, NGE_TBI_BMCR, 0);
} else {
NGE_CLRBIT(sc, NGE_TX_CFG,
(NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR));
NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX);
CSR_WRITE_4(sc, NGE_TBI_ANAR, 0);
CSR_WRITE_4(sc, NGE_TBI_BMCR, 0);
}
CSR_WRITE_4(sc, NGE_GPIO, CSR_READ_4(sc, NGE_GPIO)
& ~NGE_GPIO_GP3_OUT);
} else {
mii = device_get_softc(sc->nge_miibus);
sc->nge_link = 0;
if (mii->mii_instance) {
struct mii_softc *miisc;
for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
miisc = LIST_NEXT(miisc, mii_list))
mii_phy_reset(miisc);
}
mii_mediachg(mii);
}
mii_mediachg(mii);
return(0);
}
@ -1877,10 +2012,43 @@ nge_ifmedia_sts(ifp, ifmr)
sc = ifp->if_softc;
mii = device_get_softc(sc->nge_miibus);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
if (sc->nge_tbi) {
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
if (CSR_READ_4(sc, NGE_TBI_BMSR) & NGE_TBIBMSR_ANEG_DONE) {
ifmr->ifm_status |= IFM_ACTIVE;
}
if (CSR_READ_4(sc, NGE_TBI_BMCR) & NGE_TBIBMCR_LOOPBACK)
ifmr->ifm_active |= IFM_LOOP;
if (!CSR_READ_4(sc, NGE_TBI_BMSR) & NGE_TBIBMSR_ANEG_DONE) {
ifmr->ifm_active |= IFM_NONE;
ifmr->ifm_status = 0;
return;
}
ifmr->ifm_active |= IFM_1000_SX;
if (IFM_SUBTYPE(sc->nge_ifmedia.ifm_cur->ifm_media)
== IFM_AUTO) {
ifmr->ifm_active |= IFM_AUTO;
if (CSR_READ_4(sc, NGE_TBI_ANLPAR)
& NGE_TBIANAR_FDX) {
ifmr->ifm_active |= IFM_FDX;
}else if (CSR_READ_4(sc, NGE_TBI_ANLPAR)
& NGE_TBIANAR_HDX) {
ifmr->ifm_active |= IFM_HDX;
}
} else if ((sc->nge_ifmedia.ifm_cur->ifm_media & IFM_GMASK)
== IFM_FDX)
ifmr->ifm_active |= IFM_FDX;
else
ifmr->ifm_active |= IFM_HDX;
} else {
mii = device_get_softc(sc->nge_miibus);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
}
return;
}
@ -1953,8 +2121,14 @@ nge_ioctl(ifp, command, data)
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
mii = device_get_softc(sc->nge_miibus);
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
if (sc->nge_tbi) {
error = ifmedia_ioctl(ifp, ifr, &sc->nge_ifmedia,
command);
} else {
mii = device_get_softc(sc->nge_miibus);
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
command);
}
break;
default:
error = EINVAL;
@ -2002,7 +2176,11 @@ nge_stop(sc)
ifp = &sc->arpcom.ac_if;
ifp->if_timer = 0;
mii = device_get_softc(sc->nge_miibus);
if (sc->nge_tbi) {
mii = NULL;
} else {
mii = device_get_softc(sc->nge_miibus);
}
untimeout(nge_tick, sc, sc->nge_stat_ch);
CSR_WRITE_4(sc, NGE_IER, 0);
@ -2012,7 +2190,8 @@ nge_stop(sc)
CSR_WRITE_4(sc, NGE_TX_LISTPTR, 0);
CSR_WRITE_4(sc, NGE_RX_LISTPTR, 0);
mii_down(mii);
if (!sc->nge_tbi)
mii_down(mii);
sc->nge_link = 0;

View File

@ -128,6 +128,7 @@
#define NGE_CFG_PHYINTR_LNK 0x00080000
#define NGE_CFG_PHYINTR_DUP 0x00100000
#define NGE_CFG_MODE_1000 0x00400000
#define NGE_CFG_TBI_EN 0x01000000
#define NGE_CFG_DUPLEX_STS 0x10000000
#define NGE_CFG_SPEED_STS 0x60000000
#define NGE_CFG_LINK_STS 0x80000000
@ -421,7 +422,7 @@
/* TBI BMCR */
#define NGE_TBIBMCR_RESTART_ANEG 0x00000200
#define NGE_TBIBMCR_ENABLE_ANEG 0x00001000
#define NGE_TBIBMCR_LOOPBACK 0x00004000
#define NGE_TBIBMCR_LOOPBACK 0x00004000
/* TBI BMSR */
#define NGE_TBIBMSR_ANEG_DONE 0x00000004
@ -430,6 +431,8 @@
/* TBI ANAR */
#define NGE_TBIANAR_HDX 0x00000020
#define NGE_TBIANAR_FDX 0x00000040
#define NGE_TBIANAR_PS1 0x00000080
#define NGE_TBIANAR_PS2 0x00000100
#define NGE_TBIANAR_PCAP 0x00000180
#define NGE_TBIANAR_REMFAULT 0x00003000
#define NGE_TBIANAR_NEXTPAGE 0x00008000
@ -437,6 +440,8 @@
/* TBI ANLPAR */
#define NGE_TBIANLPAR_HDX 0x00000020
#define NGE_TBIANLPAR_FDX 0x00000040
#define NGE_TBIANAR_PS1 0x00000080
#define NGE_TBIANAR_PS2 0x00000100
#define NGE_TBIANLPAR_PCAP 0x00000180
#define NGE_TBIANLPAR_REMFAULT 0x00003000
#define NGE_TBIANLPAR_NEXTPAGE 0x00008000
@ -652,6 +657,8 @@ struct nge_softc {
SLIST_HEAD(__nge_jfreehead, nge_jpool_entry) nge_jfree_listhead;
SLIST_HEAD(__nge_jinusehead, nge_jpool_entry) nge_jinuse_listhead;
struct mtx nge_mtx;
u_int8_t nge_tbi;
struct ifmedia nge_ifmedia;
};
/*