1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-01 12:19:28 +00:00

- Move bge_phy_hack into the phy code and implement the various DSP

patch workarounds for each phy revision.
  Obtained from: NetBSD & Broadcom Linux driver

- Disable AUTOPOLL when accessing the PHY as it may cause PCI errors.
  Obtained from: NetBSD

- Check the UPDATED bit in the status block so the driver knows
  that the status block as indeed changed since the last access.
  Broadcom documentation states drivers should unset the UPDATED/CHANGED
  bits after reading them.

- When changing media types, first loop the phy then set the media.
  Broadcom documentation and Linux drivers do this and I observed
  much better handling of link after this change.

- Broadcom documentation states that for 1000BaseT operation,
  autonegotiation must be enabled.  Fix hard coding of media so that
  the driver only advertises 1000BaseT as the supported media type
  and enable autonegotition.

- Only set Master/Slave on the 5701.
  Obtained from Broadcom Linux driver.
This commit is contained in:
Paul Saab 2003-05-03 19:06:50 +00:00
parent c69284ca08
commit 37ceeb4d9f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=114590
2 changed files with 198 additions and 70 deletions

View File

@ -218,7 +218,6 @@ static int bge_miibus_writereg (device_t, int, int, int);
static void bge_miibus_statchg (device_t);
static void bge_reset (struct bge_softc *);
static void bge_phy_hack (struct bge_softc *);
static device_method_t bge_methods[] = {
/* Device interface */
@ -477,7 +476,7 @@ bge_miibus_readreg(dev, phy, reg)
{
struct bge_softc *sc;
struct ifnet *ifp;
u_int32_t val;
u_int32_t val, autopoll;
int i;
sc = device_get_softc(dev);
@ -491,6 +490,13 @@ bge_miibus_readreg(dev, phy, reg)
return(0);
}
/* Reading with autopolling on may trigger PCI errors */
autopoll = CSR_READ_4(sc, BGE_MI_MODE);
if (autopoll & BGE_MIMODE_AUTOPOLL) {
BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
DELAY(40);
}
CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_READ|BGE_MICOMM_BUSY|
BGE_MIPHY(phy)|BGE_MIREG(reg));
@ -502,11 +508,18 @@ bge_miibus_readreg(dev, phy, reg)
if (i == BGE_TIMEOUT) {
printf("bge%d: PHY read timed out\n", sc->bge_unit);
return(0);
val = 0;
goto done;
}
val = CSR_READ_4(sc, BGE_MI_COMM);
done:
if (autopoll & BGE_MIMODE_AUTOPOLL) {
BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
DELAY(40);
}
if (val & BGE_MICOMM_READFAIL)
return(0);
@ -519,10 +532,18 @@ bge_miibus_writereg(dev, phy, reg, val)
int phy, reg, val;
{
struct bge_softc *sc;
u_int32_t autopoll;
int i;
sc = device_get_softc(dev);
/* Reading with autopolling on may trigger PCI errors */
autopoll = CSR_READ_4(sc, BGE_MI_MODE);
if (autopoll & BGE_MIMODE_AUTOPOLL) {
BGE_CLRBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
DELAY(40);
}
CSR_WRITE_4(sc, BGE_MI_COMM, BGE_MICMD_WRITE|BGE_MICOMM_BUSY|
BGE_MIPHY(phy)|BGE_MIREG(reg)|val);
@ -531,6 +552,11 @@ bge_miibus_writereg(dev, phy, reg, val)
break;
}
if (autopoll & BGE_MIMODE_AUTOPOLL) {
BGE_SETBIT(sc, BGE_MI_MODE, BGE_MIMODE_AUTOPOLL);
DELAY(40);
}
if (i == BGE_TIMEOUT) {
printf("bge%d: PHY read timed out\n", sc->bge_unit);
return(0);
@ -562,8 +588,6 @@ bge_miibus_statchg(dev)
BGE_SETBIT(sc, BGE_MAC_MODE, BGE_MACMODE_HALF_DUPLEX);
}
bge_phy_hack(sc);
return;
}
@ -2029,14 +2053,20 @@ bge_intr(xsc)
BRGPHY_INTRS);
}
} else {
if (sc->bge_rdata->bge_status_block.bge_status &
BGE_STATFLAG_LINKSTATE_CHANGED) {
if ((sc->bge_rdata->bge_status_block.bge_status &
BGE_STATFLAG_UPDATED) &&
(sc->bge_rdata->bge_status_block.bge_status &
BGE_STATFLAG_LINKSTATE_CHANGED)) {
sc->bge_rdata->bge_status_block.bge_status &= ~(BGE_STATFLAG_UPDATED|BGE_STATFLAG_LINKSTATE_CHANGED);
sc->bge_link = 0;
untimeout(bge_tick, sc, sc->bge_stat_ch);
bge_tick(sc);
/* Clear the interrupt */
CSR_WRITE_4(sc, BGE_MAC_STS, BGE_MACSTAT_SYNC_CHANGED|
BGE_MACSTAT_CFG_CHANGED);
/* Force flush the status block cached by PCI bridge */
CSR_READ_4(sc, BGE_MBX_IRQ0_LO);
}
}
@ -2295,48 +2325,6 @@ bge_start(ifp)
return;
}
/*
* If we have a BCM5400 or BCM5401 PHY, we need to properly
* program its internal DSP. Failing to do this can result in
* massive packet loss at 1Gb speeds.
*/
static void
bge_phy_hack(sc)
struct bge_softc *sc;
{
struct bge_bcom_hack bhack[] = {
{ BRGPHY_MII_AUXCTL, 0x4C20 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x0012 },
{ BRGPHY_MII_DSP_RW_PORT, 0x1804 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x0013 },
{ BRGPHY_MII_DSP_RW_PORT, 0x1204 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x8006 },
{ BRGPHY_MII_DSP_RW_PORT, 0x0132 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x8006 },
{ BRGPHY_MII_DSP_RW_PORT, 0x0232 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x201F },
{ BRGPHY_MII_DSP_RW_PORT, 0x0A20 },
{ 0, 0 } };
u_int16_t vid, did;
int i;
vid = bge_miibus_readreg(sc->bge_dev, 1, MII_PHYIDR1);
did = bge_miibus_readreg(sc->bge_dev, 1, MII_PHYIDR2);
if (MII_OUI(vid, did) == MII_OUI_xxBROADCOM &&
(MII_MODEL(did) == MII_MODEL_xxBROADCOM_BCM5400 ||
MII_MODEL(did) == MII_MODEL_xxBROADCOM_BCM5401)) {
i = 0;
while(bhack[i].reg) {
bge_miibus_writereg(sc->bge_dev, 1, bhack[i].reg,
bhack[i].val);
i++;
}
}
return;
}
static void
bge_init(xsc)
void *xsc;
@ -2474,7 +2462,6 @@ bge_ifmedia_upd(ifp)
miisc = LIST_NEXT(miisc, mii_list))
mii_phy_reset(miisc);
}
bge_phy_hack(sc);
mii_mediachg(mii);
return(0);

View File

@ -82,6 +82,12 @@ DRIVER_MODULE(brgphy, miibus, brgphy_driver, brgphy_devclass, 0, 0);
static int brgphy_service(struct mii_softc *, struct mii_data *, int);
static void brgphy_status(struct mii_softc *);
static int brgphy_mii_phy_auto(struct mii_softc *);
static void brgphy_reset(struct mii_softc *);
static void brgphy_loop(struct mii_softc *);
static void bcm5401_load_dspcode(struct mii_softc *);
static void bcm5411_load_dspcode(struct mii_softc *);
static void bcm5703_load_dspcode(struct mii_softc *);
static int brgphy_mii_model;
static int
brgphy_probe(dev)
@ -163,7 +169,8 @@ brgphy_attach(dev)
BMCR_LOOP|BMCR_S100);
#endif
mii_phy_reset(sc);
brgphy_mii_model = MII_MODEL(ma->mii_id2);
brgphy_reset(sc);
sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask;
@ -193,7 +200,7 @@ brgphy_service(sc, mii, cmd)
int cmd;
{
struct ifmedia_entry *ife = mii->mii_media.ifm_cur;
int reg, speed;
int reg, speed, gig;
switch (cmd) {
case MII_POLLSTAT:
@ -221,11 +228,7 @@ brgphy_service(sc, mii, cmd)
if ((mii->mii_ifp->if_flags & IFF_UP) == 0)
break;
PHY_WRITE(sc, BRGPHY_MII_PHY_EXTCTL,
BRGPHY_PHY_EXTCTL_HIGH_LA|BRGPHY_PHY_EXTCTL_EN_LTR);
PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
BRGPHY_AUXCTL_LONG_PKT|BRGPHY_AUXCTL_TX_TST);
PHY_WRITE(sc, BRGPHY_MII_IMR, 0xFF00);
brgphy_reset(sc); /* XXX hardware bug work-around */
switch (IFM_SUBTYPE(ife->ifm_media)) {
case IFM_AUTO:
@ -247,17 +250,28 @@ brgphy_service(sc, mii, cmd)
case IFM_10_T:
speed = BRGPHY_S10;
setit:
brgphy_loop(sc);
if ((ife->ifm_media & IFM_GMASK) == IFM_FDX) {
PHY_WRITE(sc, BRGPHY_MII_BMCR,
BRGPHY_BMCR_FDX|speed);
speed |= BRGPHY_BMCR_FDX;
gig = BRGPHY_1000CTL_AFD;
} else {
PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
gig = BRGPHY_1000CTL_AHD;
}
PHY_WRITE(sc, BRGPHY_MII_1000CTL, 0);
PHY_WRITE(sc, BRGPHY_MII_BMCR, speed);
PHY_WRITE(sc, BRGPHY_MII_ANAR, BRGPHY_SEL_TYPE);
if (IFM_SUBTYPE(ife->ifm_media) != IFM_1000_T)
break;
PHY_WRITE(sc, BRGPHY_MII_1000CTL, gig);
PHY_WRITE(sc, BRGPHY_MII_BMCR,
speed|BRGPHY_BMCR_AUTOEN|BRGPHY_BMCR_STARTNEG);
if (brgphy_mii_model != MII_MODEL_xxBROADCOM_BCM5701)
break;
/*
* When settning the link manually, one side must
* be the master and the other the slave. However
@ -268,10 +282,10 @@ brgphy_service(sc, mii, cmd)
*/
if ((mii->mii_ifp->if_flags & IFF_LINK0)) {
PHY_WRITE(sc, BRGPHY_MII_1000CTL,
BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC);
gig|BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC);
} else {
PHY_WRITE(sc, BRGPHY_MII_1000CTL,
BRGPHY_1000CTL_MSE);
gig|BRGPHY_1000CTL_MSE);
}
break;
#ifdef foo
@ -320,7 +334,6 @@ brgphy_service(sc, mii, cmd)
return (0);
sc->mii_ticks = 0;
mii_phy_reset(sc);
brgphy_mii_phy_auto(sc);
return (0);
}
@ -402,12 +415,12 @@ brgphy_mii_phy_auto(mii)
{
int ktcr = 0;
mii_phy_reset(mii);
PHY_WRITE(mii, BRGPHY_MII_BMCR, 0);
DELAY(1000);
ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL);
PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr |
BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD);
brgphy_loop(mii);
brgphy_reset(mii);
ktcr = BRGPHY_1000CTL_AFD|BRGPHY_1000CTL_AHD;
if (brgphy_mii_model == MII_MODEL_xxBROADCOM_BCM5701)
ktcr |= BRGPHY_1000CTL_MSE|BRGPHY_1000CTL_MSC;
PHY_WRITE(mii, BRGPHY_MII_1000CTL, ktcr);
ktcr = PHY_READ(mii, BRGPHY_MII_1000CTL);
DELAY(1000);
PHY_WRITE(mii, BRGPHY_MII_ANAR,
@ -418,3 +431,131 @@ brgphy_mii_phy_auto(mii)
PHY_WRITE(mii, BRGPHY_MII_IMR, 0xFF00);
return (EJUSTRETURN);
}
static void
brgphy_loop(struct mii_softc *sc)
{
u_int32_t bmsr;
int i;
PHY_WRITE(sc, BRGPHY_MII_BMCR, BRGPHY_BMCR_LOOP);
for (i = 0; i < 15000; i++) {
bmsr = PHY_READ(sc, BRGPHY_MII_BMSR);
if (!(bmsr & BRGPHY_BMSR_LINK)) {
#if 0
device_printf(sc->mii_dev, "looped %d\n", i);
#endif
break;
}
DELAY(10);
}
}
/* Turn off tap power management on 5401. */
static void
bcm5401_load_dspcode(struct mii_softc *sc)
{
static const struct {
int reg;
uint16_t val;
} dspcode[] = {
{ BRGPHY_MII_AUXCTL, 0x0c20 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x0012 },
{ BRGPHY_MII_DSP_RW_PORT, 0x1804 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x0013 },
{ BRGPHY_MII_DSP_RW_PORT, 0x1204 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x8006 },
{ BRGPHY_MII_DSP_RW_PORT, 0x0132 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x8006 },
{ BRGPHY_MII_DSP_RW_PORT, 0x0232 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x201f },
{ BRGPHY_MII_DSP_RW_PORT, 0x0a20 },
{ 0, 0 },
};
int i;
for (i = 0; dspcode[i].reg != 0; i++)
PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
DELAY(40);
}
static void
bcm5411_load_dspcode(struct mii_softc *sc)
{
static const struct {
int reg;
uint16_t val;
} dspcode[] = {
{ 0x1c, 0x8c23 },
{ 0x1c, 0x8ca3 },
{ 0x1c, 0x8c23 },
{ 0, 0 },
};
int i;
for (i = 0; dspcode[i].reg != 0; i++)
PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
}
static void
bcm5703_load_dspcode(struct mii_softc *sc)
{
static const struct {
int reg;
uint16_t val;
} dspcode[] = {
{ BRGPHY_MII_AUXCTL, 0x0c00 },
{ BRGPHY_MII_DSP_ADDR_REG, 0x201f },
{ BRGPHY_MII_DSP_RW_PORT, 0x2aaa },
{ 0, 0 },
};
int i;
for (i = 0; dspcode[i].reg != 0; i++)
PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
}
static void
bcm5704_load_dspcode(struct mii_softc *sc)
{
static const struct {
int reg;
u_int16_t val;
} dspcode[] = {
{ 0x1c, 0x8d68 },
{ 0x1c, 0x8d68 },
{ 0, 0 },
};
int i;
for (i = 0; dspcode[i].reg != 0; i++)
PHY_WRITE(sc, dspcode[i].reg, dspcode[i].val);
}
static void
brgphy_reset(struct mii_softc *sc)
{
u_int32_t val;
mii_phy_reset(sc);
switch (brgphy_mii_model) {
case MII_MODEL_xxBROADCOM_BCM5401:
bcm5401_load_dspcode(sc);
break;
case MII_MODEL_xxBROADCOM_BCM5411:
bcm5411_load_dspcode(sc);
break;
case MII_MODEL_xxBROADCOM_BCM5703:
bcm5703_load_dspcode(sc);
break;
case MII_MODEL_xxBROADCOM_BCM5704:
bcm5704_load_dspcode(sc);
break;
}
/* Enable Ethernet@WireSpeed. */
PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
PHY_WRITE(sc, BRGPHY_MII_AUXCTL, val | (1 << 15) || (1 << 4));
}