Index: sys/dev/re/if_re.c =================================================================== --- sys/dev/re/if_re.c (revision 184104) +++ sys/dev/re/if_re.c (working copy) @@ -260,6 +260,10 @@ static void re_read_eeprom (struct rl_softc *, caddr_t, int, int); static int re_gmii_readreg (device_t, int, int); static int re_gmii_writereg (device_t, int, int, int); +static uint32_t re_ephy_readreg (struct rl_softc *, int); +static void re_ephy_writereg (struct rl_softc *, int, uint32_t); +static uint32_t re_csi_read (struct rl_softc *, int); +static void re_csi_write (struct rl_softc *, int, uint32_t); static int re_miibus_readreg (device_t, int, int); static int re_miibus_writereg (device_t, int, int, int); @@ -269,6 +273,7 @@ static void re_reset (struct rl_softc *); static void re_setwol (struct rl_softc *); static void re_clrwol (struct rl_softc *); +static void re_ephy_config (struct rl_softc *); #ifdef RE_DIAG static int re_diag (struct rl_softc *); @@ -461,6 +466,96 @@ return (0); } +static uint32_t +re_ephy_readreg(struct rl_softc *sc, int reg) +{ + uint32_t val; + int i; + + CSR_WRITE_4(sc, RL_EPHYAR, RL_EPHYAR_READ | + ((reg << RL_EPHYAR_PHYREG_SHIFT) & RL_EPHYAR_PHYREG_MASK)); + for (i = 100; i > 0; i--) { + DELAY(100); + val = CSR_READ_4(sc, RL_EPHYAR); + if ((val & RL_EPHYAR_BUSY) != 0) { + val &= RL_EPHYAR_PHYDATA_MASK; + break; + } + } + + if (i == 0) { + device_printf(sc->rl_dev, "EPHY read timedout!\n"); + val = 0xffff; + } + + return (val); +} + +static void +re_ephy_writereg(struct rl_softc *sc, int reg, uint32_t val) +{ + uint32_t v; + int i; + + CSR_WRITE_4(sc, RL_EPHYAR, RL_EPHYAR_WRITE | + (val & RL_EPHYAR_PHYDATA_MASK) | + ((reg << RL_EPHYAR_PHYREG_SHIFT) & RL_EPHYAR_PHYREG_MASK)); + for (i = 100; i > 0; i--) { + DELAY(100); + v = CSR_READ_4(sc, RL_EPHYAR); + if ((v & RL_EPHYAR_BUSY) == 0) + break; + } + + if (i == 0) + device_printf(sc->rl_dev, "EPHY write timedout!\n"); +} + +static uint32_t +re_csi_read(struct rl_softc *sc, int addr) +{ + uint32_t reg; + int i; + + CSR_WRITE_4(sc, RL_CSIAR, RL_CSIAR_READ | + RL_CSIAR_BYTE_EN | (addr & RL_CSIAR_ADDR_MASK)); + for (i = 100; i > 0; i--) { + DELAY(100); + reg = CSR_READ_4(sc, RL_CSIAR); + if ((reg & RL_CSIAR_BUSY) != 0) { + reg = CSR_READ_4(sc, RL_CSIDR); + break; + } + } + + if (i == 0) { + device_printf(sc->rl_dev, "CSI read timedout!\n"); + reg = 0xffffffff; + } + + return (reg); +} + +static void +re_csi_write(struct rl_softc *sc, int addr, uint32_t val) +{ + uint32_t reg; + int i; + + CSR_WRITE_4(sc, RL_CSIDR, val); + CSR_WRITE_4(sc, RL_CSIAR, RL_CSIAR_WRITE | + RL_CSIAR_BYTE_EN | (addr & RL_CSIAR_ADDR_MASK)); + for (i = 100; i > 0; i--) { + DELAY(100); + reg = CSR_READ_4(sc, RL_CSIAR); + if ((reg & RL_CSIAR_BUSY) == 0) + break; + } + + if (i == 0) + device_printf(sc->rl_dev, "CSI write timedout!\n"); +} + static int re_miibus_readreg(device_t dev, int phy, int reg) { @@ -1066,6 +1161,184 @@ } /* + * Reprogramming PHY registers with EPHY register is undocumented + * and the following code is collected from RealTek FreeBSD driver. + * Without this procedure phy probing on newer controllers seem to + * miserably fail. + */ +static void +re_ephy_config(struct rl_softc *sc) +{ + enum ephy_cfg { RL_EPHY_CFG0, RL_EPHY_CFG1, RL_EPHY_CFG2, + RL_EPHY_CFG3, RL_EPHY_CFG4, RL_EPHY_CFG5, RL_EPHY_CFGX }; + uint32_t mac, rev, val; + enum ephy_cfg cfg; + + if ((sc->rl_flags & (RL_FLAG_CSI | RL_FLAG_EPHY)) == 0) + return; + + rev = CSR_READ_4(sc, RL_TXCFG); + mac = rev & 0x00700000; + rev &= 0x7C800000; + + if ((sc->rl_flags & RL_FLAG_CSI) != 0) { + switch (rev) { + case RL_HWREV_8102E: + case RL_HWREV_8102EL: + /* PCI config space offset 0x70F to 0x17. */ + val = re_csi_read(sc, 0x70C); + val &= 0x00FFFFFF; + val |= 0x17000000; + re_csi_write(sc, 0x70C, val); + break; + case RL_HWREV_8168C: + case RL_HWREV_8168C_SPIN2: + case RL_HWREV_8168CP: + /* PCI config space offset 0x70F to 0x27. */ + val = re_csi_read(sc, 0x70C); + val &= 0x00FFFFFF; + val |= 0x27000000; + re_csi_write(sc, 0x70C, val); + break; + } + } +#if 1 + return; +#endif + cfg = RL_EPHY_CFGX; + if ((sc->rl_flags & RL_FLAG_EPHY) != 0) { + switch (rev) { + case RL_HWREV_8168C: + switch (mac) { + case 0x00000000: + cfg = RL_EPHY_CFG0; + break; + case 0x00200000: + cfg = RL_EPHY_CFG1; + break; + case 0x00300000: + cfg = RL_EPHY_CFG2; + break; + case 0x00400000: + default: + break; + } + break; + case RL_HWREV_8168CP: + switch (mac) { + case 0x00000000: + cfg = RL_EPHY_CFG3; + break; + case 0x00100000: + default: + break; + } + break; + case RL_HWREV_8102E: + case RL_HWREV_8102EL: + switch (mac) { + case 0x00000000: + cfg = RL_EPHY_CFG4; + break; + case 0x00100000: + cfg = RL_EPHY_CFG5; + break; + case 0x00200000: + default: + break; + } + } + } + if (cfg == RL_EPHY_CFGX) + return; + + /* Configure PHY registers. */ + switch (mac) { + case RL_EPHY_CFG0: + val = re_ephy_readreg(sc, 0x02); + val &= ~(1 << 11); + val |= (1 << 12); + re_ephy_writereg(sc, 0x02, val); + + val = re_ephy_readreg(sc, 0x03); + val |= (1 << 1); + re_ephy_writereg(sc, 0x03, val); + + val = re_ephy_readreg(sc, 0x06); + val &= ~(1 << 7); + re_ephy_writereg(sc, 0x06, val); + + /* Disable clock request. */ + pci_write_config(sc->rl_dev, 0x81, 0, 1); + break; + case RL_EPHY_CFG1: + val = re_ephy_readreg(sc, 0x01); + val |= (1 << 0); + re_ephy_writereg(sc, 0x01, val); + + val = re_ephy_readreg(sc, 0x03); + val |= (1 << 5); + val |= (1 << 9); + val &= ~(1 << 10); + re_ephy_writereg(sc, 0x03, val); + break; + case RL_EPHY_CFG2: + val = re_ephy_readreg(sc, 0x01); + val |= (1 << 0); + re_ephy_writereg(sc, 0x01, val); + + val = re_ephy_readreg(sc, 0x03); + val |= (1 << 5); + val |= (1 << 9); + val &= ~(1 << 10); + re_ephy_writereg(sc, 0x03, val); + /* Disable clock request. */ + pci_write_config(sc->rl_dev, 0x81, 0, 1); + break; + case RL_EPHY_CFG3: + val = re_ephy_readreg(sc, 0x01); + val |= (1 << 0); + re_ephy_writereg(sc, 0x01, val); + + val = re_ephy_readreg(sc, 0x02); + val &= ~(1 << 11); + val |= (1 << 12); + re_ephy_writereg(sc, 0x02, val); + + val = re_ephy_readreg(sc, 0x03); + val |= (1 << 1); + val |= (1 << 6); + re_ephy_writereg(sc, 0x03, val); + + val = re_ephy_readreg(sc, 0x06); + val &= ~(1 << 7); + re_ephy_writereg(sc, 0x06, val); + + val = re_ephy_readreg(sc, 0x07); + val |= (1 << 13); + re_ephy_writereg(sc, 0x07, val); + + /* Disable clock request. */ + pci_write_config(sc->rl_dev, 0x81, 0, 1); + break; + case RL_EPHY_CFG4: + re_ephy_writereg(sc, 0x01, 0x6e65); + re_ephy_writereg(sc, 0x02, 0x091f); + re_ephy_writereg(sc, 0x03, 0xc2f9); + re_ephy_writereg(sc, 0x06, 0xafb5); + re_ephy_writereg(sc, 0x07, 0x0e00); + re_ephy_writereg(sc, 0x19, 0xec80); + re_ephy_writereg(sc, 0x01, 0x2e65); + re_ephy_writereg(sc, 0x01, 0x6e65); + break; + case RL_EPHY_CFG5: + re_ephy_writereg(sc, 0x03, 0xc2f9); + default: + break; + } +} + +/* * Attach the interface. Allocate softc structures, do ifmedia * setup and ethernet/BPF attach. */ @@ -1214,7 +1487,7 @@ case RL_HWREV_8102EL: sc->rl_flags |= RL_FLAG_NOJUMBO | RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | RL_FLAG_PAR | RL_FLAG_DESCV2 | - RL_FLAG_MACSTAT; + RL_FLAG_MACSTAT | RL_FLAG_CSI | RL_FLAG_EPHY; break; case RL_HWREV_8168_SPIN1: case RL_HWREV_8168_SPIN2: @@ -1226,7 +1499,8 @@ case RL_HWREV_8168C_SPIN2: case RL_HWREV_8168CP: sc->rl_flags |= RL_FLAG_INVMAR | RL_FLAG_PHYWAKE | - RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT; + RL_FLAG_PAR | RL_FLAG_DESCV2 | RL_FLAG_MACSTAT | + RL_FLAG_CSI | RL_FLAG_EPHY; /* * These controllers support jumbo frame but it seems * that enabling it requires touching additional magic @@ -1279,6 +1553,8 @@ as[i] = le16toh(as[i]); bcopy(as, eaddr, sizeof(eaddr)); } + /* Fix PHY registers. */ + re_ephy_config(sc); if (sc->rl_type == RL_8169) { /* Set RX length mask and number of descriptors. */ Index: sys/pci/if_rlreg.h =================================================================== --- sys/pci/if_rlreg.h (revision 184104) +++ sys/pci/if_rlreg.h (working copy) @@ -135,6 +135,13 @@ #define RL_GTXSTART 0x0038 /* 8 bits */ /* + * 8168B/C/CP, 8101E/8102E/8102EL only + */ +#define RL_CSIDR 0x0064 /* 32bits */ +#define RL_CSIAR 0x0068 /* 32bits */ +#define RL_EPHYAR 0x0080 /* 32 bits */ + +/* * TX config register bits */ #define RL_TXCFG_CLRABRT 0x00000001 /* retransmit aborted pkt */ @@ -483,6 +490,22 @@ #define RL_GMEDIASTAT_TBI 0x80 /* TBI enabled */ /* + * EPHY/CSI access register (8168B/C/CP, 8101E/8102E/8102EL only) + */ +#define RL_CSIAR_BUSY 0x80000000 +#define RL_CSIAR_READ 0 +#define RL_CSIAR_WRITE 0x80000000 +#define RL_CSIAR_BYTE_EN 0x0000F000 +#define RL_CSIAR_ADDR_MASK 0x00000FFF + +#define RL_EPHYAR_BUSY 0x80000000 +#define RL_EPHYAR_READ 0 +#define RL_EPHYAR_WRITE 0x80000000 +#define RL_EPHYAR_PHYREG_MASK 0x001F0000 +#define RL_EPHYAR_PHYREG_SHIFT 16 +#define RL_EPHYAR_PHYDATA_MASK 0x0000FFFF + +/* * The RealTek doesn't use a fragment-based descriptor mechanism. * Instead, there are only four register sets, each or which represents * one 'descriptor.' Basically, each TX descriptor is just a contiguous @@ -847,6 +870,8 @@ #define RL_FLAG_PAR 0x0020 #define RL_FLAG_DESCV2 0x0040 #define RL_FLAG_MACSTAT 0x0080 +#define RL_FLAG_CSI 0x0100 +#define RL_FLAG_EPHY 0x0200 #define RL_FLAG_LINK 0x8000 };