lkml.org 
[lkml]   [2011]   [Jan]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v3] 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's including
    Faraday A320 and Andes AG101.

    Signed-off-by: Po-Yu Chuang <ratbert@faraday-tech.com>
    ---
    v2:
    always use NAPI
    do not use our own net_device_stats structure
    don't set trans_start and last_rx
    stats.rx_packets and stats.rx_bytes include dropped packets
    add missed netif_napi_del()
    initialize spinlocks in probe function
    remove rx_lock and hw_lock
    use netdev_[err/info/dbg] instead of dev_* ones
    use netdev_alloc_skb_ip_align()
    remove ftmac100_get_stats()
    use is_valid_ether_addr() instead of is_zero_ether_addr()
    add const to ftmac100_ethtool_ops and ftmac100_netdev_ops
    use net_ratelimit() instead of printk_ratelimit()
    no explicit inline
    use %pM to print MAC address
    add comment before wmb
    use napi poll() to handle all interrupts

    v3:
    undo "stats.rx_packets and stats.rx_bytes include dropped packets"
    ftmac100_mdio_read() returns 0 if error
    fix comment typos
    use pr_fmt and pr_info
    define INT_MASK_ALL_ENABLED
    define MACCR_ENABLE_ALL
    do not count length error many times
    use bool/true/false
    use cpu_to_le32/le32_to_cpu to access descriptors
    indent style fix

    drivers/net/Kconfig | 9 +
    drivers/net/Makefile | 1 +
    drivers/net/ftmac100.c | 1243 ++++++++++++++++++++++++++++++++++++++++++++++++
    drivers/net/ftmac100.h | 180 +++++++
    4 files changed, 1433 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 4f1755b..26da0ee 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..3d39fe1
    --- /dev/null
    +++ b/drivers/net/ftmac100.c
    @@ -0,0 +1,1243 @@
    +/*
    + * 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.
    + */
    +
    +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
    +
    +#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 DRV_NAME "ftmac100"
    +#define DRV_VERSION "0.2"
    +
    +#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 */
    +
    +/******************************************************************************
    + * private 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 tx_lock;
    +
    + struct net_device *netdev;
    + struct device *dev;
    + struct napi_struct napi;
    +
    + struct mii_if_info mii;
    +};
    +
    +/******************************************************************************
    + * internal functions (hardware register access)
    + *****************************************************************************/
    +#define INT_MASK_ALL_ENABLED (FTMAC100_INT_RPKT_FINISH | \
    + FTMAC100_INT_NORXBUF | \
    + FTMAC100_INT_XPKT_OK | \
    + FTMAC100_INT_XPKT_LOST | \
    + FTMAC100_INT_RPKT_LOST | \
    + FTMAC100_INT_AHB_ERR | \
    + FTMAC100_INT_PHYSTS_CHG)
    +
    +static void ftmac100_enable_all_int(struct ftmac100 *priv)
    +{
    + iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR);
    +}
    +
    +static void ftmac100_disable_all_int(struct ftmac100 *priv)
    +{
    + iowrite32(0, priv->base + FTMAC100_OFFSET_IMR);
    +}
    +
    +static void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
    +{
    + iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR);
    +}
    +
    +static void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr)
    +{
    + iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR);
    +}
    +
    +static void ftmac100_txdma_start_polling(struct ftmac100 *priv)
    +{
    + iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD);
    +}
    +
    +static int ftmac100_reset(struct ftmac100 *priv)
    +{
    + struct net_device *netdev = priv->netdev;
    + int i;
    +
    + /* NOTE: reset clears all registers */
    + iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR);
    +
    + for (i = 0; i < 5; i++) {
    + unsigned int maccr;
    +
    + maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR);
    + 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);
    + }
    +
    + netdev_err(netdev, "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);
    +}
    +
    +#define MACCR_ENABLE_ALL (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)
    +
    +static int ftmac100_start_hw(struct ftmac100 *priv)
    +{
    + struct net_device *netdev = priv->netdev;
    +
    + if (ftmac100_reset(priv))
    + return -EIO;
    +
    + /* setup ring buffer base registers */
    +
    + ftmac100_set_rx_ring_base(priv,
    + priv->descs_dma_addr +
    + offsetof(struct ftmac100_descs, rxdes));
    + ftmac100_set_tx_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);
    +
    + iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR);
    + return 0;
    +}
    +
    +static void ftmac100_stop_hw(struct ftmac100 *priv)
    +{
    + iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR);
    +}
    +
    +/******************************************************************************
    + * internal functions (receive descriptor)
    + *****************************************************************************/
    +static bool ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_FRS;
    +}
    +
    +static bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_LRS;
    +}
    +
    +static bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RXDMA_OWN;
    +}
    +
    +static void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes)
    +{
    + /* clear status bits */
    + rxdes->rxdes0 = cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN);
    +}
    +
    +static bool ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RX_ERR;
    +}
    +
    +static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_CRC_ERR;
    +}
    +
    +static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_FTL;
    +}
    +
    +static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RUNT;
    +}
    +
    +static bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RX_ODD_NB;
    +}
    +
    +static unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RFL;
    +}
    +
    +static bool ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_MULTICAST;
    +}
    +
    +static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes,
    + unsigned int size)
    +{
    + unsigned int tmp = le32_to_cpu(rxdes->rxdes1);
    +
    + tmp &= FTMAC100_RXDES1_EDORR;
    + tmp |= FTMAC100_RXDES1_RXBUF_SIZE(size);
    + rxdes->rxdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes)
    +{
    + unsigned int tmp = le32_to_cpu(rxdes->rxdes1);
    +
    + tmp |= FTMAC100_RXDES1_EDORR;
    + rxdes->rxdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes,
    + dma_addr_t addr)
    +{
    + rxdes->rxdes2 = cpu_to_le32(addr);
    +}
    +
    +static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes)
    +{
    + return le32_to_cpu(rxdes->rxdes2);
    +}
    +
    +/* rxdes3 is not used by hardware, we use it to keep track of buffer */
    +static void ftmac100_rxdes_set_va(struct ftmac100_rxdes *rxdes, void *addr)
    +{
    + rxdes->rxdes3 = cpu_to_le32(addr);
    +}
    +
    +static void *ftmac100_rxdes_get_va(struct ftmac100_rxdes *rxdes)
    +{
    + return (void *)le32_to_cpu(rxdes->rxdes3);
    +}
    +
    +/******************************************************************************
    + * internal functions (receive)
    + *****************************************************************************/
    +static int ftmac100_next_rx_pointer(int pointer)
    +{
    + return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
    +}
    +
    +static void ftmac100_rx_pointer_advance(struct ftmac100 *priv)
    +{
    + priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer);
    +}
    +
    +static 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 bool ftmac100_rx_packet_error(struct ftmac100 *priv,
    + struct ftmac100_rxdes *rxdes)
    +{
    + struct net_device *netdev = priv->netdev;
    + bool error = false;
    +
    + if (unlikely(ftmac100_rxdes_rx_error(rxdes))) {
    + if (net_ratelimit())
    + netdev_info(netdev, "rx err\n");
    +
    + netdev->stats.rx_errors++;
    + error = true;
    + }
    +
    + if (unlikely(ftmac100_rxdes_crc_error(rxdes))) {
    + if (net_ratelimit())
    + netdev_info(netdev, "rx crc err\n");
    +
    + netdev->stats.rx_crc_errors++;
    + error = true;
    + }
    +
    + if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) {
    + if (net_ratelimit())
    + netdev_info(netdev, "rx frame too long\n");
    +
    + netdev->stats.rx_length_errors++;
    + error = true;
    + } else if (unlikely(ftmac100_rxdes_runt(rxdes))) {
    + if (net_ratelimit())
    + netdev_info(netdev, "rx runt\n");
    +
    + netdev->stats.rx_length_errors++;
    + error = true;
    + } else if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) {
    + if (net_ratelimit())
    + netdev_info(netdev, "rx odd nibble\n");
    +
    + netdev->stats.rx_length_errors++;
    + error = true;
    + }
    +
    + return error;
    +}
    +
    +static void ftmac100_rx_drop_packet(struct ftmac100 *priv)
    +{
    + struct net_device *netdev = priv->netdev;
    + struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv);
    + bool done = false;
    +
    + if (net_ratelimit())
    + netdev_dbg(netdev, "drop packet %p\n", rxdes);
    +
    + do {
    + if (ftmac100_rxdes_last_segment(rxdes))
    + done = true;
    +
    + ftmac100_rxdes_set_dma_own(rxdes);
    + ftmac100_rx_pointer_advance(priv);
    + rxdes = ftmac100_current_rxdes(priv);
    + } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes));
    +
    + netdev->stats.rx_dropped++;
    +}
    +
    +static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed)
    +{
    + struct net_device *netdev = priv->netdev;
    + struct ftmac100_rxdes *rxdes;
    + struct sk_buff *skb;
    + int length;
    + bool copied = false;
    + bool done = false;
    +
    + rxdes = ftmac100_rx_locate_first_segment(priv);
    + if (!rxdes)
    + return false;
    +
    + if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) {
    + ftmac100_rx_drop_packet(priv);
    + return true;
    + }
    +
    + /* start processing */
    +
    + length = ftmac100_rxdes_frame_length(rxdes);
    +
    + skb = netdev_alloc_skb_ip_align(netdev, length);
    + if (unlikely(!skb)) {
    + if (net_ratelimit())
    + netdev_err(netdev, "rx skb alloc failed\n");
    +
    + ftmac100_rx_drop_packet(priv);
    + return true;
    + }
    +
    + if (unlikely(ftmac100_rxdes_multicast(rxdes)))
    + netdev->stats.multicast++;
    +
    + 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 = true;
    +
    + dma_sync_single_for_device(priv->dev, d, RX_BUF_SIZE,
    + DMA_FROM_DEVICE);
    +
    + ftmac100_rxdes_set_dma_own(rxdes);
    +
    + ftmac100_rx_pointer_advance(priv);
    + rxdes = ftmac100_current_rxdes(priv);
    + } while (!done && copied < length);
    +
    + skb->protocol = eth_type_trans(skb, netdev);
    +
    + /* push packet to protocol stack */
    + netif_receive_skb(skb);
    +
    + netdev->stats.rx_packets++;
    + netdev->stats.rx_bytes += skb->len;
    +
    + (*processed)++;
    +
    + return true;
    +}
    +
    +/******************************************************************************
    + * internal functions (transmit descriptor)
    + *****************************************************************************/
    +static 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 bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes)
    +{
    + return le32_to_cpu(txdes->txdes0) & FTMAC100_TXDES0_TXDMA_OWN;
    +}
    +
    +static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes0);
    +
    + tmp |= FTMAC100_TXDES0_TXDMA_OWN;
    +
    + /*
    + * Make sure dma own bit will not be set before any other
    + * descriptor fields.
    + */
    + wmb();
    + txdes->txdes0 = cpu_to_le32(tmp);
    +}
    +
    +static bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes)
    +{
    + return le32_to_cpu(txdes->txdes0) & FTMAC100_TXDES0_TXPKT_EXSCOL;
    +}
    +
    +static bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes)
    +{
    + return le32_to_cpu(txdes->txdes0) & FTMAC100_TXDES0_TXPKT_LATECOL;
    +}
    +
    +static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes1);
    +
    + tmp |= FTMAC100_TXDES1_EDOTR;
    + txdes->txdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes1);
    +
    + tmp |= FTMAC100_TXDES1_FTS;
    + txdes->txdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes1);
    +
    + tmp |= FTMAC100_TXDES1_LTS;
    + txdes->txdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes1);
    +
    + tmp |= FTMAC100_TXDES1_TXIC;
    + txdes->txdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes,
    + unsigned int len)
    +{
    + unsigned int tmp = le32_to_cpu(txdes->txdes1);
    +
    + tmp |= FTMAC100_TXDES1_TXBUF_SIZE(len);
    + txdes->txdes1 = cpu_to_le32(tmp);
    +}
    +
    +static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes,
    + dma_addr_t addr)
    +{
    + txdes->txdes2 = cpu_to_le32(addr);
    +}
    +
    +static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes)
    +{
    + return le32_to_cpu(txdes->txdes2);
    +}
    +
    +/* txdes3 is not used by hardware, we use it to keep track of socket buffer */
    +static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes,
    + struct sk_buff *skb)
    +{
    + txdes->txdes3 = cpu_to_le32(skb);
    +}
    +
    +static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes)
    +{
    + return (struct sk_buff *)le32_to_cpu(txdes->txdes3);
    +}
    +
    +/******************************************************************************
    + * internal functions (transmit)
    + *****************************************************************************/
    +static int ftmac100_next_tx_pointer(int pointer)
    +{
    + return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
    +}
    +
    +static void ftmac100_tx_pointer_advance(struct ftmac100 *priv)
    +{
    + priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer);
    +}
    +
    +static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv)
    +{
    + priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer);
    +}
    +
    +static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv)
    +{
    + return &priv->descs->txdes[priv->tx_pointer];
    +}
    +
    +static struct ftmac100_txdes *
    +ftmac100_current_clean_txdes(struct ftmac100 *priv)
    +{
    + return &priv->descs->txdes[priv->tx_clean_pointer];
    +}
    +
    +static bool 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 false;
    +
    + txdes = ftmac100_current_clean_txdes(priv);
    +
    + if (ftmac100_txdes_owned_by_dma(txdes))
    + return false;
    +
    + 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
    + */
    + netdev->stats.tx_aborted_errors++;
    + } else {
    + netdev->stats.tx_packets++;
    + netdev->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 true;
    +}
    +
    +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 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 (net_ratelimit())
    + netdev_info(netdev, "tx queue full\n");
    +
    + netif_stop_queue(netdev);
    + }
    +
    + /* start transmit */
    + ftmac100_txdes_set_dma_own(txdes);
    + spin_unlock_irqrestore(&priv->tx_lock, flags);
    +
    + ftmac100_txdma_start_polling(priv);
    +
    + 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);
    + unsigned int phycr;
    + int i;
    +
    + phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
    + FTMAC100_PHYCR_REGAD(reg) |
    + FTMAC100_PHYCR_MIIRD;
    +
    + iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
    + 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);
    + }
    +
    + netdev_err(netdev, "mdio read timed out\n");
    + return 0;
    +}
    +
    +static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg,
    + int data)
    +{
    + struct ftmac100 *priv = netdev_priv(netdev);
    + unsigned int phycr;
    + int i;
    +
    + phycr = FTMAC100_PHYCR_PHYAD(phy_id) |
    + FTMAC100_PHYCR_REGAD(reg) |
    + FTMAC100_PHYCR_MIIWR;
    +
    + data = FTMAC100_PHYWDATA_MIIWDATA(data);
    +
    + iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA);
    + iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR);
    +
    + for (i = 0; i < 10; i++) {
    + phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR);
    +
    + if ((phycr & FTMAC100_PHYCR_MIIWR) == 0)
    + return;
    +
    + usleep_range(100, 1000);
    + }
    +
    + netdev_err(netdev, "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 const 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 ftmac100 *priv = netdev_priv(netdev);
    +
    + if (likely(netif_running(netdev))) {
    + /* Disable interrupts for polling */
    + ftmac100_disable_all_int(priv);
    + napi_schedule(&priv->napi);
    + }
    +
    + return IRQ_HANDLED;
    +}
    +
    +/******************************************************************************
    + * struct napi_struct functions
    + *****************************************************************************/
    +static int ftmac100_poll(struct napi_struct *napi, int budget)
    +{
    + struct ftmac100 *priv = container_of(napi, struct ftmac100, napi);
    + struct net_device *netdev = priv->netdev;
    + unsigned int status;
    + bool completed = true;
    + int rx = 0;
    +
    + status = ioread32(priv->base + FTMAC100_OFFSET_ISR);
    +
    + 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
    + */
    + bool retry;
    +
    + do {
    + retry = ftmac100_rx_packet(priv, &rx);
    + } while (retry && rx < budget);
    +
    + if (retry && rx == budget)
    + completed = false;
    + }
    +
    + if (status & FTMAC100_INT_NORXBUF) {
    + /* RX buffer unavailable */
    + if (net_ratelimit())
    + netdev_info(netdev, "INT_NORXBUF\n");
    +
    + netdev->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 (net_ratelimit())
    + netdev_info(netdev, "INT_RPKT_LOST\n");
    +
    + netdev->stats.rx_fifo_errors++;
    + }
    +
    + if (status & FTMAC100_INT_AHB_ERR) {
    + /* AHB error */
    + if (net_ratelimit())
    + netdev_info(netdev, "INT_AHB_ERR\n");
    +
    + /* do nothing */
    + }
    +
    + if (status & FTMAC100_INT_PHYSTS_CHG) {
    + /* PHY link status change */
    + if (net_ratelimit())
    + netdev_info(netdev, "INT_PHYSTS_CHG\n");
    +
    + mii_check_link(&priv->mii);
    + }
    +
    + if (completed) {
    + /* stop polling */
    + napi_complete(napi);
    + ftmac100_enable_all_int(priv);
    + }
    +
    + return rx;
    +}
    +
    +/******************************************************************************
    + * struct net_device_ops functions
    + *****************************************************************************/
    +static int ftmac100_open(struct net_device *netdev)
    +{
    + struct ftmac100 *priv = netdev_priv(netdev);
    + int err;
    +
    + err = ftmac100_alloc_buffers(priv);
    + if (err) {
    + netdev_err(netdev, "failed to allocate buffers\n");
    + goto err_alloc;
    + }
    +
    + err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name,
    + netdev);
    + if (err) {
    + netdev_err(netdev, "failed to request irq %d\n", priv->irq);
    + goto err_irq;
    + }
    +
    + priv->rx_pointer = 0;
    + priv->tx_clean_pointer = 0;
    + priv->tx_pointer = 0;
    + priv->tx_pending = 0;
    +
    + err = ftmac100_start_hw(priv);
    + if (err)
    + goto err_hw;
    +
    + napi_enable(&priv->napi);
    + 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);
    + napi_disable(&priv->napi);
    + 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 ftmac100 *priv = netdev_priv(netdev);
    + dma_addr_t map;
    +
    + if (unlikely(skb->len > MAX_PKT_SIZE)) {
    + if (net_ratelimit())
    + netdev_dbg(netdev, "tx packet too big\n");
    +
    + netdev->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 (net_ratelimit())
    + netdev_err(netdev, "map socket buffer failed\n");
    +
    + netdev->stats.tx_dropped++;
    + dev_kfree_skb(skb);
    + return NETDEV_TX_OK;
    + }
    +
    + return ftmac100_xmit(priv, skb, map);
    +}
    +
    +/* 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 const 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_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);
    +
    + netif_napi_del(&priv->napi);
    + 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;
    +
    + spin_lock_init(&priv->tx_lock);
    +
    + /* initialize NAPI */
    + netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64);
    +
    + /* 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;
    + }
    +
    + netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);
    +
    + if (!is_valid_ether_addr(netdev->dev_addr)) {
    + random_ether_addr(netdev->dev_addr);
    + netdev_info(netdev, "generated random MAC address %pM\n",
    + netdev->dev_addr);
    + }
    +
    + 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)
    +{
    + pr_info("Loading 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-20 16:33    [W:0.098 / U:30.168 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site