patch-2.4.25 linux-2.4.25/drivers/net/au1000_eth.c
Next file: linux-2.4.25/drivers/net/au1000_eth.h
Previous file: linux-2.4.25/drivers/net/Space.c
Back to the patch index
Back to the overall index
- Lines: 1291
- Date:
2004-02-18 05:36:31.000000000 -0800
- Orig file:
linux-2.4.24/drivers/net/au1000_eth.c
- Orig date:
2002-11-28 15:53:13.000000000 -0800
diff -urN linux-2.4.24/drivers/net/au1000_eth.c linux-2.4.25/drivers/net/au1000_eth.c
@@ -1,8 +1,9 @@
/*
*
- * Alchemy Semi Au1000 ethernet driver
+ * Alchemy Au1x00 ethernet driver
*
- * Copyright 2001 MontaVista Software Inc.
+ * Copyright 2001,2002,2003 MontaVista Software Inc.
+ * Copyright 2002 TimeSys Corp.
* Author: MontaVista Software, Inc.
* ppopov@mvista.com or source@mvista.com
*
@@ -22,8 +23,15 @@
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* ########################################################################
+ *
+ *
*/
-#include <linux/config.h>
+
+#ifndef __mips__
+#error This driver only works with MIPS architectures!
+#endif
+
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -44,22 +52,27 @@
#include <asm/irq.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#include <asm/processor.h>
#include <asm/au1000.h>
+#include <asm/cpu.h>
#include "au1000_eth.h"
+
#ifdef AU1000_ETH_DEBUG
-static int au1000_debug = 10;
+static int au1000_debug = 5;
#else
static int au1000_debug = 3;
#endif
+MODULE_LICENSE("GPL");
+
// prototypes
static void *dma_alloc(size_t, dma_addr_t *);
static void dma_free(void *, size_t);
static void hard_stop(struct net_device *);
static void enable_rx_tx(struct net_device *dev);
-static int __init au1000_probe1(struct net_device *, long, int, int);
+static struct net_device * au1000_probe(u32 ioaddr, int irq, int port_num);
static int au1000_init(struct net_device *);
static int au1000_open(struct net_device *);
static int au1000_close(struct net_device *);
@@ -101,27 +114,8 @@
*/
-/*
- * Base address and interupt of the Au1xxx ethernet macs
- */
-static struct {
- unsigned int port;
- int irq;
-} au1000_iflist[NUM_INTERFACES] = {
- {AU1000_ETH0_BASE, AU1000_ETH0_IRQ},
- {AU1000_ETH1_BASE, AU1000_ETH1_IRQ}
- },
- au1500_iflist[NUM_INTERFACES] = {
- {AU1500_ETH0_BASE, AU1000_ETH0_IRQ},
- {AU1500_ETH1_BASE, AU1000_ETH1_IRQ}
- },
- au1100_iflist[NUM_INTERFACES] = {
- {AU1000_ETH0_BASE, AU1000_ETH0_IRQ},
- {NULL, NULL}
- };
-
static char version[] __devinitdata =
- "au1000eth.c:1.0 ppopov@mvista.com\n";
+ "au1000eth.c:1.4 ppopov@mvista.com\n";
/* These addresses are only used if yamon doesn't tell us what
* the mac address is, and the mac address is not passed on the
@@ -138,6 +132,7 @@
#define cpu_to_dma32 cpu_to_be32
#define dma32_to_cpu be32_to_cpu
+struct au1000_private *au_macs[NUM_ETH_INTERFACES];
/* FIXME
* All of the PHY code really should be detached from the MAC
@@ -156,7 +151,6 @@
s16 data;
/* Stop auto-negotiation */
- //printk("bcm_5201_init\n");
data = mdio_read(dev, phy_addr, MII_CONTROL);
mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
@@ -171,12 +165,8 @@
data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
mdio_write(dev, phy_addr, MII_CONTROL, data);
- /* Enable TX LED instead of FDX */
- data = mdio_read(dev, phy_addr, MII_INT);
- data &= ~MII_FDX_LED;
- mdio_write(dev, phy_addr, MII_INT, data);
-
- if (au1000_debug > 4) dump_mii(dev, phy_addr);
+ if (au1000_debug > 4)
+ dump_mii(dev, phy_addr);
return 0;
}
@@ -184,7 +174,6 @@
{
s16 mii_control, timeout;
- //printk("bcm_5201_reset\n");
mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
mdelay(1);
@@ -247,8 +236,8 @@
printk("lsi_80227_init\n");
/* restart auto-negotiation */
- mdio_write(dev, phy_addr, 0, 0x3200);
-
+ mdio_write(dev, phy_addr, MII_CONTROL,
+ MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_RST_AUTO); // | MII_CNTL_FDX);
mdelay(1);
/* set up LEDs to correct display */
@@ -299,9 +288,9 @@
mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
if (mii_data & MII_STAT_LINK) {
*link = 1;
- mii_data = mdio_read(dev, aup->phy_addr, MII_LSI_STAT);
- if (mii_data & MII_LSI_STAT_SPD) {
- if (mii_data & MII_LSI_STAT_FDX) {
+ mii_data = mdio_read(dev, aup->phy_addr, MII_LSI_PHY_STAT);
+ if (mii_data & MII_LSI_PHY_STAT_SPD) {
+ if (mii_data & MII_LSI_PHY_STAT_FDX) {
*speed = IF_PORT_100BASEFX;
dev->if_port = IF_PORT_100BASEFX;
}
@@ -342,12 +331,303 @@
return 0;
}
+int am79c874_init(struct net_device *dev, int phy_addr)
+{
+ s16 data;
+
+ /* 79c874 has quit resembled bit assignments to BCM5201 */
+ if (au1000_debug > 4)
+ printk("am79c847_init\n");
+
+ /* Stop auto-negotiation */
+ data = mdio_read(dev, phy_addr, MII_CONTROL);
+ mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+ /* Set advertisement to 10/100 and Half/Full duplex
+ * (full capabilities) */
+ data = mdio_read(dev, phy_addr, MII_ANADV);
+ data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+ mdio_write(dev, phy_addr, MII_ANADV, data);
+
+ /* Restart auto-negotiation */
+ data = mdio_read(dev, phy_addr, MII_CONTROL);
+ data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+
+ mdio_write(dev, phy_addr, MII_CONTROL, data);
+
+ if (au1000_debug > 4) dump_mii(dev, phy_addr);
+ return 0;
+}
+
+int am79c874_reset(struct net_device *dev, int phy_addr)
+{
+ s16 mii_control, timeout;
+
+ if (au1000_debug > 4)
+ printk("am79c874_reset\n");
+
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+ mdelay(1);
+ for (timeout = 100; timeout > 0; --timeout) {
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ if ((mii_control & MII_CNTL_RESET) == 0)
+ break;
+ mdelay(1);
+ }
+ if (mii_control & MII_CNTL_RESET) {
+ printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+am79c874_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+ u16 mii_data;
+ struct au1000_private *aup;
+
+ // printk("am79c874_status\n");
+ if (!dev) {
+ printk(KERN_ERR "am79c874_status error: NULL dev\n");
+ return -1;
+ }
+
+ aup = (struct au1000_private *) dev->priv;
+ mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+
+ if (mii_data & MII_STAT_LINK) {
+ *link = 1;
+ mii_data = mdio_read(dev, aup->phy_addr, MII_AMD_PHY_STAT);
+ if (mii_data & MII_AMD_PHY_STAT_SPD) {
+ if (mii_data & MII_AMD_PHY_STAT_FDX) {
+ *speed = IF_PORT_100BASEFX;
+ dev->if_port = IF_PORT_100BASEFX;
+ }
+ else {
+ *speed = IF_PORT_100BASETX;
+ dev->if_port = IF_PORT_100BASETX;
+ }
+ }
+ else {
+ *speed = IF_PORT_10BASET;
+ dev->if_port = IF_PORT_10BASET;
+ }
+
+ }
+ else {
+ *link = 0;
+ *speed = 0;
+ dev->if_port = IF_PORT_UNKNOWN;
+ }
+ return 0;
+}
+
+int lxt971a_init(struct net_device *dev, int phy_addr)
+{
+ if (au1000_debug > 4)
+ printk("lxt971a_init\n");
+
+ /* restart auto-negotiation */
+ mdio_write(dev, phy_addr, MII_CONTROL,
+ MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_RST_AUTO | MII_CNTL_FDX);
+
+ /* set up LEDs to correct display */
+ mdio_write(dev, phy_addr, 20, 0x0422);
+
+ if (au1000_debug > 4)
+ dump_mii(dev, phy_addr);
+ return 0;
+}
+
+int lxt971a_reset(struct net_device *dev, int phy_addr)
+{
+ s16 mii_control, timeout;
+
+ if (au1000_debug > 4) {
+ printk("lxt971a_reset\n");
+ dump_mii(dev, phy_addr);
+ }
+
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+ mdelay(1);
+ for (timeout = 100; timeout > 0; --timeout) {
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ if ((mii_control & MII_CNTL_RESET) == 0)
+ break;
+ mdelay(1);
+ }
+ if (mii_control & MII_CNTL_RESET) {
+ printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+lxt971a_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+ u16 mii_data;
+ struct au1000_private *aup;
+
+ if (!dev) {
+ printk(KERN_ERR "lxt971a_status error: NULL dev\n");
+ return -1;
+ }
+ aup = (struct au1000_private *) dev->priv;
+
+ mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+ if (mii_data & MII_STAT_LINK) {
+ *link = 1;
+ mii_data = mdio_read(dev, aup->phy_addr, MII_INTEL_PHY_STAT);
+ if (mii_data & MII_INTEL_PHY_STAT_SPD) {
+ if (mii_data & MII_INTEL_PHY_STAT_FDX) {
+ *speed = IF_PORT_100BASEFX;
+ dev->if_port = IF_PORT_100BASEFX;
+ }
+ else {
+ *speed = IF_PORT_100BASETX;
+ dev->if_port = IF_PORT_100BASETX;
+ }
+ }
+ else {
+ *speed = IF_PORT_10BASET;
+ dev->if_port = IF_PORT_10BASET;
+ }
+
+ }
+ else {
+ *link = 0;
+ *speed = 0;
+ dev->if_port = IF_PORT_UNKNOWN;
+ }
+ return 0;
+}
+
+int ks8995m_init(struct net_device *dev, int phy_addr)
+{
+ s16 data;
+
+// printk("ks8995m_init\n");
+ /* Stop auto-negotiation */
+ data = mdio_read(dev, phy_addr, MII_CONTROL);
+ mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+ /* Set advertisement to 10/100 and Half/Full duplex
+ * (full capabilities) */
+ data = mdio_read(dev, phy_addr, MII_ANADV);
+ data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+ mdio_write(dev, phy_addr, MII_ANADV, data);
+
+ /* Restart auto-negotiation */
+ data = mdio_read(dev, phy_addr, MII_CONTROL);
+ data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+ mdio_write(dev, phy_addr, MII_CONTROL, data);
+
+ if (au1000_debug > 4) dump_mii(dev, phy_addr);
+
+ return 0;
+}
+
+int ks8995m_reset(struct net_device *dev, int phy_addr)
+{
+ s16 mii_control, timeout;
+
+// printk("ks8995m_reset\n");
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+ mdelay(1);
+ for (timeout = 100; timeout > 0; --timeout) {
+ mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+ if ((mii_control & MII_CNTL_RESET) == 0)
+ break;
+ mdelay(1);
+ }
+ if (mii_control & MII_CNTL_RESET) {
+ printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+ return -1;
+ }
+ return 0;
+}
+
+int ks8995m_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+ u16 mii_data;
+ struct au1000_private *aup;
+
+ if (!dev) {
+ printk(KERN_ERR "ks8995m_status error: NULL dev\n");
+ return -1;
+ }
+ aup = (struct au1000_private *) dev->priv;
+
+ mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+ if (mii_data & MII_STAT_LINK) {
+ *link = 1;
+ mii_data = mdio_read(dev, aup->phy_addr, MII_AUX_CNTRL);
+ if (mii_data & MII_AUX_100) {
+ if (mii_data & MII_AUX_FDX) {
+ *speed = IF_PORT_100BASEFX;
+ dev->if_port = IF_PORT_100BASEFX;
+ }
+ else {
+ *speed = IF_PORT_100BASETX;
+ dev->if_port = IF_PORT_100BASETX;
+ }
+ }
+ else {
+ *speed = IF_PORT_10BASET;
+ dev->if_port = IF_PORT_10BASET;
+ }
+
+ }
+ else {
+ *link = 0;
+ *speed = 0;
+ dev->if_port = IF_PORT_UNKNOWN;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_MIPS_BOSPORUS
+int stub_init(struct net_device *dev, int phy_addr)
+{
+ //printk("PHY stub_init\n");
+ return 0;
+}
+
+int stub_reset(struct net_device *dev, int phy_addr)
+{
+ //printk("PHY stub_reset\n");
+ return 0;
+}
+
+int
+stub_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+ //printk("PHY stub_status\n");
+ *link = 1;
+ /* hmmm, revisit */
+ *speed = IF_PORT_100BASEFX;
+ dev->if_port = IF_PORT_100BASEFX;
+ return 0;
+}
+#endif
+
struct phy_ops bcm_5201_ops = {
bcm_5201_init,
bcm_5201_reset,
bcm_5201_status,
};
+struct phy_ops am79c874_ops = {
+ am79c874_init,
+ am79c874_reset,
+ am79c874_status,
+};
+
struct phy_ops am79c901_ops = {
am79c901_init,
am79c901_reset,
@@ -360,26 +640,82 @@
lsi_80227_status,
};
+struct phy_ops lxt971a_ops = {
+ lxt971a_init,
+ lxt971a_reset,
+ lxt971a_status,
+};
+
+struct phy_ops ks8995m_ops = {
+ ks8995m_init,
+ ks8995m_reset,
+ ks8995m_status,
+};
+
+#ifdef CONFIG_MIPS_BOSPORUS
+struct phy_ops stub_ops = {
+ stub_init,
+ stub_reset,
+ stub_status,
+};
+#endif
+
static struct mii_chip_info {
const char * name;
u16 phy_id0;
u16 phy_id1;
struct phy_ops *phy_ops;
+ int dual_phy;
} mii_chip_table[] = {
- {"Broadcom BCM5201 10/100 BaseT PHY", 0x0040, 0x6212, &bcm_5201_ops },
- {"AMD 79C901 HomePNA PHY", 0x0000, 0x35c8, &am79c901_ops },
- {"LSI 80227 10/100 BaseT PHY", 0x0016, 0xf840, &lsi_80227_ops },
- {"Broadcom BCM5221 10/100 BaseT PHY", 0x0040, 0x61e4, &bcm_5201_ops },
+ {"Broadcom BCM5201 10/100 BaseT PHY",0x0040,0x6212, &bcm_5201_ops,0},
+ {"Broadcom BCM5221 10/100 BaseT PHY",0x0040,0x61e4, &bcm_5201_ops,0},
+ {"Broadcom BCM5222 10/100 BaseT PHY",0x0040,0x6322, &bcm_5201_ops,1},
+ {"AMD 79C901 HomePNA PHY",0x0000,0x35c8, &am79c901_ops,0},
+ {"AMD 79C874 10/100 BaseT PHY",0x0022,0x561b, &am79c874_ops,0},
+ {"LSI 80227 10/100 BaseT PHY",0x0016,0xf840, &lsi_80227_ops,0},
+ {"Intel LXT971A Dual Speed PHY",0x0013,0x78e2, &lxt971a_ops,0},
+ {"Kendin KS8995M 10/100 BaseT PHY",0x0022,0x1450, &ks8995m_ops,0},
+#ifdef CONFIG_MIPS_BOSPORUS
+ {"Stub", 0x1234, 0x5678, &stub_ops },
+#endif
{0,},
};
static int mdio_read(struct net_device *dev, int phy_id, int reg)
{
struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ volatile u32 *mii_control_reg;
+ volatile u32 *mii_data_reg;
u32 timedout = 20;
u32 mii_control;
- while (aup->mac->mii_control & MAC_MII_BUSY) {
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ /* First time we probe, it's for the mac0 phy.
+ * Since we haven't determined yet that we have a dual phy,
+ * aup->mii->mii_control_reg won't be setup and we'll
+ * default to the else statement.
+ * By the time we probe for the mac1 phy, the mii_control_reg
+ * will be setup to be the address of the mac0 phy control since
+ * both phys are controlled through mac0.
+ */
+ if (aup->mii && aup->mii->mii_control_reg) {
+ mii_control_reg = aup->mii->mii_control_reg;
+ mii_data_reg = aup->mii->mii_data_reg;
+ }
+ else if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+ /* assume both phys are controlled through mac0 */
+ mii_control_reg = au_macs[0]->mii->mii_control_reg;
+ mii_data_reg = au_macs[0]->mii->mii_data_reg;
+ }
+ else
+ #endif
+ {
+ /* default control and data reg addresses */
+ mii_control_reg = &aup->mac->mii_control;
+ mii_data_reg = &aup->mac->mii_data;
+ }
+
+ while (*mii_control_reg & MAC_MII_BUSY) {
mdelay(1);
if (--timedout == 0) {
printk(KERN_ERR "%s: read_MII busy timeout!!\n",
@@ -391,10 +727,10 @@
mii_control = MAC_SET_MII_SELECT_REG(reg) |
MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_READ;
- aup->mac->mii_control = mii_control;
+ *mii_control_reg = mii_control;
timedout = 20;
- while (aup->mac->mii_control & MAC_MII_BUSY) {
+ while (*mii_control_reg & MAC_MII_BUSY) {
mdelay(1);
if (--timedout == 0) {
printk(KERN_ERR "%s: mdio_read busy timeout!!\n",
@@ -402,16 +738,36 @@
return -1;
}
}
- return (int)aup->mac->mii_data;
+ return (int)*mii_data_reg;
}
static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 value)
{
struct au1000_private *aup = (struct au1000_private *) dev->priv;
+ volatile u32 *mii_control_reg;
+ volatile u32 *mii_data_reg;
u32 timedout = 20;
u32 mii_control;
- while (aup->mac->mii_control & MAC_MII_BUSY) {
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ if (aup->mii && aup->mii->mii_control_reg) {
+ mii_control_reg = aup->mii->mii_control_reg;
+ mii_data_reg = aup->mii->mii_data_reg;
+ }
+ else if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+ /* assume both phys are controlled through mac0 */
+ mii_control_reg = au_macs[0]->mii->mii_control_reg;
+ mii_data_reg = au_macs[0]->mii->mii_data_reg;
+ }
+ else
+ #endif
+ {
+ /* default control and data reg addresses */
+ mii_control_reg = &aup->mac->mii_control;
+ mii_data_reg = &aup->mac->mii_data;
+ }
+
+ while (*mii_control_reg & MAC_MII_BUSY) {
mdelay(1);
if (--timedout == 0) {
printk(KERN_ERR "%s: mdio_write busy timeout!!\n",
@@ -423,8 +779,8 @@
mii_control = MAC_SET_MII_SELECT_REG(reg) |
MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_WRITE;
- aup->mac->mii_data = value;
- aup->mac->mii_control = mii_control;
+ *mii_data_reg = value;
+ *mii_control_reg = mii_control;
}
@@ -442,12 +798,13 @@
}
}
-static int __init mii_probe (struct net_device * dev)
+static int mii_probe (struct net_device * dev)
{
struct au1000_private *aup = (struct au1000_private *) dev->priv;
int phy_addr;
-
- aup->mii = NULL;
+#ifdef CONFIG_MIPS_BOSPORUS
+ int phy_found=0;
+#endif
/* search for total of 32 possible mii phy addresses */
for (phy_addr = 0; phy_addr < 32; phy_addr++) {
@@ -455,6 +812,14 @@
u16 phy_id0, phy_id1;
int i;
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ /* Mask the already found phy, try next one */
+ if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+ if (au_macs[0]->phy_addr == phy_addr)
+ continue;
+ }
+ #endif
+
mii_status = mdio_read(dev, phy_addr, MII_STATUS);
if (mii_status == 0xffff || mii_status == 0x0000)
/* the mii is not accessable, try next one */
@@ -467,6 +832,65 @@
for (i = 0; mii_chip_table[i].phy_id1; i++) {
if (phy_id0 == mii_chip_table[i].phy_id0 &&
phy_id1 == mii_chip_table[i].phy_id1) {
+ struct mii_phy * mii_phy = aup->mii;
+
+ printk(KERN_INFO "%s: %s at phy address %d\n",
+ dev->name, mii_chip_table[i].name,
+ phy_addr);
+#ifdef CONFIG_MIPS_BOSPORUS
+ phy_found = 1;
+#endif
+ mii_phy->chip_info = mii_chip_table+i;
+ aup->phy_addr = phy_addr;
+ aup->phy_ops = mii_chip_table[i].phy_ops;
+ aup->phy_ops->phy_init(dev,phy_addr);
+
+ // Check for dual-phy and then store required
+ // values and set indicators. We need to do
+ // this now since mdio_{read,write} need the
+ // control and data register addresses.
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ if ( mii_chip_table[i].dual_phy) {
+
+ /* assume both phys are controlled
+ * through MAC0. Board specific? */
+
+ /* sanity check */
+ if (!au_macs[0] || !au_macs[0]->mii)
+ return -1;
+ aup->mii->mii_control_reg = (u32 *)
+ &au_macs[0]->mac->mii_control;
+ aup->mii->mii_data_reg = (u32 *)
+ &au_macs[0]->mac->mii_data;
+ }
+ #endif
+ goto found;
+ }
+ }
+ }
+found:
+
+#ifdef CONFIG_MIPS_BOSPORUS
+ /* This is a workaround for the Micrel/Kendin 5 port switch
+ The second MAC doesn't see a PHY connected... so we need to
+ trick it into thinking we have one.
+
+ If this kernel is run on another Au1500 development board
+ the stub will be found as well as the actual PHY. However,
+ the last found PHY will be used... usually at Addr 31 (Db1500).
+ */
+ if ( (!phy_found) )
+ {
+ u16 phy_id0, phy_id1;
+ int i;
+
+ phy_id0 = 0x1234;
+ phy_id1 = 0x5678;
+
+ /* search our mii table for the current mii */
+ for (i = 0; mii_chip_table[i].phy_id1; i++) {
+ if (phy_id0 == mii_chip_table[i].phy_id0 &&
+ phy_id1 == mii_chip_table[i].phy_id1) {
struct mii_phy * mii_phy;
printk(KERN_INFO "%s: %s at phy address %d\n",
@@ -476,31 +900,39 @@
GFP_KERNEL);
if (mii_phy) {
mii_phy->chip_info = mii_chip_table+i;
- mii_phy->phy_addr = phy_addr;
+ aup->phy_addr = phy_addr;
mii_phy->next = aup->mii;
aup->phy_ops =
mii_chip_table[i].phy_ops;
aup->mii = mii_phy;
aup->phy_ops->phy_init(dev,phy_addr);
} else {
- printk(KERN_ERR "%s: out of memory\n",
+ printk(KERN_ERR "%s: out of memory\n",
dev->name);
return -1;
}
- /* the current mii is on our mii_info_table,
- try next address */
+ mii_phy->chip_info = mii_chip_table+i;
+ aup->phy_addr = phy_addr;
+ aup->phy_ops = mii_chip_table[i].phy_ops;
+ aup->phy_ops->phy_init(dev,phy_addr);
break;
}
}
}
+ if (aup->mac_id == 0) {
+ /* the Bosporus phy responds to addresses 0-5 but
+ * 5 is the correct one.
+ */
+ aup->phy_addr = 5;
+ }
+#endif
if (aup->mii == NULL) {
- printk(KERN_ERR "%s: No MII transceivers found!\n", dev->name);
+ printk(KERN_ERR "%s: Au1x No MII transceivers found!\n",
+ dev->name);
return -1;
}
- /* use last PHY */
- aup->phy_addr = aup->mii->phy_addr;
printk(KERN_INFO "%s: Using %s as default\n",
dev->name, aup->mii->chip_info->name);
@@ -521,7 +953,6 @@
if (pDB) {
aup->pDBfree = pDB->pnext;
}
- //printk("GetFreeDB: %x\n", pDB);
return pDB;
}
@@ -587,6 +1018,7 @@
static void reset_mac(struct net_device *dev)
{
+ int i;
u32 flags;
struct au1000_private *aup = (struct au1000_private *) dev->priv;
@@ -597,11 +1029,27 @@
spin_lock_irqsave(&aup->lock, flags);
del_timer(&aup->timer);
hard_stop(dev);
- *aup->enable = MAC_EN_CLOCK_ENABLE;
- au_sync_delay(2);
- *aup->enable = 0;
- au_sync_delay(2);
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ if (aup->mac_id != 0) {
+ #endif
+ /* If BCM5222, we can't leave MAC0 in reset because then
+ * we can't access the dual phy for ETH1 */
+ *aup->enable = MAC_EN_CLOCK_ENABLE;
+ au_sync_delay(2);
+ *aup->enable = 0;
+ au_sync_delay(2);
+ #ifdef CONFIG_BCM5222_DUAL_PHY
+ }
+ #endif
aup->tx_full = 0;
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ /* reset control bits */
+ aup->rx_dma_ring[i]->buff_stat &= ~0xf;
+ }
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ /* reset control bits */
+ aup->tx_dma_ring[i]->buff_stat &= ~0xf;
+ }
spin_unlock_irqrestore(&aup->lock, flags);
}
@@ -616,95 +1064,110 @@
{
int i;
- for (i=0; i<NUM_RX_DMA; i++) {
+ for (i = 0; i < NUM_RX_DMA; i++) {
aup->rx_dma_ring[i] =
(volatile rx_dma_t *) (rx_base + sizeof(rx_dma_t)*i);
}
- for (i=0; i<NUM_TX_DMA; i++) {
+ for (i = 0; i < NUM_TX_DMA; i++) {
aup->tx_dma_ring[i] =
(volatile tx_dma_t *) (tx_base + sizeof(tx_dma_t)*i);
}
}
+static struct {
+ int port;
+ u32 base_addr;
+ int irq;
+ struct net_device *dev;
+} iflist[2];
+
+static int num_ifs;
+
+/*
+ * Setup the base address and interupt of the Au1xxx ethernet macs
+ * based on cpu type and whether the interface is enabled in sys_pinfunc
+ * register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0.
+ */
static int __init au1000_init_module(void)
{
- int i;
- int prid;
- int base_addr, irq;
-
- prid = read_32bit_cp0_register(CP0_PRID);
- for (i=0; i<NUM_INTERFACES; i++) {
- if ( (prid & 0xffff0000) == 0x00030000 ) {
- base_addr = au1000_iflist[i].port;
- irq = au1000_iflist[i].irq;
- } else if ( (prid & 0xffff0000) == 0x01030000 ) {
- base_addr = au1500_iflist[i].port;
- irq = au1500_iflist[i].irq;
- } else if ( (prid & 0xffff0000) == 0x02030000 ) {
- base_addr = au1100_iflist[i].port;
- irq = au1100_iflist[i].irq;
- } else {
- printk(KERN_ERR "au1000 eth: unknown Processor ID\n");
- return -ENODEV;
- }
- // check for valid entries, au1100 only has one entry
- if (base_addr && irq) {
- if (au1000_probe1(NULL, base_addr, irq, i) != 0) {
- return -ENODEV;
- }
+ struct cpuinfo_mips *c = ¤t_cpu_data;
+ int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
+ struct net_device *dev;
+ int i, found_one = 0;
+
+ iflist[0].irq = AU1000_ETH0_IRQ;
+ iflist[1].irq = AU1000_ETH1_IRQ;
+ switch (c->cputype) {
+ case CPU_AU1000:
+ num_ifs = 2 - ni;
+ iflist[0].base_addr = AU1000_ETH0_BASE;
+ iflist[1].base_addr = AU1000_ETH1_BASE;
+ break;
+ case CPU_AU1100:
+ num_ifs = 1 - ni;
+ iflist[0].base_addr = AU1000_ETH0_BASE;
+ break;
+ case CPU_AU1500:
+ num_ifs = 2 - ni;
+ iflist[0].base_addr = AU1500_ETH0_BASE;
+ iflist[1].base_addr = AU1500_ETH1_BASE;
+ break;
+ default:
+ num_ifs = 0;
}
+ for(i = 0; i < num_ifs; i++) {
+ dev = au1000_probe(iflist[i].base_addr, iflist[i].irq, i);
+ iflist[i].dev = dev;
+ if (dev)
+ found_one++;
}
+ if (!found_one)
+ return -ENODEV;
return 0;
}
-static int __init
-au1000_probe1(struct net_device *dev, long ioaddr, int irq, int port_num)
+static struct net_device *
+au1000_probe(u32 ioaddr, int irq, int port_num)
{
static unsigned version_printed = 0;
struct au1000_private *aup = NULL;
- int i, retval = 0;
+ struct net_device *dev = NULL;
db_dest_t *pDB, *pDBfree;
char *pmac, *argptr;
char ethaddr[6];
+ int i, err;
- if (!request_region(ioaddr, MAC_IOSIZE, "Au1000 ENET")) {
- return -ENODEV;
- }
+ if (!request_region(ioaddr, MAC_IOSIZE, "Au1x00 ENET"))
+ return NULL;
- if (version_printed++ == 0) printk(version);
+ if (version_printed++ == 0)
+ printk(version);
+ dev = alloc_etherdev(sizeof(struct au1000_private));
if (!dev) {
- dev = init_etherdev(0, sizeof(struct au1000_private));
+ printk (KERN_ERR "au1000 eth: alloc_etherdev failed\n");
+ return NULL;
}
- if (!dev) {
- printk (KERN_ERR "au1000 eth: init_etherdev failed\n");
- return -ENODEV;
+
+ if ((err = register_netdev(dev))) {
+ printk(KERN_ERR "Au1x_eth Cannot register net device err %d\n",
+ err);
+ kfree(dev);
+ return NULL;
}
- printk("%s: Au1xxx ethernet found at 0x%lx, irq %d\n",
+ printk("%s: Au1x Ethernet found at 0x%x, irq %d\n",
dev->name, ioaddr, irq);
- /* Initialize our private structure */
- if (dev->priv == NULL) {
- aup = (struct au1000_private *)
- kmalloc(sizeof(*aup), GFP_KERNEL);
- if (aup == NULL) {
- retval = -ENOMEM;
- goto free_region;
- }
- dev->priv = aup;
- }
-
aup = dev->priv;
- memset(aup, 0, sizeof(*aup));
-
/* Allocate the data buffers */
aup->vaddr = (u32)dma_alloc(MAX_BUF_SIZE *
(NUM_TX_BUFFS+NUM_RX_BUFFS), &aup->dma_addr);
if (!aup->vaddr) {
- retval = -ENOMEM;
- goto free_region;
+ kfree(dev);
+ release_region(ioaddr, MAC_IOSIZE);
+ return NULL;
}
/* aup->mac is the base address of the MAC's registers */
@@ -737,7 +1200,10 @@
((unsigned long)AU1500_MAC0_ENABLE);
memcpy(dev->dev_addr, au1000_mac_addr, sizeof(dev->dev_addr));
setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
+ aup->mac_id = 0;
+ au_macs[0] = aup;
break;
+
case AU1000_ETH1_BASE:
case AU1500_ETH1_BASE:
if (ioaddr == AU1000_ETH1_BASE)
@@ -749,15 +1215,16 @@
memcpy(dev->dev_addr, au1000_mac_addr, sizeof(dev->dev_addr));
dev->dev_addr[4] += 0x10;
setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
+ aup->mac_id = 1;
+ au_macs[1] = aup;
break;
+
default:
printk(KERN_ERR "%s: bad ioaddr\n", dev->name);
break;
}
- aup->phy_addr = PHY_ADDRESS;
-
/* bring the device out of reset, otherwise probing the mii
* will hang */
*aup->enable = MAC_EN_CLOCK_ENABLE;
@@ -766,14 +1233,22 @@
MAC_EN_RESET2 | MAC_EN_CLOCK_ENABLE;
au_sync_delay(2);
+ aup->mii = kmalloc(sizeof(struct mii_phy), GFP_KERNEL);
+ if (!aup->mii) {
+ printk(KERN_ERR "%s: out of memory\n", dev->name);
+ goto err_out;
+ }
+ aup->mii->mii_control_reg = 0;
+ aup->mii->mii_data_reg = 0;
+
if (mii_probe(dev) != 0) {
- goto free_region;
+ goto err_out;
}
pDBfree = NULL;
/* setup the data buffer descriptors and attach a buffer to each one */
pDB = aup->db;
- for (i=0; i<(NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
+ for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
pDB->pnext = pDBfree;
pDBfree = pDB;
pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
@@ -782,15 +1257,19 @@
}
aup->pDBfree = pDBfree;
- for (i=0; i<NUM_RX_DMA; i++) {
+ for (i = 0; i < NUM_RX_DMA; i++) {
pDB = GetFreeDB(aup);
- if (!pDB) goto free_region;
+ if (!pDB) {
+ goto err_out;
+ }
aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
aup->rx_db_inuse[i] = pDB;
}
- for (i=0; i<NUM_TX_DMA; i++) {
+ for (i = 0; i < NUM_TX_DMA; i++) {
pDB = GetFreeDB(aup);
- if (!pDB) goto free_region;
+ if (!pDB) {
+ goto err_out;
+ }
aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
aup->tx_dma_ring[i]->len = 0;
aup->tx_db_inuse[i] = pDB;
@@ -809,32 +1288,36 @@
dev->tx_timeout = au1000_tx_timeout;
dev->watchdog_timeo = ETH_TX_TIMEOUT;
-
- /* Fill in the fields of the device structure with ethernet values. */
- ether_setup(dev);
-
/*
* The boot code uses the ethernet controller, so reset it to start
* fresh. au1000_init() expects that the device is in reset state.
*/
reset_mac(dev);
- return 0;
-free_region:
- release_region(ioaddr, MAC_IOSIZE);
+ return dev;
+
+err_out:
+ /* here we should have a valid dev plus aup-> register addresses
+ * so we can reset the mac properly.*/
+ reset_mac(dev);
+ if (aup->mii)
+ kfree(aup->mii);
+ for (i = 0; i < NUM_RX_DMA; i++) {
+ if (aup->rx_db_inuse[i])
+ ReleaseDB(aup, aup->rx_db_inuse[i]);
+ }
+ for (i = 0; i < NUM_TX_DMA; i++) {
+ if (aup->tx_db_inuse[i])
+ ReleaseDB(aup, aup->tx_db_inuse[i]);
+ }
+ dma_free((void *)aup->vaddr, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS+NUM_RX_BUFFS));
unregister_netdev(dev);
- if (aup->vaddr)
- dma_free((void *)aup->vaddr,
- MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS));
- if (dev->priv != NULL)
- kfree(dev->priv);
- printk(KERN_ERR "%s: au1000_probe1 failed. Returns %d\n",
- dev->name, retval);
kfree(dev);
- return retval;
+ release_region(ioaddr, MAC_IOSIZE);
+ return NULL;
}
-
/*
* Initialize the interface.
*
@@ -852,7 +1335,8 @@
u32 control;
u16 link, speed;
- if (au1000_debug > 4) printk("%s: au1000_init\n", dev->name);
+ if (au1000_debug > 4)
+ printk("%s: au1000_init\n", dev->name);
spin_lock_irqsave(&aup->lock, flags);
@@ -872,7 +1356,7 @@
aup->mac->mac_addr_low = dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
dev->dev_addr[1]<<8 | dev->dev_addr[0];
- for (i=0; i<NUM_RX_DMA; i++) {
+ for (i = 0; i < NUM_RX_DMA; i++) {
aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
}
au_sync();
@@ -992,24 +1476,48 @@
if (au1000_debug > 4)
printk("%s: close: dev=%p\n", dev->name, dev);
+ reset_mac(dev);
+
spin_lock_irqsave(&aup->lock, flags);
/* stop the device */
- if (netif_device_present(dev)) {
- netif_stop_queue(dev);
- }
+ netif_stop_queue(dev);
/* disable the interrupt */
free_irq(dev->irq, dev);
spin_unlock_irqrestore(&aup->lock, flags);
- reset_mac(dev);
MOD_DEC_USE_COUNT;
return 0;
}
static void __exit au1000_cleanup_module(void)
{
+ int i, j;
+ struct net_device *dev;
+ struct au1000_private *aup;
+
+ for (i = 0; i < num_ifs; i++) {
+ dev = iflist[i].dev;
+ if (dev) {
+ aup = (struct au1000_private *) dev->priv;
+ unregister_netdev(dev);
+ if (aup->mii)
+ kfree(aup->mii);
+ for (j = 0; j < NUM_RX_DMA; j++) {
+ if (aup->rx_db_inuse[j])
+ ReleaseDB(aup, aup->rx_db_inuse[j]);
+ }
+ for (j = 0; j < NUM_TX_DMA; j++) {
+ if (aup->tx_db_inuse[j])
+ ReleaseDB(aup, aup->tx_db_inuse[j]);
+ }
+ dma_free((void *)aup->vaddr, MAX_BUF_SIZE *
+ (NUM_TX_BUFFS+NUM_RX_BUFFS));
+ kfree(dev);
+ release_region(iflist[i].base_addr, MAC_IOSIZE);
+ }
+ }
}
@@ -1081,7 +1589,7 @@
db_dest_t *pDB;
int i;
- if (au1000_debug > 4)
+ if (au1000_debug > 5)
printk("%s: tx: aup %x len=%d, data=%p, head %d\n",
dev->name, (unsigned)aup, skb->len,
skb->data, aup->tx_head);
@@ -1106,11 +1614,11 @@
pDB = aup->tx_db_inuse[aup->tx_head];
memcpy((void *)pDB->vaddr, skb->data, skb->len);
- if (skb->len < MAC_MIN_PKT_SIZE) {
- for (i=skb->len; i<MAC_MIN_PKT_SIZE; i++) {
+ if (skb->len < ETH_ZLEN) {
+ for (i=skb->len; i<ETH_ZLEN; i++) {
((char *)pDB->vaddr)[i] = 0;
}
- ptxd->len = MAC_MIN_PKT_SIZE;
+ ptxd->len = ETH_ZLEN;
}
else
ptxd->len = skb->len;
@@ -1159,8 +1667,9 @@
volatile rx_dma_t *prxd;
u32 buff_stat, status;
db_dest_t *pDB;
+ u32 frmlen;
- if (au1000_debug > 4)
+ if (au1000_debug > 5)
printk("%s: au1000_rx head %d\n", dev->name, aup->rx_head);
prxd = aup->rx_dma_ring[aup->rx_head];
@@ -1172,7 +1681,9 @@
if (!(status & RX_ERROR)) {
/* good frame */
- skb = dev_alloc_skb((status & RX_FRAME_LEN_MASK) + 2);
+ frmlen = (status & RX_FRAME_LEN_MASK);
+ frmlen -= 4; /* Remove FCS */
+ skb = dev_alloc_skb(frmlen + 2);
if (skb == NULL) {
printk(KERN_ERR
"%s: Memory squeeze, dropping packet.\n",
@@ -1182,9 +1693,9 @@
}
skb->dev = dev;
skb_reserve(skb, 2); /* 16 byte IP header align */
- eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr,
- status & RX_FRAME_LEN_MASK, 0);
- skb_put(skb, status & RX_FRAME_LEN_MASK);
+ eth_copy_and_sum(skb,
+ (unsigned char *)pDB->vaddr, frmlen, 0);
+ skb_put(skb, frmlen);
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb); /* pass the packet to upper layers */
}
@@ -1236,8 +1747,11 @@
printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name);
return;
}
- au1000_tx_ack(dev);
+
+ /* Handle RX interrupts first to minimize chance of overrun */
+
au1000_rx(dev);
+ au1000_tx_ack(dev);
}
@@ -1250,6 +1764,8 @@
printk(KERN_ERR "%s: au1000_tx_timeout: dev=%p\n", dev->name, dev);
reset_mac(dev);
au1000_init(dev);
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
}
@@ -1305,27 +1821,19 @@
static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
- u16 *data = (u16 *)&rq->ifr_data;
+ //u16 *data = (u16 *)&rq->ifr_data;
/* fixme */
- switch (cmd) {
- case SIOCGMIIPHY: /* Get the address of the PHY in use. */
- case SIOCDEVPRIVATE: /* binary compat, remove in 2.5 */
- data[0] = PHY_ADDRESS;
-
- case SIOCGMIIREG: /* Read the specified MII register. */
- case SIOCDEVPRIVATE+1: /* binary compat, remove in 2.5 */
+ switch(cmd) {
+ case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
+ //data[0] = PHY_ADDRESS;
+ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
//data[3] = mdio_read(ioaddr, data[0], data[1]);
return 0;
-
- case SIOCSMIIREG: /* Write the specified MII register */
- case SIOCDEVPRIVATE+2: /* binary compat, remove in 2.5 */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
+ case SIOCDEVPRIVATE+2: /* Write the specified MII register */
//mdio_write(ioaddr, data[0], data[1], data[2]);
return 0;
-
- default:
+ default:
return -EOPNOTSUPP;
}
}
@@ -1389,7 +1897,6 @@
/* set Speed to 100Mbps, Half Duplex */
/* disable auto negotiation and enable 100MBit Mode */
control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
- printk("read control %x\n", control);
control &= ~(MII_CNTL_AUTO | MII_CNTL_FDX);
control |= MII_CNTL_F100;
mdio_write(dev, aup->phy_addr, MII_CONTROL, control);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)