patch-2.4.5 linux/drivers/net/gmac.c
Next file: linux/drivers/net/gmac.h
Previous file: linux/drivers/net/fealnx.c
Back to the patch index
Back to the overall index
- Lines: 530
- Date:
Wed May 16 09:58:36 2001
- Orig file:
v2.4.4/linux/drivers/net/gmac.c
- Orig date:
Tue Feb 13 13:15:05 2001
diff -u --recursive --new-file v2.4.4/linux/drivers/net/gmac.c linux/drivers/net/gmac.c
@@ -9,7 +9,7 @@
* 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
+ * BenH <benh@kernel.crashing.org> - 03/09/2000
* - Add support for new PHYs
* - Add some PowerBook sleep code
*
@@ -47,10 +47,11 @@
#define DEBUG_PHY
/* Driver version 1.3, kernel 2.4.x */
-#define GMAC_VERSION "v1.3k4"
+#define GMAC_VERSION "v1.4k4"
-static unsigned char dummy_buf[RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN];
-static struct net_device *gmacs = NULL;
+#define DUMMY_BUF_LEN RX_BUF_ALLOC_SIZE + RX_OFFSET + GMAC_BUFFER_ALIGN
+static unsigned char *dummy_buf;
+static struct net_device *gmacs;
/* Prototypes */
static int mii_read(struct gmac *gm, int phy, int r);
@@ -62,6 +63,7 @@
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 mii_init_BCM5401(struct gmac *gm);
static void gmac_set_power(struct gmac *gm, int power_up);
static int gmac_powerup_and_reset(struct net_device *dev);
@@ -211,10 +213,12 @@
int link_100 = 0;
int gigabit = 0;
#ifdef DEBUG_PHY
- printk("Link state change, phy_status: 0x%04x\n", phy_status);
+ printk("%s: Link state change, phy_status: 0x%04x\n",
+ gm->dev->name, phy_status);
#endif
gm->phy_status = phy_status;
+ /* Should we enable that in generic mode ? */
lpar_ability = mii_read(gm, gm->phy_addr, MII_ANLPA);
if (lpar_ability & MII_ANLPA_PAUS)
GM_BIS(GM_MAC_CTRL_CONFIG, GM_MAC_CTRL_CONF_SND_PAUSE_EN);
@@ -249,6 +253,9 @@
#endif
full_duplex = ((stat2 & MII_LXT971_STATUS2_FULLDUPLEX) != 0);
link_100 = ((stat2 & MII_LXT971_STATUS2_SPEED) != 0);
+ } else {
+ full_duplex = (lpar_ability & MII_ANLPA_FDAM) != 0;
+ link_100 = (lpar_ability & MII_ANLPA_100M) != 0;
}
#ifdef DEBUG_PHY
printk(" full_duplex: %d, speed: %s\n", full_duplex,
@@ -274,6 +281,144 @@
}
}
+/* Power management: stop PHY chip for suspend mode
+ */
+static void
+gmac_suspend(struct gmac* gm)
+{
+ int data, timeout;
+ unsigned long flags;
+
+ gm->sleeping = 1;
+ netif_stop_queue(gm->dev);
+
+
+ spin_lock_irqsave(&gm->lock, flags);
+ if (gm->opened) {
+ disable_irq(gm->dev->irq);
+ /* Stop polling PHY */
+ mii_poll_stop(gm);
+ }
+ /* Mask out all chips interrupts */
+ GM_OUT(GM_IRQ_MASK, 0xffffffff);
+ spin_unlock_irqrestore(&gm->lock, flags);
+
+ if (gm->opened) {
+ int i;
+ /* Empty Tx ring of any remaining gremlins */
+ gmac_tx_cleanup(gm->dev, 1);
+
+ /* Empty Rx ring of any remaining gremlins */
+ for (i = 0; i < NRX; ++i) {
+ if (gm->rx_buff[i] != 0) {
+ dev_kfree_skb_irq(gm->rx_buff[i]);
+ gm->rx_buff[i] = 0;
+ }
+ }
+ }
+
+ /* Clear interrupts on 5201 */
+ if (gm->phy_type == PHY_B5201)
+ mii_write(gm, gm->phy_addr, MII_BCM5201_INTERRUPT, 0);
+
+ /* Drive MDIO high */
+ GM_OUT(GM_MIF_CFG, 0);
+
+ /* Unchanged, don't ask me why */
+ data = mii_read(gm, gm->phy_addr, MII_ANLPA);
+ mii_write(gm, gm->phy_addr, MII_ANLPA, data);
+
+ /* Put MDIO in sane state */
+ GM_OUT(GM_MIF_CFG, GM_MIF_CFGBB);
+ GM_OUT(GM_MIF_BB_CLOCK, 0);
+ GM_OUT(GM_MIF_BB_DATA, 0);
+ GM_OUT(GM_MIF_BB_OUT_ENABLE, 0);
+
+ /* Stop everything */
+ GM_OUT(GM_MAC_RX_CONFIG, 0);
+ GM_OUT(GM_MAC_TX_CONFIG, 0);
+ GM_OUT(GM_MAC_XIF_CONFIG, 0);
+ GM_OUT(GM_TX_CONF, 0);
+ GM_OUT(GM_RX_CONF, 0);
+
+ /* Set reset state */
+ GM_OUT(GM_RESET, GM_RESET_TX | GM_RESET_RX);
+ for (timeout = 100; timeout > 0; --timeout) {
+ mdelay(10);
+ if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0)
+ break;
+ }
+ GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+ GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
+
+ /* Superisolate PHY */
+ if (gm->phy_type == PHY_B5201)
+ mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,
+ MII_BCM5201_MULTIPHY_SUPERISOLATE);
+
+ /* Unclock chip */
+ gmac_set_power(gm, 0);
+}
+
+static void
+gmac_resume(struct gmac *gm)
+{
+ int data;
+
+ if (gmac_powerup_and_reset(gm->dev)) {
+ printk(KERN_ERR "%s: Couldn't revive gmac ethernet !\n", gm->dev->name);
+ return;
+ }
+
+ gm->sleeping = 0;
+
+ if (gm->opened) {
+ /* Create fresh rings */
+ gmac_init_rings(gm, 1);
+ /* re-initialize the MAC */
+ gmac_mac_init(gm, gm->dev->dev_addr);
+ /* re-initialize the multicast tables & promisc mode if any */
+ gmac_set_multicast(gm->dev);
+ }
+
+ /* Early enable Tx and Rx so that we are clocked */
+ GM_BIS(GM_TX_CONF, GM_TX_CONF_DMA_EN);
+ mdelay(20);
+ GM_BIS(GM_RX_CONF, GM_RX_CONF_DMA_EN);
+ mdelay(20);
+ GM_BIS(GM_MAC_TX_CONFIG, GM_MAC_TX_CONF_ENABLE);
+ mdelay(20);
+ GM_BIS(GM_MAC_RX_CONFIG, GM_MAC_RX_CONF_ENABLE);
+ mdelay(20);
+ if (gm->phy_type == PHY_B5201) {
+ data = mii_read(gm, gm->phy_addr, MII_BCM5201_MULTIPHY);
+ mii_write(gm, gm->phy_addr, MII_BCM5201_MULTIPHY,
+ data & ~MII_BCM5201_MULTIPHY_SUPERISOLATE);
+ }
+ mdelay(1);
+
+ if (gm->opened) {
+ /* restart polling PHY */
+ mii_interrupt(gm);
+ /* restart DMA operations */
+ gmac_start_dma(gm);
+ netif_start_queue(gm->dev);
+ enable_irq(gm->dev->irq);
+ } else {
+ /* Driver not opened, just leave things off. Note that
+ * we could be smart and superisolate the PHY when the
+ * driver is closed, but I won't do that unless I have
+ * a better understanding of some electrical issues with
+ * this PHY chip --BenH
+ */
+ GM_OUT(GM_MAC_RX_CONFIG, 0);
+ GM_OUT(GM_MAC_TX_CONFIG, 0);
+ GM_OUT(GM_MAC_XIF_CONFIG, 0);
+ GM_OUT(GM_TX_CONF, 0);
+ GM_OUT(GM_RX_CONF, 0);
+ }
+}
+
static int
mii_do_reset_phy(struct gmac *gm, int phy_addr)
{
@@ -326,6 +471,40 @@
mii_write(gm, gm->phy_addr, MII_BCM5400_AUXCONTROL, data);
}
+static void
+mii_init_BCM5401(struct gmac *gm)
+{
+ int data;
+ int rev;
+
+ rev = mii_read(gm, gm->phy_addr, MII_ID1) & 0x000f;
+ if (rev == 0 || rev == 3) {
+ /* A bit of black magic from Apple */
+ mii_write(gm, gm->phy_addr, 0x18, 0x0c20);
+ mii_write(gm, gm->phy_addr, 0x17, 0x0012);
+ mii_write(gm, gm->phy_addr, 0x15, 0x1804);
+ mii_write(gm, gm->phy_addr, 0x17, 0x0013);
+ mii_write(gm, gm->phy_addr, 0x15, 0x1204);
+ mii_write(gm, gm->phy_addr, 0x17, 0x8006);
+ mii_write(gm, gm->phy_addr, 0x15, 0x0132);
+ mii_write(gm, gm->phy_addr, 0x17, 0x8006);
+ mii_write(gm, gm->phy_addr, 0x15, 0x0232);
+ mii_write(gm, gm->phy_addr, 0x17, 0x201f);
+ mii_write(gm, gm->phy_addr, 0x15, 0x0a20);
+ }
+
+ 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);
+}
+
static int
mii_lookup_and_reset(struct gmac *gm)
{
@@ -335,10 +514,7 @@
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);
+ feature_gmac_phy_reset(gm->of_node);
/* Find the PHY */
for(i=0; i<=31; i++) {
@@ -367,14 +543,22 @@
printk(KERN_ERR "%s Found Broadcom BCM5400 PHY (Gigabit)\n",
gm->dev->name);
mii_init_BCM5400(gm);
+ } else if ((gm->phy_id & MII_BCM5401_MASK) == MII_BCM5401_ID) {
+ gm->phy_type = PHY_B5401;
+ printk(KERN_ERR "%s Found Broadcom BCM5401 PHY (Gigabit)\n",
+ gm->dev->name);
+ mii_init_BCM5401(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_BCM5221_MASK) == MII_BCM5221_ID) {
+ gm->phy_type = PHY_B5201; /* Same as 5201 for now */
+ printk(KERN_INFO "%s Found Broadcom BCM5221 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",
+ printk(KERN_ERR "%s: Warning ! Unknown PHY ID 0x%08x, using generic mode...\n",
gm->dev->name, gm->phy_id);
}
@@ -448,8 +632,6 @@
PCI_CACHE_LINE_SIZE, 8);
}
} else {
- /* FIXME: Add PHY power down */
- gm->phy_type = 0;
feature_set_gmac_power(gm->of_node, 0);
}
}
@@ -472,11 +654,14 @@
if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) {
/* Mask out all chips interrupts */
GM_OUT(GM_IRQ_MASK, 0xffffffff);
+ GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+ GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
return 0;
}
}
printk(KERN_ERR "%s reset failed!\n", dev->name);
gmac_set_power(gm, 0);
+ gm->phy_type = 0;
return -1;
}
@@ -747,7 +932,9 @@
int multicast_hash = 0;
int multicast_all = 0;
int promisc = 0;
-
+
+ if (gm->sleeping)
+ return;
/* Lock out others. */
netif_stop_queue(dev);
@@ -881,6 +1068,7 @@
/* Shut down chip */
gmac_set_power(gm, 0);
+ gm->phy_type = 0;
/* Empty rings of any remaining gremlins */
for (i = 0; i < NRX; ++i) {
@@ -904,7 +1092,6 @@
gmac_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
struct gmac *gm;
- int i;
/* XXX should handle more than one */
if (gmacs == NULL)
@@ -920,38 +1107,10 @@
case PBOOK_SLEEP_REJECT:
break;
case PBOOK_SLEEP_NOW:
- disable_irq(gm->dev->irq);
- netif_stop_queue(gm->dev);
- 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;
- }
- }
+ gmac_suspend(gm);
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);
- netif_start_queue(gm->dev);
- enable_irq(gm->dev->irq);
+ gmac_resume(gm);
break;
}
return PBOOK_SLEEP_OK;
@@ -967,7 +1126,10 @@
struct gmac *gm = (struct gmac *) dev->priv;
int i, timeout;
unsigned long flags;
-
+
+ if (gm->sleeping)
+ return;
+
printk (KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
spin_lock_irqsave(&gm->lock, flags);
@@ -990,6 +1152,8 @@
if ((GM_IN(GM_RESET) & (GM_RESET_TX | GM_RESET_RX)) == 0) {
/* Mask out all chips interrupts */
GM_OUT(GM_IRQ_MASK, 0xffffffff);
+ GM_OUT(GM_MAC_TX_RESET, GM_MAC_TX_RESET_NOW);
+ GM_OUT(GM_MAC_RX_RESET, GM_MAC_RX_RESET_NOW);
break;
}
}
@@ -1022,6 +1186,9 @@
unsigned long flags;
int i;
+ if (gm->sleeping)
+ return 1;
+
spin_lock_irqsave(&gm->lock, flags);
i = gm->next_tx;
@@ -1274,7 +1441,7 @@
struct gmac *gm = (struct gmac *) dev->priv;
struct net_device_stats *stats = &gm->stats;
- if (gm && gm->opened) {
+ if (gm && gm->opened && !gm->sleeping) {
stats->rx_crc_errors += GM_IN(GM_MAC_RX_CRC_ERR_CTR);
GM_OUT(GM_MAC_RX_CRC_ERR_CTR, 0);
@@ -1315,10 +1482,14 @@
gmac = gmac->next)
gmac_probe1(gmac);
+#ifdef CONFIG_PMAC_PBOOK
+ if (gmacs)
+ pmu_register_sleep_notifier(&gmac_sleep_notifier);
+#endif
MOD_DEC_USE_COUNT;
- return 0;
+ return gmacs? 0: -ENODEV;
}
static void
@@ -1343,6 +1514,14 @@
return;
}
+ if (dummy_buf == NULL) {
+ dummy_buf = kmalloc(DUMMY_BUF_LEN, GFP_KERNEL);
+ if (dummy_buf == NULL) {
+ printk(KERN_ERR "GMAC: failed to allocated dummy buffer\n");
+ return;
+ }
+ }
+
tx_descpage = get_free_page(GFP_KERNEL);
if (tx_descpage == 0) {
printk(KERN_ERR "GMAC: can't get a page for tx descriptors\n");
@@ -1351,17 +1530,13 @@
rx_descpage = get_free_page(GFP_KERNEL);
if (rx_descpage == 0) {
printk(KERN_ERR "GMAC: can't get a page for rx descriptors\n");
- free_page(tx_descpage);
- return;
+ goto out_txdesc;
}
dev = init_etherdev(NULL, sizeof(struct gmac));
-
if (!dev) {
printk(KERN_ERR "GMAC: init_etherdev failed, out of memory\n");
- free_page(tx_descpage);
- free_page(rx_descpage);
- return;
+ goto out_rxdesc;
}
SET_MODULE_OWNER(dev);
@@ -1369,6 +1544,10 @@
dev->base_addr = gmac->addrs[0].address;
gm->regs = (volatile unsigned int *)
ioremap(gmac->addrs[0].address, 0x10000);
+ if (!gm->regs) {
+ printk(KERN_ERR "GMAC: unable to map I/O registers\n");
+ goto out_unreg;
+ }
dev->irq = gmac->intrs[0].line;
gm->dev = dev;
gm->of_node = gmac;
@@ -1394,6 +1573,7 @@
gm->phy_addr = 0;
gm->opened = 0;
+ gm->sleeping = 0;
dev->open = gmac_open;
dev->stop = gmac_close;
@@ -1404,13 +1584,18 @@
dev->watchdog_timeo = 5*HZ;
ether_setup(dev);
-
+
gm->next_gmac = gmacs;
gmacs = dev;
+ return;
-#ifdef CONFIG_PMAC_PBOOK
- pmu_register_sleep_notifier(&gmac_sleep_notifier);
-#endif
+out_unreg:
+ unregister_netdev(dev);
+ kfree(dev);
+out_rxdesc:
+ free_page(rx_descpage);
+out_txdesc:
+ free_page(tx_descpage);
}
MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt");
@@ -1421,16 +1606,25 @@
struct gmac *gm;
struct net_device *dev;
+#ifdef CONFIG_PMAC_PBOOK
+ if (gmacs)
+ pmu_unregister_sleep_notifier(&gmac_sleep_notifier);
+#endif
+
while ((dev = gmacs) != NULL) {
gm = (struct gmac *) dev->priv;
unregister_netdev(dev);
+ iounmap((void *) gm->regs);
free_page(gm->tx_desc_page);
free_page(gm->rx_desc_page);
gmacs = gm->next_gmac;
kfree(dev);
}
+ if (dummy_buf != NULL) {
+ kfree(dummy_buf);
+ dummy_buf = NULL;
+ }
}
module_init(gmac_probe);
module_exit(gmac_cleanup_module);
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)