From baf0fbfe7ea34cd676e3362a62033d8ca1c52d99 Mon Sep 17 00:00:00 2001 From: Jeff Kirsher Date: Tue, 21 Jun 2011 17:55:27 -0700 Subject: ftgmac100/ftmac100: Move the Faraday drivers Move the Faraday driver into drivers/net/ethernet/faraday/ and make the necessary Kconfig and Makefile changes. CC: "Po-Yu Chuang" Signed-off-by: Jeff Kirsher Acked-by: Po-Yu Chuang --- drivers/net/Kconfig | 18 - drivers/net/Makefile | 2 - drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/faraday/Kconfig | 38 + drivers/net/ethernet/faraday/Makefile | 6 + drivers/net/ethernet/faraday/ftgmac100.c | 1365 ++++++++++++++++++++++++++++++ drivers/net/ethernet/faraday/ftgmac100.h | 246 ++++++ drivers/net/ethernet/faraday/ftmac100.c | 1198 ++++++++++++++++++++++++++ drivers/net/ethernet/faraday/ftmac100.h | 180 ++++ drivers/net/ftgmac100.c | 1365 ------------------------------ drivers/net/ftgmac100.h | 246 ------ drivers/net/ftmac100.c | 1198 -------------------------- drivers/net/ftmac100.h | 180 ---- 14 files changed, 3035 insertions(+), 3009 deletions(-) create mode 100644 drivers/net/ethernet/faraday/Kconfig create mode 100644 drivers/net/ethernet/faraday/Makefile create mode 100644 drivers/net/ethernet/faraday/ftgmac100.c create mode 100644 drivers/net/ethernet/faraday/ftgmac100.h create mode 100644 drivers/net/ethernet/faraday/ftmac100.c create mode 100644 drivers/net/ethernet/faraday/ftmac100.h delete mode 100644 drivers/net/ftgmac100.c delete mode 100644 drivers/net/ftgmac100.h delete mode 100644 drivers/net/ftmac100.c delete mode 100644 drivers/net/ftmac100.h (limited to 'drivers/net') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7f09cd541c3b..8da9f9620680 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -922,15 +922,6 @@ config XILINX_EMACLITE help This driver supports the 10/100 Ethernet Lite from Xilinx. -config FTMAC100 - tristate "Faraday FTMAC100 10/100 Ethernet support" - depends on ARM - select MII - help - This driver supports the FTMAC100 10/100 Ethernet controller - from Faraday. It is used on Faraday A320, Andes AG101 and some - other ARM/NDS32 SoC's. - config LANTIQ_ETOP tristate "Lantiq SoC ETOP driver" depends on SOC_TYPE_XWAY @@ -1286,15 +1277,6 @@ config PCH_GBE ML7223 is companion chip for Intel Atom E6xx series. ML7223 is completely compatible for Intel EG20T PCH. -config FTGMAC100 - tristate "Faraday FTGMAC100 Gigabit Ethernet support" - depends on ARM - select PHYLIB - help - This driver supports the FTGMAC100 Gigabit Ethernet controller - from Faraday. It is used on Faraday A369, Andes AG102 and some - other ARM/NDS32 SoC's. - endif # NETDEV_1000 # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index ffcb6090e40f..0b341dc04c13 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -79,8 +79,6 @@ ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o endif obj-$(CONFIG_FORCEDETH) += forcedeth.o -obj-$(CONFIG_FTGMAC100) += ftgmac100.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/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 45d3eff86baa..110071ec4ce6 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -20,6 +20,7 @@ source "drivers/net/ethernet/chelsio/Kconfig" source "drivers/net/ethernet/cisco/Kconfig" source "drivers/net/ethernet/emulex/Kconfig" source "drivers/net/ethernet/neterion/Kconfig" +source "drivers/net/ethernet/faraday/Kconfig" source "drivers/net/ethernet/ibm/Kconfig" source "drivers/net/ethernet/intel/Kconfig" source "drivers/net/ethernet/i825xx/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 09536de3a366..4a6edf7141d2 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_NET_VENDOR_CHELSIO) += chelsio/ obj-$(CONFIG_NET_VENDOR_CISCO) += cisco/ obj-$(CONFIG_NET_VENDOR_EMULEX) += emulex/ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/ +obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/ obj-$(CONFIG_NET_VENDOR_IBM) += ibm/ obj-$(CONFIG_NET_VENDOR_INTEL) += intel/ obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/ diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig new file mode 100644 index 000000000000..b0d76f01d47b --- /dev/null +++ b/drivers/net/ethernet/faraday/Kconfig @@ -0,0 +1,38 @@ +# +# Faraday device configuration +# + +config NET_VENDOR_FARADAY + bool "Faraday devices" + depends on ARM + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y + and read the Ethernet-HOWTO, available from + . + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Faraday cards. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_FARADAY + +config FTMAC100 + tristate "Faraday FTMAC100 10/100 Ethernet support" + depends on ARM + select MII + ---help--- + This driver supports the FTMAC100 10/100 Ethernet controller + from Faraday. It is used on Faraday A320, Andes AG101 and some + other ARM/NDS32 SoC's. + +config FTGMAC100 + tristate "Faraday FTGMAC100 Gigabit Ethernet support" + depends on ARM + select PHYLIB + ---help--- + This driver supports the FTGMAC100 Gigabit Ethernet controller + from Faraday. It is used on Faraday A369, Andes AG102 and some + other ARM/NDS32 SoC's. + +endif # NET_VENDOR_FARADAY diff --git a/drivers/net/ethernet/faraday/Makefile b/drivers/net/ethernet/faraday/Makefile new file mode 100644 index 000000000000..408b53980d53 --- /dev/null +++ b/drivers/net/ethernet/faraday/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the Faraday device drivers. +# + +obj-$(CONFIG_FTGMAC100) += ftgmac100.o +obj-$(CONFIG_FTMAC100) += ftmac100.o diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c new file mode 100644 index 000000000000..54709af917e9 --- /dev/null +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -0,0 +1,1365 @@ +/* + * Faraday FTGMAC100 Gigabit Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftgmac100.h" + +#define DRV_NAME "ftgmac100" +#define DRV_VERSION "0.7" + +#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */ +#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */ + +#define MAX_PKT_SIZE 1518 +#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */ + +/****************************************************************************** + * private data + *****************************************************************************/ +struct ftgmac100_descs { + struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; + struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; +}; + +struct ftgmac100 { + struct resource *res; + void __iomem *base; + int irq; + + struct ftgmac100_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_bus *mii_bus; + int phy_irq[PHY_MAX_ADDR]; + struct phy_device *phydev; + int old_speed; +}; + +static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, + struct ftgmac100_rxdes *rxdes, gfp_t gfp); + +/****************************************************************************** + * internal functions (hardware register access) + *****************************************************************************/ +#define INT_MASK_ALL_ENABLED (FTGMAC100_INT_RPKT_LOST | \ + FTGMAC100_INT_XPKT_ETH | \ + FTGMAC100_INT_XPKT_LOST | \ + FTGMAC100_INT_AHB_ERR | \ + FTGMAC100_INT_PHYSTS_CHG | \ + FTGMAC100_INT_RPKT_BUF | \ + FTGMAC100_INT_NO_RXBUF) + +static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) +{ + iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); +} + +static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv, + unsigned int size) +{ + size = FTGMAC100_RBSR_SIZE(size); + iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR); +} + +static void ftgmac100_set_normal_prio_tx_ring_base(struct ftgmac100 *priv, + dma_addr_t addr) +{ + iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR); +} + +static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv) +{ + iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); +} + +static int ftgmac100_reset_hw(struct ftgmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + int i; + + /* NOTE: reset clears all registers */ + iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR); + for (i = 0; i < 5; i++) { + unsigned int maccr; + + maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); + if (!(maccr & FTGMAC100_MACCR_SW_RST)) + return 0; + + udelay(1000); + } + + netdev_err(netdev, "software reset failed\n"); + return -EIO; +} + +static void ftgmac100_set_mac(struct ftgmac100 *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 + FTGMAC100_OFFSET_MAC_MADR); + iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR); +} + +static void ftgmac100_init_hw(struct ftgmac100 *priv) +{ + /* setup ring buffer base registers */ + ftgmac100_set_rx_ring_base(priv, + priv->descs_dma_addr + + offsetof(struct ftgmac100_descs, rxdes)); + ftgmac100_set_normal_prio_tx_ring_base(priv, + priv->descs_dma_addr + + offsetof(struct ftgmac100_descs, txdes)); + + ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE); + + iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC); + + ftgmac100_set_mac(priv, priv->netdev->dev_addr); +} + +#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \ + FTGMAC100_MACCR_RXDMA_EN | \ + FTGMAC100_MACCR_TXMAC_EN | \ + FTGMAC100_MACCR_RXMAC_EN | \ + FTGMAC100_MACCR_FULLDUP | \ + FTGMAC100_MACCR_CRC_APD | \ + FTGMAC100_MACCR_RX_RUNT | \ + FTGMAC100_MACCR_RX_BROADPKT) + +static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed) +{ + int maccr = MACCR_ENABLE_ALL; + + switch (speed) { + default: + case 10: + break; + + case 100: + maccr |= FTGMAC100_MACCR_FAST_MODE; + break; + + case 1000: + maccr |= FTGMAC100_MACCR_GIGA_MODE; + break; + } + + iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); +} + +static void ftgmac100_stop_hw(struct ftgmac100 *priv) +{ + iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); +} + +/****************************************************************************** + * internal functions (receive descriptor) + *****************************************************************************/ +static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS); +} + +static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS); +} + +static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY); +} + +static void ftgmac100_rxdes_set_dma_own(struct ftgmac100_rxdes *rxdes) +{ + /* clear status bits */ + rxdes->rxdes0 &= cpu_to_le32(FTGMAC100_RXDES0_EDORR); +} + +static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR); +} + +static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR); +} + +static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL); +} + +static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT); +} + +static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB); +} + +static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes) +{ + return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC; +} + +static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST); +} + +static void ftgmac100_rxdes_set_end_of_ring(struct ftgmac100_rxdes *rxdes) +{ + rxdes->rxdes0 |= cpu_to_le32(FTGMAC100_RXDES0_EDORR); +} + +static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes, + dma_addr_t addr) +{ + rxdes->rxdes3 = cpu_to_le32(addr); +} + +static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes) +{ + return le32_to_cpu(rxdes->rxdes3); +} + +static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes) +{ + return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == + cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP); +} + +static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes) +{ + return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == + cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP); +} + +static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR); +} + +static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR); +} + +static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes) +{ + return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR); +} + +/* + * rxdes2 is not used by hardware. We use it to keep track of page. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftgmac100_rxdes_set_page(struct ftgmac100_rxdes *rxdes, struct page *page) +{ + rxdes->rxdes2 = (unsigned int)page; +} + +static struct page *ftgmac100_rxdes_get_page(struct ftgmac100_rxdes *rxdes) +{ + return (struct page *)rxdes->rxdes2; +} + +/****************************************************************************** + * internal functions (receive) + *****************************************************************************/ +static int ftgmac100_next_rx_pointer(int pointer) +{ + return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); +} + +static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv) +{ + priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer); +} + +static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv) +{ + return &priv->descs->rxdes[priv->rx_pointer]; +} + +static struct ftgmac100_rxdes * +ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv) +{ + struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); + + while (ftgmac100_rxdes_packet_ready(rxdes)) { + if (ftgmac100_rxdes_first_segment(rxdes)) + return rxdes; + + ftgmac100_rxdes_set_dma_own(rxdes); + ftgmac100_rx_pointer_advance(priv); + rxdes = ftgmac100_current_rxdes(priv); + } + + return NULL; +} + +static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv, + struct ftgmac100_rxdes *rxdes) +{ + struct net_device *netdev = priv->netdev; + bool error = false; + + if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx err\n"); + + netdev->stats.rx_errors++; + error = true; + } + + if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx crc err\n"); + + netdev->stats.rx_crc_errors++; + error = true; + } else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx IP checksum err\n"); + + error = true; + } + + if (unlikely(ftgmac100_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(ftgmac100_rxdes_runt(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx runt\n"); + + netdev->stats.rx_length_errors++; + error = true; + } else if (unlikely(ftgmac100_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 ftgmac100_rx_drop_packet(struct ftgmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); + bool done = false; + + if (net_ratelimit()) + netdev_dbg(netdev, "drop packet %p\n", rxdes); + + do { + if (ftgmac100_rxdes_last_segment(rxdes)) + done = true; + + ftgmac100_rxdes_set_dma_own(rxdes); + ftgmac100_rx_pointer_advance(priv); + rxdes = ftgmac100_current_rxdes(priv); + } while (!done && ftgmac100_rxdes_packet_ready(rxdes)); + + netdev->stats.rx_dropped++; +} + +static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) +{ + struct net_device *netdev = priv->netdev; + struct ftgmac100_rxdes *rxdes; + struct sk_buff *skb; + bool done = false; + + rxdes = ftgmac100_rx_locate_first_segment(priv); + if (!rxdes) + return false; + + if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) { + ftgmac100_rx_drop_packet(priv); + return true; + } + + /* start processing */ + skb = netdev_alloc_skb_ip_align(netdev, 128); + if (unlikely(!skb)) { + if (net_ratelimit()) + netdev_err(netdev, "rx skb alloc failed\n"); + + ftgmac100_rx_drop_packet(priv); + return true; + } + + if (unlikely(ftgmac100_rxdes_multicast(rxdes))) + netdev->stats.multicast++; + + /* + * It seems that HW does checksum incorrectly with fragmented packets, + * so we are conservative here - if HW checksum error, let software do + * the checksum again. + */ + if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) || + (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + do { + dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); + struct page *page = ftgmac100_rxdes_get_page(rxdes); + unsigned int size; + + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + + size = ftgmac100_rxdes_data_length(rxdes); + skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size); + + skb->len += size; + skb->data_len += size; + skb->truesize += size; + + if (ftgmac100_rxdes_last_segment(rxdes)) + done = true; + + ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + + ftgmac100_rx_pointer_advance(priv); + rxdes = ftgmac100_current_rxdes(priv); + } while (!done); + + __pskb_pull_tail(skb, min(skb->len, 64U)); + skb->protocol = eth_type_trans(skb, netdev); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + + /* push packet to protocol stack */ + napi_gro_receive(&priv->napi, skb); + + (*processed)++; + return true; +} + +/****************************************************************************** + * internal functions (transmit descriptor) + *****************************************************************************/ +static void ftgmac100_txdes_reset(struct ftgmac100_txdes *txdes) +{ + /* clear all except end of ring bit */ + txdes->txdes0 &= cpu_to_le32(FTGMAC100_TXDES0_EDOTR); + txdes->txdes1 = 0; + txdes->txdes2 = 0; + txdes->txdes3 = 0; +} + +static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); +} + +static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes) +{ + /* + * Make sure dma own bit will not be set before any other + * descriptor fields. + */ + wmb(); + txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); +} + +static void ftgmac100_txdes_set_end_of_ring(struct ftgmac100_txdes *txdes) +{ + txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_EDOTR); +} + +static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes) +{ + txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS); +} + +static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes) +{ + txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS); +} + +static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes, + unsigned int len) +{ + txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len)); +} + +static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC); +} + +static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM); +} + +static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM); +} + +static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM); +} + +static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes, + dma_addr_t addr) +{ + txdes->txdes3 = cpu_to_le32(addr); +} + +static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) +{ + return le32_to_cpu(txdes->txdes3); +} + +/* + * txdes2 is not used by hardware. We use it to keep track of socket buffer. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes, + struct sk_buff *skb) +{ + txdes->txdes2 = (unsigned int)skb; +} + +static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes) +{ + return (struct sk_buff *)txdes->txdes2; +} + +/****************************************************************************** + * internal functions (transmit) + *****************************************************************************/ +static int ftgmac100_next_tx_pointer(int pointer) +{ + return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); +} + +static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv) +{ + priv->tx_pointer = ftgmac100_next_tx_pointer(priv->tx_pointer); +} + +static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100 *priv) +{ + priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv->tx_clean_pointer); +} + +static struct ftgmac100_txdes *ftgmac100_current_txdes(struct ftgmac100 *priv) +{ + return &priv->descs->txdes[priv->tx_pointer]; +} + +static struct ftgmac100_txdes * +ftgmac100_current_clean_txdes(struct ftgmac100 *priv) +{ + return &priv->descs->txdes[priv->tx_clean_pointer]; +} + +static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + struct ftgmac100_txdes *txdes; + struct sk_buff *skb; + dma_addr_t map; + + if (priv->tx_pending == 0) + return false; + + txdes = ftgmac100_current_clean_txdes(priv); + + if (ftgmac100_txdes_owned_by_dma(txdes)) + return false; + + skb = ftgmac100_txdes_get_skb(txdes); + map = ftgmac100_txdes_get_dma_addr(txdes); + + 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(skb); + + ftgmac100_txdes_reset(txdes); + + ftgmac100_tx_clean_pointer_advance(priv); + + spin_lock(&priv->tx_lock); + priv->tx_pending--; + spin_unlock(&priv->tx_lock); + netif_wake_queue(netdev); + + return true; +} + +static void ftgmac100_tx_complete(struct ftgmac100 *priv) +{ + while (ftgmac100_tx_complete_packet(priv)) + ; +} + +static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, + dma_addr_t map) +{ + struct net_device *netdev = priv->netdev; + struct ftgmac100_txdes *txdes; + unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + + txdes = ftgmac100_current_txdes(priv); + ftgmac100_tx_pointer_advance(priv); + + /* setup TX descriptor */ + ftgmac100_txdes_set_skb(txdes, skb); + ftgmac100_txdes_set_dma_addr(txdes, map); + ftgmac100_txdes_set_buffer_size(txdes, len); + + ftgmac100_txdes_set_first_segment(txdes); + ftgmac100_txdes_set_last_segment(txdes); + ftgmac100_txdes_set_txint(txdes); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + __be16 protocol = skb->protocol; + + if (protocol == cpu_to_be16(ETH_P_IP)) { + u8 ip_proto = ip_hdr(skb)->protocol; + + ftgmac100_txdes_set_ipcs(txdes); + if (ip_proto == IPPROTO_TCP) + ftgmac100_txdes_set_tcpcs(txdes); + else if (ip_proto == IPPROTO_UDP) + ftgmac100_txdes_set_udpcs(txdes); + } + } + + spin_lock(&priv->tx_lock); + priv->tx_pending++; + if (priv->tx_pending == TX_QUEUE_ENTRIES) + netif_stop_queue(netdev); + + /* start transmit */ + ftgmac100_txdes_set_dma_own(txdes); + spin_unlock(&priv->tx_lock); + + ftgmac100_txdma_normal_prio_start_polling(priv); + + return NETDEV_TX_OK; +} + +/****************************************************************************** + * internal functions (buffer) + *****************************************************************************/ +static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, + struct ftgmac100_rxdes *rxdes, gfp_t gfp) +{ + struct net_device *netdev = priv->netdev; + struct page *page; + dma_addr_t map; + + page = alloc_page(gfp); + if (!page) { + if (net_ratelimit()) + netdev_err(netdev, "failed to allocate rx page\n"); + return -ENOMEM; + } + + map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + if (net_ratelimit()) + netdev_err(netdev, "failed to map rx page\n"); + __free_page(page); + return -ENOMEM; + } + + ftgmac100_rxdes_set_page(rxdes, page); + ftgmac100_rxdes_set_dma_addr(rxdes, map); + ftgmac100_rxdes_set_dma_own(rxdes); + return 0; +} + +static void ftgmac100_free_buffers(struct ftgmac100 *priv) +{ + int i; + + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + struct page *page = ftgmac100_rxdes_get_page(rxdes); + dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); + + if (!page) + continue; + + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + __free_page(page); + } + + for (i = 0; i < TX_QUEUE_ENTRIES; i++) { + struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; + struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes); + dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); + + if (!skb) + continue; + + dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); + dev_kfree_skb(skb); + } + + dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), + priv->descs, priv->descs_dma_addr); +} + +static int ftgmac100_alloc_buffers(struct ftgmac100 *priv) +{ + int i; + + priv->descs = dma_alloc_coherent(priv->dev, + sizeof(struct ftgmac100_descs), + &priv->descs_dma_addr, GFP_KERNEL); + if (!priv->descs) + return -ENOMEM; + + memset(priv->descs, 0, sizeof(struct ftgmac100_descs)); + + /* initialize RX ring */ + ftgmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); + + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + + if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) + goto err; + } + + /* initialize TX ring */ + ftgmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]); + return 0; + +err: + ftgmac100_free_buffers(priv); + return -ENOMEM; +} + +/****************************************************************************** + * internal functions (mdio) + *****************************************************************************/ +static void ftgmac100_adjust_link(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + struct phy_device *phydev = priv->phydev; + int ier; + + if (phydev->speed == priv->old_speed) + return; + + priv->old_speed = phydev->speed; + + ier = ioread32(priv->base + FTGMAC100_OFFSET_IER); + + /* disable all interrupts */ + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + + netif_stop_queue(netdev); + ftgmac100_stop_hw(priv); + + netif_start_queue(netdev); + ftgmac100_init_hw(priv); + ftgmac100_start_hw(priv, phydev->speed); + + /* re-enable interrupts */ + iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER); +} + +static int ftgmac100_mii_probe(struct ftgmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + struct phy_device *phydev = NULL; + int i; + + /* search for connect PHY device */ + for (i = 0; i < PHY_MAX_ADDR; i++) { + struct phy_device *tmp = priv->mii_bus->phy_map[i]; + + if (tmp) { + phydev = tmp; + break; + } + } + + /* now we are supposed to have a proper phydev, to attach to... */ + if (!phydev) { + netdev_info(netdev, "%s: no PHY found\n", netdev->name); + return -ENODEV; + } + + phydev = phy_connect(netdev, dev_name(&phydev->dev), + &ftgmac100_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + + if (IS_ERR(phydev)) { + netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name); + return PTR_ERR(phydev); + } + + priv->phydev = phydev; + return 0; +} + +/****************************************************************************** + * struct mii_bus functions + *****************************************************************************/ +static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) +{ + struct net_device *netdev = bus->priv; + struct ftgmac100 *priv = netdev_priv(netdev); + unsigned int phycr; + int i; + + phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); + + /* preserve MDC cycle threshold */ + phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; + + phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) | + FTGMAC100_PHYCR_REGAD(regnum) | + FTGMAC100_PHYCR_MIIRD; + + iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR); + + for (i = 0; i < 10; i++) { + phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); + + if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) { + int data; + + data = ioread32(priv->base + FTGMAC100_OFFSET_PHYDATA); + return FTGMAC100_PHYDATA_MIIRDATA(data); + } + + udelay(100); + } + + netdev_err(netdev, "mdio read timed out\n"); + return -EIO; +} + +static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr, + int regnum, u16 value) +{ + struct net_device *netdev = bus->priv; + struct ftgmac100 *priv = netdev_priv(netdev); + unsigned int phycr; + int data; + int i; + + phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); + + /* preserve MDC cycle threshold */ + phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; + + phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) | + FTGMAC100_PHYCR_REGAD(regnum) | + FTGMAC100_PHYCR_MIIWR; + + data = FTGMAC100_PHYDATA_MIIWDATA(value); + + iowrite32(data, priv->base + FTGMAC100_OFFSET_PHYDATA); + iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR); + + for (i = 0; i < 10; i++) { + phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); + + if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) + return 0; + + udelay(100); + } + + netdev_err(netdev, "mdio write timed out\n"); + return -EIO; +} + +static int ftgmac100_mdiobus_reset(struct mii_bus *bus) +{ + return 0; +} + +/****************************************************************************** + * struct ethtool_ops functions + *****************************************************************************/ +static void ftgmac100_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 ftgmac100_get_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + return phy_ethtool_gset(priv->phydev, cmd); +} + +static int ftgmac100_set_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + return phy_ethtool_sset(priv->phydev, cmd); +} + +static const struct ethtool_ops ftgmac100_ethtool_ops = { + .set_settings = ftgmac100_set_settings, + .get_settings = ftgmac100_get_settings, + .get_drvinfo = ftgmac100_get_drvinfo, + .get_link = ethtool_op_get_link, +}; + +/****************************************************************************** + * interrupt handler + *****************************************************************************/ +static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + struct ftgmac100 *priv = netdev_priv(netdev); + + if (likely(netif_running(netdev))) { + /* Disable interrupts for polling */ + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +/****************************************************************************** + * struct napi_struct functions + *****************************************************************************/ +static int ftgmac100_poll(struct napi_struct *napi, int budget) +{ + struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); + struct net_device *netdev = priv->netdev; + unsigned int status; + bool completed = true; + int rx = 0; + + status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); + iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); + + if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) { + /* + * FTGMAC100_INT_RPKT_BUF: + * RX DMA has received packets into RX buffer successfully + * + * FTGMAC100_INT_NO_RXBUF: + * RX buffer unavailable + */ + bool retry; + + do { + retry = ftgmac100_rx_packet(priv, &rx); + } while (retry && rx < budget); + + if (retry && rx == budget) + completed = false; + } + + if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) { + /* + * FTGMAC100_INT_XPKT_ETH: + * packet transmitted to ethernet successfully + * + * FTGMAC100_INT_XPKT_LOST: + * packet transmitted to ethernet lost due to late + * collision or excessive collision + */ + ftgmac100_tx_complete(priv); + } + + if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST | + FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) { + if (net_ratelimit()) + netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, + status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "", + status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", + status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "", + status & FTGMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); + + if (status & FTGMAC100_INT_NO_RXBUF) { + /* RX buffer unavailable */ + netdev->stats.rx_over_errors++; + } + + if (status & FTGMAC100_INT_RPKT_LOST) { + /* received packet lost due to RX FIFO full */ + netdev->stats.rx_fifo_errors++; + } + } + + if (completed) { + napi_complete(napi); + + /* enable all interrupts */ + iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); + } + + return rx; +} + +/****************************************************************************** + * struct net_device_ops functions + *****************************************************************************/ +static int ftgmac100_open(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + int err; + + err = ftgmac100_alloc_buffers(priv); + if (err) { + netdev_err(netdev, "failed to allocate buffers\n"); + goto err_alloc; + } + + err = request_irq(priv->irq, ftgmac100_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 = ftgmac100_reset_hw(priv); + if (err) + goto err_hw; + + ftgmac100_init_hw(priv); + ftgmac100_start_hw(priv, 10); + + phy_start(priv->phydev); + + napi_enable(&priv->napi); + netif_start_queue(netdev); + + /* enable all interrupts */ + iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); + return 0; + +err_hw: + free_irq(priv->irq, netdev); +err_irq: + ftgmac100_free_buffers(priv); +err_alloc: + return err; +} + +static int ftgmac100_stop(struct net_device *netdev) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + /* disable all interrupts */ + iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); + + netif_stop_queue(netdev); + napi_disable(&priv->napi); + phy_stop(priv->phydev); + + ftgmac100_stop_hw(priv); + free_irq(priv->irq, netdev); + ftgmac100_free_buffers(priv); + + return 0; +} + +static int ftgmac100_hard_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct ftgmac100 *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 ftgmac100_xmit(priv, skb, map); +} + +/* optional */ +static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct ftgmac100 *priv = netdev_priv(netdev); + + return phy_mii_ioctl(priv->phydev, ifr, cmd); +} + +static const struct net_device_ops ftgmac100_netdev_ops = { + .ndo_open = ftgmac100_open, + .ndo_stop = ftgmac100_stop, + .ndo_start_xmit = ftgmac100_hard_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = ftgmac100_do_ioctl, +}; + +/****************************************************************************** + * struct platform_driver functions + *****************************************************************************/ +static int ftgmac100_probe(struct platform_device *pdev) +{ + struct resource *res; + int irq; + struct net_device *netdev; + struct ftgmac100 *priv; + int err; + int i; + + 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(*priv)); + if (!netdev) { + err = -ENOMEM; + goto err_alloc_etherdev; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + + SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops); + netdev->netdev_ops = &ftgmac100_netdev_ops; + netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; + + 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, ftgmac100_poll, 64); + + /* map io memory */ + priv->res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!priv->res) { + dev_err(&pdev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_req_mem; + } + + priv->base = ioremap(res->start, resource_size(res)); + if (!priv->base) { + dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); + err = -EIO; + goto err_ioremap; + } + + priv->irq = irq; + + /* initialize mdio bus */ + priv->mii_bus = mdiobus_alloc(); + if (!priv->mii_bus) { + err = -EIO; + goto err_alloc_mdiobus; + } + + priv->mii_bus->name = "ftgmac100_mdio"; + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii"); + + priv->mii_bus->priv = netdev; + priv->mii_bus->read = ftgmac100_mdiobus_read; + priv->mii_bus->write = ftgmac100_mdiobus_write; + priv->mii_bus->reset = ftgmac100_mdiobus_reset; + priv->mii_bus->irq = priv->phy_irq; + + for (i = 0; i < PHY_MAX_ADDR; i++) + priv->mii_bus->irq[i] = PHY_POLL; + + err = mdiobus_register(priv->mii_bus); + if (err) { + dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); + goto err_register_mdiobus; + } + + err = ftgmac100_mii_probe(priv); + if (err) { + dev_err(&pdev->dev, "MII Probe failed!\n"); + goto err_mii_probe; + } + + /* register network device */ + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto err_register_netdev; + } + + 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_register_netdev: + phy_disconnect(priv->phydev); +err_mii_probe: + mdiobus_unregister(priv->mii_bus); +err_register_mdiobus: + mdiobus_free(priv->mii_bus); +err_alloc_mdiobus: + iounmap(priv->base); +err_ioremap: + release_resource(priv->res); +err_req_mem: + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_alloc_etherdev: + return err; +} + +static int __exit ftgmac100_remove(struct platform_device *pdev) +{ + struct net_device *netdev; + struct ftgmac100 *priv; + + netdev = platform_get_drvdata(pdev); + priv = netdev_priv(netdev); + + unregister_netdev(netdev); + + phy_disconnect(priv->phydev); + mdiobus_unregister(priv->mii_bus); + mdiobus_free(priv->mii_bus); + + iounmap(priv->base); + release_resource(priv->res); + + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); + return 0; +} + +static struct platform_driver ftgmac100_driver = { + .probe = ftgmac100_probe, + .remove = __exit_p(ftgmac100_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +/****************************************************************************** + * initialization / finalization + *****************************************************************************/ +static int __init ftgmac100_init(void) +{ + pr_info("Loading version " DRV_VERSION " ...\n"); + return platform_driver_register(&ftgmac100_driver); +} + +static void __exit ftgmac100_exit(void) +{ + platform_driver_unregister(&ftgmac100_driver); +} + +module_init(ftgmac100_init); +module_exit(ftgmac100_exit); + +MODULE_AUTHOR("Po-Yu Chuang "); +MODULE_DESCRIPTION("FTGMAC100 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/faraday/ftgmac100.h b/drivers/net/ethernet/faraday/ftgmac100.h new file mode 100644 index 000000000000..13408d448b05 --- /dev/null +++ b/drivers/net/ethernet/faraday/ftgmac100.h @@ -0,0 +1,246 @@ +/* + * Faraday FTGMAC100 Gigabit Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang + * + * 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 __FTGMAC100_H +#define __FTGMAC100_H + +#define FTGMAC100_OFFSET_ISR 0x00 +#define FTGMAC100_OFFSET_IER 0x04 +#define FTGMAC100_OFFSET_MAC_MADR 0x08 +#define FTGMAC100_OFFSET_MAC_LADR 0x0c +#define FTGMAC100_OFFSET_MAHT0 0x10 +#define FTGMAC100_OFFSET_MAHT1 0x14 +#define FTGMAC100_OFFSET_NPTXPD 0x18 +#define FTGMAC100_OFFSET_RXPD 0x1c +#define FTGMAC100_OFFSET_NPTXR_BADR 0x20 +#define FTGMAC100_OFFSET_RXR_BADR 0x24 +#define FTGMAC100_OFFSET_HPTXPD 0x28 +#define FTGMAC100_OFFSET_HPTXR_BADR 0x2c +#define FTGMAC100_OFFSET_ITC 0x30 +#define FTGMAC100_OFFSET_APTC 0x34 +#define FTGMAC100_OFFSET_DBLAC 0x38 +#define FTGMAC100_OFFSET_DMAFIFOS 0x3c +#define FTGMAC100_OFFSET_REVR 0x40 +#define FTGMAC100_OFFSET_FEAR 0x44 +#define FTGMAC100_OFFSET_TPAFCR 0x48 +#define FTGMAC100_OFFSET_RBSR 0x4c +#define FTGMAC100_OFFSET_MACCR 0x50 +#define FTGMAC100_OFFSET_MACSR 0x54 +#define FTGMAC100_OFFSET_TM 0x58 +#define FTGMAC100_OFFSET_PHYCR 0x60 +#define FTGMAC100_OFFSET_PHYDATA 0x64 +#define FTGMAC100_OFFSET_FCR 0x68 +#define FTGMAC100_OFFSET_BPR 0x6c +#define FTGMAC100_OFFSET_WOLCR 0x70 +#define FTGMAC100_OFFSET_WOLSR 0x74 +#define FTGMAC100_OFFSET_WFCRC 0x78 +#define FTGMAC100_OFFSET_WFBM1 0x80 +#define FTGMAC100_OFFSET_WFBM2 0x84 +#define FTGMAC100_OFFSET_WFBM3 0x88 +#define FTGMAC100_OFFSET_WFBM4 0x8c +#define FTGMAC100_OFFSET_NPTXR_PTR 0x90 +#define FTGMAC100_OFFSET_HPTXR_PTR 0x94 +#define FTGMAC100_OFFSET_RXR_PTR 0x98 +#define FTGMAC100_OFFSET_TX 0xa0 +#define FTGMAC100_OFFSET_TX_MCOL_SCOL 0xa4 +#define FTGMAC100_OFFSET_TX_ECOL_FAIL 0xa8 +#define FTGMAC100_OFFSET_TX_LCOL_UND 0xac +#define FTGMAC100_OFFSET_RX 0xb0 +#define FTGMAC100_OFFSET_RX_BC 0xb4 +#define FTGMAC100_OFFSET_RX_MC 0xb8 +#define FTGMAC100_OFFSET_RX_PF_AEP 0xbc +#define FTGMAC100_OFFSET_RX_RUNT 0xc0 +#define FTGMAC100_OFFSET_RX_CRCER_FTL 0xc4 +#define FTGMAC100_OFFSET_RX_COL_LOST 0xc8 + +/* + * Interrupt status register & interrupt enable register + */ +#define FTGMAC100_INT_RPKT_BUF (1 << 0) +#define FTGMAC100_INT_RPKT_FIFO (1 << 1) +#define FTGMAC100_INT_NO_RXBUF (1 << 2) +#define FTGMAC100_INT_RPKT_LOST (1 << 3) +#define FTGMAC100_INT_XPKT_ETH (1 << 4) +#define FTGMAC100_INT_XPKT_FIFO (1 << 5) +#define FTGMAC100_INT_NO_NPTXBUF (1 << 6) +#define FTGMAC100_INT_XPKT_LOST (1 << 7) +#define FTGMAC100_INT_AHB_ERR (1 << 8) +#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) +#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) + +/* + * Interrupt timer control register + */ +#define FTGMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0) +#define FTGMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4) +#define FTGMAC100_ITC_RXINT_TIME_SEL (1 << 7) +#define FTGMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8) +#define FTGMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12) +#define FTGMAC100_ITC_TXINT_TIME_SEL (1 << 15) + +/* + * Automatic polling timer control register + */ +#define FTGMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) +#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) +#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * DMA burst length and arbitration control register + */ +#define FTGMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 0) +#define FTGMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 3) +#define FTGMAC100_DBLAC_RX_THR_EN (1 << 6) +#define FTGMAC100_DBLAC_RXBURST_SIZE(x) (((x) & 0x3) << 8) +#define FTGMAC100_DBLAC_TXBURST_SIZE(x) (((x) & 0x3) << 10) +#define FTGMAC100_DBLAC_RXDES_SIZE(x) (((x) & 0xf) << 12) +#define FTGMAC100_DBLAC_TXDES_SIZE(x) (((x) & 0xf) << 16) +#define FTGMAC100_DBLAC_IFG_CNT(x) (((x) & 0x7) << 20) +#define FTGMAC100_DBLAC_IFG_INC (1 << 23) + +/* + * DMA FIFO status register + */ +#define FTGMAC100_DMAFIFOS_RXDMA1_SM(dmafifos) ((dmafifos) & 0xf) +#define FTGMAC100_DMAFIFOS_RXDMA2_SM(dmafifos) (((dmafifos) >> 4) & 0xf) +#define FTGMAC100_DMAFIFOS_RXDMA3_SM(dmafifos) (((dmafifos) >> 8) & 0x7) +#define FTGMAC100_DMAFIFOS_TXDMA1_SM(dmafifos) (((dmafifos) >> 12) & 0xf) +#define FTGMAC100_DMAFIFOS_TXDMA2_SM(dmafifos) (((dmafifos) >> 16) & 0x3) +#define FTGMAC100_DMAFIFOS_TXDMA3_SM(dmafifos) (((dmafifos) >> 18) & 0xf) +#define FTGMAC100_DMAFIFOS_RXFIFO_EMPTY (1 << 26) +#define FTGMAC100_DMAFIFOS_TXFIFO_EMPTY (1 << 27) +#define FTGMAC100_DMAFIFOS_RXDMA_GRANT (1 << 28) +#define FTGMAC100_DMAFIFOS_TXDMA_GRANT (1 << 29) +#define FTGMAC100_DMAFIFOS_RXDMA_REQ (1 << 30) +#define FTGMAC100_DMAFIFOS_TXDMA_REQ (1 << 31) + +/* + * Receive buffer size register + */ +#define FTGMAC100_RBSR_SIZE(x) ((x) & 0x3fff) + +/* + * MAC control register + */ +#define FTGMAC100_MACCR_TXDMA_EN (1 << 0) +#define FTGMAC100_MACCR_RXDMA_EN (1 << 1) +#define FTGMAC100_MACCR_TXMAC_EN (1 << 2) +#define FTGMAC100_MACCR_RXMAC_EN (1 << 3) +#define FTGMAC100_MACCR_RM_VLAN (1 << 4) +#define FTGMAC100_MACCR_HPTXR_EN (1 << 5) +#define FTGMAC100_MACCR_LOOP_EN (1 << 6) +#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7) +#define FTGMAC100_MACCR_FULLDUP (1 << 8) +#define FTGMAC100_MACCR_GIGA_MODE (1 << 9) +#define FTGMAC100_MACCR_CRC_APD (1 << 10) +#define FTGMAC100_MACCR_RX_RUNT (1 << 12) +#define FTGMAC100_MACCR_JUMBO_LF (1 << 13) +#define FTGMAC100_MACCR_RX_ALL (1 << 14) +#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15) +#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17) +#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18) +#define FTGMAC100_MACCR_FAST_MODE (1 << 19) +#define FTGMAC100_MACCR_SW_RST (1 << 31) + +/* + * PHY control register + */ +#define FTGMAC100_PHYCR_MDC_CYCTHR_MASK 0x3f +#define FTGMAC100_PHYCR_MDC_CYCTHR(x) ((x) & 0x3f) +#define FTGMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) +#define FTGMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) +#define FTGMAC100_PHYCR_MIIRD (1 << 26) +#define FTGMAC100_PHYCR_MIIWR (1 << 27) + +/* + * PHY data register + */ +#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) +#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff) + +/* + * Transmit descriptor, aligned to 16 bytes + */ +struct ftgmac100_txdes { + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* not used by HW */ + unsigned int txdes3; /* TXBUF_BADR */ +} __attribute__ ((aligned(16))); + +#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) +#define FTGMAC100_TXDES0_EDOTR (1 << 15) +#define FTGMAC100_TXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_TXDES0_LTS (1 << 28) +#define FTGMAC100_TXDES0_FTS (1 << 29) +#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff) +#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16) +#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17) +#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18) +#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19) +#define FTGMAC100_TXDES1_LLC (1 << 22) +#define FTGMAC100_TXDES1_TX2FIC (1 << 30) +#define FTGMAC100_TXDES1_TXIC (1 << 31) + +/* + * Receive descriptor, aligned to 16 bytes + */ +struct ftgmac100_rxdes { + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* not used by HW */ + unsigned int rxdes3; /* RXBUF_BADR */ +} __attribute__ ((aligned(16))); + +#define FTGMAC100_RXDES0_VDBC 0x3fff +#define FTGMAC100_RXDES0_EDORR (1 << 15) +#define FTGMAC100_RXDES0_MULTICAST (1 << 16) +#define FTGMAC100_RXDES0_BROADCAST (1 << 17) +#define FTGMAC100_RXDES0_RX_ERR (1 << 18) +#define FTGMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTGMAC100_RXDES0_FTL (1 << 20) +#define FTGMAC100_RXDES0_RUNT (1 << 21) +#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23) +#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24) +#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25) +#define FTGMAC100_RXDES0_LRS (1 << 28) +#define FTGMAC100_RXDES0_FRS (1 << 29) +#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) + +#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff +#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) +#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) +#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20) +#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20) +#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20) +#define FTGMAC100_RXDES1_LLC (1 << 22) +#define FTGMAC100_RXDES1_DF (1 << 23) +#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24) +#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25) +#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) +#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) + +#endif /* __FTGMAC100_H */ diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c new file mode 100644 index 000000000000..9bd7746cbfcf --- /dev/null +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -0,0 +1,1198 @@ +/* + * Faraday FTMAC100 10/100 Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ + +#if MAX_PKT_SIZE > 0x7ff +#error invalid MAX_PKT_SIZE +#endif + +#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE +#error invalid RX_BUF_SIZE +#endif + +/****************************************************************************** + * 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 __iomem *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; +}; + +static int ftmac100_alloc_rx_page(struct ftmac100 *priv, + struct ftmac100_rxdes *rxdes, gfp_t gfp); + +/****************************************************************************** + * 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) + +#define INT_MASK_ALL_DISABLED 0 + +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(INT_MASK_ALL_DISABLED, 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. + */ + udelay(500); + return 0; + } + + udelay(1000); + } + + 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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS); +} + +static bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS); +} + +static bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR); +} + +static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR); +} + +static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FTL); +} + +static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT); +} + +static bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST); +} + +static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes, + unsigned int size) +{ + rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR); + rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size)); +} + +static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes) +{ + rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR); +} + +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 page. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page) +{ + rxdes->rxdes3 = (unsigned int)page; +} + +static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes) +{ + return (struct page *)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; + struct page *page; + dma_addr_t map; + int length; + + 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; + } + + /* + * It is impossible to get multi-segment packets + * because we always provide big enough receive buffers. + */ + if (unlikely(!ftmac100_rxdes_last_segment(rxdes))) + BUG(); + + /* start processing */ + skb = netdev_alloc_skb_ip_align(netdev, 128); + 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++; + + map = ftmac100_rxdes_get_dma_addr(rxdes); + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + + length = ftmac100_rxdes_frame_length(rxdes); + page = ftmac100_rxdes_get_page(rxdes); + skb_fill_page_desc(skb, 0, page, 0, length); + skb->len += length; + skb->data_len += length; + skb->truesize += length; + __pskb_pull_tail(skb, min(length, 64)); + + ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + + ftmac100_rx_pointer_advance(priv); + + skb->protocol = eth_type_trans(skb, netdev); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + + /* push packet to protocol stack */ + netif_receive_skb(skb); + + (*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 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR); + txdes->txdes2 = 0; + txdes->txdes3 = 0; +} + +static bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); +} + +static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes) +{ + /* + * Make sure dma own bit will not be set before any other + * descriptor fields. + */ + wmb(); + txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); +} + +static bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL); +} + +static bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL); +} + +static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR); +} + +static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS); +} + +static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS); +} + +static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC); +} + +static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes, + unsigned int len) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len)); +} + +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. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb) +{ + txdes->txdes3 = (unsigned int)skb; +} + +static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes) +{ + return (struct sk_buff *)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(skb); + + ftmac100_txdes_reset(txdes); + + ftmac100_tx_clean_pointer_advance(priv); + + spin_lock(&priv->tx_lock); + priv->tx_pending--; + spin_unlock(&priv->tx_lock); + netif_wake_queue(netdev); + + return true; +} + +static void ftmac100_tx_complete(struct ftmac100 *priv) +{ + while (ftmac100_tx_complete_packet(priv)) + ; +} + +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; + + txdes = ftmac100_current_txdes(priv); + ftmac100_tx_pointer_advance(priv); + + /* setup TX descriptor */ + 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); + + spin_lock(&priv->tx_lock); + priv->tx_pending++; + if (priv->tx_pending == TX_QUEUE_ENTRIES) + netif_stop_queue(netdev); + + /* start transmit */ + ftmac100_txdes_set_dma_own(txdes); + spin_unlock(&priv->tx_lock); + + ftmac100_txdma_start_polling(priv); + return NETDEV_TX_OK; +} + +/****************************************************************************** + * internal functions (buffer) + *****************************************************************************/ +static int ftmac100_alloc_rx_page(struct ftmac100 *priv, + struct ftmac100_rxdes *rxdes, gfp_t gfp) +{ + struct net_device *netdev = priv->netdev; + struct page *page; + dma_addr_t map; + + page = alloc_page(gfp); + if (!page) { + if (net_ratelimit()) + netdev_err(netdev, "failed to allocate rx page\n"); + return -ENOMEM; + } + + map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + if (net_ratelimit()) + netdev_err(netdev, "failed to map rx page\n"); + __free_page(page); + return -ENOMEM; + } + + ftmac100_rxdes_set_page(rxdes, page); + ftmac100_rxdes_set_dma_addr(rxdes, map); + ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE); + ftmac100_rxdes_set_dma_own(rxdes); + return 0; +} + +static void ftmac100_free_buffers(struct ftmac100 *priv) +{ + int i; + + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + struct page *page = ftmac100_rxdes_get_page(rxdes); + dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes); + + if (!page) + continue; + + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + __free_page(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); + dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes); + + if (!skb) + continue; + + 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); + if (!priv->descs) + 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++) { + struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + + if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) + goto err; + } + + /* 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; + + udelay(100); + } + + 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; + + udelay(100); + } + + 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_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_NORXBUF | FTMAC100_INT_RPKT_LOST | + FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) { + if (net_ratelimit()) + netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, + status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "", + status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", + status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "", + status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); + + if (status & FTMAC100_INT_NORXBUF) { + /* RX buffer unavailable */ + netdev->stats.rx_over_errors++; + } + + if (status & FTMAC100_INT_RPKT_LOST) { + /* received packet lost due to RX FIFO full */ + netdev->stats.rx_fifo_errors++; + } + + if (status & FTMAC100_INT_PHYSTS_CHG) { + /* PHY link status change */ + 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_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(*priv)); + if (!netdev) { + err = -ENOMEM; + goto err_alloc_etherdev; + } + + 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, resource_size(res), + dev_name(&pdev->dev)); + if (!priv->res) { + dev_err(&pdev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_req_mem; + } + + priv->base = ioremap(res->start, resource_size(res)); + if (!priv->base) { + dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); + err = -EIO; + goto err_ioremap; + } + + 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_register_netdev; + } + + 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_register_netdev: + iounmap(priv->base); +err_ioremap: + release_resource(priv->res); +err_req_mem: + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_alloc_etherdev: + return err; +} + +static int __exit ftmac100_remove(struct platform_device *pdev) +{ + struct net_device *netdev; + struct ftmac100 *priv; + + netdev = platform_get_drvdata(pdev); + priv = netdev_priv(netdev); + + unregister_netdev(netdev); + + iounmap(priv->base); + release_resource(priv->res); + + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); + return 0; +} + +static struct platform_driver ftmac100_driver = { + .probe = ftmac100_probe, + .remove = __exit_p(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 "); +MODULE_DESCRIPTION("FTMAC100 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/faraday/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h new file mode 100644 index 000000000000..46a0c47b1ee1 --- /dev/null +++ b/drivers/net/ethernet/faraday/ftmac100.h @@ -0,0 +1,180 @@ +/* + * Faraday FTMAC100 10/100 Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang + * + * 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 */ diff --git a/drivers/net/ftgmac100.c b/drivers/net/ftgmac100.c deleted file mode 100644 index 54709af917e9..000000000000 --- a/drivers/net/ftgmac100.c +++ /dev/null @@ -1,1365 +0,0 @@ -/* - * Faraday FTGMAC100 Gigabit Ethernet - * - * (C) Copyright 2009-2011 Faraday Technology - * Po-Yu Chuang - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ftgmac100.h" - -#define DRV_NAME "ftgmac100" -#define DRV_VERSION "0.7" - -#define RX_QUEUE_ENTRIES 256 /* must be power of 2 */ -#define TX_QUEUE_ENTRIES 512 /* must be power of 2 */ - -#define MAX_PKT_SIZE 1518 -#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */ - -/****************************************************************************** - * private data - *****************************************************************************/ -struct ftgmac100_descs { - struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; - struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; -}; - -struct ftgmac100 { - struct resource *res; - void __iomem *base; - int irq; - - struct ftgmac100_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_bus *mii_bus; - int phy_irq[PHY_MAX_ADDR]; - struct phy_device *phydev; - int old_speed; -}; - -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, gfp_t gfp); - -/****************************************************************************** - * internal functions (hardware register access) - *****************************************************************************/ -#define INT_MASK_ALL_ENABLED (FTGMAC100_INT_RPKT_LOST | \ - FTGMAC100_INT_XPKT_ETH | \ - FTGMAC100_INT_XPKT_LOST | \ - FTGMAC100_INT_AHB_ERR | \ - FTGMAC100_INT_PHYSTS_CHG | \ - FTGMAC100_INT_RPKT_BUF | \ - FTGMAC100_INT_NO_RXBUF) - -static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) -{ - iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); -} - -static void ftgmac100_set_rx_buffer_size(struct ftgmac100 *priv, - unsigned int size) -{ - size = FTGMAC100_RBSR_SIZE(size); - iowrite32(size, priv->base + FTGMAC100_OFFSET_RBSR); -} - -static void ftgmac100_set_normal_prio_tx_ring_base(struct ftgmac100 *priv, - dma_addr_t addr) -{ - iowrite32(addr, priv->base + FTGMAC100_OFFSET_NPTXR_BADR); -} - -static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv) -{ - iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); -} - -static int ftgmac100_reset_hw(struct ftgmac100 *priv) -{ - struct net_device *netdev = priv->netdev; - int i; - - /* NOTE: reset clears all registers */ - iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR); - for (i = 0; i < 5; i++) { - unsigned int maccr; - - maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); - if (!(maccr & FTGMAC100_MACCR_SW_RST)) - return 0; - - udelay(1000); - } - - netdev_err(netdev, "software reset failed\n"); - return -EIO; -} - -static void ftgmac100_set_mac(struct ftgmac100 *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 + FTGMAC100_OFFSET_MAC_MADR); - iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR); -} - -static void ftgmac100_init_hw(struct ftgmac100 *priv) -{ - /* setup ring buffer base registers */ - ftgmac100_set_rx_ring_base(priv, - priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, rxdes)); - ftgmac100_set_normal_prio_tx_ring_base(priv, - priv->descs_dma_addr + - offsetof(struct ftgmac100_descs, txdes)); - - ftgmac100_set_rx_buffer_size(priv, RX_BUF_SIZE); - - iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1), priv->base + FTGMAC100_OFFSET_APTC); - - ftgmac100_set_mac(priv, priv->netdev->dev_addr); -} - -#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \ - FTGMAC100_MACCR_RXDMA_EN | \ - FTGMAC100_MACCR_TXMAC_EN | \ - FTGMAC100_MACCR_RXMAC_EN | \ - FTGMAC100_MACCR_FULLDUP | \ - FTGMAC100_MACCR_CRC_APD | \ - FTGMAC100_MACCR_RX_RUNT | \ - FTGMAC100_MACCR_RX_BROADPKT) - -static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed) -{ - int maccr = MACCR_ENABLE_ALL; - - switch (speed) { - default: - case 10: - break; - - case 100: - maccr |= FTGMAC100_MACCR_FAST_MODE; - break; - - case 1000: - maccr |= FTGMAC100_MACCR_GIGA_MODE; - break; - } - - iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); -} - -static void ftgmac100_stop_hw(struct ftgmac100 *priv) -{ - iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); -} - -/****************************************************************************** - * internal functions (receive descriptor) - *****************************************************************************/ -static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS); -} - -static bool ftgmac100_rxdes_last_segment(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_LRS); -} - -static bool ftgmac100_rxdes_packet_ready(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY); -} - -static void ftgmac100_rxdes_set_dma_own(struct ftgmac100_rxdes *rxdes) -{ - /* clear status bits */ - rxdes->rxdes0 &= cpu_to_le32(FTGMAC100_RXDES0_EDORR); -} - -static bool ftgmac100_rxdes_rx_error(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ERR); -} - -static bool ftgmac100_rxdes_crc_error(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_CRC_ERR); -} - -static bool ftgmac100_rxdes_frame_too_long(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FTL); -} - -static bool ftgmac100_rxdes_runt(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RUNT); -} - -static bool ftgmac100_rxdes_odd_nibble(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RX_ODD_NB); -} - -static unsigned int ftgmac100_rxdes_data_length(struct ftgmac100_rxdes *rxdes) -{ - return le32_to_cpu(rxdes->rxdes0) & FTGMAC100_RXDES0_VDBC; -} - -static bool ftgmac100_rxdes_multicast(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_MULTICAST); -} - -static void ftgmac100_rxdes_set_end_of_ring(struct ftgmac100_rxdes *rxdes) -{ - rxdes->rxdes0 |= cpu_to_le32(FTGMAC100_RXDES0_EDORR); -} - -static void ftgmac100_rxdes_set_dma_addr(struct ftgmac100_rxdes *rxdes, - dma_addr_t addr) -{ - rxdes->rxdes3 = cpu_to_le32(addr); -} - -static dma_addr_t ftgmac100_rxdes_get_dma_addr(struct ftgmac100_rxdes *rxdes) -{ - return le32_to_cpu(rxdes->rxdes3); -} - -static bool ftgmac100_rxdes_is_tcp(struct ftgmac100_rxdes *rxdes) -{ - return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == - cpu_to_le32(FTGMAC100_RXDES1_PROT_TCPIP); -} - -static bool ftgmac100_rxdes_is_udp(struct ftgmac100_rxdes *rxdes) -{ - return (rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_PROT_MASK)) == - cpu_to_le32(FTGMAC100_RXDES1_PROT_UDPIP); -} - -static bool ftgmac100_rxdes_tcpcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_TCP_CHKSUM_ERR); -} - -static bool ftgmac100_rxdes_udpcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_UDP_CHKSUM_ERR); -} - -static bool ftgmac100_rxdes_ipcs_err(struct ftgmac100_rxdes *rxdes) -{ - return rxdes->rxdes1 & cpu_to_le32(FTGMAC100_RXDES1_IP_CHKSUM_ERR); -} - -/* - * rxdes2 is not used by hardware. We use it to keep track of page. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftgmac100_rxdes_set_page(struct ftgmac100_rxdes *rxdes, struct page *page) -{ - rxdes->rxdes2 = (unsigned int)page; -} - -static struct page *ftgmac100_rxdes_get_page(struct ftgmac100_rxdes *rxdes) -{ - return (struct page *)rxdes->rxdes2; -} - -/****************************************************************************** - * internal functions (receive) - *****************************************************************************/ -static int ftgmac100_next_rx_pointer(int pointer) -{ - return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); -} - -static void ftgmac100_rx_pointer_advance(struct ftgmac100 *priv) -{ - priv->rx_pointer = ftgmac100_next_rx_pointer(priv->rx_pointer); -} - -static struct ftgmac100_rxdes *ftgmac100_current_rxdes(struct ftgmac100 *priv) -{ - return &priv->descs->rxdes[priv->rx_pointer]; -} - -static struct ftgmac100_rxdes * -ftgmac100_rx_locate_first_segment(struct ftgmac100 *priv) -{ - struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); - - while (ftgmac100_rxdes_packet_ready(rxdes)) { - if (ftgmac100_rxdes_first_segment(rxdes)) - return rxdes; - - ftgmac100_rxdes_set_dma_own(rxdes); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } - - return NULL; -} - -static bool ftgmac100_rx_packet_error(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes) -{ - struct net_device *netdev = priv->netdev; - bool error = false; - - if (unlikely(ftgmac100_rxdes_rx_error(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx err\n"); - - netdev->stats.rx_errors++; - error = true; - } - - if (unlikely(ftgmac100_rxdes_crc_error(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx crc err\n"); - - netdev->stats.rx_crc_errors++; - error = true; - } else if (unlikely(ftgmac100_rxdes_ipcs_err(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx IP checksum err\n"); - - error = true; - } - - if (unlikely(ftgmac100_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(ftgmac100_rxdes_runt(rxdes))) { - if (net_ratelimit()) - netdev_info(netdev, "rx runt\n"); - - netdev->stats.rx_length_errors++; - error = true; - } else if (unlikely(ftgmac100_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 ftgmac100_rx_drop_packet(struct ftgmac100 *priv) -{ - struct net_device *netdev = priv->netdev; - struct ftgmac100_rxdes *rxdes = ftgmac100_current_rxdes(priv); - bool done = false; - - if (net_ratelimit()) - netdev_dbg(netdev, "drop packet %p\n", rxdes); - - do { - if (ftgmac100_rxdes_last_segment(rxdes)) - done = true; - - ftgmac100_rxdes_set_dma_own(rxdes); - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } while (!done && ftgmac100_rxdes_packet_ready(rxdes)); - - netdev->stats.rx_dropped++; -} - -static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) -{ - struct net_device *netdev = priv->netdev; - struct ftgmac100_rxdes *rxdes; - struct sk_buff *skb; - bool done = false; - - rxdes = ftgmac100_rx_locate_first_segment(priv); - if (!rxdes) - return false; - - if (unlikely(ftgmac100_rx_packet_error(priv, rxdes))) { - ftgmac100_rx_drop_packet(priv); - return true; - } - - /* start processing */ - skb = netdev_alloc_skb_ip_align(netdev, 128); - if (unlikely(!skb)) { - if (net_ratelimit()) - netdev_err(netdev, "rx skb alloc failed\n"); - - ftgmac100_rx_drop_packet(priv); - return true; - } - - if (unlikely(ftgmac100_rxdes_multicast(rxdes))) - netdev->stats.multicast++; - - /* - * It seems that HW does checksum incorrectly with fragmented packets, - * so we are conservative here - if HW checksum error, let software do - * the checksum again. - */ - if ((ftgmac100_rxdes_is_tcp(rxdes) && !ftgmac100_rxdes_tcpcs_err(rxdes)) || - (ftgmac100_rxdes_is_udp(rxdes) && !ftgmac100_rxdes_udpcs_err(rxdes))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - - do { - dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); - struct page *page = ftgmac100_rxdes_get_page(rxdes); - unsigned int size; - - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - - size = ftgmac100_rxdes_data_length(rxdes); - skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags, page, 0, size); - - skb->len += size; - skb->data_len += size; - skb->truesize += size; - - if (ftgmac100_rxdes_last_segment(rxdes)) - done = true; - - ftgmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - - ftgmac100_rx_pointer_advance(priv); - rxdes = ftgmac100_current_rxdes(priv); - } while (!done); - - __pskb_pull_tail(skb, min(skb->len, 64U)); - skb->protocol = eth_type_trans(skb, netdev); - - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; - - /* push packet to protocol stack */ - napi_gro_receive(&priv->napi, skb); - - (*processed)++; - return true; -} - -/****************************************************************************** - * internal functions (transmit descriptor) - *****************************************************************************/ -static void ftgmac100_txdes_reset(struct ftgmac100_txdes *txdes) -{ - /* clear all except end of ring bit */ - txdes->txdes0 &= cpu_to_le32(FTGMAC100_TXDES0_EDOTR); - txdes->txdes1 = 0; - txdes->txdes2 = 0; - txdes->txdes3 = 0; -} - -static bool ftgmac100_txdes_owned_by_dma(struct ftgmac100_txdes *txdes) -{ - return txdes->txdes0 & cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); -} - -static void ftgmac100_txdes_set_dma_own(struct ftgmac100_txdes *txdes) -{ - /* - * Make sure dma own bit will not be set before any other - * descriptor fields. - */ - wmb(); - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXDMA_OWN); -} - -static void ftgmac100_txdes_set_end_of_ring(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_EDOTR); -} - -static void ftgmac100_txdes_set_first_segment(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_FTS); -} - -static void ftgmac100_txdes_set_last_segment(struct ftgmac100_txdes *txdes) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_LTS); -} - -static void ftgmac100_txdes_set_buffer_size(struct ftgmac100_txdes *txdes, - unsigned int len) -{ - txdes->txdes0 |= cpu_to_le32(FTGMAC100_TXDES0_TXBUF_SIZE(len)); -} - -static void ftgmac100_txdes_set_txint(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TXIC); -} - -static void ftgmac100_txdes_set_tcpcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_TCP_CHKSUM); -} - -static void ftgmac100_txdes_set_udpcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_UDP_CHKSUM); -} - -static void ftgmac100_txdes_set_ipcs(struct ftgmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTGMAC100_TXDES1_IP_CHKSUM); -} - -static void ftgmac100_txdes_set_dma_addr(struct ftgmac100_txdes *txdes, - dma_addr_t addr) -{ - txdes->txdes3 = cpu_to_le32(addr); -} - -static dma_addr_t ftgmac100_txdes_get_dma_addr(struct ftgmac100_txdes *txdes) -{ - return le32_to_cpu(txdes->txdes3); -} - -/* - * txdes2 is not used by hardware. We use it to keep track of socket buffer. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftgmac100_txdes_set_skb(struct ftgmac100_txdes *txdes, - struct sk_buff *skb) -{ - txdes->txdes2 = (unsigned int)skb; -} - -static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes) -{ - return (struct sk_buff *)txdes->txdes2; -} - -/****************************************************************************** - * internal functions (transmit) - *****************************************************************************/ -static int ftgmac100_next_tx_pointer(int pointer) -{ - return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); -} - -static void ftgmac100_tx_pointer_advance(struct ftgmac100 *priv) -{ - priv->tx_pointer = ftgmac100_next_tx_pointer(priv->tx_pointer); -} - -static void ftgmac100_tx_clean_pointer_advance(struct ftgmac100 *priv) -{ - priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv->tx_clean_pointer); -} - -static struct ftgmac100_txdes *ftgmac100_current_txdes(struct ftgmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_pointer]; -} - -static struct ftgmac100_txdes * -ftgmac100_current_clean_txdes(struct ftgmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_clean_pointer]; -} - -static bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv) -{ - struct net_device *netdev = priv->netdev; - struct ftgmac100_txdes *txdes; - struct sk_buff *skb; - dma_addr_t map; - - if (priv->tx_pending == 0) - return false; - - txdes = ftgmac100_current_clean_txdes(priv); - - if (ftgmac100_txdes_owned_by_dma(txdes)) - return false; - - skb = ftgmac100_txdes_get_skb(txdes); - map = ftgmac100_txdes_get_dma_addr(txdes); - - 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(skb); - - ftgmac100_txdes_reset(txdes); - - ftgmac100_tx_clean_pointer_advance(priv); - - spin_lock(&priv->tx_lock); - priv->tx_pending--; - spin_unlock(&priv->tx_lock); - netif_wake_queue(netdev); - - return true; -} - -static void ftgmac100_tx_complete(struct ftgmac100 *priv) -{ - while (ftgmac100_tx_complete_packet(priv)) - ; -} - -static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, - dma_addr_t map) -{ - struct net_device *netdev = priv->netdev; - struct ftgmac100_txdes *txdes; - unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; - - txdes = ftgmac100_current_txdes(priv); - ftgmac100_tx_pointer_advance(priv); - - /* setup TX descriptor */ - ftgmac100_txdes_set_skb(txdes, skb); - ftgmac100_txdes_set_dma_addr(txdes, map); - ftgmac100_txdes_set_buffer_size(txdes, len); - - ftgmac100_txdes_set_first_segment(txdes); - ftgmac100_txdes_set_last_segment(txdes); - ftgmac100_txdes_set_txint(txdes); - if (skb->ip_summed == CHECKSUM_PARTIAL) { - __be16 protocol = skb->protocol; - - if (protocol == cpu_to_be16(ETH_P_IP)) { - u8 ip_proto = ip_hdr(skb)->protocol; - - ftgmac100_txdes_set_ipcs(txdes); - if (ip_proto == IPPROTO_TCP) - ftgmac100_txdes_set_tcpcs(txdes); - else if (ip_proto == IPPROTO_UDP) - ftgmac100_txdes_set_udpcs(txdes); - } - } - - spin_lock(&priv->tx_lock); - priv->tx_pending++; - if (priv->tx_pending == TX_QUEUE_ENTRIES) - netif_stop_queue(netdev); - - /* start transmit */ - ftgmac100_txdes_set_dma_own(txdes); - spin_unlock(&priv->tx_lock); - - ftgmac100_txdma_normal_prio_start_polling(priv); - - return NETDEV_TX_OK; -} - -/****************************************************************************** - * internal functions (buffer) - *****************************************************************************/ -static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, - struct ftgmac100_rxdes *rxdes, gfp_t gfp) -{ - struct net_device *netdev = priv->netdev; - struct page *page; - dma_addr_t map; - - page = alloc_page(gfp); - if (!page) { - if (net_ratelimit()) - netdev_err(netdev, "failed to allocate rx page\n"); - return -ENOMEM; - } - - map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - if (net_ratelimit()) - netdev_err(netdev, "failed to map rx page\n"); - __free_page(page); - return -ENOMEM; - } - - ftgmac100_rxdes_set_page(rxdes, page); - ftgmac100_rxdes_set_dma_addr(rxdes, map); - ftgmac100_rxdes_set_dma_own(rxdes); - return 0; -} - -static void ftgmac100_free_buffers(struct ftgmac100 *priv) -{ - int i; - - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - struct page *page = ftgmac100_rxdes_get_page(rxdes); - dma_addr_t map = ftgmac100_rxdes_get_dma_addr(rxdes); - - if (!page) - continue; - - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - __free_page(page); - } - - for (i = 0; i < TX_QUEUE_ENTRIES; i++) { - struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; - struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes); - dma_addr_t map = ftgmac100_txdes_get_dma_addr(txdes); - - if (!skb) - continue; - - dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); - dev_kfree_skb(skb); - } - - dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs), - priv->descs, priv->descs_dma_addr); -} - -static int ftgmac100_alloc_buffers(struct ftgmac100 *priv) -{ - int i; - - priv->descs = dma_alloc_coherent(priv->dev, - sizeof(struct ftgmac100_descs), - &priv->descs_dma_addr, GFP_KERNEL); - if (!priv->descs) - return -ENOMEM; - - memset(priv->descs, 0, sizeof(struct ftgmac100_descs)); - - /* initialize RX ring */ - ftgmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); - - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - - if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) - goto err; - } - - /* initialize TX ring */ - ftgmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]); - return 0; - -err: - ftgmac100_free_buffers(priv); - return -ENOMEM; -} - -/****************************************************************************** - * internal functions (mdio) - *****************************************************************************/ -static void ftgmac100_adjust_link(struct net_device *netdev) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - struct phy_device *phydev = priv->phydev; - int ier; - - if (phydev->speed == priv->old_speed) - return; - - priv->old_speed = phydev->speed; - - ier = ioread32(priv->base + FTGMAC100_OFFSET_IER); - - /* disable all interrupts */ - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - - netif_stop_queue(netdev); - ftgmac100_stop_hw(priv); - - netif_start_queue(netdev); - ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv, phydev->speed); - - /* re-enable interrupts */ - iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER); -} - -static int ftgmac100_mii_probe(struct ftgmac100 *priv) -{ - struct net_device *netdev = priv->netdev; - struct phy_device *phydev = NULL; - int i; - - /* search for connect PHY device */ - for (i = 0; i < PHY_MAX_ADDR; i++) { - struct phy_device *tmp = priv->mii_bus->phy_map[i]; - - if (tmp) { - phydev = tmp; - break; - } - } - - /* now we are supposed to have a proper phydev, to attach to... */ - if (!phydev) { - netdev_info(netdev, "%s: no PHY found\n", netdev->name); - return -ENODEV; - } - - phydev = phy_connect(netdev, dev_name(&phydev->dev), - &ftgmac100_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); - - if (IS_ERR(phydev)) { - netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name); - return PTR_ERR(phydev); - } - - priv->phydev = phydev; - return 0; -} - -/****************************************************************************** - * struct mii_bus functions - *****************************************************************************/ -static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) -{ - struct net_device *netdev = bus->priv; - struct ftgmac100 *priv = netdev_priv(netdev); - unsigned int phycr; - int i; - - phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); - - /* preserve MDC cycle threshold */ - phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; - - phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) | - FTGMAC100_PHYCR_REGAD(regnum) | - FTGMAC100_PHYCR_MIIRD; - - iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR); - - for (i = 0; i < 10; i++) { - phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); - - if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) { - int data; - - data = ioread32(priv->base + FTGMAC100_OFFSET_PHYDATA); - return FTGMAC100_PHYDATA_MIIRDATA(data); - } - - udelay(100); - } - - netdev_err(netdev, "mdio read timed out\n"); - return -EIO; -} - -static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr, - int regnum, u16 value) -{ - struct net_device *netdev = bus->priv; - struct ftgmac100 *priv = netdev_priv(netdev); - unsigned int phycr; - int data; - int i; - - phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); - - /* preserve MDC cycle threshold */ - phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK; - - phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) | - FTGMAC100_PHYCR_REGAD(regnum) | - FTGMAC100_PHYCR_MIIWR; - - data = FTGMAC100_PHYDATA_MIIWDATA(value); - - iowrite32(data, priv->base + FTGMAC100_OFFSET_PHYDATA); - iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR); - - for (i = 0; i < 10; i++) { - phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR); - - if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) - return 0; - - udelay(100); - } - - netdev_err(netdev, "mdio write timed out\n"); - return -EIO; -} - -static int ftgmac100_mdiobus_reset(struct mii_bus *bus) -{ - return 0; -} - -/****************************************************************************** - * struct ethtool_ops functions - *****************************************************************************/ -static void ftgmac100_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 ftgmac100_get_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_ethtool_gset(priv->phydev, cmd); -} - -static int ftgmac100_set_settings(struct net_device *netdev, - struct ethtool_cmd *cmd) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_ethtool_sset(priv->phydev, cmd); -} - -static const struct ethtool_ops ftgmac100_ethtool_ops = { - .set_settings = ftgmac100_set_settings, - .get_settings = ftgmac100_get_settings, - .get_drvinfo = ftgmac100_get_drvinfo, - .get_link = ethtool_op_get_link, -}; - -/****************************************************************************** - * interrupt handler - *****************************************************************************/ -static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) -{ - struct net_device *netdev = dev_id; - struct ftgmac100 *priv = netdev_priv(netdev); - - if (likely(netif_running(netdev))) { - /* Disable interrupts for polling */ - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - napi_schedule(&priv->napi); - } - - return IRQ_HANDLED; -} - -/****************************************************************************** - * struct napi_struct functions - *****************************************************************************/ -static int ftgmac100_poll(struct napi_struct *napi, int budget) -{ - struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); - struct net_device *netdev = priv->netdev; - unsigned int status; - bool completed = true; - int rx = 0; - - status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); - iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); - - if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) { - /* - * FTGMAC100_INT_RPKT_BUF: - * RX DMA has received packets into RX buffer successfully - * - * FTGMAC100_INT_NO_RXBUF: - * RX buffer unavailable - */ - bool retry; - - do { - retry = ftgmac100_rx_packet(priv, &rx); - } while (retry && rx < budget); - - if (retry && rx == budget) - completed = false; - } - - if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) { - /* - * FTGMAC100_INT_XPKT_ETH: - * packet transmitted to ethernet successfully - * - * FTGMAC100_INT_XPKT_LOST: - * packet transmitted to ethernet lost due to late - * collision or excessive collision - */ - ftgmac100_tx_complete(priv); - } - - if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST | - FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) { - if (net_ratelimit()) - netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, - status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "", - status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", - status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "", - status & FTGMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); - - if (status & FTGMAC100_INT_NO_RXBUF) { - /* RX buffer unavailable */ - netdev->stats.rx_over_errors++; - } - - if (status & FTGMAC100_INT_RPKT_LOST) { - /* received packet lost due to RX FIFO full */ - netdev->stats.rx_fifo_errors++; - } - } - - if (completed) { - napi_complete(napi); - - /* enable all interrupts */ - iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); - } - - return rx; -} - -/****************************************************************************** - * struct net_device_ops functions - *****************************************************************************/ -static int ftgmac100_open(struct net_device *netdev) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - int err; - - err = ftgmac100_alloc_buffers(priv); - if (err) { - netdev_err(netdev, "failed to allocate buffers\n"); - goto err_alloc; - } - - err = request_irq(priv->irq, ftgmac100_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 = ftgmac100_reset_hw(priv); - if (err) - goto err_hw; - - ftgmac100_init_hw(priv); - ftgmac100_start_hw(priv, 10); - - phy_start(priv->phydev); - - napi_enable(&priv->napi); - netif_start_queue(netdev); - - /* enable all interrupts */ - iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER); - return 0; - -err_hw: - free_irq(priv->irq, netdev); -err_irq: - ftgmac100_free_buffers(priv); -err_alloc: - return err; -} - -static int ftgmac100_stop(struct net_device *netdev) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - /* disable all interrupts */ - iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); - - netif_stop_queue(netdev); - napi_disable(&priv->napi); - phy_stop(priv->phydev); - - ftgmac100_stop_hw(priv); - free_irq(priv->irq, netdev); - ftgmac100_free_buffers(priv); - - return 0; -} - -static int ftgmac100_hard_start_xmit(struct sk_buff *skb, - struct net_device *netdev) -{ - struct ftgmac100 *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 ftgmac100_xmit(priv, skb, map); -} - -/* optional */ -static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) -{ - struct ftgmac100 *priv = netdev_priv(netdev); - - return phy_mii_ioctl(priv->phydev, ifr, cmd); -} - -static const struct net_device_ops ftgmac100_netdev_ops = { - .ndo_open = ftgmac100_open, - .ndo_stop = ftgmac100_stop, - .ndo_start_xmit = ftgmac100_hard_start_xmit, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, - .ndo_do_ioctl = ftgmac100_do_ioctl, -}; - -/****************************************************************************** - * struct platform_driver functions - *****************************************************************************/ -static int ftgmac100_probe(struct platform_device *pdev) -{ - struct resource *res; - int irq; - struct net_device *netdev; - struct ftgmac100 *priv; - int err; - int i; - - 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(*priv)); - if (!netdev) { - err = -ENOMEM; - goto err_alloc_etherdev; - } - - SET_NETDEV_DEV(netdev, &pdev->dev); - - SET_ETHTOOL_OPS(netdev, &ftgmac100_ethtool_ops); - netdev->netdev_ops = &ftgmac100_netdev_ops; - netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO; - - 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, ftgmac100_poll, 64); - - /* map io memory */ - priv->res = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - if (!priv->res) { - dev_err(&pdev->dev, "Could not reserve memory region\n"); - err = -ENOMEM; - goto err_req_mem; - } - - priv->base = ioremap(res->start, resource_size(res)); - if (!priv->base) { - dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); - err = -EIO; - goto err_ioremap; - } - - priv->irq = irq; - - /* initialize mdio bus */ - priv->mii_bus = mdiobus_alloc(); - if (!priv->mii_bus) { - err = -EIO; - goto err_alloc_mdiobus; - } - - priv->mii_bus->name = "ftgmac100_mdio"; - snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii"); - - priv->mii_bus->priv = netdev; - priv->mii_bus->read = ftgmac100_mdiobus_read; - priv->mii_bus->write = ftgmac100_mdiobus_write; - priv->mii_bus->reset = ftgmac100_mdiobus_reset; - priv->mii_bus->irq = priv->phy_irq; - - for (i = 0; i < PHY_MAX_ADDR; i++) - priv->mii_bus->irq[i] = PHY_POLL; - - err = mdiobus_register(priv->mii_bus); - if (err) { - dev_err(&pdev->dev, "Cannot register MDIO bus!\n"); - goto err_register_mdiobus; - } - - err = ftgmac100_mii_probe(priv); - if (err) { - dev_err(&pdev->dev, "MII Probe failed!\n"); - goto err_mii_probe; - } - - /* register network device */ - err = register_netdev(netdev); - if (err) { - dev_err(&pdev->dev, "Failed to register netdev\n"); - goto err_register_netdev; - } - - 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_register_netdev: - phy_disconnect(priv->phydev); -err_mii_probe: - mdiobus_unregister(priv->mii_bus); -err_register_mdiobus: - mdiobus_free(priv->mii_bus); -err_alloc_mdiobus: - iounmap(priv->base); -err_ioremap: - release_resource(priv->res); -err_req_mem: - netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); - free_netdev(netdev); -err_alloc_etherdev: - return err; -} - -static int __exit ftgmac100_remove(struct platform_device *pdev) -{ - struct net_device *netdev; - struct ftgmac100 *priv; - - netdev = platform_get_drvdata(pdev); - priv = netdev_priv(netdev); - - unregister_netdev(netdev); - - phy_disconnect(priv->phydev); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); - - iounmap(priv->base); - release_resource(priv->res); - - netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); - free_netdev(netdev); - return 0; -} - -static struct platform_driver ftgmac100_driver = { - .probe = ftgmac100_probe, - .remove = __exit_p(ftgmac100_remove), - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, -}; - -/****************************************************************************** - * initialization / finalization - *****************************************************************************/ -static int __init ftgmac100_init(void) -{ - pr_info("Loading version " DRV_VERSION " ...\n"); - return platform_driver_register(&ftgmac100_driver); -} - -static void __exit ftgmac100_exit(void) -{ - platform_driver_unregister(&ftgmac100_driver); -} - -module_init(ftgmac100_init); -module_exit(ftgmac100_exit); - -MODULE_AUTHOR("Po-Yu Chuang "); -MODULE_DESCRIPTION("FTGMAC100 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ftgmac100.h b/drivers/net/ftgmac100.h deleted file mode 100644 index 13408d448b05..000000000000 --- a/drivers/net/ftgmac100.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Faraday FTGMAC100 Gigabit Ethernet - * - * (C) Copyright 2009-2011 Faraday Technology - * Po-Yu Chuang - * - * 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 __FTGMAC100_H -#define __FTGMAC100_H - -#define FTGMAC100_OFFSET_ISR 0x00 -#define FTGMAC100_OFFSET_IER 0x04 -#define FTGMAC100_OFFSET_MAC_MADR 0x08 -#define FTGMAC100_OFFSET_MAC_LADR 0x0c -#define FTGMAC100_OFFSET_MAHT0 0x10 -#define FTGMAC100_OFFSET_MAHT1 0x14 -#define FTGMAC100_OFFSET_NPTXPD 0x18 -#define FTGMAC100_OFFSET_RXPD 0x1c -#define FTGMAC100_OFFSET_NPTXR_BADR 0x20 -#define FTGMAC100_OFFSET_RXR_BADR 0x24 -#define FTGMAC100_OFFSET_HPTXPD 0x28 -#define FTGMAC100_OFFSET_HPTXR_BADR 0x2c -#define FTGMAC100_OFFSET_ITC 0x30 -#define FTGMAC100_OFFSET_APTC 0x34 -#define FTGMAC100_OFFSET_DBLAC 0x38 -#define FTGMAC100_OFFSET_DMAFIFOS 0x3c -#define FTGMAC100_OFFSET_REVR 0x40 -#define FTGMAC100_OFFSET_FEAR 0x44 -#define FTGMAC100_OFFSET_TPAFCR 0x48 -#define FTGMAC100_OFFSET_RBSR 0x4c -#define FTGMAC100_OFFSET_MACCR 0x50 -#define FTGMAC100_OFFSET_MACSR 0x54 -#define FTGMAC100_OFFSET_TM 0x58 -#define FTGMAC100_OFFSET_PHYCR 0x60 -#define FTGMAC100_OFFSET_PHYDATA 0x64 -#define FTGMAC100_OFFSET_FCR 0x68 -#define FTGMAC100_OFFSET_BPR 0x6c -#define FTGMAC100_OFFSET_WOLCR 0x70 -#define FTGMAC100_OFFSET_WOLSR 0x74 -#define FTGMAC100_OFFSET_WFCRC 0x78 -#define FTGMAC100_OFFSET_WFBM1 0x80 -#define FTGMAC100_OFFSET_WFBM2 0x84 -#define FTGMAC100_OFFSET_WFBM3 0x88 -#define FTGMAC100_OFFSET_WFBM4 0x8c -#define FTGMAC100_OFFSET_NPTXR_PTR 0x90 -#define FTGMAC100_OFFSET_HPTXR_PTR 0x94 -#define FTGMAC100_OFFSET_RXR_PTR 0x98 -#define FTGMAC100_OFFSET_TX 0xa0 -#define FTGMAC100_OFFSET_TX_MCOL_SCOL 0xa4 -#define FTGMAC100_OFFSET_TX_ECOL_FAIL 0xa8 -#define FTGMAC100_OFFSET_TX_LCOL_UND 0xac -#define FTGMAC100_OFFSET_RX 0xb0 -#define FTGMAC100_OFFSET_RX_BC 0xb4 -#define FTGMAC100_OFFSET_RX_MC 0xb8 -#define FTGMAC100_OFFSET_RX_PF_AEP 0xbc -#define FTGMAC100_OFFSET_RX_RUNT 0xc0 -#define FTGMAC100_OFFSET_RX_CRCER_FTL 0xc4 -#define FTGMAC100_OFFSET_RX_COL_LOST 0xc8 - -/* - * Interrupt status register & interrupt enable register - */ -#define FTGMAC100_INT_RPKT_BUF (1 << 0) -#define FTGMAC100_INT_RPKT_FIFO (1 << 1) -#define FTGMAC100_INT_NO_RXBUF (1 << 2) -#define FTGMAC100_INT_RPKT_LOST (1 << 3) -#define FTGMAC100_INT_XPKT_ETH (1 << 4) -#define FTGMAC100_INT_XPKT_FIFO (1 << 5) -#define FTGMAC100_INT_NO_NPTXBUF (1 << 6) -#define FTGMAC100_INT_XPKT_LOST (1 << 7) -#define FTGMAC100_INT_AHB_ERR (1 << 8) -#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) -#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) - -/* - * Interrupt timer control register - */ -#define FTGMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0) -#define FTGMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4) -#define FTGMAC100_ITC_RXINT_TIME_SEL (1 << 7) -#define FTGMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8) -#define FTGMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12) -#define FTGMAC100_ITC_TXINT_TIME_SEL (1 << 15) - -/* - * Automatic polling timer control register - */ -#define FTGMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) -#define FTGMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) -#define FTGMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) -#define FTGMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) - -/* - * DMA burst length and arbitration control register - */ -#define FTGMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 0) -#define FTGMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 3) -#define FTGMAC100_DBLAC_RX_THR_EN (1 << 6) -#define FTGMAC100_DBLAC_RXBURST_SIZE(x) (((x) & 0x3) << 8) -#define FTGMAC100_DBLAC_TXBURST_SIZE(x) (((x) & 0x3) << 10) -#define FTGMAC100_DBLAC_RXDES_SIZE(x) (((x) & 0xf) << 12) -#define FTGMAC100_DBLAC_TXDES_SIZE(x) (((x) & 0xf) << 16) -#define FTGMAC100_DBLAC_IFG_CNT(x) (((x) & 0x7) << 20) -#define FTGMAC100_DBLAC_IFG_INC (1 << 23) - -/* - * DMA FIFO status register - */ -#define FTGMAC100_DMAFIFOS_RXDMA1_SM(dmafifos) ((dmafifos) & 0xf) -#define FTGMAC100_DMAFIFOS_RXDMA2_SM(dmafifos) (((dmafifos) >> 4) & 0xf) -#define FTGMAC100_DMAFIFOS_RXDMA3_SM(dmafifos) (((dmafifos) >> 8) & 0x7) -#define FTGMAC100_DMAFIFOS_TXDMA1_SM(dmafifos) (((dmafifos) >> 12) & 0xf) -#define FTGMAC100_DMAFIFOS_TXDMA2_SM(dmafifos) (((dmafifos) >> 16) & 0x3) -#define FTGMAC100_DMAFIFOS_TXDMA3_SM(dmafifos) (((dmafifos) >> 18) & 0xf) -#define FTGMAC100_DMAFIFOS_RXFIFO_EMPTY (1 << 26) -#define FTGMAC100_DMAFIFOS_TXFIFO_EMPTY (1 << 27) -#define FTGMAC100_DMAFIFOS_RXDMA_GRANT (1 << 28) -#define FTGMAC100_DMAFIFOS_TXDMA_GRANT (1 << 29) -#define FTGMAC100_DMAFIFOS_RXDMA_REQ (1 << 30) -#define FTGMAC100_DMAFIFOS_TXDMA_REQ (1 << 31) - -/* - * Receive buffer size register - */ -#define FTGMAC100_RBSR_SIZE(x) ((x) & 0x3fff) - -/* - * MAC control register - */ -#define FTGMAC100_MACCR_TXDMA_EN (1 << 0) -#define FTGMAC100_MACCR_RXDMA_EN (1 << 1) -#define FTGMAC100_MACCR_TXMAC_EN (1 << 2) -#define FTGMAC100_MACCR_RXMAC_EN (1 << 3) -#define FTGMAC100_MACCR_RM_VLAN (1 << 4) -#define FTGMAC100_MACCR_HPTXR_EN (1 << 5) -#define FTGMAC100_MACCR_LOOP_EN (1 << 6) -#define FTGMAC100_MACCR_ENRX_IN_HALFTX (1 << 7) -#define FTGMAC100_MACCR_FULLDUP (1 << 8) -#define FTGMAC100_MACCR_GIGA_MODE (1 << 9) -#define FTGMAC100_MACCR_CRC_APD (1 << 10) -#define FTGMAC100_MACCR_RX_RUNT (1 << 12) -#define FTGMAC100_MACCR_JUMBO_LF (1 << 13) -#define FTGMAC100_MACCR_RX_ALL (1 << 14) -#define FTGMAC100_MACCR_HT_MULTI_EN (1 << 15) -#define FTGMAC100_MACCR_RX_MULTIPKT (1 << 16) -#define FTGMAC100_MACCR_RX_BROADPKT (1 << 17) -#define FTGMAC100_MACCR_DISCARD_CRCERR (1 << 18) -#define FTGMAC100_MACCR_FAST_MODE (1 << 19) -#define FTGMAC100_MACCR_SW_RST (1 << 31) - -/* - * PHY control register - */ -#define FTGMAC100_PHYCR_MDC_CYCTHR_MASK 0x3f -#define FTGMAC100_PHYCR_MDC_CYCTHR(x) ((x) & 0x3f) -#define FTGMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) -#define FTGMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) -#define FTGMAC100_PHYCR_MIIRD (1 << 26) -#define FTGMAC100_PHYCR_MIIWR (1 << 27) - -/* - * PHY data register - */ -#define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) -#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff) - -/* - * Transmit descriptor, aligned to 16 bytes - */ -struct ftgmac100_txdes { - unsigned int txdes0; - unsigned int txdes1; - unsigned int txdes2; /* not used by HW */ - unsigned int txdes3; /* TXBUF_BADR */ -} __attribute__ ((aligned(16))); - -#define FTGMAC100_TXDES0_TXBUF_SIZE(x) ((x) & 0x3fff) -#define FTGMAC100_TXDES0_EDOTR (1 << 15) -#define FTGMAC100_TXDES0_CRC_ERR (1 << 19) -#define FTGMAC100_TXDES0_LTS (1 << 28) -#define FTGMAC100_TXDES0_FTS (1 << 29) -#define FTGMAC100_TXDES0_TXDMA_OWN (1 << 31) - -#define FTGMAC100_TXDES1_VLANTAG_CI(x) ((x) & 0xffff) -#define FTGMAC100_TXDES1_INS_VLANTAG (1 << 16) -#define FTGMAC100_TXDES1_TCP_CHKSUM (1 << 17) -#define FTGMAC100_TXDES1_UDP_CHKSUM (1 << 18) -#define FTGMAC100_TXDES1_IP_CHKSUM (1 << 19) -#define FTGMAC100_TXDES1_LLC (1 << 22) -#define FTGMAC100_TXDES1_TX2FIC (1 << 30) -#define FTGMAC100_TXDES1_TXIC (1 << 31) - -/* - * Receive descriptor, aligned to 16 bytes - */ -struct ftgmac100_rxdes { - unsigned int rxdes0; - unsigned int rxdes1; - unsigned int rxdes2; /* not used by HW */ - unsigned int rxdes3; /* RXBUF_BADR */ -} __attribute__ ((aligned(16))); - -#define FTGMAC100_RXDES0_VDBC 0x3fff -#define FTGMAC100_RXDES0_EDORR (1 << 15) -#define FTGMAC100_RXDES0_MULTICAST (1 << 16) -#define FTGMAC100_RXDES0_BROADCAST (1 << 17) -#define FTGMAC100_RXDES0_RX_ERR (1 << 18) -#define FTGMAC100_RXDES0_CRC_ERR (1 << 19) -#define FTGMAC100_RXDES0_FTL (1 << 20) -#define FTGMAC100_RXDES0_RUNT (1 << 21) -#define FTGMAC100_RXDES0_RX_ODD_NB (1 << 22) -#define FTGMAC100_RXDES0_FIFO_FULL (1 << 23) -#define FTGMAC100_RXDES0_PAUSE_OPCODE (1 << 24) -#define FTGMAC100_RXDES0_PAUSE_FRAME (1 << 25) -#define FTGMAC100_RXDES0_LRS (1 << 28) -#define FTGMAC100_RXDES0_FRS (1 << 29) -#define FTGMAC100_RXDES0_RXPKT_RDY (1 << 31) - -#define FTGMAC100_RXDES1_VLANTAG_CI 0xffff -#define FTGMAC100_RXDES1_PROT_MASK (0x3 << 20) -#define FTGMAC100_RXDES1_PROT_NONIP (0x0 << 20) -#define FTGMAC100_RXDES1_PROT_IP (0x1 << 20) -#define FTGMAC100_RXDES1_PROT_TCPIP (0x2 << 20) -#define FTGMAC100_RXDES1_PROT_UDPIP (0x3 << 20) -#define FTGMAC100_RXDES1_LLC (1 << 22) -#define FTGMAC100_RXDES1_DF (1 << 23) -#define FTGMAC100_RXDES1_VLANTAG_AVAIL (1 << 24) -#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR (1 << 25) -#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR (1 << 26) -#define FTGMAC100_RXDES1_IP_CHKSUM_ERR (1 << 27) - -#endif /* __FTGMAC100_H */ diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c deleted file mode 100644 index 9bd7746cbfcf..000000000000 --- a/drivers/net/ftmac100.c +++ /dev/null @@ -1,1198 +0,0 @@ -/* - * Faraday FTMAC100 10/100 Ethernet - * - * (C) Copyright 2009-2011 Faraday Technology - * Po-Yu Chuang - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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 */ - -#if MAX_PKT_SIZE > 0x7ff -#error invalid MAX_PKT_SIZE -#endif - -#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE -#error invalid RX_BUF_SIZE -#endif - -/****************************************************************************** - * 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 __iomem *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; -}; - -static int ftmac100_alloc_rx_page(struct ftmac100 *priv, - struct ftmac100_rxdes *rxdes, gfp_t gfp); - -/****************************************************************************** - * 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) - -#define INT_MASK_ALL_DISABLED 0 - -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(INT_MASK_ALL_DISABLED, 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. - */ - udelay(500); - return 0; - } - - udelay(1000); - } - - 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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS); -} - -static bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS); -} - -static bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR); -} - -static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR); -} - -static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FTL); -} - -static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT); -} - -static bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes) -{ - return rxdes->rxdes0 & cpu_to_le32(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 rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST); -} - -static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes, - unsigned int size) -{ - rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR); - rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size)); -} - -static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes) -{ - rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR); -} - -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 page. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page) -{ - rxdes->rxdes3 = (unsigned int)page; -} - -static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes) -{ - return (struct page *)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; - struct page *page; - dma_addr_t map; - int length; - - 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; - } - - /* - * It is impossible to get multi-segment packets - * because we always provide big enough receive buffers. - */ - if (unlikely(!ftmac100_rxdes_last_segment(rxdes))) - BUG(); - - /* start processing */ - skb = netdev_alloc_skb_ip_align(netdev, 128); - 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++; - - map = ftmac100_rxdes_get_dma_addr(rxdes); - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - - length = ftmac100_rxdes_frame_length(rxdes); - page = ftmac100_rxdes_get_page(rxdes); - skb_fill_page_desc(skb, 0, page, 0, length); - skb->len += length; - skb->data_len += length; - skb->truesize += length; - __pskb_pull_tail(skb, min(length, 64)); - - ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); - - ftmac100_rx_pointer_advance(priv); - - skb->protocol = eth_type_trans(skb, netdev); - - netdev->stats.rx_packets++; - netdev->stats.rx_bytes += skb->len; - - /* push packet to protocol stack */ - netif_receive_skb(skb); - - (*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 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR); - txdes->txdes2 = 0; - txdes->txdes3 = 0; -} - -static bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes) -{ - return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); -} - -static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes) -{ - /* - * Make sure dma own bit will not be set before any other - * descriptor fields. - */ - wmb(); - txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); -} - -static bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes) -{ - return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL); -} - -static bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes) -{ - return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL); -} - -static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR); -} - -static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS); -} - -static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS); -} - -static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes) -{ - txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC); -} - -static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes, - unsigned int len) -{ - txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len)); -} - -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. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb) -{ - txdes->txdes3 = (unsigned int)skb; -} - -static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes) -{ - return (struct sk_buff *)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(skb); - - ftmac100_txdes_reset(txdes); - - ftmac100_tx_clean_pointer_advance(priv); - - spin_lock(&priv->tx_lock); - priv->tx_pending--; - spin_unlock(&priv->tx_lock); - netif_wake_queue(netdev); - - return true; -} - -static void ftmac100_tx_complete(struct ftmac100 *priv) -{ - while (ftmac100_tx_complete_packet(priv)) - ; -} - -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; - - txdes = ftmac100_current_txdes(priv); - ftmac100_tx_pointer_advance(priv); - - /* setup TX descriptor */ - 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); - - spin_lock(&priv->tx_lock); - priv->tx_pending++; - if (priv->tx_pending == TX_QUEUE_ENTRIES) - netif_stop_queue(netdev); - - /* start transmit */ - ftmac100_txdes_set_dma_own(txdes); - spin_unlock(&priv->tx_lock); - - ftmac100_txdma_start_polling(priv); - return NETDEV_TX_OK; -} - -/****************************************************************************** - * internal functions (buffer) - *****************************************************************************/ -static int ftmac100_alloc_rx_page(struct ftmac100 *priv, - struct ftmac100_rxdes *rxdes, gfp_t gfp) -{ - struct net_device *netdev = priv->netdev; - struct page *page; - dma_addr_t map; - - page = alloc_page(gfp); - if (!page) { - if (net_ratelimit()) - netdev_err(netdev, "failed to allocate rx page\n"); - return -ENOMEM; - } - - map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); - if (unlikely(dma_mapping_error(priv->dev, map))) { - if (net_ratelimit()) - netdev_err(netdev, "failed to map rx page\n"); - __free_page(page); - return -ENOMEM; - } - - ftmac100_rxdes_set_page(rxdes, page); - ftmac100_rxdes_set_dma_addr(rxdes, map); - ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE); - ftmac100_rxdes_set_dma_own(rxdes); - return 0; -} - -static void ftmac100_free_buffers(struct ftmac100 *priv) -{ - int i; - - for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - struct page *page = ftmac100_rxdes_get_page(rxdes); - dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes); - - if (!page) - continue; - - dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); - __free_page(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); - dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes); - - if (!skb) - continue; - - 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); - if (!priv->descs) - 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++) { - struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - - if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) - goto err; - } - - /* 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; - - udelay(100); - } - - 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; - - udelay(100); - } - - 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_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_NORXBUF | FTMAC100_INT_RPKT_LOST | - FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) { - if (net_ratelimit()) - netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, - status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "", - status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", - status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "", - status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); - - if (status & FTMAC100_INT_NORXBUF) { - /* RX buffer unavailable */ - netdev->stats.rx_over_errors++; - } - - if (status & FTMAC100_INT_RPKT_LOST) { - /* received packet lost due to RX FIFO full */ - netdev->stats.rx_fifo_errors++; - } - - if (status & FTMAC100_INT_PHYSTS_CHG) { - /* PHY link status change */ - 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_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(*priv)); - if (!netdev) { - err = -ENOMEM; - goto err_alloc_etherdev; - } - - 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, resource_size(res), - dev_name(&pdev->dev)); - if (!priv->res) { - dev_err(&pdev->dev, "Could not reserve memory region\n"); - err = -ENOMEM; - goto err_req_mem; - } - - priv->base = ioremap(res->start, resource_size(res)); - if (!priv->base) { - dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); - err = -EIO; - goto err_ioremap; - } - - 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_register_netdev; - } - - 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_register_netdev: - iounmap(priv->base); -err_ioremap: - release_resource(priv->res); -err_req_mem: - netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); - free_netdev(netdev); -err_alloc_etherdev: - return err; -} - -static int __exit ftmac100_remove(struct platform_device *pdev) -{ - struct net_device *netdev; - struct ftmac100 *priv; - - netdev = platform_get_drvdata(pdev); - priv = netdev_priv(netdev); - - unregister_netdev(netdev); - - iounmap(priv->base); - release_resource(priv->res); - - netif_napi_del(&priv->napi); - platform_set_drvdata(pdev, NULL); - free_netdev(netdev); - return 0; -} - -static struct platform_driver ftmac100_driver = { - .probe = ftmac100_probe, - .remove = __exit_p(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 "); -MODULE_DESCRIPTION("FTMAC100 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h deleted file mode 100644 index 46a0c47b1ee1..000000000000 --- a/drivers/net/ftmac100.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Faraday FTMAC100 10/100 Ethernet - * - * (C) Copyright 2009-2011 Faraday Technology - * Po-Yu Chuang - * - * 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 */ -- cgit v1.2.3