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