📄 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-2008 Freescale Semiconductor, Inc. All rights reserved. * Copyright (c) 2007 MontaVista Software, Inc. * * ChangeLog: * 2007 Scott Wood <scottwood@freescale.com> * Add power manangement support * 2008 Tanya Jiang <tanya.jiang@freescale.com> * Enhanced pm support for waking up from phy interrupt * * 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-skbr";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 struct net_device_stats *gfar_get_stats(struct net_device *dev);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 net_device *dev, 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);static void gfar_halt_nodisable(struct net_device *dev);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;#ifdef CONFIG_GFAR_SKBUFF_RECYCLINGstatic unsigned int skbuff_truesize(unsigned int buffer_size);static void gfar_skbr_register_truesize(struct gfar_private *priv);static int gfar_kfree_skb(struct sk_buff *skb);static void gfar_reset_skb_handler(struct gfar_skb_handler *sh);#endifMODULE_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 idx; int err = 0; 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); /* 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); spin_lock_init(&priv->bflock); 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_MODULE_OWNER(dev); 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 dev->poll = gfar_poll; dev->weight = GFAR_DEV_WEIGHT;#endif#ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = gfar_netpoll;#endif dev->stop = gfar_close; dev->get_stats = gfar_get_stats; 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->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, dev->name); for (idx = 0; idx < 6; idx++) printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':'); printk("\n");#ifdef CONFIG_GFAR_SKBUFF_RECYCLING priv->rx_skbuff_truesize = GFAR_DEFAULT_RECYCLE_TRUESIZE; gfar_reset_skb_handler(&priv->skb_handler);#endif /* Setup MTU, receive buffer size */ gfar_change_mtu(dev, 1500); /* 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;}#ifdef CONFIG_PMstatic int gfar_suspend(struct platform_device *pdev, pm_message_t state){ struct net_device *dev = platform_get_drvdata(pdev); struct gfar_private *priv = netdev_priv(dev); unsigned long flags; u32 tempval; int magic_packet = priv->wol_magic_packet && (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); int wake_phy = priv->wol_wake_phy && (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_PHY); netif_device_detach(dev); if (netif_running(dev)) { spin_lock_irqsave(&priv->txlock, flags); spin_lock(&priv->rxlock); gfar_halt_nodisable(dev); /* Disable Tx, and Rx if wake-on-LAN is disabled. */ tempval = gfar_read(&priv->regs->maccfg1); tempval &= ~MACCFG1_TX_EN; if (!magic_packet) tempval &= ~MACCFG1_RX_EN; gfar_write(&priv->regs->maccfg1, tempval); spin_unlock(&priv->rxlock); spin_unlock_irqrestore(&priv->txlock, flags);#ifdef CONFIG_GFAR_NAPI netif_poll_disable(dev);#endif if (magic_packet) { /* Enable interrupt on Magic Packet */ gfar_write(&priv->regs->imask, IMASK_MAG); /* Enable Magic Packet mode */ tempval = gfar_read(&priv->regs->maccfg2); tempval |= MACCFG2_MPEN; gfar_write(&priv->regs->maccfg2, tempval); } if (!wake_phy) phy_stop(priv->phydev); } return 0;}static int gfar_resume(struct platform_device *pdev)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -