diff -urN linux-2.4.32.new/arch/mips/ar531x/ae531xlnx.c linux-2.4.32.new-eth/arch/mips/ar531x/ae531xlnx.c --- linux-2.4.32.new/arch/mips/ar531x/ae531xlnx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/ae531xlnx.c 2005-12-25 11:54:20.756273952 +0000 @@ -0,0 +1,1534 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "ar531xlnx.h" +#include "ae531xreg.h" +#include "ae531xmac.h" + +/* + * A word about CONFIG_VENETDEV: It's intended to support two + * "virtualized ethernet devices" that share a single underlying MAC. + * To upper layers, it appears that the hardware supports two MACs, + * with enet0 used for LAN ports and enet1 used for a WAN port. This + * is useful, for instance, when building a 5-port router on hardware + * that uses only one of the AR5312's ethernet MACs. + * + * Virtualization is accomplished through trickery at the ethernet + * PHY layer. We use PHY hardware to determine which port a packet + * was received on, and in order to direct a packet to a particular + * port or set of ports. + * + * The code is mostly written to be generalized to more than two + * virtual devices; but it's intended for one multi-port LAN enet + * device and one single-port WAN enet device. + */ + +#define AE531X_LAN_PORT 0 +#ifdef CONFIG_VENETDEV +#define AE531X_DEV_PER_MAC 2 +#define AE531X_WAN_PORT 1 +#else +#define AE531X_DEV_PER_MAC 1 +#endif + + +/* + * 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; + +/* + * 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. + * In the case of CONFIG_VENETDEV, each virtual device has its own + * ae531x_dev_sw_state, and virtual devices that share a physical MAC + * point to the same ae531x_MAC_state. + */ +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 */ +#ifdef CONFIG_VENETDEV + BOOL isLAN; /* 0-->WAN; 1-->LAN */ +#endif +} ae531x_dev_sw_state_t; + +/* + * Driver-independent linux-specific per-ethernet device software information. + * Regarding CONFIG_VENETDEV: If a system has 2 physical MACs, and each + * physical MAC has 2 virtual ethernet devices (one for LAN and one for WAN), + * then there are a total of 4 ethernet devices. + */ +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??? + */ + +#ifdef CONFIG_VLAN_8021Q +#define ETH_MAX_MTU 1522 +#define HLEN 18 +#else +#define ETH_MAX_MTU 1518 +#define HLEN ETH_HLEN +#endif + +#define AE531X_RX_BUF_SIZE \ + (((2 + RXBUFF_RESERVE + HLEN + ETH_MAX_MTU + PHY_TRAILER_SIZE) + 3) & ~3) + +/* Forward references to local functions */ +static void ae531x_TxReap(ae531x_MAC_state_t *MAC_state); +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); + +/* Global to track number of MACs */ + +int ar531x_num_enet_macs; + +#undef DEBUG_VENETDEV +#define AR531X_NAPI + +#if defined(CONFIG_VENETDEV) && defined(DEBUG_VENETDEV) +static int cloned_counter; +static int expand_counter; +static int both_counter; +#endif + +#ifdef AR531X_NAPI +/******************************************************************************* +* ae531x_MAC_poll checks for received packets, and sends data +* up the stack. +*/ +int +ae531x_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; + int early_stop; + int retval; +#ifdef CONFIG_VENETDEV + int i; +#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; + +#ifdef CONFIG_VENETDEV + /* + * Non-primary devs don't explicitly get polled by the upper layers; + * rather, they rely on primary_dev polling to feed packets. But in + * order to keep netif_receive_skb happy, we need to temporarily put the + * net_devices into "polling mode". We pull them back out before + * leaving this function. + */ + for (i=0; idev_sw_state[i]->dev) && + (MAC_state->dev_sw_state[i]->unit_on_MAC != MAC_state->primary_dev)) { + netif_rx_schedule(MAC_state->dev_sw_state[i]->dev); + } + } +#endif + rxDescCount = 0; + + early_stop = 0; + do { + for(;;) { + // ae531x_AckIntr(MACInfo, (DmaIntRxCompleted | DmaIntRxNoBuffer)); + + 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, rxBufp); + AE531X_DESC_SWPTR_SET(rxDesc, newskb); + } + + AE531X_DESC_STATUS_SET(rxDesc, DescOwnByDma); + A_DATA_CACHE_FLUSH_INVAL(rxDesc, AE531X_DESC_SIZE); + // 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_VENETDEV + /* Determine which associated device owns this rx buffer */ + { + int fromLAN; + + fromLAN = phyDetermineSource(skb->data, length); + + if (fromLAN == -1) { + /* + * Could not determine source, so drop the packet + */ + dev_kfree_skb(skb); + continue; + } + + length -= PHY_TRAILER_SIZE; + if (fromLAN) { + dev_sw_state = MAC_state->dev_sw_state[AE531X_LAN_PORT]; + } else { + dev_sw_state = MAC_state->dev_sw_state[AE531X_WAN_PORT]; + } + } +#endif + rxdev = dev_sw_state->dev; + + if (rxdev == NULL) { + /* + * We received a packet for a virtual enet device + * that is no longer up. Ignore it. + */ + dev_kfree_skb(skb); + continue; + } + + /* Advance data pointer to show that there's data here */ + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, rxdev); + skb->dev = rxdev; + 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)) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("Runt | RX Late Collision Error\n")); + MAC_state->stats.collisions++; + } + + if (cmdsts & DescRxLengthError) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("RX Length Error\n")); + MAC_state->stats.rx_length_errors++; + } + + if (cmdsts & DescRxCrc) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("RX CRC Error\n")); + MAC_state->stats.rx_crc_errors++; + } + + if (cmdsts & DescRxDribbling) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("Dribbling Error\n")); + MAC_state->stats.rx_frame_errors++; + } + + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("Bad receive. rxDesc=%p cmdsts=0x%8.8x\n", + (void *)rxDesc, cmdsts)); + + /* Give this one back */ + AE531X_DESC_STATUS_SET(rxDesc, DescOwnByDma); + A_DATA_CACHE_FLUSH_INVAL(rxDesc, AE531X_DESC_SIZE); + sysWbFlush(); + } + } + } 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: + +#ifdef CONFIG_VENETDEV + for (i=0; idev_sw_state[i]->dev) && + (MAC_state->dev_sw_state[i]->unit_on_MAC != + MAC_state->primary_dev)) { + netif_rx_complete(MAC_state->dev_sw_state[i]->dev); + } + } +#endif + + LEAVE(); + return retval; +} +#endif + +#ifndef AR531X_NAPI +/******************************************************************************* +* ae531x_MAC_recv checks for received packets, and sends data +* up the stack. +*/ +static void +ae531x_MAC_recv(struct net_device *dev) +{ + 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; + struct net_device *rxdev; + int retval; + + 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; + + for(;;) { + 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 */ + break; + } + + 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, rxBufp); + AE531X_DESC_SWPTR_SET(rxDesc, newskb); + } + + AE531X_DESC_STATUS_SET(rxDesc, DescOwnByDma); + A_DATA_CACHE_FLUSH_INVAL(rxDesc, AE531X_DESC_SIZE); + sysWbFlush(); + + if (newskb == NULL) { + break; + } else { +#ifdef CONFIG_VENETDEV + /* Determine which associated device owns this rx buffer */ + { + int fromLAN; + + fromLAN = phyDetermineSource(skb->data, length); + + if (fromLAN == -1) { + /* + * Could not determine source, so drop the packet + */ + dev_kfree_skb(skb); + continue; + } + + length -= PHY_TRAILER_SIZE; + if (fromLAN) { + dev_sw_state = + MAC_state->dev_sw_state[AE531X_LAN_PORT]; + } else { + dev_sw_state = + MAC_state->dev_sw_state[AE531X_WAN_PORT]; + } + } +#endif + rxdev = dev_sw_state->dev; + + if (rxdev == NULL) { + /* + * We received a packet for a virtual enet device + * that is no longer up. Ignore it. + */ + dev_kfree_skb(skb); + continue; + } + + /* Advance data pointer to show that there's data here */ + skb_put(skb, length); + skb->protocol = eth_type_trans(skb, rxdev); + skb->dev = rxdev; + rxdev->last_rx = jiffies; + rxdev->quota--; + + 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_rx(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_PRINT(AE531X_DEBUG_ERROR, + ("Bad receive. rxDesc=%p cmdsts=0x%8.8x\n", + (void *)rxDesc, cmdsts)); + } + } + + LEAVE(); + return; +} +#endif + +/******************************************************************************* +* 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 void +ae531x_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; idev_sw_state[i]->dev) != NULL) { + ae531x_MAC_stop(saved_dev[i]); + } + } + + for (i=0; iOSinfo; + primary_dev = MAC_state->dev_sw_state[MAC_state->primary_dev]->dev; + + 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\n", + MACInfo->unit, regIsr, regImr)); + + if ((pendIntrs & DmaAllIntCauseMask) == 0) + break; + + if ((pendIntrs & DmaIntRxCompleted) || + (pendIntrs & DmaIntRxNoBuffer)) { +#ifdef AR531X_NAPI + if (netif_rx_schedule_prep(primary_dev)) { + ae531x_ClearDmaReg(MACInfo, + DmaIntrEnb, + DmaIeRxCompleted | DmaIeRxNoBuffer); + ae531x_AckIntr(MACInfo, + DmaIntRxCompleted | DmaIntRxNoBuffer); + (void)ae531x_ReadDmaReg(MACInfo, DmaIntrEnb); + __netif_rx_schedule(primary_dev); + } else { + 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])); + ae531x_ClearDmaReg(MACInfo, + DmaIntrEnb, + DmaIeRxCompleted | DmaIeRxNoBuffer); + ae531x_AckIntr(MACInfo, + DmaIntRxCompleted | DmaIntRxNoBuffer); + } +#else + ae531x_MAC_recv(primary_dev); + ae531x_AckIntr(MACInfo, + DmaIntRxCompleted | DmaIntRxNoBuffer); +#endif + } + + 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); + } + } + + ae531x_DmaIntEnable(MACInfo); + +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 + +/******************************************************************************* +* ae531x_phy_poll periodically checks for changes in phy status +* (e.g. dropped link). +*/ +static int +ae531x_phy_poll(void *data) +{ + ae531x_dev_sw_state_t *dev_sw_state = (ae531x_dev_sw_state_t *)data; + ae531x_MAC_t *MACInfo = &dev_sw_state->MAC_state->MACInfo; + int unit = dev_sw_state->enetUnit; + +#if defined(CONFIG_VENETDEV) && defined(DEBUG_VENETDEV) + int previous_cloned = 0; + int previous_expand = 0; + int previous_both = 0; +#endif + + daemonize(); + reparent_to_init(); + spin_lock_irq(¤t->sigmask_lock); + sigemptyset(¤t->blocked); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + snprintf(current->comm, sizeof(current->comm), "%s", + dev_sw_state->dev->name); + + for(;;) { + if (MACInfo->port_is_up) { + phyCheckStatusChange(unit); + } + +#if defined(CONFIG_VENETDEV) && defined(DEBUG_VENETDEV) + if (cloned_counter != previous_cloned) { + printk("Cloned Counter: %d (delta %d)\n", + cloned_counter, cloned_counter - previous_cloned); + previous_cloned = cloned_counter; + } + if (expand_counter != previous_expand) { + printk("Expand Counter: %d (delta %d)\n", + expand_counter, expand_counter - previous_expand); + previous_expand = expand_counter; + } + if (both_counter != previous_both) { + printk("Expand & Cloned Counter: %d (delta %d)\n", + both_counter, both_counter - previous_both); + previous_both = both_counter; + } +#endif + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AE531X_PHY_POLL_SECONDS * HZ); + } + + return 0; +} + + +/******************************************************************************* +* ae531x_MAC_set_rx_mode is used to set the RX mode options, such as +* promiscuous or multicast. +*/ +static void +ae531x_MAC_set_rx_mode(struct net_device *dev) +{ + ae531x_dev_sw_state_t *dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + ae531x_MAC_state_t *MAC_state=dev_sw_state->MAC_state; + ae531x_MAC_t *MACInfo = &MAC_state->MACInfo; + + if (dev->flags & IFF_PROMISC) { + ae531x_SetMacReg(MACInfo, MacControl, MacPromiscuousModeOn); + } else { + ae531x_ClearMacReg(MACInfo, MacControl, MacPromiscuousModeOn); + } + + if (dev->flags & IFF_MULTICAST) { + ae531x_SetMacReg(MACInfo, MacControl, MacMulticastFilterOff); + } else { + ae531x_ClearMacReg(MACInfo, MacControl, MacMulticastFilterOff); + } +} + +/******************************************************************************* +* ae531x_MAC_open is the standard Linux open function. It puts +* hardware into a known good state, allocates queues, starts +* the phy polling task, and arranges for interrupts to be handled. +*/ +static int +ae531x_MAC_open(struct net_device *dev) +{ + ae531x_dev_sw_state_t *dev_sw_state; + ae531x_MAC_state_t *MAC_state; + ae531x_MAC_t *MACInfo; + int rv; + struct tq_struct *restart_task; + pid_t phy_poll_pid; + + ARRIVE(); + + dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + dev_sw_state->dev = dev; + MAC_state = dev_sw_state->MAC_state; + MACInfo = &MAC_state->MACInfo; + + restart_task = &MAC_state->restart_task; + restart_task->routine = ae531x_restart; + restart_task->data = (void *)MACInfo; + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ae531x_MAC_open eth%d ethmac%d macBase=0x%x dmaBase=0x%x irq=0x%x\n", + dev_sw_state->enetUnit, + MACInfo->unit, + MACInfo->macBase, + MACInfo->dmaBase, + MAC_state->irq)); + + if (!MACInfo->port_is_up) { + /* Bring MAC and PHY out of reset */ + ae531x_reset(MACInfo); + + /* Attach interrupt handler */ + rv = request_irq(MAC_state->irq, ae531x_MAC_intr, SA_INTERRUPT, + "ae531x_MAC_intr", (void *)MACInfo); + if (rv < 0) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("request_irq(0x%x) failed (%d)\n", + MAC_state->irq, rv)); + goto open_failure; + } + + /* Initialize PHY */ + phySetup(MACInfo->unit, MACInfo->phyBase); + + /* Start thread to poll for phy link status changes */ + phy_poll_pid = kernel_thread(ae531x_phy_poll, + dev_sw_state, + CLONE_FS | CLONE_FILES); + if (phy_poll_pid < 0) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("ethmac%d unable to start Phy Poll thread\n", + MACInfo->unit)); + } + + /* Allocate RX/TX Queues */ + if (ae531x_AllocateQueues(MACInfo) < 0) { + AE531X_PRINT(AE531X_DEBUG_RESET, ("Queue allocation failed")); + free_irq(MAC_state->irq, (void *)MACInfo); + goto open_failure; + } + + /* Initialize DMA and descriptors */ + ae531x_DmaReset(MACInfo); + + /* Initialize MAC */ + ae531x_MACReset(MACInfo); + + /* Set RX mode */ + ae531x_MAC_set_rx_mode(dev); + + /* Enable Receive/Transmit */ + ae531x_EnableComm(MACInfo); + + MAC_state->primary_dev = dev_sw_state->unit_on_MAC; + MACInfo->port_is_up = TRUE; + } + + dev->trans_start = jiffies; + + LEAVE(); + return 0; + +open_failure: + LEAVE(); + return -1; +} + +/* + * Shut down MAC hardware. + */ +static void +ae531x_MAC_shutdown(ae531x_MAC_state_t *MAC_state) +{ + ae531x_MAC_t *MACInfo; + + MACInfo = &MAC_state->MACInfo; + MACInfo->port_is_up = FALSE; + + /* Disable Receive/Transmit */ + ae531x_DisableComm(MACInfo); + + /* Disable Interrupts */ + ae531x_DmaIntDisable(MACInfo); + sysWbFlush(); + free_irq(MAC_state->irq, (void *)MACInfo); + + /* Free Transmit & Receive skb's/descriptors */ + ae531x_TxReap(MAC_state); /* one last time */ + ae531x_FreeQueues(MACInfo); +} + +/******************************************************************************* +* ae531x_MAC_stop is the standard Linux stop function. It undoes +* everything set up by ae531x_MAC_open. +*/ +static int +ae531x_MAC_stop(struct net_device *dev) +{ + ae531x_dev_sw_state_t *dev_sw_state; + ae531x_MAC_state_t *MAC_state; + ae531x_MAC_t *MACInfo; + int i; + + ARRIVE(); + + dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + MAC_state = dev_sw_state->MAC_state; + MACInfo = &MAC_state->MACInfo; + + for (i=0; idev_sw_state[i]->dev) && + (MAC_state->dev_sw_state[i]->dev != dev_sw_state->dev)) { + break; + } + } + + if (i < AE531X_DEV_PER_MAC) { + /* Physical MAC is still in use */ + if (MAC_state->primary_dev == dev_sw_state->unit_on_MAC) { + /* + * If the primary_dev is being stopped + * then we need to assign a new one. + */ + MAC_state->primary_dev = i; + } + } else { + /* Physical MAC is no longer in use */ + ae531x_MAC_shutdown(MAC_state); + } + + dev_sw_state->dev = NULL; + + LEAVE(); + return 0; +} + +/******************************************************************************* +* ae531x_rxbuf_alloc - Allocate an skb to be associated with an RX descriptor. +* +* RETURNS: A pointer to the skb. Also returns a pointer to the underlying +* buffer and the size of that buffer. +*/ +void * +ae531x_rxbuf_alloc(ae531x_MAC_t *MACInfo, char **rxBuffp, int *rxBuffSizep) +{ + struct sk_buff *skb; + char *rxBuff; + int rxBuffSize; + + skb = dev_alloc_skb(AE531X_RX_BUF_SIZE); + if (skb) { + /* Add 2 to align the IP header on a DWORD boundary */ + skb_reserve(skb, RXBUFF_RESERVE + 2); + + rxBuffSize = skb_tailroom(skb); + rxBuff = skb->tail; + + *rxBuffp = rxBuff; + *rxBuffSizep = rxBuffSize; + + A_DATA_CACHE_INVAL(rxBuff, rxBuffSize); + } + + return skb; +} + +/******************************************************************************* +* ae531x_swptr_free - Free the skb, if any, associated with a descriptor. +*/ +void +ae531x_swptr_free(VIRT_ADDR desc) +{ + struct sk_buff *skb; + + skb = (struct sk_buff *)AE531X_DESC_SWPTR_GET(desc); + if (skb) { + AE531X_DESC_SWPTR_SET(desc, NULL); + kfree_skb(skb); + } +} + +/******************************************************************************* +* +* ae531x_TxReap - the driver Tx completion routine. +* +* This routine reaps sk_buffs which have already been transmitted. +* +*/ +static void +ae531x_TxReap(ae531x_MAC_state_t *MAC_state) +{ + AE531X_QUEUE *txq; + VIRT_ADDR txDesc; + UINT32 cmdsts; + struct sk_buff *skb; + int reaped; + ae531x_MAC_t *MACInfo; + static int aeUselessReap = 0; + + ARRIVE(); + + MACInfo = &MAC_state->MACInfo; + txq = &MACInfo->txQueue; + reaped = 0; + + while (1) { + txDesc = AE531X_QUEUE_ELE_NEXT_GET(txq, txq->reapDescAddr); + if (txDesc == txq->curDescAddr) { + break; + } + + cmdsts = AE531X_DESC_STATUS_GET(KSEG1ADDR(txDesc)); + if (cmdsts & DescOwnByDma) { + break; + } + + /* Release sk_buff associated with completed transmit */ + skb = (struct sk_buff *)AE531X_DESC_SWPTR_GET(txDesc); + + if (skb) { + kfree_skb(skb); + AE531X_DESC_SWPTR_SET(txDesc, NULL); + } + + /* Update statistics according to completed transmit desc */ + if (cmdsts & DescTxErrors) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("enetmac%d Tx prior error: 0x%8.8x <0x%8.8x> 0x%8.8x\n", + MACInfo->unit, + cmdsts, + DescTxErrors, + (int)txDesc)); + MAC_state->stats.tx_errors++; + if (cmdsts & (DescTxLateCollision | DescTxExcCollisions)) { + MAC_state->stats.tx_aborted_errors++; + } + if (cmdsts & (DescTxLostCarrier | DescTxNoCarrier)) { + MAC_state->stats.tx_carrier_errors++; + } + } else { + MAC_state->stats.tx_bytes += AE531X_DESC_STATUS_RX_SIZE(cmdsts); + MAC_state->stats.tx_packets++; + } + + MAC_state->stats.collisions += + ((cmdsts & DescTxCollMask) >> DescTxCollShift); + + txq->reapDescAddr = txDesc; + reaped++; + } + + if (reaped > 0) { + int i; + + AE531X_PRINT(AE531X_DEBUG_TX_REAP, + ("reaped %d\n", reaped)); + + /* + * Re-start transmit queues for all ethernet devices + * associated with this MAC. + */ + for (i=0; idev_sw_state[i]->dev) + netif_start_queue(MAC_state->dev_sw_state[i]->dev); + } + } else { + aeUselessReap++; + } + + LEAVE(); +} + +/******************************************************************************* +* ae531x_MAC_start_xmit sends a packet. +*/ +static int +ae531x_MAC_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + ae531x_dev_sw_state_t *dev_sw_state; + ae531x_MAC_state_t *MAC_state; + ae531x_MAC_t *MACInfo; + u32 buf; + u32 ctrlen; + u32 length; + int mtu; + int max_buf_size; + VIRT_ADDR txDesc; + + ARRIVE(); + + dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + MAC_state = dev_sw_state->MAC_state; + MACInfo = &MAC_state->MACInfo; + + length = skb->len; + + /* Check if this port is up, else toss packet */ + if (!MACInfo->port_is_up) { + buf = virt_to_bus(skb->data); + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("eth%d Tx Down, dropping buf=0x%8.8x, length=0x%8.8x, skb=%p\n", + dev_sw_state->enetUnit, buf, length, (void *)skb)); + + MAC_state->stats.tx_dropped++; + MAC_state->stats.tx_carrier_errors++; + goto dropFrame; + } + + if (ae531x_IsInResetMode(MACInfo)) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("eth%d Tx: In Chip reset - drop frame\n", + dev_sw_state->enetUnit)); + + MAC_state->stats.tx_dropped++; + MAC_state->stats.tx_aborted_errors++; + goto dropFrame; + } + + /* Check if we can transport this packet */ + length = max((u32)60, length); /* total length */ + mtu = dev->mtu; + max_buf_size = mtu + HLEN; + if (length > max_buf_size) { + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("eth%d Tx: length %d too long. mtu=%d, trailer=%d\n", + dev_sw_state->enetUnit, length, mtu, PHY_TRAILER_SIZE)); + + MAC_state->stats.tx_errors++; + MAC_state->stats.tx_aborted_errors++; + + goto dropFrame; + } + + /* Reap any old, completed Tx descriptors */ + ae531x_TxReap(MAC_state); + + txDesc = MACInfo->txQueue.curDescAddr; + if (txDesc == MACInfo->txQueue.reapDescAddr) { + int i; + + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("eth%d Tx: cannot get txDesc\n", + dev_sw_state->enetUnit)); + + MAC_state->stats.tx_dropped++; + MAC_state->stats.tx_fifo_errors++; + + /* + * Stop transmit queues for any ethernet devices + * associated with this MAC. + */ + for (i=0; idev_sw_state[i]->dev) + netif_stop_queue(MAC_state->dev_sw_state[i]->dev); + } + goto dropFrame; + } + +#ifdef CONFIG_VENETDEV + { + struct sk_buff *newskb; + + if (skb_cloned(skb) || (skb_tailroom(skb) < PHY_TRAILER_SIZE)) { +#ifdef DEBUG_VENETDEV + if (skb_cloned(skb)) { + cloned_counter++; + if (skb_tailroom(skb) < PHY_TRAILER_SIZE) { + both_counter++; + } + } else { + expand_counter++; + } +#endif + newskb = skb_copy_expand(skb, 0, PHY_TRAILER_SIZE, GFP_ATOMIC); + if (newskb == NULL) { + goto dropFrame; + } + + dev_kfree_skb(skb); + skb = newskb; + } + + phySetDestinationPort(skb->data, length, dev_sw_state->isLAN); + skb_put(skb, PHY_TRAILER_SIZE); + length += PHY_TRAILER_SIZE; + } +#endif + + /* We won't fail now; so consume this descriptor */ + AE531X_CONSUME_DESC((&MACInfo->txQueue)); + + /* Update the descriptor */ + buf = virt_to_bus(skb->data); + A_DATA_CACHE_FLUSH(skb->data, skb->len); + AE531X_DESC_BUFPTR_SET(txDesc, buf); + AE531X_DESC_SWPTR_SET(txDesc, skb); + ctrlen = AE531X_DESC_CTRLEN_GET(txDesc); + ctrlen = (ctrlen & (DescEndOfRing)) | + DescTxFirst | + DescTxLast | + DescTxIntEnable; + + ctrlen |= ((length << DescSize1Shift) & DescSize1Mask); + + AE531X_DESC_CTRLEN_SET(txDesc, ctrlen); + AE531X_DESC_STATUS_SET(txDesc, DescOwnByDma); + + AE531X_PRINT(AE531X_DEBUG_TX, + ("eth%d Tx: Desc=0x%8.8x, L=0x%8.8x, D=0x%8.8x, d=0x%8.8x, length=0x%8.8x\n", + dev_sw_state->enetUnit, + (UINT32)txDesc, + AE531X_DESC_CTRLEN_GET(txDesc), + buf, + AE531X_DESC_LNKBUF_GET(txDesc), + length)); + + /* Must not use txDesc after this point */ + A_DATA_CACHE_FLUSH_INVAL(txDesc, AE531X_DESC_SIZE); + + /* Alert DMA engine to resume Tx */ + ae531x_WriteDmaReg(MACInfo, DmaTxPollDemand, 0); + sysWbFlush(); + + MAC_state->stats.tx_packets++; + MAC_state->stats.tx_bytes += length; + + /* Tell upper layers to keep it coming */ + dev->trans_start = jiffies; + + LEAVE(); + return 0; + +dropFrame: + dev_kfree_skb(skb); + + LEAVE(); + return 0; +} + + +/******************************************************************************* +* ae531x_MAC_tx_timeout handles transmit timeouts +*/ +static void +ae531x_MAC_tx_timeout(struct net_device *dev) +{ + ae531x_dev_sw_state_t *dev_sw_state; + ae531x_MAC_state_t *MAC_state; + ae531x_MAC_t *MACInfo; + + ARRIVE(); + + dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + MAC_state = dev_sw_state->MAC_state; + MACInfo = &MAC_state->MACInfo; + + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("enet%d: Tx timeout\n", dev_sw_state->enetUnit)); + + ae531x_restart(MACInfo); + + LEAVE(); +} + + +/******************************************************************************* +* ae531x_MAC_do_ioctl is a placeholder for future ioctls. +*/ +static int +ae531x_MAC_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int rv; + ae531x_MAC_t *MACInfo; + struct ioctl_data { + u32 unit; + u32 addr; + u32 data; + } *req; + 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; + MACInfo = &MAC_state->MACInfo; + + req = (struct ioctl_data *)ifr->ifr_data; + + switch( cmd ) { + default: + AE531X_PRINT(AE531X_DEBUG_ERROR, + ("Unsupported ioctl: 0x%x\n", cmd)); + rv = -EOPNOTSUPP; + } + + LEAVE(); + return rv; +} + +/******************************************************************************* +* ae531x_MAC_set_mac_address sets a new hardware address for the device +*/ +static int +ae531x_MAC_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *saddr = (struct sockaddr *)addr; + + /* update dev struct */ + memcpy(dev->dev_addr, &saddr->sa_data[0], dev->addr_len); + + return 0; +} + +/****************************************************************************** +* macAddrGet - Given a MACInfo pointer, return a pointer to an +* array of chars that holds the corresponding MAC address. +*/ +char *macAddrGet(ae531x_MAC_t *MACInfo) +{ + // return enet_mac_address_get(MACInfo->unit); + return ae531x_MAC_dev[MACInfo->unit]->dev_addr; +} + +static void +ae531x_MAC_setup_fntable(struct net_device *dev) +{ + /* Set a default (should be overridden by software) */ + u8 default_MAC_address[] = { 0x00, 0x03, 0x7f, 0xe0, 0x02, 0xbF }; + + ARRIVE(); + + dev->get_stats = ae531x_MAC_get_stats; + dev->open = ae531x_MAC_open; + dev->stop = ae531x_MAC_stop; + dev->hard_start_xmit = ae531x_MAC_start_xmit; + dev->do_ioctl = ae531x_MAC_do_ioctl; +#ifdef AR531X_NAPI + dev->poll = ae531x_MAC_poll; + dev->weight = 16; +#endif + dev->tx_timeout = ae531x_MAC_tx_timeout; + dev->features = NETIF_F_HW_CSUM |\ + NETIF_F_HIGHDMA; + dev->set_mac_address = ae531x_MAC_set_mac_address; + dev->set_multicast_list = ae531x_MAC_set_rx_mode; + + /* Copy default MAC address into device descriptor */ + memcpy(dev->dev_addr, default_MAC_address, dev->addr_len ); + + LEAVE(); +} + +/* + * ae531x_twisted_enet() returns 1 for chips where there is only one usable + * MAC, and that MAC is 1. + */ +static BOOL +ae531x_twisted_enet(void) +{ + int wisoc_revision; + int flash_bus_width; + + wisoc_revision = (sysRegRead(AR531X_REV) & AR531X_REV_MAJ) >> + AR531X_REV_MAJ_S; + if (wisoc_revision == AR531X_REV_MAJ_AR2313) + return TRUE; + + flash_bus_width = sysRegRead(AR531X_FLASHCTL0) & FLASHCTL_MWx16; + + if (flash_bus_width == 0) { + printk("Found AR2312-01\n"); + return TRUE; /* AR2312-01 has 8 bit flash bus */ + } else { + printk("Found AR2312-00\n"); + return FALSE; + } +} + +int +ae531x_MAC_setup(void) +{ + int i; + int next_dev; + int rev; + struct net_device *dev; + ae531x_dev_sw_state_t *dev_sw_state; + ae531x_MAC_state_t *MAC_state; + ae531x_MAC_t *MACInfo; + + ARRIVE(); + +#if 0 + /* + * This does not work since the AR2312 and AR5312 both have the same + * revision information in the CPU :-( + */ + rev = (sysRegRead(AR531X_REV) & AR531X_REV_CHIP); + + if ((rev & AR531X_REV_MIN) == AR5312_REV_MIN_SINGLE_ENET) { + ar531x_num_enet_macs = 1; + } else { + ar531x_num_enet_macs = 2; + } +#else + /* + * Need to select the number of ethernet MACs based on the config + * information (sadly) + */ +#ifdef CONFIG_AR5312 + ar531x_num_enet_macs = 2; +#else + ar531x_num_enet_macs = 1; +#endif +#endif + + next_dev = 0; + for (i=0; ipriv; + dev_sw_state->enetUnit = next_dev; + dev_sw_state->unit_on_MAC = 0; + MAC_state = &per_MAC_info[i]; + dev_sw_state->MAC_state = MAC_state; + MAC_state->dev_sw_state[AE531X_LAN_PORT] = dev_sw_state; + MAC_state->primary_dev = -1; + + next_dev++; + +#ifdef CONFIG_VENETDEV + { + ae531x_dev_sw_state_t *lan_dev_sw_state; + + lan_dev_sw_state = dev_sw_state; + + dev = ae531x_MAC_dev[next_dev] = + init_etherdev(NULL, sizeof(ae531x_dev_sw_state_t)); + + if (dev == NULL) { + LEAVE(); + return -1; + } + + ae531x_MAC_setup_fntable(dev); + + dev_sw_state = (ae531x_dev_sw_state_t *)dev->priv; + dev_sw_state->enetUnit = next_dev; + dev_sw_state->unit_on_MAC = 1; + dev_sw_state->MAC_state = MAC_state; + MAC_state->dev_sw_state[AE531X_WAN_PORT] = dev_sw_state; + lan_dev_sw_state->isLAN = TRUE; /* enet0 is LAN */ + dev_sw_state->isLAN = FALSE ; /* enet1 is WAN */ + + next_dev++; + } +#endif + + /* Initialize per-MAC information */ + MACInfo = &MAC_state->MACInfo; + MACInfo->unit = i; + + if (ar531x_num_enet_macs == 1) { + if (ae531x_twisted_enet()) { + MACInfo->macBase = + (u32)(PHYS_TO_K1(AR531X_ENET1)+AE531X_MAC_OFFSET); + MACInfo->dmaBase = + (u32)(PHYS_TO_K1(AR531X_ENET1)+AE531X_DMA_OFFSET); + MACInfo->phyBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_PHY_OFFSET); + MAC_state->irq = AR531X_IRQ_ENET1_INTRS; + } else { + MACInfo->macBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_MAC_OFFSET); + MACInfo->dmaBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_DMA_OFFSET); + MACInfo->phyBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_PHY_OFFSET); + MAC_state->irq = AR531X_IRQ_ENET0_INTRS; + } + } else { + if (MACInfo->unit == 0) { + MACInfo->macBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_MAC_OFFSET); + MACInfo->dmaBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_DMA_OFFSET); + MACInfo->phyBase = + (u32)(PHYS_TO_K1(AR531X_ENET0)+AE531X_PHY_OFFSET); + MAC_state->irq = AR531X_IRQ_ENET0_INTRS; + } else { + MACInfo->macBase = + (u32)(PHYS_TO_K1(AR531X_ENET1)+AE531X_MAC_OFFSET); + MACInfo->dmaBase = + (u32)(PHYS_TO_K1(AR531X_ENET1)+AE531X_DMA_OFFSET); + MACInfo->phyBase = + (u32)(PHYS_TO_K1(AR531X_ENET1)+AE531X_PHY_OFFSET); + MAC_state->irq = AR531X_IRQ_ENET1_INTRS; + } + } + MACInfo->OSinfo = (void *)MAC_state; + } + + LEAVE(); + return 0; +} + +module_init(ae531x_MAC_setup); + diff -urN linux-2.4.32.new/arch/mips/ar531x/ae531xmac.c linux-2.4.32.new-eth/arch/mips/ar531x/ae531xmac.c --- linux-2.4.32.new/arch/mips/ar531x/ae531xmac.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/ae531xmac.c 2005-12-25 11:54:20.771271672 +0000 @@ -0,0 +1,942 @@ +/* + * 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. + */ + +#if linux +#include +#include +#include +#include +#include +#include +#include + +#include "ar531xlnx.h" +#endif /* linux */ + +#include "ae531xreg.h" +#include "ae531xmac.h" + +int ae531x_MAC_debug = AE531X_DEBUG_ERROR; + +/* + * These externs are for functions that this layer relies on + * that have OS-dependent implementations. + */ +extern UINT8 *macAddrGet(ae531x_MAC_t *MACInfo); + +/* Forward references to local functions */ +static void ae531x_QueueDestroy(AE531X_QUEUE *q); + + +/****************************************************************************** +* +* ae531x_ReadMacReg - read AE MAC register +* +* RETURNS: register value +*/ +UINT32 +ae531x_ReadMacReg(ae531x_MAC_t *MACInfo, UINT32 reg) +{ + UINT32 addr = MACInfo->macBase+reg; + UINT32 data; + + data = RegRead(addr); + return data; +} + + +/****************************************************************************** +* +* ae531x_WriteMacReg - write AE MAC register +* +* RETURNS: N/A +*/ +void +ae531x_WriteMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 data) +{ + UINT32 addr = MACInfo->macBase+reg; + + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_SetMacReg - set bits in AE MAC register +* +* RETURNS: N/A +*/ +void +ae531x_SetMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val) +{ + UINT32 addr = MACInfo->macBase+reg; + UINT32 data = RegRead(addr); + + data |= val; + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_ClearMacReg - clear bits in AE MAC register +* +* RETURNS: N/A +*/ +void +ae531x_ClearMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val) +{ + UINT32 addr = MACInfo->macBase+reg; + UINT32 data = RegRead(addr); + + data &= ~val; + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_ReadDmaReg - read AE DMA register +* +* RETURNS: register value +*/ +UINT32 +ae531x_ReadDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg) +{ + UINT32 addr = MACInfo->dmaBase+reg; + UINT32 data = RegRead(addr); + + return data; +} + + +/****************************************************************************** +* +* ae531x_WriteDmaReg - write AE DMA register +* +* RETURNS: N/A +*/ +void +ae531x_WriteDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 data) +{ + UINT32 addr = MACInfo->dmaBase+reg; + + RegWrite(data, addr); +} + + +/****************************************************************************** + * + * ae531x_AckIntr - clear interrupt bits in the status register. + * Note: Interrupt bits are *cleared* by writing a 1. + */ +void +ae531x_AckIntr(ae531x_MAC_t *MACInfo, UINT32 data) +{ + ae531x_WriteDmaReg(MACInfo, DmaStatus, data); +} + + +/****************************************************************************** +* +* ae531x_SetDmaReg - set bits in an AE DMA register +* +* RETURNS: N/A +*/ +void +ae531x_SetDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val) +{ + UINT32 addr = MACInfo->dmaBase+reg; + UINT32 data = RegRead(addr); + + data |= val; + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_ClearDmaReg - clear bits in an AE DMA register +* +* RETURNS: N/A +*/ +void +ae531x_ClearDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val) +{ + UINT32 addr = MACInfo->dmaBase+reg; + UINT32 data = RegRead(addr); + + data &= ~val; + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_ReadMiiReg - read PHY registers via AE MAC Mii addr/data registers +* +* RETURNS: register value +*/ +UINT32 +ae531x_ReadMiiReg(UINT32 phyBase, UINT32 reg) +{ + UINT32 data; + UINT32 addr = phyBase+reg; + + data = RegRead(addr); + return data; +} + + +/****************************************************************************** +* +* ae531x_WriteMiiReg - write PHY registers via AE MAC Mii addr/data registers +* +* RETURNS: N/A +*/ +void +ae531x_WriteMiiReg(UINT32 phyBase, UINT32 reg, UINT32 data) +{ + UINT32 addr = phyBase+reg; + + RegWrite(data, addr); +} + + +/****************************************************************************** +* +* ae531x_MiiRead - read AE Mii register +* +* RETURNS: register value +*/ +UINT16 +ae531x_MiiRead(UINT32 phyBase, UINT32 phyAddr, UINT8 reg) +{ + UINT32 addr; + UINT16 data; + + addr = ((phyAddr << MiiDevShift) & MiiDevMask) | ((reg << MiiRegShift) & MiiRegMask); + + ae531x_WriteMiiReg(phyBase, MacMiiAddr, addr ); + do { + /* nop */ + } while ((ae531x_ReadMiiReg(phyBase, MacMiiAddr ) & MiiBusy) == MiiBusy); + + data = ae531x_ReadMiiReg(phyBase, MacMiiData) & 0xFFFF; + + return data; +} + + +/****************************************************************************** +* +* ae531x_MiiWrite - write AE Mii register +* +* RETURNS: N/A +*/ +void +ae531x_MiiWrite(UINT32 phyBase, UINT32 phyAddr, UINT8 reg, UINT16 data) +{ + UINT32 addr; + + ae531x_WriteMiiReg(phyBase, MacMiiData, data ); + + addr = ((phyAddr << MiiDevShift) & MiiDevMask) | + ((reg << MiiRegShift) & MiiRegMask) | MiiWrite; + ae531x_WriteMiiReg(phyBase, MacMiiAddr, addr ); + + do { + /* nop */ + } while ((ae531x_ReadMiiReg(phyBase, MacMiiAddr ) & MiiBusy) == MiiBusy); +} + + +/******************************************************************************* +* ae531x_BeginResetMode - enter a special "reset mode" in which +* -no interrupts are expected from the device +* -the device will not transmit nor receive +* -attempts to send or receive will return with an error and +* -the device will be reset at the next convenient opportunity. +*/ +void +ae531x_BeginResetMode(ae531x_MAC_t *MACInfo) +{ + /* Set the reset flag */ + MACInfo->aeProcessRst = 1; +} + + +/******************************************************************************* +* ae531x_EndResetMode - exit the special "reset mode" entered +* earlier via a call to ae531x_BeginResetMode. +*/ +void +ae531x_EndResetMode(ae531x_MAC_t *MACInfo) +{ + MACInfo->aeProcessRst = 0; +} + + +/******************************************************************************* +* ae531x_IsInResetMode - determine whether or not the device is +* currently in "reset mode" (i.e. that a device reset is pending) +*/ +BOOL +ae531x_IsInResetMode(ae531x_MAC_t *MACInfo) +{ + return MACInfo->aeProcessRst; +} + + +/****************************************************************************** +* +* ae531x_DmaRxStart - Start Rx +* +* RETURNS: N/A +*/ +static void +ae531x_DmaRxStart(ae531x_MAC_t *MACInfo) +{ + ae531x_SetDmaReg(MACInfo, DmaControl, DmaRxStart); + sysWbFlush(); +} + + +/****************************************************************************** +* +* ae531x_DmaRxStop - Stop Rx +* +* RETURNS: N/A +*/ +void +ae531x_DmaRxStop(ae531x_MAC_t *MACInfo) +{ + ae531x_ClearDmaReg(MACInfo, DmaControl, DmaRxStart); + sysWbFlush(); +} + + +/****************************************************************************** +* +* ae531x_DmaTxStart - Start Tx +* +* RETURNS: N/A +*/ +void +ae531x_DmaTxStart(ae531x_MAC_t *MACInfo) +{ + ae531x_SetDmaReg(MACInfo, DmaControl, DmaTxStart); + sysWbFlush(); +} + + +/****************************************************************************** +* +* ae531x_DmaTxStop - Stop Tx +* +* RETURNS: N/A +*/ +void +ae531x_DmaTxStop(ae531x_MAC_t *MACInfo) +{ + ae531x_ClearDmaReg(MACInfo, DmaControl, DmaTxStart); + sysWbFlush(); +} + + +/****************************************************************************** +* +* ae531x_DmaIntEnable - Enable DMA interrupts +* +* RETURNS: N/A +*/ +void +ae531x_DmaIntEnable(ae531x_MAC_t *MACInfo) +{ + ae531x_WriteDmaReg(MACInfo, DmaIntrEnb, DmaIntEnable); +} + + +/****************************************************************************** +* +* ae531x_DmaIntDisable - Disable DMA interrupts +* +* RETURNS: N/A +*/ +void +ae531x_DmaIntDisable(ae531x_MAC_t *MACInfo) +{ + ae531x_WriteDmaReg(MACInfo, DmaIntrEnb, DmaIntDisable); +} + + +/****************************************************************************** +* +* ae531x_DmaIntClear - Clear DMA interrupts +* +* RETURNS: N/A +*/ +static void +ae531x_DmaIntClear(ae531x_MAC_t *MACInfo) +{ + /* clear all interrupt requests */ + ae531x_WriteDmaReg(MACInfo, DmaStatus, + ae531x_ReadDmaReg(MACInfo, DmaStatus)); +} + + +/****************************************************************************** +* Initialize generic queue data +*/ +void +ae531x_QueueInit(AE531X_QUEUE *q, char *pMem, int count) +{ + ARRIVE(); + q->firstDescAddr = pMem; + q->lastDescAddr = (VIRT_ADDR)((UINT32)q->firstDescAddr + + (count - 1) * AE531X_QUEUE_ELE_SIZE); + q->curDescAddr = q->firstDescAddr; + q->count = count; + LEAVE(); +} + + +/****************************************************************************** +* ae531x_TxQueueCreate - create a circular queue of descriptors for Transmit +*/ +static int +ae531x_TxQueueCreate(ae531x_MAC_t *MACInfo, + AE531X_QUEUE *q, + char *pMem, + int count) +{ + int i; + VIRT_ADDR descAddr; + + ARRIVE(); + + ae531x_QueueInit(q, pMem, count); + q->reapDescAddr = q->lastDescAddr; + + /* Initialize Tx buffer descriptors. */ + for (i=0, descAddr=q->firstDescAddr; + ilastDescAddr, + DescEndOfRing|AE531X_DESC_CTRLEN_GET(q->lastDescAddr)); + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Txbuf begin = %x, end = %x\n", + MACInfo->unit, + (UINT32)q->firstDescAddr, + (UINT32)q->lastDescAddr)); + + LEAVE(); + return 0; +} + + +/****************************************************************************** +* ae531x_RxQueueCreate - create a circular queue of Rx descriptors +*/ +int +ae531x_RxQueueCreate(ae531x_MAC_t *MACInfo, + AE531X_QUEUE *q, + char *pMem, + int count) +{ + int i; + VIRT_ADDR descAddr; + + ARRIVE(); + + ae531x_QueueInit(q, pMem, count); + q->reapDescAddr = NULL; + + + /* Initialize Rx buffer descriptors */ + for (i=0, descAddr=q->firstDescAddr; + iunit)); + ae531x_QueueDestroy(q); + return -1; + } + AE531X_DESC_SWPTR_SET(descAddr, swptr); + + AE531X_DESC_STATUS_SET(descAddr, DescOwnByDma); + AE531X_DESC_CTRLEN_SET(descAddr, rxBufferSize); + AE531X_DESC_BUFPTR_SET(descAddr, virt_to_bus(rxBuffer)); + AE531X_DESC_LNKBUF_SET(descAddr, (UINT32)0); + } /* for each desc */ + + /* Make the queue circular */ + AE531X_DESC_CTRLEN_SET(q->lastDescAddr, + DescEndOfRing|AE531X_DESC_CTRLEN_GET(q->lastDescAddr)); + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Rxbuf begin = %x, end = %x\n", + MACInfo->unit, + (UINT32)q->firstDescAddr, + (UINT32)q->lastDescAddr)); + + LEAVE(); + return 0; +} + + +/****************************************************************************** +* ae531x_QueueDestroy -- Free all buffers and descriptors associated +* with a queue. +*/ +static void +ae531x_QueueDestroy(AE531X_QUEUE *q) +{ + int i; + int count; + VIRT_ADDR descAddr; + + ARRIVE(); + + count = q->count; + + for (i=0, descAddr=q->firstDescAddr; + itxQueue); +} + +static void +ae531x_RxQueueDestroy(ae531x_MAC_t *MACInfo) +{ + ae531x_QueueDestroy(&MACInfo->rxQueue); +} + + +/****************************************************************************** +* ae531x_AllocateQueues - Allocate receive and transmit queues +*/ +int +ae531x_AllocateQueues(ae531x_MAC_t *MACInfo) +{ + size_t QMemSize; + char *pTxBuf = NULL; + char *pRxBuf = NULL; + + ARRIVE(); + + MACInfo->txDescCount = AE531X_TX_DESC_COUNT_DEFAULT; + QMemSize = AE531X_QUEUE_ELE_SIZE * MACInfo->txDescCount; + pTxBuf = MALLOC(QMemSize); + if (pTxBuf == NULL) { + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Failed to allocate TX queue\n", MACInfo->unit)); + goto AllocQFail; + } + + if (ae531x_TxQueueCreate(MACInfo, &MACInfo->txQueue, pTxBuf, + MACInfo->txDescCount) < 0) + { + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Failed to create TX queue\n", MACInfo->unit)); + goto AllocQFail; + } + + MACInfo->rxDescCount = AE531X_RX_DESC_COUNT_DEFAULT; + QMemSize = AE531X_QUEUE_ELE_SIZE * MACInfo->rxDescCount; + pRxBuf = MALLOC(QMemSize); + if (pRxBuf == NULL) { + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Failed to allocate RX queue\n", MACInfo->unit)); + goto AllocQFail; + } + + if (ae531x_RxQueueCreate(MACInfo, &MACInfo->rxQueue, pRxBuf, + MACInfo->rxDescCount) < 0) + { + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Failed to create RX queue\n", MACInfo->unit)); + goto AllocQFail; + } + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Memory setup complete.\n", MACInfo->unit)); + + LEAVE(); + return 0; + +AllocQFail: + MACInfo->txDescCount = 0; /* sanity */ + MACInfo->rxDescCount = 0; /* sanity */ + + if (pTxBuf) { + FREE(pTxBuf); + } + if (pRxBuf) { + FREE(pRxBuf); + } + + LEAVE(); + return -1; +} + + +/****************************************************************************** +* +* ae531x_FreeQueues - Free Transmit & Receive queues +*/ +void +ae531x_FreeQueues(ae531x_MAC_t *MACInfo) +{ + ae531x_TxQueueDestroy(MACInfo); + FREE(MACInfo->txQueue.firstDescAddr); + + ae531x_RxQueueDestroy(MACInfo); + FREE(MACInfo->rxQueue.firstDescAddr); +} + +/****************************************************************************** +* +* ae531x_DmaReset - Reset DMA and TLI controllers +* +* RETURNS: N/A +*/ +void +ae531x_DmaReset(ae531x_MAC_t *MACInfo) +{ + int i; + UINT32 descAddr; + + ARRIVE(); + + /* Disable device interrupts prior to any errors during stop */ + intDisable(MACInfo->ilevel); + + /* Disable MAC rx and tx */ + ae531x_ClearMacReg(MACInfo, MacControl, (MacRxEnable | MacTxEnable)); + + /* Reset dma controller */ + ae531x_WriteDmaReg(MACInfo, DmaBusMode, DmaResetOn); + + /* Delay 2 usec */ + sysUDelay(2); + + /* Flush the rx queue */ + descAddr = (UINT32)MACInfo->rxQueue.firstDescAddr; + MACInfo->rxQueue.curDescAddr = MACInfo->rxQueue.firstDescAddr; + for (i=0; + i<(MACInfo->rxDescCount); + i++, descAddr += AE531X_QUEUE_ELE_SIZE) { + AE531X_DESC_STATUS_SET(descAddr, DescOwnByDma); + } + + /* Flush the tx queue */ + descAddr = (UINT32)MACInfo->txQueue.firstDescAddr; + MACInfo->txQueue.curDescAddr = MACInfo->txQueue.firstDescAddr; + MACInfo->txQueue.reapDescAddr = MACInfo->txQueue.lastDescAddr; + for (i=0; + i<(MACInfo->txDescCount); + i++, descAddr += AE531X_QUEUE_ELE_SIZE) { + AE531X_DESC_STATUS_SET (descAddr, 0); + } + + /* Set init register values */ + ae531x_WriteDmaReg(MACInfo, DmaBusMode, DmaBusModeInit); + + /* Install the first Tx and Rx queues on the device */ + ae531x_WriteDmaReg(MACInfo, DmaRxBaseAddr, + (UINT32)MACInfo->rxQueue.firstDescAddr); + ae531x_WriteDmaReg(MACInfo, DmaTxBaseAddr, + (UINT32)MACInfo->txQueue.firstDescAddr); + + ae531x_WriteDmaReg(MACInfo, DmaControl, DmaStoreAndForward); + + ae531x_WriteDmaReg(MACInfo, DmaIntrEnb, DmaIntDisable); + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d: DMA RESET!\n", MACInfo->unit)); + + /* Turn on device interrupts -- enable most errors */ + ae531x_DmaIntClear(MACInfo); /* clear interrupt requests */ + ae531x_DmaIntEnable(MACInfo); /* enable interrupts */ + /* Enable receive interrupts separately (they are not part + * of the main group since they are enabled & disabled by + * the polling routine. + */ + ae531x_SetDmaReg(MACInfo, DmaIntrEnb, + (DmaIntRxNoBuffer | DmaIntRxCompleted)); + + ae531x_EndResetMode(MACInfo); + + intEnable(MACInfo->ilevel); + + LEAVE(); +} + + +/****************************************************************************** +* +* ae531x_MACAddressSet - Set the ethernet address +* +* Sets the ethernet address according to settings in flash. +* +* RETURNS: void +*/ +static void +ae531x_MACAddressSet(ae531x_MAC_t *MACInfo) +{ + unsigned int data; + UINT8 *macAddr; + + ARRIVE(); + + macAddr = macAddrGet(MACInfo); + + /* set our MAC address */ + data = (macAddr[5]<<8) | macAddr[4]; + ae531x_WriteMacReg(MACInfo, MacAddrHigh, data ); + + data = (macAddr[3]<<24) | (macAddr[2]<<16) | (macAddr[1]<<8) | macAddr[0]; + ae531x_WriteMacReg(MACInfo, MacAddrLow, data ); + + AE531X_PRINT(AE531X_DEBUG_RESET, + ("ethmac%d Verify MAC address %8.8X %8.8X \n", + MACInfo->unit, + ae531x_ReadMacReg(MACInfo, MacAddrLow), + ae531x_ReadMacReg(MACInfo, MacAddrHigh))); + + AE531X_PRINT(AE531X_DEBUG_RESET, + (" sb = %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", + 0xff&macAddr[0], + 0xff&macAddr[1], + 0xff&macAddr[2], + 0xff&macAddr[3], + 0xff&macAddr[4], + 0xff&macAddr[5])); + LEAVE(); +} + + +/****************************************************************************** +* +* ae_SetMACFromPhy - read Phy settings and update Mac +* with current duplex and speed. +* +* RETURNS: +*/ +static void +ae531x_SetMACFromPhy(ae531x_MAC_t *MACInfo) +{ + UINT32 macCtl; + BOOL fullDuplex; + + ARRIVE(); + + /* Get duplex mode from Phy */ + fullDuplex = phyIsFullDuplex(MACInfo->unit); + + /* Flag is set for full duplex mode, else cleared */ + macCtl = ae531x_ReadMacReg(MACInfo, MacControl); + + if (fullDuplex) { + /* set values of control registers */ + macCtl &= ~MacDisableRxOwn; + macCtl |= MacFullDuplex; + ae531x_WriteMacReg(MACInfo, MacControl, macCtl); + ae531x_WriteMacReg(MACInfo, MacFlowControl, MacFlowControlInitFdx); +#if 0 + printk ("[Full Duplex] CTRL=%#x FLOW=%#x\n", macCtl, + MacFlowControlInitFdx); +#endif + } else { + /* set values of control registers */ + ae531x_WriteMacReg(MACInfo, MacFlowControl, MacFlowControlInitHdx); + macCtl |= MacDisableRxOwn; + macCtl &= ~MacFullDuplex; + ae531x_WriteMacReg(MACInfo, MacControl, macCtl); +#if 0 + printk ("[Half Duplex] CTRL=%#x FLOW=%#x\n", macCtl, + MacFlowControlInitHdx); +#endif + } + + LEAVE(); +} + + +/****************************************************************************** +* ae531x_MACReset -- sets MAC address and duplex. +*/ +void +ae531x_MACReset(ae531x_MAC_t *MACInfo) +{ + ae531x_MACAddressSet(MACInfo); + + ae531x_SetMACFromPhy(MACInfo); +} + + +/****************************************************************************** +* ae531x_EnableComm -- enable Transmit and Receive +*/ +void +ae531x_EnableComm(ae531x_MAC_t *MACInfo) +{ + ae531x_SetMacReg(MACInfo, MacControl, (MacRxEnable | MacTxEnable)); + ae531x_DmaRxStart(MACInfo); /* start receiver */ + ae531x_DmaTxStart(MACInfo); /* start transmitter */ +} + + +/****************************************************************************** +* ae531x_DisableComm -- disable Transmit and Receive +*/ +void +ae531x_DisableComm(ae531x_MAC_t *MACInfo) +{ + ae531x_ClearMacReg(MACInfo, MacControl, (MacRxEnable | MacTxEnable)); +} + + +/****************************************************************************** +* ae531x_reset -- Cold reset ethernet interface +*/ +void +ae531x_reset(ae531x_MAC_t *MACInfo) +{ + UINT32 mask = 0; + UINT32 regtmp; + + if (ar531x_num_enet_macs == 2) { + if (MACInfo->unit == 0) { + mask = AR531X_RESET_ENET0 | AR531X_RESET_EPHY0; + } else { + mask = AR531X_RESET_ENET1 | AR531X_RESET_EPHY1; + } + } else { + mask = AR531X_RESET_ENET0 | AR531X_RESET_EPHY0 | + AR531X_RESET_ENET1 | AR531X_RESET_EPHY1; + } + + /* Put into reset */ + regtmp = sysRegRead(AR531X_RESET); + sysRegWrite(AR531X_RESET, regtmp | mask); + sysMsDelay(15); + + /* Pull out of reset */ + regtmp = sysRegRead(AR531X_RESET); + sysRegWrite(AR531X_RESET, regtmp & ~mask); + sysUDelay(25); + + /* Enable */ + if (ar531x_num_enet_macs == 2) { + if (MACInfo->unit == 0) { + mask = AR531X_ENABLE_ENET0; + } else { + mask = AR531X_ENABLE_ENET1; + } + } else { + mask = AR531X_ENABLE_ENET0 | AR531X_ENABLE_ENET1; + } + regtmp = sysRegRead(AR531X_ENABLE); + sysRegWrite(AR531X_ENABLE, regtmp | mask); +} + + +/****************************************************************************** +* ae531x_unitLinkLost -- Called from PHY layer to notify the MAC layer +* that there are no longer any live links associated with a MAC. +*/ +void +ae531x_unitLinkLost(int ethUnit) +{ + AE531X_PRINT(AE531X_DEBUG_LINK_CHANGE, + ("enetmac%d link down\n", ethUnit)); +} + + +/****************************************************************************** +* ae531x_unitLinkGained -- Called from PHY layer to notify the MAC layer +* that there are 1 or more live links associated with a MAC. +*/ +void +ae531x_unitLinkGained(int ethUnit) +{ + AE531X_PRINT(AE531X_DEBUG_LINK_CHANGE, + ("enet%d link up\n", ethUnit)); +} + +/****************************************************************************** +* ae531x_ethMacDefault -- Called from PHY layer to determine the default +* ethernet MAC. On some "twisted" platforms, the only usable MAC is 1, +* while on others the usable MAC is 0. Future boards may allow both MACs +* to be used; in this case, return -1 to indicate that there IS NO default +* MAC. +* +* Note: one some AR2312 platforms the PHY needs to be accessed through +* MAC 0, even though the MAC itself is addressed through MAC 1. Since this +* function is used by the PHY layer to determine which MAC it should use, +* the twisted case is limited to the one where the PHY is actually addressed +* through MAC 1 rather than MAC 0. +*/ +int +ae531x_ethMacDefault(void) +{ + /* + * Where there are two MACs, there is no real default + */ + if (ar531x_num_enet_macs == 2) + return -1; + + /* + * All single MAC platforms seem to address the PHY through MAC 0, + * even when they use MAC 1 for the actual MAC functions. + */ + return 0; +} diff -urN linux-2.4.32.new/arch/mips/ar531x/ae531xmac.h linux-2.4.32.new-eth/arch/mips/ar531x/ae531xmac.h --- linux-2.4.32.new/arch/mips/ar531x/ae531xmac.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/ae531xmac.h 2005-12-25 11:54:20.819264376 +0000 @@ -0,0 +1,208 @@ +/* + * 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. + */ + +/* + * See README to understand the decomposition of the ethernet driver. + * + * This file contains OS-independent pure software definitions for + * ethernet support on the AR531X platform. + */ + +#ifndef _AE531XMAC_H_ +#define _AE531XMAC_H_ + +/* + * DEBUG switches to control verbosity. + * Just modify the value of ae531x_MAC_debug. + */ +#define AE531X_DEBUG_ALL 0xffffffff +#define AE531X_DEBUG_ERROR 0x00000001 /* Unusual conditions and Errors */ +#define AE531X_DEBUG_ARRIVE 0x00000002 /* Arrive into a function */ +#define AE531X_DEBUG_LEAVE 0x00000004 /* Leave a function */ +#define AE531X_DEBUG_RESET 0x00000008 /* Reset */ +#define AE531X_DEBUG_TX 0x00000010 /* Transmit */ +#define AE531X_DEBUG_TX_REAP 0x00000020 /* Transmit Descriptor Reaping */ +#define AE531X_DEBUG_RX 0x00000040 /* Receive */ +#define AE531X_DEBUG_RX_STOP 0x00000080 /* Receive Early Stop */ +#define AE531X_DEBUG_INT 0x00000100 /* Interrupts */ +#define AE531X_DEBUG_LINK_CHANGE 0x00000200 /* PHY Link status changed */ + +extern int ae531x_MAC_debug; + +extern int ar531x_num_enet_macs; + +#define AE531X_PRINT(FLG, X) \ +{ \ + if (ae531x_MAC_debug & (FLG)) { \ + DEBUG_PRINTF("%s#%d:%s ", \ + __FILE__, \ + __LINE__, \ + __FUNCTION__); \ + DEBUG_PRINTF X; \ + } \ +} + +#define ARRIVE() AE531X_PRINT(AE531X_DEBUG_ARRIVE, ("Arrive{\n")) +#define LEAVE() AE531X_PRINT(AE531X_DEBUG_LEAVE, ("}Leave\n")) + +#define RegRead(addr) \ + (*(volatile unsigned int *)(addr)) + +#define RegWrite(val,addr) \ + ((*(volatile unsigned int *)(addr)) = (val)) + +/***************************************************************** + * Phy code is broken out into a separate layer, so that different + * PHY hardware can easily be supported. + * + * These functions are provided by the PHY layer for use by the MAC layer. + * phySetup -- Set phy hardware appropriately for a MAC unit + * + * phyCheckStatusChange -- Look for dropped/initiated links on any + * phy port associated with a MAC unit + * + * phyIsSpeed100 -- Determines whether or not a PHY is up and + * running at 100Mbit + * + * phyIsFullDuplex -- Determines whether or not a PHY is up and + * running in Full Duplex mode + * + */ +#ifdef CONFIG_MARVELL_ENET_PHY +/* + * Mapping of generic phy APIs to Marvell Ethernet Switch phy functions. + */ +#include "mvPhy.h" +#define phySetup(ethUnit, phyBase) mv_phySetup((ethUnit), (phyBase)) +#define phyCheckStatusChange(ethUnit) mv_phyCheckStatusChange(ethUnit) +#define phyIsSpeed100(ethUnit) mv_phyIsSpeed100(ethUnit) +#define phyIsFullDuplex(ethUnit) mv_phyIsFullDuplex(ethUnit) + +#ifdef CONFIG_VENETDEV +#define PHY_TRAILER_SIZE MV_PHY_TRAILER_SIZE +extern int mv_phyDetermineSource(char *data, int len); +extern void mv_phySetDestinationPort(char *data, int len, int fromLAN); +#define phyDetermineSource(data, len) mv_phyDetermineSource((data), (len)) +#define phySetDestinationPort(data, len, fromLAN) mv_phySetDestinationPort((data), (len), (fromLAN)) +#else +#define PHY_TRAILER_SIZE 0 +#endif +#endif /* CONFIG_MARVELL_ENET_PHY */ + +#if defined(CONFIG_KENDIN_ENET_PHY) || defined(CONFIG_REALTEK_ENET_PHY) +/* + * Mapping of generic phy APIs to Kendin KS8721B and RealTek RTL8201BL phys. + */ +#include "rtPhy.h" +#define phySetup(ethUnit, phyBase) rt_phySetup((ethUnit), (phyBase)) +#define phyCheckStatusChange(ethUnit) rt_phyCheckStatusChange(ethUnit) +#define phyIsSpeed100(ethUnit) rt_phyIsSpeed100(ethUnit) +#define phyIsFullDuplex(ethUnit) rt_phyIsFullDuplex(ethUnit) +#endif + +#if !defined(PHY_TRAILER_SIZE) +#define PHY_TRAILER_SIZE 0 +#endif + +/***************************************************************** + * MAC-independent interface to be used by PHY code + * + * These functions are provided by the MAC layer for use by the PHY layer. + */ +#define phyRegRead ae531x_MiiRead +#define phyRegWrite ae531x_MiiWrite +#define phyLinkLost(ethUnit) ae531x_unitLinkLost(ethUnit) +#define phyLinkGained(ethUnit) ae531x_unitLinkGained(ethUnit) +#define phyEthMacDefault() ae531x_ethMacDefault() + +void ae531x_unitLinkLost(int unit); +void ae531x_unitLinkGained(int unit); +int ae531x_ethMacDefault(void); + + +/* RXBUFF_RESERVE enables building header on WLAN-side in place */ +#define RXBUFF_RESERVE 96 +#define ETH_CRC_LEN 4 + +/***************************************************************** + * Descriptor queue + */ +typedef struct ae531x_queue { + VIRT_ADDR firstDescAddr; /* descriptor array address */ + VIRT_ADDR lastDescAddr; /* last descriptor address */ + VIRT_ADDR curDescAddr; /* current descriptor address */ + VIRT_ADDR reapDescAddr; /* current tail of tx descriptors reaped */ + UINT16 count; /* number of elements */ +} AE531X_QUEUE; + +/* Given a descriptor, return the next one in a circular list */ +#define AE531X_QUEUE_ELE_NEXT_GET(q, descAddr) \ + ((descAddr) == (q)->lastDescAddr) ? (q)->firstDescAddr : \ + (VIRT_ADDR)((UINT32)(descAddr) + AE531X_QUEUE_ELE_SIZE) + +/* Move the "current descriptor" forward to the next one */ +#define AE531X_CONSUME_DESC(q) \ + q->curDescAddr = AE531X_QUEUE_ELE_NEXT_GET(q, q->curDescAddr) + +/***************************************************************** + * Per-ethernet-MAC OS-independent information + */ +typedef struct ae531x_MAC_s { + u32 unit; /* MAC unit ID */ + u32 macBase; /* MAC base address */ + u32 dmaBase; /* DMA base address */ + u32 phyBase; /* PHY base address */ + AE531X_QUEUE txQueue; /* Transmit descriptor queue */ + AE531X_QUEUE rxQueue; /* Receive descriptor queue */ + UINT16 txDescCount; /* Transmit descriptor count */ + UINT16 rxDescCount; /* Receive descriptor count */ + BOOL aeProcessRst; /* flag to indicate reset in progress */ + BOOL port_is_up; /* flag to indicate port is up */ + void *OSinfo; /* OS-dependent data */ +} ae531x_MAC_t; + +#define AE531X_TX_DESC_COUNT_DEFAULT 64 /* Transmit descriptors */ +#define AE531X_RX_DESC_COUNT_DEFAULT 64 /* Receive descriptors */ + + +/***************************************************************** + * Interfaces exported by the OS-independent MAC layer + */ +void ae531x_BeginResetMode(ae531x_MAC_t *MACInfo); +void ae531x_EndResetMode(ae531x_MAC_t *MACInfo); +BOOL ae531x_IsInResetMode(ae531x_MAC_t *MACInfo); +int ae531x_RxQueueCreate(ae531x_MAC_t *MACInfo, AE531X_QUEUE *q, + char *pMem, int count); +int ae531x_QueueDelete(struct ae531x_queue *q); +void ae531x_DmaReset(ae531x_MAC_t *MACInfo); +void ae531x_MACReset(ae531x_MAC_t *MACInfo); +void ae531x_EnableComm(ae531x_MAC_t *MACInfo); +void ae531x_DisableComm(ae531x_MAC_t *MACInfo); +void ae531x_reset(ae531x_MAC_t *MACInfo); +int ae531x_AllocateQueues(ae531x_MAC_t *MACInfo); +void ae531x_FreeQueues(ae531x_MAC_t *MACInfo); +void ae531x_QueueInit(AE531X_QUEUE *q, char *pMem, int count); +UINT32 ae531x_ReadMacReg(ae531x_MAC_t *MACInfo, UINT32 reg); +void ae531x_WriteMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 data); +void ae531x_SetMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val); +void ae531x_ClearMacReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val); +void ae531x_SetDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val); +void ae531x_ClearDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 val); +UINT32 ae531x_ReadDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg); +void ae531x_WriteDmaReg(ae531x_MAC_t *MACInfo, UINT32 reg, UINT32 data); +UINT32 ae531x_ReadMiiReg(UINT32 phyBase, UINT32 reg); +void ae531x_WriteMiiReg(UINT32 phyBase, UINT32 reg, UINT32 data); +UINT16 ae531x_MiiRead(UINT32 phyBase, UINT32 phyAddr, UINT8 reg); +void ae531x_MiiWrite(UINT32 phyBase, UINT32 phyAddr, UINT8 reg, UINT16 data); +void ae531x_DmaIntEnable(ae531x_MAC_t *MACInfo); +void ae531x_DmaIntDisable(ae531x_MAC_t *MACInfo); +void ae531x_AckIntr(ae531x_MAC_t *MACInfo, UINT32 val); +void *ae531x_rxbuf_alloc(ae531x_MAC_t *MACInfo, char **rxBptr, int *rxBSize); +void ae531x_swptr_free(VIRT_ADDR txDesc); + +#endif /* _AE531XMAC_H_ */ diff -urN linux-2.4.32.new/arch/mips/ar531x/ae531xreg.h linux-2.4.32.new-eth/arch/mips/ar531x/ae531xreg.h --- linux-2.4.32.new/arch/mips/ar531x/ae531xreg.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/ae531xreg.h 2005-12-25 11:54:20.834262096 +0000 @@ -0,0 +1,437 @@ +/* + * 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. + */ + +/* + * See README to understand the decomposition of the ethernet driver. + * + * Register definitions for Atheros AR531X Ethernet MAC. + */ + +#ifndef _AE531XREG_H_ +#define _AE531XREG_H_ + +#define AE531X_MAC_OFFSET 0x0000 +#define AE531X_PHY_OFFSET 0x0000 /* Same as MAC offset */ +#define AE531X_DMA_OFFSET 0x1000 + +/***********************************************************/ +/* MAC110 registers, base address is BAR+AE531X_MAC_OFFSET */ +/***********************************************************/ +#define MacControl 0x00 /* control */ +#define MacAddrHigh 0x04 /* address high */ +#define MacAddrLow 0x08 /* address low */ +#define MacMultiHashHigh 0x0C /* multicast hash table high */ +#define MacMultiHashLow 0x10 /* multicast hash table low */ +#define MacMiiAddr 0x14 /* MII address */ +#define MacMiiData 0x18 /* MII data */ +#define MacFlowControl 0x1C /* Flow control */ +#define MacVlan1Tag 0x4C /* VLAN1 tag */ +#define MacVlan2Tag 0x50 /* VLAN2 tag */ + + +/***************************************************************/ +/* DMA engine registers, base address is BAR+AE531X_DMA_OFFSET */ +/***************************************************************/ +#define DmaBusMode 0x00 /* CSR0 - Bus Mode */ +#define DmaTxPollDemand 0x04 /* CSR1 - Transmit Poll Demand */ +#define DmaRxPollDemand 0x08 /* CSR2 - Receive Poll Demand */ +#define DmaRxBaseAddr 0x0C /* CSR3 - Receive list base address */ +#define DmaTxBaseAddr 0x10 /* CSR4 - Transmit list base address */ +#define DmaStatus 0x14 /* CSR5 - Dma status */ +#define DmaControl 0x18 /* CSR6 - Dma control */ +#define DmaIntrEnb 0x1C /* CSR7 - Interrupt enable */ +#define DmaOverflowCnt 0x20 /* CSR8 - Missed Frame and Buff Overflow counter */ +#define DmaTxCurrAddr 0x50 /* CSR20 - Current host transmit buffer address */ +#define DmaRxCurrAddr 0x54 /* CSR21 - Current host receive buffer address */ + +/**********************************************************/ +/* MAC Control register layout */ +/**********************************************************/ +#define MacFilterOff 0x80000000 /* Receive all incoming packets RW */ +#define MacFilterOn 0 /* Receive filtered packets only 0 */ +#define MacBigEndian 0x40000000 /* Big endian mode RW */ +#define MacLittleEndian 0 /* Little endian 0 */ +#define MacHeartBeatOff 0x10000000 /* Heartbeat signal qual disable RW*/ +#define MacHeartBeatOn 0 /* Heartbeat signal qual enable 0 */ +#define MacSelectSrl 0x08000000 /* Select SRL port RW */ +#define MacSelectMii 0 /* Select MII port 0 */ +#define MacDisableRxOwn 0x00800000 /* Disable receive own packets RW */ +#define MacEnableRxOwn 0 /* Enable receive own packets 0 */ +#define MacLoopbackExt 0x00400000 /* External loopback RW */ +#define MacLoopbackInt 0x00200000 /* Internal loopback */ +#define MacLoopbackOff 0 /* Normal mode 00 */ +#define MacFullDuplex 0x00100000 /* Full duplex mode RW */ +#define MacHalfDuplex 0 /* Half duplex mode 0 */ +#define MacMulticastFilterOff 0x00080000 /* Pass all multicast packets RW */ +#define MacMulticastFilterOn 0 /* Pass filtered mcast packets 0 */ +#define MacPromiscuousModeOn 0x00040000 /* Receive all valid packets RW 1 */ +#define MacPromiscuousModeOff 0 /* Receive filtered packets only */ +#define MacFilterInverse 0x00020000 /* Inverse filtering RW */ +#define MacFilterNormal 0 /* Normal filtering 0 */ +#define MacBadFramesEnable 0x00010000 /* Pass bad frames RW */ +#define MacBadFramesDisable 0 /* Do not pass bad frames 0 */ +#define MacPerfectFilterOff 0x00008000 /* Hash filtering only RW */ +#define MacPerfectFilterOn 0 /* Both perfect and hash filtering 0 */ +#define MacHashFilterOn 0x00002000 /* perform hash filtering RW */ +#define MacHashFilterOff 0 /* perfect filtering only 0 */ +#define MacLateCollisionOn 0x00001000 /* Enable late collision control RW */ +#define MacLateCollisionOff 0 /* Disable late collision control 0 */ +#define MacBroadcastDisable 0x00000800 /* Disable reception of bcast frames RW */ +#define MacBroadcastEnable 0 /* Enable broadcast frames 0 */ +#define MacRetryDisable 0x00000400 /* Disable retransmission RW */ +#define MacRetryEnable 0 /* Enable retransmission 0 */ +#define MacPadStripEnable 0x00000100 /* Pad stripping enable RW */ +#define MacPadStripDisable 0 /* Pad stripping disable 0 */ +#define MacBackoff 0 /* Backoff Limit RW 00 */ +#define MacDeferralCheckEnable 0x00000020 /* Deferral check enable RW */ +#define MacDeferralCheckDisable 0 /* Deferral check disable 0 */ +#define MacTxEnable 0x00000008 /* Transmitter enable RW */ +#define MacTxDisable 0 /* Transmitter disable 0 */ +#define MacRxEnable 0x00000004 /* Receiver enable RW */ +#define MacRxDisable 0 /* Receiver disable 0 */ + + +/**********************************************************/ +/* MII address register layout */ +/**********************************************************/ +#define MiiDevMask 0x0000F800 /* MII device address */ +#define MiiDevShift 11 +#define MiiRegMask 0x000007C0 /* MII register */ +#define MiiRegShift 6 +#define MiiWrite 0x00000002 /* Write to register */ +#define MiiRead 0 /* Read from register */ +#define MiiBusy 0x00000001 /* MII interface is busy */ + +/**********************************************************/ +/* MII Data register layout */ +/**********************************************************/ +#define MiiDataMask 0x0000FFFF /* MII Data */ + +/**********************************************************/ +/* MAC flow control register layout */ +/**********************************************************/ +#define MacPauseTimeMask 0xFFFF0000 /* PAUSE TIME field in ctrl frame */ +#define MacPauseTimeShift 15 +#define MacControlFrameEnable 0x00000004 /* Enable pass ctrl frames to host */ +#define MacControlFrameDisable 0 /* Do not pass ctrl frames to host */ +#define MacFlowControlEnable 0x00000002 /* Enable flow control */ +#define MacFlowControlDisable 0 /* Disable flow control */ +#define MacSendPauseFrame 0x00000001 /* send pause frame */ + +/**********************************************************/ +/* DMA bus mode register layout */ +/**********************************************************/ +#define DmaRxAlign16 0x01000000 /* Force all rx buffers to align on odd hw bndry */ +#define DmaBigEndianDes 0x00100000 /* Big endian data buffer descriptors RW */ +#define DmaLittleEndianDesc 0 /* Little endian data descriptors */ +#define DmaBurstLength32 0x00002000 /* Dma burst length 32 RW */ +#define DmaBurstLength16 0x00001000 /* Dma burst length 16 */ +#define DmaBurstLength8 0x00000800 /* Dma burst length 8 */ +#define DmaBurstLength4 0x00000400 /* Dma burst length 4 */ +#define DmaBurstLength2 0x00000200 /* Dma burst length 2 */ +#define DmaBurstLength1 0x00000100 /* Dma burst length 1 */ +#define DmaBurstLength0 0x00000000 /* Dma burst length 0 */ +#define DmaBigEndianData 0x00000080 /* Big endian data buffers RW */ +#define DmaLittleEndianData 0 /* Little endian data buffers 0 */ +#define DmaDescriptorSkip16 0x00000040 /* number of dwords to skip RW */ +#define DmaDescriptorSkip8 0x00000020 /* between two unchained descriptors */ +#define DmaDescriptorSkip4 0x00000010 +#define DmaDescriptorSkip2 0x00000008 +#define DmaDescriptorSkip1 0x00000004 +#define DmaDescriptorSkip0 0 +#define DmaReceivePriorityOff 0x00000002 /* equal rx and tx priorities RW */ +#define DmaReceivePriorityOn 0 /* Rx has prioryty over Tx 0 */ +#define DmaResetOn 0x00000001 /* Reset DMA engine RW */ +#define DmaResetOff 0 + +/**********************************************************/ +/* DMA Status register layout */ +/**********************************************************/ +#define DmaRxAbort 0x01000000 /* receiver bus abort R 0 */ +#define DmaTxAbort 0x00800000 /* transmitter bus abort R 0 */ +#define DmaTxState 0x00700000 /* Transmit process state R 000 */ +#define DmaTxStopped 0x00000000 /* Stopped */ +#define DmaTxFetching 0x00100000 /* Running - fetching the descriptor */ +#define DmaTxWaiting 0x00200000 /* Running - waiting for end of transmission */ +#define DmaTxReading 0x00300000 /* Running - reading the data from memory */ +#define DmaTxSuspended 0x00600000 /* Suspended */ +#define DmaTxClosing 0x00700000 /* Running - closing descriptor */ +#define DmaRxState 0x000E0000 /* Receive process state 000 */ +#define DmaRxStopped 0x00000000 /* Stopped */ +#define DmaRxFetching 0x00020000 /* Running - fetching the descriptor */ +#define DmaRxChecking 0x00040000 /* Running - checking for end of packet */ +#define DmaRxWaiting 0x00060000 /* Running - waiting for packet */ +#define DmaRxSuspended 0x00080000 /* Suspended */ +#define DmaRxClosing 0x000A0000 /* Running - closing descriptor */ +#define DmaRxFlushing 0x000C0000 /* Running - flushing the current frame */ +#define DmaRxQueuing 0x000E0000 /* Running - queuing the recieve frame into host memory */ +#define DmaIntNormal 0x00010000 /* Normal interrupt summary RW 0 */ +#define DmaIntAbnormal 0x00008000 /* Abnormal interrupt summary RW 0 */ +#define DmaIntEarlyRx 0x00004000 /* Early receive interrupt (Normal) RW 0 */ +#define DmaIntBusError 0x00002000 /* Fatal bus error (Abnormal) RW 0 */ +#define DmaIntEarlyTx 0x00000400 /* Early transmit interrupt RW 0 */ +#define DmaIntRxStopped 0x00000100 /* Receive process stopped (Abnormal) RW 0 */ +#define DmaIntRxNoBuffer 0x00000080 /* Receive buffer unavailable (Abnormal) RW 0*/ +#define DmaIntRxCompleted 0x00000040 /* Completion of frame reception(Normal) RW 0*/ +#define DmaIntTxUnderflow 0x00000020 /* Transmit underflow (Abnormal) RW 0 */ +#define DmaIntTxJabber 0x00000008 /* Transmit Jabber Timeout (Abnormal) RW 0 */ +#define DmaIntTxNoBuffer 0x00000004 /* Transmit buffer unavailable (Normal) RW 0*/ +#define DmaIntTxStopped 0x00000002 /* Transmit process stopped (Abnormal) RW 0 */ +#define DmaIntTxCompleted 0x00000001 /* Transmit completed (Normal) RW 0 */ + +/**********************************************************/ +/* DMA control register layout */ +/**********************************************************/ +#define DmaStoreAndForward 0x00200000 /* Store and forward RW 0 */ +#define DmaTxThreshCtl256 0x0000c000 /* Non-SF threshold is 256 words */ +#define DmaTxThreshCtl128 0x00008000 /* Non-SF threshold is 128 words */ +#define DmaTxThreshCtl064 0x00004000 /* Non-SF threshold is 64 words */ +#define DmaTxThreshCtl032 0x00000000 /* Non-SF threshold is 32 words */ +#define DmaTxStart 0x00002000 /* Start/Stop transmission RW 0 */ +#define DmaTxSecondFrame 0x00000004 /* Operate on second frame RW 0 */ +#define DmaRxStart 0x00000002 /* Start/Stop reception RW 0 */ + +/**********************************************************/ +/* DMA interrupt enable register layout */ +/**********************************************************/ +#define DmaIeNormal DmaIntNormal /* Normal interrupt enable RW 0 */ +#define DmaIeAbnormal DmaIntAbnormal /* Abnormal interrupt enable RW 0 */ +#define DmaIeEarlyRx DmaIntEarlyRx /* Early receive interrupt enable RW 0 */ +#define DmaIeBusError DmaIntBusError /* Fatal bus error enable RW 0 */ +#define DmaIeEarlyTx DmaIntEarlyTx /* Early transmit interrupt enable RW 0 */ +#define DmaIeRxStopped DmaIntRxStopped /* Receive process stopped enable RW 0 */ +#define DmaIeRxNoBuffer DmaIntRxNoBuffer /* Receive buffer unavailable enable RW 0 */ +#define DmaIeRxCompleted DmaIntRxCompleted /* Completion of frame reception enable RW 0 */ +#define DmaIeTxUnderflow DmaIntTxUnderflow /* Transmit underflow enable RW 0 */ +#define DmaIeTxJabber DmaIntTxJabber /* Transmit jabber timeout RW 0 */ +#define DmaIeTxNoBuffer DmaIntTxNoBuffer /* Transmit buffer unavailable enable RW 0 */ +#define DmaIeTxStopped DmaIntTxStopped /* Transmit process stopped enable RW 0 */ +#define DmaIeTxCompleted DmaIntTxCompleted /* Transmit completed enable RW 0 */ + +/****************************************************************/ +/* DMA Missed Frame and Buffer Overflow Counter register layout */ +/****************************************************************/ +#define DmaRxBufferMissedFrame 0xffff0000 /* cleared on read */ +#define DmaMissedFrameShift 16 +#define DmaRxBufferOverflowCnt 0x0000ffff /* cleared on read */ +#define DmaMissedFrameCountMask 0x0000ffff + +/**********************************************************/ +/* DMA Engine descriptor layout */ +/**********************************************************/ +/* status word of DMA descriptor */ +#define DescOwnByDma 0x80000000 /* Descriptor is owned by DMA engine */ +#define DescFrameLengthMask 0x3FFF0000 /* Receive descriptor frame length */ +#define DescFrameLengthShift 16 +#define DescError 0x00008000 /* Error summary bit OR of following bits */ +#define DescRxTruncated 0x00004000 /* Rx - no more descs for receive frame */ +#define DescRxLengthError 0x00001000 /* Rx - frame size not matching with length field */ +#define DescRxRunt 0x00000800 /* Rx - runt frame, damaged by a + collision or term before 64 bytes */ +#define DescRxMulticast 0x00000400 /* Rx - received frame is multicast */ +#define DescRxFirst 0x00000200 /* Rx - first descriptor of the frame */ +#define DescRxLast 0x00000100 /* Rx - last descriptor of the frame */ +#define DescRxLongFrame 0x00000080 /* Rx - frame is longer than 1518 bytes */ +#define DescRxLateColl 0x00000040 /* Rx - frame was damaged by a late collision */ +#define DescRxFrameEther 0x00000020 /* Rx - Frame type Ethernet 802.3*/ +#define DescRxMiiError 0x00000008 /* Rx - error reported by MII interface */ +#define DescRxDribbling 0x00000004 /* Rx - frame contains noninteger multiple of 8 bits */ +#define DescRxCrc 0x00000002 /* Rx - CRC error */ +#define DescTxTimeout 0x00004000 /* Tx - Transmit jabber timeout */ +#define DescTxLostCarrier 0x00000800 /* Tx - carrier lost during tramsmission */ +#define DescTxNoCarrier 0x00000400 /* Tx - no carrier signal from tranceiver */ +#define DescTxLateCollision 0x00000200 /* Tx - transmission aborted due to collision */ +#define DescTxExcCollisions 0x00000100 /* Tx - transmission aborted after 16 collisions */ +#define DescTxHeartbeatFail 0x00000080 /* Tx - heartbeat collision check failure */ +#define DescTxCollMask 0x00000078 /* Tx - Collision count */ +#define DescTxCollShift 3 +#define DescTxExcDeferral 0x00000004 /* Tx - excessive deferral */ +#define DescTxUnderflow 0x00000002 /* Tx - late data arrival from memory */ +#define DescTxDeferred 0x00000001 /* Tx - frame transmision deferred */ + +/* length word of DMA descriptor */ +#define DescTxIntEnable 0x80000000 /* Tx - interrupt on completion */ +#define DescTxLast 0x40000000 /* Tx - Last segment of the frame */ +#define DescTxFirst 0x20000000 /* Tx - First segment of the frame */ +#define DescTxDisableCrc 0x04000000 /* Tx - Add CRC disabled (first segment only) */ +#define DescEndOfRing 0x02000000 /* End of descriptors ring */ +#define DescChain 0x01000000 /* Second buffer address is chain address */ +#define DescTxDisablePadd 0x00800000 /* disable padding */ +#define DescSize2Mask 0x003FF800 /* Buffer 2 size */ +#define DescSize2Shift 11 +#define DescSize1Mask 0x000007FF /* Buffer 1 size */ +#define DescSize1Shift 0 + +/**********************************************************/ +/* Initial register values */ +/**********************************************************/ +/* Full-duplex mode with perfect filter on */ +#define MacControlInitFdx \ + ( MacFilterOn \ + | MacLittleEndian \ + | MacHeartBeatOn \ + | MacSelectMii \ + | MacEnableRxOwn \ + | MacLoopbackOff \ + | MacFullDuplex \ + | MacMulticastFilterOn \ + | MacPromiscuousModeOff \ + | MacFilterNormal \ + | MacBadFramesDisable \ + | MacPerfectFilterOn \ + | MacHashFilterOff \ + | MacLateCollisionOff \ + | MacBroadcastEnable \ + | MacRetryEnable \ + | MacPadStripDisable \ + | MacDeferralCheckDisable \ + | MacTxEnable \ + | MacRxEnable) + +/* Full-duplex mode */ +#define MacFlowControlInitFdx \ + ( MacControlFrameDisable \ + | MacFlowControlEnable) + +/* Half-duplex mode with perfect filter on */ +#define MacControlInitHdx \ + ( MacFilterOn \ + | MacLittleEndian \ + | MacHeartBeatOn \ + | MacSelectMii \ + | MacDisableRxOwn \ + | MacLoopbackOff \ + | MacHalfDuplex \ + | MacMulticastFilterOn \ + | MacPromiscuousModeOff \ + | MacFilterNormal \ + | MacBadFramesDisable \ + | MacPerfectFilterOn \ + | MacHashFilterOff \ + | MacLateCollisionOff \ + | MacBroadcastEnable \ + | MacRetryEnable \ + | MacPadStripDisable \ + | MacDeferralCheckDisable \ + | MacTxEnable \ + | MacRxEnable) + +/* Half-duplex mode */ +#define MacFlowControlInitHdx \ + ( MacControlFrameDisable \ + | MacFlowControlDisable) + +/* Bus Mode Rx odd half word align */ +#define DmaBusModeInit \ + ( DmaLittleEndianDesc \ + | DmaRxAlign16 \ + | DmaBurstLength4 \ + | DmaBigEndianData \ + | DmaDescriptorSkip1 \ + | DmaReceivePriorityOn \ + | DmaResetOff) + +#define DmaControlInit (DmaStoreAndForward) + +/* Interrupt groups */ +#define DmaIntEnable \ + ( DmaIeNormal \ + | DmaIeAbnormal \ + | DmaIntBusError \ + | DmaIntRxStopped \ + | DmaIntTxUnderflow \ + | DmaIntTxStopped) + +#define DmaIntDisable 0 + +#define DmaAllIntCauseMask \ + ( DmaIeNormal \ + | DmaIeAbnormal \ + | DmaIntEarlyRx \ + | DmaIntBusError \ + | DmaIntEarlyTx \ + | DmaIntRxStopped \ + | DmaIntRxNoBuffer \ + | DmaIntRxCompleted \ + | DmaIntTxUnderflow \ + | DmaIntTxJabber \ + | DmaIntTxNoBuffer \ + | DmaIntTxStopped \ + | DmaIntTxCompleted) + +#define UnhandledIntrMask \ + (DmaAllIntCauseMask \ + & ~(DmaIntRxNoBuffer \ + | DmaIntTxStopped \ + | DmaIntTxJabber \ + | DmaIntTxUnderflow \ + | DmaIntBusError \ + | DmaIntRxCompleted )) + +#define DescRxErrors \ + (DescRxTruncated \ + | DescRxRunt \ + | DescRxLateColl \ + | DescRxMiiError \ + | DescRxCrc) + +#define DescTxErrors \ + ( DescTxTimeout \ + | DescTxLateCollision \ + | DescTxExcCollisions \ + | DescTxExcDeferral \ + | DescTxUnderflow) + +/**********************************************************/ +/* Descriptor Layout */ +/**********************************************************/ +#define AE531X_DESC_STATUS 0x00 /* Status offset */ +#define AE531X_DESC_CTRLEN 0x04 /* Control and Length offset */ +#define AE531X_DESC_BUFPTR 0x08 /* Buffer pointer offset */ +#define AE531X_DESC_LNKBUF 0x0c /* Link field offset, or ptr to 2nd buf */ +#define AE531X_DESC_SWPTR 0x10 /* OS-Dependent software pointer */ + +#define AE531X_DESC_SIZE 0x10 /* 4 words, 16 bytes */ +#define AE531X_QUEUE_ELE_SIZE 0x14 /* with software pointer extension */ + +/* Accessors to the dma descriptor fields */ +#define AE531X_DESC_STATUS_GET(ptr) \ + *(volatile UINT32 *)((UINT32)(ptr) + AE531X_DESC_STATUS) + +#define AE531X_DESC_STATUS_SET(ptr, val) \ + AE531X_DESC_STATUS_GET(ptr) = (val) + +#define AE531X_DESC_CTRLEN_GET(ptr) \ + *(volatile UINT32 *)((UINT32)ptr + AE531X_DESC_CTRLEN) + +#define AE531X_DESC_CTRLEN_SET(ptr, val) \ + AE531X_DESC_CTRLEN_GET(ptr) = (val) + +#define AE531X_DESC_BUFPTR_GET(ptr) \ + *(volatile UINT32 *)((UINT32)ptr + AE531X_DESC_BUFPTR) + +#define AE531X_DESC_BUFPTR_SET(ptr,val) \ + AE531X_DESC_BUFPTR_GET(ptr) = (UINT32)(val) + +#define AE531X_DESC_LNKBUF_GET(ptr) \ + *(volatile UINT32 *)((UINT32)ptr + AE531X_DESC_LNKBUF) + +#define AE531X_DESC_LNKBUF_SET(ptr, val) \ + AE531X_DESC_LNKBUF_GET(ptr) = (val) + +#define AE531X_DESC_SWPTR_GET(ptr) \ + (void *)(*(volatile UINT32 *) ((UINT32)ptr + AE531X_DESC_SWPTR)) + +#define AE531X_DESC_SWPTR_SET(ptr,val) \ + AE531X_DESC_SWPTR_GET(ptr) = (void *)(val) + +/* Get size of Rx data from desc, in bytes */ +#define AE531X_DESC_STATUS_RX_SIZE(x) \ + (((x) & DescFrameLengthMask) >> DescFrameLengthShift) + +#endif /* _AE531XREG_H_ */ diff -urN linux-2.4.32.new/arch/mips/ar531x/Makefile linux-2.4.32.new-eth/arch/mips/ar531x/Makefile --- linux-2.4.32.new/arch/mips/ar531x/Makefile 2005-12-24 20:29:42.010325312 +0000 +++ linux-2.4.32.new-eth/arch/mips/ar531x/Makefile 2005-12-25 12:03:16.213872024 +0000 @@ -28,6 +28,13 @@ ar531xirq.o \ ar531xintr.o \ ar531xgpio.o \ - ar531xksyms.o + ar531xksyms.o \ + ae531xlnx.o \ + ae531xmac.o include $(TOPDIR)/Rules.make +obj-$(CONFIG_MARVELL_ENET_PHY) += mvPhy.o +obj-$(CONFIG_KENDIN_ENET_PHY)) += rtPhy.o +obj-$(CONFIG_REALTEK_ENET_PHY) += rtPhy.o + + diff -urN linux-2.4.32.new/arch/mips/ar531x/mvPhy.c linux-2.4.32.new-eth/arch/mips/ar531x/mvPhy.c --- linux-2.4.32.new/arch/mips/ar531x/mvPhy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/mvPhy.c 2005-12-25 11:54:39.730389448 +0000 @@ -0,0 +1,1237 @@ +/* + * 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. + */ + +/* +* Manage the ethernet PHY switch, Marvell 88E6060. +* +* This module is intended to be largely OS and platform-independent. +*/ + +#if defined(linux) +#include +#include +#include +#include +#include + +#include "ar531xlnx.h" +#endif + +#if defined(__ECOS) +#include "ae531xecos.h" +#endif + + +#include "ae531xmac.h" +#include "ae531xreg.h" +#include "mvPhy.h" + +#if /* DEBUG */ 1 +#define MV_DEBUG_ERROR 0x00000001 +#define MV_DEBUG_PHYSETUP 0x00000002 +#define MV_DEBUG_PHYCHANGE 0x00000004 + +int mvPhyDebug = MV_DEBUG_ERROR; + +#define MV_PRINT(FLG, X) \ +{ \ + if (mvPhyDebug & (FLG)) { \ + DEBUG_PRINTF X; \ + } \ +} +#else +#define MV_PRINT(FLG, X) +#endif + +#ifdef CONFIG_VENETDEV +/* + * On AR5312 with CONFIG_VENETDEV==1, + * ports 0..3 are LAN ports (accessed through ae0) + * port 4 is the WAN port. (accessed through ae1) + * + * The phy switch settings in the mvPhyInfo table are set accordingly. + */ +#define MV_WAN_PORT 4 +#define MV_IS_LAN_PORT(port) ((port) < MV_WAN_PORT) +#define MV_IS_WAN_PORT(port) ((port) == MV_WAN_PORT) +#endif + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL isEnetPort; /* normal enet port */ + BOOL isPhyAlive; /* last known state of link */ + int ethUnit; /* MAC associated with this phy port */ + UINT32 phyBase; + UINT32 phyAddr; /* PHY registers associated with this phy port */ + UINT32 switchPortAddr; /* switch port regs assoc'ed with this phy port */ + UINT32 VLANTableSetting; /* Value to be written to VLAN table */ +} mvPhyInfo_t; + +/****************************************************************************** + * Per-PHY information, indexed by PHY unit number. + * + * This table is board-dependent. It includes information + * about which enet MAC controls which PHY port. + */ +mvPhyInfo_t mvPhyInfo[] = { + /* + * On AP30/AR5312, all PHYs are associated with MAC0. + * AP30/AR5312's MAC1 isn't used for anything. + * CONFIG_VENETDEV==1 (router) configuration: + * Ports 0,1,2, and 3 are "LAN ports" + * Port 4 is a WAN port + * Port 5 connects to MAC0 in the AR5312 + * CONFIG_VENETDEV==0 (bridge) configuration: + * Ports 0,1,2,3,4 are "LAN ports" + * Port 5 connects to the MAC0 in the AR5312 + */ + {isEnetPort: TRUE, /* phy port 0 -- LAN port 0 */ + isPhyAlive: FALSE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x10, + switchPortAddr: 0x18, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x2e +#else + VLANTableSetting: 0x3e +#endif + }, + + {isEnetPort: TRUE, /* phy port 1 -- LAN port 1 */ + isPhyAlive: FALSE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x11, + switchPortAddr: 0x19, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x2d +#else + VLANTableSetting: 0x3d +#endif + }, + + {isEnetPort: TRUE, /* phy port 2 -- LAN port 2 */ + isPhyAlive: FALSE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x12, + switchPortAddr: 0x1a, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x2b +#else + VLANTableSetting: 0x3b +#endif + }, + + {isEnetPort: TRUE, /* phy port 3 -- LAN port 3 */ + isPhyAlive: FALSE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x13, + switchPortAddr: 0x1b, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x27 +#else + VLANTableSetting: 0x37 +#endif + }, + + {isEnetPort: TRUE, /* phy port 4 -- WAN port or LAN port 4 */ + isPhyAlive: FALSE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x14, + switchPortAddr: 0x1c, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x1020 /* WAN port */ +#else + VLANTableSetting: 0x2f /* LAN port 4 */ +#endif + }, + + {isEnetPort: FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */ + isPhyAlive: TRUE, + ethUnit: 0, + phyBase: 0, + phyAddr: 0x15, + switchPortAddr: 0x1d, +#ifdef CONFIG_VENETDEV + VLANTableSetting: 0x0f /* Send only to LAN ports */ +#else + VLANTableSetting: 0x1f /* Send to all ports */ +#endif + }, +}; + +#define MV_PHY_MAX (sizeof(mvPhyInfo) / sizeof(mvPhyInfo[0])) + +/* Range of valid PHY IDs is [MIN..MAX] */ +#define MV_ID_MIN 0 +#define MV_ID_MAX (MV_PHY_MAX-1) + +/* Convenience macros to access myPhyInfo */ +#define MV_IS_ENET_PORT(phyUnit) (mvPhyInfo[phyUnit].isEnetPort) +#define MV_IS_PHY_ALIVE(phyUnit) (mvPhyInfo[phyUnit].isPhyAlive) +#define MV_ETHUNIT(phyUnit) (mvPhyInfo[phyUnit].ethUnit) +#define MV_PHYBASE(phyUnit) (mvPhyInfo[phyUnit].phyBase) +#define MV_PHYADDR(phyUnit) (mvPhyInfo[phyUnit].phyAddr) +#define MV_SWITCH_PORT_ADDR(phyUnit) (mvPhyInfo[phyUnit].switchPortAddr) +#define MV_VLAN_TABLE_SETTING(phyUnit) (mvPhyInfo[phyUnit].VLANTableSetting) + +#define MV_IS_ETHUNIT(phyUnit, ethUnit) \ + (MV_IS_ENET_PORT(phyUnit) && \ + MV_ETHUNIT(phyUnit) == (ethUnit)) + + +/* Forward references */ +BOOL mv_phyIsLinkAlive(int phyUnit); +LOCAL void mv_VLANInit(int ethUnit); +LOCAL void mv_enableConfiguredPorts(int ethUnit); +LOCAL void mv_verifyReady(int ethUnit); +BOOL mv_phySetup(int ethUnit, UINT32 phyBase); +BOOL mv_phyIsFullDuplex(int ethUnit); +BOOL mv_phyIsSpeed100(int phyUnit); +LOCAL BOOL mv_validPhyId(int phyUnit); +void mv_flushATUDB(int phyUnit); +void mv_phyCheckStatusChange(int ethUnit); +#if DEBUG +void mv_phyShow(int phyUnit); +void mv_phySet(int phyUnit, UINT32 regnum, UINT32 value); +void mv_switchPortSet(int phyUnit, UINT32 regnum, UINT32 value); +void mv_switchGlobalSet(int phyUnit, UINT32 regnum, UINT32 value); +void mv_showATUDB(int phyUnit); +void mv_countGoodFrames(int phyUnit); +void mv_countBadFrames(int phyUnit); +void mv_showFrameCounts(int phyUnit); +#endif + + +/****************************************************************************** +* +* mv_phyIsLinkAlive - test to see if the specified link is alive +* +* RETURNS: +* TRUE --> link is alive +* FALSE --> link is down +*/ +BOOL +mv_phyIsLinkAlive(int phyUnit) +{ + UINT16 phyHwStatus; + UINT32 phyBase; + UINT32 phyAddr; + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyHwStatus = phyRegRead(phyBase, phyAddr, MV_PHY_SPECIFIC_STATUS); + + if (phyHwStatus & MV_STATUS_REAL_TIME_LINK_UP) { + return TRUE; + } else { + return FALSE; + } +} + +/****************************************************************************** +* +* mv_VLANInit - initialize "port-based VLANs" for the specified enet unit. +*/ +LOCAL void +mv_VLANInit(int ethUnit) +{ + int phyUnit; + UINT32 phyBase; + UINT32 switchPortAddr; + + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (MV_ETHUNIT(phyUnit) != ethUnit) { + continue; + } + + phyBase = MV_PHYBASE(phyUnit); + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + phyRegWrite(phyBase, switchPortAddr, MV_PORT_BASED_VLAN_MAP, + MV_VLAN_TABLE_SETTING(phyUnit)); + } +} + +#define phyPortConfigured(phyUnit) TRUE /* TBDFREEDOM2 */ + +/****************************************************************************** +* +* mv_enableConfiguredPorts - enable whichever PHY ports are supposed +* to be enabled according to administrative configuration. +*/ +LOCAL void +mv_enableConfiguredPorts(int ethUnit) +{ + int phyUnit; + UINT32 phyBase; + UINT32 switchPortAddr; + UINT16 portControl; + UINT16 portAssociationVector; + + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (MV_ETHUNIT(phyUnit) != ethUnit) { + continue; + } + + phyBase = MV_PHYBASE(phyUnit); + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + if (phyPortConfigured(phyUnit)) { + + portControl = MV_PORT_CONTROL_PORT_STATE_FORWARDING; +#ifdef CONFIG_VENETDEV + if (!MV_IS_ENET_PORT(phyUnit)) { /* CPU port */ + portControl |= MV_PORT_CONTROL_INGRESS_TRAILER + | MV_PORT_CONTROL_EGRESS_MODE; + } +#endif + phyRegWrite(phyBase, switchPortAddr, MV_PORT_CONTROL, portControl); + + if (MV_IS_ENET_PORT(phyUnit)) { + portAssociationVector = 1 << phyUnit; + } else { + /* Disable learning on the CPU port */ + portAssociationVector = 0; + } + phyRegWrite(phyBase, switchPortAddr, + MV_PORT_ASSOCIATION_VECTOR, portAssociationVector); + } + } +} + +/****************************************************************************** +* +* mv_verifyReady - validates that we're dealing with the device +* we think we're dealing with, and that it's ready. +*/ +LOCAL void +mv_verifyReady(int ethUnit) +{ + int phyUnit; + UINT16 globalStatus; + UINT32 phyBase = 0; + UINT32 phyAddr; + UINT32 switchPortAddr; + UINT16 phyID1; + UINT16 phyID2; + UINT16 switchID; + + /* + * The first read to the Phy port registers always fails and + * returns 0. So get things started with a bogus read. + */ + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + (void)phyRegRead(phyBase, phyAddr, MV_PHY_ID1); /* returns 0 */ + break; + } + + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + /*******************/ + /* Verify phy port */ + /*******************/ + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyID1 = phyRegRead(phyBase, phyAddr, MV_PHY_ID1); + if (phyID1 != MV_PHY_ID1_EXPECTATION) { + MV_PRINT(MV_DEBUG_PHYSETUP, + ("Invalid PHY ID1 for ethmac%d port%d. Expected 0x%04x, read 0x%04x\n", + ethUnit, + phyUnit, + MV_PHY_ID1_EXPECTATION, + phyID1)); + return; + } + + phyID2 = phyRegRead(phyBase, phyAddr, MV_PHY_ID2); + if ((phyID2 & MV_OUI_LSB_MASK) != MV_OUI_LSB_EXPECTATION) { + MV_PRINT(MV_DEBUG_PHYSETUP, + ("Invalid PHY ID2 for ethmac%d port %d. Expected 0x%04x, read 0x%04x\n", + ethUnit, + phyUnit, + MV_OUI_LSB_EXPECTATION, + phyID2)); + return; + } + + MV_PRINT(MV_DEBUG_PHYSETUP, + ("Found PHY ethmac%d port%d: model 0x%x revision 0x%x\n", + ethUnit, + phyUnit, + (phyID2 & MV_MODEL_NUM_MASK) >> MV_MODEL_NUM_SHIFT, + (phyID2 & MV_REV_NUM_MASK) >> MV_REV_NUM_SHIFT)); + + + /**********************/ + /* Verify switch port */ + /**********************/ + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + switchID = phyRegRead(phyBase, switchPortAddr, MV_SWITCH_ID); + if ((switchID & MV_SWITCH_ID_DEV_MASK) != + MV_SWITCH_ID_DEV_EXPECTATION) { + + MV_PRINT(MV_DEBUG_PHYSETUP, + ("Invalid switch ID for ethmac%d port %d. Expected 0x%04x, read 0x%04x\n", + ethUnit, + phyUnit, + MV_SWITCH_ID_DEV_EXPECTATION, + switchID)); + return; + } + + MV_PRINT(MV_DEBUG_PHYSETUP, + ("Found PHY switch for enet %d port %d deviceID 0x%x revision 0x%x\n", + ethUnit, + phyUnit, + (switchID & MV_SWITCH_ID_DEV_MASK) >> MV_SWITCH_ID_DEV_SHIFT, + (switchID & MV_SWITCH_ID_REV_MASK) >> MV_SWITCH_ID_REV_SHIFT)) + } + + /*******************************/ + /* Verify that switch is ready */ + /*******************************/ + if (phyBase) { + globalStatus = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_STATUS); + + if (!(globalStatus & MV_SWITCH_STATUS_READY_MASK)) { + MV_PRINT(MV_DEBUG_PHYSETUP, + ("PHY switch for ethmac%d NOT ready!\n", + ethUnit)); + } + } else { + MV_PRINT(MV_DEBUG_PHYSETUP, + ("No ports configured for ethmac%d\n", ethUnit)); + } +} + +/****************************************************************************** +* +* mv_phySetup - reset and setup the PHY switch. +* +* Resets each PHY port. +* +* RETURNS: +* TRUE --> at least 1 PHY with LINK +* FALSE --> no LINKs on this ethernet unit +*/ +BOOL +mv_phySetup(int ethUnit, UINT32 phyBase) +{ + int phyUnit; + int liveLinks = 0; + BOOL foundPhy = FALSE; + UINT32 phyAddr; + UINT16 atuControl; + + /* + * Allow platform-specific code to determine the default Ethernet MAC + * at run-time. If phyEthMacDefault returns a negative value, use the + * static mvPhyInfo table "as is". But if phyEthMacDefault returns a + * non-negative value, use it as the default ethernet unit. + */ + { + int ethMacDefault = phyEthMacDefault(); + + if (ethMacDefault >= 0) { + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + MV_ETHUNIT(phyUnit)=ethMacDefault; + } + } + } + + /* + * See if there's any configuration data for this enet, + * and set up phyBase in table. + */ + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (MV_ETHUNIT(phyUnit) != ethUnit) { + continue; + } + + MV_PHYBASE(phyUnit) = phyBase; + foundPhy = TRUE; + } + + if (!foundPhy) { + return FALSE; /* No PHY's configured for this ethUnit */ + } + + /* Verify that the switch is what we think it is, and that it's ready */ + mv_verifyReady(ethUnit); + + /* Initialize global switch settings */ + atuControl = MV_ATUCTRL_AGE_TIME_DEFAULT << MV_ATUCTRL_AGE_TIME_SHIFT; + atuControl |= MV_ATUCTRL_ATU_SIZE_DEFAULT << MV_ATUCTRL_ATU_SIZE_SHIFT; + // atuControl |= MV_ATUCTRL_ATU_LEARNDIS; + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_CONTROL, atuControl); + + /* Reset PHYs and start autonegoation on each. */ + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (MV_ETHUNIT(phyUnit) != ethUnit) { + continue; + } + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyRegWrite(phyBase, phyAddr, MV_PHY_CONTROL, + MV_CTRL_SOFTWARE_RESET | MV_CTRL_AUTONEGOTIATION_ENABLE); + } + + { + int timeout; + UINT16 phyHwStatus; + + /* + * Wait 5 seconds for ALL associated PHYs to finish autonegotiation. + */ + timeout=50; + for (phyUnit=0; (phyUnit < MV_PHY_MAX) && (timeout > 0); phyUnit++) { + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + for (;;) { + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyHwStatus = phyRegRead(phyBase, phyAddr, MV_PHY_SPECIFIC_STATUS); + + if (MV_AUTONEG_DONE(phyHwStatus)) { + break; + } + + if (--timeout == 0) { + break; + } + + sysMsDelay(100); + } + } + } + + /* + * All PHYs have had adequate time to autonegotiate. + * Now initialize software status. + * + * It's possible that some ports may take a bit longer + * to autonegotiate; but we can't wait forever. They'll + * get noticed by mv_phyCheckStatusChange during regular + * polling activities. + */ + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + if (mv_phyIsLinkAlive(phyUnit)) { + liveLinks++; + MV_IS_PHY_ALIVE(phyUnit) = TRUE; +#ifdef DEBUG + mv_phyShow(phyUnit); +#endif + } else { + MV_IS_PHY_ALIVE(phyUnit) = FALSE; + } + + MV_PRINT(MV_DEBUG_PHYSETUP, + ("ethmac%d: Phy Status=%4.4x\n", + ethUnit, + phyRegRead(MV_PHYBASE(phyUnit), + MV_PHYADDR(phyUnit), + MV_PHY_SPECIFIC_STATUS))); + + } + +#ifdef DEBUG + /* PHY 5 is the connection to the CPU */ + mv_phyShow(5); +#endif + + mv_VLANInit(ethUnit); + + mv_enableConfiguredPorts(ethUnit); + + return (liveLinks > 0); +} + + +/****************************************************************************** +* +* mv_phyIsDuplexFull - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* TRUE --> at least one associated PHY in FULL DUPLEX +* FALSE --> all half duplex, or no links +*/ +BOOL +mv_phyIsFullDuplex(int ethUnit) +{ + int phyUnit; + UINT32 phyBase; + UINT32 phyAddr; + UINT16 phyHwStatus; + + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + if (mv_phyIsLinkAlive(phyUnit)) { + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyHwStatus = phyRegRead(phyBase, phyAddr, MV_PHY_SPECIFIC_STATUS); + + if (phyHwStatus & MV_STATUS_RESOLVED_DUPLEX_FULL) { + return TRUE; + } + } + } + + return FALSE; +} + +/****************************************************************************** +* +* mv_phyIsSpeed100 - Determines the speed of a phy port +* +* RETURNS: +* TRUE --> PHY operating at 100 Mbit +* FALSE --> link down, or not operating at 100 Mbit +*/ +BOOL +mv_phyIsSpeed100(int phyUnit) +{ + UINT16 phyHwStatus; + UINT32 phyBase; + UINT32 phyAddr; + + if (MV_IS_ENET_PORT(phyUnit)) { + if (mv_phyIsLinkAlive(phyUnit)) { + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyHwStatus = phyRegRead(phyBase, phyAddr, MV_PHY_SPECIFIC_STATUS); + + if (phyHwStatus & MV_STATUS_RESOLVED_SPEED_100) { + return TRUE; + } + } + } + + return FALSE; +} + +#ifdef CONFIG_VENETDEV +/****************************************************************************** +* +* mv_phyDetermineSource - Examine a received frame's Egress Trailer +* to determine whether it came from a LAN or WAN port. +* +* RETURNS: +* Returns 1-->LAN, 0-->WAN, -1-->ERROR +*/ +int +mv_phyDetermineSource(char *data, int len) +{ + unsigned char *phyTrailer; + unsigned char incomingPort; + + phyTrailer = &data[len - MV_PHY_TRAILER_SIZE]; + + /* ASSERT(phyTrailer[0] == MV_EGRESS_TRAILER_VALID); */ + if (phyTrailer[0] != MV_EGRESS_TRAILER_VALID) { + printk(KERN_ERR "PHY trailer invalid; got %#02x, expected %#02x\n", + phyTrailer[0], MV_EGRESS_TRAILER_VALID); + return -1; + } + + incomingPort = phyTrailer[1]; + if (MV_IS_LAN_PORT(incomingPort)) { + return 1; + } else { + /* ASSERT(MV_IS_WAN_PORT(incomingPort)); */ + if (!MV_IS_WAN_PORT(incomingPort)) { + printk(KERN_ERR "incoming port was %d; expected 0-4\n", incomingPort); + return -1; + } + return 0; + } +} + + +/****************************************************************************** +* +* mv_phySetDestinationPort - Set the Ingress Trailer to force the +* frame to be sent to LAN or WAN, as specified. +* +*/ +void +mv_phySetDestinationPort(char *data, int len, int fromLAN) +{ + char *phyTrailer; + + phyTrailer = &data[len]; + if (fromLAN) { + /* LAN ports: Use default settings, as per mvPhyInfo */ + phyTrailer[0] = 0x00; + phyTrailer[1] = 0x00; + } else { + /* WAN port: Direct to WAN port */ + phyTrailer[0] = MV_INGRESS_TRAILER_OVERRIDE; + phyTrailer[1] = 1 << MV_WAN_PORT; + } + phyTrailer[2] = 0x00; + phyTrailer[3] = 0x00; +} +#endif + + +/***************************************************************************** +* +* Validate that the specified PHY unit number is a valid PHY ID. +* Print a message if it is invalid. +* RETURNS +* TRUE --> valid +* FALSE --> invalid +*/ +LOCAL BOOL +mv_validPhyId(int phyUnit) +{ + if ((phyUnit >= MV_ID_MIN) && (phyUnit <= MV_ID_MAX)) { + return TRUE; + } else { + PRINTF("PHY unit number must be in the range [%d..%d]\n", + MV_ID_MIN, MV_ID_MAX); + return FALSE; + } +} + + +/***************************************************************************** +* +* mv_waitWhileATUBusy - spins until the ATU completes +* its previous operation. +*/ +LOCAL void +mv_waitWhileATUBusy(UINT32 phyBase) +{ + BOOL isBusy; + UINT16 ATUOperation; + + do { + + ATUOperation = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_ATU_OPERATION); + + isBusy = (ATUOperation & MV_ATU_BUSY_MASK) == MV_ATU_IS_BUSY; + + } while(isBusy); +} + +/***************************************************************************** +* +* mv_flushATUDB - flushes ALL entries in the Address Translation Unit +* DataBase associated with phyUnit. [Since we use a single DB for +* all PHYs, this flushes the entire shared DataBase.] +* +* The current implementation flushes even more than absolutely needed -- +* it flushes all entries for all phyUnits on the same ethernet as the +* specified phyUnit. +* +* It is called only when a link failure is detected on a port that was +* previously working. In other words, when the cable is unplugged. +*/ +void +mv_flushATUDB(int phyUnit) +{ + UINT32 phyBase; + + if (!mv_validPhyId(phyUnit)) { + PRINTF("Invalid port number: %d\n", phyUnit); + return; + } + + phyBase = MV_PHYBASE(phyUnit); + + /* Wait for previous operation (if any) to complete */ + mv_waitWhileATUBusy(phyBase); + + /* Tell hardware to flush all entries */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_OPERATION, + MV_ATU_OP_FLUSH_ALL | MV_ATU_IS_BUSY); + + mv_waitWhileATUBusy(phyBase); +} + +/***************************************************************************** +* +* mv_phyCheckStatusChange -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +*/ +void +mv_phyCheckStatusChange(int ethUnit) +{ + int phyUnit; + UINT16 phyHwStatus; + mvPhyInfo_t *lastStatus; + int linkCount = 0; + int lostLinks = 0; + int gainedLinks = 0; + UINT32 phyBase; + UINT32 phyAddr; + + for (phyUnit=0; phyUnit < MV_PHY_MAX; phyUnit++) { + if (!MV_IS_ETHUNIT(phyUnit, ethUnit)) { + continue; + } + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + lastStatus = &mvPhyInfo[phyUnit]; + phyHwStatus = phyRegRead(phyBase, phyAddr, MV_PHY_SPECIFIC_STATUS); + + if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ + /* See if we've lost link */ + if (phyHwStatus & MV_STATUS_REAL_TIME_LINK_UP) { + linkCount++; + } else { + lostLinks++; + mv_flushATUDB(phyUnit); + MV_PRINT(MV_DEBUG_PHYCHANGE,("\nethmac%d port%d down\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = FALSE; + } + } else { /* last known link status was DEAD */ + /* Check for AutoNegotiation complete */ + if (MV_AUTONEG_DONE(phyHwStatus)) { + gainedLinks++; + linkCount++; + MV_PRINT(MV_DEBUG_PHYCHANGE,("\nethmac%d port%d up\n", + ethUnit, phyUnit)); + lastStatus->isPhyAlive = TRUE; + } + } + } + + if (linkCount == 0) { + if (lostLinks) { + /* We just lost the last link for this MAC */ + phyLinkLost(ethUnit); + } + } else { + if (gainedLinks == linkCount) { + /* We just gained our first link(s) for this MAC */ + phyLinkGained(ethUnit); + } + } +} + +#if DEBUG + +/* Define the registers of interest for a phyShow command */ +typedef struct mvRegisterTableEntry_s { + UINT32 regNum; + char *regIdString; +} mvRegisterTableEntry_t; + +mvRegisterTableEntry_t mvPhyRegisterTable[] = { + {MV_PHY_CONTROL, "PHY Control "}, + {MV_PHY_STATUS, "PHY Status "}, + {MV_PHY_ID1, "PHY Identifier 1 "}, + {MV_PHY_ID2, "PHY Identifier 2 "}, + {MV_AUTONEG_ADVERT, "Auto-Negotiation Advertisement "}, + {MV_LINK_PARTNER_ABILITY, "Link Partner Ability "}, + {MV_AUTONEG_EXPANSION, "Auto-Negotiation Expansion "}, + {MV_NEXT_PAGE_TRANSMIT, "Next Page Transmit "}, + {MV_LINK_PARTNER_NEXT_PAGE, "Link Partner Next Page "}, + {MV_PHY_SPECIFIC_CONTROL_1, "PHY-Specific Control Register 1 "}, + {MV_PHY_SPECIFIC_STATUS, "PHY-Specific Status "}, + {MV_PHY_INTERRUPT_ENABLE, "PHY Interrupt Enable "}, + {MV_PHY_INTERRUPT_STATUS, "PHY Interrupt Status "}, + {MV_PHY_INTERRUPT_PORT_SUMMARY, "PHY Interrupt Port Summary "}, + {MV_RECEIVE_ERROR_COUNTER, "Receive Error Counter "}, + {MV_LED_PARALLEL_SELECT, "LED Parallel Select "}, + {MV_LED_STREAM_SELECT_LEDS, "LED Stream Select "}, + {MV_PHY_LED_CONTROL, "PHY LED Control "}, + {MV_PHY_MANUAL_LED_OVERRIDE, "PHY Manual LED Override "}, + {MV_VCT_CONTROL, "VCT Control "}, + {MV_VCT_STATUS, "VCT Status "}, + {MV_PHY_SPECIFIC_CONTROL_2, "PHY-Specific Control Register 2 "}, +}; +int mvPhyNumRegs = sizeof(mvPhyRegisterTable) / sizeof(mvPhyRegisterTable[0]); + + +mvRegisterTableEntry_t mvSwitchPortRegisterTable[] = { + {MV_PORT_STATUS, "Port Status "}, + {MV_SWITCH_ID, "Switch ID "}, + {MV_PORT_CONTROL, "Port Control "}, + {MV_PORT_BASED_VLAN_MAP, "Port-Based VLAN Map "}, + {MV_PORT_ASSOCIATION_VECTOR, "Port Association Vector "}, + {MV_RX_COUNTER, "RX Counter "}, + {MV_TX_COUNTER, "TX Counter "}, +}; +int mvSwitchPortNumRegs = + sizeof(mvSwitchPortRegisterTable) / sizeof(mvSwitchPortRegisterTable[0]); + + +mvRegisterTableEntry_t mvSwitchGlobalRegisterTable[] = { + {MV_SWITCH_GLOBAL_STATUS, "Switch Global Status "}, + {MV_SWITCH_MAC_ADDR0, "Switch MAC Addr 0 & 1 "}, + {MV_SWITCH_MAC_ADDR2, "Switch MAC Addr 2 & 3 "}, + {MV_SWITCH_MAC_ADDR4, "Switch MAC Addr 4 & 5 "}, + {MV_SWITCH_GLOBAL_CONTROL, "Switch Global Control "}, + {MV_ATU_CONTROL, "ATU Control "}, + {MV_ATU_OPERATION, "ATU Operation "}, + {MV_ATU_DATA, "ATU Data "}, + {MV_ATU_MAC_ADDR0, "ATU MAC Addr 0 & 1 "}, + {MV_ATU_MAC_ADDR2, "ATU MAC Addr 2 & 3 "}, + {MV_ATU_MAC_ADDR4, "ATU MAC Addr 4 & 5 "}, +}; +int mvSwitchGlobalNumRegs = + sizeof(mvSwitchGlobalRegisterTable) / sizeof(mvSwitchGlobalRegisterTable[0]); + + +/***************************************************************************** +* +* mv_phyShow - Dump the state of a PHY. +* There are two sets of registers for each phy port: +* "phy registers" and +* "switch port registers" +* We dump 'em all, plus the switch global registers. +*/ +void +mv_phyShow(int phyUnit) +{ + int i; + UINT16 value; + UINT32 phyBase; + UINT32 phyAddr; + UINT32 switchPortAddr; + + if (!mv_validPhyId(phyUnit)) { + return; + } + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + PRINTF("PHY state for PHY%d (ethmac%d, phyBase 0x%8x, phyAddr 0x%x, switchAddr 0x%x)\n", + phyUnit, + MV_ETHUNIT(phyUnit), + MV_PHYBASE(phyUnit), + MV_PHYADDR(phyUnit), + MV_SWITCH_PORT_ADDR(phyUnit)); + + PRINTF("PHY Registers:\n"); + for (i=0; i < mvPhyNumRegs; i++) { + + value = phyRegRead(phyBase, phyAddr, mvPhyRegisterTable[i].regNum); + + PRINTF("Reg %02d (0x%02x) %s = 0x%08x\n", + mvPhyRegisterTable[i].regNum, + mvPhyRegisterTable[i].regNum, + mvPhyRegisterTable[i].regIdString, + value); + } + + PRINTF("Switch Port Registers:\n"); + for (i=0; i < mvSwitchPortNumRegs; i++) { + + value = phyRegRead(phyBase, switchPortAddr, + mvSwitchPortRegisterTable[i].regNum); + + PRINTF("Reg %02d (0x%02x) %s = 0x%08x\n", + mvSwitchPortRegisterTable[i].regNum, + mvSwitchPortRegisterTable[i].regNum, + mvSwitchPortRegisterTable[i].regIdString, + value); + } + + PRINTF("Switch Global Registers:\n"); + for (i=0; i < mvSwitchGlobalNumRegs; i++) { + + value = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, + mvSwitchGlobalRegisterTable[i].regNum); + + PRINTF("Reg %02d (0x%02x) %s = 0x%08x\n", + mvSwitchGlobalRegisterTable[i].regNum, + mvSwitchGlobalRegisterTable[i].regNum, + mvSwitchGlobalRegisterTable[i].regIdString, + value); + } +} + +/***************************************************************************** +* +* mv_phySet - Modify the value of a PHY register (debug only). +*/ +void +mv_phySet(int phyUnit, UINT32 regnum, UINT32 value) +{ + UINT32 phyBase; + UINT32 phyAddr; + + if (mv_validPhyId(phyUnit)) { + + phyBase = MV_PHYBASE(phyUnit); + phyAddr = MV_PHYADDR(phyUnit); + + phyRegWrite(phyBase, phyAddr, regnum, value); + } +} + + +/***************************************************************************** +* +* mv_switchPortSet - Modify the value of a switch port register (debug only). +*/ +void +mv_switchPortSet(int phyUnit, UINT32 regnum, UINT32 value) +{ + UINT32 phyBase; + UINT32 switchPortAddr; + + if (mv_validPhyId(phyUnit)) { + + phyBase = MV_PHYBASE(phyUnit); + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + phyRegWrite(phyBase, switchPortAddr, regnum, value); + } +} + +/***************************************************************************** +* +* mv_switchGlobalSet - Modify the value of a switch global register +* (debug only). +*/ +void +mv_switchGlobalSet(int phyUnit, UINT32 regnum, UINT32 value) +{ + UINT32 phyBase; + + if (mv_validPhyId(phyUnit)) { + + phyBase = MV_PHYBASE(phyUnit); + + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, regnum, value); + } +} + +/***************************************************************************** +* +* mv_showATUDB - Dump the contents of the Address Translation Unit DataBase +* for the PHY switch associated with the specified phy. +*/ +void +mv_showATUDB(int phyUnit) +{ + UINT32 phyBase; + UINT16 ATUData; + UINT16 ATUMac0; + UINT16 ATUMac2; + UINT16 ATUMac4; + int portVec; + int entryState; + + if (!mv_validPhyId(phyUnit)) { + PRINTF("Invalid port number: %d\n", phyUnit); + return; + } + + phyBase = MV_PHYBASE(phyUnit); + + /* Wait for previous operation (if any) to complete */ + mv_waitWhileATUBusy(phyBase); + + /* Initialize ATU MAC to all 1's */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR0, 0xffff); + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR2, 0xffff); + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR4, 0xffff); + + PRINTF(" MAC ADDRESS EntryState PortVector\n"); + + for(;;) { + /* Tell hardware to get next MAC info */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_OPERATION, + MV_ATU_OP_GET_NEXT | MV_ATU_IS_BUSY); + + mv_waitWhileATUBusy(phyBase); + + ATUData = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_DATA); + entryState = (ATUData & MV_ENTRYSTATE_MASK) >> MV_ENTRYSTATE_SHIFT; + + if (entryState == 0) { + /* We've hit the end of the list */ + break; + } + + ATUMac0 = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR0); + ATUMac2 = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR2); + ATUMac4 = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, MV_ATU_MAC_ADDR4); + + portVec = (ATUData & MV_PORTVEC_MASK) >> MV_PORTVEC_SHIFT; + + PRINTF("%02x:%02x:%02x:%02x:%02x:%02x 0x%02x 0x%02x\n", + ATUMac0 >> 8, /* MAC byte 0 */ + ATUMac0 & 0xff, /* MAC byte 1 */ + ATUMac2 >> 8, /* MAC byte 2 */ + ATUMac2 & 0xff, /* MAC byte 3 */ + ATUMac4 >> 8, /* MAC byte 4 */ + ATUMac4 & 0xff, /* MAC byte 5 */ + entryState, + portVec); + } +} + +LOCAL BOOL countingGoodFrames; + +/***************************************************************************** +* +* mv_countGoodFrames - starts counting GOOD RX/TX frames per port +*/ +void +mv_countGoodFrames(int phyUnit) +{ + UINT32 phyBase; + UINT16 globalControl; + + if (mv_validPhyId(phyUnit)) { + /* + * Guarantee that counters are cleared by + * forcing CtrMode to toggle and end on GOODFRAMES. + */ + + phyBase = MV_PHYBASE(phyUnit); + + /* Read current Switch Global Control Register */ + globalControl = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL); + + /* Set CtrMode to count BAD frames */ + globalControl = ((globalControl & ~MV_CTRMODE_MASK) | + MV_CTRMODE_BADFRAMES); + + /* Push new value out to hardware */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL, globalControl); + + /* Now toggle CtrMode to count GOOD frames */ + globalControl = ((globalControl & ~MV_CTRMODE_MASK) | + MV_CTRMODE_GOODFRAMES); + + /* Push new value out to hardware */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL, globalControl); + + countingGoodFrames = TRUE; + } +} + +/***************************************************************************** +* +* mv_countBadFrames - starts counting BAD RX/TX frames per port +*/ +void +mv_countBadFrames(int phyUnit) +{ + UINT32 phyBase; + UINT16 globalControl; + + if (mv_validPhyId(phyUnit)) { + /* + * Guarantee that counters are cleared by + * forcing CtrMode to toggle and end on BADFRAMES. + */ + + phyBase = MV_PHYBASE(phyUnit); + + /* Read current Switch Global Control Register */ + globalControl = phyRegRead(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL); + + /* Set CtrMode to count GOOD frames */ + globalControl = ((globalControl & ~MV_CTRMODE_MASK) | + MV_CTRMODE_GOODFRAMES); + + /* Push new value out to hardware */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL, globalControl); + + /* Now toggle CtrMode to count BAD frames */ + globalControl = ((globalControl & ~MV_CTRMODE_MASK) | + MV_CTRMODE_BADFRAMES); + + /* Push new value out to hardware */ + phyRegWrite(phyBase, MV_SWITCH_GLOBAL_ADDR, + MV_SWITCH_GLOBAL_CONTROL, globalControl); + + countingGoodFrames = FALSE; + } +} + +/***************************************************************************** +* +* mv_showFrameCounts - shows current GOOD/BAD Frame counts +*/ +void +mv_showFrameCounts(int phyUnit) +{ + UINT16 rxCounter; + UINT16 txCounter; + UINT32 phyBase; + UINT32 switchPortAddr; + + if (!mv_validPhyId(phyUnit)) { + return; + } + + phyBase = MV_PHYBASE(phyUnit); + switchPortAddr = MV_SWITCH_PORT_ADDR(phyUnit); + + rxCounter = phyRegRead(phyBase, switchPortAddr, MV_RX_COUNTER); + + txCounter = phyRegRead(phyBase, switchPortAddr, MV_TX_COUNTER); + + PRINTF("port%d %s frames: receive: %05d transmit: %05d\n", + phyUnit, + (countingGoodFrames ? "good" : "error"), + rxCounter, + txCounter); +} +#endif diff -urN linux-2.4.32.new/arch/mips/ar531x/mvPhy.h linux-2.4.32.new-eth/arch/mips/ar531x/mvPhy.h --- linux-2.4.32.new/arch/mips/ar531x/mvPhy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/mvPhy.h 2005-12-25 11:54:39.775382608 +0000 @@ -0,0 +1,160 @@ +/* + * 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. + */ + +/* + * mvPhy.h - definitions for the ethernet PHY -- Marvell 88E6060 + * All definitions in this file are operating system independent! + */ + +#ifndef MVPHY_H +#define MVPHY_H + +/*****************/ +/* PHY Registers */ +/*****************/ +#define MV_PHY_CONTROL 0 +#define MV_PHY_STATUS 1 +#define MV_PHY_ID1 2 +#define MV_PHY_ID2 3 +#define MV_AUTONEG_ADVERT 4 +#define MV_LINK_PARTNER_ABILITY 5 +#define MV_AUTONEG_EXPANSION 6 +#define MV_NEXT_PAGE_TRANSMIT 7 +#define MV_LINK_PARTNER_NEXT_PAGE 8 +#define MV_PHY_SPECIFIC_CONTROL_1 16 +#define MV_PHY_SPECIFIC_STATUS 17 +#define MV_PHY_INTERRUPT_ENABLE 18 +#define MV_PHY_INTERRUPT_STATUS 19 +#define MV_PHY_INTERRUPT_PORT_SUMMARY 20 +#define MV_RECEIVE_ERROR_COUNTER 21 +#define MV_LED_PARALLEL_SELECT 22 +#define MV_LED_STREAM_SELECT_LEDS 23 +#define MV_PHY_LED_CONTROL 24 +#define MV_PHY_MANUAL_LED_OVERRIDE 25 +#define MV_VCT_CONTROL 26 +#define MV_VCT_STATUS 27 +#define MV_PHY_SPECIFIC_CONTROL_2 28 + +/* MV_PHY_CONTROL fields */ +#define MV_CTRL_SOFTWARE_RESET 0x8000 +#define MV_CTRL_AUTONEGOTIATION_ENABLE 0x1000 + +/* MV_PHY_ID1 fields */ +#define MV_PHY_ID1_EXPECTATION 0x0141 /* OUI >> 6 */ + +/* MV_PHY_ID2 fields */ +#define MV_OUI_LSB_MASK 0xfc00 +#define MV_OUI_LSB_EXPECTATION 0x0c00 +#define MV_OUI_LSB_SHIFT 10 +#define MV_MODEL_NUM_MASK 0x03f0 +#define MV_MODEL_NUM_SHIFT 4 +#define MV_REV_NUM_MASK 0x000f +#define MV_REV_NUM_SHIFT 0 + +/* MV_PHY_SPECIFIC_STATUS fields */ +#define MV_STATUS_RESOLVED_SPEED_100 0x4000 +#define MV_STATUS_RESOLVED_DUPLEX_FULL 0x2000 +#define MV_STATUS_RESOLVED 0x0800 +#define MV_STATUS_REAL_TIME_LINK_UP 0x0400 + +/* Check if autonegotiation is complete and link is up */ +#define MV_AUTONEG_DONE(mv_phy_specific_status) \ + (((mv_phy_specific_status) & \ + (MV_STATUS_RESOLVED | MV_STATUS_REAL_TIME_LINK_UP)) == \ + (MV_STATUS_RESOLVED | MV_STATUS_REAL_TIME_LINK_UP)) + + +/*************************/ +/* Switch Port Registers */ +/*************************/ +#define MV_PORT_STATUS 0 +#define MV_SWITCH_ID 3 +#define MV_PORT_CONTROL 4 +#define MV_PORT_BASED_VLAN_MAP 6 +#define MV_PORT_ASSOCIATION_VECTOR 11 +#define MV_RX_COUNTER 16 +#define MV_TX_COUNTER 17 + +/* MV_SWITCH_ID fields */ +#define MV_SWITCH_ID_DEV_MASK 0xfff0 +#define MV_SWITCH_ID_DEV_EXPECTATION 0x0600 +#define MV_SWITCH_ID_DEV_SHIFT 4 +#define MV_SWITCH_ID_REV_MASK 0x000f +#define MV_SWITCH_ID_REV_SHIFT 0 + +/* MV_PORT_CONTROL fields */ +#define MV_PORT_CONTROL_PORT_STATE_MASK 0x0003 +#define MV_PORT_CONTROL_PORT_STATE_DISABLED 0x0000 +#define MV_PORT_CONTROL_PORT_STATE_FORWARDING 0x0003 + +#define MV_PORT_CONTROL_EGRESS_MODE 0x0100 /* Receive */ +#define MV_PORT_CONTROL_INGRESS_TRAILER 0x4000 /* Transmit */ + +#define MV_EGRESS_TRAILER_VALID 0x80 +#define MV_INGRESS_TRAILER_OVERRIDE 0x80 + +#define MV_PHY_TRAILER_SIZE 4 + + +/***************************/ +/* Switch Global Registers */ +/***************************/ +#define MV_SWITCH_GLOBAL_STATUS 0 +#define MV_SWITCH_MAC_ADDR0 1 +#define MV_SWITCH_MAC_ADDR2 2 +#define MV_SWITCH_MAC_ADDR4 3 +#define MV_SWITCH_GLOBAL_CONTROL 4 +#define MV_ATU_CONTROL 10 +#define MV_ATU_OPERATION 11 +#define MV_ATU_DATA 12 +#define MV_ATU_MAC_ADDR0 13 +#define MV_ATU_MAC_ADDR2 14 +#define MV_ATU_MAC_ADDR4 15 + +/* MV_SWITCH_GLOBAL_STATUS fields */ +#define MV_SWITCH_STATUS_READY_MASK 0x0800 + +/* MV_SWITCH_GLOBAL_CONTROL fields */ +#define MV_CTRMODE_MASK 0x0100 +#define MV_CTRMODE_GOODFRAMES 0x0000 +#define MV_CTRMODE_BADFRAMES 0x0100 + +/* MV_ATU_CONTROL fields */ +#define MV_ATUCTRL_ATU_LEARNDIS 0x4000 +#define MV_ATUCTRL_ATU_SIZE_MASK 0x3000 +#define MV_ATUCTRL_ATU_SIZE_SHIFT 12 +#define MV_ATUCTRL_ATU_SIZE_DEFAULT 2 /* 1024 entry database */ +#define MV_ATUCTRL_AGE_TIME_MASK 0x0ff0 +#define MV_ATUCTRL_AGE_TIME_SHIFT 4 +#define MV_ATUCTRL_AGE_TIME_DEFAULT 19 /* 19 * 16 = 304 seconds */ + +/* MV_ATU_OPERATION fields */ +#define MV_ATU_BUSY_MASK 0x8000 +#define MV_ATU_IS_BUSY 0x8000 +#define MV_ATU_IS_FREE 0x0000 +#define MV_ATU_OP_MASK 0x7000 +#define MV_ATU_OP_FLUSH_ALL 0x1000 +#define MV_ATU_OP_GET_NEXT 0x4000 + +/* MV_ATU_DATA fields */ +#define MV_ENTRYPRI_MASK 0xc000 +#define MV_ENTRYPRI_SHIFT 14 +#define MV_PORTVEC_MASK 0x03f0 +#define MV_PORTVEC_SHIFT 4 +#define MV_ENTRYSTATE_MASK 0x000f +#define MV_ENTRYSTATE_SHIFT 0 + +/* PHY Address for the switch itself */ +#define MV_SWITCH_GLOBAL_ADDR 0x1f + +BOOL mv_phySetup(int ethUnit, UINT32 phyBase); +void mv_phyCheckStatusChange(int ethUnit); +BOOL mv_phyIsSpeed100(int ethUnit); +BOOL mv_phyIsFullDuplex(int ethUnit); + +#endif /* MVPHY_H */ diff -urN linux-2.4.32.new/arch/mips/ar531x/rtPhy.c linux-2.4.32.new-eth/arch/mips/ar531x/rtPhy.c --- linux-2.4.32.new/arch/mips/ar531x/rtPhy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.32.new-eth/arch/mips/ar531x/rtPhy.c 2005-12-25 11:54:46.086423184 +0000 @@ -0,0 +1,272 @@ +/* + * 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. + */ + +/* + * Manage the ethernet PHY. + * This code supports a simple 1-port ethernet phy, Realtek RTL8201BL, + * and compatible PHYs, such as the Kendin KS8721B. + * All definitions in this file are operating system independent! + */ + +#if defined(linux) +#include +#include +#include +#include +#include + +#include "ar531xlnx.h" +#endif + +#if defined(__ECOS) +#include "ae531xecos.h" +#endif + + +#include "ae531xmac.h" +#include "ae531xreg.h" +#include "rtPhy.h" + +#if /* DEBUG */ 1 +#define RT_DEBUG_ERROR 0x00000001 +#define RT_DEBUG_PHYSETUP 0x00000002 +#define RT_DEBUG_PHYCHANGE 0x00000004 + +int rtPhyDebug = RT_DEBUG_ERROR; + +#define RT_PRINT(FLG, X) \ +{ \ + if (rtPhyDebug & (FLG)) { \ + DEBUG_PRINTF X; \ + } \ +} +#else +#define RT_PRINT(FLG, X) +#endif + +/* + * Track per-PHY port information. + */ +typedef struct { + BOOL phyAlive; /* last known state of link */ + UINT32 phyBase; + UINT32 phyAddr; +} rtPhyInfo_t; + +#define ETH_PHY_ADDR 1 + +/* + * This table defines the mapping from phy units to + * per-PHY information. + * + * This table is somewhat board-dependent. + */ +rtPhyInfo_t rtPhyInfo[] = { + {phyAlive: FALSE, /* PHY 0 */ + phyBase: 0, /* filled in by rt_phySetup */ + phyAddr: ETH_PHY_ADDR}, + + {phyAlive: FALSE, /* PHY 1 */ + phyBase: 0, /* filled in by rt_phySetup */ + phyAddr: ETH_PHY_ADDR} +}; + +/* Convert from phy unit# to (phyBase, phyAddr) pair */ +#define RT_PHYBASE(phyUnit) (rtPhyInfo[phyUnit].phyBase) +#define RT_PHYADDR(phyUnit) (rtPhyInfo[phyUnit].phyAddr) + + +/****************************************************************************** +* +* rt_phySetup - reset and setup the PHY associated with +* the specified MAC unit number. +* +* Resets the associated PHY port. +* +* RETURNS: +* TRUE --> associated PHY is alive +* FALSE --> no LINKs on this ethernet unit +*/ + +BOOL +rt_phySetup(int ethUnit, UINT32 phyBase) +{ + BOOL linkAlive = FALSE; + UINT32 phyAddr; + + RT_PHYBASE(ethUnit) = phyBase; + + phyAddr = RT_PHYADDR(ethUnit); + + /* Reset phy */ + phyRegWrite(phyBase, phyAddr, GEN_ctl, PHY_SW_RST | AUTONEGENA); + + sysMsDelay(1500); + + return linkAlive; +} + +/****************************************************************************** +* +* rt_phyIsDuplexFull - Determines whether the phy ports associated with the +* specified device are FULL or HALF duplex. +* +* RETURNS: +* TRUE --> FULL +* FALSE --> HALF +*/ +BOOL +rt_phyIsFullDuplex(int ethUnit) +{ + UINT16 phyCtl; + UINT32 phyBase; + UINT32 phyAddr; + + phyBase = RT_PHYBASE(ethUnit); + phyAddr = RT_PHYADDR(ethUnit); + + phyCtl = phyRegRead(phyBase, phyAddr, GEN_ctl); + + if (phyCtl & DUPLEX) { + return TRUE; + } else { + return FALSE; + } +} + +/****************************************************************************** +* +* rt_phyIsSpeed100 - Determines the speed of phy ports associated with the +* specified device. +* +* RETURNS: +* TRUE --> 100Mbit +* FALSE --> 10Mbit +*/ +BOOL +rt_phyIsSpeed100(int phyUnit) +{ + UINT16 phyLpa; + UINT32 phyBase; + UINT32 phyAddr; + + phyBase = RT_PHYBASE(phyUnit); + phyAddr = RT_PHYADDR(phyUnit); + + phyLpa = phyRegRead(phyBase, phyAddr, AN_lpa); + + if (phyLpa & (LPA_TXFD | LPA_TX)) { + return TRUE; + } else { + return FALSE; + } +} + +/***************************************************************************** +* +* rt_phyCheckStatusChange -- checks for significant changes in PHY state. +* +* A "significant change" is: +* dropped link (e.g. ethernet cable unplugged) OR +* autonegotiation completed + link (e.g. ethernet cable plugged in) +* +* On AR5311, there is a 1-to-1 mapping of ethernet units to PHYs. +* When a PHY is plugged in, phyLinkGained is called. +* When a PHY is unplugged, phyLinkLost is called. +*/ +void +rt_phyCheckStatusChange(int ethUnit) +{ + UINT16 phyHwStatus; + rtPhyInfo_t *lastStatus = &rtPhyInfo[ethUnit]; + UINT32 phyBase; + UINT32 phyAddr; + + phyBase = RT_PHYBASE(ethUnit); + phyAddr = RT_PHYADDR(ethUnit); + + phyHwStatus = phyRegRead(phyBase, phyAddr, GEN_sts); + + if (lastStatus->phyAlive) { /* last known status was ALIVE */ + /* See if we've lost link */ + if (!(phyHwStatus & LINK)) { + RT_PRINT(RT_DEBUG_PHYCHANGE,("\nethmac%d link down\n", ethUnit)); + lastStatus->phyAlive = FALSE; + phyLinkLost(ethUnit); + } + } else { /* last known status was DEAD */ + /* Check for AN complete */ + if ((phyHwStatus & (AUTOCMPLT | LINK)) == (AUTOCMPLT | LINK)) { + RT_PRINT(RT_DEBUG_PHYCHANGE,("\nethmac%d link up\n", ethUnit)); + lastStatus->phyAlive = TRUE; + phyLinkGained(ethUnit); + } + } +} + +#if DEBUG + +/* Define the PHY registers of interest for a phyShow command */ +struct rtRegisterTable_s { + UINT32 regNum; + char *regIdString; +} rtRegisterTable[] = +{ + {GEN_ctl, "Basic Mode Control (GEN_ctl) "}, + {GEN_sts, "Basic Mode Status (GEN_sts) "}, + {GEN_id_hi, "PHY Identifier 1 (GET_id_hi) "}, + {GEN_id_lo, "PHY Identifier 2 (GET_id_lo) "}, + {AN_adv, "Auto-Neg Advertisement (AN_adv) "}, + {AN_lpa, "Auto-Neg Link Partner Ability "}, + {AN_exp, "Auto-Neg Expansion "}, +}; + +int rtNumRegs = sizeof(rtRegisterTable) / sizeof(rtRegisterTable[0]); + +/* + * Dump the state of a PHY. + */ +void +rt_phyShow(int phyUnit) +{ + int i; + UINT16 value; + UINT32 phyBase; + UINT32 phyAddr; + + phyBase = RT_PHYBASE(phyUnit); + phyAddr = RT_PHYADDR(phyUnit); + + printf("PHY state for ethphy%d\n", phyUnit); + + for (i=0; i