macb.c
来自「linux 内核源代码」· C语言 代码 · 共 1,302 行 · 第 1/3 页
C
1,302 行
/* * Atmel MACB Ethernet Controller driver * * Copyright (C) 2004-2006 Atmel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/clk.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/dma-mapping.h>#include <linux/platform_device.h>#include <linux/phy.h>#include <asm/arch/board.h>#include <asm/arch/cpu.h>#include "macb.h"#define RX_BUFFER_SIZE 128#define RX_RING_SIZE 512#define RX_RING_BYTES (sizeof(struct dma_desc) * RX_RING_SIZE)/* Make the IP header word-aligned (the ethernet header is 14 bytes) */#define RX_OFFSET 2#define TX_RING_SIZE 128#define DEF_TX_RING_PENDING (TX_RING_SIZE - 1)#define TX_RING_BYTES (sizeof(struct dma_desc) * TX_RING_SIZE)#define TX_RING_GAP(bp) \ (TX_RING_SIZE - (bp)->tx_pending)#define TX_BUFFS_AVAIL(bp) \ (((bp)->tx_tail <= (bp)->tx_head) ? \ (bp)->tx_tail + (bp)->tx_pending - (bp)->tx_head : \ (bp)->tx_tail - (bp)->tx_head - TX_RING_GAP(bp))#define NEXT_TX(n) (((n) + 1) & (TX_RING_SIZE - 1))#define NEXT_RX(n) (((n) + 1) & (RX_RING_SIZE - 1))/* minimum number of free TX descriptors before waking up TX process */#define MACB_TX_WAKEUP_THRESH (TX_RING_SIZE / 4)#define MACB_RX_INT_FLAGS (MACB_BIT(RCOMP) | MACB_BIT(RXUBR) \ | MACB_BIT(ISR_ROVR))static void __macb_set_hwaddr(struct macb *bp){ u32 bottom; u16 top; bottom = cpu_to_le32(*((u32 *)bp->dev->dev_addr)); macb_writel(bp, SA1B, bottom); top = cpu_to_le16(*((u16 *)(bp->dev->dev_addr + 4))); macb_writel(bp, SA1T, top);}static void __init macb_get_hwaddr(struct macb *bp){ u32 bottom; u16 top; u8 addr[6]; bottom = macb_readl(bp, SA1B); top = macb_readl(bp, SA1T); addr[0] = bottom & 0xff; addr[1] = (bottom >> 8) & 0xff; addr[2] = (bottom >> 16) & 0xff; addr[3] = (bottom >> 24) & 0xff; addr[4] = top & 0xff; addr[5] = (top >> 8) & 0xff; if (is_valid_ether_addr(addr)) memcpy(bp->dev->dev_addr, addr, sizeof(addr));}static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum){ struct macb *bp = bus->priv; int value; macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF) | MACB_BF(RW, MACB_MAN_READ) | MACB_BF(PHYA, mii_id) | MACB_BF(REGA, regnum) | MACB_BF(CODE, MACB_MAN_CODE))); /* wait for end of transfer */ while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR))) cpu_relax(); value = MACB_BFEXT(DATA, macb_readl(bp, MAN)); return value;}static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value){ struct macb *bp = bus->priv; macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF) | MACB_BF(RW, MACB_MAN_WRITE) | MACB_BF(PHYA, mii_id) | MACB_BF(REGA, regnum) | MACB_BF(CODE, MACB_MAN_CODE) | MACB_BF(DATA, value))); /* wait for end of transfer */ while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR))) cpu_relax(); return 0;}static int macb_mdio_reset(struct mii_bus *bus){ return 0;}static void macb_handle_link_change(struct net_device *dev){ struct macb *bp = netdev_priv(dev); struct phy_device *phydev = bp->phy_dev; unsigned long flags; int status_change = 0; spin_lock_irqsave(&bp->lock, flags); if (phydev->link) { if ((bp->speed != phydev->speed) || (bp->duplex != phydev->duplex)) { u32 reg; reg = macb_readl(bp, NCFGR); reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); if (phydev->duplex) reg |= MACB_BIT(FD); if (phydev->speed) reg |= MACB_BIT(SPD); macb_writel(bp, NCFGR, reg); bp->speed = phydev->speed; bp->duplex = phydev->duplex; status_change = 1; } } if (phydev->link != bp->link) { if (phydev->link) netif_schedule(dev); else { bp->speed = 0; bp->duplex = -1; } bp->link = phydev->link; status_change = 1; } spin_unlock_irqrestore(&bp->lock, flags); if (status_change) { if (phydev->link) printk(KERN_INFO "%s: link up (%d/%s)\n", dev->name, phydev->speed, DUPLEX_FULL == phydev->duplex ? "Full":"Half"); else printk(KERN_INFO "%s: link down\n", dev->name); }}/* based on au1000_eth. c*/static int macb_mii_probe(struct net_device *dev){ struct macb *bp = netdev_priv(dev); struct phy_device *phydev = NULL; struct eth_platform_data *pdata; int phy_addr; /* find the first phy */ for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { if (bp->mii_bus.phy_map[phy_addr]) { phydev = bp->mii_bus.phy_map[phy_addr]; break; } } if (!phydev) { printk (KERN_ERR "%s: no PHY found\n", dev->name); return -1; } pdata = bp->pdev->dev.platform_data; /* TODO : add pin_irq */ /* attach the mac to the phy */ if (pdata && pdata->is_rmii) { phydev = phy_connect(dev, phydev->dev.bus_id, &macb_handle_link_change, 0, PHY_INTERFACE_MODE_RMII); } else { phydev = phy_connect(dev, phydev->dev.bus_id, &macb_handle_link_change, 0, PHY_INTERFACE_MODE_MII); } if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); return PTR_ERR(phydev); } /* mask with MAC supported features */ phydev->supported &= PHY_BASIC_FEATURES; phydev->advertising = phydev->supported; bp->link = 0; bp->speed = 0; bp->duplex = -1; bp->phy_dev = phydev; return 0;}static int macb_mii_init(struct macb *bp){ struct eth_platform_data *pdata; int err = -ENXIO, i; /* Enable managment port */ macb_writel(bp, NCR, MACB_BIT(MPE)); bp->mii_bus.name = "MACB_mii_bus", bp->mii_bus.read = &macb_mdio_read, bp->mii_bus.write = &macb_mdio_write, bp->mii_bus.reset = &macb_mdio_reset, bp->mii_bus.id = bp->pdev->id, bp->mii_bus.priv = bp, bp->mii_bus.dev = &bp->dev->dev; pdata = bp->pdev->dev.platform_data; if (pdata) bp->mii_bus.phy_mask = pdata->phy_mask; bp->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); if (!bp->mii_bus.irq) { err = -ENOMEM; goto err_out; } for (i = 0; i < PHY_MAX_ADDR; i++) bp->mii_bus.irq[i] = PHY_POLL; platform_set_drvdata(bp->dev, &bp->mii_bus); if (mdiobus_register(&bp->mii_bus)) goto err_out_free_mdio_irq; if (macb_mii_probe(bp->dev) != 0) { goto err_out_unregister_bus; } return 0;err_out_unregister_bus: mdiobus_unregister(&bp->mii_bus);err_out_free_mdio_irq: kfree(bp->mii_bus.irq);err_out: return err;}static void macb_update_stats(struct macb *bp){ u32 __iomem *reg = bp->regs + MACB_PFR; u32 *p = &bp->hw_stats.rx_pause_frames; u32 *end = &bp->hw_stats.tx_pause_frames + 1; WARN_ON((unsigned long)(end - p - 1) != (MACB_TPF - MACB_PFR) / 4); for(; p < end; p++, reg++) *p += __raw_readl(reg);}static void macb_tx(struct macb *bp){ unsigned int tail; unsigned int head; u32 status; status = macb_readl(bp, TSR); macb_writel(bp, TSR, status); dev_dbg(&bp->pdev->dev, "macb_tx status = %02lx\n", (unsigned long)status); if (status & MACB_BIT(UND)) { int i; printk(KERN_ERR "%s: TX underrun, resetting buffers\n", bp->dev->name); head = bp->tx_head; /*Mark all the buffer as used to avoid sending a lost buffer*/ for (i = 0; i < TX_RING_SIZE; i++) bp->tx_ring[i].ctrl = MACB_BIT(TX_USED); /* free transmit buffer in upper layer*/ for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) { struct ring_info *rp = &bp->tx_skb[tail]; struct sk_buff *skb = rp->skb; BUG_ON(skb == NULL); rmb(); dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len, DMA_TO_DEVICE); rp->skb = NULL; dev_kfree_skb_irq(skb); } bp->tx_head = bp->tx_tail = 0; } if (!(status & MACB_BIT(COMP))) /* * This may happen when a buffer becomes complete * between reading the ISR and scanning the * descriptors. Nothing to worry about. */ return; head = bp->tx_head; for (tail = bp->tx_tail; tail != head; tail = NEXT_TX(tail)) { struct ring_info *rp = &bp->tx_skb[tail]; struct sk_buff *skb = rp->skb; u32 bufstat; BUG_ON(skb == NULL); rmb(); bufstat = bp->tx_ring[tail].ctrl; if (!(bufstat & MACB_BIT(TX_USED))) break; dev_dbg(&bp->pdev->dev, "skb %u (data %p) TX complete\n", tail, skb->data); dma_unmap_single(&bp->pdev->dev, rp->mapping, skb->len, DMA_TO_DEVICE); bp->stats.tx_packets++; bp->stats.tx_bytes += skb->len; rp->skb = NULL; dev_kfree_skb_irq(skb); } bp->tx_tail = tail; if (netif_queue_stopped(bp->dev) && TX_BUFFS_AVAIL(bp) > MACB_TX_WAKEUP_THRESH) netif_wake_queue(bp->dev);}static int macb_rx_frame(struct macb *bp, unsigned int first_frag, unsigned int last_frag){ unsigned int len; unsigned int frag; unsigned int offset = 0; struct sk_buff *skb; len = MACB_BFEXT(RX_FRMLEN, bp->rx_ring[last_frag].ctrl); dev_dbg(&bp->pdev->dev, "macb_rx_frame frags %u - %u (len %u)\n", first_frag, last_frag, len); skb = dev_alloc_skb(len + RX_OFFSET); if (!skb) { bp->stats.rx_dropped++; for (frag = first_frag; ; frag = NEXT_RX(frag)) { bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED); if (frag == last_frag) break; } wmb(); return 1; } skb_reserve(skb, RX_OFFSET); skb->ip_summed = CHECKSUM_NONE; skb_put(skb, len); for (frag = first_frag; ; frag = NEXT_RX(frag)) { unsigned int frag_len = RX_BUFFER_SIZE; if (offset + frag_len > len) { BUG_ON(frag != last_frag); frag_len = len - offset; } skb_copy_to_linear_data_offset(skb, offset, (bp->rx_buffers + (RX_BUFFER_SIZE * frag)), frag_len); offset += RX_BUFFER_SIZE; bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED); wmb(); if (frag == last_frag) break; } skb->protocol = eth_type_trans(skb, bp->dev); bp->stats.rx_packets++; bp->stats.rx_bytes += len; bp->dev->last_rx = jiffies; dev_dbg(&bp->pdev->dev, "received skb of length %u, csum: %08x\n", skb->len, skb->csum); netif_receive_skb(skb); return 0;}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?