bfin_mac.c

来自「linux 内核源代码」· C语言 代码 · 共 1,080 行 · 第 1/2 页

C
1,080
字号
/* * File:	drivers/net/bfin_mac.c * Based on: * Maintainer: * 		Bryan Wu <bryan.wu@analog.com> * * Original author: * 		Luke Yang <luke.yang@analog.com> * * Created: * Description: * * Modified: *		Copyright 2004-2006 Analog Devices Inc. * * Bugs:	Enter bugs at http://blackfin.uclinux.org/ * * 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, 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 ;  see the file COPYING. * If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/irq.h>#include <linux/io.h>#include <linux/ioport.h>#include <linux/crc32.h>#include <linux/device.h>#include <linux/spinlock.h>#include <linux/ethtool.h>#include <linux/mii.h>#include <linux/phy.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/platform_device.h>#include <asm/dma.h>#include <linux/dma-mapping.h>#include <asm/blackfin.h>#include <asm/cacheflush.h>#include <asm/portmux.h>#include "bfin_mac.h"#define DRV_NAME	"bfin_mac"#define DRV_VERSION	"1.1"#define DRV_AUTHOR	"Bryan Wu, Luke Yang"#define DRV_DESC	"Blackfin BF53[67] on-chip Ethernet MAC driver"MODULE_AUTHOR(DRV_AUTHOR);MODULE_LICENSE("GPL");MODULE_DESCRIPTION(DRV_DESC);#if defined(CONFIG_BFIN_MAC_USE_L1)# define bfin_mac_alloc(dma_handle, size)  l1_data_sram_zalloc(size)# define bfin_mac_free(dma_handle, ptr)    l1_data_sram_free(ptr)#else# define bfin_mac_alloc(dma_handle, size) \	dma_alloc_coherent(NULL, size, dma_handle, GFP_KERNEL)# define bfin_mac_free(dma_handle, ptr) \	dma_free_coherent(NULL, sizeof(*ptr), ptr, dma_handle)#endif#define PKT_BUF_SZ 1580#define MAX_TIMEOUT_CNT	500/* pointers to maintain transmit list */static struct net_dma_desc_tx *tx_list_head;static struct net_dma_desc_tx *tx_list_tail;static struct net_dma_desc_rx *rx_list_head;static struct net_dma_desc_rx *rx_list_tail;static struct net_dma_desc_rx *current_rx_ptr;static struct net_dma_desc_tx *current_tx_ptr;static struct net_dma_desc_tx *tx_desc;static struct net_dma_desc_rx *rx_desc;static void bf537mac_disable(void);static void bf537mac_enable(void);static void desc_list_free(void){	struct net_dma_desc_rx *r;	struct net_dma_desc_tx *t;	int i;#if !defined(CONFIG_BFIN_MAC_USE_L1)	dma_addr_t dma_handle = 0;#endif	if (tx_desc) {		t = tx_list_head;		for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {			if (t) {				if (t->skb) {					dev_kfree_skb(t->skb);					t->skb = NULL;				}				t = t->next;			}		}		bfin_mac_free(dma_handle, tx_desc);	}	if (rx_desc) {		r = rx_list_head;		for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {			if (r) {				if (r->skb) {					dev_kfree_skb(r->skb);					r->skb = NULL;				}				r = r->next;			}		}		bfin_mac_free(dma_handle, rx_desc);	}}static int desc_list_init(void){	int i;	struct sk_buff *new_skb;#if !defined(CONFIG_BFIN_MAC_USE_L1)	/*	 * This dma_handle is useless in Blackfin dma_alloc_coherent().	 * The real dma handler is the return value of dma_alloc_coherent().	 */	dma_addr_t dma_handle;#endif	tx_desc = bfin_mac_alloc(&dma_handle,				sizeof(struct net_dma_desc_tx) *				CONFIG_BFIN_TX_DESC_NUM);	if (tx_desc == NULL)		goto init_error;	rx_desc = bfin_mac_alloc(&dma_handle,				sizeof(struct net_dma_desc_rx) *				CONFIG_BFIN_RX_DESC_NUM);	if (rx_desc == NULL)		goto init_error;	/* init tx_list */	tx_list_head = tx_list_tail = tx_desc;	for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {		struct net_dma_desc_tx *t = tx_desc + i;		struct dma_descriptor *a = &(t->desc_a);		struct dma_descriptor *b = &(t->desc_b);		/*		 * disable DMA		 * read from memory WNR = 0		 * wordsize is 32 bits		 * 6 half words is desc size		 * large desc flow		 */		a->config = WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;		a->start_addr = (unsigned long)t->packet;		a->x_count = 0;		a->next_dma_desc = b;		/*		 * enabled DMA		 * write to memory WNR = 1		 * wordsize is 32 bits		 * disable interrupt		 * 6 half words is desc size		 * large desc flow		 */		b->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;		b->start_addr = (unsigned long)(&(t->status));		b->x_count = 0;		t->skb = NULL;		tx_list_tail->desc_b.next_dma_desc = a;		tx_list_tail->next = t;		tx_list_tail = t;	}	tx_list_tail->next = tx_list_head;	/* tx_list is a circle */	tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);	current_tx_ptr = tx_list_head;	/* init rx_list */	rx_list_head = rx_list_tail = rx_desc;	for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {		struct net_dma_desc_rx *r = rx_desc + i;		struct dma_descriptor *a = &(r->desc_a);		struct dma_descriptor *b = &(r->desc_b);		/* allocate a new skb for next time receive */		new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);		if (!new_skb) {			printk(KERN_NOTICE DRV_NAME			       ": init: low on mem - packet dropped\n");			goto init_error;		}		skb_reserve(new_skb, 2);		r->skb = new_skb;		/*		 * enabled DMA		 * write to memory WNR = 1		 * wordsize is 32 bits		 * disable interrupt		 * 6 half words is desc size		 * large desc flow		 */		a->config = DMAEN | WNR | WDSIZE_32 | NDSIZE_6 | DMAFLOW_LARGE;		/* since RXDWA is enabled */		a->start_addr = (unsigned long)new_skb->data - 2;		a->x_count = 0;		a->next_dma_desc = b;		/*		 * enabled DMA		 * write to memory WNR = 1		 * wordsize is 32 bits		 * enable interrupt		 * 6 half words is desc size		 * large desc flow		 */		b->config = DMAEN | WNR | WDSIZE_32 | DI_EN |				NDSIZE_6 | DMAFLOW_LARGE;		b->start_addr = (unsigned long)(&(r->status));		b->x_count = 0;		rx_list_tail->desc_b.next_dma_desc = a;		rx_list_tail->next = r;		rx_list_tail = r;	}	rx_list_tail->next = rx_list_head;	/* rx_list is a circle */	rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);	current_rx_ptr = rx_list_head;	return 0;init_error:	desc_list_free();	printk(KERN_ERR DRV_NAME ": kmalloc failed\n");	return -ENOMEM;}/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*//* Set FER regs to MUX in Ethernet pins */static int setup_pin_mux(int action){#if defined(CONFIG_BFIN_MAC_RMII)	u16 pin_req[] = P_RMII0;#else	u16 pin_req[] = P_MII0;#endif	if (action) {		if (peripheral_request_list(pin_req, DRV_NAME)) {			printk(KERN_ERR DRV_NAME			": Requesting Peripherals failed\n");			return -EFAULT;		}	} else		peripheral_free_list(pin_req);	return 0;}/* * MII operations *//* Wait until the previous MDC/MDIO transaction has completed */static void mdio_poll(void){	int timeout_cnt = MAX_TIMEOUT_CNT;	/* poll the STABUSY bit */	while ((bfin_read_EMAC_STAADD()) & STABUSY) {		mdelay(10);		if (timeout_cnt-- < 0) {			printk(KERN_ERR DRV_NAME			": wait MDC/MDIO transaction to complete timeout\n");			break;		}	}}/* Read an off-chip register in a PHY through the MDC/MDIO port */static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum){	mdio_poll();	/* read mode */	bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |				SET_REGAD((u16) regnum) |				STABUSY);	mdio_poll();	return (int) bfin_read_EMAC_STADAT();}/* Write an off-chip register in a PHY through the MDC/MDIO port */static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,			 u16 value){	mdio_poll();	bfin_write_EMAC_STADAT((u32) value);	/* write mode */	bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) |				SET_REGAD((u16) regnum) |				STAOP |				STABUSY);	mdio_poll();	return 0;}static int mdiobus_reset(struct mii_bus *bus){	return 0;}static void bf537_adjust_link(struct net_device *dev){	struct bf537mac_local *lp = netdev_priv(dev);	struct phy_device *phydev = lp->phydev;	unsigned long flags;	int new_state = 0;	spin_lock_irqsave(&lp->lock, flags);	if (phydev->link) {		/* Now we make sure that we can be in full duplex mode.		 * If not, we operate in half-duplex mode. */		if (phydev->duplex != lp->old_duplex) {			u32 opmode = bfin_read_EMAC_OPMODE();			new_state = 1;			if (phydev->duplex)				opmode |= FDMODE;			else				opmode &= ~(FDMODE);			bfin_write_EMAC_OPMODE(opmode);			lp->old_duplex = phydev->duplex;		}		if (phydev->speed != lp->old_speed) {#if defined(CONFIG_BFIN_MAC_RMII)			u32 opmode = bfin_read_EMAC_OPMODE();			switch (phydev->speed) {			case 10:				opmode |= RMII_10;				break;			case 100:				opmode &= ~(RMII_10);				break;			default:				printk(KERN_WARNING					"%s: Ack!  Speed (%d) is not 10/100!\n",					DRV_NAME, phydev->speed);				break;			}			bfin_write_EMAC_OPMODE(opmode);#endif			new_state = 1;			lp->old_speed = phydev->speed;		}		if (!lp->old_link) {			new_state = 1;			lp->old_link = 1;			netif_schedule(dev);		}	} else if (lp->old_link) {		new_state = 1;		lp->old_link = 0;		lp->old_speed = 0;		lp->old_duplex = -1;	}	if (new_state) {		u32 opmode = bfin_read_EMAC_OPMODE();		phy_print_status(phydev);		pr_debug("EMAC_OPMODE = 0x%08x\n", opmode);	}	spin_unlock_irqrestore(&lp->lock, flags);}static int mii_probe(struct net_device *dev){	struct bf537mac_local *lp = netdev_priv(dev);	struct phy_device *phydev = NULL;	unsigned short sysctl;	int i;	/* Enable PHY output early */	if (!(bfin_read_VR_CTL() & PHYCLKOE))		bfin_write_VR_CTL(bfin_read_VR_CTL() | PHYCLKOE);	/* MDC  = 2.5 MHz */	sysctl = bfin_read_EMAC_SYSCTL();	sysctl |= SET_MDCDIV(24);	bfin_write_EMAC_SYSCTL(sysctl);	/* search for connect PHY device */	for (i = 0; i < PHY_MAX_ADDR; i++) {		struct phy_device *const tmp_phydev = lp->mii_bus.phy_map[i];		if (!tmp_phydev)			continue; /* no PHY here... */		phydev = tmp_phydev;		break; /* found it */	}	/* now we are supposed to have a proper phydev, to attach to... */	if (!phydev) {		printk(KERN_INFO "%s: Don't found any phy device at all\n",			dev->name);		return -ENODEV;	}#if defined(CONFIG_BFIN_MAC_RMII)	phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0,			PHY_INTERFACE_MODE_RMII);#else	phydev = phy_connect(dev, phydev->dev.bus_id, &bf537_adjust_link, 0,			PHY_INTERFACE_MODE_MII);#endif	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 &= (SUPPORTED_10baseT_Half			      | SUPPORTED_10baseT_Full			      | SUPPORTED_100baseT_Half			      | SUPPORTED_100baseT_Full			      | SUPPORTED_Autoneg			      | SUPPORTED_Pause | SUPPORTED_Asym_Pause			      | SUPPORTED_MII			      | SUPPORTED_TP);	phydev->advertising = phydev->supported;	lp->old_link = 0;	lp->old_speed = 0;	lp->old_duplex = -1;	lp->phydev = phydev;	printk(KERN_INFO "%s: attached PHY driver [%s] "	       "(mii_bus:phy_addr=%s, irq=%d)\n",	       DRV_NAME, phydev->drv->name, phydev->dev.bus_id, phydev->irq);	return 0;}/**************************************************************************/void setup_system_regs(struct net_device *dev){	unsigned short sysctl;	/*	 * Odd word alignment for Receive Frame DMA word	 * Configure checksum support and rcve frame word alignment	 */	sysctl = bfin_read_EMAC_SYSCTL();#if defined(BFIN_MAC_CSUM_OFFLOAD)	sysctl |= RXDWA | RXCKS;#else	sysctl |= RXDWA;#endif	bfin_write_EMAC_SYSCTL(sysctl);	bfin_write_EMAC_MMC_CTL(RSTC | CROLL);	/* Initialize the TX DMA channel registers */	bfin_write_DMA2_X_COUNT(0);	bfin_write_DMA2_X_MODIFY(4);	bfin_write_DMA2_Y_COUNT(0);	bfin_write_DMA2_Y_MODIFY(0);	/* Initialize the RX DMA channel registers */	bfin_write_DMA1_X_COUNT(0);	bfin_write_DMA1_X_MODIFY(4);	bfin_write_DMA1_Y_COUNT(0);	bfin_write_DMA1_Y_MODIFY(0);}static void setup_mac_addr(u8 *mac_addr){	u32 addr_low = le32_to_cpu(*(__le32 *) & mac_addr[0]);	u16 addr_hi = le16_to_cpu(*(__le16 *) & mac_addr[4]);	/* this depends on a little-endian machine */	bfin_write_EMAC_ADDRLO(addr_low);	bfin_write_EMAC_ADDRHI(addr_hi);}static int bf537mac_set_mac_address(struct net_device *dev, void *p){	struct sockaddr *addr = p;	if (netif_running(dev))		return -EBUSY;	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);	setup_mac_addr(dev->dev_addr);	return 0;}static void adjust_tx_list(void){	int timeout_cnt = MAX_TIMEOUT_CNT;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?