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