📄 ibm_ocp_enet.c
字号:
/* * ibm_ocp_enet.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. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. * * 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 * */#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/pci.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <asm/processor.h>#include <asm/bitops.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_ocp_enet.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 OCP EMAC Ethernet driver"MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");static int skb_res = SKB_RES;MODULE_PARM(skb_res, "i");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 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 };/* 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 emac_phy_map entries */static u32 busy_phy_map = 0;static struct net_device_stats *emac_stats(struct net_device *dev){ struct ocp_enet_private *fep = dev->priv; return &fep->stats;}static intemac_init_zmii(struct ocp_device *ocpdev, int mode){ struct ibm_ocp_zmii *zmii = ZMII_PRIV(ocpdev); struct zmii_regs *zmiip; const char *mode_name[] = { "SMII", "RMII", "MII" }; if (zmii){ /* We have already initialized ZMII device, so just increment refcount and return */ zmii->users++; return 0; } zmii = kmalloc(sizeof(struct ibm_ocp_zmii), GFP_KERNEL); if (zmii == NULL) { printk(KERN_ERR "zmii%d: Out of memory allocating ZMII structure!\n", ocpdev->def->index); return -ENOMEM; } memset(zmii, 0, sizeof(*zmii)); zmiip = (struct zmii_regs *)ioremap(ocpdev->def->paddr, sizeof(*zmiip)); if (zmiip == NULL){ printk(KERN_ERR "zmii%d: Cannot ioremap bridge registers!\n", ocpdev->def->index); kfree(zmii); return -ENOMEM; } if (mode == ZMII_AUTO) { if (zmiip->fer & (ZMII_MII0 | ZMII_MII1 | ZMII_MII2 | ZMII_MII3)) mode = MII; if (zmiip->fer & (ZMII_RMII0 | ZMII_RMII1 | ZMII_RMII2 | ZMII_RMII3)) mode = RMII; if (zmiip->fer & (ZMII_SMII0 | ZMII_SMII1 | ZMII_SMII2 | ZMII_SMII3)) mode = SMII; /* Failsafe: ZMII_AUTO is invalid index into the arrays, so force SMII if all else fails. */ if (mode == ZMII_AUTO) mode = SMII; } zmii->base = zmiip; zmii->mode = mode; zmii->users++; ocp_set_drvdata(ocpdev, zmii); printk(KERN_NOTICE "zmii%d: bridge in %s mode\n", ocpdev->def->index, mode_name[mode]); return 0;}static voidemac_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] | 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 voidemac_fini_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); }}intemac_phy_read(struct net_device *dev, int mii_id, int reg){ register int i; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; volatile 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; } /* Wait for data transfer complete bit */ for (i = 0; i < OCP_RESET_DELAY; ++i) { if (emacp->em0stacr & EMAC_STACR_OC) break; udelay(MDIO_DELAY); /* changed to 2 with new scheme -armin */ } if ((emacp->em0stacr & 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); stacr = in_be32(&emacp->em0stacr); /* Wait for data transfer complete bit */ for (i = 0; i < OCP_RESET_DELAY; ++i) { if ((stacr = in_be32(&emacp->em0stacr)) & EMAC_STACR_OC) break; udelay(MDIO_DELAY); } 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(("OCP MDIO PHY error !\n")); return -1; } MDIO_DEBUG((" -> 0x%x\n", stacr >> 16)); return (stacr >> 16);}voidemac_phy_write(struct net_device *dev, int mii_id, int reg, int data){ register int i = 0; uint32_t stacr; struct ocp_enet_private *fep = dev->priv; volatile 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; } /* Wait for data transfer complete bit */ for (i = 0; i < OCP_RESET_DELAY; ++i) { if (emacp->em0stacr & EMAC_STACR_OC) break; udelay(MDIO_DELAY); /* changed to 2 with new scheme -armin */ } if ((emacp->em0stacr & 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); /* Wait for data transfer complete bit */ for (i = 0; i < OCP_RESET_DELAY; ++i) { if ((stacr = emacp->em0stacr) & EMAC_STACR_OC) break; udelay(MDIO_DELAY); } if ((emacp->em0stacr & 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(("OCP MDIO PHY error !\n")); }}static voidemac_wakeup_irq(int irq, void *param, struct pt_regs *regs){ struct net_device *dev = param; /* On Linux the 405 ethernet will always be active if configured * in. This interrupt should never occur. */ printk(KERN_INFO "%s: WakeUp interrupt !\n", dev->name);}static voidemac_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)); while (fep->tx_cnt && !(fep->tx_desc[fep->ack_slot].ctrl & MAL_TX_CTRL_READY)) { /* Tell the system the transmit completed. */ dev_kfree_skb_irq(fep->tx_skb[fep->ack_slot]); if (fep->tx_desc[fep->ack_slot].ctrl & (EMAC_TX_ST_EC | EMAC_TX_ST_MC | EMAC_TX_ST_SC)) fep->stats.collisions++; fep->tx_skb[fep->ack_slot] = (struct sk_buff *) NULL; if (++fep->ack_slot == NUM_TX_BUFF) fep->ack_slot = 0; fep->tx_cnt--; } if (fep->tx_cnt < NUM_TX_BUFF) netif_wake_queue(dev); PKT_DEBUG(("emac_txeob_dev() exit, tx_cnt: %d\n", fep->tx_cnt)); spin_unlock_irqrestore(&fep->lock, flags);}/* Fill/Re-fill the rx chain with valid ctrl/ptrs. This function will fill from rx_slot up to the parm end. So to completely fill the chain pre-set rx_slot to 0 and pass in an end of 0. */static voidemac_rx_fill(struct net_device *dev, int end){ int i; struct ocp_enet_private *fep = dev->priv; unsigned char *ptr; i = fep->rx_slot; do { if (fep->rx_skb[i] != NULL) { /*We will trust the skb is still in a good state */ ptr = (char *) virt_to_phys(fep->rx_skb[i]->data); } else { /* We don't want the 16 bytes skb_reserve done by dev_alloc_skb, * it breaks our cache line alignement. However, we still allocate * +16 so that we end up allocating the exact same size as * dev_alloc_skb() would do. * Also, because of the skb_res, the max DMA size we give to EMAC * is slighly wrong, causing it to potentially DMA 2 more bytes * from a broken/oversized packet. These 16 bytes will take care * that we don't walk on somebody else toes with that. */ fep->rx_skb[i] = alloc_skb(DESC_RX_BUF_SIZE + 16, GFP_ATOMIC); if (fep->rx_skb[i] == NULL) { /* Keep rx_slot here, the next time clean/fill is called * we will try again before the MAL wraps back here * If the MAL tries to use this descriptor with * the EMPTY bit off it will cause the * rxde interrupt. That is where we will * try again to allocate an sk_buff. */ break; } if (skb_res) skb_reserve(fep->rx_skb[i], skb_res); /* We must NOT consistent_sync the cache line right after the * buffer, so we must crop our sync size to account for the * reserved space */ consistent_sync((void *) fep->rx_skb[i]-> data, (DESC_RX_BUF_SIZE-skb_res), PCI_DMA_FROMDEVICE); ptr = (char *) virt_to_phys(fep->rx_skb[i]->data); } fep->rx_desc[i].ctrl = MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR | /*could be smarter about this to avoid ints at high loads */ (i == (NUM_RX_BUFF - 1) ? MAL_RX_CTRL_WRAP : 0); fep->rx_desc[i].data_ptr = ptr; /* * 440GP uses the previously reserved bits in the * data_len to encode the upper 4-bits of the buffer * physical address (ERPN). Initialize these. */ fep->rx_desc[i].data_len = 0; } while ((i = (i + 1) % NUM_RX_BUFF) != end); fep->rx_slot = i;}static void emac_rx_clean(struct net_device *dev, int call_rx_fill){ int i; int error, frame_length; struct ocp_enet_private *fep = dev->priv; unsigned short ctrl; int slots_walked = 0; i = fep->rx_slot; PKT_DEBUG(("emac_rx_clean() entry, call_rx_fill: %d, rx_slot: %d\n", call_rx_fill, fep->rx_slot)); do { if (fep->rx_skb[i] == NULL) goto skip; /*we have already handled the packet but haved failed to alloc */ /* since rx_desc is in uncached mem we don't keep reading it directly we pull out a local copy of ctrl and do the checks on the copy. */ ctrl = fep->rx_desc[i].ctrl; if (ctrl & MAL_RX_CTRL_EMPTY) break; /*we don't have any more ready packets */ if (ctrl & EMAC_BAD_RX_PACKET) { fep->stats.rx_errors++; fep->stats.rx_dropped++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -