lkml.org 
[lkml]   [2011]   [Jan]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH] net: add Faraday FTMAC100 10/100 Ethernet driver
Date
From: Po-Yu Chuang <ratbert@faraday-tech.com>

FTMAC100 Ethernet Media Access Controller supports 10/100 Mbps and
MII. This driver has been working on some ARM/NDS32 SoC including
Faraday A320 and Andes AG101.

Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
---
drivers/net/Kconfig | 9 +
drivers/net/Makefile | 1 +
drivers/net/ftmac100.c | 1341 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/net/ftmac100.h | 180 +++++++
4 files changed, 1531 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/ftmac100.c
create mode 100644 drivers/net/ftmac100.h
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3fda24a..0720acc 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2014,6 +2014,15 @@ config BCM63XX_ENET
This driver supports the ethernet MACs in the Broadcom 63xx
MIPS chipset family (BCM63XX).

+config FTMAC100
+ tristate "Faraday FTMAC100 10/100 Ethernet support"
+ depends on ARM
+ select MII
+ help
+ This driver supports the FTMAC100 Ethernet controller from
+ Faraday. It is used on Faraday A320, Andes AG101, AG101P
+ and some other ARM/NDS32 SoC's.
+
source "drivers/net/fs_enet/Kconfig"

source "drivers/net/octeon/Kconfig"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b90738d..7c21711 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -147,6 +147,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o
obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o
obj-$(CONFIG_AX88796) += ax88796.o
obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
+obj-$(CONFIG_FTMAC100) += ftmac100.o

obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c
new file mode 100644
index 0000000..97d1f8d
--- /dev/null
+++ b/drivers/net/ftmac100.c
@@ -0,0 +1,1341 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+
+#include "ftmac100.h"
+
+#define USE_NAPI
+
+#define DRV_NAME "ftmac100"
+#define DRV_VERSION "0.1"
+
+#define RX_QUEUE_ENTRIES 128 /* must be power of 2 */
+#define TX_QUEUE_ENTRIES 16 /* must be power of 2 */
+
+#define MAX_PKT_SIZE 1518
+#define RX_BUF_SIZE 2044 /* must be smaller than 0x7ff */
+
+/******************************************************************************
+ * priveate data
+ *****************************************************************************/
+struct ftmac100_descs {
+ struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
+ struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES];
+};
+
+struct ftmac100 {
+ struct resource *res;
+ void *base;
+ int irq;
+
+ struct ftmac100_descs *descs;
+ dma_addr_t descs_dma_addr;
+
+ unsigned int rx_pointer;
+ unsigned int tx_clean_pointer;
+ unsigned int tx_pointer;
+ unsigned int tx_pending;
+
+ spinlock_t hw_lock;
+ spinlock_t rx_lock;
+ spinlock_t tx_lock;
+
+ struct net_device *netdev;
+ struct device *dev;
+#ifdef USE_NAPI
+ struct napi_struct napi;
+#endif
+
+ struct net_device_stats stats;
+
+ struct mii_if_info mii;
+};
+
+/******************************************************************************
+ * internal functions (hardware register access)
+ *****************************************************************************/
+#define INT_MASK_RX_DISABLED (FTMAC100_INT_XPKT_OK | \
+ FTMAC100_INT_XPKT_LOST | \
+ FTMAC100_INT_RPKT_LOST | \
+ FTMAC100_INT_AHB_ERR | \
+ FTMAC100_INT_PHYSTS_CHG)
+
+#define INT_MASK_ALL_ENABLED (INT_MASK_RX_DISABLED | \
+ FTMAC100_INT_RPKT_FINISH | \
+ FTMAC100_INT_NORXBUF)
+
+#define INT_MASK_ALL_DISABLED 0
+
+#ifdef USE_NAPI
+static inline void ftmac100_disable_rxint(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(INT_MASK_RX_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+#endif
+
+static inline void ftmac100_enable_all_int(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+static inline void ftmac100_disable_all_int(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+static inline void ftmac100_set_receive_ring_base(struct ftmac100 *priv,
+ dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
+}
+
+static inline void ftmac100_set_transmit_ring_base(struct ftmac100 *priv,
+ dma_addr_t addr)
+{
+ iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
+}
+
+static inline void ftmac100_txdma_start_polling(struct ftmac100 *priv)
+{
+ iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
+}
+
+static int ftmac100_reset(struct ftmac100 *priv)
+{
+ struct device *dev = &priv->netdev->dev;
+ unsigned long flags;
+ int i;
+
+ /* NOTE: reset clears all registers */
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+ for (i = 0; i < 5; i++) {
+ int maccr;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+ if (!(maccr & FTMAC100_MACCR_SW_RST)) {
+ /*
+ * FTMAC100_MACCR_SW_RST cleared does not indicate
+ * that hardware reset completed (what the f*ck).
+ * We still need to wait for a while.
+ */
+ usleep_range(500, 1000);
+ return 0;
+ }
+
+ usleep_range(1000, 10000);
+ }
+
+ dev_err(dev, "software reset failed\n");
+ return -EIO;
+}
+
+static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac)
+{
+ unsigned int maddr = mac[0] << 8 | mac[1];
+ unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
+
+ iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR);
+ iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR);
+}
+
+static int ftmac100_start_hw(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ unsigned long flags;
+ int maccr;
+
+ if (ftmac100_reset(priv))
+ return -EIO;
+
+ /* setup ring buffer base registers */
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ ftmac100_set_receive_ring_base(priv,
+ priv->descs_dma_addr + offsetof(struct ftmac100_descs, rxdes));
+ ftmac100_set_transmit_ring_base(priv,
+ priv->descs_dma_addr + offsetof(struct ftmac100_descs, txdes));
+
+ iowrite32(FTMAC100_APTC_RXPOLL_CNT(1),
+ priv->base + FTMAC100_OFFSET_APTC);
+
+ ftmac100_set_mac(priv, netdev->dev_addr);
+
+ maccr = FTMAC100_MACCR_XMT_EN |
+ FTMAC100_MACCR_RCV_EN |
+ FTMAC100_MACCR_XDMA_EN |
+ FTMAC100_MACCR_RDMA_EN |
+ FTMAC100_MACCR_CRC_APD |
+ FTMAC100_MACCR_FULLDUP |
+ FTMAC100_MACCR_RX_RUNT |
+ FTMAC100_MACCR_RX_BROADPKT;
+
+ iowrite32(maccr, priv->base + FTMAC100_OFFSET_MACCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+ return 0;
+}
+
+static void ftmac100_stop_hw(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+}
+
+/******************************************************************************
+ * internal functions (receive descriptor)
+ *****************************************************************************/
+static inline int ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_FRS;
+}
+
+static inline int ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_LRS;
+}
+
+static inline int ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static inline void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
+{
+ /* clear status bits */
+ rxdes->rxdes0 = FTMAC100_RXDES0_RXDMA_OWN;
+}
+
+static inline int ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ERR;
+}
+
+static inline int ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_CRC_ERR;
+}
+
+static inline int ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_FTL;
+}
+
+static inline int ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RUNT;
+}
+
+static inline int ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RX_ODD_NB;
+}
+
+static inline unsigned int ftmac100_rxdes_frame_length(
+ struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_RFL;
+}
+
+static inline int ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes0 & FTMAC100_RXDES0_MULTICAST;
+}
+
+static inline void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
+ unsigned int size)
+{
+ rxdes->rxdes1 = (rxdes->rxdes1 & FTMAC100_RXDES1_EDORR) |
+ FTMAC100_RXDES1_RXBUF_SIZE(size);
+}
+
+static inline void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
+{
+ rxdes->rxdes1 |= FTMAC100_RXDES1_EDORR;
+}
+
+static inline void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
+ dma_addr_t addr)
+{
+ rxdes->rxdes2 = addr;
+}
+
+static inline dma_addr_t ftmac100_rxdes_get_dma_addr(
+ struct ftmac100_rxdes *rxdes)
+{
+ return rxdes->rxdes2;
+}
+
+/* rxdes3 is not used by hardware, we use it to keep track of buffer */
+static inline void ftmac100_rxdes_set_va(struct ftmac100_rxdes *rxdes,
+ void *addr)
+{
+ rxdes->rxdes3 = (unsigned int)addr;
+}
+
+static inline void *ftmac100_rxdes_get_va(struct ftmac100_rxdes *rxdes)
+{
+ return (void *)rxdes->rxdes3;
+}
+
+/******************************************************************************
+ * internal functions (receive)
+ *****************************************************************************/
+static inline int ftmac100_next_rx_pointer(int pointer)
+{
+ return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
+}
+
+static inline void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
+{
+ priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
+}
+
+static inline struct ftmac100_rxdes *ftmac100_current_rxdes(
+ struct ftmac100 *priv)
+{
+ return &priv->descs->rxdes[priv->rx_pointer];
+}
+
+static struct ftmac100_rxdes *ftmac100_rx_locate_first_segment(
+ struct ftmac100 *priv)
+{
+ struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+
+ while (!ftmac100_rxdes_owned_by_dma(rxdes)) {
+ if (ftmac100_rxdes_first_segment(rxdes))
+ return rxdes;
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+ }
+
+ return NULL;
+}
+
+static int ftmac100_rx_packet_error(struct ftmac100 *priv,
+ struct ftmac100_rxdes *rxdes)
+{
+ struct device *dev = &priv->netdev->dev;
+ int error = 0;
+
+ if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
+ if (printk_ratelimit())
+ dev_info(dev, "rx err\n");
+
+ priv->stats.rx_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
+ if (printk_ratelimit())
+ dev_info(dev, "rx crc err\n");
+
+ priv->stats.rx_crc_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) {
+ if (printk_ratelimit())
+ dev_info(dev, "rx frame too long\n");
+
+ priv->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_runt(rxdes))) {
+ if (printk_ratelimit())
+ dev_info(dev, "rx runt\n");
+
+ priv->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
+ if (printk_ratelimit())
+ dev_info(dev, "rx odd nibble\n");
+
+ priv->stats.rx_length_errors++;
+ error = 1;
+ }
+
+ return error;
+}
+
+static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
+{
+ struct device *dev = &priv->netdev->dev;
+ struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
+ int done = 0;
+
+ if (printk_ratelimit())
+ dev_dbg(dev, "drop packet %p\n", rxdes);
+
+ do {
+ if (ftmac100_rxdes_last_segment(rxdes))
+ done = 1;
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+ } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
+
+ priv->stats.rx_dropped++;
+}
+
+static int ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
+{
+ struct net_device *netdev = priv->netdev;
+ struct device *dev = &netdev->dev;
+ unsigned long flags;
+ struct ftmac100_rxdes *rxdes;
+ struct sk_buff *skb;
+ int length;
+ int copied = 0;
+ int done = 0;
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ rxdes = ftmac100_rx_locate_first_segment(priv);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+ if (!rxdes)
+ return 0;
+
+ if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ ftmac100_rx_drop_packet(priv);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+ return 1;
+ }
+
+ /* start processing */
+
+ length = ftmac100_rxdes_frame_length(rxdes);
+
+ skb = dev_alloc_skb(length + NET_IP_ALIGN);
+ if (unlikely(!skb)) {
+ if (printk_ratelimit())
+ dev_err(dev, "rx skb alloc failed\n");
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ ftmac100_rx_drop_packet(priv);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+ return 1;
+ }
+
+ if (unlikely(ftmac100_rxdes_multicast(rxdes)))
+ priv->stats.multicast++;
+
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ do {
+ dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+ void *buf = ftmac100_rxdes_get_va(rxdes);
+ int size;
+
+ size = min(length - copied, RX_BUF_SIZE);
+
+ dma_sync_single_for_cpu(priv->dev, d, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+ memcpy(skb_put(skb, size), buf, size);
+
+ copied += size;
+
+ if (ftmac100_rxdes_last_segment(rxdes))
+ done = 1;
+
+ dma_sync_single_for_device(priv->dev, d, RX_BUF_SIZE,
+ DMA_FROM_DEVICE);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+
+ ftmac100_rx_pointer_advance(priv);
+ rxdes = ftmac100_current_rxdes(priv);
+
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+ } while (!done && copied < length);
+
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ /* push packet to protocol stack */
+
+#ifdef USE_NAPI
+ netif_receive_skb(skb);
+#else
+ netif_rx(skb);
+#endif
+
+ netdev->last_rx = jiffies;
+
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skb->len;
+
+ (*processed)++;
+
+ return 1;
+}
+
+/******************************************************************************
+ * internal functions (transmit descriptor)
+ *****************************************************************************/
+static inline void ftmac100_txdes_reset(struct ftmac100_txdes *txdes)
+{
+ /* clear all except end of ring bit */
+ txdes->txdes0 = 0;
+ txdes->txdes1 &= FTMAC100_TXDES1_EDOTR;
+ txdes->txdes2 = 0;
+ txdes->txdes3 = 0;
+}
+
+static inline int ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static inline void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes0 |= FTMAC100_TXDES0_TXDMA_OWN;
+}
+
+static inline int ftmac100_txdes_excessive_collision(
+ struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_EXSCOL;
+}
+
+static inline int ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes0 & FTMAC100_TXDES0_TXPKT_LATECOL;
+}
+
+static inline void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_EDOTR;
+}
+
+static inline void ftmac100_txdes_set_first_segment(
+ struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_FTS;
+}
+
+static inline void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_LTS;
+}
+
+static inline void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_TXIC;
+}
+
+static inline void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
+ unsigned int len)
+{
+ txdes->txdes1 |= FTMAC100_TXDES1_TXBUF_SIZE(len);
+}
+
+static inline void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
+ dma_addr_t addr)
+{
+ txdes->txdes2 = addr;
+}
+
+static inline dma_addr_t ftmac100_txdes_get_dma_addr(
+ struct ftmac100_txdes *txdes)
+{
+ return txdes->txdes2;
+}
+
+/* txdes3 is not used by hardware, we use it to keep track of socket buffer */
+static inline void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes,
+ struct sk_buff *skb)
+{
+ txdes->txdes3 = (unsigned int)skb;
+}
+
+static inline struct sk_buff *ftmac100_txdes_get_skb(
+ struct ftmac100_txdes *txdes)
+{
+ return (struct sk_buff *)txdes->txdes3;
+}
+
+/******************************************************************************
+ * internal functions (transmit)
+ *****************************************************************************/
+static inline int ftmac100_next_tx_pointer(int pointer)
+{
+ return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
+}
+
+static inline void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
+{
+ priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
+}
+
+static inline void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
+{
+ priv->tx_clean_pointer =
+ ftmac100_next_tx_pointer(priv->tx_clean_pointer);
+}
+
+static inline struct ftmac100_txdes *ftmac100_current_txdes(
+ struct ftmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_pointer];
+}
+
+static inline struct ftmac100_txdes *ftmac100_current_clean_txdes(
+ struct ftmac100 *priv)
+{
+ return &priv->descs->txdes[priv->tx_clean_pointer];
+}
+
+static int ftmac100_tx_complete_packet(struct ftmac100 *priv)
+{
+ struct net_device *netdev = priv->netdev;
+ struct ftmac100_txdes *txdes;
+ struct sk_buff *skb;
+ dma_addr_t map;
+
+ if (priv->tx_pending == 0)
+ return 0;
+
+ txdes = ftmac100_current_clean_txdes(priv);
+
+ if (ftmac100_txdes_owned_by_dma(txdes))
+ return 0;
+
+ skb = ftmac100_txdes_get_skb(txdes);
+ map = ftmac100_txdes_get_dma_addr(txdes);
+
+ if (unlikely(ftmac100_txdes_excessive_collision(txdes) ||
+ ftmac100_txdes_late_collision(txdes))) {
+ /*
+ * packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+ priv->stats.tx_aborted_errors++;
+ } else {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ }
+
+ dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
+
+ dev_kfree_skb_irq(skb);
+
+ ftmac100_txdes_reset(txdes);
+
+ ftmac100_tx_clean_pointer_advance(priv);
+
+ priv->tx_pending--;
+ netif_wake_queue(netdev);
+
+ return 1;
+}
+
+static void ftmac100_tx_complete(struct ftmac100 *priv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ while (ftmac100_tx_complete_packet(priv))
+ ;
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+}
+
+static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb,
+ dma_addr_t map)
+{
+ struct net_device *netdev = priv->netdev;
+ struct device *dev = &netdev->dev;
+ struct ftmac100_txdes *txdes;
+ unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ unsigned long flags;
+
+ txdes = ftmac100_current_txdes(priv);
+ ftmac100_tx_pointer_advance(priv);
+
+ /* setup TX descriptor */
+
+ spin_lock_irqsave(&priv->tx_lock, flags);
+ ftmac100_txdes_set_skb(txdes, skb);
+ ftmac100_txdes_set_dma_addr(txdes, map);
+
+ ftmac100_txdes_set_first_segment(txdes);
+ ftmac100_txdes_set_last_segment(txdes);
+ ftmac100_txdes_set_txint(txdes);
+ ftmac100_txdes_set_buffer_size(txdes, len);
+
+ priv->tx_pending++;
+ if (priv->tx_pending == TX_QUEUE_ENTRIES) {
+ if (printk_ratelimit())
+ dev_info(dev, "tx queue full\n");
+
+ netif_stop_queue(netdev);
+ }
+
+ /* start transmit */
+
+ wmb();
+ ftmac100_txdes_set_dma_own(txdes);
+ spin_unlock_irqrestore(&priv->tx_lock, flags);
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ ftmac100_txdma_start_polling(priv);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+ netdev->trans_start = jiffies;
+
+ return NETDEV_TX_OK;
+}
+
+/******************************************************************************
+ * internal functions (buffer)
+ *****************************************************************************/
+static void ftmac100_free_buffers(struct ftmac100 *priv)
+{
+ int i;
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+ struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+ dma_addr_t d = ftmac100_rxdes_get_dma_addr(rxdes);
+ void *page = ftmac100_rxdes_get_va(rxdes);
+
+ if (d)
+ dma_unmap_single(priv->dev, d, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+
+ if (page != NULL)
+ free_page((unsigned long)page);
+ }
+
+ for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
+ struct ftmac100_txdes *txdes = &priv->descs->txdes[i];
+ struct sk_buff *skb = ftmac100_txdes_get_skb(txdes);
+
+ if (skb) {
+ dma_addr_t map;
+
+ map = ftmac100_txdes_get_dma_addr(txdes);
+ dma_unmap_single(priv->dev, map, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ dev_kfree_skb(skb);
+ }
+ }
+
+ dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs),
+ priv->descs, priv->descs_dma_addr);
+}
+
+static int ftmac100_alloc_buffers(struct ftmac100 *priv)
+{
+ int i;
+
+ priv->descs = dma_alloc_coherent(priv->dev,
+ sizeof(struct ftmac100_descs), &priv->descs_dma_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (priv->descs == NULL)
+ return -ENOMEM;
+
+ memset(priv->descs, 0, sizeof(struct ftmac100_descs));
+
+ /* initialize RX ring */
+
+ ftmac100_rxdes_set_end_of_ring(
+ &priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+
+ for (i = 0; i < RX_QUEUE_ENTRIES; i += 2) {
+ struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i];
+ void *page;
+ dma_addr_t d;
+
+ page = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ if (page == NULL)
+ goto err;
+
+ d = dma_map_single(priv->dev, page, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, d))) {
+ free_page((unsigned long)page);
+ goto err;
+ }
+
+ /*
+ * The hardware enforces a sub-2K maximum packet size, so we
+ * put two buffers on every hardware page.
+ */
+ ftmac100_rxdes_set_va(rxdes, page);
+ ftmac100_rxdes_set_va(rxdes + 1, page + PAGE_SIZE / 2);
+
+ ftmac100_rxdes_set_dma_addr(rxdes, d);
+ ftmac100_rxdes_set_dma_addr(rxdes + 1, d + PAGE_SIZE / 2);
+
+ ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE);
+ ftmac100_rxdes_set_buffer_size(rxdes + 1, RX_BUF_SIZE);
+
+ ftmac100_rxdes_set_dma_own(rxdes);
+ ftmac100_rxdes_set_dma_own(rxdes + 1);
+ }
+
+ /* initialize TX ring */
+
+ ftmac100_txdes_set_end_of_ring(
+ &priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
+ return 0;
+
+err:
+ ftmac100_free_buffers(priv);
+ return -ENOMEM;
+}
+
+/******************************************************************************
+ * struct mii_if_info functions
+ *****************************************************************************/
+static int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ struct device *dev = &netdev->dev;
+ unsigned long flags;
+ int phycr;
+ int i;
+
+ phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+ FTMAC100_PHYCR_REGAD(reg) |
+ FTMAC100_PHYCR_MIIRD;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTMAC100_PHYCR_MIIRD) == 0)
+ return phycr & FTMAC100_PHYCR_MIIRDATA;
+
+ usleep_range(100, 1000);
+ }
+
+ dev_err(dev, "mdio read timed out\n");
+ return 0xffff;
+}
+
+static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
+ int data)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ struct device *dev = &netdev->dev;
+ unsigned long flags;
+ int phycr;
+ int i;
+
+ phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
+ FTMAC100_PHYCR_REGAD(reg) |
+ FTMAC100_PHYCR_MIIWR;
+
+ data = FTMAC100_PHYWDATA_MIIWDATA(data);
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
+ iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+ for (i = 0; i < 10; i++) {
+ phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
+
+ if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
+ return;
+
+ usleep_range(100, 1000);
+ }
+
+ dev_err(dev, "mdio write timed out\n");
+}
+
+/******************************************************************************
+ * struct ethtool_ops functions
+ *****************************************************************************/
+static void ftmac100_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->bus_info, dev_name(&netdev->dev));
+}
+
+static int ftmac100_get_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_ethtool_gset(&priv->mii, cmd);
+}
+
+static int ftmac100_set_settings(struct net_device *netdev,
+ struct ethtool_cmd *cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_ethtool_sset(&priv->mii, cmd);
+}
+
+static int ftmac100_nway_reset(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_nway_restart(&priv->mii);
+}
+
+static u32 ftmac100_get_link(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return mii_link_ok(&priv->mii);
+}
+
+static struct ethtool_ops ftmac100_ethtool_ops = {
+ .set_settings = ftmac100_set_settings,
+ .get_settings = ftmac100_get_settings,
+ .get_drvinfo = ftmac100_get_drvinfo,
+ .nway_reset = ftmac100_nway_reset,
+ .get_link = ftmac100_get_link,
+};
+
+/******************************************************************************
+ * interrupt handler
+ *****************************************************************************/
+static irqreturn_t ftmac100_interrupt(int irq, void *dev_id)
+{
+ struct net_device *netdev = dev_id;
+ struct device *dev = &netdev->dev;
+ struct ftmac100 *priv = netdev_priv(netdev);
+ unsigned long flags;
+ unsigned int status;
+ unsigned int imr;
+
+ spin_lock_irqsave(&priv->hw_lock, flags);
+ status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
+ imr = ioread32(priv->base + FTMAC100_OFFSET_IMR);
+ spin_unlock_irqrestore(&priv->hw_lock, flags);
+
+ status &= imr;
+ if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) {
+ /*
+ * FTMAC100_INT_RPKT_FINISH:
+ * RX DMA has received packets into RX buffer successfully
+ *
+ * FTMAC100_INT_NORXBUF:
+ * RX buffer unavailable
+ */
+#ifdef USE_NAPI
+ /* Disable interrupts for polling */
+ ftmac100_disable_rxint(priv);
+
+ napi_schedule(&priv->napi);
+#else
+ int rx = 0;
+
+ while (ftmac100_rx_packet(priv, &rx))
+ ;
+#endif
+ }
+
+ if (status & FTMAC100_INT_NORXBUF) {
+ /* RX buffer unavailable */
+ if (printk_ratelimit())
+ dev_info(dev, "INT_NORXBUF\n");
+
+ priv->stats.rx_over_errors++;
+ }
+
+ if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) {
+ /*
+ * FTMAC100_INT_XPKT_OK:
+ * packet transmitted to ethernet successfully
+ *
+ * FTMAC100_INT_XPKT_LOST:
+ * packet transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+ ftmac100_tx_complete(priv);
+ }
+
+ if (status & FTMAC100_INT_RPKT_LOST) {
+ /* received packet lost due to RX FIFO full */
+ if (printk_ratelimit())
+ dev_info(dev, "INT_RPKT_LOST\n");
+
+ priv->stats.rx_fifo_errors++;
+ }
+
+ if (status & FTMAC100_INT_AHB_ERR) {
+ /* AHB error */
+ if (printk_ratelimit())
+ dev_info(dev, "INT_AHB_ERR\n");
+
+ /* do nothing */
+ }
+
+ if (status & FTMAC100_INT_PHYSTS_CHG) {
+ /* PHY link status change */
+ if (printk_ratelimit())
+ dev_info(dev, "INT_PHYSTS_CHG\n");
+
+ mii_check_link(&priv->mii);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************
+ * struct napi_struct functions
+ *****************************************************************************/
+#ifdef USE_NAPI
+static int ftmac100_poll(struct napi_struct *napi, int budget)
+{
+ struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
+ int retry;
+ int rx = 0;
+
+ do {
+ retry = ftmac100_rx_packet(priv, &rx);
+ } while (retry && rx < budget);
+
+ if (!retry || rx < budget) {
+ /* stop polling */
+ napi_complete(napi);
+ ftmac100_enable_all_int(priv);
+ }
+
+ return rx;
+}
+#endif
+
+/******************************************************************************
+ * struct net_device_ops functions
+ *****************************************************************************/
+static int ftmac100_open(struct net_device *netdev)
+{
+ struct device *dev = &netdev->dev;
+ struct ftmac100 *priv = netdev_priv(netdev);
+ int err;
+
+ err = ftmac100_alloc_buffers(priv);
+ if (err) {
+ dev_err(dev, "failed to allocate buffers\n");
+ goto err_alloc;
+ }
+
+ err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name,
+ netdev);
+ if (err) {
+ dev_err(dev, "failed to request irq %d\n", priv->irq);
+ goto err_irq;
+ }
+
+ priv->rx_pointer = 0;
+ priv->tx_clean_pointer = 0;
+ priv->tx_pointer = 0;
+ spin_lock_init(&priv->hw_lock);
+ spin_lock_init(&priv->rx_lock);
+ spin_lock_init(&priv->tx_lock);
+ priv->tx_pending = 0;
+
+ err = ftmac100_start_hw(priv);
+ if (err)
+ goto err_hw;
+
+#ifdef USE_NAPI
+ napi_enable(&priv->napi);
+#endif
+ netif_start_queue(netdev);
+
+ ftmac100_enable_all_int(priv);
+
+ return 0;
+
+err_hw:
+ free_irq(priv->irq, netdev);
+err_irq:
+ ftmac100_free_buffers(priv);
+err_alloc:
+ return err;
+}
+
+static int ftmac100_stop(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+
+ ftmac100_disable_all_int(priv);
+ netif_stop_queue(netdev);
+#ifdef USE_NAPI
+ napi_disable(&priv->napi);
+#endif
+ ftmac100_stop_hw(priv);
+ free_irq(priv->irq, netdev);
+ ftmac100_free_buffers(priv);
+
+ return 0;
+}
+
+static int ftmac100_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct device *dev = &netdev->dev;
+ struct ftmac100 *priv = netdev_priv(netdev);
+ dma_addr_t map;
+
+ if (unlikely(skb->len > MAX_PKT_SIZE)) {
+ if (printk_ratelimit())
+ dev_dbg(dev, "tx packet too big\n");
+
+ priv->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ map = dma_map_single(priv->dev, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(priv->dev, map))) {
+ /* drop packet */
+ if (printk_ratelimit())
+ dev_err(dev, "map socket buffer failed\n");
+
+ priv->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ return ftmac100_xmit(priv, skb, map);
+}
+
+static struct net_device_stats *ftmac100_get_stats(struct net_device *netdev)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ return &priv->stats;
+}
+
+/* optional */
+static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr,
+ int cmd)
+{
+ struct ftmac100 *priv = netdev_priv(netdev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+
+ return generic_mii_ioctl(&priv->mii, data, cmd, NULL);
+}
+
+static struct net_device_ops ftmac100_netdev_ops = {
+ .ndo_open = ftmac100_open,
+ .ndo_stop = ftmac100_stop,
+ .ndo_start_xmit = ftmac100_hard_start_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_get_stats = ftmac100_get_stats,
+ .ndo_do_ioctl = ftmac100_do_ioctl,
+};
+
+/******************************************************************************
+ * struct platform_driver functions
+ *****************************************************************************/
+static int ftmac100_remove(struct platform_device *pdev)
+{
+ struct net_device *netdev;
+ struct ftmac100 *priv;
+
+ netdev = platform_get_drvdata(pdev);
+ if (netdev == NULL)
+ return 0;
+
+ platform_set_drvdata(pdev, NULL);
+
+ priv = netdev_priv(netdev);
+
+ unregister_netdev(netdev);
+
+ if (priv->base != NULL)
+ iounmap(priv->base);
+
+ if (priv->res != NULL)
+ release_resource(priv->res);
+
+ free_netdev(netdev);
+
+ return 0;
+}
+
+static int ftmac100_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int irq;
+ struct net_device *netdev;
+ struct ftmac100 *priv;
+ int err;
+
+ if (!pdev)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* setup net_device */
+
+ netdev = alloc_etherdev(sizeof(struct ftmac100));
+ if (netdev == NULL) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops);
+ netdev->netdev_ops = &ftmac100_netdev_ops;
+
+ platform_set_drvdata(pdev, netdev);
+
+ /* setup private data */
+
+ priv = netdev_priv(netdev);
+ priv->netdev = netdev;
+ priv->dev = &pdev->dev;
+
+#ifdef USE_NAPI
+ /* initialize NAPI */
+ netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64);
+#endif
+
+ /* map io memory */
+
+ priv->res = request_mem_region(res->start, res->end - res->start,
+ dev_name(&pdev->dev));
+ if (priv->res == NULL) {
+ dev_err(&pdev->dev, "Could not reserve memory region\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ priv->base = ioremap(res->start, res->end - res->start);
+ if (priv->base == NULL) {
+ dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
+ err = -EIO;
+ goto err_out;
+ }
+
+ priv->irq = irq;
+
+ /* initialize struct mii_if_info */
+
+ priv->mii.phy_id = 0;
+ priv->mii.phy_id_mask = 0x1f;
+ priv->mii.reg_num_mask = 0x1f;
+ priv->mii.dev = netdev;
+ priv->mii.mdio_read = ftmac100_mdio_read;
+ priv->mii.mdio_write = ftmac100_mdio_write;
+
+ /* register network device */
+
+ err = register_netdev(netdev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register netdev\n");
+ goto err_out;
+ }
+
+ dev_info(&netdev->dev, "irq %d, mapped at %p\n", priv->irq, priv->base);
+
+ if (is_zero_ether_addr(netdev->dev_addr)) {
+ random_ether_addr(netdev->dev_addr);
+ dev_info(&netdev->dev, "generated random MAC address "
+ "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.\n",
+ netdev->dev_addr[0], netdev->dev_addr[1],
+ netdev->dev_addr[2], netdev->dev_addr[3],
+ netdev->dev_addr[4], netdev->dev_addr[5]);
+ }
+
+ return 0;
+
+err_out:
+ ftmac100_remove(pdev);
+ return err;
+}
+
+static struct platform_driver ftmac100_driver = {
+ .probe = ftmac100_probe,
+ .remove = ftmac100_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/******************************************************************************
+ * initialization / finalization
+ *****************************************************************************/
+static int __init ftmac100_init(void)
+{
+ printk(KERN_INFO "Loading " DRV_NAME ": version " DRV_VERSION " ...\n");
+ return platform_driver_register(&ftmac100_driver);
+}
+
+static void __exit ftmac100_exit(void)
+{
+ platform_driver_unregister(&ftmac100_driver);
+}
+
+module_init(ftmac100_init);
+module_exit(ftmac100_exit);
+
+MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
+MODULE_DESCRIPTION("FTMAC100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h
new file mode 100644
index 0000000..46a0c47
--- /dev/null
+++ b/drivers/net/ftmac100.h
@@ -0,0 +1,180 @@
+/*
+ * Faraday FTMAC100 10/100 Ethernet
+ *
+ * (C) Copyright 2009-2011 Faraday Technology
+ * Po-Yu Chuang <ratbert@faraday-tech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __FTMAC100_H
+#define __FTMAC100_H
+
+#define FTMAC100_OFFSET_ISR 0x00
+#define FTMAC100_OFFSET_IMR 0x04
+#define FTMAC100_OFFSET_MAC_MADR 0x08
+#define FTMAC100_OFFSET_MAC_LADR 0x0c
+#define FTMAC100_OFFSET_MAHT0 0x10
+#define FTMAC100_OFFSET_MAHT1 0x14
+#define FTMAC100_OFFSET_TXPD 0x18
+#define FTMAC100_OFFSET_RXPD 0x1c
+#define FTMAC100_OFFSET_TXR_BADR 0x20
+#define FTMAC100_OFFSET_RXR_BADR 0x24
+#define FTMAC100_OFFSET_ITC 0x28
+#define FTMAC100_OFFSET_APTC 0x2c
+#define FTMAC100_OFFSET_DBLAC 0x30
+#define FTMAC100_OFFSET_MACCR 0x88
+#define FTMAC100_OFFSET_MACSR 0x8c
+#define FTMAC100_OFFSET_PHYCR 0x90
+#define FTMAC100_OFFSET_PHYWDATA 0x94
+#define FTMAC100_OFFSET_FCR 0x98
+#define FTMAC100_OFFSET_BPR 0x9c
+#define FTMAC100_OFFSET_TS 0xc4
+#define FTMAC100_OFFSET_DMAFIFOS 0xc8
+#define FTMAC100_OFFSET_TM 0xcc
+#define FTMAC100_OFFSET_TX_MCOL_SCOL 0xd4
+#define FTMAC100_OFFSET_RPF_AEP 0xd8
+#define FTMAC100_OFFSET_XM_PG 0xdc
+#define FTMAC100_OFFSET_RUNT_TLCC 0xe0
+#define FTMAC100_OFFSET_CRCER_FTL 0xe4
+#define FTMAC100_OFFSET_RLC_RCC 0xe8
+#define FTMAC100_OFFSET_BROC 0xec
+#define FTMAC100_OFFSET_MULCA 0xf0
+#define FTMAC100_OFFSET_RP 0xf4
+#define FTMAC100_OFFSET_XP 0xf8
+
+/*
+ * Interrupt status register & interrupt mask register
+ */
+#define FTMAC100_INT_RPKT_FINISH (1 << 0)
+#define FTMAC100_INT_NORXBUF (1 << 1)
+#define FTMAC100_INT_XPKT_FINISH (1 << 2)
+#define FTMAC100_INT_NOTXBUF (1 << 3)
+#define FTMAC100_INT_XPKT_OK (1 << 4)
+#define FTMAC100_INT_XPKT_LOST (1 << 5)
+#define FTMAC100_INT_RPKT_SAV (1 << 6)
+#define FTMAC100_INT_RPKT_LOST (1 << 7)
+#define FTMAC100_INT_AHB_ERR (1 << 8)
+#define FTMAC100_INT_PHYSTS_CHG (1 << 9)
+
+/*
+ * Interrupt timer control register
+ */
+#define FTMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0)
+#define FTMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4)
+#define FTMAC100_ITC_RXINT_TIME_SEL (1 << 7)
+#define FTMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8)
+#define FTMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12)
+#define FTMAC100_ITC_TXINT_TIME_SEL (1 << 15)
+
+/*
+ * Automatic polling timer control register
+ */
+#define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0)
+#define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4)
+#define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8)
+#define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12)
+
+/*
+ * DMA burst length and arbitration control register
+ */
+#define FTMAC100_DBLAC_INCR4_EN (1 << 0)
+#define FTMAC100_DBLAC_INCR8_EN (1 << 1)
+#define FTMAC100_DBLAC_INCR16_EN (1 << 2)
+#define FTMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 3)
+#define FTMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 6)
+#define FTMAC100_DBLAC_RX_THR_EN (1 << 9)
+
+/*
+ * MAC control register
+ */
+#define FTMAC100_MACCR_XDMA_EN (1 << 0)
+#define FTMAC100_MACCR_RDMA_EN (1 << 1)
+#define FTMAC100_MACCR_SW_RST (1 << 2)
+#define FTMAC100_MACCR_LOOP_EN (1 << 3)
+#define FTMAC100_MACCR_CRC_DIS (1 << 4)
+#define FTMAC100_MACCR_XMT_EN (1 << 5)
+#define FTMAC100_MACCR_ENRX_IN_HALFTX (1 << 6)
+#define FTMAC100_MACCR_RCV_EN (1 << 8)
+#define FTMAC100_MACCR_HT_MULTI_EN (1 << 9)
+#define FTMAC100_MACCR_RX_RUNT (1 << 10)
+#define FTMAC100_MACCR_RX_FTL (1 << 11)
+#define FTMAC100_MACCR_RCV_ALL (1 << 12)
+#define FTMAC100_MACCR_CRC_APD (1 << 14)
+#define FTMAC100_MACCR_FULLDUP (1 << 15)
+#define FTMAC100_MACCR_RX_MULTIPKT (1 << 16)
+#define FTMAC100_MACCR_RX_BROADPKT (1 << 17)
+
+/*
+ * PHY control register
+ */
+#define FTMAC100_PHYCR_MIIRDATA 0xffff
+#define FTMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16)
+#define FTMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21)
+#define FTMAC100_PHYCR_MIIRD (1 << 26)
+#define FTMAC100_PHYCR_MIIWR (1 << 27)
+
+/*
+ * PHY write data register
+ */
+#define FTMAC100_PHYWDATA_MIIWDATA(x) ((x) & 0xffff)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftmac100_txdes {
+ unsigned int txdes0;
+ unsigned int txdes1;
+ unsigned int txdes2; /* TXBUF_BADR */
+ unsigned int txdes3; /* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0)
+#define FTMAC100_TXDES0_TXPKT_EXSCOL (1 << 1)
+#define FTMAC100_TXDES0_TXDMA_OWN (1 << 31)
+
+#define FTMAC100_TXDES1_TXBUF_SIZE(x) ((x) & 0x7ff)
+#define FTMAC100_TXDES1_LTS (1 << 27)
+#define FTMAC100_TXDES1_FTS (1 << 28)
+#define FTMAC100_TXDES1_TX2FIC (1 << 29)
+#define FTMAC100_TXDES1_TXIC (1 << 30)
+#define FTMAC100_TXDES1_EDOTR (1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftmac100_rxdes {
+ unsigned int rxdes0;
+ unsigned int rxdes1;
+ unsigned int rxdes2; /* RXBUF_BADR */
+ unsigned int rxdes3; /* not used by HW */
+} __attribute__ ((aligned(16)));
+
+#define FTMAC100_RXDES0_RFL 0x7ff
+#define FTMAC100_RXDES0_MULTICAST (1 << 16)
+#define FTMAC100_RXDES0_BROADCAST (1 << 17)
+#define FTMAC100_RXDES0_RX_ERR (1 << 18)
+#define FTMAC100_RXDES0_CRC_ERR (1 << 19)
+#define FTMAC100_RXDES0_FTL (1 << 20)
+#define FTMAC100_RXDES0_RUNT (1 << 21)
+#define FTMAC100_RXDES0_RX_ODD_NB (1 << 22)
+#define FTMAC100_RXDES0_LRS (1 << 28)
+#define FTMAC100_RXDES0_FRS (1 << 29)
+#define FTMAC100_RXDES0_RXDMA_OWN (1 << 31)
+
+#define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff)
+#define FTMAC100_RXDES1_EDORR (1 << 31)
+
+#endif /* __FTMAC100_H */
--
1.6.3.3


\
 
 \ /
  Last update: 2011-01-13 12:53    [from the cache]
©2003-2011 Jasper Spaans