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 + -
显示快捷键?