cpmac.c
来自「linux 内核源代码」· C语言 代码 · 共 1,197 行 · 第 1/3 页
C
1,197 行
/* * Copyright (C) 2006, 2007 Eugene Konev * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */#include <linux/module.h>#include <linux/init.h>#include <linux/moduleparam.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/version.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/skbuff.h>#include <linux/mii.h>#include <linux/phy.h>#include <linux/phy_fixed.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <asm/gpio.h>MODULE_AUTHOR("Eugene Konev <ejka@imfi.kspu.ru>");MODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");MODULE_LICENSE("GPL");static int debug_level = 8;static int dumb_switch;/* Next 2 are only used in cpmac_probe, so it's pointless to change them */module_param(debug_level, int, 0444);module_param(dumb_switch, int, 0444);MODULE_PARM_DESC(debug_level, "Number of NETIF_MSG bits to enable");MODULE_PARM_DESC(dumb_switch, "Assume switch is not connected to MDIO bus");#define CPMAC_VERSION "0.5.0"/* frame size + 802.1q tag */#define CPMAC_SKB_SIZE (ETH_FRAME_LEN + 4)#define CPMAC_QUEUES 8/* Ethernet registers */#define CPMAC_TX_CONTROL 0x0004#define CPMAC_TX_TEARDOWN 0x0008#define CPMAC_RX_CONTROL 0x0014#define CPMAC_RX_TEARDOWN 0x0018#define CPMAC_MBP 0x0100# define MBP_RXPASSCRC 0x40000000# define MBP_RXQOS 0x20000000# define MBP_RXNOCHAIN 0x10000000# define MBP_RXCMF 0x01000000# define MBP_RXSHORT 0x00800000# define MBP_RXCEF 0x00400000# define MBP_RXPROMISC 0x00200000# define MBP_PROMISCCHAN(channel) (((channel) & 0x7) << 16)# define MBP_RXBCAST 0x00002000# define MBP_BCASTCHAN(channel) (((channel) & 0x7) << 8)# define MBP_RXMCAST 0x00000020# define MBP_MCASTCHAN(channel) ((channel) & 0x7)#define CPMAC_UNICAST_ENABLE 0x0104#define CPMAC_UNICAST_CLEAR 0x0108#define CPMAC_MAX_LENGTH 0x010c#define CPMAC_BUFFER_OFFSET 0x0110#define CPMAC_MAC_CONTROL 0x0160# define MAC_TXPTYPE 0x00000200# define MAC_TXPACE 0x00000040# define MAC_MII 0x00000020# define MAC_TXFLOW 0x00000010# define MAC_RXFLOW 0x00000008# define MAC_MTEST 0x00000004# define MAC_LOOPBACK 0x00000002# define MAC_FDX 0x00000001#define CPMAC_MAC_STATUS 0x0164# define MAC_STATUS_QOS 0x00000004# define MAC_STATUS_RXFLOW 0x00000002# define MAC_STATUS_TXFLOW 0x00000001#define CPMAC_TX_INT_ENABLE 0x0178#define CPMAC_TX_INT_CLEAR 0x017c#define CPMAC_MAC_INT_VECTOR 0x0180# define MAC_INT_STATUS 0x00080000# define MAC_INT_HOST 0x00040000# define MAC_INT_RX 0x00020000# define MAC_INT_TX 0x00010000#define CPMAC_MAC_EOI_VECTOR 0x0184#define CPMAC_RX_INT_ENABLE 0x0198#define CPMAC_RX_INT_CLEAR 0x019c#define CPMAC_MAC_INT_ENABLE 0x01a8#define CPMAC_MAC_INT_CLEAR 0x01ac#define CPMAC_MAC_ADDR_LO(channel) (0x01b0 + (channel) * 4)#define CPMAC_MAC_ADDR_MID 0x01d0#define CPMAC_MAC_ADDR_HI 0x01d4#define CPMAC_MAC_HASH_LO 0x01d8#define CPMAC_MAC_HASH_HI 0x01dc#define CPMAC_TX_PTR(channel) (0x0600 + (channel) * 4)#define CPMAC_RX_PTR(channel) (0x0620 + (channel) * 4)#define CPMAC_TX_ACK(channel) (0x0640 + (channel) * 4)#define CPMAC_RX_ACK(channel) (0x0660 + (channel) * 4)#define CPMAC_REG_END 0x0680/* * Rx/Tx statistics * TODO: use some of them to fill stats in cpmac_stats() */#define CPMAC_STATS_RX_GOOD 0x0200#define CPMAC_STATS_RX_BCAST 0x0204#define CPMAC_STATS_RX_MCAST 0x0208#define CPMAC_STATS_RX_PAUSE 0x020c#define CPMAC_STATS_RX_CRC 0x0210#define CPMAC_STATS_RX_ALIGN 0x0214#define CPMAC_STATS_RX_OVER 0x0218#define CPMAC_STATS_RX_JABBER 0x021c#define CPMAC_STATS_RX_UNDER 0x0220#define CPMAC_STATS_RX_FRAG 0x0224#define CPMAC_STATS_RX_FILTER 0x0228#define CPMAC_STATS_RX_QOSFILTER 0x022c#define CPMAC_STATS_RX_OCTETS 0x0230#define CPMAC_STATS_TX_GOOD 0x0234#define CPMAC_STATS_TX_BCAST 0x0238#define CPMAC_STATS_TX_MCAST 0x023c#define CPMAC_STATS_TX_PAUSE 0x0240#define CPMAC_STATS_TX_DEFER 0x0244#define CPMAC_STATS_TX_COLLISION 0x0248#define CPMAC_STATS_TX_SINGLECOLL 0x024c#define CPMAC_STATS_TX_MULTICOLL 0x0250#define CPMAC_STATS_TX_EXCESSCOLL 0x0254#define CPMAC_STATS_TX_LATECOLL 0x0258#define CPMAC_STATS_TX_UNDERRUN 0x025c#define CPMAC_STATS_TX_CARRIERSENSE 0x0260#define CPMAC_STATS_TX_OCTETS 0x0264#define cpmac_read(base, reg) (readl((void __iomem *)(base) + (reg)))#define cpmac_write(base, reg, val) (writel(val, (void __iomem *)(base) + \ (reg)))/* MDIO bus */#define CPMAC_MDIO_VERSION 0x0000#define CPMAC_MDIO_CONTROL 0x0004# define MDIOC_IDLE 0x80000000# define MDIOC_ENABLE 0x40000000# define MDIOC_PREAMBLE 0x00100000# define MDIOC_FAULT 0x00080000# define MDIOC_FAULTDETECT 0x00040000# define MDIOC_INTTEST 0x00020000# define MDIOC_CLKDIV(div) ((div) & 0xff)#define CPMAC_MDIO_ALIVE 0x0008#define CPMAC_MDIO_LINK 0x000c#define CPMAC_MDIO_ACCESS(channel) (0x0080 + (channel) * 8)# define MDIO_BUSY 0x80000000# define MDIO_WRITE 0x40000000# define MDIO_REG(reg) (((reg) & 0x1f) << 21)# define MDIO_PHY(phy) (((phy) & 0x1f) << 16)# define MDIO_DATA(data) ((data) & 0xffff)#define CPMAC_MDIO_PHYSEL(channel) (0x0084 + (channel) * 8)# define PHYSEL_LINKSEL 0x00000040# define PHYSEL_LINKINT 0x00000020struct cpmac_desc { u32 hw_next; u32 hw_data; u16 buflen; u16 bufflags; u16 datalen; u16 dataflags;#define CPMAC_SOP 0x8000#define CPMAC_EOP 0x4000#define CPMAC_OWN 0x2000#define CPMAC_EOQ 0x1000 struct sk_buff *skb; struct cpmac_desc *next; dma_addr_t mapping; dma_addr_t data_mapping;};struct cpmac_priv { spinlock_t lock; spinlock_t rx_lock; struct cpmac_desc *rx_head; int ring_size; struct cpmac_desc *desc_ring; dma_addr_t dma_ring; void __iomem *regs; struct mii_bus *mii_bus; struct phy_device *phy; char phy_name[BUS_ID_SIZE]; int oldlink, oldspeed, oldduplex; u32 msg_enable; struct net_device *dev; struct work_struct reset_work; struct platform_device *pdev; struct napi_struct napi;};static irqreturn_t cpmac_irq(int, void *);static void cpmac_hw_start(struct net_device *dev);static void cpmac_hw_stop(struct net_device *dev);static int cpmac_stop(struct net_device *dev);static int cpmac_open(struct net_device *dev);static void cpmac_dump_regs(struct net_device *dev){ int i; struct cpmac_priv *priv = netdev_priv(dev); for (i = 0; i < CPMAC_REG_END; i += 4) { if (i % 16 == 0) { if (i) printk("\n"); printk(KERN_DEBUG "%s: reg[%p]:", dev->name, priv->regs + i); } printk(" %08x", cpmac_read(priv->regs, i)); } printk("\n");}static void cpmac_dump_desc(struct net_device *dev, struct cpmac_desc *desc){ int i; printk(KERN_DEBUG "%s: desc[%p]:", dev->name, desc); for (i = 0; i < sizeof(*desc) / 4; i++) printk(" %08x", ((u32 *)desc)[i]); printk("\n");}static void cpmac_dump_skb(struct net_device *dev, struct sk_buff *skb){ int i; printk(KERN_DEBUG "%s: skb 0x%p, len=%d\n", dev->name, skb, skb->len); for (i = 0; i < skb->len; i++) { if (i % 16 == 0) { if (i) printk("\n"); printk(KERN_DEBUG "%s: data[%p]:", dev->name, skb->data + i); } printk(" %02x", ((u8 *)skb->data)[i]); } printk("\n");}static int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int reg){ u32 val; while (cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0)) & MDIO_BUSY) cpu_relax(); cpmac_write(bus->priv, CPMAC_MDIO_ACCESS(0), MDIO_BUSY | MDIO_REG(reg) | MDIO_PHY(phy_id)); while ((val = cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0))) & MDIO_BUSY) cpu_relax(); return MDIO_DATA(val);}static int cpmac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val){ while (cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0)) & MDIO_BUSY) cpu_relax(); cpmac_write(bus->priv, CPMAC_MDIO_ACCESS(0), MDIO_BUSY | MDIO_WRITE | MDIO_REG(reg) | MDIO_PHY(phy_id) | MDIO_DATA(val)); return 0;}static int cpmac_mdio_reset(struct mii_bus *bus){ ar7_device_reset(AR7_RESET_BIT_MDIO); cpmac_write(bus->priv, CPMAC_MDIO_CONTROL, MDIOC_ENABLE | MDIOC_CLKDIV(ar7_cpmac_freq() / 2200000 - 1)); return 0;}static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, };static struct mii_bus cpmac_mii = { .name = "cpmac-mii", .read = cpmac_mdio_read, .write = cpmac_mdio_write, .reset = cpmac_mdio_reset, .irq = mii_irqs,};static int cpmac_config(struct net_device *dev, struct ifmap *map){ if (dev->flags & IFF_UP) return -EBUSY; /* Don't allow changing the I/O address */ if (map->base_addr != dev->base_addr) return -EOPNOTSUPP; /* ignore other fields */ return 0;}static void cpmac_set_multicast_list(struct net_device *dev){ struct dev_mc_list *iter; int i; u8 tmp; u32 mbp, bit, hash[2] = { 0, }; struct cpmac_priv *priv = netdev_priv(dev); mbp = cpmac_read(priv->regs, CPMAC_MBP); if (dev->flags & IFF_PROMISC) { cpmac_write(priv->regs, CPMAC_MBP, (mbp & ~MBP_PROMISCCHAN(0)) | MBP_RXPROMISC); } else { cpmac_write(priv->regs, CPMAC_MBP, mbp & ~MBP_RXPROMISC); if (dev->flags & IFF_ALLMULTI) { /* enable all multicast mode */ cpmac_write(priv->regs, CPMAC_MAC_HASH_LO, 0xffffffff); cpmac_write(priv->regs, CPMAC_MAC_HASH_HI, 0xffffffff); } else { /* * cpmac uses some strange mac address hashing * (not crc32) */ for (i = 0, iter = dev->mc_list; i < dev->mc_count; i++, iter = iter->next) { bit = 0; tmp = iter->dmi_addr[0]; bit ^= (tmp >> 2) ^ (tmp << 4); tmp = iter->dmi_addr[1]; bit ^= (tmp >> 4) ^ (tmp << 2); tmp = iter->dmi_addr[2]; bit ^= (tmp >> 6) ^ tmp; tmp = iter->dmi_addr[3]; bit ^= (tmp >> 2) ^ (tmp << 4); tmp = iter->dmi_addr[4]; bit ^= (tmp >> 4) ^ (tmp << 2); tmp = iter->dmi_addr[5]; bit ^= (tmp >> 6) ^ tmp; bit &= 0x3f; hash[bit / 32] |= 1 << (bit % 32); } cpmac_write(priv->regs, CPMAC_MAC_HASH_LO, hash[0]); cpmac_write(priv->regs, CPMAC_MAC_HASH_HI, hash[1]); } }}static struct sk_buff *cpmac_rx_one(struct cpmac_priv *priv, struct cpmac_desc *desc){ struct sk_buff *skb, *result = NULL; if (unlikely(netif_msg_hw(priv))) cpmac_dump_desc(priv->dev, desc); cpmac_write(priv->regs, CPMAC_RX_ACK(0), (u32)desc->mapping); if (unlikely(!desc->datalen)) { if (netif_msg_rx_err(priv) && net_ratelimit()) printk(KERN_WARNING "%s: rx: spurious interrupt\n", priv->dev->name); return NULL; } skb = netdev_alloc_skb(priv->dev, CPMAC_SKB_SIZE); if (likely(skb)) { skb_reserve(skb, 2); skb_put(desc->skb, desc->datalen); desc->skb->protocol = eth_type_trans(desc->skb, priv->dev); desc->skb->ip_summed = CHECKSUM_NONE; priv->dev->stats.rx_packets++; priv->dev->stats.rx_bytes += desc->datalen; result = desc->skb; dma_unmap_single(&priv->dev->dev, desc->data_mapping, CPMAC_SKB_SIZE, DMA_FROM_DEVICE); desc->skb = skb; desc->data_mapping = dma_map_single(&priv->dev->dev, skb->data, CPMAC_SKB_SIZE, DMA_FROM_DEVICE); desc->hw_data = (u32)desc->data_mapping; if (unlikely(netif_msg_pktdata(priv))) { printk(KERN_DEBUG "%s: received packet:\n", priv->dev->name); cpmac_dump_skb(priv->dev, result); } } else { if (netif_msg_rx_err(priv) && net_ratelimit()) printk(KERN_WARNING
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?