📄 ibm_emac_core.c
字号:
/* * ibm_emac_core.c * * Ethernet driver for the built in ethernet on the IBM 4xx PowerPC * processors. * * (c) 2003 Benjamin Herrenschmidt <benh@kernel.crashing.org> * * Based on original work by * * Armin Kuster <akuster@mvista.com> * Johnnie Peters <jpeters@mvista.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * TODO * - Check for races in the "remove" code path * - Add some Power Management to the MAC and the PHY * - Audit remaining of non-rewritten code (--BenH) * - Cleanup message display using msglevel mecanism * - Address all errata * - Audit all register update paths to ensure they * are being written post soft reset if required. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/types.h>#include <linux/dma-mapping.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/bitops.h>#include <asm/processor.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/ocp.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/crc32.h>#include "ibm_emac_core.h"//#define MDIO_DEBUG(fmt) printk fmt#define MDIO_DEBUG(fmt)//#define LINK_DEBUG(fmt) printk fmt#define LINK_DEBUG(fmt)//#define PKT_DEBUG(fmt) printk fmt#define PKT_DEBUG(fmt)#define DRV_NAME "emac"#define DRV_VERSION "2.0"#define DRV_AUTHOR "Benjamin Herrenschmidt <benh@kernel.crashing.org>"#define DRV_DESC "IBM EMAC Ethernet driver"/* * When mdio_idx >= 0, contains a list of emac ocp_devs * that have had their initialization deferred until the * common MDIO controller has been initialized. */LIST_HEAD(emac_init_list);MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");static int skb_res = SKB_RES;module_param(skb_res, int, 0444);MODULE_PARM_DESC(skb_res, "Amount of data to reserve on skb buffs\n" "The 405 handles a misaligned IP header fine but\n" "this can help if you are routing to a tunnel or a\n" "device that needs aligned data. 0..2");#define RGMII_PRIV(ocpdev) ((struct ibm_ocp_rgmii*)ocp_get_drvdata(ocpdev))static unsigned int rgmii_enable[] = { RGMII_RTBI, RGMII_RGMII, RGMII_TBI, RGMII_GMII};static unsigned int rgmii_speed_mask[] = { RGMII_MII2_SPDMASK, RGMII_MII3_SPDMASK};static unsigned int rgmii_speed100[] = { RGMII_MII2_100MB, RGMII_MII3_100MB};static unsigned int rgmii_speed1000[] = { RGMII_MII2_1000MB, RGMII_MII3_1000MB};#define ZMII_PRIV(ocpdev) ((struct ibm_ocp_zmii*)ocp_get_drvdata(ocpdev))static unsigned int zmii_enable[][4] = { {ZMII_SMII0, ZMII_RMII0, ZMII_MII0, ~(ZMII_MDI1 | ZMII_MDI2 | ZMII_MDI3)}, {ZMII_SMII1, ZMII_RMII1, ZMII_MII1, ~(ZMII_MDI0 | ZMII_MDI2 | ZMII_MDI3)}, {ZMII_SMII2, ZMII_RMII2, ZMII_MII2, ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI3)}, {ZMII_SMII3, ZMII_RMII3, ZMII_MII3, ~(ZMII_MDI0 | ZMII_MDI1 | ZMII_MDI2)}};static unsigned int mdi_enable[] = { ZMII_MDI0, ZMII_MDI1, ZMII_MDI2, ZMII_MDI3};static unsigned int zmii_speed = 0x0;static unsigned int zmii_speed100[] = { ZMII_MII0_100MB, ZMII_MII1_100MB, ZMII_MII2_100MB, ZMII_MII3_100MB};/* Since multiple EMACs share MDIO lines in various ways, we need * to avoid re-using the same PHY ID in cases where the arch didn't * setup precise phy_map entries */static u32 busy_phy_map = 0;/* If EMACs share a common MDIO device, this points to it */static struct net_device *mdio_ndev = NULL;struct emac_def_dev { struct list_head link; struct ocp_device *ocpdev; struct ibm_ocp_mal *mal;};static struct net_device_stats *emac_stats(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; return &fep->stats;};static intemac_init_rgmii(struct ocp_device *rgmii_dev, int input, int phy_mode){ struct ibm_ocp_rgmii *rgmii = RGMII_PRIV(rgmii_dev); const char *mode_name[] = { "RTBI", "RGMII", "TBI", "GMII" }; int mode = -1; if (!rgmii) { rgmii = kmalloc(sizeof(struct ibm_ocp_rgmii), GFP_KERNEL); if (rgmii == NULL) { printk(KERN_ERR "rgmii%d: Out of memory allocating RGMII structure!\n", rgmii_dev->def->index); return -ENOMEM; } memset(rgmii, 0, sizeof(*rgmii)); rgmii->base = (struct rgmii_regs *)ioremap(rgmii_dev->def->paddr, sizeof(*rgmii->base)); if (rgmii->base == NULL) { printk(KERN_ERR "rgmii%d: Cannot ioremap bridge registers!\n", rgmii_dev->def->index); kfree(rgmii); return -ENOMEM; } ocp_set_drvdata(rgmii_dev, rgmii); } if (phy_mode) { switch (phy_mode) { case PHY_MODE_GMII: mode = GMII; break; case PHY_MODE_TBI: mode = TBI; break; case PHY_MODE_RTBI: mode = RTBI; break; case PHY_MODE_RGMII: default: mode = RGMII; } rgmii->base->fer &= ~RGMII_FER_MASK(input); rgmii->base->fer |= rgmii_enable[mode] << (4 * input); } else { switch ((rgmii->base->fer & RGMII_FER_MASK(input)) >> (4 * input)) { case RGMII_RTBI: mode = RTBI; break; case RGMII_RGMII: mode = RGMII; break; case RGMII_TBI: mode = TBI; break; case RGMII_GMII: mode = GMII; } } /* Set mode to RGMII if nothing valid is detected */ if (mode < 0) mode = RGMII; printk(KERN_NOTICE "rgmii%d: input %d in %s mode\n", rgmii_dev->def->index, input, mode_name[mode]); rgmii->mode[input] = mode; rgmii->users++; return 0;}static voidemac_rgmii_port_speed(struct ocp_device *ocpdev, int input, int speed){ struct ibm_ocp_rgmii *rgmii = RGMII_PRIV(ocpdev); unsigned int rgmii_speed; rgmii_speed = in_be32(&rgmii->base->ssr); rgmii_speed &= ~rgmii_speed_mask[input]; if (speed == 1000) rgmii_speed |= rgmii_speed1000[input]; else if (speed == 100) rgmii_speed |= rgmii_speed100[input]; out_be32(&rgmii->base->ssr, rgmii_speed);}static void emac_close_rgmii(struct ocp_device *ocpdev){ struct ibm_ocp_rgmii *rgmii = RGMII_PRIV(ocpdev); BUG_ON(!rgmii || rgmii->users == 0); if (!--rgmii->users) { ocp_set_drvdata(ocpdev, NULL); iounmap((void *)rgmii->base); kfree(rgmii); }}static int emac_init_zmii(struct ocp_device *zmii_dev, int input, int phy_mode){ struct ibm_ocp_zmii *zmii = ZMII_PRIV(zmii_dev); const char *mode_name[] = { "SMII", "RMII", "MII" }; int mode = -1; if (!zmii) { zmii = kmalloc(sizeof(struct ibm_ocp_zmii), GFP_KERNEL); if (zmii == NULL) { printk(KERN_ERR "zmii%d: Out of memory allocating ZMII structure!\n", zmii_dev->def->index); return -ENOMEM; } memset(zmii, 0, sizeof(*zmii)); zmii->base = (struct zmii_regs *)ioremap(zmii_dev->def->paddr, sizeof(*zmii->base)); if (zmii->base == NULL) { printk(KERN_ERR "zmii%d: Cannot ioremap bridge registers!\n", zmii_dev->def->index); kfree(zmii); return -ENOMEM; } ocp_set_drvdata(zmii_dev, zmii); } if (phy_mode) { switch (phy_mode) { case PHY_MODE_MII: mode = MII; break; case PHY_MODE_RMII: mode = RMII; break; case PHY_MODE_SMII: default: mode = SMII; } zmii->base->fer &= ~ZMII_FER_MASK(input); zmii->base->fer |= zmii_enable[input][mode]; } else { switch ((zmii->base->fer & ZMII_FER_MASK(input)) << (4 * input)) { case ZMII_MII0: mode = MII; break; case ZMII_RMII0: mode = RMII; break; case ZMII_SMII0: mode = SMII; } } /* Set mode to SMII if nothing valid is detected */ if (mode < 0) mode = SMII; printk(KERN_NOTICE "zmii%d: input %d in %s mode\n", zmii_dev->def->index, input, mode_name[mode]); zmii->mode[input] = mode; zmii->users++; return 0;}static void emac_enable_zmii_port(struct ocp_device *ocpdev, int input){ u32 mask; struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev); mask = in_be32(&zmii->base->fer); mask &= zmii_enable[input][MDI]; /* turn all non enabled MDI's off */ mask |= zmii_enable[input][zmii->mode[input]] | mdi_enable[input]; out_be32(&zmii->base->fer, mask);}static voidemac_zmii_port_speed(struct ocp_device *ocpdev, int input, int speed){ struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev); if (speed == 100) zmii_speed |= zmii_speed100[input]; else zmii_speed &= ~zmii_speed100[input]; out_be32(&zmii->base->ssr, zmii_speed);}static void emac_close_zmii(struct ocp_device *ocpdev){ struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev); BUG_ON(!zmii || zmii->users == 0); if (!--zmii->users) { ocp_set_drvdata(ocpdev, NULL); iounmap((void *)zmii->base); kfree(zmii); }}int emac_phy_read(struct net_device *dev, int mii_id, int reg){ int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; MDIO_DEBUG(("%s: phy_read, id: 0x%x, reg: 0x%x\n", dev->name, mii_id, reg)); /* Enable proper ZMII port */ if (fep->zmii_dev) emac_enable_zmii_port(fep->zmii_dev, fep->zmii_input); /* Use the EMAC that has the MDIO port */ if (fep->mdio_dev) { dev = fep->mdio_dev; fep = dev->priv; emacp = fep->emacp; } count = 0; while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) && (count++ < MDIO_DELAY)) udelay(1); MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #1!\n", dev->name); return -1; } /* Clear the speed bits and make a read request to the PHY */ stacr = ((EMAC_STACR_READ | (reg & 0x1f)) & ~EMAC_STACR_CLK_100MHZ); stacr |= ((mii_id & 0x1F) << 5); out_be32(&emacp->em0stacr, stacr); count = 0; while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) && (count++ < MDIO_DELAY)) udelay(1); MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY read timeout #2!\n", dev->name); return -1; } /* Check for a read error */ if (stacr & EMAC_STACR_PHYE) { MDIO_DEBUG(("EMAC MDIO PHY error !\n")); return -1; } MDIO_DEBUG((" -> 0x%x\n", stacr >> 16)); return (stacr >> 16);}void emac_phy_write(struct net_device *dev, int mii_id, int reg, int data){ int count; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; emac_t *emacp = fep->emacp; MDIO_DEBUG(("%s phy_write, id: 0x%x, reg: 0x%x, data: 0x%x\n", dev->name, mii_id, reg, data)); /* Enable proper ZMII port */ if (fep->zmii_dev) emac_enable_zmii_port(fep->zmii_dev, fep->zmii_input); /* Use the EMAC that has the MDIO port */ if (fep->mdio_dev) { dev = fep->mdio_dev; fep = dev->priv; emacp = fep->emacp; } count = 0; while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) && (count++ < MDIO_DELAY)) udelay(1); MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) { printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); return; } /* Clear the speed bits and make a read request to the PHY */ stacr = ((EMAC_STACR_WRITE | (reg & 0x1f)) & ~EMAC_STACR_CLK_100MHZ); stacr |= ((mii_id & 0x1f) << 5) | ((data & 0xffff) << 16); out_be32(&emacp->em0stacr, stacr); count = 0; while ((((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) == 0) && (count++ < MDIO_DELAY)) udelay(1); MDIO_DEBUG((" (count was %d)\n", count)); if ((stacr & EMAC_STACR_OC) == 0) printk(KERN_WARNING "%s: PHY write timeout #2!\n", dev->name); /* Check for a write error */ if ((stacr & EMAC_STACR_PHYE) != 0) { MDIO_DEBUG(("EMAC MDIO PHY error !\n")); }}static void emac_txeob_dev(void *param, u32 chanmask){ struct net_device *dev = param; struct ocp_enet_private *fep = dev->priv; unsigned long flags; spin_lock_irqsave(&fep->lock, flags); PKT_DEBUG(("emac_txeob_dev() entry, tx_cnt: %d\n", fep->tx_cnt));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -