From: "Gary N Spiess" <Gary.Spiess@intermec.com>
To: netdev@oss.sgi.com
Cc: manfred@colorfullife.com
Subject: [PATCH] natsemi update 3/4 External PHY operation
Date: Fri, 04 Jun 2004 14:55:29 -0500 [thread overview]
Message-ID: <200406041455290031.0BC56C76@136.179.85.112> (raw)
[-- 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).
next reply other threads:[~2004-06-04 19:55 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-06-04 19:55 Gary N Spiess [this message]
2004-06-06 15:50 ` [PATCH] natsemi update 3/4 External PHY operation Manfred Spraul
2004-06-07 21:08 ` Gary N Spiess
2004-06-08 5:30 ` Manfred Spraul
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200406041455290031.0BC56C76@136.179.85.112 \
--to=gary.spiess@intermec.com \
--cc=manfred@colorfullife.com \
--cc=netdev@oss.sgi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).