Commit 22ae03a1 authored by Ayaz Abdulla's avatar Ayaz Abdulla Committed by Jeff Garzik

forcedeth bug fix: realtek phy 8211c errata

This patch adds support for the realtek 8211c phy. The driver must
perform a hardware reset of the phy due to an errata where the phy could
not detect the link.
Signed-off-by: default avatarAyaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 281c7413
...@@ -333,6 +333,7 @@ enum { ...@@ -333,6 +333,7 @@ enum {
NvRegPowerState2 = 0x600, NvRegPowerState2 = 0x600,
#define NVREG_POWERSTATE2_POWERUP_MASK 0x0F11 #define NVREG_POWERSTATE2_POWERUP_MASK 0x0F11
#define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001 #define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001
#define NVREG_POWERSTATE2_PHY_RESET 0x0004
}; };
/* Big endian: should work, but is untested */ /* Big endian: should work, but is untested */
...@@ -529,6 +530,7 @@ union ring_type { ...@@ -529,6 +530,7 @@ union ring_type {
#define PHY_REALTEK_INIT_REG4 0x14 #define PHY_REALTEK_INIT_REG4 0x14
#define PHY_REALTEK_INIT_REG5 0x18 #define PHY_REALTEK_INIT_REG5 0x18
#define PHY_REALTEK_INIT_REG6 0x11 #define PHY_REALTEK_INIT_REG6 0x11
#define PHY_REALTEK_INIT_REG7 0x01
#define PHY_REALTEK_INIT1 0x0000 #define PHY_REALTEK_INIT1 0x0000
#define PHY_REALTEK_INIT2 0x8e00 #define PHY_REALTEK_INIT2 0x8e00
#define PHY_REALTEK_INIT3 0x0001 #define PHY_REALTEK_INIT3 0x0001
...@@ -537,6 +539,9 @@ union ring_type { ...@@ -537,6 +539,9 @@ union ring_type {
#define PHY_REALTEK_INIT6 0xf5c7 #define PHY_REALTEK_INIT6 0xf5c7
#define PHY_REALTEK_INIT7 0x1000 #define PHY_REALTEK_INIT7 0x1000
#define PHY_REALTEK_INIT8 0x0003 #define PHY_REALTEK_INIT8 0x0003
#define PHY_REALTEK_INIT9 0x0008
#define PHY_REALTEK_INIT10 0x0005
#define PHY_REALTEK_INIT11 0x0200
#define PHY_REALTEK_INIT_MSK1 0x0003 #define PHY_REALTEK_INIT_MSK1 0x0003
#define PHY_GIGABIT 0x0100 #define PHY_GIGABIT 0x0100
...@@ -1149,6 +1154,42 @@ static int phy_init(struct net_device *dev) ...@@ -1149,6 +1154,42 @@ static int phy_init(struct net_device *dev)
return PHY_ERROR; return PHY_ERROR;
} }
} }
if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
np->phy_rev == PHY_REV_REALTEK_8211C) {
u32 powerstate = readl(base + NvRegPowerState2);
/* need to perform hw phy reset */
powerstate |= NVREG_POWERSTATE2_PHY_RESET;
writel(powerstate, base + NvRegPowerState2);
msleep(25);
powerstate &= ~NVREG_POWERSTATE2_PHY_RESET;
writel(powerstate, base + NvRegPowerState2);
msleep(25);
reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
reg |= PHY_REALTEK_INIT9;
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, reg)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, MII_READ);
if (!(reg & PHY_REALTEK_INIT11)) {
reg |= PHY_REALTEK_INIT11;
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, reg)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
}
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
}
if (np->phy_model == PHY_MODEL_REALTEK_8201) { if (np->phy_model == PHY_MODEL_REALTEK_8201) {
if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 || if (np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 || np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 ||
...@@ -1201,6 +1242,16 @@ static int phy_init(struct net_device *dev) ...@@ -1201,6 +1242,16 @@ static int phy_init(struct net_device *dev)
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ); mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
mii_control |= BMCR_ANENABLE; mii_control |= BMCR_ANENABLE;
if (np->phy_oui == PHY_OUI_REALTEK &&
np->phy_model == PHY_MODEL_REALTEK_8211 &&
np->phy_rev == PHY_REV_REALTEK_8211C) {
/* start autoneg since we already performed hw reset above */
mii_control |= BMCR_ANRESTART;
if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
printk(KERN_INFO "%s: phy init failed\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
} else {
/* reset the phy /* reset the phy
* (certain phys need bmcr to be setup with reset) * (certain phys need bmcr to be setup with reset)
*/ */
...@@ -1208,6 +1259,7 @@ static int phy_init(struct net_device *dev) ...@@ -1208,6 +1259,7 @@ static int phy_init(struct net_device *dev)
printk(KERN_INFO "%s: phy reset failed\n", pci_name(np->pci_dev)); printk(KERN_INFO "%s: phy reset failed\n", pci_name(np->pci_dev));
return PHY_ERROR; return PHY_ERROR;
} }
}
/* phy vendor specific configuration */ /* phy vendor specific configuration */
if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII) ) { if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII) ) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment