Commit 9f3f7910 authored by Ayaz Abdulla's avatar Ayaz Abdulla Committed by Jeff Garzik

forcedeth: realtek phy crossover detection

This patch fixes an issue seen with the realtek 8201 phy. This phy has a
problem with crossover detection and it needs to be disabled. The
problem only arises on certain switches. Therefore, a module parameter
has been added to allow enabling crossover detection if needed. The
default will be set to disabled.
Signed-off-by: default avatarAyaz Abdulla <aabdulla@nvidia.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent cca87c18
...@@ -487,12 +487,18 @@ union ring_type { ...@@ -487,12 +487,18 @@ union ring_type {
#define PHY_OUI_CICADA 0x03f1 #define PHY_OUI_CICADA 0x03f1
#define PHY_OUI_VITESSE 0x01c1 #define PHY_OUI_VITESSE 0x01c1
#define PHY_OUI_REALTEK 0x0732 #define PHY_OUI_REALTEK 0x0732
#define PHY_OUI_REALTEK2 0x0020
#define PHYID1_OUI_MASK 0x03ff #define PHYID1_OUI_MASK 0x03ff
#define PHYID1_OUI_SHFT 6 #define PHYID1_OUI_SHFT 6
#define PHYID2_OUI_MASK 0xfc00 #define PHYID2_OUI_MASK 0xfc00
#define PHYID2_OUI_SHFT 10 #define PHYID2_OUI_SHFT 10
#define PHYID2_MODEL_MASK 0x03f0 #define PHYID2_MODEL_MASK 0x03f0
#define PHY_MODEL_MARVELL_E3016 0x220 #define PHY_MODEL_REALTEK_8211 0x0110
#define PHY_REV_MASK 0x0001
#define PHY_REV_REALTEK_8211B 0x0000
#define PHY_REV_REALTEK_8211C 0x0001
#define PHY_MODEL_REALTEK_8201 0x0200
#define PHY_MODEL_MARVELL_E3016 0x0220
#define PHY_MARVELL_E3016_INITMASK 0x0300 #define PHY_MARVELL_E3016_INITMASK 0x0300
#define PHY_CICADA_INIT1 0x0f000 #define PHY_CICADA_INIT1 0x0f000
#define PHY_CICADA_INIT2 0x0e00 #define PHY_CICADA_INIT2 0x0e00
...@@ -519,10 +525,18 @@ union ring_type { ...@@ -519,10 +525,18 @@ union ring_type {
#define PHY_REALTEK_INIT_REG1 0x1f #define PHY_REALTEK_INIT_REG1 0x1f
#define PHY_REALTEK_INIT_REG2 0x19 #define PHY_REALTEK_INIT_REG2 0x19
#define PHY_REALTEK_INIT_REG3 0x13 #define PHY_REALTEK_INIT_REG3 0x13
#define PHY_REALTEK_INIT_REG4 0x14
#define PHY_REALTEK_INIT_REG5 0x18
#define PHY_REALTEK_INIT_REG6 0x11
#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
#define PHY_REALTEK_INIT4 0xad17 #define PHY_REALTEK_INIT4 0xad17
#define PHY_REALTEK_INIT5 0xfb54
#define PHY_REALTEK_INIT6 0xf5c7
#define PHY_REALTEK_INIT7 0x1000
#define PHY_REALTEK_INIT8 0x0003
#define PHY_REALTEK_INIT_MSK1 0x0003
#define PHY_GIGABIT 0x0100 #define PHY_GIGABIT 0x0100
...@@ -701,6 +715,7 @@ struct fe_priv { ...@@ -701,6 +715,7 @@ struct fe_priv {
int wolenabled; int wolenabled;
unsigned int phy_oui; unsigned int phy_oui;
unsigned int phy_model; unsigned int phy_model;
unsigned int phy_rev;
u16 gigabit; u16 gigabit;
int intr_test; int intr_test;
int recover_error; int recover_error;
...@@ -714,6 +729,7 @@ struct fe_priv { ...@@ -714,6 +729,7 @@ struct fe_priv {
u32 txrxctl_bits; u32 txrxctl_bits;
u32 vlanctl_bits; u32 vlanctl_bits;
u32 driver_data; u32 driver_data;
u32 device_id;
u32 register_size; u32 register_size;
int rx_csum; int rx_csum;
u32 mac_in_use; u32 mac_in_use;
...@@ -824,6 +840,16 @@ enum { ...@@ -824,6 +840,16 @@ enum {
}; };
static int dma_64bit = NV_DMA_64BIT_ENABLED; static int dma_64bit = NV_DMA_64BIT_ENABLED;
/*
* Crossover Detection
* Realtek 8201 phy + some OEM boards do not work properly.
*/
enum {
NV_CROSSOVER_DETECTION_DISABLED,
NV_CROSSOVER_DETECTION_ENABLED
};
static int phy_cross = NV_CROSSOVER_DETECTION_DISABLED;
static inline struct fe_priv *get_nvpriv(struct net_device *dev) static inline struct fe_priv *get_nvpriv(struct net_device *dev)
{ {
return netdev_priv(dev); return netdev_priv(dev);
...@@ -1088,6 +1114,8 @@ static int phy_init(struct net_device *dev) ...@@ -1088,6 +1114,8 @@ static int phy_init(struct net_device *dev)
} }
} }
if (np->phy_oui == PHY_OUI_REALTEK) { if (np->phy_oui == PHY_OUI_REALTEK) {
if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
np->phy_rev == PHY_REV_REALTEK_8211B) {
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { 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)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR; return PHY_ERROR;
...@@ -1104,11 +1132,37 @@ static int phy_init(struct net_device *dev) ...@@ -1104,11 +1132,37 @@ static int phy_init(struct net_device *dev)
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR; return PHY_ERROR;
} }
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) {
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_REG5, PHY_REALTEK_INIT6)) {
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)) { 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)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR; 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 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) {
phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
phy_reserved |= PHY_REALTEK_INIT7;
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
}
}
}
/* set advertise register */ /* set advertise register */
reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ); reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
...@@ -1246,6 +1300,8 @@ static int phy_init(struct net_device *dev) ...@@ -1246,6 +1300,8 @@ static int phy_init(struct net_device *dev)
} }
} }
if (np->phy_oui == PHY_OUI_REALTEK) { if (np->phy_oui == PHY_OUI_REALTEK) {
if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
np->phy_rev == PHY_REV_REALTEK_8211B) {
/* reset could have cleared these out, set them back */ /* reset could have cleared these out, set them back */
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1)) { 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)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
...@@ -1263,11 +1319,54 @@ static int phy_init(struct net_device *dev) ...@@ -1263,11 +1319,54 @@ static int phy_init(struct net_device *dev)
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR; return PHY_ERROR;
} }
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5)) {
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_REG5, PHY_REALTEK_INIT6)) {
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)) { 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)); printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR; 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 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_34 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_35 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_36 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_37 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_38 ||
np->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_39) {
phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
phy_reserved |= PHY_REALTEK_INIT7;
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, phy_reserved)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
}
if (phy_cross == NV_CROSSOVER_DETECTION_DISABLED) {
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3)) {
printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
return PHY_ERROR;
}
phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ);
phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
phy_reserved |= PHY_REALTEK_INIT3;
if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved)) {
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;
}
}
}
}
/* some phys clear out pause advertisment on reset, set it back */ /* some phys clear out pause advertisment on reset, set it back */
mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg); mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
...@@ -5254,6 +5353,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i ...@@ -5254,6 +5353,8 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
/* copy of driver data */ /* copy of driver data */
np->driver_data = id->driver_data; np->driver_data = id->driver_data;
/* copy of device id */
np->device_id = id->device;
/* handle different descriptor versions */ /* handle different descriptor versions */
if (id->driver_data & DEV_HAS_HIGH_DMA) { if (id->driver_data & DEV_HAS_HIGH_DMA) {
...@@ -5543,6 +5644,14 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i ...@@ -5543,6 +5644,14 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
pci_name(pci_dev), id1, id2, phyaddr); pci_name(pci_dev), id1, id2, phyaddr);
np->phyaddr = phyaddr; np->phyaddr = phyaddr;
np->phy_oui = id1 | id2; np->phy_oui = id1 | id2;
/* Realtek hardcoded phy id1 to all zero's on certain phys */
if (np->phy_oui == PHY_OUI_REALTEK2)
np->phy_oui = PHY_OUI_REALTEK;
/* Setup phy revision for Realtek */
if (np->phy_oui == PHY_OUI_REALTEK && np->phy_model == PHY_MODEL_REALTEK_8211)
np->phy_rev = mii_rw(dev, phyaddr, MII_RESV1, MII_READ) & PHY_REV_MASK;
break; break;
} }
if (i == 33) { if (i == 33) {
...@@ -5621,6 +5730,28 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i ...@@ -5621,6 +5730,28 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
return err; return err;
} }
static void nv_restore_phy(struct net_device *dev)
{
struct fe_priv *np = netdev_priv(dev);
u16 phy_reserved, mii_control;
if (np->phy_oui == PHY_OUI_REALTEK &&
np->phy_model == PHY_MODEL_REALTEK_8201 &&
phy_cross == NV_CROSSOVER_DETECTION_DISABLED) {
mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3);
phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ);
phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
phy_reserved |= PHY_REALTEK_INIT8;
mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved);
mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1);
/* restart auto negotiation */
mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
mii_rw(dev, np->phyaddr, MII_BMCR, mii_control);
}
}
static void __devexit nv_remove(struct pci_dev *pci_dev) static void __devexit nv_remove(struct pci_dev *pci_dev)
{ {
struct net_device *dev = pci_get_drvdata(pci_dev); struct net_device *dev = pci_get_drvdata(pci_dev);
...@@ -5637,6 +5768,9 @@ static void __devexit nv_remove(struct pci_dev *pci_dev) ...@@ -5637,6 +5768,9 @@ static void __devexit nv_remove(struct pci_dev *pci_dev)
writel(readl(base + NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV, writel(readl(base + NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV,
base + NvRegTransmitPoll); base + NvRegTransmitPoll);
/* restore any phy related changes */
nv_restore_phy(dev);
/* free all structures */ /* free all structures */
free_rings(dev); free_rings(dev);
iounmap(get_hwbase(dev)); iounmap(get_hwbase(dev));
...@@ -5888,6 +6022,8 @@ module_param(msix, int, 0); ...@@ -5888,6 +6022,8 @@ module_param(msix, int, 0);
MODULE_PARM_DESC(msix, "MSIX interrupts are enabled by setting to 1 and disabled by setting to 0."); MODULE_PARM_DESC(msix, "MSIX interrupts are enabled by setting to 1 and disabled by setting to 0.");
module_param(dma_64bit, int, 0); module_param(dma_64bit, int, 0);
MODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0."); MODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0.");
module_param(phy_cross, int, 0);
MODULE_PARM_DESC(phy_cross, "Phy crossover detection for Realtek 8201 phy is enabled by setting to 1 and disabled by setting to 0.");
MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>"); MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver"); MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
......
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