patch-2.2.18 linux/drivers/net/gmac.c
Next file: linux/drivers/net/gmac.h
Previous file: linux/drivers/net/fc/iph5526.c
Back to the patch index
Back to the overall index
- Lines: 544
- Date:
Wed Nov 8 23:00:35 2000
- Orig file:
v2.2.17/drivers/net/gmac.c
- Orig date:
Sat Sep 9 18:42:38 2000
diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/drivers/net/gmac.c linux/drivers/net/gmac.c
@@ -6,6 +6,13 @@
*
* portions based on sunhme.c by David S. Miller
*
+ * Changes:
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/06/2000
+ * - check init_etherdev return in gmac_probe1
+ * BenH <bh40@calva.net> - 03/09/2000
+ * - Add support for new PHYs
+ * - Add some PowerBook sleep code
+ *
*/
#include <linux/module.h>
@@ -21,18 +28,25 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/timer.h>
+#include <linux/init.h>
#include <linux/pci.h>
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/pgtable.h>
-#include <asm/bitops.h>
+#include <asm/feature.h>
+#include <asm/keylargo.h>
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/adb.h>
+#include <asm/pmu.h>
+#include <asm/irq.h>
+#endif
#include "gmac.h"
#define DEBUG_PHY
-/* Driver version 1.1, kernel 2.2.x */
-#define GMAC_VERSION "v1.1k2"
+/* Driver version 1.3, kernel 2.2.x */
+#define GMAC_VERSION "v1.3k2"
static unsigned char dummy_buf[RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN];
static struct device *gmacs = NULL;
@@ -45,9 +59,12 @@
static void mii_interrupt(struct gmac *gm);
static int mii_lookup_and_reset(struct gmac *gm);
static void mii_setup_phy(struct gmac *gm);
+static int mii_do_reset_phy(struct gmac *gm, int phy_addr);
+static void mii_init_BCM5400(struct gmac *gm);
static void gmac_set_power(struct gmac *gm, int power_up);
static int gmac_powerup_and_reset(struct device *dev);
+static void gmac_set_gigabit_mode(struct gmac *gm, int gigabit);
static void gmac_set_duplex_mode(struct gmac *gm, int full_duplex);
static void gmac_mac_init(struct gmac *gm, unsigned char *mac_addr);
static void gmac_init_rings(struct gmac *gm, int from_irq);
@@ -67,6 +84,13 @@
extern int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr,
unsigned char *devfn_ptr);
+#ifdef CONFIG_PMAC_PBOOK
+int gmac_sleep_notify(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier gmac_sleep_notifier = {
+ gmac_sleep_notify, SLEEP_LEVEL_NET,
+};
+#endif
+
/* Stuff for talking to the physical-layer chip */
static int
mii_read(struct gmac *gm, int phy, int r)
@@ -136,6 +160,18 @@
udelay(GM_MIF_POLL_DELAY);
}
+/* Link modes of the BCM5400 PHY */
+static int phy_BCM5400_link_table[8][3] = {
+ { 0, 0, 0 }, /* No link */
+ { 0, 0, 0 }, /* 10BT Half Duplex */
+ { 1, 0, 0 }, /* 10BT Full Duplex */
+ { 0, 1, 0 }, /* 100BT Half Duplex */
+ { 0, 1, 0 }, /* 100BT Half Duplex */
+ { 1, 1, 0 }, /* 100BT Full Duplex*/
+ { 1, 0, 1 }, /* 1000BT */
+ { 1, 0, 1 }, /* 1000BT */
+};
+
static void
mii_interrupt(struct gmac *gm)
{
@@ -150,8 +186,9 @@
/* We read the Auxilliary Status Summary register */
phy_status = mii_read(gm, gm->phy_addr, MII_SR);
if ((phy_status ^ gm->phy_status) & (MII_SR_ASSC | MII_SR_LKS)) {
- int full_duplex;
- int link_100;
+ int full_duplex = 0;
+ int link_100 = 0;
+ int gigabit = 0;
#ifdef DEBUG_PHY
printk("Link state change, phy_status: 0x%04x\n", phy_status);
#endif
@@ -163,8 +200,9 @@
else
GM_BIC(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN);
- /* Link ? For now we handle only the 5201 PHY */
+ /* Link ? Check for speed and duplex */
if ((phy_status & MII_SR_LKS) && (phy_status & MII_SR_ASSC)) {
+ int restart = 0;
if (gm->phy_type == PHY_B5201) {
int aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5201_AUXCTLSTATUS);
#ifdef DEBUG_PHY
@@ -172,19 +210,41 @@
#endif
full_duplex = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_DUPLEX) != 0);
link_100 = ((aux_stat & MII_BCM5201_AUXCTLSTATUS_SPEED) != 0);
- } else {
- full_duplex = 1;
- link_100 = 1;
+ } else if (gm->phy_type == PHY_B5400) {
+ int aux_stat = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXSTATUS);
+ int link = (aux_stat & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
+ MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT;
+#ifdef DEBUG_PHY
+ printk(" Link up ! BCM5400 aux_stat: 0x%04x (link mode: %d)\n",
+ aux_stat, link);
+#endif
+ full_duplex = phy_BCM5400_link_table[link][0];
+ link_100 = phy_BCM5400_link_table[link][1];
+ gigabit = phy_BCM5400_link_table[link][2];
+ } else if (gm->phy_type == PHY_LXT971) {
+ int stat2 = mii_read(gm, gm->phy_addr, MII_LXT971_STATUS2);
+#ifdef DEBUG_PHY
+ printk(" Link up ! LXT971 stat2: 0x%04x\n", stat2);
+#endif
+ full_duplex = ((stat2 & MII_LXT971_STATUS2_FULLDUPLEX) != 0);
+ link_100 = ((stat2 & MII_LXT971_STATUS2_SPEED) != 0);
}
#ifdef DEBUG_PHY
printk(" full_duplex: %d, speed: %s\n", full_duplex,
- link_100 ? "100" : "10");
+ gigabit ? "1000" : (link_100 ? "100" : "10"));
#endif
+ if (gigabit != gm->gigabit) {
+ gm->gigabit = gigabit;
+ gmac_set_gigabit_mode(gm, gm->gigabit);
+ restart = 1;
+ }
if (full_duplex != gm->full_duplex) {
gm->full_duplex = full_duplex;
gmac_set_duplex_mode(gm, gm->full_duplex);
- gmac_start_dma(gm);
+ restart = 1;
}
+ if (restart)
+ gmac_start_dma(gm);
} else if (!(phy_status & MII_SR_LKS)) {
#ifdef DEBUG_PHY
printk(" Link down !\n");
@@ -194,16 +254,73 @@
}
static int
+mii_do_reset_phy(struct gmac *gm, int phy_addr)
+{
+ int mii_control, timeout;
+
+ mii_control = mii_read(gm, phy_addr, MII_CR);
+ mii_write(gm, phy_addr, MII_CR, mii_control | MII_CR_RST);
+ mdelay(10);
+ for (timeout = 100; timeout > 0; --timeout) {
+ mii_control = mii_read(gm, phy_addr, MII_CR);
+ if (mii_control == -1) {
+ printk(KERN_ERR "%s PHY died after reset !\n",
+ gm->dev->name);
+ return 1;
+ }
+ if ((mii_control & MII_CR_RST) == 0)
+ break;
+ mdelay(10);
+ }
+ if (mii_control & MII_CR_RST) {
+ printk(KERN_ERR "%s PHY reset timeout !\n", gm->dev->name);
+ return 1;
+ }
+ mii_write(gm, phy_addr, MII_CR, mii_control & ~MII_CR_ISOL);
+ return 0;
+}
+
+static void
+mii_init_BCM5400(struct gmac *gm)
+{
+ int data;
+
+ data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL);
+ data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
+ mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);
+
+ data = mii_read(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL);
+ data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
+ mii_write(gm, gm->phy_addr, MII_BCM5400_GB_CONTROL, data);
+
+ mdelay(10);
+ mii_do_reset_phy(gm, 0x1f);
+
+ data = mii_read(gm, 0x1f, MII_BCM5201_MULTIPHY);
+ data |= MII_BCM5201_MULTIPHY_SERIALMODE;
+ mii_write(gm, 0x1f, MII_BCM5201_MULTIPHY, data);
+
+ data = mii_read(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL);
+ data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
+ mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);
+}
+
+static int
mii_lookup_and_reset(struct gmac *gm)
{
- int i, timeout;
- int mii_status, mii_control;
+ int i, mii_status, mii_control;
- /* Find the PHY */
gm->phy_addr = -1;
gm->phy_type = PHY_UNKNOWN;
+
+ /* Hard reset the PHY */
+ feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_ASSERT);
+ mdelay(10);
+ feature_set_gmac_phy_reset(gm->of_node, KL_GPIO_ETH_PHY_RESET_RELEASE);
+ mdelay(10);
- for(i=31; i>0; --i) {
+ /* Find the PHY */
+ for(i=0; i<32; i++) {
mii_control = mii_read(gm, i, MII_CR);
mii_status = mii_read(gm, i, MII_SR);
if (mii_control != -1 && mii_status != -1 &&
@@ -211,29 +328,13 @@
break;
}
gm->phy_addr = i;
- if (gm->phy_addr < 0)
+ if (gm->phy_addr > 31)
return 0;
/* Reset it */
- mii_write(gm, gm->phy_addr, MII_CR, mii_control | MII_CR_RST);
- mdelay(10);
- for (timeout = 100; timeout > 0; --timeout) {
- mii_control = mii_read(gm, gm->phy_addr, MII_CR);
- if (mii_control == -1) {
- printk(KERN_ERR "%s PHY died after reset !\n",
- gm->dev->name);
- goto fail;
- }
- if ((mii_control & MII_CR_RST) == 0)
- break;
- mdelay(10);
- }
- if (mii_control & MII_CR_RST) {
- printk(KERN_ERR "%s PHY reset timeout !\n", gm->dev->name);
+ if (mii_do_reset_phy(gm, gm->phy_addr))
goto fail;
- }
- mii_write(gm, gm->phy_addr, MII_CR, mii_control & ~MII_CR_ISOL);
-
+
/* Read the PHY ID */
gm->phy_id = (mii_read(gm, gm->phy_addr, MII_ID0) << 16) |
mii_read(gm, gm->phy_addr, MII_ID1);
@@ -242,10 +343,15 @@
#endif
if ((gm->phy_id & MII_BCM5400_MASK) == MII_BCM5400_ID) {
gm->phy_type = PHY_B5400;
- printk(KERN_ERR "%s Warning ! Unsupported BCM5400 PHY !\n",
+ printk(KERN_ERR "%s Found Broadcom BCM5400 PHY (Gigabit)\n",
gm->dev->name);
+ mii_init_BCM5400(gm);
} else if ((gm->phy_id & MII_BCM5201_MASK) == MII_BCM5201_ID) {
gm->phy_type = PHY_B5201;
+ printk(KERN_INFO "%s Found Broadcom BCM5201 PHY\n", gm->dev->name);
+ } else if ((gm->phy_id & MII_LXT971_MASK) == MII_LXT971_ID) {
+ gm->phy_type = PHY_LXT971;
+ printk(KERN_INFO "%s Found LevelOne LX971 PHY\n", gm->dev->name);
} else {
printk(KERN_ERR "%s: Warning ! Unknown PHY ID 0x%08x !\n",
gm->dev->name, gm->phy_id);
@@ -288,9 +394,7 @@
gmac_set_power(struct gmac *gm, int power_up)
{
if (power_up) {
- out_le32(gm->sysregs + 0x20/4,
- in_le32(gm->sysregs + 0x20/4) | 0x02000000);
- udelay(20);
+ feature_set_gmac_power(gm->of_node, 1);
if (gm->pci_devfn != 0xff) {
u16 cmd;
@@ -308,9 +412,7 @@
} else {
/* FIXME: Add PHY power down */
gm->phy_type = 0;
- out_le32(gm->sysregs + 0x20/4,
- in_le32(gm->sysregs + 0x20/4) & ~0x02000000);
- udelay(20);
+ feature_set_gmac_power(gm->of_node, 0);
}
}
@@ -357,6 +459,22 @@
}
}
+/* Set the MAC gigabit mode. Side effect: stops Tx MAC */
+static void
+gmac_set_gigabit_mode(struct gmac *gm, int gigabit)
+{
+ /* Stop Tx MAC */
+ GM_BIC(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE);
+ while(GM_IN(GM_MAC_TX_CONFIG) & GM_MAC_TX_CONF_ENABLE)
+ ;
+
+ if (gigabit) {
+ GM_BIS(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE);
+ } else {
+ GM_BIC(GM_MAC_XIF_CONFIG, GM_MAC_XIF_CONF_GMII_MODE);
+ }
+}
+
static void
gmac_mac_init(struct gmac *gm, unsigned char *mac_addr)
{
@@ -388,12 +506,12 @@
(31 << GM_GCONF_TXDMA_LIMIT_SHIFT) |
(31 << GM_GCONF_RXDMA_LIMIT_SHIFT));
GM_OUT(GM_TX_CONF,
- GM_TX_CONF_FIFO_THR_DEFAULT << GM_TX_CONF_FIFO_THR_SHIFT |
+ (GM_TX_CONF_FIFO_THR_DEFAULT << GM_TX_CONF_FIFO_THR_SHIFT) |
NTX_CONF);
-/* 34 byte offset for checksum computation. This works because ip_input() will clear out
- * the skb->csum and skb->ip_summed fields and recompute the csum if IP options are
- * present in the header. 34 == (ethernet header len) + sizeof(struct iphdr)
- */
+ /* 34 byte offset for checksum computation. This works because ip_input() will clear out
+ * the skb->csum and skb->ip_summed fields and recompute the csum if IP options are
+ * present in the header. 34 == (ethernet header len) + sizeof(struct iphdr)
+ */
GM_OUT(GM_RX_CONF,
(RX_OFFSET << GM_RX_CONF_FBYTE_OFF_SHIFT) |
(0x22 << GM_RX_CONF_CHK_START_SHIFT) |
@@ -703,6 +821,65 @@
return 0;
}
+#ifdef CONFIG_PMAC_PBOOK
+int
+gmac_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+ struct gmac *gm;
+ int i;
+
+ /* XXX should handle more than one */
+ if (gmacs == NULL)
+ return PBOOK_SLEEP_OK;
+
+ gm = (struct gmac *) gmacs->priv;
+ if (!gm->opened)
+ return PBOOK_SLEEP_OK;
+
+ switch (when) {
+ case PBOOK_SLEEP_REQUEST:
+ break;
+ case PBOOK_SLEEP_REJECT:
+ break;
+ case PBOOK_SLEEP_NOW:
+ disable_irq(gm->dev->irq);
+ gm->dev->tbusy = 1;
+ gmac_stop_dma(gm);
+ mii_poll_stop(gm);
+ gmac_set_power(gm, 0);
+ for (i = 0; i < NRX; ++i) {
+ if (gm->rx_buff[i] != 0) {
+ dev_kfree_skb(gm->rx_buff[i]);
+ gm->rx_buff[i] = 0;
+ }
+ }
+ for (i = 0; i < NTX; ++i) {
+ if (gm->tx_buff[i] != 0) {
+ dev_kfree_skb(gm->tx_buff[i]);
+ gm->tx_buff[i] = 0;
+ }
+ }
+ break;
+ case PBOOK_WAKE:
+ /* see if this is enough */
+ gmac_powerup_and_reset(gm->dev);
+ gm->full_duplex = 0;
+ gm->phy_status = 0;
+ mii_lookup_and_reset(gm);
+ mii_setup_phy(gm);
+ gmac_init_rings(gm, 0);
+ gmac_mac_init(gm, gm->dev->dev_addr);
+ gmac_set_multicast(gm->dev);
+ mii_interrupt(gm);
+ gmac_start_dma(gm);
+ gm->dev->tbusy = 0;
+ enable_irq(gm->dev->irq);
+ break;
+ }
+ return PBOOK_SLEEP_OK;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
/*
* Handle a transmit timeout
*/
@@ -712,12 +889,11 @@
struct gmac *gm = (struct gmac *) dev->priv;
int i, timeout;
unsigned long flags;
-
- save_flags(flags);
- cli();
printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
+ spin_lock_irqsave(&gm->lock, flags);
+
/*
* Do something useful here
*
@@ -758,7 +934,7 @@
/* Restart chip */
gmac_start_dma(gm);
- restore_flags(flags);
+ spin_unlock_irqrestore(&gm->lock, flags);
dev->tbusy = 0;
}
@@ -770,6 +946,7 @@
struct gmac *gm = (struct gmac *) dev->priv;
volatile struct gmac_dma_desc *dp;
int i;
+ unsigned long flags;
/* Check tbusy bit and handle eventual transmitter timeout */
if(test_and_set_bit(0, (void *) &dev->tbusy) != 0) {
@@ -780,9 +957,12 @@
return 1;
}
+ spin_lock_irqsave(&gm->lock, flags);
+
i = gm->next_tx;
if (gm->tx_buff[i] != 0) {
/* buffer is full, can't send this packet at the moment */
+ spin_unlock_irqrestore(&gm->lock, flags);
return 1;
}
gm->next_tx = (i + 1) & (NTX - 1);
@@ -804,6 +984,8 @@
dev->tbusy = (gm->tx_buff[gm->next_tx] != 0);
+ spin_unlock_irqrestore(&gm->lock, flags);
+
return 0;
}
@@ -820,9 +1002,8 @@
int gone, i;
i = gm->tx_gone;
- gone = GM_IN(GM_TX_COMP);
-
- while (force_cleanup || i != gone) {
+ do {
+ gone = GM_IN(GM_TX_COMP);
skb = gm->tx_buff[i];
if (skb == NULL)
break;
@@ -837,7 +1018,7 @@
dev_kfree_skb(skb);
if (++i >= NTX)
i = 0;
- }
+ } while (force_cleanup || i != gone);
gm->tx_gone = i;
if (!force_cleanup && dev->tbusy &&
@@ -987,15 +1168,23 @@
}
if (status & GM_IRQ_MIF) {
+ spin_lock(&gm->lock);
mii_interrupt(gm);
+ spin_unlock(&gm->lock);
}
- if (status & GM_IRQ_RX_DONE)
+ if (status & GM_IRQ_RX_DONE) {
+ spin_lock(&gm->lock);
gmac_receive(dev);
-
- if (status & (GM_IRQ_TX_INT_ME | GM_IRQ_TX_ALL))
+ spin_unlock(&gm->lock);
+ }
+
+ if (status & (GM_IRQ_TX_INT_ME | GM_IRQ_TX_ALL)) {
+ spin_lock(&gm->lock);
gmac_tx_cleanup(dev, 0);
-
+ spin_unlock(&gm->lock);
+ }
+
dev->interrupt = 0;
}
@@ -1076,10 +1265,12 @@
dev->base_addr = gmac->addrs[0].address;
gm->regs = (volatile unsigned int *)
ioremap(gmac->addrs[0].address, 0x10000);
- gm->sysregs = (volatile unsigned int *) ioremap(0xf8000000, 0x1000);
dev->irq = gmac->intrs[0].line;
gm->dev = dev;
-
+ gm->of_node = gmac;
+
+ spin_lock_init(&gm->lock);
+
if (pci_device_loc(gmac, &gm->pci_bus, &gm->pci_devfn)) {
gm->pci_bus = gm->pci_devfn = 0xff;
printk(KERN_ERR "Can't locate GMAC PCI entry\n");
@@ -1117,6 +1308,9 @@
gmacs = dev;
+#ifdef CONFIG_PMAC_PBOOK
+ pmu_register_sleep_notifier(&gmac_sleep_notifier);
+#endif
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)