Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+178 −42
Original line number Diff line number Diff line
@@ -487,12 +487,18 @@ union ring_type {
#define PHY_OUI_CICADA		0x03f1
#define PHY_OUI_VITESSE		0x01c1
#define PHY_OUI_REALTEK		0x0732
#define PHY_OUI_REALTEK2	0x0020
#define PHYID1_OUI_MASK	0x03ff
#define PHYID1_OUI_SHFT	6
#define PHYID2_OUI_MASK	0xfc00
#define PHYID2_OUI_SHFT	10
#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_CICADA_INIT1	0x0f000
#define PHY_CICADA_INIT2	0x0e00
@@ -519,10 +525,18 @@ union ring_type {
#define PHY_REALTEK_INIT_REG1	0x1f
#define PHY_REALTEK_INIT_REG2	0x19
#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_INIT2	0x8e00
#define PHY_REALTEK_INIT3	0x0001
#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

@@ -701,6 +715,7 @@ struct fe_priv {
	int wolenabled;
	unsigned int phy_oui;
	unsigned int phy_model;
	unsigned int phy_rev;
	u16 gigabit;
	int intr_test;
	int recover_error;
@@ -714,6 +729,7 @@ struct fe_priv {
	u32 txrxctl_bits;
	u32 vlanctl_bits;
	u32 driver_data;
	u32 device_id;
	u32 register_size;
	int rx_csum;
	u32 mac_in_use;
@@ -824,6 +840,16 @@ enum {
};
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)
{
	return netdev_priv(dev);
@@ -1088,6 +1114,8 @@ static int phy_init(struct net_device *dev)
		}
	}
	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)) {
				printk(KERN_INFO "%s: phy init failed.\n", pci_name(np->pci_dev));
				return PHY_ERROR;
@@ -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));
				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)) {
				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 ||
			    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 */
	reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
@@ -1246,6 +1300,8 @@ static int phy_init(struct net_device *dev)
		}
	}
	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 */
			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));
@@ -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));
				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)) {
				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 ||
			    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 */
	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

	/* copy of driver data */
	np->driver_data = id->driver_data;
	/* copy of device id */
	np->device_id = id->device;

	/* handle different descriptor versions */
	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
			pci_name(pci_dev), id1, id2, phyaddr);
		np->phyaddr = phyaddr;
		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;
	}
	if (i == 33) {
@@ -5621,6 +5730,28 @@ out:
	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)
{
	struct net_device *dev = pci_get_drvdata(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,
	       base + NvRegTransmitPoll);

	/* restore any phy related changes */
	nv_restore_phy(dev);

	/* free all structures */
	free_rings(dev);
	iounmap(get_hwbase(dev));
@@ -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_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_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_DESCRIPTION("Reverse Engineered nForce ethernet driver");