📄 adapter.c
字号:
/* * Xilinx Ethernet Adapter component to interface XTemac component to Linux * * Author: MontaVista Software, Inc. * source@mvista.com * * 2002-2004 (c) MontaVista, Software, Inc. This file is licensed under the terms * of the GNU General Public License version 2.1. This program is licensed * "as is" without any warranty of any kind, whether express or implied. * * <pre> * MODIFICATION HISTORY: * * Ver Who Date Changes * ----- ---- -------- ------------------------------------------------------- * 1.00a xd 12/12/05 First release * 2.00a jvb 12/21/05 Added support for checksum offload, and receive side DRE * 2.00b wgr 08/17/06 Port to kernel 2.6.10_mvl401. * </pre> * *//* * This driver is a bit unusual in that it is composed of two logical * parts where one part is the OS independent code and the other part is * the OS dependent code. Xilinx provides their drivers split in this * fashion. This file represents the Linux OS dependent part known as * the Linux adapter. The other files in this directory are the OS * independent files as provided by Xilinx with no changes made to them. * The names exported by those files begin with XTemac_. All functions * in this file that are called by Linux have names that begin with * xenet_. The functions in this file that have Handler in their name * are registered as callbacks with the underlying Xilinx OS independent * layer. Any other functions are static helper functions. *//* * With the way the hardened PLB Temac works, the driver needs to communicate * with the PHY controller. Since each board will have a different * type of PHY, the code that communicates with the MII type controller * is inside #ifdef XILINX_PLB_TEMAC_3_00A_ML403_PHY_SUPPORT conditional * compilation. For your specific board, you will want to replace this code with * code of your own for your specific board. */#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/mii.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/xilinx_devices.h>#include <asm/io.h>#include <linux/ethtool.h>#include <linux/vmalloc.h>#include "xbasic_types.h"#include "xtemac.h"#include "xipif_v1_23_b.h"#include "xpacket_fifo_v2_00_a.h"#include "xdmav3.h"#include "xdmabdv3.h"#define LOCAL_FEATURE_RX_CSUM 0x01#define LOCAL_FEATURE_RX_DRE 0x02/* * Default SEND and RECV buffer descriptors (BD) numbers. * BD Space needed is (XTE_SEND_BD_CNT+XTE_RECV_BD_CNT)*Sizeof(XDmaBdV3). * Each XDmaBdV3 instance currently takes 40 bytes. */#define XTE_SEND_BD_CNT 256#define XTE_RECV_BD_CNT 256/* Must be shorter than length of ethtool_drvinfo.driver field to fit */#define DRIVER_NAME "xilinx_temac"#define DRIVER_DESCRIPTION "Xilinx Tri-Mode Ethernet MAC driver"#define DRIVER_VERSION "2.00b"#define TX_TIMEOUT (3*HZ) /* Transmission timeout is 3 seconds. *//* * When Xilinx TEMAC is configured to use the TX Data Realignment Engine (DRE), * alignment restrictions are as follows: * - SGDMA transmit buffers can be aligned on any boundary, but receive buffers * must be aligned on a 8-byte boundary. * * Without TX DRE, buffer alignment restrictions are as follows: * - SGDMA transmit and receive buffers must be aligned on a 8-byte boundary * * There are no alignment restrictions when using XTemac_FifoRead() and * XTemac_FifoWrite(). * *//* * ALIGNMENT_RECV = the alignement required to receive (8 required by plb bus w/no DRE) * ALIGNMENT_SEND = the alignement required to send (8 required by plb bus w/no DRE) * ALIGNMENT_SEND_PERF = tx alignment for better performance * * ALIGNMENT_SEND is used to see if we *need* to copy the data to re-align. * ALIGNMENT_SEND_PERF is used if we've decided we need to copy anyway, we just * copy to this alignment for better performance. */#define ALIGNMENT_RECV 32#define ALIGNMENT_SEND 8#define ALIGNMENT_SEND_PERF 32/* SGDMA buffer descriptors must be aligned on a 8-byte boundary. */#define ALIGNMENT_BD 4/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */#define BUFFER_ALIGNSEND(adr) ((ALIGNMENT_SEND - ((u32) adr)) % ALIGNMENT_SEND)#define BUFFER_ALIGNSEND_PERF(adr) ((ALIGNMENT_SEND_PERF - ((u32) adr)) % ALIGNMENT_SEND_PERF)#define BUFFER_ALIGNRECV(adr) ((ALIGNMENT_RECV - ((u32) adr)) % ALIGNMENT_RECV)/* Default TX/RX Threshold and waitbound values for SGDMA mode */#define DFT_TX_THRESHOLD 16#define DFT_TX_WAITBOUND 1#define DFT_RX_THRESHOLD 2#define DFT_RX_WAITBOUND 1#define XTE_AUTOSTRIPPING 1/* Put Buffer Descriptors in BRAM? * NOTE: * Putting BDs in BRAM only works if there is only ONE instance of the TEMAC * in hardware. The code does not handle multiple instances, e.g. it does * not manage the memory in BRAM. */#define BD_IN_BRAM 0#define BRAM_BASEADDR 0xffff8000/* * Our private per device data. When a net_device is allocated we will * ask for enough extra space for this. */struct net_local { struct list_head rcv; struct list_head xmit; struct net_device *ndev; /* this device */ struct net_device *next_dev; /* The next device in dev_list */ struct net_device_stats stats; /* Statistics for this device */ struct timer_list phy_timer; /* PHY monitoring timer */ u32 index; /* Which interface is this */ XInterruptHandler Isr; /* Pointer to the XTemac ISR routine */ u8 gmii_addr; /* The GMII address of the PHY */ /* The underlying OS independent code needs space as well. A * pointer to the following XTemac structure will be passed to * any XTemac_ function that requires it. However, we treat the * data as an opaque object in this file (meaning that we never * reference any of the fields inside of the structure). */ XTemac Emac; unsigned int max_frame_size; int cur_speed; /* Buffer Descriptor space for both TX and RX BD ring */ void *desc_space; /* virtual address of BD space */ dma_addr_t desc_space_handle; /* physical address of BD space */ int desc_space_size; /* size of BD space */ /* buffer for one skb in case no room is available for transmission */ struct sk_buff* deferred_skb; /* send buffers for non tx-dre hw */ void **tx_orig_buffers; /* Buffer addresses as returned by dma_alloc_coherent() */ void **tx_buffers; /* Buffers addresses aligned for DMA */ dma_addr_t *tx_phys_buffers; /* Buffer addresses in physical memory */ size_t tx_buffers_cur; /* Index of current buffer used */ /* stats */ int max_frags_in_a_packet; unsigned long realignments; unsigned long tx_hw_csums; unsigned long rx_hw_csums; unsigned long local_features;#if ! XTE_AUTOSTRIPPING unsigned long stripping;#endif};/* for exclusion of all program flows (processes, ISRs and BHs) */spinlock_t XTE_spinlock;spinlock_t XTE_tx_spinlock;spinlock_t XTE_rx_spinlock;/* * ethtool has a status reporting feature where we can report any sort of * status information we'd like. This is the list of strings used for that * status reporting. ETH_GSTRING_LEN is defined in ethtool.h */static char xenet_ethtool_gstrings_stats[][ETH_GSTRING_LEN] = { "txdmaerr", "txpfifoerr", "txstatuserr", "rxrejerr", "rxdmaerr", "rxpfifoerror", "fifoerr", "ipiferr", "intr", "max_frags", "tx_hw_csums", "rx_hw_csums",};#define XENET_STATS_LEN sizeof(xenet_ethtool_gstrings_stats) / ETH_GSTRING_LEN/* Helper function to determine if a given XTemac error warrants a reset. */extern inline intstatus_requires_reset(XStatus s){ return (s == XST_FIFO_ERROR || s == XST_PFIFO_DEADLOCK || s == XST_DMA_ERROR || s == XST_IPIF_ERROR);}/* BH statics */LIST_HEAD(receivedQueue);static spinlock_t receivedQueueSpin = SPIN_LOCK_UNLOCKED;LIST_HEAD(sentQueue);static spinlock_t sentQueueSpin = SPIN_LOCK_UNLOCKED;/* from mii.h * * Items in mii.h but not in gmii.h */#define ADVERTISE_100FULL 0x0100#define ADVERTISE_100HALF 0x0080#define ADVERTISE_10FULL 0x0040#define ADVERTISE_10HALF 0x0020#define ADVERTISE_CSMA 0x0001#define EX_ADVERTISE_1000FULL 0x0200#define EX_ADVERTISE_1000HALF 0x0100/* * items not in mii.h nor gmii.h but should be */#define MII_EXADVERTISE 0x09typedef enum DUPLEX { UNKNOWN_DUPLEX, HALF_DUPLEX, FULL_DUPLEX } DUPLEX;int renegotiate_speed(struct net_device *dev, int speed, DUPLEX duplex){ struct net_local *lp = (struct net_local *) dev->priv; XStatus status; int retries = 2; int wait_count; u16 phy_reg0 = BMCR_ANENABLE | BMCR_ANRESTART; u16 phy_reg1; u16 phy_reg4; u16 phy_reg9 = 0; /* * It appears that the 10baset full and half duplex settings * are overloaded for gigabit ethernet */ if ((duplex == FULL_DUPLEX) && (speed == 10)) { phy_reg4 = ADVERTISE_10FULL | ADVERTISE_CSMA; } else if ((duplex == FULL_DUPLEX) && (speed == 100)) { phy_reg4 = ADVERTISE_100FULL | ADVERTISE_CSMA; } else if ((duplex == FULL_DUPLEX) && (speed == 1000)) { phy_reg4 = ADVERTISE_CSMA; phy_reg9 = EX_ADVERTISE_1000FULL; } else if (speed == 10) { phy_reg4 = ADVERTISE_10HALF | ADVERTISE_CSMA; } else if (speed == 100) { phy_reg4 = ADVERTISE_100HALF | ADVERTISE_CSMA; } else if (speed == 1000) { phy_reg4 = ADVERTISE_CSMA; phy_reg9 = EX_ADVERTISE_1000HALF; } else { printk(KERN_ERR "%s: XTemac: unsupported speed requested: %d\n", dev->name, speed); return -1; } /* * link status in register 1: * first read / second read: * 0 0 link is down * 0 1 link is up (but it was down earlier) * 1 0 link is down (but it was just up) * 1 1 link is up * */ status = XTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); status |= XTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); status |= XTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_ADVERTISE, phy_reg4); status |= XTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_EXADVERTISE, phy_reg9); if (status != XST_SUCCESS) { printk(KERN_ERR "%s: XTemac: error accessing PHY: %d\n", dev->name, status); return -1; } while (retries--) { /* initiate an autonegotiation of the speed */ status = XTemac_PhyWrite(&lp->Emac, lp->gmii_addr, MII_BMCR, phy_reg0); if (status != XST_SUCCESS) { printk(KERN_ERR "%s: XTemac: error starting autonegotiateion: %d\n", dev->name, status); return -1; } wait_count = 20; /* so we don't loop forever */ while (wait_count--) { /* wait a bit for the negotiation to complete */ mdelay(500); status = XTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); status |= XTemac_PhyRead(&lp->Emac, lp->gmii_addr, MII_BMSR, &phy_reg1); if (status != XST_SUCCESS) { printk(KERN_ERR "%s: XTemac: error reading MII status %d\n", dev->name, status); return -1; } if ((phy_reg1 & BMSR_LSTATUS) && (phy_reg1 & BMSR_ANEGCAPABLE)) break; } if (phy_reg1 & BMSR_LSTATUS) { printk(KERN_INFO "%s: XTemac: We renegotiated the speed to: %d\n", dev->name, speed); return 0; } else { printk(KERN_ERR "%s: XTemac: Not able to set the speed to %d (status: 0x%0x)\n", dev->name, speed, phy_reg1); return -1; } } printk(KERN_ERR "%s: XTemac: Not able to set the speed to %d\n", dev->name, speed); return -1;}// #define XILINX_PLB_TEMAC_3_00A_ML403_PHY_SUPPORT/* * This function sets up MAC's speed according to link speed of PHY * This function is specific to MARVELL 88E1111 PHY chip on Xilinx ML403 * board and assumes GMII interface is being used by the TEMAC */void set_mac_speed(struct net_local *lp){ u16 phylinkspeed; struct net_device *dev = lp->ndev; XStatus ret;#ifndef XILINX_PLB_TEMAC_3_00A_ML403_PHY_SUPPORT int retry_count = 1;#endif /* * See comments at top for an explanation of * XILINX_PLB_TEMAC_3_00A_ML403_PHY_SUPPORT */#ifdef XILINX_PLB_TEMAC_3_00A_ML403_PHY_SUPPORT#define MARVELL_88E1111_PHY_SPECIFIC_STATUS_REG_OFFSET 17#define MARVELL_88E1111_LINKSPEED_MARK 0xC000#define MARVELL_88E1111_LINKSPEED_SHIFT 14#define MARVELL_88E1111_LINKSPEED_1000M 0x0002#define MARVELL_88E1111_LINKSPEED_100M 0x0001#define MARVELL_88E1111_LINKSPEED_10M 0x0000 u16 RegValue; /* Loop until read of PHY specific status register is successful. */ do { ret = XTemac_PhyRead(&lp->Emac, lp->gmii_addr, MARVELL_88E1111_PHY_SPECIFIC_STATUS_REG_OFFSET, &RegValue); } while (ret != XST_SUCCESS); /* Get current link speed */ phylinkspeed = (RegValue & MARVELL_88E1111_LINKSPEED_MARK) >> MARVELL_88E1111_LINKSPEED_SHIFT; /* Update TEMAC speed accordingly */ switch (phylinkspeed) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -