📄 gianfar.c
字号:
/* * drivers/net/gianfar.c * * Gianfar Ethernet Driver * This driver is designed for the non-CPM ethernet controllers * on the 85xx and 83xx family of integrated processors * Based on 8260_io/fcc_enet.c * * Author: Andy Fleming * Maintainer: Kumar Gala * * Copyright (c) 2002-2006 Freescale Semiconductor, Inc. * Copyright (c) 2007 MontaVista Software, Inc. * * 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. * * Gianfar: AKA Lambda Draconis, "Dragon" * RA 11 31 24.2 * Dec +69 19 52 * V 3.84 * B-V +1.62 * * Theory of operation * * The driver is initialized through platform_device. Structures which * define the configuration needed by the board are defined in a * board structure in arch/ppc/platforms (though I do not * discount the possibility that other architectures could one * day be supported. * * The Gianfar Ethernet Controller uses a ring of buffer * descriptors. The beginning is indicated by a register * pointing to the physical address of the start of the ring. * The end is determined by a "wrap" bit being set in the * last descriptor of the ring. * * When a packet is received, the RXF bit in the * IEVENT register is set, triggering an interrupt when the * corresponding bit in the IMASK register is also set (if * interrupt coalescing is active, then the interrupt may not * happen immediately, but will wait until either a set number * of frames or amount of time have passed). In NAPI, the * interrupt handler will signal there is work to be done, and * exit. Without NAPI, the packet(s) will be handled * immediately. Both methods will start at the last known empty * descriptor, and process every subsequent descriptor until there * are none left with data (NAPI will stop after a set number of * packets to give time to other tasks, but will eventually * process all the packets). The data arrives inside a * pre-allocated skb, and so after the skb is passed up to the * stack, a new skb must be allocated, and the address field in * the buffer descriptor must be updated to indicate this new * skb. * * When the kernel requests that a packet be transmitted, the * driver starts where it left off last time, and points the * descriptor at the buffer which was passed in. The driver * then informs the DMA engine that there are packets ready to * be transmitted. Once the controller is finished transmitting * the packet, an interrupt may be triggered (under the same * conditions as for reception, but depending on the TXF bit). * The driver then cleans up the buffer. */#include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/unistd.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/if_vlan.h>#include <linux/spinlock.h>#include <linux/mm.h>#include <linux/platform_device.h>#include <linux/ip.h>#include <linux/tcp.h>#include <linux/udp.h>#include <linux/in.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <linux/module.h>#include <linux/dma-mapping.h>#include <linux/crc32.h>#include <linux/mii.h>#include <linux/phy.h>#include "gianfar.h"#include "gianfar_mii.h"#define TX_TIMEOUT (1*HZ)#define SKB_ALLOC_TIMEOUT 1000000#undef BRIEF_GFAR_ERRORS#undef VERBOSE_GFAR_ERRORS#ifdef CONFIG_GFAR_NAPI#define RECEIVE(x) netif_receive_skb(x)#else#define RECEIVE(x) netif_rx(x)#endifconst char gfar_driver_name[] = "Gianfar Ethernet";const char gfar_driver_version[] = "1.3";static int gfar_enet_open(struct net_device *dev);static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);static void gfar_timeout(struct net_device *dev);static int gfar_close(struct net_device *dev);struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp);static int gfar_set_mac_address(struct net_device *dev);static int gfar_change_mtu(struct net_device *dev, int new_mtu);static irqreturn_t gfar_error(int irq, void *dev_id);static irqreturn_t gfar_transmit(int irq, void *dev_id);static irqreturn_t gfar_interrupt(int irq, void *dev_id);static void adjust_link(struct net_device *dev);static void init_registers(struct net_device *dev);static int init_phy(struct net_device *dev);static int gfar_probe(struct platform_device *pdev);static int gfar_remove(struct platform_device *pdev);static void free_skb_resources(struct gfar_private *priv);static void gfar_set_multi(struct net_device *dev);static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);static void gfar_configure_serdes(struct net_device *dev);extern int gfar_local_mdio_write(struct gfar_mii *regs, int mii_id, int regnum, u16 value);extern int gfar_local_mdio_read(struct gfar_mii *regs, int mii_id, int regnum);#ifdef CONFIG_GFAR_NAPIstatic int gfar_poll(struct napi_struct *napi, int budget);#endif#ifdef CONFIG_NET_POLL_CONTROLLERstatic void gfar_netpoll(struct net_device *dev);#endifint gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);static void gfar_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);void gfar_halt(struct net_device *dev);void gfar_start(struct net_device *dev);static void gfar_clear_exact_match(struct net_device *dev);static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);extern const struct ethtool_ops gfar_ethtool_ops;MODULE_AUTHOR("Freescale Semiconductor, Inc");MODULE_DESCRIPTION("Gianfar Ethernet Driver");MODULE_LICENSE("GPL");/* Returns 1 if incoming frames use an FCB */static inline int gfar_uses_fcb(struct gfar_private *priv){ return (priv->vlan_enable || priv->rx_csum_enable);}/* Set up the ethernet device structure, private data, * and anything else we need before we start */static int gfar_probe(struct platform_device *pdev){ u32 tempval; struct net_device *dev = NULL; struct gfar_private *priv = NULL; struct gianfar_platform_data *einfo; struct resource *r; int err = 0; DECLARE_MAC_BUF(mac); einfo = (struct gianfar_platform_data *) pdev->dev.platform_data; if (NULL == einfo) { printk(KERN_ERR "gfar %d: Missing additional data!\n", pdev->id); return -ENODEV; } /* Create an ethernet device instance */ dev = alloc_etherdev(sizeof (*priv)); if (NULL == dev) return -ENOMEM; priv = netdev_priv(dev); priv->dev = dev; /* Set the info in the priv to the current info */ priv->einfo = einfo; /* fill out IRQ fields */ if (einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { priv->interruptTransmit = platform_get_irq_byname(pdev, "tx"); priv->interruptReceive = platform_get_irq_byname(pdev, "rx"); priv->interruptError = platform_get_irq_byname(pdev, "error"); if (priv->interruptTransmit < 0 || priv->interruptReceive < 0 || priv->interruptError < 0) goto regs_fail; } else { priv->interruptTransmit = platform_get_irq(pdev, 0); if (priv->interruptTransmit < 0) goto regs_fail; } /* get a pointer to the register memory */ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->regs = ioremap(r->start, sizeof (struct gfar)); if (NULL == priv->regs) { err = -ENOMEM; goto regs_fail; } spin_lock_init(&priv->txlock); spin_lock_init(&priv->rxlock); platform_set_drvdata(pdev, dev); /* Stop the DMA engine now, in case it was running before */ /* (The firmware could have used it, and left it running). */ /* To do this, we write Graceful Receive Stop and Graceful */ /* Transmit Stop, and then wait until the corresponding bits */ /* in IEVENT indicate the stops have completed. */ tempval = gfar_read(&priv->regs->dmactrl); tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); gfar_write(&priv->regs->dmactrl, tempval); tempval = gfar_read(&priv->regs->dmactrl); tempval |= (DMACTRL_GRS | DMACTRL_GTS); gfar_write(&priv->regs->dmactrl, tempval); while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC))) cpu_relax(); /* Reset MAC layer */ gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW); gfar_write(&priv->regs->maccfg1, tempval); /* Initialize MACCFG2. */ gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS); /* Initialize ECNTRL */ gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS); /* Copy the station address into the dev structure, */ memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN); /* Set the dev->base_addr to the gfar reg region */ dev->base_addr = (unsigned long) (priv->regs); SET_NETDEV_DEV(dev, &pdev->dev); /* Fill in the dev structure */ dev->open = gfar_enet_open; dev->hard_start_xmit = gfar_start_xmit; dev->tx_timeout = gfar_timeout; dev->watchdog_timeo = TX_TIMEOUT;#ifdef CONFIG_GFAR_NAPI netif_napi_add(dev, &priv->napi, gfar_poll, GFAR_DEV_WEIGHT);#endif#ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = gfar_netpoll;#endif dev->stop = gfar_close; dev->change_mtu = gfar_change_mtu; dev->mtu = 1500; dev->set_multicast_list = gfar_set_multi; dev->ethtool_ops = &gfar_ethtool_ops; if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { priv->rx_csum_enable = 1; dev->features |= NETIF_F_IP_CSUM; } else priv->rx_csum_enable = 0; priv->vlgrp = NULL; if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) { dev->vlan_rx_register = gfar_vlan_rx_register; dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; priv->vlan_enable = 1; } if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) { priv->extended_hash = 1; priv->hash_width = 9; priv->hash_regs[0] = &priv->regs->igaddr0; priv->hash_regs[1] = &priv->regs->igaddr1; priv->hash_regs[2] = &priv->regs->igaddr2; priv->hash_regs[3] = &priv->regs->igaddr3; priv->hash_regs[4] = &priv->regs->igaddr4; priv->hash_regs[5] = &priv->regs->igaddr5; priv->hash_regs[6] = &priv->regs->igaddr6; priv->hash_regs[7] = &priv->regs->igaddr7; priv->hash_regs[8] = &priv->regs->gaddr0; priv->hash_regs[9] = &priv->regs->gaddr1; priv->hash_regs[10] = &priv->regs->gaddr2; priv->hash_regs[11] = &priv->regs->gaddr3; priv->hash_regs[12] = &priv->regs->gaddr4; priv->hash_regs[13] = &priv->regs->gaddr5; priv->hash_regs[14] = &priv->regs->gaddr6; priv->hash_regs[15] = &priv->regs->gaddr7; } else { priv->extended_hash = 0; priv->hash_width = 8; priv->hash_regs[0] = &priv->regs->gaddr0; priv->hash_regs[1] = &priv->regs->gaddr1; priv->hash_regs[2] = &priv->regs->gaddr2; priv->hash_regs[3] = &priv->regs->gaddr3; priv->hash_regs[4] = &priv->regs->gaddr4; priv->hash_regs[5] = &priv->regs->gaddr5; priv->hash_regs[6] = &priv->regs->gaddr6; priv->hash_regs[7] = &priv->regs->gaddr7; } if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_PADDING) priv->padding = DEFAULT_PADDING; else priv->padding = 0; if (dev->features & NETIF_F_IP_CSUM) dev->hard_header_len += GMAC_FCB_LEN; priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; priv->tx_ring_size = DEFAULT_TX_RING_SIZE; priv->rx_ring_size = DEFAULT_RX_RING_SIZE; priv->txcoalescing = DEFAULT_TX_COALESCE; priv->txcount = DEFAULT_TXCOUNT; priv->txtime = DEFAULT_TXTIME; priv->rxcoalescing = DEFAULT_RX_COALESCE; priv->rxcount = DEFAULT_RXCOUNT; priv->rxtime = DEFAULT_RXTIME; /* Enable most messages by default */ priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; err = register_netdev(dev); if (err) { printk(KERN_ERR "%s: Cannot register net device, aborting.\n", dev->name); goto register_fail; } /* Create all the sysfs files */ gfar_init_sysfs(dev); /* Print out the device info */ printk(KERN_INFO DEVICE_NAME "%s\n", dev->name, print_mac(mac, dev->dev_addr)); /* Even more device info helps when determining which kernel */ /* provided which set of benchmarks. */#ifdef CONFIG_GFAR_NAPI printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);#else printk(KERN_INFO "%s: Running with NAPI disabled\n", dev->name);#endif printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n", dev->name, priv->rx_ring_size, priv->tx_ring_size); return 0;register_fail: iounmap(priv->regs);regs_fail: free_netdev(dev); return err;}static int gfar_remove(struct platform_device *pdev){ struct net_device *dev = platform_get_drvdata(pdev); struct gfar_private *priv = netdev_priv(dev); platform_set_drvdata(pdev, NULL); iounmap(priv->regs); free_netdev(dev); return 0;}/* Reads the controller's registers to determine what interface * connects it to the PHY. */static phy_interface_t gfar_get_interface(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev); u32 ecntrl = gfar_read(&priv->regs->ecntrl); if (ecntrl & ECNTRL_SGMII_MODE) return PHY_INTERFACE_MODE_SGMII; if (ecntrl & ECNTRL_TBI_MODE) { if (ecntrl & ECNTRL_REDUCED_MODE) return PHY_INTERFACE_MODE_RTBI; else return PHY_INTERFACE_MODE_TBI; } if (ecntrl & ECNTRL_REDUCED_MODE) { if (ecntrl & ECNTRL_REDUCED_MII_MODE) return PHY_INTERFACE_MODE_RMII; else { phy_interface_t interface = priv->einfo->interface; /* * This isn't autodetected right now, so it must * be set by the device tree or platform code. */ if (interface == PHY_INTERFACE_MODE_RGMII_ID) return PHY_INTERFACE_MODE_RGMII_ID; return PHY_INTERFACE_MODE_RGMII; } } if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT) return PHY_INTERFACE_MODE_GMII; return PHY_INTERFACE_MODE_MII;}/* Initializes driver's PHY state, and attaches to the PHY. * Returns 0 on success. */static int init_phy(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev); uint gigabit_support = priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0; struct phy_device *phydev; char phy_id[BUS_ID_SIZE]; phy_interface_t interface; priv->oldlink = 0; priv->oldspeed = 0; priv->oldduplex = -1; snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->einfo->bus_id, priv->einfo->phy_id); interface = gfar_get_interface(dev); phydev = phy_connect(dev, phy_id, &adjust_link, 0, interface); if (interface == PHY_INTERFACE_MODE_SGMII) gfar_configure_serdes(dev); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); } /* Remove any features not supported by the controller */ phydev->supported &= (GFAR_SUPPORTED | gigabit_support); phydev->advertising = phydev->supported; priv->phydev = phydev; return 0;}static void gfar_configure_serdes(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev); struct gfar_mii __iomem *regs = (void __iomem *)&priv->regs->gfar_mii_regs; /* Initialise TBI i/f to communicate with serdes (lynx phy) */ /* Single clk mode, mii mode off(for aerdes communication) */ gfar_local_mdio_write(regs, TBIPA_VALUE, MII_TBICON, TBICON_CLK_SELECT); /* Supported pause and full-duplex, no half-duplex */ gfar_local_mdio_write(regs, TBIPA_VALUE, MII_ADVERTISE, ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM); /* ANEG enable, restart ANEG, full duplex mode, speed[1] set */ gfar_local_mdio_write(regs, TBIPA_VALUE, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000);}static void init_registers(struct net_device *dev){ struct gfar_private *priv = netdev_priv(dev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -