netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] natsemi update 3/4 External PHY operation
@ 2004-06-04 19:55 Gary N Spiess
  2004-06-06 15:50 ` Manfred Spraul
  0 siblings, 1 reply; 4+ messages in thread
From: Gary N Spiess @ 2004-06-04 19:55 UTC (permalink / raw)
  To: netdev; +Cc: manfred


[-- Attachment #1.1: Type: text/plain, Size: 989 bytes --]

Jeff,
This is the third of a series of patches needed to support our product using the DP83815.
This patch permits operation of an external PHY.  Updates the mdio_read and mdio_write functions to permit actual MII operation.  Modify miscellaneous code to use the MII registers instead of the internal registers where it makes a difference.  Relocate the internal phy to phy_address=1, and add find_mii() to locate the address of the external mii phy.  Prevent the 'nasty random phy-reset' code from operating when an external phy is being used.
The probe code determines the previously used internal or external phy to determine the initial mode it will use.  Again, our implementation does not have an EEPROM, so we rely on the mode that our boot rom used to setup the Ethernet.
Gary
 oooooooooooooooooooooooooooooooooooooooooooooooooo
 Gary Spiess (Gary.Spiess@Intermec.com)
 MobileLan Wireless Products Group, Intermec Technology Corp
 voice: 319 369-3580  fax: 319 369-3804


[-- Attachment #1.2: Type: text/html, Size: 1379 bytes --]

[-- Attachment #2: natsemi-2.6.6-3.patch --]
[-- Type: application/octet-stream, Size: 7352 bytes --]

--- linux-2.6.6/drivers/net/natsemi.c	2004-06-03 15:44:34.000000000 -0500
+++ linux/drivers/net/natsemi.c	2004-06-03 15:44:47.000000000 -0500
@@ -468,6 +468,9 @@
 	EE_DataIn		= 0x01,
 	EE_ChipSelect		= 0x08,
 	EE_DataOut		= 0x02,
+	MII_Data 		= 0x10,
+	MII_Write		= 0x20,
+	MII_ShiftClk		= 0x40,
 };
 
 enum PCIBusCfg_bits {
@@ -602,6 +605,9 @@
 	PhyAddrMask		= 0x1f,
 };
 
+/* Internal Phy address is fixed */
+#define PHY_ADDR_INTERNAL	0x01
+
 /* values we might find in the silicon revision register */
 #define SRR_DP83815_C	0x0302
 #define SRR_DP83815_D	0x0403
@@ -685,6 +691,7 @@
 static int eeprom_read(long ioaddr, int location);
 static int mdio_read(struct net_device *dev, int phy_id, int reg);
 static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data);
+static int find_mii(struct net_device *dev);
 static void natsemi_reset(struct net_device *dev);
 static void natsemi_reload_eeprom(struct net_device *dev);
 static void natsemi_stop_rxtx(struct net_device *dev);
@@ -812,10 +819,17 @@
 	np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
 	np->hands_off = 0;
 
+	/* default to external phy, if previously selected */
+	if (readl(dev->base_addr + ChipConfig) & CfgExtPhy)
+		dev->if_port = PORT_MII;
+	else
+		dev->if_port = PORT_TP;
+
 	/* Reset the chip to erase previous misconfiguration. */
 	natsemi_reload_eeprom(dev);
 	natsemi_reset(dev);
 
+	np->phy_addr_external = find_mii(dev);
 	option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -860,16 +874,22 @@
 	}
 
 	np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
-	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000
+	if ((np->advertising & (ADVERTISE_100FULL|ADVERTISE_10FULL|
+				ADVERTISE_100HALF|ADVERTISE_10HALF)) !=
+			       (ADVERTISE_100FULL|ADVERTISE_10FULL|
+				ADVERTISE_100HALF|ADVERTISE_10HALF)
 	 && netif_msg_probe(np)) {
-		u32 chip_config = readl(ioaddr + ChipConfig);
 		printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
 			"10%s %s duplex.\n",
 			dev->name,
-			chip_config & CfgAnegEnable ?
+			(mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE)?
 			  "enabled, advertise" : "disabled, force",
-			chip_config & CfgAneg100 ? "0" : "",
-			chip_config & CfgAnegFull ? "full" : "half");
+			(np->advertising &
+			  (ADVERTISE_100FULL|ADVERTISE_100HALF))?
+			    "0" : "",
+			(np->advertising &
+			  (ADVERTISE_100FULL|ADVERTISE_10FULL))?
+			    "full" : "half");
 	}
 	if (netif_msg_probe(np))
 		printk(KERN_INFO
@@ -954,25 +974,107 @@
 
 /* MII transceiver control section.
  * The 83815 series has an internal transceiver, and we present the
- * management registers as if they were MII connected. */
+ * internal management registers as if they were MII connected.
+ * External Phy registers are referenced through the MII interface.
+ */
+
+static int mii_getbit (struct net_device *dev)
+{
+	int data;
+
+	writeb(MII_ShiftClk, dev->base_addr + EECtrl);
+	data = readb(dev->base_addr + EECtrl);
+	udelay(1);
+	writeb(0, dev->base_addr + EECtrl);
+	udelay(1);
+	return (data & MII_Data)? 1 : 0;
+}
+
+static void mii_send_bits (struct net_device *dev, u32 data, int len)
+{
+	u32 i;
+
+	for (i = (1 << (len-1)); i; i >>= 1)
+	{
+		u32 mdio_val = MII_Write | ((data & i)? MII_Data : 0);
+		writeb(mdio_val, dev->base_addr + EECtrl);
+		udelay(1);
+		writeb(mdio_val | MII_ShiftClk, dev->base_addr + EECtrl);
+		udelay(1);
+	}
+	writeb(0, dev->base_addr + EECtrl);
+	udelay(1);
+}
 
 static int mdio_read(struct net_device *dev, int phy_id, int reg)
 {
-	if (phy_id == 1 && reg < 32)
+	struct netdev_private *np = dev->priv;
+	u32 cmd;
+	int i;
+	u32 retval = 0;
+
+	/* The 83815 series has an internal transceiver; handle separately */
+	if (dev->if_port == PORT_TP)
 		return readl(dev->base_addr+BasicControl+(reg<<2))&0xffff;
-	else
-		return 0xffff;
+	if (phy_id == PHY_ADDR_INTERNAL)
+		phy_id = np->phy_addr_external;
+
+	/* Ensure sync */
+	mii_send_bits (dev, 0xffffffff, 32);
+	/* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
+	/* ST,OP = 0110'b for read operation */
+	cmd = (0x06 << 10) | (phy_id << 5) | reg;
+	mii_send_bits (dev, cmd, 14);
+	/* Turnaround */
+	if (mii_getbit (dev))
+		return 0;
+	/* Read data */
+	for (i = 0; i < 16; i++) {
+		retval <<= 1;
+		retval |= mii_getbit (dev);
+	}
+	/* End cycle */
+	mii_getbit (dev);
+	return retval;
 }
 
 static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data)
 {
 	struct netdev_private *np = dev->priv;
-	if (phy_id == 1 && reg < 32) {
+	u32 cmd;
+
+	switch (reg) {
+		case MII_ADVERTISE: np->advertising = data; break;
+	}
+
+	/* The 83815 series has an internal transceiver; handle separately */
+	if (dev->if_port == PORT_TP) {
 		writew(data, dev->base_addr+BasicControl+(reg<<2));
-		switch (reg) {
-			case MII_ADVERTISE: np->advertising = data; break;
-		}
+		return;
 	}
+	if (phy_id == PHY_ADDR_INTERNAL)
+		phy_id = np->phy_addr_external;
+
+	/* Ensure sync */
+	mii_send_bits (dev, 0xffffffff, 32);
+	/* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
+	/* ST,OP,AAAAA,RRRRR,TA = 0101xxxxxxxxxx10'b = 0x5002 for write */
+	cmd = (0x5002 << 16) | (phy_id << 23) | (reg << 18) | data;
+	mii_send_bits (dev, cmd, 32);
+	/* End cycle */
+	mii_getbit (dev);
+}
+
+static int find_mii(struct net_device *dev)
+{
+	int tmp;
+	int i;
+	for (i = 0x1f; i; i--) {
+		tmp = mdio_read(dev, i, MII_BMSR);
+		if (tmp != 0xffff && tmp != 0x0000)
+			return i;
+	}
+	return PHY_ADDR_INTERNAL;
 }
 
 /* CFG bits [13:16] [18:23] */
@@ -1032,6 +1134,11 @@
 			dev->name, i*5);
 	}
 
+	/* reset internal phy to fixed mii address */
+	writew(PHY_ADDR_INTERNAL, dev->base_addr + PhyCtrl);
+	/* turn on external phy if it was selected */
+	if (dev->if_port != PORT_TP)
+		cfg |= (CfgExtPhy | CfgPhyDis);
 	/* restore CFG */
 	cfg |= readl(dev->base_addr + ChipConfig) & ~CFG_RESET_SAVE;
 	writel(cfg, dev->base_addr + ChipConfig);
@@ -1204,9 +1311,9 @@
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 	int duplex;
-	int chipcfg = readl(ioaddr + ChipConfig);
+	int bmsr = mdio_read(dev, 1, MII_BMSR);
 
-	if (!(chipcfg & CfgLink)) {
+	if (!(bmsr & BMSR_LSTATUS)) {
 		if (netif_carrier_ok(dev)) {
 			if (netif_msg_link(np))
 				printk(KERN_NOTICE "%s: link down.\n",
@@ -1223,7 +1330,16 @@
 		do_cable_magic(dev);
 	}
 
-	duplex = np->full_duplex || (chipcfg & CfgFullDuplex ? 1 : 0);
+	duplex = np->full_duplex;
+	if (!duplex) {
+		if (bmsr & BMSR_ANEGCOMPLETE) {
+			int tmp = mii_nway_result(
+				np->advertising & mdio_read(dev, 1, MII_LPA));
+			if (tmp == LPA_100FULL || tmp == LPA_10FULL)
+				duplex = 1;
+		} else if (mdio_read(dev, 1, MII_BMCR) & BMCR_FULLDPLX)
+			duplex = 1;
+	}
 
 	/* if duplex is set then bit 28 must be set, too */
 	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
@@ -1278,6 +1394,13 @@
 	writew(0, ioaddr + PGSEL);
 	np->dspcfg = DSPCFG_VAL;
 
+	/* if external phy, then DSPCFG register isn't functional.
+	   Fix the value here so the "nasty random phy-reset" code doesn't
+	   think it needs to reinitialize the chip.
+	 */
+	if (dev->if_port != PORT_TP)
+		np->dspcfg = 0;
+
 	/* Enable PHY Specific event based interrupts.  Link state change
 	   and Auto-Negotiation Completion are among the affected.
 	   Read the intr status to clear it (needed for wake events).

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2004-06-08  5:30 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-06-04 19:55 [PATCH] natsemi update 3/4 External PHY operation Gary N Spiess
2004-06-06 15:50 ` Manfred Spraul
2004-06-07 21:08   ` Gary N Spiess
2004-06-08  5:30     ` Manfred Spraul

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).