summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAyaz Abdulla <aabdulla@nvidia.com>2008-07-25 15:31:29 -0400
committerJeff Garzik <jgarzik@redhat.com>2008-07-29 17:48:22 -0400
commit22ae03a190011fa2241e68a6c51369d78039348e (patch)
treeb1224c29bdf78d27c69cd1a0a52d54ca9ea6cff1
parent281c7413ed914623d3245299a4761b6b27ab9fdb (diff)
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: Ayaz Abdulla <aabdulla@nvidia.com> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
-rw-r--r--drivers/net/forcedeth.c64
1 files changed, 58 insertions, 6 deletions
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 4ed89fa9ae4..01b38b092c7 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -333,6 +333,7 @@ enum {
NvRegPowerState2 = 0x600,
#define NVREG_POWERSTATE2_POWERUP_MASK 0x0F11
#define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001
+#define NVREG_POWERSTATE2_PHY_RESET 0x0004
};
/* Big endian: should work, but is untested */
@@ -529,6 +530,7 @@ union ring_type {
#define PHY_REALTEK_INIT_REG4 0x14
#define PHY_REALTEK_INIT_REG5 0x18
#define PHY_REALTEK_INIT_REG6 0x11
+#define PHY_REALTEK_INIT_REG7 0x01
#define PHY_REALTEK_INIT1 0x0000
#define PHY_REALTEK_INIT2 0x8e00
#define PHY_REALTEK_INIT3 0x0001
@@ -537,6 +539,9 @@ union ring_type {
#define PHY_REALTEK_INIT6 0xf5c7
#define PHY_REALTEK_INIT7 0x1000
#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_GIGABIT 0x0100
@@ -1149,6 +1154,42 @@ static int phy_init(struct net_device *dev)
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->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_32 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_33 ||
@@ -1201,12 +1242,23 @@ static int phy_init(struct net_device *dev)
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
mii_control |= BMCR_ANENABLE;
- /* reset the phy
- * (certain phys need bmcr to be setup with reset)
- */
- if (phy_reset(dev, mii_control)) {
- printk(KERN_INFO "%s: phy reset failed\n", pci_name(np->pci_dev));
- return PHY_ERROR;
+ 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
+ * (certain phys need bmcr to be setup with reset)
+ */
+ if (phy_reset(dev, mii_control)) {
+ printk(KERN_INFO "%s: phy reset failed\n", pci_name(np->pci_dev));
+ return PHY_ERROR;
+ }
}
/* phy vendor specific configuration */