📄 ae531xlnx.c
字号:
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright © 2003 Atheros Communications, Inc., All Rights Reserved. *//* * Ethernet driver for Atheros' ae531x ethernet MAC. * This is a fairly generic driver, but it's intended * for use in typical Atheros products. */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/init.h>#include <linux/skbuff.h>#include <asm/io.h>#include "ar531xlnx.h"#include "ae531xreg.h"#include "ae531xmac.h"#include "ae531x.h" #ifndef EXPORT_SYMTAB#define EXPORT_SYMTAB#endif#ifdef DEBUGvoid my_mvPhyShow(int ethUnit);#endifstatic struct ar531x_boarddata *ar531x_boardConfig=NULL;static char *radioConfig=NULL;#define AE531X_LAN_PORT 0#define AE531X_DEV_PER_MAC 1#define TIMER_WUT (jiffies + HZ * AE531X_PHY_POLL_SECONDS)/* timer wakeup time : 2 second */#define CPU_PORT 5/* * ae531x_MAC_state contains driver-specific linux-specific per-MAC information. * The OSinfo member of ae531x_MAC_t points to one of these. */typedef struct ae531x_MAC_state { int irq; struct tq_struct restart_task; struct net_device_stats stats; struct ae531x_dev_sw_state *dev_sw_state[AE531X_DEV_PER_MAC]; int primary_dev; ae531x_MAC_t MACInfo; /* hardware state */} ae531x_MAC_state_t;#define TX_TWO_BUF 1/* * ae531x_dev_sw_state contains driver-specific linux-specific per-device * information. The net_device priv member points to one of these, and * this structure contains a pointer to the associated MAC information. */typedef struct ae531x_dev_sw_state { int enetUnit; /* system unit number "eth%d" */ int unit_on_MAC; /* MAC-relative unit number */ struct net_device *dev; ae531x_MAC_state_t *MAC_state; /* underlying MAC hw/sw state */ struct timer_list timer;} ae531x_dev_sw_state_t;/* * Driver-independent linux-specific per-ethernet device software information. */static struct net_device *ae531x_MAC_dev[AR531X_NUM_ENET_MAC * AE531X_DEV_PER_MAC];/* Driver-dependent per-MAC information */static ae531x_MAC_state_t per_MAC_info[AR531X_NUM_ENET_MAC];/* * Receive buffers need enough room to hold the following: * 1) a max MTU-sized packet. * 2) space for an ethernet header * 3) room at the beginning of the receive buffer in order * to facilitate cooperating drivers that need to PREpend * data. * 4) Depending on configuration, we may need some additional * room at the END of the rx buffer for phy-supplied * trailers (if any). (c.f. CONFIG_VENETDEV) * * The DMA engine insists on 32-bit aligned RX buffers. * TBDXXX: With current code, the IP stack ends up looking * at misaligned headers with word operations. The misaligned * reads are software-emulated via handle_adel_int. We'd * rather align the buffers on a 16-bit boundary, but the * DMA engine doesn't permit it??? */ #define ETH_MAX_MTU 1518#ifdef HEADER_MODE#define AE531X_RX_BUF_SIZE \ (((RXBUFF_RESERVE + ETH_HLEN + ETH_MAX_MTU/* + PHY_HEADER_SIZE*/) + 3) & ~3)#else#define AE531X_RX_BUF_SIZE \ (((RXBUFF_RESERVE + ETH_HLEN + ETH_MAX_MTU + PHY_TRAILER_SIZE) + 3) & ~3)#endif /* Forward references to local functions */static void ae531x_TxReap(ae531x_MAC_state_t *MAC_state, int length);static int ae531x_phy_poll(void *data);static int ae531x_MAC_stop(struct net_device *dev);static int ae531x_MAC_open(struct net_device *dev);#ifdef CONFIG_VLAN_ROUTERstatic BOOL hwInit = FALSE;#endif /******************************************************************************** ae531x_MAC_poll checks for received packets, and sends data* up the stack.*/intae531x_MAC_poll(struct net_device *dev, int *budget){ struct sk_buff *skb; struct sk_buff *newskb; char *rxBufp; int unused_length; VIRT_ADDR rxDesc; int length; ae531x_dev_sw_state_t *dev_sw_state; ae531x_MAC_state_t *MAC_state; ae531x_MAC_t *MACInfo; u32 cmdsts; int rx_limit; int rx_received; int rxDescCount; struct net_device *rxdev;#ifdef CONFIG_VLAN_ROUTER struct net_device *v_rxdev; static int from;#endif int early_stop; int retval;#ifdef DEBUG static int rxDescCountMax = 0;#endif ARRIVE(); dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; MAC_state = dev_sw_state->MAC_state; MACInfo = &MAC_state->MACInfo; rx_limit = MAC_state->dev_sw_state[MAC_state->primary_dev]->dev->quota; rx_received = 0; rxDescCount = 0; early_stop = 0; do { ae531x_AckIntr(MACInfo, DmaIntRxCompleted); for(;!early_stop;) { rxDesc = MACInfo->rxQueue.curDescAddr; cmdsts = AE531X_DESC_STATUS_GET(KSEG1ADDR(rxDesc)); AE531X_PRINT(AE531X_DEBUG_RX, ("examine rxDesc %p with cmdsts=0x%x\n", (void *)rxDesc, cmdsts)); if (cmdsts & DescOwnByDma) { /* There's nothing left to process in the RX ring */ goto rx_all_done; } rxDescCount++; AE531X_CONSUME_DESC((&MACInfo->rxQueue)); A_DATA_CACHE_INVAL(rxDesc, AE531X_DESC_SIZE); /* Process a packet */ length = AE531X_DESC_STATUS_RX_SIZE(cmdsts) - ETH_CRC_LEN; if ( (cmdsts & (DescRxFirst |DescRxLast | DescRxErrors)) == (DescRxFirst | DescRxLast) ) { /* Descriptor status indicates "NO errors" */ skb = AE531X_DESC_SWPTR_GET(rxDesc); /* * Allocate a replacement skb. * We want to get another buffer ready for Rx ASAP. */ newskb = (struct sk_buff *)ae531x_rxbuf_alloc(MACInfo, &rxBufp, &unused_length); if(newskb == NULL ) { /* * Give this descriptor back to the DMA engine, * and drop the received packet. */ MAC_state->stats.rx_dropped++; AE531X_PRINT(AE531X_DEBUG_ERROR, ("Can't allocate new skb\n")); } else { AE531X_DESC_BUFPTR_SET(rxDesc, virt_to_bus(rxBufp)); AE531X_DESC_SWPTR_SET(rxDesc, newskb); } AE531X_DESC_STATUS_SET(rxDesc, DescOwnByDma); rxDesc = NULL; /* sanity -- cannot use rxDesc now */ sysWbFlush(); if (newskb == NULL) { retval = 1; goto rx_no_skbs; } else { /* Sync data cache w.r.t. DMA */ A_DATA_CACHE_INVAL(skb->data, length);#ifdef CONFIG_VLAN_ROUTER phyDetermineSource(skb->data, length, &from); rxdev = (struct net_device *)per_MAC_info[from].dev_sw_state[MAC_state->primary_dev]->dev; MAC_state = &per_MAC_info[from]; #if 0 { int i; if((*(unsigned char*)(skb->data + 12) == 0x08 && *(unsigned char*)(skb->data + 13) == 0x06) || (*(unsigned char*)(skb->data + 12) == 0x45 && *(unsigned char*)(skb->data + 13) == 0x00)) { printf("iface = %d poll length = %d \n",from, length); for(i = 0; i< 16; i++) { printf("0x%2.2x ", *(unsigned char*)(skb->data + i)); if (i%16 == 0 && i != 0) printf("\n"); } printf("\n"); } }#endif #else rxdev = dev_sw_state->dev;#endif if (rxdev == NULL) { /* * We received a packet for a virtual enet device * that is no longer up. Ignore it. */ kfree_skb(skb); continue; } /* Advance data pointer to show that there's data here */ skb_put(skb, length); #ifdef CONFIG_VLAN_ROUTER#ifdef HEADER_MODE //skb_reserve(skb, PHY_HEADER_SIZE); //memmove(skb->data, skb->data+2, skb->len); //skb_trim(skb, (length - 2)); skb->data += PHY_HEADER_SIZE; skb->len -= PHY_HEADER_SIZE; #else skb_trim(skb, length - PHY_TRAILER_SIZE);#endif #endif skb->protocol = eth_type_trans(skb, rxdev); skb->dev = rxdev; skb->ip_summed = CHECKSUM_NONE; rxdev->last_rx = jiffies; rxdev->quota--; if (rx_limit-- < 0) { early_stop=1; /* We've done enough for now -- more later */ AE531X_PRINT(AE531X_DEBUG_RX_STOP, ("Enet%d RX early stop. Quota=%d rxDescCount=%d budget=%d\n", MACInfo->unit, dev->quota, rxDescCount, *budget)); } rx_received++; /* Send the data up the stack */ AE531X_PRINT(AE531X_DEBUG_RX, ("Send data up stack: skb=%p data=%p length=%d\n", (void *)skb, (void *)skb->data, length)); netif_receive_skb(skb); MAC_state->stats.rx_packets++; MAC_state->stats.rx_bytes += length; } } else { /* Descriptor status indicates ERRORS */ MAC_state->stats.rx_errors++; if (cmdsts & (DescRxRunt | DescRxLateColl)) { MAC_state->stats.collisions++; } if (cmdsts & DescRxLengthError) { MAC_state->stats.rx_length_errors++; } if (cmdsts & DescRxCrc) { MAC_state->stats.rx_crc_errors++; } if (cmdsts & DescRxDribbling) { MAC_state->stats.rx_frame_errors++; } AE531X_DESC_STATUS_SET(rxDesc, DescOwnByDma); AE531X_PRINT(AE531X_DEBUG_ERROR, ("Bad receive. rxDesc=%p cmdsts=0x%8.8x\n", (void *)rxDesc, cmdsts)); } } } while ((!early_stop) && ae531x_ReadDmaReg(MACInfo, DmaStatus) & DmaIntRxCompleted);rx_all_done: AE531X_PRINT(AE531X_DEBUG_RX, ("rx done (%d)\n", rxDescCount)); *budget -= rxDescCount; if (!early_stop) { netif_rx_complete(dev); ae531x_SetDmaReg(MACInfo, DmaIntrEnb, DmaIeRxCompleted | DmaIeRxNoBuffer); ae531x_WriteDmaReg(MACInfo, DmaRxPollDemand, 0); } retval = early_stop;rx_no_skbs: LEAVE();#ifdef DEBUG if (rxDescCount > rxDescCountMax) { printk("max rx %d\n", rxDescCount); rxDescCountMax = rxDescCount; }#endif return retval;}/******************************************************************************** ae531x_restart stops all ethernet devices associated with a physical MAC,* then shuts down the MAC. Then it re-opens all devices that were in use.* TBDXXX: needs testing!*/static voidae531x_restart(void *data){ ae531x_MAC_t *MACInfo = (ae531x_MAC_t *)data; ae531x_MAC_state_t *MAC_state = (ae531x_MAC_state_t *)MACInfo->OSinfo; struct net_device *saved_dev[AE531X_DEV_PER_MAC]; int i; for (i=0; i<AE531X_DEV_PER_MAC; i++) { if ((saved_dev[i] = MAC_state->dev_sw_state[i]->dev) != NULL) { ae531x_MAC_stop(saved_dev[i]); } } for (i=0; i<AE531X_DEV_PER_MAC; i++) { if (saved_dev[i]) ae531x_MAC_open(saved_dev[i]); }}/******************************************************************************** ae531x_MAC_intr handle interrupts from an ethernet MAC.* It checks MAC status registers, and dispatches as appropriate.*/voidae531x_MAC_intr(int cpl, void *dev_id, struct pt_regs *regs){ ae531x_MAC_state_t *MAC_state; ae531x_MAC_t *MACInfo; u32 regIsr; u32 regImr; u32 pendIntrs; ARRIVE(); MACInfo = (ae531x_MAC_t *)dev_id; MAC_state = (ae531x_MAC_state_t *)MACInfo->OSinfo; for(;;) { /* Clear any unhandled intr causes. */ ae531x_WriteDmaReg(MACInfo, DmaStatus, UnhandledIntrMask); regIsr = ae531x_ReadDmaReg(MACInfo, DmaStatus); regImr = ae531x_ReadDmaReg(MACInfo, DmaIntrEnb); pendIntrs = regIsr & regImr; AE531X_PRINT(AE531X_DEBUG_INT, ("ethmac%d: intIsr=0x%8.8x intImr=0x%8.8x pendIntrs=0x%8.8x\n", MACInfo->unit, regIsr, regImr, pendIntrs )); if ((pendIntrs & DmaAllIntCauseMask) == 0) break; if ((pendIntrs & DmaIntRxCompleted) || (pendIntrs & DmaIntRxNoBuffer)) { if (netif_rx_schedule_prep(MAC_state->dev_sw_state[MAC_state->primary_dev]->dev)) { ae531x_ClearDmaReg(MACInfo, DmaIntrEnb, DmaIeRxCompleted | DmaIeRxNoBuffer); ae531x_AckIntr(MACInfo, DmaIntRxCompleted | DmaIntRxNoBuffer); (void)ae531x_ReadDmaReg(MACInfo, DmaIntrEnb); __netif_rx_schedule(MAC_state->dev_sw_state[MAC_state->primary_dev]->dev); } else {#if 0 AE531X_PRINT(AE531X_DEBUG_ERROR, ("%s: Interrupt (0x%8.8x/0x%8.8x) while in poll. regs@%p, pc=%p, ra=%p\n", __FILE__, regIsr, ae531x_ReadDmaReg(MACInfo, DmaIntrEnb), (void *)regs, (void *)regs->cp0_epc, (void *)regs->regs[31]));#endif ae531x_AckIntr(MACInfo, DmaIntRxCompleted | DmaIntRxNoBuffer); } } if (pendIntrs & (DmaIntTxStopped | DmaIntTxJabber | DmaIntTxUnderflow)) { AE531X_PRINT(AE531X_DEBUG_ERROR, ("ethmac%d: TX Error Intr (0x%x)\n", MACInfo->unit, pendIntrs)); ae531x_AckIntr(MACInfo, (DmaIntTxStopped | DmaIntTxJabber | DmaIntTxUnderflow)); } if (pendIntrs & DmaIntBusError) { AE531X_PRINT(AE531X_DEBUG_ERROR, ("ethmac%d: DMA Bus Error Intr (0x%x)\n", MACInfo->unit, pendIntrs)); ae531x_AckIntr(MACInfo, DmaIntBusError); /* Reset the chip, if it's not already being done */ if (ae531x_IsInResetMode(MACInfo)) { goto intr_done; } ae531x_BeginResetMode(MACInfo); schedule_task(&MAC_state->restart_task); } if (pendIntrs & DmaIntRxStopped) { AE531X_PRINT(AE531X_DEBUG_ERROR, ("ethmac%d: RX Stopped Intr (0x%x)\n", MACInfo->unit, pendIntrs)); ae531x_AckIntr(MACInfo, DmaIntRxStopped); } } intr_done: LEAVE();}/******************************************************************************** ae531x_MAC_get_stats returns statistics for a specified device*/static struct net_device_stats*ae531x_MAC_get_stats(struct net_device *dev){ ae531x_dev_sw_state_t *dev_sw_state; ae531x_MAC_state_t *MAC_state; ARRIVE(); dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; MAC_state = dev_sw_state->MAC_state; LEAVE(); return &MAC_state->stats;}#define AE531X_PHY_POLL_SECONDS 2#if CONFIG_AR531X_COBRA/******************************************************************************** ae531x_getMACInfo returns the MACInfo of the interface given by unit */ae531x_MAC_t *ae531x_getMAcInfo(int ethUnit){ int i,j; for(i=0;i<AR531X_NUM_ENET_MAC;++i) { if(per_MAC_info[i].dev_sw_state) { for(j=0;j<AE531X_DEV_PER_MAC;++j) { if(per_MAC_info[i].dev_sw_state[j] && per_MAC_info[i].dev_sw_state[j]->enetUnit == ethUnit) return (&(per_MAC_info[i].MACInfo)); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -