2013-07-18 09:47:10

by Jonas Jensen

[permalink] [raw]
Subject: [PATCH] net: Add MOXA ART SoCs ethernet driver

The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
developed internally. The IC used is "RTL8201CP".

Since there is no public documentation, this driver is mostly the one
published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.

Signed-off-by: Jonas Jensen <[email protected]>
---

Notes:
Applies to next-20130716

.../devicetree/bindings/net/moxa,moxart-mac.txt | 25 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/moxa/Kconfig | 30 ++
drivers/net/ethernet/moxa/Makefile | 6 +
drivers/net/ethernet/moxa/moxart_ether.c | 544 +++++++++++++++++++++
drivers/net/ethernet/moxa/moxart_ether.h | 525 ++++++++++++++++++++
7 files changed, 1132 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
create mode 100644 drivers/net/ethernet/moxa/Kconfig
create mode 100644 drivers/net/ethernet/moxa/Makefile
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h

diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
new file mode 100644
index 0000000..12e12a5
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
@@ -0,0 +1,25 @@
+MOXA ART Ethernet Controller
+
+Required properties:
+
+- compatible : Should be "moxa,moxart-mac"
+- reg : Should contain registers location and length
+ index 0 : main register
+ index 1 : mac address (stored on flash)
+- interrupts : Should contain the mac interrupt number
+
+Example:
+
+ mac0: mac@90900000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x90900000 0x100>,
+ <0x80000050 0x6>;
+ interrupts = <25 0>;
+ };
+
+ mac1: mac@92000000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x92000000 0x100>,
+ <0x80000056 0x6>;
+ interrupts = <27 0>;
+ };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2037080..506b024 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,6 +90,7 @@ source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"

config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 390bd0b..c0b8789 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/moxa/Kconfig b/drivers/net/ethernet/moxa/Kconfig
new file mode 100644
index 0000000..1731e05
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Kconfig
@@ -0,0 +1,30 @@
+#
+# MOXART device configuration
+#
+
+config NET_VENDOR_MOXART
+ bool "MOXA ART devices"
+ default y
+ depends on (ARM && ARCH_MOXART)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ 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 MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MOXART
+
+config ARM_MOXART_ETHER
+ tristate "MOXART Ethernet support"
+ depends on ARM && ARCH_MOXART
+ select NET_CORE
+ ---help---
+ If you wish to compile a kernel for a hardware with MOXA ART SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_MOXART
diff --git a/drivers/net/ethernet/moxa/Makefile b/drivers/net/ethernet/moxa/Makefile
new file mode 100644
index 0000000..d757a78
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the MOXART network device drivers.
+#
+
+obj-$(CONFIG_ARM_MOXART_ETHER) += moxart_ether.o
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
new file mode 100644
index 0000000..172f718
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -0,0 +1,544 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/crc32.h>
+#include <linux/crc32c.h>
+
+#include "moxart_ether.h"
+
+static inline unsigned long moxart_emac_read(struct net_device *ndev,
+ unsigned int reg)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return readl(priv->base + reg);
+}
+
+static inline void moxart_emac_write(struct net_device *ndev,
+ unsigned int reg, unsigned long value)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(value, priv->base + reg);
+}
+
+static void moxart_update_mac_address(struct net_device *ndev)
+{
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET,
+ ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])));
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET + 4,
+ ((ndev->dev_addr[2] << 24) |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5])));
+}
+
+static int moxart_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+ moxart_update_mac_address(ndev);
+
+ return 0;
+}
+
+static void moxart_mac_free_memory(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (priv->virt_tx_desc_baseaddr)
+ dma_free_coherent(NULL, sizeof(struct tx_desc_t)*TX_DESC_NUM,
+ priv->virt_tx_desc_baseaddr,
+ priv->phy_tx_desc_baseaddr);
+ if (priv->virt_rx_desc_baseaddr)
+ dma_free_coherent(NULL, sizeof(struct rx_desc_t)*RX_DESC_NUM,
+ priv->virt_rx_desc_baseaddr,
+ priv->phy_rx_desc_baseaddr);
+ if (priv->virt_tx_buf_baseaddr)
+ dma_free_coherent(NULL, TX_BUF_SIZE*TX_DESC_NUM,
+ priv->virt_tx_buf_baseaddr,
+ priv->phy_tx_buf_baseaddr);
+ if (priv->virt_rx_buf_baseaddr)
+ dma_free_coherent(NULL, RX_BUF_SIZE*RX_DESC_NUM,
+ priv->virt_rx_buf_baseaddr,
+ priv->phy_rx_buf_baseaddr);
+}
+
+static void moxart_mac_reset(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(SW_RST, priv->base + MACCR_REG_OFFSET);
+ while (readl(priv->base + MACCR_REG_OFFSET) & SW_RST)
+ mdelay(10);
+
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ priv->maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL;
+}
+
+static void moxart_mac_enable(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(0x00001010, priv->base + ITC_REG_OFFSET);
+ writel(0x00000001, priv->base + APTC_REG_OFFSET);
+ writel(0x00000390, priv->base + DBLAC_REG_OFFSET);
+
+ writel(RPKT_FINISH_M, priv->base + IMR_REG_OFFSET);
+ priv->maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN);
+ writel(priv->maccr, priv->base + MACCR_REG_OFFSET);
+}
+
+static void moxart_mac_setup_desc_ring(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ struct rx_desc_t *rxdesc;
+ unsigned char *virtbuf;
+ unsigned int phybuf;
+ int i;
+
+ virtbuf = priv->virt_tx_buf_baseaddr;
+ phybuf = priv->phy_tx_buf_baseaddr;
+ for (i = 0; i < TX_DESC_NUM; i++,
+ virtbuf += TX_BUF_SIZE, phybuf += TX_BUF_SIZE) {
+ txdesc = &priv->virt_tx_desc_baseaddr[i];
+ memset(txdesc, 0, sizeof(struct tx_desc_t));
+ txdesc->txdes2.phy_tx_buf_baseaddr = phybuf;
+ txdesc->txdes2.virt_tx_buf_baseaddr = virtbuf;
+ }
+ priv->virt_tx_desc_baseaddr[TX_DESC_NUM - 1].txdes1.ubit.edotr = 1;
+ priv->tx_desc_now = 0;
+
+ virtbuf = priv->virt_rx_buf_baseaddr;
+ phybuf = priv->phy_rx_buf_baseaddr;
+ for (i = 0; i < RX_DESC_NUM; i++,
+ virtbuf += RX_BUF_SIZE, phybuf += RX_BUF_SIZE) {
+ rxdesc = &priv->virt_rx_desc_baseaddr[i];
+ memset(rxdesc, 0, sizeof(struct rx_desc_t));
+ rxdesc->rxdes0.ubit.rx_dma_own = 1;
+ rxdesc->rxdes1.ubit.rx_buf_size = RX_BUF_SIZE;
+ rxdesc->rxdes2.phy_rx_buf_baseaddr = phybuf;
+ rxdesc->rxdes2.virt_rx_buf_baseaddr = virtbuf;
+ }
+ priv->virt_rx_desc_baseaddr[RX_DESC_NUM - 1].rxdes1.ubit.edorr = 1;
+ priv->rx_desc_now = 0;
+
+ /* reset the MAC controler TX/RX desciptor base address */
+ writel(priv->phy_tx_desc_baseaddr, priv->base + TXR_BADR_REG_OFFSET);
+ writel(priv->phy_rx_desc_baseaddr, priv->base + RXR_BADR_REG_OFFSET);
+}
+
+static int moxart_mac_open(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ spin_lock_irq(&priv->txlock);
+ moxart_mac_reset(ndev);
+ moxart_update_mac_address(ndev);
+ moxart_mac_setup_desc_ring(ndev);
+ moxart_mac_enable(ndev);
+ spin_unlock_irq(&priv->txlock);
+ netif_start_queue(ndev);
+
+ netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
+ __func__, readl(priv->base + IMR_REG_OFFSET),
+ readl(priv->base + MACCR_REG_OFFSET));
+
+ return 0;
+}
+
+static int moxart_mac_stop(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ spin_lock_irq(&priv->txlock);
+
+ /* disable all interrupts */
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ /* disable all functions */
+ writel(0, priv->base + MACCR_REG_OFFSET);
+
+ spin_unlock_irq(&priv->txlock);
+
+ return 0;
+}
+
+static void moxart_mac_recv(struct work_struct *ptr)
+{
+ struct net_device *ndev = (struct net_device *) ptr;
+ struct moxart_mac_priv_t *priv = netdev_priv((struct net_device *)ptr);
+ struct rx_desc_t *rxdesc;
+ struct sk_buff *skb;
+ unsigned int ui, len;
+ int rxnow = priv->rx_desc_now;
+ int loops = RX_DESC_NUM;
+
+repeat_recv:
+ rxdesc = &priv->virt_rx_desc_baseaddr[rxnow];
+ ui = rxdesc->rxdes0.ui;
+
+ if (ui & RXDMA_OWN)
+ return;
+
+ if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
+ netdev_err(ndev, "%s: packet error\n", __func__);
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ goto recv_finish;
+ }
+
+ len = ui & RFL_MASK;
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+
+ skb = dev_alloc_skb(len + 2);
+ if (skb == NULL) {
+ netdev_err(ndev, "%s: dev_alloc_skb failed\n", __func__);
+ priv->stats.rx_dropped++;
+ goto recv_finish;
+ }
+
+ skb_reserve(skb, 2);
+ skb->dev = ndev;
+ memcpy(skb_put(skb, len), rxdesc->rxdes2.virt_rx_buf_baseaddr, len);
+ netif_rx(skb);
+ skb->protocol = eth_type_trans(skb, ndev);
+ ndev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ if (ui & MULTICAST_RXDES0)
+ priv->stats.multicast++;
+
+recv_finish:
+ rxdesc->rxdes0.ui = RXDMA_OWN;
+ rxnow++;
+ rxnow &= RX_DESC_NUM_MASK;
+ priv->rx_desc_now = rxnow;
+ if (loops-- > 0)
+ goto repeat_recv;
+}
+
+static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned int ists = readl(priv->base + ISR_REG_OFFSET);
+
+ if (ists & RPKT_FINISH)
+ moxart_mac_recv((void *) ndev);
+
+ return IRQ_HANDLED;
+}
+
+static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ int len;
+ int txnow = priv->tx_desc_now;
+
+ spin_lock_irq(&priv->txlock);
+ txdesc = &priv->virt_tx_desc_baseaddr[txnow];
+ if (txdesc->txdes0.ubit.tx_dma_own) {
+ netdev_err(ndev, "%s: no TX space for packet\n", __func__);
+ priv->stats.tx_dropped++;
+ goto xmit_final;
+ }
+
+ len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
+ memcpy(txdesc->txdes2.virt_tx_buf_baseaddr, skb->data, len);
+
+ if (skb->len < ETH_ZLEN) {
+ memset(&txdesc->txdes2.virt_tx_buf_baseaddr[skb->len],
+ 0, ETH_ZLEN - skb->len);
+ len = ETH_ZLEN;
+ }
+
+ txdesc->txdes1.ubit.lts = 1;
+ txdesc->txdes1.ubit.fts = 1;
+ txdesc->txdes1.ubit.tx2_fic = 0;
+ txdesc->txdes1.ubit.tx_ic = 0;
+ txdesc->txdes1.ubit.tx_buf_size = len;
+ txdesc->txdes0.ui = TXDMA_OWN;
+
+ /* start to send packet */
+ writel(0xffffffff, priv->base + TXPD_REG_OFFSET);
+
+ txnow++;
+ txnow &= TX_DESC_NUM_MASK;
+ priv->tx_desc_now = txnow;
+ ndev->trans_start = jiffies;
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += len;
+
+xmit_final:
+ spin_unlock_irq(&priv->txlock);
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int desc = priv->rx_desc_now;
+
+ desc++;
+ desc &= RX_DESC_NUM_MASK;
+
+ return &priv->stats;
+}
+
+static void moxart_mac_setmulticast(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ int crc_val;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_val = crc32_le(~0, ha->addr, ETH_ALEN);
+ crc_val = (crc_val >> 26) & 0x3f;
+ if (crc_val >= 32) {
+ writel(readl(priv->base + MATH1_REG_OFFSET) |
+ (1UL << (crc_val - 32)),
+ priv->base + MATH1_REG_OFFSET);
+ } else {
+ writel(readl(priv->base + MATH0_REG_OFFSET) |
+ (1UL << crc_val),
+ priv->base + MATH0_REG_OFFSET);
+ }
+ }
+}
+
+static void moxart_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ spin_lock_irq(&priv->txlock);
+
+ (ndev->flags & IFF_PROMISC) ? (priv->maccr |= RCV_ALL) :
+ (priv->maccr &= ~RCV_ALL);
+
+ (ndev->flags & IFF_ALLMULTI) ? (priv->maccr |= RX_MULTIPKT) :
+ (priv->maccr &= ~RX_MULTIPKT);
+
+ if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) {
+ priv->maccr |= HT_MULTI_EN;
+ moxart_mac_setmulticast(ndev);
+ } else {
+ priv->maccr &= ~HT_MULTI_EN;
+ }
+
+ writel(priv->maccr, priv->base + MACCR_REG_OFFSET);
+
+ spin_unlock_irq(&priv->txlock);
+}
+
+static struct net_device_ops moxart_netdev_ops = {
+ .ndo_open = moxart_mac_open,
+ .ndo_stop = moxart_mac_stop,
+ .ndo_start_xmit = moxart_mac_start_xmit,
+ .ndo_get_stats = moxart_mac_get_stats,
+ .ndo_set_rx_mode = moxart_mac_set_rx_mode,
+ .ndo_set_mac_address = moxart_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void moxart_get_mac_address(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i <= 5; i++)
+ ndev->dev_addr[i] = readb(priv->flash_base + i);
+}
+
+static int moxart_mac_probe(struct platform_device *pdev)
+{
+ struct device *p_dev = &pdev->dev;
+ struct device_node *node = p_dev->of_node;
+ struct net_device *ndev;
+ struct moxart_mac_priv_t *priv;
+ struct resource *res;
+ unsigned int irq;
+ void *tmp;
+
+ ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
+ if (!ndev)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(node, 0);
+
+ priv = netdev_priv(ndev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ndev->base_addr = res->start;
+ priv->base = devm_ioremap_resource(p_dev, res);
+ if (IS_ERR(priv->base)) {
+ dev_err(p_dev, "%s: devm_ioremap_resource res_mac failed\n",
+ __func__);
+ goto init_fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ /* the flash driver (physmap_of) requests the same region
+ * so use ioremap instead of devm_ioremap_resource
+ */
+ priv->flash_base = ioremap(res->start, resource_size(res));
+ if (IS_ERR(priv->flash_base)) {
+ dev_err(p_dev, "%s: devm_ioremap_resource res_flash failed\n",
+ __func__);
+ goto init_fail;
+ }
+
+ spin_lock_init(&priv->txlock);
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct tx_desc_t) * TX_DESC_NUM,
+ (dma_addr_t *) &priv->phy_tx_desc_baseaddr,
+ GFP_DMA | GFP_KERNEL);
+ priv->virt_tx_desc_baseaddr = (struct tx_desc_t *) tmp;
+ if (priv->virt_tx_desc_baseaddr == NULL ||
+ (priv->phy_tx_desc_baseaddr & 0x0f)) {
+ netdev_err(ndev, "TX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct rx_desc_t) * RX_DESC_NUM,
+ (dma_addr_t *)&priv->phy_rx_desc_baseaddr,
+ GFP_DMA | GFP_KERNEL);
+ priv->virt_rx_desc_baseaddr = (struct rx_desc_t *) tmp;
+ if (priv->virt_rx_desc_baseaddr == NULL ||
+ (priv->phy_rx_desc_baseaddr & 0x0f)) {
+ netdev_err(ndev, "RX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ tmp = dma_alloc_coherent(NULL, TX_BUF_SIZE * TX_DESC_NUM,
+ (dma_addr_t *)&priv->phy_tx_buf_baseaddr,
+ GFP_DMA | GFP_KERNEL);
+ priv->virt_tx_buf_baseaddr = (unsigned char *) tmp;
+ if (priv->virt_tx_buf_baseaddr == NULL ||
+ (priv->phy_tx_buf_baseaddr & 0x03)) {
+ netdev_err(ndev, "TX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ tmp = dma_alloc_coherent(NULL, RX_BUF_SIZE * RX_DESC_NUM,
+ (dma_addr_t *)&priv->phy_rx_buf_baseaddr,
+ GFP_DMA | GFP_KERNEL);
+ priv->virt_rx_buf_baseaddr = (unsigned char *) tmp;
+ if (priv->virt_rx_buf_baseaddr == NULL ||
+ (priv->phy_rx_buf_baseaddr & 0x03)) {
+ netdev_err(ndev, "RX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &moxart_netdev_ops;
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ moxart_get_mac_address(ndev);
+ moxart_update_mac_address(ndev);
+
+ if (register_netdev(ndev)) {
+ free_netdev(ndev);
+ goto init_fail;
+ }
+
+ ndev->irq = irq;
+
+ if (devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0,
+ pdev->name, ndev)) {
+ netdev_err(ndev, "%s: devm_request_irq failed\n", __func__);
+ free_netdev(ndev);
+ return -EBUSY;
+ }
+
+ netdev_dbg(ndev, "%s: IRQ=%d address=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__, ndev->irq,
+ ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
+ ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
+
+ return 0;
+
+init_fail:
+ netdev_err(ndev, "%s: init failed\n", __func__);
+ moxart_mac_free_memory(ndev);
+
+ return -ENOMEM;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ moxart_mac_free_memory(ndev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_mac_match[] = {
+ { .compatible = "moxa,moxart-mac" },
+ { }
+};
+
+struct __initdata platform_driver moxart_mac_driver = {
+ .probe = moxart_mac_probe,
+ .remove = moxart_remove,
+ .driver = {
+ .name = "moxart-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_mac_match,
+ },
+};
+module_platform_driver(moxart_mac_driver);
+
+MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <[email protected]>");
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
new file mode 100644
index 0000000..9c477f6
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -0,0 +1,525 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MOXART_ETHERNET_H
+#define _MOXART_ETHERNET_H
+
+#define TX_DESC_NUM 64
+#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
+#define RX_DESC_NUM 64
+#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
+#define TX_BUF_SIZE 1600
+#define RX_BUF_SIZE 1600
+
+struct tx_desc_t {
+ union {
+#define TXDMA_OWN BIT(31)
+#define TXPKT_EXSCOL BIT(1)
+#define TXPKT_LATECOL BIT(0)
+ unsigned int ui;
+
+ struct {
+ /* is aborted due to late collision */
+ unsigned int tx_pkt_late_col:1;
+
+ /* is aborted after 16 collisions */
+ unsigned int rx_pkt_exs_col:1;
+
+ unsigned int reserved1:29;
+
+ /* is owned by the MAC controller */
+ unsigned int tx_dma_own:1;
+ } ubit;
+ } txdes0;
+
+ union {
+#define EDOTR BIT(31)
+#define TXIC BIT(30)
+#define TX2FIC BIT(29)
+#define FTS BIT(28)
+#define LTS BIT(27)
+#define TXBUF_SIZE_MASK 0x7ff
+#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* transmit buffer size in byte */
+ unsigned int tx_buf_size:11;
+
+ unsigned int reserved2:16;
+
+ /* is the last descriptor of a Tx packet */
+ unsigned int lts:1;
+
+ /* is the first descriptor of a Tx packet */
+ unsigned int fts:1;
+
+ /* transmit to FIFO interrupt on completion */
+ unsigned int tx2_fic:1;
+
+ /* transmit interrupt on completion */
+ unsigned int tx_ic:1;
+
+ /* end descriptor of transmit ring */
+ unsigned int edotr:1;
+ } ubit;
+ } txdes1;
+
+ struct {
+ /* transmit buffer physical base address */
+ unsigned int phy_tx_buf_baseaddr;
+
+ /* transmit buffer virtual base address */
+ unsigned char *virt_tx_buf_baseaddr;
+ } txdes2;
+};
+
+struct rx_desc_t {
+ union {
+#define RXDMA_OWN BIT(31)
+#define FRS BIT(29)
+#define LRS BIT(28)
+#define RX_ODD_NB BIT(22)
+#define RUNT BIT(21)
+#define FTL BIT(20)
+#define CRC_ERR BIT(19)
+#define RX_ERR BIT(18)
+#define BROADCAST_RXDES0 BIT(17)
+#define MULTICAST_RXDES0 BIT(16)
+#define RFL_MASK 0x7ff
+#define RFL_MAX (RFL_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive frame length */
+ unsigned int recv_frame_len:11;
+ unsigned int reserved1:5;
+
+ /* multicast frame */
+ unsigned int multicast:1;
+
+ /* broadcast frame */
+ unsigned int broadcast:1;
+ unsigned int rx_err:1; /* receive error */
+ unsigned int crc_err:1; /* CRC error */
+ unsigned int ftl:1; /* frame too long */
+
+ /* runt packet, less than 64 bytes */
+ unsigned int runt:1;
+
+ /* receive odd nibbles */
+ unsigned int rx_odd_nb:1;
+ unsigned int reserved2:5;
+
+ /* last receive segment descriptor */
+ unsigned int lrs:1;
+
+ /* first receive segment descriptor */
+ unsigned int frs:1;
+
+ unsigned int reserved3:1;
+ unsigned int rx_dma_own:1; /* RXDMA onwership */
+ } ubit;
+ } rxdes0;
+
+ union {
+#define EDORR BIT(31)
+#define RXBUF_SIZE_MASK 0x7ff
+#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive buffer size */
+ unsigned int rx_buf_size:11;
+
+ unsigned int reserved4:20;
+
+ /* end descriptor of receive ring */
+ unsigned int edorr:1;
+ } ubit;
+ } rxdes1;
+
+ struct {
+ /* receive buffer physical base address */
+ unsigned int phy_rx_buf_baseaddr;
+
+ /* receive buffer virtual base address */
+ unsigned char *virt_rx_buf_baseaddr;
+ } rxdes2;
+};
+
+struct mac_control_reg_t {
+
+/* RXDMA has received packets into RX buffer successfully */
+#define RPKT_FINISH BIT(0)
+/* receive buffer unavailable */
+#define NORXBUF BIT(1)
+/* TXDMA has moved data into the TX FIFO */
+#define XPKT_FINISH BIT(2)
+/* transmit buffer unavailable */
+#define NOTXBUF BIT(3)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK_INT_STS BIT(4)
+/* packets transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+#define XPKT_LOST_INT_STS BIT(5)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAV BIT(6)
+/* received packet lost due to RX FIFO full */
+#define RPKT_LOST_INT_STS BIT(7)
+#define AHB_ERR BIT(8) /* AHB error */
+#define PHYSTS_CHG BIT(9) /* PHY link status change */
+ unsigned int isr; /* interrupt status, 0x0 */
+
+#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
+#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
+#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
+#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
+#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
+#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
+#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
+#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
+#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
+#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
+ unsigned int imr; /* interrupt mask, 0x4 */
+
+/* the most significant 2 bytes of MAC address */
+#define MAC_MADR_MASK 0xffff
+ /* MAC most significant address, 0x8 */
+ unsigned int mac_madr;
+
+ /* MAC least significant address, 0xc */
+ unsigned int mac_ldar;
+
+ /* multicast address hash table 0, 0x10 */
+ unsigned int matht0;
+
+ /* multicast address hash table 1, 0x14 */
+ unsigned int matht1;
+
+ /* transmit poll demand, 0x18 */
+ unsigned int txpd;
+
+ /* receive poll demand, 0x1c */
+ unsigned int rxpd;
+
+ /* transmit ring base address, 0x20 */
+ unsigned int txr_badr;
+
+ /* receive ring base address, 0x24 */
+ unsigned int rxr_badr;
+
+/* defines the period of TX cycle time */
+#define TXINT_TIME_SEL BIT(15)
+#define TXINT_THR_MASK 0x7000
+#define TXINT_CNT_MASK 0xf00
+/* defines the period of RX cycle time */
+#define RXINT_TIME_SEL BIT(7)
+#define RXINT_THR_MASK 0x70
+#define RXINT_CNT_MASK 0xF
+ /* interrupt timer control, 0x28 */
+ unsigned int itc;
+
+/* defines the period of TX poll time */
+#define TXPOLL_TIME_SEL BIT(12)
+#define TXPOLL_CNT_MASK 0xf00
+#define TXPOLL_CNT_SHIFT_BIT 8
+/* defines the period of RX poll time */
+#define RXPOLL_TIME_SEL BIT(4)
+#define RXPOLL_CNT_MASK 0xF
+#define RXPOLL_CNT_SHIFT_BIT 0
+ /* automatic polling timer control, 0x2c */
+ unsigned int aptc;
+
+/* enable RX FIFO threshold arbitration */
+#define RX_THR_EN BIT(9)
+#define RXFIFO_HTHR_MASK 0x1c0
+#define RXFIFO_LTHR_MASK 0x38
+/* use INCR16 burst command in AHB bus */
+#define INCR16_EN BIT(2)
+/* use INCR8 burst command in AHB bus */
+#define INCR8_EN BIT(1)
+/* use INCR4 burst command in AHB bus */
+#define INCR4_EN BIT(0)
+ /* DMA burst length and arbitration control, 0x30 */
+ unsigned int dblac;
+
+ unsigned int reserved1[21]; /* 0x34 - 0x84 */
+
+#define RX_BROADPKT BIT(17) /* receive boradcast packet */
+/* receive all multicast packet */
+#define RX_MULTIPKT BIT(16)
+#define FULLDUP BIT(15) /* full duplex */
+/* append CRC to transmitted packet */
+#define CRC_APD BIT(14)
+/* do not check incoming packet's destination address */
+#define RCV_ALL BIT(12)
+/* store incoming packet even if its length is great than 1518 bytes */
+#define RX_FTL BIT(11)
+/* store incoming packet even if its length is less than 64 bytes */
+#define RX_RUNT BIT(10)
+/* enable storing incoming packet if the packet passes hash table
+ * address filtering and is a multicast packet
+ */
+#define HT_MULTI_EN BIT(9)
+#define RCV_EN BIT(8) /* receiver enable */
+/* enable packet reception when transmitting packet in half duplex mode */
+#define ENRX_IN_HALFTX BIT(6)
+#define XMT_EN BIT(5) /* transmitter enable */
+/* disable CRC check when receiving packets */
+#define CRC_DIS BIT(4)
+#define LOOP_EN BIT(3) /* internal loop-back */
+/* software reset, last 64 AHB bus clocks */
+#define SW_RST BIT(2)
+#define RDMA_EN BIT(1) /* enable receive DMA chan */
+#define XDMA_EN BIT(0) /* enable transmit DMA chan */
+ unsigned int maccr; /* MAC control, 0x88 */
+
+#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
+/* transmitter detects late collision */
+#define LATE_COL BIT(10)
+/* packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+#define XPKT_LOST BIT(9)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK BIT(8)
+/* receiver detects a runt packet */
+#define RUNT_MAC_STS BIT(7)
+/* receiver detects a frame that is too long */
+#define FTL_MAC_STS BIT(6)
+#define CRC_ERR_MAC_STS BIT(5)
+/* received packets list due to RX FIFO full */
+#define RPKT_LOST BIT(4)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAVE BIT(3)
+/* incoming packet dropped due to collision */
+#define COL BIT(2)
+#define MCPU_BROADCAST BIT(1)
+#define MCPU_MULTICAST BIT(0)
+ unsigned int macsr; /* MAC status, 0x8c */
+
+/* initialize a write sequence to PHY by setting this bit to 1
+ * this bit would be auto cleared after the write operation is finished.
+ */
+#define MIIWR BIT(27)
+#define MIIRD BIT(26)
+#define REGAD_MASK 0x3e00000
+#define PHYAD_MASK 0x1f0000
+#define MIIRDATA_MASK 0xffff
+ unsigned int phycr; /* PHY control, 0x90 */
+
+#define MIIWDATA_MASK 0xffff
+ unsigned int phywdata; /* PHY write data, 0x94 */
+
+#define PAUSE_TIME_MASK 0xffff0000
+#define FC_HIGH_MASK 0xf000
+#define FC_LOW_MASK 0xf00
+#define RX_PAUSE BIT(4) /* receive pause frame */
+/* packet transmission is paused due to receive */
+#define TXPAUSED BIT(3)
+ /* pause frame */
+/* enable flow control threshold mode. */
+#define FCTHR_EN BIT(2)
+#define TX_PAUSE BIT(1) /* transmit pause frame */
+#define FC_EN BIT(0) /* flow control mode enable */
+ unsigned int fcr; /* flow control, 0x98 */
+
+#define BK_LOW_MASK 0xf00
+#define BKJAM_LEN_MASK 0xf0
+#define BK_MODE BIT(1) /* back pressure address mode */
+#define BK_EN BIT(0) /* back pressure mode enable */
+ unsigned int bpr; /* back pressure, 0x9c */
+
+ unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
+
+#define TEST_SEED_MASK 0x3fff
+ unsigned int ts; /* test seed, 0xc4 */
+
+#define TXD_REQ BIT(31) /* TXDMA request */
+#define RXD_REQ BIT(30) /* RXDMA request */
+#define DARB_TXGNT BIT(29) /* TXDMA grant */
+#define DARB_RXGNT BIT(28) /* RXDMA grant */
+#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
+#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
+#define TXDMA2_SM_MASK 0x7000
+#define TXDMA1_SM_MASK 0xf00
+#define RXDMA2_SM_MASK 0x70
+#define RXDMA1_SM_MASK 0xF
+ unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
+
+#define SINGLE_PKT BIT(26) /* single packet mode */
+/* automatic polling timer test mode */
+#define PTIMER_TEST BIT(25)
+#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
+#define TEST_SEED_SEL BIT(22) /* test seed select */
+#define SEED_SEL BIT(21) /* seed select */
+#define TEST_MODE BIT(20) /* transmission test mode */
+#define TEST_TIME_MASK 0xffc00
+#define TEST_EXCEL_MASK 0x3e0
+ unsigned int tm; /* test mode, 0xcc */
+
+ unsigned int reserved3; /* 0xd0 */
+
+#define TX_MCOL_MASK 0xffff0000
+#define TX_MCOL_SHIFT_BIT 16
+#define TX_SCOL_MASK 0xffff
+#define TX_SCOL_SHIFT_BIT 0
+ /* TX_MCOL and TX_SCOL counter, 0xd4 */
+ unsigned int txmcol_xscol;
+
+#define RPF_MASK 0xffff0000
+#define RPF_SHIFT_BIT 16
+#define AEP_MASK 0xffff
+#define AEP_SHIFT_BIT 0
+ unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
+
+#define XM_MASK 0xffff0000
+#define XM_SHIFT_BIT 16
+#define PG_MASK 0xffff
+#define PG_SHIFT_BIT 0
+ unsigned int xm_pg; /* XM and PG counter, 0xdc */
+
+#define RUNT_CNT_MASK 0xffff0000
+#define RUNT_CNT_SHIFT_BIT 16
+#define TLCC_MASK 0xffff
+#define TLCC_SHIFT_BIT 0
+ /* RUNT_CNT and TLCC counter, 0xe0 */
+ unsigned int runtcnt_tlcc;
+
+#define CRCER_CNT_MASK 0xffff0000
+#define CRCER_CNT_SHIFT_BIT 16
+#define FTL_CNT_MASK 0xffff
+#define FTL_CNT_SHIFT_BIT 0
+ /* CRCER_CNT and FTL_CNT counter, 0xe4 */
+ unsigned int crcercnt_ftlcnt;
+
+#define RLC_MASK 0xffff0000
+#define RLC_SHIFT_BIT 16
+#define RCC_MASK 0xffff
+#define RCC_SHIFT_BIT 0
+ unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
+
+ unsigned int broc; /* BROC counter, 0xec */
+ unsigned int mulca; /* MULCA counter, 0xf0 */
+ unsigned int rp; /* RP counter, 0xf4 */
+ unsigned int xp; /* XP counter, 0xf8 */
+};
+
+#define ISR_REG_OFFSET 0x0
+#define IMR_REG_OFFSET 0x4
+#define MAC_MADR_REG_OFFSET 0x8
+#define MAC_LADR_REG_OFFSET 0xC
+#define MATH0_REG_OFFSET 0x10
+#define MATH1_REG_OFFSET 0x14
+#define TXPD_REG_OFFSET 0x18
+#define RXPD_REG_OFFSET 0x1C
+#define TXR_BADR_REG_OFFSET 0x20
+#define RXR_BADR_REG_OFFSET 0x24
+#define ITC_REG_OFFSET 0x28
+#define APTC_REG_OFFSET 0x2C
+#define DBLAC_REG_OFFSET 0x30
+#define MACCR_REG_OFFSET 0x88
+#define MACSR_REG_OFFSET 0x8C
+#define PHYCR_REG_OFFSET 0x90
+#define PHYWDATA_REG_OFFSET 0x94
+#define FCR_REG_OFFSET 0x98
+#define BPR_REG_OFFSET 0x9C
+#define TS_REG_OFFSET 0xC4
+#define DMAFIFOS_REG_OFFSET 0xC8
+#define TM_REG_OFFSET 0xCC
+#define TX_MCOL_TX_SCOL_REG_OFFSET 0xD4
+#define RPF_AEP_REG_OFFSET 0xD8
+#define XM_PG_REG_OFFSET 0xDC
+#define RUNT_CNT_TLCC_REG_OFFSET 0xE0
+#define CRCER_CNT_FTL_CNT_REG_OFFSET 0xE4
+#define RLC_RCC_REG_OFFSET 0xE8
+#define BROC_REG_OFFSET 0xEC
+#define MULCA_REG_OFFSET 0xF0
+#define RP_REG_OFFSET 0xF4
+#define XP_REG_OFFSET 0xF8
+#define PHY_CNTL_REG 0x0
+#define PHY_STATUS_REG 0x1
+#define PHY_ID_REG1 0x2
+#define PHY_ID_REG2 0x3
+#define PHY_ANA_REG 0x4
+#define PHY_ANLPAR_REG 0x5
+#define PHY_ANE_REG 0x6
+#define PHY_ECNTL_REG1 0x10
+#define PHY_QPDS_REG 0x11
+#define PHY_10BOP_REG 0x12
+#define PHY_ECNTL_REG2 0x13
+#define FTMAC100_REG_PHY_WRITE 0x8000000
+#define FTMAC100_REG_PHY_READ 0x4000000
+
+/* PHY Status register */
+#define AN_COMPLETE 0x20
+
+#define LINK_STATUS 0x4
+
+struct moxart_mac_priv_t {
+ void __iomem *base;
+ void __iomem *flash_base;
+
+ /* Tx descriptor physical base address */
+ unsigned int phy_tx_desc_baseaddr;
+
+ /* Tx descriptor virtual base address */
+ struct tx_desc_t *virt_tx_desc_baseaddr;
+
+ /* Rx descriptor physical base address */
+ unsigned int phy_rx_desc_baseaddr;
+
+ /* Rx descriptor virtual base address */
+ struct rx_desc_t *virt_rx_desc_baseaddr;
+
+ /* Tx buffer physical base address */
+ unsigned int phy_tx_buf_baseaddr;
+
+ /* Tx buffer virtual base address */
+ unsigned char *virt_tx_buf_baseaddr;
+
+ /* Rx buffer physical base address */
+ unsigned int phy_rx_buf_baseaddr;
+
+ /* Rx buffer virtual base address */
+ unsigned char *virt_rx_buf_baseaddr;
+
+ /* Tx descriptor now first used index */
+ int tx_desc_now;
+
+ /* Rx descriptor now first used index */
+ int rx_desc_now;
+
+ /* OS about the ethernet statistics */
+ struct net_device_stats stats;
+
+ spinlock_t txlock;
+ spinlock_t rxlock;
+
+ /* store the maccr control register value */
+ unsigned int maccr;
+
+ struct work_struct rqueue;
+};
+
+#if TX_BUF_SIZE >= TXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Tx buffer size too large !
+#endif
+#if RX_BUF_SIZE >= RXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Rx buffer size too large !
+#endif
+
+#endif
--
1.8.2.1


2013-07-20 10:55:38

by Jonas Jensen

[permalink] [raw]
Subject: Re: [PATCH] net: Add MOXA ART SoCs ethernet driver

I think this driver may have a bug.

After some time running ping successfully, the message "no TX space
for packet" is printed resulting in 100% packet loss.

The message is printed from .ndo_start_xmit and I think it may be
because of how priv->tx_desc_now is now set up in
moxart_mac_setup_desc_ring.

I will take a look at this and try to fix the issue.

Best regards,
Jonas

2013-07-22 11:10:18

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH] net: Add MOXA ART SoCs ethernet driver

Hello Jonas,

A bunch of comments inline.

2013/7/18 Jonas Jensen <[email protected]>:
> The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
> developed internally. The IC used is "RTL8201CP".
>
> Since there is no public documentation, this driver is mostly the one
> published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.
>
> Signed-off-by: Jonas Jensen <[email protected]>
> ---
>
> Notes:
> Applies to next-20130716
>
> .../devicetree/bindings/net/moxa,moxart-mac.txt | 25 +
> drivers/net/ethernet/Kconfig | 1 +
> drivers/net/ethernet/Makefile | 1 +
> drivers/net/ethernet/moxa/Kconfig | 30 ++
> drivers/net/ethernet/moxa/Makefile | 6 +
> drivers/net/ethernet/moxa/moxart_ether.c | 544 +++++++++++++++++++++
> drivers/net/ethernet/moxa/moxart_ether.h | 525 ++++++++++++++++++++
> 7 files changed, 1132 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> create mode 100644 drivers/net/ethernet/moxa/Kconfig
> create mode 100644 drivers/net/ethernet/moxa/Makefile
> create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
> create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h
>
> diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> new file mode 100644
> index 0000000..12e12a5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> @@ -0,0 +1,25 @@
> +MOXA ART Ethernet Controller
> +
> +Required properties:
> +
> +- compatible : Should be "moxa,moxart-mac"
> +- reg : Should contain registers location and length
> + index 0 : main register
> + index 1 : mac address (stored on flash)

That is kind of unusual, since the MAC register is within the range of
the MAC base register, just read the MAC from the base register and
use that to fill in dev->dev_addr in your ndo_open() callback.

[snip]

> +static void moxart_mac_setup_desc_ring(struct net_device *ndev)
> +{
> + struct moxart_mac_priv_t *priv = netdev_priv(ndev);
> + struct tx_desc_t *txdesc;
> + struct rx_desc_t *rxdesc;
> + unsigned char *virtbuf;
> + unsigned int phybuf;
> + int i;
> +
> + virtbuf = priv->virt_tx_buf_baseaddr;
> + phybuf = priv->phy_tx_buf_baseaddr;
> + for (i = 0; i < TX_DESC_NUM; i++,
> + virtbuf += TX_BUF_SIZE, phybuf += TX_BUF_SIZE) {
> + txdesc = &priv->virt_tx_desc_baseaddr[i];
> + memset(txdesc, 0, sizeof(struct tx_desc_t));
> + txdesc->txdes2.phy_tx_buf_baseaddr = phybuf;
> + txdesc->txdes2.virt_tx_buf_baseaddr = virtbuf;
> + }
> + priv->virt_tx_desc_baseaddr[TX_DESC_NUM - 1].txdes1.ubit.edotr = 1;
> + priv->tx_desc_now = 0;
> +
> + virtbuf = priv->virt_rx_buf_baseaddr;
> + phybuf = priv->phy_rx_buf_baseaddr;
> + for (i = 0; i < RX_DESC_NUM; i++,
> + virtbuf += RX_BUF_SIZE, phybuf += RX_BUF_SIZE) {
> + rxdesc = &priv->virt_rx_desc_baseaddr[i];
> + memset(rxdesc, 0, sizeof(struct rx_desc_t));
> + rxdesc->rxdes0.ubit.rx_dma_own = 1;
> + rxdesc->rxdes1.ubit.rx_buf_size = RX_BUF_SIZE;
> + rxdesc->rxdes2.phy_rx_buf_baseaddr = phybuf;
> + rxdesc->rxdes2.virt_rx_buf_baseaddr = virtbuf;
> + }

Your RX path is supposed to be preallocating SKBs, create a DMA
mapping for them and then only SKB refilling would then happen when
you process a SKB in your RX handler. I do not see you doing it here.

> + priv->virt_rx_desc_baseaddr[RX_DESC_NUM - 1].rxdes1.ubit.edorr = 1;
> + priv->rx_desc_now = 0;
> +
> + /* reset the MAC controler TX/RX desciptor base address */
> + writel(priv->phy_tx_desc_baseaddr, priv->base + TXR_BADR_REG_OFFSET);
> + writel(priv->phy_rx_desc_baseaddr, priv->base + RXR_BADR_REG_OFFSET);
> +}
> +
> +static int moxart_mac_open(struct net_device *ndev)
> +{
> + struct moxart_mac_priv_t *priv = netdev_priv(ndev);
> +
> + if (!is_valid_ether_addr(ndev->dev_addr))
> + return -EADDRNOTAVAIL;
> +
> + spin_lock_irq(&priv->txlock);
> + moxart_mac_reset(ndev);
> + moxart_update_mac_address(ndev);
> + moxart_mac_setup_desc_ring(ndev);
> + moxart_mac_enable(ndev);
> + spin_unlock_irq(&priv->txlock);

Unless you did not properly disable interrupts at the MAC level,
taking a spinlock here does not seem to be required at all since the
various steps you call are done in the correct order.

> + netif_start_queue(ndev);
> +
> + netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
> + __func__, readl(priv->base + IMR_REG_OFFSET),
> + readl(priv->base + MACCR_REG_OFFSET));
> +
> + return 0;
> +}
> +
> +static int moxart_mac_stop(struct net_device *ndev)
> +{
> + struct moxart_mac_priv_t *priv = netdev_priv(ndev);
> +
> + netif_stop_queue(ndev);
> + spin_lock_irq(&priv->txlock);
> +
> + /* disable all interrupts */
> + writel(0, priv->base + IMR_REG_OFFSET);
> +
> + /* disable all functions */
> + writel(0, priv->base + MACCR_REG_OFFSET);
> +
> + spin_unlock_irq(&priv->txlock);

Same here, the serialisation is not required here once you have
disabled the interrupts at the MAC level. You should also free the RX
buffers and the potentially remaining transmit SKBs.

> +
> + return 0;
> +}
> +
> +static void moxart_mac_recv(struct work_struct *ptr)
> +{
> + struct net_device *ndev = (struct net_device *) ptr;
> + struct moxart_mac_priv_t *priv = netdev_priv((struct net_device *)ptr);
> + struct rx_desc_t *rxdesc;
> + struct sk_buff *skb;
> + unsigned int ui, len;
> + int rxnow = priv->rx_desc_now;
> + int loops = RX_DESC_NUM;

This screams to me: implement NAPI. You are doing all of the
processing in an interrupt handler, which runs in hard IRQ context,
this is bad for latency. This very specific function won't change that
much once you convert it to NAPI.

> +
> +repeat_recv:
> + rxdesc = &priv->virt_rx_desc_baseaddr[rxnow];
> + ui = rxdesc->rxdes0.ui;
> +
> + if (ui & RXDMA_OWN)
> + return;
> +
> + if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
> + netdev_err(ndev, "%s: packet error\n", __func__);
> + priv->stats.rx_dropped++;
> + priv->stats.rx_errors++;
> + goto recv_finish;
> + }
> +
> + len = ui & RFL_MASK;
> +
> + if (len > RX_BUF_SIZE)
> + len = RX_BUF_SIZE;
> +
> + skb = dev_alloc_skb(len + 2);

You should use netdev_alloc_skb_ip_align() here.

> + if (skb == NULL) {
> + netdev_err(ndev, "%s: dev_alloc_skb failed\n", __func__);
> + priv->stats.rx_dropped++;
> + goto recv_finish;
> + }
> +
> + skb_reserve(skb, 2);

And remove this.

> + skb->dev = ndev;

eth_type_trans() already does this for you.

> + memcpy(skb_put(skb, len), rxdesc->rxdes2.virt_rx_buf_baseaddr, len);

You should use a dma_unmap_single() operation here.

> + netif_rx(skb);
> + skb->protocol = eth_type_trans(skb, ndev);

I think these two steps are inverted, you should first call eth_type_trans().

> + ndev->last_rx = jiffies;
> + priv->stats.rx_packets++;
> + priv->stats.rx_bytes += len;
> + if (ui & MULTICAST_RXDES0)
> + priv->stats.multicast++;
> +
> +recv_finish:
> + rxdesc->rxdes0.ui = RXDMA_OWN;
> + rxnow++;
> + rxnow &= RX_DESC_NUM_MASK;
> + priv->rx_desc_now = rxnow;
> + if (loops-- > 0)
> + goto repeat_recv;

Better code this with a while() loop directly, using gotos here is
silly and I believe this might be bad for your CPU branch predictor as
well.

> +}
> +
> +static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
> +{
> + struct net_device *ndev = (struct net_device *) dev_id;
> + struct moxart_mac_priv_t *priv = netdev_priv(ndev);
> + unsigned int ists = readl(priv->base + ISR_REG_OFFSET);
> +
> + if (ists & RPKT_FINISH)
> + moxart_mac_recv((void *) ndev);

Unnecessary void * casting here.

> +
> + return IRQ_HANDLED;
> +}
> +
> +static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> + struct moxart_mac_priv_t *priv = netdev_priv(ndev);
> + struct tx_desc_t *txdesc;
> + int len;
> + int txnow = priv->tx_desc_now;
> +
> + spin_lock_irq(&priv->txlock);
> + txdesc = &priv->virt_tx_desc_baseaddr[txnow];
> + if (txdesc->txdes0.ubit.tx_dma_own) {
> + netdev_err(ndev, "%s: no TX space for packet\n", __func__);
> + priv->stats.tx_dropped++;
> + goto xmit_final;
> + }

You should return NETDEV_TX_BUSY here if there are no TX descriptors available.

> +
> + len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
> + memcpy(txdesc->txdes2.virt_tx_buf_baseaddr, skb->data, len);

This looks wrong, you should get a TX descriptor for this SKB, and
then perform a DMA mapping of skb->data for skb->len to that specific
TX descriptor address and set the transmit descriptor control words as
you do below.

> +
> + if (skb->len < ETH_ZLEN) {
> + memset(&txdesc->txdes2.virt_tx_buf_baseaddr[skb->len],
> + 0, ETH_ZLEN - skb->len);
> + len = ETH_ZLEN;
> + }
> +
> + txdesc->txdes1.ubit.lts = 1;
> + txdesc->txdes1.ubit.fts = 1;
> + txdesc->txdes1.ubit.tx2_fic = 0;
> + txdesc->txdes1.ubit.tx_ic = 0;
> + txdesc->txdes1.ubit.tx_buf_size = len;
> + txdesc->txdes0.ui = TXDMA_OWN;
> +
> + /* start to send packet */
> + writel(0xffffffff, priv->base + TXPD_REG_OFFSET);
> +
> + txnow++;
> + txnow &= TX_DESC_NUM_MASK;
> + priv->tx_desc_now = txnow;
> + ndev->trans_start = jiffies;
> + priv->stats.tx_packets++;
> + priv->stats.tx_bytes += len;

Incrementing statistics must happen in the transmit completion
handler. At this point there is no guarantee that the hardware really
processed your packet (your DMA engine could signal an error like no
carrier or anything like that).

> +
> +xmit_final:
> + spin_unlock_irq(&priv->txlock);
> + dev_kfree_skb_any(skb);

This looks wrong, transmit buffer reclaim should happen once
transmission is complete. Your interrupt handler do not handle the
XPKT_FINISH_M bit which seems to indicate transmit completion of
buffers.

[snip]

> +
> +static int moxart_mac_probe(struct platform_device *pdev)
> +{
> + struct device *p_dev = &pdev->dev;
> + struct device_node *node = p_dev->of_node;
> + struct net_device *ndev;
> + struct moxart_mac_priv_t *priv;
> + struct resource *res;
> + unsigned int irq;
> + void *tmp;
> +
> + ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
> + if (!ndev)
> + return -ENOMEM;
> +
> + irq = irq_of_parse_and_map(node, 0);
> +
> + priv = netdev_priv(ndev);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + ndev->base_addr = res->start;
> + priv->base = devm_ioremap_resource(p_dev, res);
> + if (IS_ERR(priv->base)) {
> + dev_err(p_dev, "%s: devm_ioremap_resource res_mac failed\n",
> + __func__);
> + goto init_fail;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +
> + /* the flash driver (physmap_of) requests the same region
> + * so use ioremap instead of devm_ioremap_resource
> + */
> + priv->flash_base = ioremap(res->start, resource_size(res));
> + if (IS_ERR(priv->flash_base)) {
> + dev_err(p_dev, "%s: devm_ioremap_resource res_flash failed\n",
> + __func__);
> + goto init_fail;
> + }

As stated above from the Device Tree binding documentation, this is
unnecessary, since you have a get_mac() function.

> +
> + spin_lock_init(&priv->txlock);
> +
> + tmp = dma_alloc_coherent(NULL, sizeof(struct tx_desc_t) * TX_DESC_NUM,
> + (dma_addr_t *) &priv->phy_tx_desc_baseaddr,
> + GFP_DMA | GFP_KERNEL);
> + priv->virt_tx_desc_baseaddr = (struct tx_desc_t *) tmp;
> + if (priv->virt_tx_desc_baseaddr == NULL ||
> + (priv->phy_tx_desc_baseaddr & 0x0f)) {
> + netdev_err(ndev, "TX descriptor alloc failed\n");
> + goto init_fail;
> + }
> +
> + tmp = dma_alloc_coherent(NULL, sizeof(struct rx_desc_t) * RX_DESC_NUM,
> + (dma_addr_t *)&priv->phy_rx_desc_baseaddr,
> + GFP_DMA | GFP_KERNEL);
> + priv->virt_rx_desc_baseaddr = (struct rx_desc_t *) tmp;
> + if (priv->virt_rx_desc_baseaddr == NULL ||
> + (priv->phy_rx_desc_baseaddr & 0x0f)) {
> + netdev_err(ndev, "RX descriptor alloc failed\n");
> + goto init_fail;
> + }
> +
> + tmp = dma_alloc_coherent(NULL, TX_BUF_SIZE * TX_DESC_NUM,
> + (dma_addr_t *)&priv->phy_tx_buf_baseaddr,
> + GFP_DMA | GFP_KERNEL);
> + priv->virt_tx_buf_baseaddr = (unsigned char *) tmp;
> + if (priv->virt_tx_buf_baseaddr == NULL ||
> + (priv->phy_tx_buf_baseaddr & 0x03)) {
> + netdev_err(ndev, "TX buffer alloc failed\n");
> + goto init_fail;
> + }
> +
> + tmp = dma_alloc_coherent(NULL, RX_BUF_SIZE * RX_DESC_NUM,
> + (dma_addr_t *)&priv->phy_rx_buf_baseaddr,
> + GFP_DMA | GFP_KERNEL);
> + priv->virt_rx_buf_baseaddr = (unsigned char *) tmp;
> + if (priv->virt_rx_buf_baseaddr == NULL ||
> + (priv->phy_rx_buf_baseaddr & 0x03)) {
> + netdev_err(ndev, "RX buffer alloc failed\n");
> + goto init_fail;
> + }

This pool of buffers looks unnecessary, as stated above, you should
rather perform DMA map_single/unmap_single operations instead.
--
Florian

2013-07-26 12:49:38

by Jonas Jensen

[permalink] [raw]
Subject: [PATCH v2] net: Add MOXA ART SoCs ethernet driver

The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
developed internally. The IC used is "RTL8201CP".

Since there is no public documentation, this driver is mostly the one
published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.

Signed-off-by: Jonas Jensen <[email protected]>
---

Notes:
According to issues pointed out by Florian:

One more thing is now known about the controller:

It requires descriptors to be located in continuous memory.

.. which is why the driver allocates buffers with kmalloc,
and memory is reused in RX path to build new skb:s (build_skb),
before giving packet to stack.

Changes since v1:

1. use NAPI RX polling
2. preallocate SKBs / use build_skb
3. increment stats only on success
4. return NETDEV_TX_BUSY on TX ring busy
5. use dma_map_single / set descriptors (no memcpy)
6. free skb:s, unmap DMA on transfer complete
7. general code cleanup

Applies to next-20130726

.../devicetree/bindings/net/moxa,moxart-mac.txt | 25 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/moxa/Kconfig | 30 ++
drivers/net/ethernet/moxa/Makefile | 7 +
drivers/net/ethernet/moxa/moxart_ether.c | 586 +++++++++++++++++++++
drivers/net/ethernet/moxa/moxart_ether.h | 513 ++++++++++++++++++
7 files changed, 1163 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
create mode 100644 drivers/net/ethernet/moxa/Kconfig
create mode 100644 drivers/net/ethernet/moxa/Makefile
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h

diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
new file mode 100644
index 0000000..1fc44ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
@@ -0,0 +1,25 @@
+MOXA ART Ethernet Controller
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-mac"
+- reg : Should contain registers location and length
+ index 0 : main register
+ index 1 : mac address (stored on flash)
+- interrupts : Should contain the mac interrupt number
+
+Example:
+
+ mac0: mac@90900000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x90900000 0x100>,
+ <0x80000050 0x6>;
+ interrupts = <25 0>;
+ };
+
+ mac1: mac@92000000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x92000000 0x100>,
+ <0x80000056 0x6>;
+ interrupts = <27 0>;
+ };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2037080..506b024 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,6 +90,7 @@ source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"

config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 390bd0b..c0b8789 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/moxa/Kconfig b/drivers/net/ethernet/moxa/Kconfig
new file mode 100644
index 0000000..1731e05
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Kconfig
@@ -0,0 +1,30 @@
+#
+# MOXART device configuration
+#
+
+config NET_VENDOR_MOXART
+ bool "MOXA ART devices"
+ default y
+ depends on (ARM && ARCH_MOXART)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ 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 MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MOXART
+
+config ARM_MOXART_ETHER
+ tristate "MOXART Ethernet support"
+ depends on ARM && ARCH_MOXART
+ select NET_CORE
+ ---help---
+ If you wish to compile a kernel for a hardware with MOXA ART SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_MOXART
diff --git a/drivers/net/ethernet/moxa/Makefile b/drivers/net/ethernet/moxa/Makefile
new file mode 100644
index 0000000..e182981
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the MOXART network device drivers.
+#
+
+obj-$(CONFIG_ARM_MOXART_ETHER) += moxart_ether.o
+
+CFLAGS_moxart_ether.o := -DDEBUG
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
new file mode 100644
index 0000000..af73c9c
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -0,0 +1,586 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/crc32.h>
+#include <linux/crc32c.h>
+#include <linux/dma-mapping.h>
+
+#include "moxart_ether.h"
+
+static inline unsigned long moxart_emac_read(struct net_device *ndev,
+ unsigned int reg)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return readl(priv->base + reg);
+}
+
+static inline void moxart_emac_write(struct net_device *ndev,
+ unsigned int reg, unsigned long value)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(value, priv->base + reg);
+}
+
+static void moxart_update_mac_address(struct net_device *ndev)
+{
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET,
+ ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])));
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET + 4,
+ ((ndev->dev_addr[2] << 24) |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5])));
+}
+
+static int moxart_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+ moxart_update_mac_address(ndev);
+
+ return 0;
+}
+
+static void moxart_mac_free_memory(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ dma_unmap_single(&ndev->dev, priv->rx_mapping[i],
+ priv->rx_buf_size, DMA_FROM_DEVICE);
+
+ if (priv->tx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct tx_desc_t)*TX_DESC_NUM,
+ priv->tx_desc_base, priv->tx_base);
+ if (priv->rx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct rx_desc_t)*RX_DESC_NUM,
+ priv->rx_desc_base, priv->rx_base);
+
+ kfree(priv->tx_buf_base);
+ kfree(priv->rx_buf_base);
+}
+
+static void moxart_mac_reset(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(SW_RST, priv->base + MACCR_REG_OFFSET);
+ while (readl(priv->base + MACCR_REG_OFFSET) & SW_RST)
+ mdelay(10);
+
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL;
+}
+
+static void moxart_mac_enable(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(0x00001010, priv->base + ITC_REG_OFFSET);
+ writel(0x00000001, priv->base + APTC_REG_OFFSET);
+ writel(0x00000390, priv->base + DBLAC_REG_OFFSET);
+
+ priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M);
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN);
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+}
+
+static void moxart_mac_setup_desc_ring(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ struct rx_desc_t *rxdesc;
+ int i;
+
+ for (i = 0; i < TX_DESC_NUM; i++) {
+ txdesc = &priv->tx_desc_base[i];
+ memset(txdesc, 0, sizeof(struct tx_desc_t));
+
+ priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i;
+ }
+ priv->tx_desc_base[TX_DESC_NUM - 1].txdes1.ubit.edotr = 1;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ rxdesc = &priv->rx_desc_base[i];
+ memset(rxdesc, 0, sizeof(struct rx_desc_t));
+ rxdesc->rxdes0.ubit.rx_dma_own = 1;
+ rxdesc->rxdes1.ubit.rx_buf_size = RX_BUF_SIZE;
+
+ priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i;
+ priv->rx_mapping[i] = dma_map_single(&ndev->dev,
+ priv->rx_buf[i],
+ priv->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->rx_mapping[i]))
+ netdev_err(ndev, "DMA mapping error\n");
+
+ rxdesc->rxdes2.addr_phys = priv->rx_mapping[i];
+ rxdesc->rxdes2.addr_virt = priv->rx_buf[i];
+ }
+ priv->rx_desc_base[RX_DESC_NUM - 1].rxdes1.ubit.edorr = 1;
+ priv->rx_head = 0;
+
+ /* reset the MAC controler TX/RX desciptor base address */
+ writel(priv->tx_base, priv->base + TXR_BADR_REG_OFFSET);
+ writel(priv->rx_base, priv->base + RXR_BADR_REG_OFFSET);
+}
+
+static int moxart_mac_open(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ napi_enable(&priv->napi);
+
+ moxart_mac_reset(ndev);
+ moxart_update_mac_address(ndev);
+ moxart_mac_setup_desc_ring(ndev);
+ moxart_mac_enable(ndev);
+ netif_start_queue(ndev);
+
+ netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
+ __func__, readl(priv->base + IMR_REG_OFFSET),
+ readl(priv->base + MACCR_REG_OFFSET));
+
+ return 0;
+}
+
+static int moxart_mac_stop(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ napi_disable(&priv->napi);
+
+ netif_stop_queue(ndev);
+
+ /* disable all interrupts */
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ /* disable all functions */
+ writel(0, priv->base + MACCR_REG_OFFSET);
+
+ return 0;
+}
+
+static int moxart_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct moxart_mac_priv_t *priv = container_of(napi,
+ struct moxart_mac_priv_t,
+ napi);
+ struct net_device *ndev = priv->ndev;
+ struct rx_desc_t *rxdesc;
+ struct sk_buff *skb;
+ unsigned int ui, len;
+ int rx_head = priv->rx_head;
+ int rx = 0;
+
+ while (1) {
+ rxdesc = &priv->rx_desc_base[rx_head];
+ ui = rxdesc->rxdes0.ui;
+
+ if (ui & RXDMA_OWN)
+ break;
+
+ if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
+ netdev_err(ndev, "packet error\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ continue;
+ }
+
+ len = ui & RFL_MASK;
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+
+ skb = build_skb(priv->rx_buf[rx_head], priv->rx_buf_size);
+ if (unlikely(!skb)) {
+ netdev_err(ndev, "build_skb failed\n");
+ priv->stats.rx_dropped++;
+ }
+
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+ rx++;
+
+ ndev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ if (ui & MULTICAST_RXDES0)
+ priv->stats.multicast++;
+
+ rxdesc->rxdes0.ui = RXDMA_OWN;
+
+ rx_head = RX_NEXT(rx_head);
+ priv->rx_head = rx_head;
+
+ if (rx >= budget)
+ break;
+ }
+
+ if (rx < budget) {
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+ }
+
+ priv->reg_imr |= RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ return rx;
+}
+
+static void moxart_tx_finished(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+
+ while (tx_tail != tx_head) {
+ dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail],
+ priv->tx_len[tx_tail], DMA_TO_DEVICE);
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
+
+ priv->tx_skb[tx_tail] = NULL;
+
+ tx_tail = TX_NEXT(tx_tail);
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned int ists = readl(priv->base + ISR_REG_OFFSET);
+
+ if (ists & XPKT_OK_INT_STS)
+ moxart_tx_finished(ndev);
+
+ if (ists & RPKT_FINISH) {
+ if (napi_schedule_prep(&priv->napi)) {
+ priv->reg_imr &= ~RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ unsigned int len;
+ unsigned int tx_head = priv->tx_head;
+
+ spin_lock_irq(&priv->txlock);
+
+ txdesc = &priv->tx_desc_base[tx_head];
+
+ if (txdesc->txdes0.ubit.tx_dma_own) {
+ netdev_err(ndev, "no TX space for packet\n");
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
+
+ priv->tx_mapping[tx_head] = dma_map_single(&ndev->dev, skb->data,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->tx_mapping[tx_head])) {
+ netdev_err(ndev, "DMA mapping error\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_len[tx_head] = len;
+ priv->tx_skb[tx_head] = skb;
+
+ txdesc->txdes2.addr_phys = priv->tx_mapping[tx_head];
+ txdesc->txdes2.addr_virt = skb->data;
+
+ if (skb->len < ETH_ZLEN) {
+ memset(&txdesc->txdes2.addr_virt[skb->len],
+ 0, ETH_ZLEN - skb->len);
+ len = ETH_ZLEN;
+ }
+
+ txdesc->txdes1.ubit.lts = 1;
+ txdesc->txdes1.ubit.fts = 1;
+ txdesc->txdes1.ubit.tx2_fic = 0;
+ txdesc->txdes1.ubit.tx_ic = 0;
+ txdesc->txdes1.ubit.tx_buf_size = len;
+ txdesc->txdes0.ui = TXDMA_OWN;
+
+ /* start to send packet */
+ writel(0xffffffff, priv->base + TXPD_REG_OFFSET);
+
+ priv->tx_head = TX_NEXT(tx_head);
+
+ ndev->trans_start = jiffies;
+
+ spin_unlock_irq(&priv->txlock);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return &priv->stats;
+}
+
+static void moxart_mac_setmulticast(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ int crc_val;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_val = crc32_le(~0, ha->addr, ETH_ALEN);
+ crc_val = (crc_val >> 26) & 0x3f;
+ if (crc_val >= 32) {
+ writel(readl(priv->base + MATH1_REG_OFFSET) |
+ (1UL << (crc_val - 32)),
+ priv->base + MATH1_REG_OFFSET);
+ } else {
+ writel(readl(priv->base + MATH0_REG_OFFSET) |
+ (1UL << crc_val),
+ priv->base + MATH0_REG_OFFSET);
+ }
+ }
+}
+
+static void moxart_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ spin_lock_irq(&priv->txlock);
+
+ (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) :
+ (priv->reg_maccr &= ~RCV_ALL);
+
+ (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) :
+ (priv->reg_maccr &= ~RX_MULTIPKT);
+
+ if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) {
+ priv->reg_maccr |= HT_MULTI_EN;
+ moxart_mac_setmulticast(ndev);
+ } else {
+ priv->reg_maccr &= ~HT_MULTI_EN;
+ }
+
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+
+ spin_unlock_irq(&priv->txlock);
+}
+
+static struct net_device_ops moxart_netdev_ops = {
+ .ndo_open = moxart_mac_open,
+ .ndo_stop = moxart_mac_stop,
+ .ndo_start_xmit = moxart_mac_start_xmit,
+ .ndo_get_stats = moxart_mac_get_stats,
+ .ndo_set_rx_mode = moxart_mac_set_rx_mode,
+ .ndo_set_mac_address = moxart_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void moxart_get_mac_address(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i <= 5; i++)
+ ndev->dev_addr[i] = readb(priv->flash_base + i);
+}
+
+static int moxart_mac_probe(struct platform_device *pdev)
+{
+ struct device *p_dev = &pdev->dev;
+ struct device_node *node = p_dev->of_node;
+ struct net_device *ndev;
+ struct moxart_mac_priv_t *priv;
+ struct resource *res;
+ unsigned int irq;
+ void *tmp;
+
+ ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
+ if (!ndev)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(node, 0);
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ndev->base_addr = res->start;
+ priv->base = devm_ioremap_resource(p_dev, res);
+ if (IS_ERR(priv->base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ /* the flash driver (physmap_of) requests the same region
+ * so use ioremap instead of devm_ioremap_resource
+ */
+ priv->flash_base = ioremap(res->start, resource_size(res));
+ if (IS_ERR(priv->flash_base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ spin_lock_init(&priv->txlock);
+
+ priv->tx_buf_size = TX_BUF_SIZE;
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct tx_desc_t) * TX_DESC_NUM,
+ &priv->tx_base, GFP_DMA | GFP_KERNEL);
+ priv->tx_desc_base = (struct tx_desc_t *) tmp;
+ if (priv->tx_desc_base == NULL) {
+ netdev_err(ndev, "TX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct rx_desc_t) * RX_DESC_NUM,
+ &priv->rx_base, GFP_DMA | GFP_KERNEL);
+ priv->rx_desc_base = (struct rx_desc_t *) tmp;
+ if (priv->rx_desc_base == NULL) {
+ netdev_err(ndev, "RX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->tx_buf_base) {
+ netdev_err(ndev, "TX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->rx_buf_base) {
+ netdev_err(ndev, "RX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &moxart_netdev_ops;
+ netif_napi_add(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM);
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ moxart_get_mac_address(ndev);
+ moxart_update_mac_address(ndev);
+
+ if (register_netdev(ndev)) {
+ free_netdev(ndev);
+ goto init_fail;
+ }
+
+ ndev->irq = irq;
+
+ if (devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0,
+ pdev->name, ndev)) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ free_netdev(ndev);
+ return -EBUSY;
+ }
+
+ netdev_dbg(ndev, "%s: IRQ=%d address=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__, ndev->irq,
+ ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
+ ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
+
+ return 0;
+
+init_fail:
+ netdev_err(ndev, "%s: init failed\n", __func__);
+ moxart_mac_free_memory(ndev);
+
+ return -ENOMEM;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ moxart_mac_free_memory(ndev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_mac_match[] = {
+ { .compatible = "moxa,moxart-mac" },
+ { }
+};
+
+struct __initdata platform_driver moxart_mac_driver = {
+ .probe = moxart_mac_probe,
+ .remove = moxart_remove,
+ .driver = {
+ .name = "moxart-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_mac_match,
+ },
+};
+module_platform_driver(moxart_mac_driver);
+
+MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <[email protected]>");
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
new file mode 100644
index 0000000..790b6a9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -0,0 +1,513 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MOXART_ETHERNET_H
+#define _MOXART_ETHERNET_H
+
+#define TX_DESC_NUM 64
+#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
+#define TX_BUF_SIZE 1600
+
+#define RX_DESC_NUM 64
+#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
+#define RX_BUF_SIZE 1600
+
+struct tx_desc_t {
+ union {
+#define TXDMA_OWN BIT(31)
+#define TXPKT_EXSCOL BIT(1)
+#define TXPKT_LATECOL BIT(0)
+ unsigned int ui;
+
+ struct {
+ /* is aborted due to late collision */
+ unsigned int tx_pkt_late_col:1;
+
+ /* is aborted after 16 collisions */
+ unsigned int rx_pkt_exs_col:1;
+
+ unsigned int reserved1:29;
+
+ /* is owned by the MAC controller */
+ unsigned int tx_dma_own:1;
+ } ubit;
+ } txdes0;
+
+ union {
+#define EDOTR BIT(31)
+#define TXIC BIT(30)
+#define TX2FIC BIT(29)
+#define FTS BIT(28)
+#define LTS BIT(27)
+#define TXBUF_SIZE_MASK 0x7ff
+#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* transmit buffer size in byte */
+ unsigned int tx_buf_size:11;
+
+ unsigned int reserved2:16;
+
+ /* is the last descriptor of a Tx packet */
+ unsigned int lts:1;
+
+ /* is the first descriptor of a Tx packet */
+ unsigned int fts:1;
+
+ /* transmit to FIFO interrupt on completion */
+ unsigned int tx2_fic:1;
+
+ /* transmit interrupt on completion */
+ unsigned int tx_ic:1;
+
+ /* end descriptor of transmit ring */
+ unsigned int edotr:1;
+ } ubit;
+ } txdes1;
+
+ struct {
+ /* transmit buffer physical base address */
+ unsigned int addr_phys;
+
+ /* transmit buffer virtual base address */
+ unsigned char *addr_virt;
+ } txdes2;
+};
+
+struct rx_desc_t {
+ union {
+#define RXDMA_OWN BIT(31)
+#define FRS BIT(29)
+#define LRS BIT(28)
+#define RX_ODD_NB BIT(22)
+#define RUNT BIT(21)
+#define FTL BIT(20)
+#define CRC_ERR BIT(19)
+#define RX_ERR BIT(18)
+#define BROADCAST_RXDES0 BIT(17)
+#define MULTICAST_RXDES0 BIT(16)
+#define RFL_MASK 0x7ff
+#define RFL_MAX (RFL_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive frame length */
+ unsigned int recv_frame_len:11;
+ unsigned int reserved1:5;
+
+ /* multicast frame */
+ unsigned int multicast:1;
+
+ /* broadcast frame */
+ unsigned int broadcast:1;
+ unsigned int rx_err:1; /* receive error */
+ unsigned int crc_err:1; /* CRC error */
+ unsigned int ftl:1; /* frame too long */
+
+ /* runt packet, less than 64 bytes */
+ unsigned int runt:1;
+
+ /* receive odd nibbles */
+ unsigned int rx_odd_nb:1;
+ unsigned int reserved2:5;
+
+ /* last receive segment descriptor */
+ unsigned int lrs:1;
+
+ /* first receive segment descriptor */
+ unsigned int frs:1;
+
+ unsigned int reserved3:1;
+ unsigned int rx_dma_own:1; /* RXDMA onwership */
+ } ubit;
+ } rxdes0;
+
+ union {
+#define EDORR BIT(31)
+#define RXBUF_SIZE_MASK 0x7ff
+#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive buffer size */
+ unsigned int rx_buf_size:11;
+
+ unsigned int reserved4:20;
+
+ /* end descriptor of receive ring */
+ unsigned int edorr:1;
+ } ubit;
+ } rxdes1;
+
+ struct {
+ /* receive buffer physical base address */
+ unsigned int addr_phys;
+
+ /* receive buffer virtual base address */
+ unsigned char *addr_virt;
+ } rxdes2;
+};
+
+struct mac_control_reg_t {
+
+/* RXDMA has received packets into RX buffer successfully */
+#define RPKT_FINISH BIT(0)
+/* receive buffer unavailable */
+#define NORXBUF BIT(1)
+/* TXDMA has moved data into the TX FIFO */
+#define XPKT_FINISH BIT(2)
+/* transmit buffer unavailable */
+#define NOTXBUF BIT(3)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK_INT_STS BIT(4)
+/* packets transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+#define XPKT_LOST_INT_STS BIT(5)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAV BIT(6)
+/* received packet lost due to RX FIFO full */
+#define RPKT_LOST_INT_STS BIT(7)
+#define AHB_ERR BIT(8) /* AHB error */
+#define PHYSTS_CHG BIT(9) /* PHY link status change */
+ unsigned int isr; /* interrupt status, 0x0 */
+
+#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
+#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
+#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
+#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
+#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
+#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
+#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
+#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
+#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
+#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
+ unsigned int imr; /* interrupt mask, 0x4 */
+
+/* the most significant 2 bytes of MAC address */
+#define MAC_MADR_MASK 0xffff
+ /* MAC most significant address, 0x8 */
+ unsigned int mac_madr;
+
+ /* MAC least significant address, 0xc */
+ unsigned int mac_ldar;
+
+ /* multicast address hash table 0, 0x10 */
+ unsigned int matht0;
+
+ /* multicast address hash table 1, 0x14 */
+ unsigned int matht1;
+
+ /* transmit poll demand, 0x18 */
+ unsigned int txpd;
+
+ /* receive poll demand, 0x1c */
+ unsigned int rxpd;
+
+ /* transmit ring base address, 0x20 */
+ unsigned int txr_badr;
+
+ /* receive ring base address, 0x24 */
+ unsigned int rxr_badr;
+
+/* defines the period of TX cycle time */
+#define TXINT_TIME_SEL BIT(15)
+#define TXINT_THR_MASK 0x7000
+#define TXINT_CNT_MASK 0xf00
+/* defines the period of RX cycle time */
+#define RXINT_TIME_SEL BIT(7)
+#define RXINT_THR_MASK 0x70
+#define RXINT_CNT_MASK 0xF
+ /* interrupt timer control, 0x28 */
+ unsigned int itc;
+
+/* defines the period of TX poll time */
+#define TXPOLL_TIME_SEL BIT(12)
+#define TXPOLL_CNT_MASK 0xf00
+#define TXPOLL_CNT_SHIFT_BIT 8
+/* defines the period of RX poll time */
+#define RXPOLL_TIME_SEL BIT(4)
+#define RXPOLL_CNT_MASK 0xF
+#define RXPOLL_CNT_SHIFT_BIT 0
+ /* automatic polling timer control, 0x2c */
+ unsigned int aptc;
+
+/* enable RX FIFO threshold arbitration */
+#define RX_THR_EN BIT(9)
+#define RXFIFO_HTHR_MASK 0x1c0
+#define RXFIFO_LTHR_MASK 0x38
+/* use INCR16 burst command in AHB bus */
+#define INCR16_EN BIT(2)
+/* use INCR8 burst command in AHB bus */
+#define INCR8_EN BIT(1)
+/* use INCR4 burst command in AHB bus */
+#define INCR4_EN BIT(0)
+ /* DMA burst length and arbitration control, 0x30 */
+ unsigned int dblac;
+
+ unsigned int reserved1[21]; /* 0x34 - 0x84 */
+
+#define RX_BROADPKT BIT(17) /* receive boradcast packet */
+/* receive all multicast packet */
+#define RX_MULTIPKT BIT(16)
+#define FULLDUP BIT(15) /* full duplex */
+/* append CRC to transmitted packet */
+#define CRC_APD BIT(14)
+/* do not check incoming packet's destination address */
+#define RCV_ALL BIT(12)
+/* store incoming packet even if its length is great than 1518 bytes */
+#define RX_FTL BIT(11)
+/* store incoming packet even if its length is less than 64 bytes */
+#define RX_RUNT BIT(10)
+/* enable storing incoming packet if the packet passes hash table
+ * address filtering and is a multicast packet
+ */
+#define HT_MULTI_EN BIT(9)
+#define RCV_EN BIT(8) /* receiver enable */
+/* enable packet reception when transmitting packet in half duplex mode */
+#define ENRX_IN_HALFTX BIT(6)
+#define XMT_EN BIT(5) /* transmitter enable */
+/* disable CRC check when receiving packets */
+#define CRC_DIS BIT(4)
+#define LOOP_EN BIT(3) /* internal loop-back */
+/* software reset, last 64 AHB bus clocks */
+#define SW_RST BIT(2)
+#define RDMA_EN BIT(1) /* enable receive DMA chan */
+#define XDMA_EN BIT(0) /* enable transmit DMA chan */
+ unsigned int maccr; /* MAC control, 0x88 */
+
+#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
+/* transmitter detects late collision */
+#define LATE_COL BIT(10)
+/* packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+#define XPKT_LOST BIT(9)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK BIT(8)
+/* receiver detects a runt packet */
+#define RUNT_MAC_STS BIT(7)
+/* receiver detects a frame that is too long */
+#define FTL_MAC_STS BIT(6)
+#define CRC_ERR_MAC_STS BIT(5)
+/* received packets list due to RX FIFO full */
+#define RPKT_LOST BIT(4)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAVE BIT(3)
+/* incoming packet dropped due to collision */
+#define COL BIT(2)
+#define MCPU_BROADCAST BIT(1)
+#define MCPU_MULTICAST BIT(0)
+ unsigned int macsr; /* MAC status, 0x8c */
+
+/* initialize a write sequence to PHY by setting this bit to 1
+ * this bit would be auto cleared after the write operation is finished.
+ */
+#define MIIWR BIT(27)
+#define MIIRD BIT(26)
+#define REGAD_MASK 0x3e00000
+#define PHYAD_MASK 0x1f0000
+#define MIIRDATA_MASK 0xffff
+ unsigned int phycr; /* PHY control, 0x90 */
+
+#define MIIWDATA_MASK 0xffff
+ unsigned int phywdata; /* PHY write data, 0x94 */
+
+#define PAUSE_TIME_MASK 0xffff0000
+#define FC_HIGH_MASK 0xf000
+#define FC_LOW_MASK 0xf00
+#define RX_PAUSE BIT(4) /* receive pause frame */
+/* packet transmission is paused due to receive */
+#define TXPAUSED BIT(3)
+ /* pause frame */
+/* enable flow control threshold mode. */
+#define FCTHR_EN BIT(2)
+#define TX_PAUSE BIT(1) /* transmit pause frame */
+#define FC_EN BIT(0) /* flow control mode enable */
+ unsigned int fcr; /* flow control, 0x98 */
+
+#define BK_LOW_MASK 0xf00
+#define BKJAM_LEN_MASK 0xf0
+#define BK_MODE BIT(1) /* back pressure address mode */
+#define BK_EN BIT(0) /* back pressure mode enable */
+ unsigned int bpr; /* back pressure, 0x9c */
+
+ unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
+
+#define TEST_SEED_MASK 0x3fff
+ unsigned int ts; /* test seed, 0xc4 */
+
+#define TXD_REQ BIT(31) /* TXDMA request */
+#define RXD_REQ BIT(30) /* RXDMA request */
+#define DARB_TXGNT BIT(29) /* TXDMA grant */
+#define DARB_RXGNT BIT(28) /* RXDMA grant */
+#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
+#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
+#define TXDMA2_SM_MASK 0x7000
+#define TXDMA1_SM_MASK 0xf00
+#define RXDMA2_SM_MASK 0x70
+#define RXDMA1_SM_MASK 0xF
+ unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
+
+#define SINGLE_PKT BIT(26) /* single packet mode */
+/* automatic polling timer test mode */
+#define PTIMER_TEST BIT(25)
+#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
+#define TEST_SEED_SEL BIT(22) /* test seed select */
+#define SEED_SEL BIT(21) /* seed select */
+#define TEST_MODE BIT(20) /* transmission test mode */
+#define TEST_TIME_MASK 0xffc00
+#define TEST_EXCEL_MASK 0x3e0
+ unsigned int tm; /* test mode, 0xcc */
+
+ unsigned int reserved3; /* 0xd0 */
+
+#define TX_MCOL_MASK 0xffff0000
+#define TX_MCOL_SHIFT_BIT 16
+#define TX_SCOL_MASK 0xffff
+#define TX_SCOL_SHIFT_BIT 0
+ /* TX_MCOL and TX_SCOL counter, 0xd4 */
+ unsigned int txmcol_xscol;
+
+#define RPF_MASK 0xffff0000
+#define RPF_SHIFT_BIT 16
+#define AEP_MASK 0xffff
+#define AEP_SHIFT_BIT 0
+ unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
+
+#define XM_MASK 0xffff0000
+#define XM_SHIFT_BIT 16
+#define PG_MASK 0xffff
+#define PG_SHIFT_BIT 0
+ unsigned int xm_pg; /* XM and PG counter, 0xdc */
+
+#define RUNT_CNT_MASK 0xffff0000
+#define RUNT_CNT_SHIFT_BIT 16
+#define TLCC_MASK 0xffff
+#define TLCC_SHIFT_BIT 0
+ /* RUNT_CNT and TLCC counter, 0xe0 */
+ unsigned int runtcnt_tlcc;
+
+#define CRCER_CNT_MASK 0xffff0000
+#define CRCER_CNT_SHIFT_BIT 16
+#define FTL_CNT_MASK 0xffff
+#define FTL_CNT_SHIFT_BIT 0
+ /* CRCER_CNT and FTL_CNT counter, 0xe4 */
+ unsigned int crcercnt_ftlcnt;
+
+#define RLC_MASK 0xffff0000
+#define RLC_SHIFT_BIT 16
+#define RCC_MASK 0xffff
+#define RCC_SHIFT_BIT 0
+ unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
+
+ unsigned int broc; /* BROC counter, 0xec */
+ unsigned int mulca; /* MULCA counter, 0xf0 */
+ unsigned int rp; /* RP counter, 0xf4 */
+ unsigned int xp; /* XP counter, 0xf8 */
+};
+
+#define ISR_REG_OFFSET 0x0
+#define IMR_REG_OFFSET 0x4
+#define MAC_MADR_REG_OFFSET 0x8
+#define MAC_LADR_REG_OFFSET 0xC
+#define MATH0_REG_OFFSET 0x10
+#define MATH1_REG_OFFSET 0x14
+#define TXPD_REG_OFFSET 0x18
+#define RXPD_REG_OFFSET 0x1C
+#define TXR_BADR_REG_OFFSET 0x20
+#define RXR_BADR_REG_OFFSET 0x24
+#define ITC_REG_OFFSET 0x28
+#define APTC_REG_OFFSET 0x2C
+#define DBLAC_REG_OFFSET 0x30
+#define MACCR_REG_OFFSET 0x88
+#define MACSR_REG_OFFSET 0x8C
+#define PHYCR_REG_OFFSET 0x90
+#define PHYWDATA_REG_OFFSET 0x94
+#define FCR_REG_OFFSET 0x98
+#define BPR_REG_OFFSET 0x9C
+#define TS_REG_OFFSET 0xC4
+#define DMAFIFOS_REG_OFFSET 0xC8
+#define TM_REG_OFFSET 0xCC
+#define TX_MCOL_TX_SCOL_REG_OFFSET 0xD4
+#define RPF_AEP_REG_OFFSET 0xD8
+#define XM_PG_REG_OFFSET 0xDC
+#define RUNT_CNT_TLCC_REG_OFFSET 0xE0
+#define CRCER_CNT_FTL_CNT_REG_OFFSET 0xE4
+#define RLC_RCC_REG_OFFSET 0xE8
+#define BROC_REG_OFFSET 0xEC
+#define MULCA_REG_OFFSET 0xF0
+#define RP_REG_OFFSET 0xF4
+#define XP_REG_OFFSET 0xF8
+#define PHY_CNTL_REG 0x0
+#define PHY_STATUS_REG 0x1
+#define PHY_ID_REG1 0x2
+#define PHY_ID_REG2 0x3
+#define PHY_ANA_REG 0x4
+#define PHY_ANLPAR_REG 0x5
+#define PHY_ANE_REG 0x6
+#define PHY_ECNTL_REG1 0x10
+#define PHY_QPDS_REG 0x11
+#define PHY_10BOP_REG 0x12
+#define PHY_ECNTL_REG2 0x13
+#define FTMAC100_REG_PHY_WRITE 0x8000000
+#define FTMAC100_REG_PHY_READ 0x4000000
+
+/* PHY Status register */
+#define AN_COMPLETE 0x20
+
+#define LINK_STATUS 0x4
+
+struct moxart_mac_priv_t {
+ void __iomem *base;
+ void __iomem *flash_base;
+ struct net_device_stats stats;
+ unsigned int reg_maccr;
+ unsigned int reg_imr;
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ dma_addr_t rx_base;
+ dma_addr_t rx_mapping[RX_DESC_NUM];
+ struct rx_desc_t *rx_desc_base;
+ unsigned char *rx_buf_base;
+ unsigned char *rx_buf[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ dma_addr_t tx_base;
+ dma_addr_t tx_mapping[TX_DESC_NUM];
+ struct tx_desc_t *tx_desc_base;
+ unsigned char *tx_buf_base;
+ unsigned char *tx_buf[RX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_buf_size;
+
+ spinlock_t txlock;
+ unsigned int tx_len[TX_DESC_NUM];
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ unsigned int tx_tail;
+};
+
+#if TX_BUF_SIZE >= TXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Tx buffer size too large !
+#endif
+#if RX_BUF_SIZE >= RXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Rx buffer size too large !
+#endif
+
+#endif
--
1.8.2.1

2013-07-26 13:12:05

by Jonas Jensen

[permalink] [raw]
Subject: Re: [PATCH] net: Add MOXA ART SoCs ethernet driver

Hi Florian,

Thanks for your help on IRC with this. I think most of the issues you
pointed out should be fixed. Comments on still unresolved below:

On 22 July 2013 13:09, Florian Fainelli <[email protected]> wrote:
>> +++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
>> @@ -0,0 +1,25 @@
>> +MOXA ART Ethernet Controller
>> +
>> +Required properties:
>> +
>> +- compatible : Should be "moxa,moxart-mac"
>> +- reg : Should contain registers location and length
>> + index 0 : main register
>> + index 1 : mac address (stored on flash)
>
> That is kind of unusual, since the MAC register is within the range of
> the MAC base register, just read the MAC from the base register and
> use that to fill in dev->dev_addr in your ndo_open() callback.

It is, but MAC is zeroed out in controller register after boot. That's why it
has to be read from flash memory (bootloader section), and the reason
it needs to map flash memory at all.

>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +
>> + /* the flash driver (physmap_of) requests the same region
>> + * so use ioremap instead of devm_ioremap_resource
>> + */
>> + priv->flash_base = ioremap(res->start, resource_size(res));
>> + if (IS_ERR(priv->flash_base)) {
>> + dev_err(p_dev, "%s: devm_ioremap_resource res_flash failed\n",
>> + __func__);
>> + goto init_fail;
>> + }
>
> As stated above from the Device Tree binding documentation, this is
> unnecessary, since you have a get_mac() function.

This is what the node looks like right now:

mac0: mac@90900000 {
compatible = "moxa,moxart-mac";
reg = <0x90900000 0x100>,
<0x80000050 0x6>;
interrupts = <25 0>;
};

Bootloader starts at 0x80000000, looks like MAC is stored in code. I
currently see no way around mapping both registers.

Best regards,
Jonas

2013-07-30 23:24:36

by David Miller

[permalink] [raw]
Subject: Re: [PATCH v2] net: Add MOXA ART SoCs ethernet driver

From: Jonas Jensen <[email protected]>
Date: Fri, 26 Jul 2013 14:48:10 +0200

> +
> +CFLAGS_moxart_ether.o := -DDEBUG

This is not appropriate, please remove this.

2013-07-31 14:20:51

by Jonas Jensen

[permalink] [raw]
Subject: [PATCH v3] net: Add MOXA ART SoCs ethernet driver

The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
developed internally. The IC used is "RTL8201CP".

Since there is no public documentation, this driver is mostly the one
published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.

Signed-off-by: Jonas Jensen <[email protected]>
---

Notes:
Changes since v2:

1. Makefile: remove debug flag

Applies to next-20130731

.../devicetree/bindings/net/moxa,moxart-mac.txt | 25 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/moxa/Kconfig | 30 ++
drivers/net/ethernet/moxa/Makefile | 5 +
drivers/net/ethernet/moxa/moxart_ether.c | 586 +++++++++++++++++++++
drivers/net/ethernet/moxa/moxart_ether.h | 513 ++++++++++++++++++
7 files changed, 1161 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
create mode 100644 drivers/net/ethernet/moxa/Kconfig
create mode 100644 drivers/net/ethernet/moxa/Makefile
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h

diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
new file mode 100644
index 0000000..1fc44ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
@@ -0,0 +1,25 @@
+MOXA ART Ethernet Controller
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-mac"
+- reg : Should contain registers location and length
+ index 0 : main register
+ index 1 : mac address (stored on flash)
+- interrupts : Should contain the mac interrupt number
+
+Example:
+
+ mac0: mac@90900000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x90900000 0x100>,
+ <0x80000050 0x6>;
+ interrupts = <25 0>;
+ };
+
+ mac1: mac@92000000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x92000000 0x100>,
+ <0x80000056 0x6>;
+ interrupts = <27 0>;
+ };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2037080..506b024 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,6 +90,7 @@ source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"

config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 390bd0b..c0b8789 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/moxa/Kconfig b/drivers/net/ethernet/moxa/Kconfig
new file mode 100644
index 0000000..1731e05
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Kconfig
@@ -0,0 +1,30 @@
+#
+# MOXART device configuration
+#
+
+config NET_VENDOR_MOXART
+ bool "MOXA ART devices"
+ default y
+ depends on (ARM && ARCH_MOXART)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ 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 MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MOXART
+
+config ARM_MOXART_ETHER
+ tristate "MOXART Ethernet support"
+ depends on ARM && ARCH_MOXART
+ select NET_CORE
+ ---help---
+ If you wish to compile a kernel for a hardware with MOXA ART SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_MOXART
diff --git a/drivers/net/ethernet/moxa/Makefile b/drivers/net/ethernet/moxa/Makefile
new file mode 100644
index 0000000..aa3c73e9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the MOXART network device drivers.
+#
+
+obj-$(CONFIG_ARM_MOXART_ETHER) += moxart_ether.o
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
new file mode 100644
index 0000000..af73c9c
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -0,0 +1,586 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/crc32.h>
+#include <linux/crc32c.h>
+#include <linux/dma-mapping.h>
+
+#include "moxart_ether.h"
+
+static inline unsigned long moxart_emac_read(struct net_device *ndev,
+ unsigned int reg)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return readl(priv->base + reg);
+}
+
+static inline void moxart_emac_write(struct net_device *ndev,
+ unsigned int reg, unsigned long value)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(value, priv->base + reg);
+}
+
+static void moxart_update_mac_address(struct net_device *ndev)
+{
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET,
+ ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])));
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET + 4,
+ ((ndev->dev_addr[2] << 24) |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5])));
+}
+
+static int moxart_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+ moxart_update_mac_address(ndev);
+
+ return 0;
+}
+
+static void moxart_mac_free_memory(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ dma_unmap_single(&ndev->dev, priv->rx_mapping[i],
+ priv->rx_buf_size, DMA_FROM_DEVICE);
+
+ if (priv->tx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct tx_desc_t)*TX_DESC_NUM,
+ priv->tx_desc_base, priv->tx_base);
+ if (priv->rx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct rx_desc_t)*RX_DESC_NUM,
+ priv->rx_desc_base, priv->rx_base);
+
+ kfree(priv->tx_buf_base);
+ kfree(priv->rx_buf_base);
+}
+
+static void moxart_mac_reset(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(SW_RST, priv->base + MACCR_REG_OFFSET);
+ while (readl(priv->base + MACCR_REG_OFFSET) & SW_RST)
+ mdelay(10);
+
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL;
+}
+
+static void moxart_mac_enable(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(0x00001010, priv->base + ITC_REG_OFFSET);
+ writel(0x00000001, priv->base + APTC_REG_OFFSET);
+ writel(0x00000390, priv->base + DBLAC_REG_OFFSET);
+
+ priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M);
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN);
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+}
+
+static void moxart_mac_setup_desc_ring(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ struct rx_desc_t *rxdesc;
+ int i;
+
+ for (i = 0; i < TX_DESC_NUM; i++) {
+ txdesc = &priv->tx_desc_base[i];
+ memset(txdesc, 0, sizeof(struct tx_desc_t));
+
+ priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i;
+ }
+ priv->tx_desc_base[TX_DESC_NUM - 1].txdes1.ubit.edotr = 1;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ rxdesc = &priv->rx_desc_base[i];
+ memset(rxdesc, 0, sizeof(struct rx_desc_t));
+ rxdesc->rxdes0.ubit.rx_dma_own = 1;
+ rxdesc->rxdes1.ubit.rx_buf_size = RX_BUF_SIZE;
+
+ priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i;
+ priv->rx_mapping[i] = dma_map_single(&ndev->dev,
+ priv->rx_buf[i],
+ priv->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->rx_mapping[i]))
+ netdev_err(ndev, "DMA mapping error\n");
+
+ rxdesc->rxdes2.addr_phys = priv->rx_mapping[i];
+ rxdesc->rxdes2.addr_virt = priv->rx_buf[i];
+ }
+ priv->rx_desc_base[RX_DESC_NUM - 1].rxdes1.ubit.edorr = 1;
+ priv->rx_head = 0;
+
+ /* reset the MAC controler TX/RX desciptor base address */
+ writel(priv->tx_base, priv->base + TXR_BADR_REG_OFFSET);
+ writel(priv->rx_base, priv->base + RXR_BADR_REG_OFFSET);
+}
+
+static int moxart_mac_open(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ napi_enable(&priv->napi);
+
+ moxart_mac_reset(ndev);
+ moxart_update_mac_address(ndev);
+ moxart_mac_setup_desc_ring(ndev);
+ moxart_mac_enable(ndev);
+ netif_start_queue(ndev);
+
+ netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
+ __func__, readl(priv->base + IMR_REG_OFFSET),
+ readl(priv->base + MACCR_REG_OFFSET));
+
+ return 0;
+}
+
+static int moxart_mac_stop(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ napi_disable(&priv->napi);
+
+ netif_stop_queue(ndev);
+
+ /* disable all interrupts */
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ /* disable all functions */
+ writel(0, priv->base + MACCR_REG_OFFSET);
+
+ return 0;
+}
+
+static int moxart_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct moxart_mac_priv_t *priv = container_of(napi,
+ struct moxart_mac_priv_t,
+ napi);
+ struct net_device *ndev = priv->ndev;
+ struct rx_desc_t *rxdesc;
+ struct sk_buff *skb;
+ unsigned int ui, len;
+ int rx_head = priv->rx_head;
+ int rx = 0;
+
+ while (1) {
+ rxdesc = &priv->rx_desc_base[rx_head];
+ ui = rxdesc->rxdes0.ui;
+
+ if (ui & RXDMA_OWN)
+ break;
+
+ if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
+ netdev_err(ndev, "packet error\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ continue;
+ }
+
+ len = ui & RFL_MASK;
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+
+ skb = build_skb(priv->rx_buf[rx_head], priv->rx_buf_size);
+ if (unlikely(!skb)) {
+ netdev_err(ndev, "build_skb failed\n");
+ priv->stats.rx_dropped++;
+ }
+
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+ rx++;
+
+ ndev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ if (ui & MULTICAST_RXDES0)
+ priv->stats.multicast++;
+
+ rxdesc->rxdes0.ui = RXDMA_OWN;
+
+ rx_head = RX_NEXT(rx_head);
+ priv->rx_head = rx_head;
+
+ if (rx >= budget)
+ break;
+ }
+
+ if (rx < budget) {
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+ }
+
+ priv->reg_imr |= RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ return rx;
+}
+
+static void moxart_tx_finished(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+
+ while (tx_tail != tx_head) {
+ dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail],
+ priv->tx_len[tx_tail], DMA_TO_DEVICE);
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
+
+ priv->tx_skb[tx_tail] = NULL;
+
+ tx_tail = TX_NEXT(tx_tail);
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned int ists = readl(priv->base + ISR_REG_OFFSET);
+
+ if (ists & XPKT_OK_INT_STS)
+ moxart_tx_finished(ndev);
+
+ if (ists & RPKT_FINISH) {
+ if (napi_schedule_prep(&priv->napi)) {
+ priv->reg_imr &= ~RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ unsigned int len;
+ unsigned int tx_head = priv->tx_head;
+
+ spin_lock_irq(&priv->txlock);
+
+ txdesc = &priv->tx_desc_base[tx_head];
+
+ if (txdesc->txdes0.ubit.tx_dma_own) {
+ netdev_err(ndev, "no TX space for packet\n");
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
+
+ priv->tx_mapping[tx_head] = dma_map_single(&ndev->dev, skb->data,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->tx_mapping[tx_head])) {
+ netdev_err(ndev, "DMA mapping error\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_len[tx_head] = len;
+ priv->tx_skb[tx_head] = skb;
+
+ txdesc->txdes2.addr_phys = priv->tx_mapping[tx_head];
+ txdesc->txdes2.addr_virt = skb->data;
+
+ if (skb->len < ETH_ZLEN) {
+ memset(&txdesc->txdes2.addr_virt[skb->len],
+ 0, ETH_ZLEN - skb->len);
+ len = ETH_ZLEN;
+ }
+
+ txdesc->txdes1.ubit.lts = 1;
+ txdesc->txdes1.ubit.fts = 1;
+ txdesc->txdes1.ubit.tx2_fic = 0;
+ txdesc->txdes1.ubit.tx_ic = 0;
+ txdesc->txdes1.ubit.tx_buf_size = len;
+ txdesc->txdes0.ui = TXDMA_OWN;
+
+ /* start to send packet */
+ writel(0xffffffff, priv->base + TXPD_REG_OFFSET);
+
+ priv->tx_head = TX_NEXT(tx_head);
+
+ ndev->trans_start = jiffies;
+
+ spin_unlock_irq(&priv->txlock);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return &priv->stats;
+}
+
+static void moxart_mac_setmulticast(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ int crc_val;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_val = crc32_le(~0, ha->addr, ETH_ALEN);
+ crc_val = (crc_val >> 26) & 0x3f;
+ if (crc_val >= 32) {
+ writel(readl(priv->base + MATH1_REG_OFFSET) |
+ (1UL << (crc_val - 32)),
+ priv->base + MATH1_REG_OFFSET);
+ } else {
+ writel(readl(priv->base + MATH0_REG_OFFSET) |
+ (1UL << crc_val),
+ priv->base + MATH0_REG_OFFSET);
+ }
+ }
+}
+
+static void moxart_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ spin_lock_irq(&priv->txlock);
+
+ (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) :
+ (priv->reg_maccr &= ~RCV_ALL);
+
+ (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) :
+ (priv->reg_maccr &= ~RX_MULTIPKT);
+
+ if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) {
+ priv->reg_maccr |= HT_MULTI_EN;
+ moxart_mac_setmulticast(ndev);
+ } else {
+ priv->reg_maccr &= ~HT_MULTI_EN;
+ }
+
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+
+ spin_unlock_irq(&priv->txlock);
+}
+
+static struct net_device_ops moxart_netdev_ops = {
+ .ndo_open = moxart_mac_open,
+ .ndo_stop = moxart_mac_stop,
+ .ndo_start_xmit = moxart_mac_start_xmit,
+ .ndo_get_stats = moxart_mac_get_stats,
+ .ndo_set_rx_mode = moxart_mac_set_rx_mode,
+ .ndo_set_mac_address = moxart_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void moxart_get_mac_address(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i <= 5; i++)
+ ndev->dev_addr[i] = readb(priv->flash_base + i);
+}
+
+static int moxart_mac_probe(struct platform_device *pdev)
+{
+ struct device *p_dev = &pdev->dev;
+ struct device_node *node = p_dev->of_node;
+ struct net_device *ndev;
+ struct moxart_mac_priv_t *priv;
+ struct resource *res;
+ unsigned int irq;
+ void *tmp;
+
+ ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
+ if (!ndev)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(node, 0);
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ndev->base_addr = res->start;
+ priv->base = devm_ioremap_resource(p_dev, res);
+ if (IS_ERR(priv->base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ /* the flash driver (physmap_of) requests the same region
+ * so use ioremap instead of devm_ioremap_resource
+ */
+ priv->flash_base = ioremap(res->start, resource_size(res));
+ if (IS_ERR(priv->flash_base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ spin_lock_init(&priv->txlock);
+
+ priv->tx_buf_size = TX_BUF_SIZE;
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct tx_desc_t) * TX_DESC_NUM,
+ &priv->tx_base, GFP_DMA | GFP_KERNEL);
+ priv->tx_desc_base = (struct tx_desc_t *) tmp;
+ if (priv->tx_desc_base == NULL) {
+ netdev_err(ndev, "TX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct rx_desc_t) * RX_DESC_NUM,
+ &priv->rx_base, GFP_DMA | GFP_KERNEL);
+ priv->rx_desc_base = (struct rx_desc_t *) tmp;
+ if (priv->rx_desc_base == NULL) {
+ netdev_err(ndev, "RX descriptor alloc failed\n");
+ goto init_fail;
+ }
+
+ priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->tx_buf_base) {
+ netdev_err(ndev, "TX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->rx_buf_base) {
+ netdev_err(ndev, "RX buffer alloc failed\n");
+ goto init_fail;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &moxart_netdev_ops;
+ netif_napi_add(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM);
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ moxart_get_mac_address(ndev);
+ moxart_update_mac_address(ndev);
+
+ if (register_netdev(ndev)) {
+ free_netdev(ndev);
+ goto init_fail;
+ }
+
+ ndev->irq = irq;
+
+ if (devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0,
+ pdev->name, ndev)) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ free_netdev(ndev);
+ return -EBUSY;
+ }
+
+ netdev_dbg(ndev, "%s: IRQ=%d address=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__, ndev->irq,
+ ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
+ ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);
+
+ return 0;
+
+init_fail:
+ netdev_err(ndev, "%s: init failed\n", __func__);
+ moxart_mac_free_memory(ndev);
+
+ return -ENOMEM;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ moxart_mac_free_memory(ndev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_mac_match[] = {
+ { .compatible = "moxa,moxart-mac" },
+ { }
+};
+
+struct __initdata platform_driver moxart_mac_driver = {
+ .probe = moxart_mac_probe,
+ .remove = moxart_remove,
+ .driver = {
+ .name = "moxart-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_mac_match,
+ },
+};
+module_platform_driver(moxart_mac_driver);
+
+MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <[email protected]>");
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
new file mode 100644
index 0000000..790b6a9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -0,0 +1,513 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MOXART_ETHERNET_H
+#define _MOXART_ETHERNET_H
+
+#define TX_DESC_NUM 64
+#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
+#define TX_BUF_SIZE 1600
+
+#define RX_DESC_NUM 64
+#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
+#define RX_BUF_SIZE 1600
+
+struct tx_desc_t {
+ union {
+#define TXDMA_OWN BIT(31)
+#define TXPKT_EXSCOL BIT(1)
+#define TXPKT_LATECOL BIT(0)
+ unsigned int ui;
+
+ struct {
+ /* is aborted due to late collision */
+ unsigned int tx_pkt_late_col:1;
+
+ /* is aborted after 16 collisions */
+ unsigned int rx_pkt_exs_col:1;
+
+ unsigned int reserved1:29;
+
+ /* is owned by the MAC controller */
+ unsigned int tx_dma_own:1;
+ } ubit;
+ } txdes0;
+
+ union {
+#define EDOTR BIT(31)
+#define TXIC BIT(30)
+#define TX2FIC BIT(29)
+#define FTS BIT(28)
+#define LTS BIT(27)
+#define TXBUF_SIZE_MASK 0x7ff
+#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* transmit buffer size in byte */
+ unsigned int tx_buf_size:11;
+
+ unsigned int reserved2:16;
+
+ /* is the last descriptor of a Tx packet */
+ unsigned int lts:1;
+
+ /* is the first descriptor of a Tx packet */
+ unsigned int fts:1;
+
+ /* transmit to FIFO interrupt on completion */
+ unsigned int tx2_fic:1;
+
+ /* transmit interrupt on completion */
+ unsigned int tx_ic:1;
+
+ /* end descriptor of transmit ring */
+ unsigned int edotr:1;
+ } ubit;
+ } txdes1;
+
+ struct {
+ /* transmit buffer physical base address */
+ unsigned int addr_phys;
+
+ /* transmit buffer virtual base address */
+ unsigned char *addr_virt;
+ } txdes2;
+};
+
+struct rx_desc_t {
+ union {
+#define RXDMA_OWN BIT(31)
+#define FRS BIT(29)
+#define LRS BIT(28)
+#define RX_ODD_NB BIT(22)
+#define RUNT BIT(21)
+#define FTL BIT(20)
+#define CRC_ERR BIT(19)
+#define RX_ERR BIT(18)
+#define BROADCAST_RXDES0 BIT(17)
+#define MULTICAST_RXDES0 BIT(16)
+#define RFL_MASK 0x7ff
+#define RFL_MAX (RFL_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive frame length */
+ unsigned int recv_frame_len:11;
+ unsigned int reserved1:5;
+
+ /* multicast frame */
+ unsigned int multicast:1;
+
+ /* broadcast frame */
+ unsigned int broadcast:1;
+ unsigned int rx_err:1; /* receive error */
+ unsigned int crc_err:1; /* CRC error */
+ unsigned int ftl:1; /* frame too long */
+
+ /* runt packet, less than 64 bytes */
+ unsigned int runt:1;
+
+ /* receive odd nibbles */
+ unsigned int rx_odd_nb:1;
+ unsigned int reserved2:5;
+
+ /* last receive segment descriptor */
+ unsigned int lrs:1;
+
+ /* first receive segment descriptor */
+ unsigned int frs:1;
+
+ unsigned int reserved3:1;
+ unsigned int rx_dma_own:1; /* RXDMA onwership */
+ } ubit;
+ } rxdes0;
+
+ union {
+#define EDORR BIT(31)
+#define RXBUF_SIZE_MASK 0x7ff
+#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive buffer size */
+ unsigned int rx_buf_size:11;
+
+ unsigned int reserved4:20;
+
+ /* end descriptor of receive ring */
+ unsigned int edorr:1;
+ } ubit;
+ } rxdes1;
+
+ struct {
+ /* receive buffer physical base address */
+ unsigned int addr_phys;
+
+ /* receive buffer virtual base address */
+ unsigned char *addr_virt;
+ } rxdes2;
+};
+
+struct mac_control_reg_t {
+
+/* RXDMA has received packets into RX buffer successfully */
+#define RPKT_FINISH BIT(0)
+/* receive buffer unavailable */
+#define NORXBUF BIT(1)
+/* TXDMA has moved data into the TX FIFO */
+#define XPKT_FINISH BIT(2)
+/* transmit buffer unavailable */
+#define NOTXBUF BIT(3)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK_INT_STS BIT(4)
+/* packets transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+#define XPKT_LOST_INT_STS BIT(5)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAV BIT(6)
+/* received packet lost due to RX FIFO full */
+#define RPKT_LOST_INT_STS BIT(7)
+#define AHB_ERR BIT(8) /* AHB error */
+#define PHYSTS_CHG BIT(9) /* PHY link status change */
+ unsigned int isr; /* interrupt status, 0x0 */
+
+#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
+#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
+#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
+#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
+#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
+#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
+#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
+#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
+#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
+#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
+ unsigned int imr; /* interrupt mask, 0x4 */
+
+/* the most significant 2 bytes of MAC address */
+#define MAC_MADR_MASK 0xffff
+ /* MAC most significant address, 0x8 */
+ unsigned int mac_madr;
+
+ /* MAC least significant address, 0xc */
+ unsigned int mac_ldar;
+
+ /* multicast address hash table 0, 0x10 */
+ unsigned int matht0;
+
+ /* multicast address hash table 1, 0x14 */
+ unsigned int matht1;
+
+ /* transmit poll demand, 0x18 */
+ unsigned int txpd;
+
+ /* receive poll demand, 0x1c */
+ unsigned int rxpd;
+
+ /* transmit ring base address, 0x20 */
+ unsigned int txr_badr;
+
+ /* receive ring base address, 0x24 */
+ unsigned int rxr_badr;
+
+/* defines the period of TX cycle time */
+#define TXINT_TIME_SEL BIT(15)
+#define TXINT_THR_MASK 0x7000
+#define TXINT_CNT_MASK 0xf00
+/* defines the period of RX cycle time */
+#define RXINT_TIME_SEL BIT(7)
+#define RXINT_THR_MASK 0x70
+#define RXINT_CNT_MASK 0xF
+ /* interrupt timer control, 0x28 */
+ unsigned int itc;
+
+/* defines the period of TX poll time */
+#define TXPOLL_TIME_SEL BIT(12)
+#define TXPOLL_CNT_MASK 0xf00
+#define TXPOLL_CNT_SHIFT_BIT 8
+/* defines the period of RX poll time */
+#define RXPOLL_TIME_SEL BIT(4)
+#define RXPOLL_CNT_MASK 0xF
+#define RXPOLL_CNT_SHIFT_BIT 0
+ /* automatic polling timer control, 0x2c */
+ unsigned int aptc;
+
+/* enable RX FIFO threshold arbitration */
+#define RX_THR_EN BIT(9)
+#define RXFIFO_HTHR_MASK 0x1c0
+#define RXFIFO_LTHR_MASK 0x38
+/* use INCR16 burst command in AHB bus */
+#define INCR16_EN BIT(2)
+/* use INCR8 burst command in AHB bus */
+#define INCR8_EN BIT(1)
+/* use INCR4 burst command in AHB bus */
+#define INCR4_EN BIT(0)
+ /* DMA burst length and arbitration control, 0x30 */
+ unsigned int dblac;
+
+ unsigned int reserved1[21]; /* 0x34 - 0x84 */
+
+#define RX_BROADPKT BIT(17) /* receive boradcast packet */
+/* receive all multicast packet */
+#define RX_MULTIPKT BIT(16)
+#define FULLDUP BIT(15) /* full duplex */
+/* append CRC to transmitted packet */
+#define CRC_APD BIT(14)
+/* do not check incoming packet's destination address */
+#define RCV_ALL BIT(12)
+/* store incoming packet even if its length is great than 1518 bytes */
+#define RX_FTL BIT(11)
+/* store incoming packet even if its length is less than 64 bytes */
+#define RX_RUNT BIT(10)
+/* enable storing incoming packet if the packet passes hash table
+ * address filtering and is a multicast packet
+ */
+#define HT_MULTI_EN BIT(9)
+#define RCV_EN BIT(8) /* receiver enable */
+/* enable packet reception when transmitting packet in half duplex mode */
+#define ENRX_IN_HALFTX BIT(6)
+#define XMT_EN BIT(5) /* transmitter enable */
+/* disable CRC check when receiving packets */
+#define CRC_DIS BIT(4)
+#define LOOP_EN BIT(3) /* internal loop-back */
+/* software reset, last 64 AHB bus clocks */
+#define SW_RST BIT(2)
+#define RDMA_EN BIT(1) /* enable receive DMA chan */
+#define XDMA_EN BIT(0) /* enable transmit DMA chan */
+ unsigned int maccr; /* MAC control, 0x88 */
+
+#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
+/* transmitter detects late collision */
+#define LATE_COL BIT(10)
+/* packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+#define XPKT_LOST BIT(9)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK BIT(8)
+/* receiver detects a runt packet */
+#define RUNT_MAC_STS BIT(7)
+/* receiver detects a frame that is too long */
+#define FTL_MAC_STS BIT(6)
+#define CRC_ERR_MAC_STS BIT(5)
+/* received packets list due to RX FIFO full */
+#define RPKT_LOST BIT(4)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAVE BIT(3)
+/* incoming packet dropped due to collision */
+#define COL BIT(2)
+#define MCPU_BROADCAST BIT(1)
+#define MCPU_MULTICAST BIT(0)
+ unsigned int macsr; /* MAC status, 0x8c */
+
+/* initialize a write sequence to PHY by setting this bit to 1
+ * this bit would be auto cleared after the write operation is finished.
+ */
+#define MIIWR BIT(27)
+#define MIIRD BIT(26)
+#define REGAD_MASK 0x3e00000
+#define PHYAD_MASK 0x1f0000
+#define MIIRDATA_MASK 0xffff
+ unsigned int phycr; /* PHY control, 0x90 */
+
+#define MIIWDATA_MASK 0xffff
+ unsigned int phywdata; /* PHY write data, 0x94 */
+
+#define PAUSE_TIME_MASK 0xffff0000
+#define FC_HIGH_MASK 0xf000
+#define FC_LOW_MASK 0xf00
+#define RX_PAUSE BIT(4) /* receive pause frame */
+/* packet transmission is paused due to receive */
+#define TXPAUSED BIT(3)
+ /* pause frame */
+/* enable flow control threshold mode. */
+#define FCTHR_EN BIT(2)
+#define TX_PAUSE BIT(1) /* transmit pause frame */
+#define FC_EN BIT(0) /* flow control mode enable */
+ unsigned int fcr; /* flow control, 0x98 */
+
+#define BK_LOW_MASK 0xf00
+#define BKJAM_LEN_MASK 0xf0
+#define BK_MODE BIT(1) /* back pressure address mode */
+#define BK_EN BIT(0) /* back pressure mode enable */
+ unsigned int bpr; /* back pressure, 0x9c */
+
+ unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
+
+#define TEST_SEED_MASK 0x3fff
+ unsigned int ts; /* test seed, 0xc4 */
+
+#define TXD_REQ BIT(31) /* TXDMA request */
+#define RXD_REQ BIT(30) /* RXDMA request */
+#define DARB_TXGNT BIT(29) /* TXDMA grant */
+#define DARB_RXGNT BIT(28) /* RXDMA grant */
+#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
+#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
+#define TXDMA2_SM_MASK 0x7000
+#define TXDMA1_SM_MASK 0xf00
+#define RXDMA2_SM_MASK 0x70
+#define RXDMA1_SM_MASK 0xF
+ unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
+
+#define SINGLE_PKT BIT(26) /* single packet mode */
+/* automatic polling timer test mode */
+#define PTIMER_TEST BIT(25)
+#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
+#define TEST_SEED_SEL BIT(22) /* test seed select */
+#define SEED_SEL BIT(21) /* seed select */
+#define TEST_MODE BIT(20) /* transmission test mode */
+#define TEST_TIME_MASK 0xffc00
+#define TEST_EXCEL_MASK 0x3e0
+ unsigned int tm; /* test mode, 0xcc */
+
+ unsigned int reserved3; /* 0xd0 */
+
+#define TX_MCOL_MASK 0xffff0000
+#define TX_MCOL_SHIFT_BIT 16
+#define TX_SCOL_MASK 0xffff
+#define TX_SCOL_SHIFT_BIT 0
+ /* TX_MCOL and TX_SCOL counter, 0xd4 */
+ unsigned int txmcol_xscol;
+
+#define RPF_MASK 0xffff0000
+#define RPF_SHIFT_BIT 16
+#define AEP_MASK 0xffff
+#define AEP_SHIFT_BIT 0
+ unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
+
+#define XM_MASK 0xffff0000
+#define XM_SHIFT_BIT 16
+#define PG_MASK 0xffff
+#define PG_SHIFT_BIT 0
+ unsigned int xm_pg; /* XM and PG counter, 0xdc */
+
+#define RUNT_CNT_MASK 0xffff0000
+#define RUNT_CNT_SHIFT_BIT 16
+#define TLCC_MASK 0xffff
+#define TLCC_SHIFT_BIT 0
+ /* RUNT_CNT and TLCC counter, 0xe0 */
+ unsigned int runtcnt_tlcc;
+
+#define CRCER_CNT_MASK 0xffff0000
+#define CRCER_CNT_SHIFT_BIT 16
+#define FTL_CNT_MASK 0xffff
+#define FTL_CNT_SHIFT_BIT 0
+ /* CRCER_CNT and FTL_CNT counter, 0xe4 */
+ unsigned int crcercnt_ftlcnt;
+
+#define RLC_MASK 0xffff0000
+#define RLC_SHIFT_BIT 16
+#define RCC_MASK 0xffff
+#define RCC_SHIFT_BIT 0
+ unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
+
+ unsigned int broc; /* BROC counter, 0xec */
+ unsigned int mulca; /* MULCA counter, 0xf0 */
+ unsigned int rp; /* RP counter, 0xf4 */
+ unsigned int xp; /* XP counter, 0xf8 */
+};
+
+#define ISR_REG_OFFSET 0x0
+#define IMR_REG_OFFSET 0x4
+#define MAC_MADR_REG_OFFSET 0x8
+#define MAC_LADR_REG_OFFSET 0xC
+#define MATH0_REG_OFFSET 0x10
+#define MATH1_REG_OFFSET 0x14
+#define TXPD_REG_OFFSET 0x18
+#define RXPD_REG_OFFSET 0x1C
+#define TXR_BADR_REG_OFFSET 0x20
+#define RXR_BADR_REG_OFFSET 0x24
+#define ITC_REG_OFFSET 0x28
+#define APTC_REG_OFFSET 0x2C
+#define DBLAC_REG_OFFSET 0x30
+#define MACCR_REG_OFFSET 0x88
+#define MACSR_REG_OFFSET 0x8C
+#define PHYCR_REG_OFFSET 0x90
+#define PHYWDATA_REG_OFFSET 0x94
+#define FCR_REG_OFFSET 0x98
+#define BPR_REG_OFFSET 0x9C
+#define TS_REG_OFFSET 0xC4
+#define DMAFIFOS_REG_OFFSET 0xC8
+#define TM_REG_OFFSET 0xCC
+#define TX_MCOL_TX_SCOL_REG_OFFSET 0xD4
+#define RPF_AEP_REG_OFFSET 0xD8
+#define XM_PG_REG_OFFSET 0xDC
+#define RUNT_CNT_TLCC_REG_OFFSET 0xE0
+#define CRCER_CNT_FTL_CNT_REG_OFFSET 0xE4
+#define RLC_RCC_REG_OFFSET 0xE8
+#define BROC_REG_OFFSET 0xEC
+#define MULCA_REG_OFFSET 0xF0
+#define RP_REG_OFFSET 0xF4
+#define XP_REG_OFFSET 0xF8
+#define PHY_CNTL_REG 0x0
+#define PHY_STATUS_REG 0x1
+#define PHY_ID_REG1 0x2
+#define PHY_ID_REG2 0x3
+#define PHY_ANA_REG 0x4
+#define PHY_ANLPAR_REG 0x5
+#define PHY_ANE_REG 0x6
+#define PHY_ECNTL_REG1 0x10
+#define PHY_QPDS_REG 0x11
+#define PHY_10BOP_REG 0x12
+#define PHY_ECNTL_REG2 0x13
+#define FTMAC100_REG_PHY_WRITE 0x8000000
+#define FTMAC100_REG_PHY_READ 0x4000000
+
+/* PHY Status register */
+#define AN_COMPLETE 0x20
+
+#define LINK_STATUS 0x4
+
+struct moxart_mac_priv_t {
+ void __iomem *base;
+ void __iomem *flash_base;
+ struct net_device_stats stats;
+ unsigned int reg_maccr;
+ unsigned int reg_imr;
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ dma_addr_t rx_base;
+ dma_addr_t rx_mapping[RX_DESC_NUM];
+ struct rx_desc_t *rx_desc_base;
+ unsigned char *rx_buf_base;
+ unsigned char *rx_buf[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ dma_addr_t tx_base;
+ dma_addr_t tx_mapping[TX_DESC_NUM];
+ struct tx_desc_t *tx_desc_base;
+ unsigned char *tx_buf_base;
+ unsigned char *tx_buf[RX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_buf_size;
+
+ spinlock_t txlock;
+ unsigned int tx_len[TX_DESC_NUM];
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ unsigned int tx_tail;
+};
+
+#if TX_BUF_SIZE >= TXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Tx buffer size too large !
+#endif
+#if RX_BUF_SIZE >= RXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Rx buffer size too large !
+#endif
+
+#endif
--
1.8.2.1

2013-07-31 18:56:41

by Joe Perches

[permalink] [raw]
Subject: Re: [PATCH v3] net: Add MOXA ART SoCs ethernet driver

On Wed, 2013-07-31 at 16:20 +0200, Jonas Jensen wrote:
> The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
> developed internally. The IC used is "RTL8201CP".

Just some simple comments...

> diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
[]
> +static int moxart_rx_poll(struct napi_struct *napi, int budget)
[]
> + while (1) {
[]
> + if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
> + netdev_err(ndev, "packet error\n");

This is generally a bad idea as it can flood
the log. It's probably better to remove it.
If you really want logging, then at least use
net_ratelimit(). Generally, the stats are
enough.

> + priv->stats.rx_dropped++;
> + priv->stats.rx_errors++;
[]
> +static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
[]
> + if (txdesc->txdes0.ubit.tx_dma_own) {
> + netdev_err(ndev, "no TX space for packet\n");
> + priv->stats.tx_dropped++;

here too.

> +static int moxart_mac_probe(struct platform_device *pdev)
[]
> + priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
> + GFP_ATOMIC);
> + if (!priv->tx_buf_base) {
> + netdev_err(ndev, "TX buffer alloc failed\n");
> + goto init_fail;
> + }
> +
> + priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
> + GFP_ATOMIC);
> + if (!priv->rx_buf_base) {
> + netdev_err(ndev, "RX buffer alloc failed\n");
> + goto init_fail;
> + }

Extra OOM messages aren't useful, there's a generic
OOM and a dump_stack from the kernel.

> + netdev_dbg(ndev, "%s: IRQ=%d address=%02x:%02x:%02x:%02x:%02x:%02x\n",
> + __func__, ndev->irq,
> + ndev->dev_addr[0], ndev->dev_addr[1], ndev->dev_addr[2],
> + ndev->dev_addr[3], ndev->dev_addr[4], ndev->dev_addr[5]);

There's a mac address helper you can use:

netdev_dbg(ndev, "%s: IRQ=%d address=%pM\n"
__func__, ndev->irq, ndev->dev_addr);

2013-08-01 00:04:49

by David Miller

[permalink] [raw]
Subject: Re: [PATCH v3] net: Add MOXA ART SoCs ethernet driver

From: Joe Perches <[email protected]>
Date: Wed, 31 Jul 2013 11:56:38 -0700

>> + if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
>> + netdev_err(ndev, "packet error\n");
>
> This is generally a bad idea as it can flood
> the log. It's probably better to remove it.
> If you really want logging, then at least use
> net_ratelimit(). Generally, the stats are
> enough.
...
>> + priv->stats.rx_dropped++;
>> + priv->stats.rx_errors++;
> []
>> +static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
> []
>> + if (txdesc->txdes0.ubit.tx_dma_own) {
>> + netdev_err(ndev, "no TX space for packet\n");
>> + priv->stats.tx_dropped++;
>
> here too.

Agreed, these should not be logged, the statistics are sufficient.

2013-08-01 09:40:08

by Jonas Jensen

[permalink] [raw]
Subject: [PATCH v4] net: Add MOXA ART SoCs ethernet driver

The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
developed internally. The IC used is "RTL8201CP".

Since there is no public documentation, this driver is mostly the one
published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.

Signed-off-by: Jonas Jensen <[email protected]>
---

Notes:
Thanks, changes from your feedback is enclosed.

I have since found a problem occurring on high load. Please point out
anything the driver can do differently to eliminate it.

The error only occurs during external ping, i.e. ping -s 50000 from
10.0.1.200 to hardware.

After the error, ping reports 100% packet loss from the hardware, but
it's still possible to ping in outgoing direction.

Changes since v3:

1. don't flood the log, use net_dbg_ratelimited(), not netdev_err()
2. free TX skb:s after stats increment tx_bytes, not before
3. don't print extra message on alloc failure
4. use %pM extended format printk specifier
5. increment rx_errors if build_skb() fails

Applies to next-20130731

Interestingly, on the old port, 1980 is the largest allowed size,
ping segfaults between sizes 1976 to 1980:

uname -a
Linux Moxa 2.6.9-uc0 #230 Fri Mar 5 11:04:24 CST 2010 armv4tl unknown
root@Moxa:/# ping -s 1981 10.0.1.200
ping: packet size too large.
root@Moxa:/# ping -s 1980 10.0.1.200
Segmentation fault
root@Moxa:/# ping -s 1976 10.0.1.200
Segmentation fault
root@Moxa:/# ping -s 1975 10.0.1.200
PING (10.0.1.200): 1975 data bytes
1983 bytes from 10.0.1.200: icmp_seq=0 ttl=64 time=6.9 ms
1983 bytes from 10.0.1.200: icmp_seq=1 ttl=64 time=16.2 ms

uname -a
Linux (none) 3.11.0-rc3-next-20130731+ #200 PREEMPT Thu Aug 1 10:09:16 CEST 2013
armv4l GNU/Linux
ping -s 50000 10.0.1.200
PING 10.0.1.200 (10.0.1.200): 50000 data bytes
50008 bytes from 10.0.1.200: seq=0 ttl=64 time=36.294 ms
50008 bytes from 10.0.1.200: seq=1 ttl=64 time=35.708 ms
50008 bytes from 10.0.1.200: seq=2 ttl=64 time=35.763 ms
50008 bytes from 10.0.1.200: seq=3 ttl=64 time=35.703 ms
50008 bytes from 10.0.1.200: seq=4 ttl=64 time=35.789 ms
50008 bytes from 10.0.1.200: seq=5 ttl=64 time=35.647 ms
50008 bytes from 10.0.1.200: seq=6 ttl=64 time=36.012 ms
50008 bytes from 10.0.1.200: seq=7 ttl=64 time=35.650 ms
50008 bytes from 10.0.1.200: seq=8 ttl=64 time=35.700 ms
50008 bytes from 10.0.1.200: seq=9 ttl=64 time=35.845 ms
50008 bytes from 10.0.1.200: seq=10 ttl=64 time=35.886 ms
^C
--- 10.0.1.200 ping statistics ---
11 packets transmitted, 11 packets received, 0% packet loss
round-trip min/avg/max = 35.647/35.817/36.294 ms
ping -s 50000 10.0.1.200
PING 10.0.1.200 (10.0.1.200): 50000 data bytes
50008 bytes from 10.0.1.200: seq=0 ttl=64 time=35.694 ms
ping: recvfrom: Bad address
[ 33.340000] BUG: Bad page state in process ping pfn:019c0
[ 33.350000] page:c082e800 count:-59 mapcount:0 mapping: (null) index:0x0
[ 33.360000] page flags: 0x0()
[ 33.360000] CPU: 0 PID: 229 Comm: ping Not tainted 3.11.0-rc3-next-20130731+ #198
[ 33.390000] [<c000c680>] (unwind_backtrace+0x0/0xf0) from [<c000b294>] (show_stack+0x10/0x14)
[ 33.400000] [<c000b294>] (show_stack+0x10/0x14) from [<c0060404>] (bad_page+0xa8/0x108)
[ 33.410000] [<c0060404>] (bad_page+0xa8/0x108) from [<c00609dc>] (get_page_from_freelist+0x474/0x594)
[ 33.420000] [<c00609dc>] (get_page_from_freelist+0x474/0x594) from [<c0061424>] (__alloc_pages_nodemask+0xcc/0x794)
[ 33.430000] [<c0061424>] (__alloc_pages_nodemask+0xcc/0x794) from [<c00852d0>] (new_slab+0x64/0x2dc)
[ 33.440000] [<c00852d0>] (new_slab+0x64/0x2dc) from [<c021b17c>] (__slab_alloc.isra.57.constprop.59+0x4cc/0x5f0)
[ 33.450000] [<c021b17c>] (__slab_alloc.isra.57.constprop.59+0x4cc/0x5f0) from [<c0086f20>] (__kmalloc_track_caller+0x120/0x180)
[ 33.460000] [<c0086f20>] (__kmalloc_track_caller+0x120/0x180) from [<c01a33c4>] (__kmalloc_reserve.isra.16+0x24/0x70)
[ 33.470000] [<c01a33c4>] (__kmalloc_reserve.isra.16+0x24/0x70) from [<c01a34bc>] (__alloc_skb+0x5c/0x148)
[ 33.480000] [<c01a34bc>] (__alloc_skb+0x5c/0x148) from [<c01a00d8>] (sock_wmalloc+0x38/0x88)
[ 33.490000] [<c01a00d8>] (sock_wmalloc+0x38/0x88) from [<c01d3dfc>] (__ip_append_data+0x8cc/0x980)
[ 33.500000] [<c01d3dfc>] (__ip_append_data+0x8cc/0x980) from [<c01d59b4>] (ip_append_data+0x94/0xbc)
[ 33.510000] [<c01d59b4>] (ip_append_data+0x94/0xbc) from [<c01f6350>] (raw_sendmsg+0x2e8/0x858)
[ 33.520000] [<c01f6350>] (raw_sendmsg+0x2e8/0x858) from [<c020247c>] (inet_sendmsg+0x3c/0x70)
[ 33.530000] [<c020247c>] (inet_sendmsg+0x3c/0x70) from [<c019c5c4>] (sock_sendmsg+0x78/0x8c)
[ 33.540000] [<c019c5c4>] (sock_sendmsg+0x78/0x8c) from [<c019d830>] (SyS_sendto+0xb4/0xd4)
[ 33.550000] [<c019d830>] (SyS_sendto+0xb4/0xd4) from [<c0009080>] (ret_fast_syscall+0x0/0x44)
[ 33.560000] BUG: Bad page state in process ping pfn:019c4
[ 33.560000] page:c082e880 count:-38 mapcount:0 mapping: (null) index:0x0
[ 33.570000] page flags: 0x0()
[ 33.570000] CPU: 0 PID: 229 Comm: ping Tainted: G B 3.11.0-rc3-next-20130731+ #198
[ 33.580000] [<c000c680>] (unwind_backtrace+0x0/0xf0) from [<c000b294>] (show_stack+0x10/0x14)
[ 33.590000] [<c000b294>] (show_stack+0x10/0x14) from [<c0060404>] (bad_page+0xa8/0x108)
[ 33.600000] [<c0060404>] (bad_page+0xa8/0x108) from [<c00609dc>] (get_page_from_freelist+0x474/0x594)
[ 33.610000] [<c00609dc>] (get_page_from_freelist+0x474/0x594) from [<c0061424>] (__alloc_pages_nodemask+0xcc/0x794)
[ 33.620000] [<c0061424>] (__alloc_pages_nodemask+0xcc/0x794) from [<c00852d0>] (new_slab+0x64/0x2dc)
[ 33.630000] [<c00852d0>] (new_slab+0x64/0x2dc) from [<c021b17c>] (__slab_alloc.isra.57.constprop.59+0x4cc/0x5f0)
[ 33.640000] [<c021b17c>] (__slab_alloc.isra.57.constprop.59+0x4cc/0x5f0) from [<c0086f20>] (__kmalloc_track_caller+0x120/0x180)
[ 33.650000] [<c0086f20>] (__kmalloc_track_caller+0x120/0x180) from [<c01a33c4>] (__kmalloc_reserve.isra.16+0x24/0x70)
[ 33.660000] [<c01a33c4>] (__kmalloc_reserve.isra.16+0x24/0x70) from [<c01a34bc>] (__alloc_skb+0x5c/0x148)
[ 33.670000] [<c01a34bc>] (__alloc_skb+0x5c/0x148) from [<c01a00d8>] (sock_wmalloc+0x38/0x88)
[ 33.680000] [<c01a00d8>] (sock_wmalloc+0x38/0x88) from [<c01d3dfc>] (__ip_append_data+0x8cc/0x980)
[ 33.690000] [<c01d3dfc>] (__ip_append_data+0x8cc/0x980) from [<c01d59b4>] (ip_append_data+0x94/0xbc)
[ 33.700000] [<c01d59b4>] (ip_append_data+0x94/0xbc) from [<c01f6350>] (raw_sendmsg+0x2e8/0x858)
[ 33.710000] [<c01f6350>] (raw_sendmsg+0x2e8/0x858) from [<c020247c>] (inet_sendmsg+0x3c/0x70)
[ 33.720000] [<c020247c>] (inet_sendmsg+0x3c/0x70) from [<c019c5c4>] (sock_sendmsg+0x78/0x8c)
[ 33.730000] [<c019c5c4>] (sock_sendmsg+0x78/0x8c) from [<c019d830>] (SyS_sendto+0xb4/0xd4)
[ 33.740000] [<c019d830>] (SyS_sendto+0xb4/0xd4) from [<c0009080>] (ret_fast_syscall+0x0/0x44)
...
http://ideone.com/2FkfMB

.../devicetree/bindings/net/moxa,moxart-mac.txt | 25 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/moxa/Kconfig | 30 ++
drivers/net/ethernet/moxa/Makefile | 5 +
drivers/net/ethernet/moxa/moxart_ether.c | 577 +++++++++++++++++++++
drivers/net/ethernet/moxa/moxart_ether.h | 513 ++++++++++++++++++
7 files changed, 1152 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
create mode 100644 drivers/net/ethernet/moxa/Kconfig
create mode 100644 drivers/net/ethernet/moxa/Makefile
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h

diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
new file mode 100644
index 0000000..1fc44ff
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
@@ -0,0 +1,25 @@
+MOXA ART Ethernet Controller
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-mac"
+- reg : Should contain registers location and length
+ index 0 : main register
+ index 1 : mac address (stored on flash)
+- interrupts : Should contain the mac interrupt number
+
+Example:
+
+ mac0: mac@90900000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x90900000 0x100>,
+ <0x80000050 0x6>;
+ interrupts = <25 0>;
+ };
+
+ mac1: mac@92000000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x92000000 0x100>,
+ <0x80000056 0x6>;
+ interrupts = <27 0>;
+ };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2037080..506b024 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,6 +90,7 @@ source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"

config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 390bd0b..c0b8789 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/moxa/Kconfig b/drivers/net/ethernet/moxa/Kconfig
new file mode 100644
index 0000000..1731e05
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Kconfig
@@ -0,0 +1,30 @@
+#
+# MOXART device configuration
+#
+
+config NET_VENDOR_MOXART
+ bool "MOXA ART devices"
+ default y
+ depends on (ARM && ARCH_MOXART)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ 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 MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MOXART
+
+config ARM_MOXART_ETHER
+ tristate "MOXART Ethernet support"
+ depends on ARM && ARCH_MOXART
+ select NET_CORE
+ ---help---
+ If you wish to compile a kernel for a hardware with MOXA ART SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_MOXART
diff --git a/drivers/net/ethernet/moxa/Makefile b/drivers/net/ethernet/moxa/Makefile
new file mode 100644
index 0000000..aa3c73e9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the MOXART network device drivers.
+#
+
+obj-$(CONFIG_ARM_MOXART_ETHER) += moxart_ether.o
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
new file mode 100644
index 0000000..8856f4e
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -0,0 +1,577 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/crc32.h>
+#include <linux/crc32c.h>
+#include <linux/dma-mapping.h>
+
+#include "moxart_ether.h"
+
+static inline unsigned long moxart_emac_read(struct net_device *ndev,
+ unsigned int reg)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return readl(priv->base + reg);
+}
+
+static inline void moxart_emac_write(struct net_device *ndev,
+ unsigned int reg, unsigned long value)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(value, priv->base + reg);
+}
+
+static void moxart_update_mac_address(struct net_device *ndev)
+{
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET,
+ ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])));
+ moxart_emac_write(ndev, MAC_MADR_REG_OFFSET + 4,
+ ((ndev->dev_addr[2] << 24) |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5])));
+}
+
+static int moxart_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+ moxart_update_mac_address(ndev);
+
+ return 0;
+}
+
+static void moxart_mac_free_memory(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ dma_unmap_single(&ndev->dev, priv->rx_mapping[i],
+ priv->rx_buf_size, DMA_FROM_DEVICE);
+
+ if (priv->tx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct tx_desc_t)*TX_DESC_NUM,
+ priv->tx_desc_base, priv->tx_base);
+ if (priv->rx_desc_base)
+ dma_free_coherent(NULL, sizeof(struct rx_desc_t)*RX_DESC_NUM,
+ priv->rx_desc_base, priv->rx_base);
+
+ kfree(priv->tx_buf_base);
+ kfree(priv->rx_buf_base);
+}
+
+static void moxart_mac_reset(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(SW_RST, priv->base + MACCR_REG_OFFSET);
+ while (readl(priv->base + MACCR_REG_OFFSET) & SW_RST)
+ mdelay(10);
+
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL;
+}
+
+static void moxart_mac_enable(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(0x00001010, priv->base + ITC_REG_OFFSET);
+ writel(0x00000001, priv->base + APTC_REG_OFFSET);
+ writel(0x00000390, priv->base + DBLAC_REG_OFFSET);
+
+ priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M);
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN);
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+}
+
+static void moxart_mac_setup_desc_ring(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ struct rx_desc_t *rxdesc;
+ int i;
+
+ for (i = 0; i < TX_DESC_NUM; i++) {
+ txdesc = &priv->tx_desc_base[i];
+ memset(txdesc, 0, sizeof(struct tx_desc_t));
+
+ priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i;
+ }
+ priv->tx_desc_base[TX_DESC_NUM - 1].txdes1.ubit.edotr = 1;
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ rxdesc = &priv->rx_desc_base[i];
+ memset(rxdesc, 0, sizeof(struct rx_desc_t));
+ rxdesc->rxdes0.ubit.rx_dma_own = 1;
+ rxdesc->rxdes1.ubit.rx_buf_size = RX_BUF_SIZE;
+
+ priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i;
+ priv->rx_mapping[i] = dma_map_single(&ndev->dev,
+ priv->rx_buf[i],
+ priv->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->rx_mapping[i]))
+ netdev_err(ndev, "DMA mapping error\n");
+
+ rxdesc->rxdes2.addr_phys = priv->rx_mapping[i];
+ rxdesc->rxdes2.addr_virt = priv->rx_buf[i];
+ }
+ priv->rx_desc_base[RX_DESC_NUM - 1].rxdes1.ubit.edorr = 1;
+ priv->rx_head = 0;
+
+ /* reset the MAC controler TX/RX desciptor base address */
+ writel(priv->tx_base, priv->base + TXR_BADR_REG_OFFSET);
+ writel(priv->rx_base, priv->base + RXR_BADR_REG_OFFSET);
+}
+
+static int moxart_mac_open(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ napi_enable(&priv->napi);
+
+ moxart_mac_reset(ndev);
+ moxart_update_mac_address(ndev);
+ moxart_mac_setup_desc_ring(ndev);
+ moxart_mac_enable(ndev);
+ netif_start_queue(ndev);
+
+ netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
+ __func__, readl(priv->base + IMR_REG_OFFSET),
+ readl(priv->base + MACCR_REG_OFFSET));
+
+ return 0;
+}
+
+static int moxart_mac_stop(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ napi_disable(&priv->napi);
+
+ netif_stop_queue(ndev);
+
+ /* disable all interrupts */
+ writel(0, priv->base + IMR_REG_OFFSET);
+
+ /* disable all functions */
+ writel(0, priv->base + MACCR_REG_OFFSET);
+
+ return 0;
+}
+
+static int moxart_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct moxart_mac_priv_t *priv = container_of(napi,
+ struct moxart_mac_priv_t,
+ napi);
+ struct net_device *ndev = priv->ndev;
+ struct rx_desc_t *rxdesc;
+ struct sk_buff *skb;
+ unsigned int ui, len;
+ int rx_head = priv->rx_head;
+ int rx = 0;
+
+ while (1) {
+ rxdesc = &priv->rx_desc_base[rx_head];
+ ui = rxdesc->rxdes0.ui;
+
+ if (ui & RXDMA_OWN)
+ break;
+
+ if (ui & (RX_ERR | CRC_ERR | FTL | RUNT | RX_ODD_NB)) {
+ net_dbg_ratelimited("packet error\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ continue;
+ }
+
+ len = ui & RFL_MASK;
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+
+ skb = build_skb(priv->rx_buf[rx_head], priv->rx_buf_size);
+ if (unlikely(!skb)) {
+ net_dbg_ratelimited("build_skb failed\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ }
+
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+ rx++;
+
+ ndev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ if (ui & MULTICAST_RXDES0)
+ priv->stats.multicast++;
+
+ rxdesc->rxdes0.ui = RXDMA_OWN;
+
+ rx_head = RX_NEXT(rx_head);
+ priv->rx_head = rx_head;
+
+ if (rx >= budget)
+ break;
+ }
+
+ if (rx < budget) {
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+ }
+
+ priv->reg_imr |= RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+
+ return rx;
+}
+
+static void moxart_tx_finished(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+
+ while (tx_tail != tx_head) {
+ dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail],
+ priv->tx_len[tx_tail], DMA_TO_DEVICE);
+
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
+
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+ priv->tx_skb[tx_tail] = NULL;
+
+ tx_tail = TX_NEXT(tx_tail);
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned int ists = readl(priv->base + ISR_REG_OFFSET);
+
+ if (ists & XPKT_OK_INT_STS)
+ moxart_tx_finished(ndev);
+
+ if (ists & RPKT_FINISH) {
+ if (napi_schedule_prep(&priv->napi)) {
+ priv->reg_imr &= ~RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + IMR_REG_OFFSET);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct tx_desc_t *txdesc;
+ unsigned int len;
+ unsigned int tx_head = priv->tx_head;
+
+ spin_lock_irq(&priv->txlock);
+
+ txdesc = &priv->tx_desc_base[tx_head];
+
+ if (txdesc->txdes0.ubit.tx_dma_own) {
+ net_dbg_ratelimited("no TX space for packet\n");
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
+
+ priv->tx_mapping[tx_head] = dma_map_single(&ndev->dev, skb->data,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->tx_mapping[tx_head])) {
+ netdev_err(ndev, "DMA mapping error\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_len[tx_head] = len;
+ priv->tx_skb[tx_head] = skb;
+
+ txdesc->txdes2.addr_phys = priv->tx_mapping[tx_head];
+ txdesc->txdes2.addr_virt = skb->data;
+
+ if (skb->len < ETH_ZLEN) {
+ memset(&txdesc->txdes2.addr_virt[skb->len],
+ 0, ETH_ZLEN - skb->len);
+ len = ETH_ZLEN;
+ }
+
+ txdesc->txdes1.ubit.lts = 1;
+ txdesc->txdes1.ubit.fts = 1;
+ txdesc->txdes1.ubit.tx2_fic = 0;
+ txdesc->txdes1.ubit.tx_ic = 0;
+ txdesc->txdes1.ubit.tx_buf_size = len;
+ txdesc->txdes0.ui = TXDMA_OWN;
+
+ /* start to send packet */
+ writel(0xffffffff, priv->base + TXPD_REG_OFFSET);
+
+ priv->tx_head = TX_NEXT(tx_head);
+
+ ndev->trans_start = jiffies;
+
+ spin_unlock_irq(&priv->txlock);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return &priv->stats;
+}
+
+static void moxart_mac_setmulticast(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ int crc_val;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_val = crc32_le(~0, ha->addr, ETH_ALEN);
+ crc_val = (crc_val >> 26) & 0x3f;
+ if (crc_val >= 32) {
+ writel(readl(priv->base + MATH1_REG_OFFSET) |
+ (1UL << (crc_val - 32)),
+ priv->base + MATH1_REG_OFFSET);
+ } else {
+ writel(readl(priv->base + MATH0_REG_OFFSET) |
+ (1UL << crc_val),
+ priv->base + MATH0_REG_OFFSET);
+ }
+ }
+}
+
+static void moxart_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ spin_lock_irq(&priv->txlock);
+
+ (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) :
+ (priv->reg_maccr &= ~RCV_ALL);
+
+ (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) :
+ (priv->reg_maccr &= ~RX_MULTIPKT);
+
+ if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) {
+ priv->reg_maccr |= HT_MULTI_EN;
+ moxart_mac_setmulticast(ndev);
+ } else {
+ priv->reg_maccr &= ~HT_MULTI_EN;
+ }
+
+ writel(priv->reg_maccr, priv->base + MACCR_REG_OFFSET);
+
+ spin_unlock_irq(&priv->txlock);
+}
+
+static struct net_device_ops moxart_netdev_ops = {
+ .ndo_open = moxart_mac_open,
+ .ndo_stop = moxart_mac_stop,
+ .ndo_start_xmit = moxart_mac_start_xmit,
+ .ndo_get_stats = moxart_mac_get_stats,
+ .ndo_set_rx_mode = moxart_mac_set_rx_mode,
+ .ndo_set_mac_address = moxart_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void moxart_get_mac_address(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i <= 5; i++)
+ ndev->dev_addr[i] = readb(priv->flash_base + i);
+}
+
+static int moxart_mac_probe(struct platform_device *pdev)
+{
+ struct device *p_dev = &pdev->dev;
+ struct device_node *node = p_dev->of_node;
+ struct net_device *ndev;
+ struct moxart_mac_priv_t *priv;
+ struct resource *res;
+ unsigned int irq;
+ void *tmp;
+
+ ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
+ if (!ndev)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(node, 0);
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ndev->base_addr = res->start;
+ priv->base = devm_ioremap_resource(p_dev, res);
+ if (IS_ERR(priv->base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ /* the flash driver (physmap_of) requests the same region
+ * so use ioremap instead of devm_ioremap_resource
+ */
+ priv->flash_base = ioremap(res->start, resource_size(res));
+ if (IS_ERR(priv->flash_base)) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ spin_lock_init(&priv->txlock);
+
+ priv->tx_buf_size = TX_BUF_SIZE;
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct tx_desc_t) * TX_DESC_NUM,
+ &priv->tx_base, GFP_DMA | GFP_KERNEL);
+ priv->tx_desc_base = (struct tx_desc_t *) tmp;
+ if (priv->tx_desc_base == NULL)
+ goto init_fail;
+
+ tmp = dma_alloc_coherent(NULL, sizeof(struct rx_desc_t) * RX_DESC_NUM,
+ &priv->rx_base, GFP_DMA | GFP_KERNEL);
+ priv->rx_desc_base = (struct rx_desc_t *) tmp;
+ if (priv->rx_desc_base == NULL)
+ goto init_fail;
+
+ priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->tx_buf_base)
+ goto init_fail;
+
+ priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->rx_buf_base)
+ goto init_fail;
+
+ platform_set_drvdata(pdev, ndev);
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &moxart_netdev_ops;
+ netif_napi_add(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM);
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ moxart_get_mac_address(ndev);
+ moxart_update_mac_address(ndev);
+
+ if (register_netdev(ndev)) {
+ free_netdev(ndev);
+ goto init_fail;
+ }
+
+ ndev->irq = irq;
+
+ if (devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0,
+ pdev->name, ndev)) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ free_netdev(ndev);
+ return -EBUSY;
+ }
+
+ netdev_dbg(ndev, "%s: IRQ=%d address=%pM\n",
+ __func__, ndev->irq, ndev->dev_addr);
+
+ return 0;
+
+init_fail:
+ netdev_err(ndev, "%s: init failed\n", __func__);
+ moxart_mac_free_memory(ndev);
+
+ return -ENOMEM;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ moxart_mac_free_memory(ndev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_mac_match[] = {
+ { .compatible = "moxa,moxart-mac" },
+ { }
+};
+
+struct __initdata platform_driver moxart_mac_driver = {
+ .probe = moxart_mac_probe,
+ .remove = moxart_remove,
+ .driver = {
+ .name = "moxart-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_mac_match,
+ },
+};
+module_platform_driver(moxart_mac_driver);
+
+MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <[email protected]>");
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
new file mode 100644
index 0000000..790b6a9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -0,0 +1,513 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MOXART_ETHERNET_H
+#define _MOXART_ETHERNET_H
+
+#define TX_DESC_NUM 64
+#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
+#define TX_BUF_SIZE 1600
+
+#define RX_DESC_NUM 64
+#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
+#define RX_BUF_SIZE 1600
+
+struct tx_desc_t {
+ union {
+#define TXDMA_OWN BIT(31)
+#define TXPKT_EXSCOL BIT(1)
+#define TXPKT_LATECOL BIT(0)
+ unsigned int ui;
+
+ struct {
+ /* is aborted due to late collision */
+ unsigned int tx_pkt_late_col:1;
+
+ /* is aborted after 16 collisions */
+ unsigned int rx_pkt_exs_col:1;
+
+ unsigned int reserved1:29;
+
+ /* is owned by the MAC controller */
+ unsigned int tx_dma_own:1;
+ } ubit;
+ } txdes0;
+
+ union {
+#define EDOTR BIT(31)
+#define TXIC BIT(30)
+#define TX2FIC BIT(29)
+#define FTS BIT(28)
+#define LTS BIT(27)
+#define TXBUF_SIZE_MASK 0x7ff
+#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* transmit buffer size in byte */
+ unsigned int tx_buf_size:11;
+
+ unsigned int reserved2:16;
+
+ /* is the last descriptor of a Tx packet */
+ unsigned int lts:1;
+
+ /* is the first descriptor of a Tx packet */
+ unsigned int fts:1;
+
+ /* transmit to FIFO interrupt on completion */
+ unsigned int tx2_fic:1;
+
+ /* transmit interrupt on completion */
+ unsigned int tx_ic:1;
+
+ /* end descriptor of transmit ring */
+ unsigned int edotr:1;
+ } ubit;
+ } txdes1;
+
+ struct {
+ /* transmit buffer physical base address */
+ unsigned int addr_phys;
+
+ /* transmit buffer virtual base address */
+ unsigned char *addr_virt;
+ } txdes2;
+};
+
+struct rx_desc_t {
+ union {
+#define RXDMA_OWN BIT(31)
+#define FRS BIT(29)
+#define LRS BIT(28)
+#define RX_ODD_NB BIT(22)
+#define RUNT BIT(21)
+#define FTL BIT(20)
+#define CRC_ERR BIT(19)
+#define RX_ERR BIT(18)
+#define BROADCAST_RXDES0 BIT(17)
+#define MULTICAST_RXDES0 BIT(16)
+#define RFL_MASK 0x7ff
+#define RFL_MAX (RFL_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive frame length */
+ unsigned int recv_frame_len:11;
+ unsigned int reserved1:5;
+
+ /* multicast frame */
+ unsigned int multicast:1;
+
+ /* broadcast frame */
+ unsigned int broadcast:1;
+ unsigned int rx_err:1; /* receive error */
+ unsigned int crc_err:1; /* CRC error */
+ unsigned int ftl:1; /* frame too long */
+
+ /* runt packet, less than 64 bytes */
+ unsigned int runt:1;
+
+ /* receive odd nibbles */
+ unsigned int rx_odd_nb:1;
+ unsigned int reserved2:5;
+
+ /* last receive segment descriptor */
+ unsigned int lrs:1;
+
+ /* first receive segment descriptor */
+ unsigned int frs:1;
+
+ unsigned int reserved3:1;
+ unsigned int rx_dma_own:1; /* RXDMA onwership */
+ } ubit;
+ } rxdes0;
+
+ union {
+#define EDORR BIT(31)
+#define RXBUF_SIZE_MASK 0x7ff
+#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
+ unsigned int ui;
+
+ struct {
+ /* receive buffer size */
+ unsigned int rx_buf_size:11;
+
+ unsigned int reserved4:20;
+
+ /* end descriptor of receive ring */
+ unsigned int edorr:1;
+ } ubit;
+ } rxdes1;
+
+ struct {
+ /* receive buffer physical base address */
+ unsigned int addr_phys;
+
+ /* receive buffer virtual base address */
+ unsigned char *addr_virt;
+ } rxdes2;
+};
+
+struct mac_control_reg_t {
+
+/* RXDMA has received packets into RX buffer successfully */
+#define RPKT_FINISH BIT(0)
+/* receive buffer unavailable */
+#define NORXBUF BIT(1)
+/* TXDMA has moved data into the TX FIFO */
+#define XPKT_FINISH BIT(2)
+/* transmit buffer unavailable */
+#define NOTXBUF BIT(3)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK_INT_STS BIT(4)
+/* packets transmitted to ethernet lost due to late
+ * collision or excessive collision
+ */
+#define XPKT_LOST_INT_STS BIT(5)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAV BIT(6)
+/* received packet lost due to RX FIFO full */
+#define RPKT_LOST_INT_STS BIT(7)
+#define AHB_ERR BIT(8) /* AHB error */
+#define PHYSTS_CHG BIT(9) /* PHY link status change */
+ unsigned int isr; /* interrupt status, 0x0 */
+
+#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
+#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
+#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
+#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
+#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
+#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
+#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
+#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
+#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
+#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
+ unsigned int imr; /* interrupt mask, 0x4 */
+
+/* the most significant 2 bytes of MAC address */
+#define MAC_MADR_MASK 0xffff
+ /* MAC most significant address, 0x8 */
+ unsigned int mac_madr;
+
+ /* MAC least significant address, 0xc */
+ unsigned int mac_ldar;
+
+ /* multicast address hash table 0, 0x10 */
+ unsigned int matht0;
+
+ /* multicast address hash table 1, 0x14 */
+ unsigned int matht1;
+
+ /* transmit poll demand, 0x18 */
+ unsigned int txpd;
+
+ /* receive poll demand, 0x1c */
+ unsigned int rxpd;
+
+ /* transmit ring base address, 0x20 */
+ unsigned int txr_badr;
+
+ /* receive ring base address, 0x24 */
+ unsigned int rxr_badr;
+
+/* defines the period of TX cycle time */
+#define TXINT_TIME_SEL BIT(15)
+#define TXINT_THR_MASK 0x7000
+#define TXINT_CNT_MASK 0xf00
+/* defines the period of RX cycle time */
+#define RXINT_TIME_SEL BIT(7)
+#define RXINT_THR_MASK 0x70
+#define RXINT_CNT_MASK 0xF
+ /* interrupt timer control, 0x28 */
+ unsigned int itc;
+
+/* defines the period of TX poll time */
+#define TXPOLL_TIME_SEL BIT(12)
+#define TXPOLL_CNT_MASK 0xf00
+#define TXPOLL_CNT_SHIFT_BIT 8
+/* defines the period of RX poll time */
+#define RXPOLL_TIME_SEL BIT(4)
+#define RXPOLL_CNT_MASK 0xF
+#define RXPOLL_CNT_SHIFT_BIT 0
+ /* automatic polling timer control, 0x2c */
+ unsigned int aptc;
+
+/* enable RX FIFO threshold arbitration */
+#define RX_THR_EN BIT(9)
+#define RXFIFO_HTHR_MASK 0x1c0
+#define RXFIFO_LTHR_MASK 0x38
+/* use INCR16 burst command in AHB bus */
+#define INCR16_EN BIT(2)
+/* use INCR8 burst command in AHB bus */
+#define INCR8_EN BIT(1)
+/* use INCR4 burst command in AHB bus */
+#define INCR4_EN BIT(0)
+ /* DMA burst length and arbitration control, 0x30 */
+ unsigned int dblac;
+
+ unsigned int reserved1[21]; /* 0x34 - 0x84 */
+
+#define RX_BROADPKT BIT(17) /* receive boradcast packet */
+/* receive all multicast packet */
+#define RX_MULTIPKT BIT(16)
+#define FULLDUP BIT(15) /* full duplex */
+/* append CRC to transmitted packet */
+#define CRC_APD BIT(14)
+/* do not check incoming packet's destination address */
+#define RCV_ALL BIT(12)
+/* store incoming packet even if its length is great than 1518 bytes */
+#define RX_FTL BIT(11)
+/* store incoming packet even if its length is less than 64 bytes */
+#define RX_RUNT BIT(10)
+/* enable storing incoming packet if the packet passes hash table
+ * address filtering and is a multicast packet
+ */
+#define HT_MULTI_EN BIT(9)
+#define RCV_EN BIT(8) /* receiver enable */
+/* enable packet reception when transmitting packet in half duplex mode */
+#define ENRX_IN_HALFTX BIT(6)
+#define XMT_EN BIT(5) /* transmitter enable */
+/* disable CRC check when receiving packets */
+#define CRC_DIS BIT(4)
+#define LOOP_EN BIT(3) /* internal loop-back */
+/* software reset, last 64 AHB bus clocks */
+#define SW_RST BIT(2)
+#define RDMA_EN BIT(1) /* enable receive DMA chan */
+#define XDMA_EN BIT(0) /* enable transmit DMA chan */
+ unsigned int maccr; /* MAC control, 0x88 */
+
+#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
+/* transmitter detects late collision */
+#define LATE_COL BIT(10)
+/* packet transmitted to ethernet lost due to late collision
+ * or excessive collision
+ */
+#define XPKT_LOST BIT(9)
+/* packets transmitted to ethernet successfully */
+#define XPKT_OK BIT(8)
+/* receiver detects a runt packet */
+#define RUNT_MAC_STS BIT(7)
+/* receiver detects a frame that is too long */
+#define FTL_MAC_STS BIT(6)
+#define CRC_ERR_MAC_STS BIT(5)
+/* received packets list due to RX FIFO full */
+#define RPKT_LOST BIT(4)
+/* packets received into RX FIFO successfully */
+#define RPKT_SAVE BIT(3)
+/* incoming packet dropped due to collision */
+#define COL BIT(2)
+#define MCPU_BROADCAST BIT(1)
+#define MCPU_MULTICAST BIT(0)
+ unsigned int macsr; /* MAC status, 0x8c */
+
+/* initialize a write sequence to PHY by setting this bit to 1
+ * this bit would be auto cleared after the write operation is finished.
+ */
+#define MIIWR BIT(27)
+#define MIIRD BIT(26)
+#define REGAD_MASK 0x3e00000
+#define PHYAD_MASK 0x1f0000
+#define MIIRDATA_MASK 0xffff
+ unsigned int phycr; /* PHY control, 0x90 */
+
+#define MIIWDATA_MASK 0xffff
+ unsigned int phywdata; /* PHY write data, 0x94 */
+
+#define PAUSE_TIME_MASK 0xffff0000
+#define FC_HIGH_MASK 0xf000
+#define FC_LOW_MASK 0xf00
+#define RX_PAUSE BIT(4) /* receive pause frame */
+/* packet transmission is paused due to receive */
+#define TXPAUSED BIT(3)
+ /* pause frame */
+/* enable flow control threshold mode. */
+#define FCTHR_EN BIT(2)
+#define TX_PAUSE BIT(1) /* transmit pause frame */
+#define FC_EN BIT(0) /* flow control mode enable */
+ unsigned int fcr; /* flow control, 0x98 */
+
+#define BK_LOW_MASK 0xf00
+#define BKJAM_LEN_MASK 0xf0
+#define BK_MODE BIT(1) /* back pressure address mode */
+#define BK_EN BIT(0) /* back pressure mode enable */
+ unsigned int bpr; /* back pressure, 0x9c */
+
+ unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
+
+#define TEST_SEED_MASK 0x3fff
+ unsigned int ts; /* test seed, 0xc4 */
+
+#define TXD_REQ BIT(31) /* TXDMA request */
+#define RXD_REQ BIT(30) /* RXDMA request */
+#define DARB_TXGNT BIT(29) /* TXDMA grant */
+#define DARB_RXGNT BIT(28) /* RXDMA grant */
+#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
+#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
+#define TXDMA2_SM_MASK 0x7000
+#define TXDMA1_SM_MASK 0xf00
+#define RXDMA2_SM_MASK 0x70
+#define RXDMA1_SM_MASK 0xF
+ unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
+
+#define SINGLE_PKT BIT(26) /* single packet mode */
+/* automatic polling timer test mode */
+#define PTIMER_TEST BIT(25)
+#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
+#define TEST_SEED_SEL BIT(22) /* test seed select */
+#define SEED_SEL BIT(21) /* seed select */
+#define TEST_MODE BIT(20) /* transmission test mode */
+#define TEST_TIME_MASK 0xffc00
+#define TEST_EXCEL_MASK 0x3e0
+ unsigned int tm; /* test mode, 0xcc */
+
+ unsigned int reserved3; /* 0xd0 */
+
+#define TX_MCOL_MASK 0xffff0000
+#define TX_MCOL_SHIFT_BIT 16
+#define TX_SCOL_MASK 0xffff
+#define TX_SCOL_SHIFT_BIT 0
+ /* TX_MCOL and TX_SCOL counter, 0xd4 */
+ unsigned int txmcol_xscol;
+
+#define RPF_MASK 0xffff0000
+#define RPF_SHIFT_BIT 16
+#define AEP_MASK 0xffff
+#define AEP_SHIFT_BIT 0
+ unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
+
+#define XM_MASK 0xffff0000
+#define XM_SHIFT_BIT 16
+#define PG_MASK 0xffff
+#define PG_SHIFT_BIT 0
+ unsigned int xm_pg; /* XM and PG counter, 0xdc */
+
+#define RUNT_CNT_MASK 0xffff0000
+#define RUNT_CNT_SHIFT_BIT 16
+#define TLCC_MASK 0xffff
+#define TLCC_SHIFT_BIT 0
+ /* RUNT_CNT and TLCC counter, 0xe0 */
+ unsigned int runtcnt_tlcc;
+
+#define CRCER_CNT_MASK 0xffff0000
+#define CRCER_CNT_SHIFT_BIT 16
+#define FTL_CNT_MASK 0xffff
+#define FTL_CNT_SHIFT_BIT 0
+ /* CRCER_CNT and FTL_CNT counter, 0xe4 */
+ unsigned int crcercnt_ftlcnt;
+
+#define RLC_MASK 0xffff0000
+#define RLC_SHIFT_BIT 16
+#define RCC_MASK 0xffff
+#define RCC_SHIFT_BIT 0
+ unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
+
+ unsigned int broc; /* BROC counter, 0xec */
+ unsigned int mulca; /* MULCA counter, 0xf0 */
+ unsigned int rp; /* RP counter, 0xf4 */
+ unsigned int xp; /* XP counter, 0xf8 */
+};
+
+#define ISR_REG_OFFSET 0x0
+#define IMR_REG_OFFSET 0x4
+#define MAC_MADR_REG_OFFSET 0x8
+#define MAC_LADR_REG_OFFSET 0xC
+#define MATH0_REG_OFFSET 0x10
+#define MATH1_REG_OFFSET 0x14
+#define TXPD_REG_OFFSET 0x18
+#define RXPD_REG_OFFSET 0x1C
+#define TXR_BADR_REG_OFFSET 0x20
+#define RXR_BADR_REG_OFFSET 0x24
+#define ITC_REG_OFFSET 0x28
+#define APTC_REG_OFFSET 0x2C
+#define DBLAC_REG_OFFSET 0x30
+#define MACCR_REG_OFFSET 0x88
+#define MACSR_REG_OFFSET 0x8C
+#define PHYCR_REG_OFFSET 0x90
+#define PHYWDATA_REG_OFFSET 0x94
+#define FCR_REG_OFFSET 0x98
+#define BPR_REG_OFFSET 0x9C
+#define TS_REG_OFFSET 0xC4
+#define DMAFIFOS_REG_OFFSET 0xC8
+#define TM_REG_OFFSET 0xCC
+#define TX_MCOL_TX_SCOL_REG_OFFSET 0xD4
+#define RPF_AEP_REG_OFFSET 0xD8
+#define XM_PG_REG_OFFSET 0xDC
+#define RUNT_CNT_TLCC_REG_OFFSET 0xE0
+#define CRCER_CNT_FTL_CNT_REG_OFFSET 0xE4
+#define RLC_RCC_REG_OFFSET 0xE8
+#define BROC_REG_OFFSET 0xEC
+#define MULCA_REG_OFFSET 0xF0
+#define RP_REG_OFFSET 0xF4
+#define XP_REG_OFFSET 0xF8
+#define PHY_CNTL_REG 0x0
+#define PHY_STATUS_REG 0x1
+#define PHY_ID_REG1 0x2
+#define PHY_ID_REG2 0x3
+#define PHY_ANA_REG 0x4
+#define PHY_ANLPAR_REG 0x5
+#define PHY_ANE_REG 0x6
+#define PHY_ECNTL_REG1 0x10
+#define PHY_QPDS_REG 0x11
+#define PHY_10BOP_REG 0x12
+#define PHY_ECNTL_REG2 0x13
+#define FTMAC100_REG_PHY_WRITE 0x8000000
+#define FTMAC100_REG_PHY_READ 0x4000000
+
+/* PHY Status register */
+#define AN_COMPLETE 0x20
+
+#define LINK_STATUS 0x4
+
+struct moxart_mac_priv_t {
+ void __iomem *base;
+ void __iomem *flash_base;
+ struct net_device_stats stats;
+ unsigned int reg_maccr;
+ unsigned int reg_imr;
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ dma_addr_t rx_base;
+ dma_addr_t rx_mapping[RX_DESC_NUM];
+ struct rx_desc_t *rx_desc_base;
+ unsigned char *rx_buf_base;
+ unsigned char *rx_buf[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ dma_addr_t tx_base;
+ dma_addr_t tx_mapping[TX_DESC_NUM];
+ struct tx_desc_t *tx_desc_base;
+ unsigned char *tx_buf_base;
+ unsigned char *tx_buf[RX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_buf_size;
+
+ spinlock_t txlock;
+ unsigned int tx_len[TX_DESC_NUM];
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ unsigned int tx_tail;
+};
+
+#if TX_BUF_SIZE >= TXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Tx buffer size too large !
+#endif
+#if RX_BUF_SIZE >= RXBUF_SIZE_MAX
+#error MOXA ART Ethernet device driver Rx buffer size too large !
+#endif
+
+#endif
--
1.8.2.1

2013-08-01 22:52:03

by David Miller

[permalink] [raw]
Subject: Re: [PATCH v4] net: Add MOXA ART SoCs ethernet driver

From: Jonas Jensen <[email protected]>
Date: Thu, 1 Aug 2013 11:39:28 +0200

> I have since found a problem occurring on high load. Please point out
> anything the driver can do differently to eliminate it.
>
> The error only occurs during external ping, i.e. ping -s 50000 from
> 10.0.1.200 to hardware.

Looks like pure memory corruption.

2013-08-02 10:04:59

by Mark Rutland

[permalink] [raw]
Subject: Re: [PATCH v4] net: Add MOXA ART SoCs ethernet driver

On Thu, Aug 01, 2013 at 10:39:28AM +0100, Jonas Jensen wrote:
> The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
> developed internally. The IC used is "RTL8201CP".
>
> Since there is no public documentation, this driver is mostly the one
> published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.
>
> Signed-off-by: Jonas Jensen <[email protected]>
> ---

[...]

> diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> new file mode 100644
> index 0000000..1fc44ff
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
> @@ -0,0 +1,25 @@
> +MOXA ART Ethernet Controller
> +
> +Required properties:
> +
> +- compatible : Must be "moxa,moxart-mac"
> +- reg : Should contain registers location and length
> + index 0 : main register

I assume there's more than one register in the main register bank?

Are there any other register banks not used by this driver?

> + index 1 : mac address (stored on flash)

Is that flash a part of the MAC unit?

Is it only 6 bytes long (judging by the examples), or is the th only
portion used by the driver?

If it's bigger, I'd prefer to describe the whole of flash. This driver
will know the offset within it, and can choose to map only the protion
it wants to use. Another driver may choose to do more interesting
things. We need to describe the hardware, not how we expect it to be
used...

If it's not associated with the MAC, I'm not sure this is the best way
of describing the linkage...

> +- interrupts : Should contain the mac interrupt number

Is there no need to link this to a phy, or is it a fixed-link device?

> +
> +Example:
> +
> + mac0: mac@90900000 {
> + compatible = "moxa,moxart-mac";
> + reg = <0x90900000 0x100>,
> + <0x80000050 0x6>;
> + interrupts = <25 0>;
> + };
> +
> + mac1: mac@92000000 {
> + compatible = "moxa,moxart-mac";
> + reg = <0x92000000 0x100>,
> + <0x80000056 0x6>;
> + interrupts = <27 0>;
> + };

[...]

> diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
> new file mode 100644
> index 0000000..790b6a9
> --- /dev/null
> +++ b/drivers/net/ethernet/moxa/moxart_ether.h
> @@ -0,0 +1,513 @@
> +/* MOXA ART Ethernet (RTL8201CP) driver.
> + *
> + * Copyright (C) 2013 Jonas Jensen
> + *
> + * Jonas Jensen <[email protected]>
> + *
> + * Based on code from
> + * Moxa Technology Co., Ltd. <http://www.moxa.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#ifndef _MOXART_ETHERNET_H
> +#define _MOXART_ETHERNET_H
> +
> +#define TX_DESC_NUM 64
> +#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
> +#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
> +#define TX_BUF_SIZE 1600
> +
> +#define RX_DESC_NUM 64
> +#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
> +#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
> +#define RX_BUF_SIZE 1600
> +
> +struct tx_desc_t {
> + union {
> +#define TXDMA_OWN BIT(31)
> +#define TXPKT_EXSCOL BIT(1)
> +#define TXPKT_LATECOL BIT(0)
> + unsigned int ui;
> +
> + struct {
> + /* is aborted due to late collision */
> + unsigned int tx_pkt_late_col:1;
> +
> + /* is aborted after 16 collisions */
> + unsigned int rx_pkt_exs_col:1;
> +
> + unsigned int reserved1:29;
> +
> + /* is owned by the MAC controller */
> + unsigned int tx_dma_own:1;
> + } ubit;
> + } txdes0;

Unless I've misunderstood the later code, this is the format of data
shared with the device? Bitfields aren't portable, and you can't rely on
the padding here being what you expect...

Similarly, the other structures below used in this way below seem scary
to me.

> +
> + union {
> +#define EDOTR BIT(31)
> +#define TXIC BIT(30)
> +#define TX2FIC BIT(29)
> +#define FTS BIT(28)
> +#define LTS BIT(27)
> +#define TXBUF_SIZE_MASK 0x7ff
> +#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
> + unsigned int ui;
> +
> + struct {
> + /* transmit buffer size in byte */
> + unsigned int tx_buf_size:11;
> +
> + unsigned int reserved2:16;
> +
> + /* is the last descriptor of a Tx packet */
> + unsigned int lts:1;
> +
> + /* is the first descriptor of a Tx packet */
> + unsigned int fts:1;
> +
> + /* transmit to FIFO interrupt on completion */
> + unsigned int tx2_fic:1;
> +
> + /* transmit interrupt on completion */
> + unsigned int tx_ic:1;
> +
> + /* end descriptor of transmit ring */
> + unsigned int edotr:1;
> + } ubit;
> + } txdes1;
> +
> + struct {
> + /* transmit buffer physical base address */
> + unsigned int addr_phys;
> +
> + /* transmit buffer virtual base address */
> + unsigned char *addr_virt;
> + } txdes2;
> +};
> +
> +struct rx_desc_t {
> + union {
> +#define RXDMA_OWN BIT(31)
> +#define FRS BIT(29)
> +#define LRS BIT(28)
> +#define RX_ODD_NB BIT(22)
> +#define RUNT BIT(21)
> +#define FTL BIT(20)
> +#define CRC_ERR BIT(19)
> +#define RX_ERR BIT(18)
> +#define BROADCAST_RXDES0 BIT(17)
> +#define MULTICAST_RXDES0 BIT(16)
> +#define RFL_MASK 0x7ff
> +#define RFL_MAX (RFL_MASK+1)
> + unsigned int ui;
> +
> + struct {
> + /* receive frame length */
> + unsigned int recv_frame_len:11;
> + unsigned int reserved1:5;
> +
> + /* multicast frame */
> + unsigned int multicast:1;
> +
> + /* broadcast frame */
> + unsigned int broadcast:1;
> + unsigned int rx_err:1; /* receive error */
> + unsigned int crc_err:1; /* CRC error */
> + unsigned int ftl:1; /* frame too long */
> +
> + /* runt packet, less than 64 bytes */
> + unsigned int runt:1;
> +
> + /* receive odd nibbles */
> + unsigned int rx_odd_nb:1;
> + unsigned int reserved2:5;
> +
> + /* last receive segment descriptor */
> + unsigned int lrs:1;
> +
> + /* first receive segment descriptor */
> + unsigned int frs:1;
> +
> + unsigned int reserved3:1;
> + unsigned int rx_dma_own:1; /* RXDMA onwership */
> + } ubit;
> + } rxdes0;
> +
> + union {
> +#define EDORR BIT(31)
> +#define RXBUF_SIZE_MASK 0x7ff
> +#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
> + unsigned int ui;
> +
> + struct {
> + /* receive buffer size */
> + unsigned int rx_buf_size:11;
> +
> + unsigned int reserved4:20;
> +
> + /* end descriptor of receive ring */
> + unsigned int edorr:1;
> + } ubit;
> + } rxdes1;
> +
> + struct {
> + /* receive buffer physical base address */
> + unsigned int addr_phys;
> +
> + /* receive buffer virtual base address */
> + unsigned char *addr_virt;
> + } rxdes2;
> +};
> +
> +struct mac_control_reg_t {
> +
> +/* RXDMA has received packets into RX buffer successfully */
> +#define RPKT_FINISH BIT(0)
> +/* receive buffer unavailable */
> +#define NORXBUF BIT(1)
> +/* TXDMA has moved data into the TX FIFO */
> +#define XPKT_FINISH BIT(2)
> +/* transmit buffer unavailable */
> +#define NOTXBUF BIT(3)
> +/* packets transmitted to ethernet successfully */
> +#define XPKT_OK_INT_STS BIT(4)
> +/* packets transmitted to ethernet lost due to late
> + * collision or excessive collision
> + */
> +#define XPKT_LOST_INT_STS BIT(5)
> +/* packets received into RX FIFO successfully */
> +#define RPKT_SAV BIT(6)
> +/* received packet lost due to RX FIFO full */
> +#define RPKT_LOST_INT_STS BIT(7)
> +#define AHB_ERR BIT(8) /* AHB error */
> +#define PHYSTS_CHG BIT(9) /* PHY link status change */
> + unsigned int isr; /* interrupt status, 0x0 */
> +
> +#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
> +#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
> +#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
> +#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
> +#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
> +#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
> +#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
> +#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
> +#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
> +#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
> + unsigned int imr; /* interrupt mask, 0x4 */
> +
> +/* the most significant 2 bytes of MAC address */
> +#define MAC_MADR_MASK 0xffff
> + /* MAC most significant address, 0x8 */
> + unsigned int mac_madr;
> +
> + /* MAC least significant address, 0xc */
> + unsigned int mac_ldar;
> +
> + /* multicast address hash table 0, 0x10 */
> + unsigned int matht0;
> +
> + /* multicast address hash table 1, 0x14 */
> + unsigned int matht1;
> +
> + /* transmit poll demand, 0x18 */
> + unsigned int txpd;
> +
> + /* receive poll demand, 0x1c */
> + unsigned int rxpd;
> +
> + /* transmit ring base address, 0x20 */
> + unsigned int txr_badr;
> +
> + /* receive ring base address, 0x24 */
> + unsigned int rxr_badr;
> +
> +/* defines the period of TX cycle time */
> +#define TXINT_TIME_SEL BIT(15)
> +#define TXINT_THR_MASK 0x7000
> +#define TXINT_CNT_MASK 0xf00
> +/* defines the period of RX cycle time */
> +#define RXINT_TIME_SEL BIT(7)
> +#define RXINT_THR_MASK 0x70
> +#define RXINT_CNT_MASK 0xF
> + /* interrupt timer control, 0x28 */
> + unsigned int itc;
> +
> +/* defines the period of TX poll time */
> +#define TXPOLL_TIME_SEL BIT(12)
> +#define TXPOLL_CNT_MASK 0xf00
> +#define TXPOLL_CNT_SHIFT_BIT 8
> +/* defines the period of RX poll time */
> +#define RXPOLL_TIME_SEL BIT(4)
> +#define RXPOLL_CNT_MASK 0xF
> +#define RXPOLL_CNT_SHIFT_BIT 0
> + /* automatic polling timer control, 0x2c */
> + unsigned int aptc;
> +
> +/* enable RX FIFO threshold arbitration */
> +#define RX_THR_EN BIT(9)
> +#define RXFIFO_HTHR_MASK 0x1c0
> +#define RXFIFO_LTHR_MASK 0x38
> +/* use INCR16 burst command in AHB bus */
> +#define INCR16_EN BIT(2)
> +/* use INCR8 burst command in AHB bus */
> +#define INCR8_EN BIT(1)
> +/* use INCR4 burst command in AHB bus */
> +#define INCR4_EN BIT(0)
> + /* DMA burst length and arbitration control, 0x30 */
> + unsigned int dblac;
> +
> + unsigned int reserved1[21]; /* 0x34 - 0x84 */
> +
> +#define RX_BROADPKT BIT(17) /* receive boradcast packet */
> +/* receive all multicast packet */
> +#define RX_MULTIPKT BIT(16)
> +#define FULLDUP BIT(15) /* full duplex */
> +/* append CRC to transmitted packet */
> +#define CRC_APD BIT(14)
> +/* do not check incoming packet's destination address */
> +#define RCV_ALL BIT(12)
> +/* store incoming packet even if its length is great than 1518 bytes */
> +#define RX_FTL BIT(11)
> +/* store incoming packet even if its length is less than 64 bytes */
> +#define RX_RUNT BIT(10)
> +/* enable storing incoming packet if the packet passes hash table
> + * address filtering and is a multicast packet
> + */
> +#define HT_MULTI_EN BIT(9)
> +#define RCV_EN BIT(8) /* receiver enable */
> +/* enable packet reception when transmitting packet in half duplex mode */
> +#define ENRX_IN_HALFTX BIT(6)
> +#define XMT_EN BIT(5) /* transmitter enable */
> +/* disable CRC check when receiving packets */
> +#define CRC_DIS BIT(4)
> +#define LOOP_EN BIT(3) /* internal loop-back */
> +/* software reset, last 64 AHB bus clocks */
> +#define SW_RST BIT(2)
> +#define RDMA_EN BIT(1) /* enable receive DMA chan */
> +#define XDMA_EN BIT(0) /* enable transmit DMA chan */
> + unsigned int maccr; /* MAC control, 0x88 */
> +
> +#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
> +/* transmitter detects late collision */
> +#define LATE_COL BIT(10)
> +/* packet transmitted to ethernet lost due to late collision
> + * or excessive collision
> + */
> +#define XPKT_LOST BIT(9)
> +/* packets transmitted to ethernet successfully */
> +#define XPKT_OK BIT(8)
> +/* receiver detects a runt packet */
> +#define RUNT_MAC_STS BIT(7)
> +/* receiver detects a frame that is too long */
> +#define FTL_MAC_STS BIT(6)
> +#define CRC_ERR_MAC_STS BIT(5)
> +/* received packets list due to RX FIFO full */
> +#define RPKT_LOST BIT(4)
> +/* packets received into RX FIFO successfully */
> +#define RPKT_SAVE BIT(3)
> +/* incoming packet dropped due to collision */
> +#define COL BIT(2)
> +#define MCPU_BROADCAST BIT(1)
> +#define MCPU_MULTICAST BIT(0)
> + unsigned int macsr; /* MAC status, 0x8c */
> +
> +/* initialize a write sequence to PHY by setting this bit to 1
> + * this bit would be auto cleared after the write operation is finished.
> + */
> +#define MIIWR BIT(27)
> +#define MIIRD BIT(26)
> +#define REGAD_MASK 0x3e00000
> +#define PHYAD_MASK 0x1f0000
> +#define MIIRDATA_MASK 0xffff
> + unsigned int phycr; /* PHY control, 0x90 */
> +
> +#define MIIWDATA_MASK 0xffff
> + unsigned int phywdata; /* PHY write data, 0x94 */
> +
> +#define PAUSE_TIME_MASK 0xffff0000
> +#define FC_HIGH_MASK 0xf000
> +#define FC_LOW_MASK 0xf00
> +#define RX_PAUSE BIT(4) /* receive pause frame */
> +/* packet transmission is paused due to receive */
> +#define TXPAUSED BIT(3)
> + /* pause frame */
> +/* enable flow control threshold mode. */
> +#define FCTHR_EN BIT(2)
> +#define TX_PAUSE BIT(1) /* transmit pause frame */
> +#define FC_EN BIT(0) /* flow control mode enable */
> + unsigned int fcr; /* flow control, 0x98 */
> +
> +#define BK_LOW_MASK 0xf00
> +#define BKJAM_LEN_MASK 0xf0
> +#define BK_MODE BIT(1) /* back pressure address mode */
> +#define BK_EN BIT(0) /* back pressure mode enable */
> + unsigned int bpr; /* back pressure, 0x9c */
> +
> + unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
> +
> +#define TEST_SEED_MASK 0x3fff
> + unsigned int ts; /* test seed, 0xc4 */
> +
> +#define TXD_REQ BIT(31) /* TXDMA request */
> +#define RXD_REQ BIT(30) /* RXDMA request */
> +#define DARB_TXGNT BIT(29) /* TXDMA grant */
> +#define DARB_RXGNT BIT(28) /* RXDMA grant */
> +#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
> +#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
> +#define TXDMA2_SM_MASK 0x7000
> +#define TXDMA1_SM_MASK 0xf00
> +#define RXDMA2_SM_MASK 0x70
> +#define RXDMA1_SM_MASK 0xF
> + unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
> +
> +#define SINGLE_PKT BIT(26) /* single packet mode */
> +/* automatic polling timer test mode */
> +#define PTIMER_TEST BIT(25)
> +#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
> +#define TEST_SEED_SEL BIT(22) /* test seed select */
> +#define SEED_SEL BIT(21) /* seed select */
> +#define TEST_MODE BIT(20) /* transmission test mode */
> +#define TEST_TIME_MASK 0xffc00
> +#define TEST_EXCEL_MASK 0x3e0
> + unsigned int tm; /* test mode, 0xcc */
> +
> + unsigned int reserved3; /* 0xd0 */
> +
> +#define TX_MCOL_MASK 0xffff0000
> +#define TX_MCOL_SHIFT_BIT 16
> +#define TX_SCOL_MASK 0xffff
> +#define TX_SCOL_SHIFT_BIT 0
> + /* TX_MCOL and TX_SCOL counter, 0xd4 */
> + unsigned int txmcol_xscol;
> +
> +#define RPF_MASK 0xffff0000
> +#define RPF_SHIFT_BIT 16
> +#define AEP_MASK 0xffff
> +#define AEP_SHIFT_BIT 0
> + unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
> +
> +#define XM_MASK 0xffff0000
> +#define XM_SHIFT_BIT 16
> +#define PG_MASK 0xffff
> +#define PG_SHIFT_BIT 0
> + unsigned int xm_pg; /* XM and PG counter, 0xdc */
> +
> +#define RUNT_CNT_MASK 0xffff0000
> +#define RUNT_CNT_SHIFT_BIT 16
> +#define TLCC_MASK 0xffff
> +#define TLCC_SHIFT_BIT 0
> + /* RUNT_CNT and TLCC counter, 0xe0 */
> + unsigned int runtcnt_tlcc;
> +
> +#define CRCER_CNT_MASK 0xffff0000
> +#define CRCER_CNT_SHIFT_BIT 16
> +#define FTL_CNT_MASK 0xffff
> +#define FTL_CNT_SHIFT_BIT 0
> + /* CRCER_CNT and FTL_CNT counter, 0xe4 */
> + unsigned int crcercnt_ftlcnt;
> +
> +#define RLC_MASK 0xffff0000
> +#define RLC_SHIFT_BIT 16
> +#define RCC_MASK 0xffff
> +#define RCC_SHIFT_BIT 0
> + unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
> +
> + unsigned int broc; /* BROC counter, 0xec */
> + unsigned int mulca; /* MULCA counter, 0xf0 */
> + unsigned int rp; /* RP counter, 0xf4 */
> + unsigned int xp; /* XP counter, 0xf8 */
> +};

I don't think you can rely on the fields to be the size you expect (int
is not necessarily 32 bits), and nor can you expect them to have the
padding you expect (a 64 bit arch could pad ints to 64 bits). I think it
would be better to just #define the offsets explicitly, which will be
safe and easy to verify.

Thanks,
Mark.

2013-08-02 10:24:29

by Florian Fainelli

[permalink] [raw]
Subject: Re: [PATCH v4] net: Add MOXA ART SoCs ethernet driver

2013/8/2 Mark Rutland <[email protected]>:
> On Thu, Aug 01, 2013 at 10:39:28AM +0100, Jonas Jensen wrote:
>> The MOXA UC-711X hardware(s) has an ethernet controller that seem to be
>> developed internally. The IC used is "RTL8201CP".
>>
>> Since there is no public documentation, this driver is mostly the one
>> published by MOXA that has been heavily cleaned up / ported from linux 2.6.9.
>>
>> Signed-off-by: Jonas Jensen <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
>> new file mode 100644
>> index 0000000..1fc44ff
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
>> @@ -0,0 +1,25 @@
>> +MOXA ART Ethernet Controller
>> +
>> +Required properties:
>> +
>> +- compatible : Must be "moxa,moxart-mac"
>> +- reg : Should contain registers location and length
>> + index 0 : main register
>
> I assume there's more than one register in the main register bank?
>
> Are there any other register banks not used by this driver?
>
>> + index 1 : mac address (stored on flash)
>
> Is that flash a part of the MAC unit?
>
> Is it only 6 bytes long (judging by the examples), or is the th only
> portion used by the driver?
>
> If it's bigger, I'd prefer to describe the whole of flash. This driver
> will know the offset within it, and can choose to map only the protion
> it wants to use. Another driver may choose to do more interesting
> things. We need to describe the hardware, not how we expect it to be
> used...
>
> If it's not associated with the MAC, I'm not sure this is the best way
> of describing the linkage...

I also do not quite like it, this is very unusual. Here is how you
could potentially solve this:

- generate a random ethernet MAC adddress and later on let user-space
fetch the MAC address from the flash and set it correctly
- have some early code in arch/arm/mach-moxart which fetches the MAC
address from the flash and then update the ethernet node
local-mac-address property accordingly

Ideally, fetching the MAC address from whatever backend storage is
something that should really be left to user-space...

>
>> +- interrupts : Should contain the mac interrupt number
>
> Is there no need to link this to a phy, or is it a fixed-link device?
>
>> +
>> +Example:
>> +
>> + mac0: mac@90900000 {
>> + compatible = "moxa,moxart-mac";
>> + reg = <0x90900000 0x100>,
>> + <0x80000050 0x6>;
>> + interrupts = <25 0>;
>> + };
>> +
>> + mac1: mac@92000000 {
>> + compatible = "moxa,moxart-mac";
>> + reg = <0x92000000 0x100>,
>> + <0x80000056 0x6>;
>> + interrupts = <27 0>;
>> + };
>
> [...]
>
>> diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
>> new file mode 100644
>> index 0000000..790b6a9
>> --- /dev/null
>> +++ b/drivers/net/ethernet/moxa/moxart_ether.h
>> @@ -0,0 +1,513 @@
>> +/* MOXA ART Ethernet (RTL8201CP) driver.
>> + *
>> + * Copyright (C) 2013 Jonas Jensen
>> + *
>> + * Jonas Jensen <[email protected]>
>> + *
>> + * Based on code from
>> + * Moxa Technology Co., Ltd. <http://www.moxa.com>
>> + *
>> + * This file is licensed under the terms of the GNU General Public
>> + * License version 2. This program is licensed "as is" without any
>> + * warranty of any kind, whether express or implied.
>> + */
>> +
>> +#ifndef _MOXART_ETHERNET_H
>> +#define _MOXART_ETHERNET_H
>> +
>> +#define TX_DESC_NUM 64
>> +#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
>> +#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
>> +#define TX_BUF_SIZE 1600
>> +
>> +#define RX_DESC_NUM 64
>> +#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
>> +#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
>> +#define RX_BUF_SIZE 1600
>> +
>> +struct tx_desc_t {
>> + union {
>> +#define TXDMA_OWN BIT(31)
>> +#define TXPKT_EXSCOL BIT(1)
>> +#define TXPKT_LATECOL BIT(0)
>> + unsigned int ui;
>> +
>> + struct {
>> + /* is aborted due to late collision */
>> + unsigned int tx_pkt_late_col:1;
>> +
>> + /* is aborted after 16 collisions */
>> + unsigned int rx_pkt_exs_col:1;
>> +
>> + unsigned int reserved1:29;
>> +
>> + /* is owned by the MAC controller */
>> + unsigned int tx_dma_own:1;
>> + } ubit;
>> + } txdes0;
>
> Unless I've misunderstood the later code, this is the format of data
> shared with the device? Bitfields aren't portable, and you can't rely on
> the padding here being what you expect...
>
> Similarly, the other structures below used in this way below seem scary
> to me.
>
>> +
>> + union {
>> +#define EDOTR BIT(31)
>> +#define TXIC BIT(30)
>> +#define TX2FIC BIT(29)
>> +#define FTS BIT(28)
>> +#define LTS BIT(27)
>> +#define TXBUF_SIZE_MASK 0x7ff
>> +#define TXBUF_SIZE_MAX (TXBUF_SIZE_MASK+1)
>> + unsigned int ui;
>> +
>> + struct {
>> + /* transmit buffer size in byte */
>> + unsigned int tx_buf_size:11;
>> +
>> + unsigned int reserved2:16;
>> +
>> + /* is the last descriptor of a Tx packet */
>> + unsigned int lts:1;
>> +
>> + /* is the first descriptor of a Tx packet */
>> + unsigned int fts:1;
>> +
>> + /* transmit to FIFO interrupt on completion */
>> + unsigned int tx2_fic:1;
>> +
>> + /* transmit interrupt on completion */
>> + unsigned int tx_ic:1;
>> +
>> + /* end descriptor of transmit ring */
>> + unsigned int edotr:1;
>> + } ubit;
>> + } txdes1;
>> +
>> + struct {
>> + /* transmit buffer physical base address */
>> + unsigned int addr_phys;
>> +
>> + /* transmit buffer virtual base address */
>> + unsigned char *addr_virt;
>> + } txdes2;
>> +};
>> +
>> +struct rx_desc_t {
>> + union {
>> +#define RXDMA_OWN BIT(31)
>> +#define FRS BIT(29)
>> +#define LRS BIT(28)
>> +#define RX_ODD_NB BIT(22)
>> +#define RUNT BIT(21)
>> +#define FTL BIT(20)
>> +#define CRC_ERR BIT(19)
>> +#define RX_ERR BIT(18)
>> +#define BROADCAST_RXDES0 BIT(17)
>> +#define MULTICAST_RXDES0 BIT(16)
>> +#define RFL_MASK 0x7ff
>> +#define RFL_MAX (RFL_MASK+1)
>> + unsigned int ui;
>> +
>> + struct {
>> + /* receive frame length */
>> + unsigned int recv_frame_len:11;
>> + unsigned int reserved1:5;
>> +
>> + /* multicast frame */
>> + unsigned int multicast:1;
>> +
>> + /* broadcast frame */
>> + unsigned int broadcast:1;
>> + unsigned int rx_err:1; /* receive error */
>> + unsigned int crc_err:1; /* CRC error */
>> + unsigned int ftl:1; /* frame too long */
>> +
>> + /* runt packet, less than 64 bytes */
>> + unsigned int runt:1;
>> +
>> + /* receive odd nibbles */
>> + unsigned int rx_odd_nb:1;
>> + unsigned int reserved2:5;
>> +
>> + /* last receive segment descriptor */
>> + unsigned int lrs:1;
>> +
>> + /* first receive segment descriptor */
>> + unsigned int frs:1;
>> +
>> + unsigned int reserved3:1;
>> + unsigned int rx_dma_own:1; /* RXDMA onwership */
>> + } ubit;
>> + } rxdes0;
>> +
>> + union {
>> +#define EDORR BIT(31)
>> +#define RXBUF_SIZE_MASK 0x7ff
>> +#define RXBUF_SIZE_MAX (RXBUF_SIZE_MASK+1)
>> + unsigned int ui;
>> +
>> + struct {
>> + /* receive buffer size */
>> + unsigned int rx_buf_size:11;
>> +
>> + unsigned int reserved4:20;
>> +
>> + /* end descriptor of receive ring */
>> + unsigned int edorr:1;
>> + } ubit;
>> + } rxdes1;
>> +
>> + struct {
>> + /* receive buffer physical base address */
>> + unsigned int addr_phys;
>> +
>> + /* receive buffer virtual base address */
>> + unsigned char *addr_virt;
>> + } rxdes2;
>> +};
>> +
>> +struct mac_control_reg_t {
>> +
>> +/* RXDMA has received packets into RX buffer successfully */
>> +#define RPKT_FINISH BIT(0)
>> +/* receive buffer unavailable */
>> +#define NORXBUF BIT(1)
>> +/* TXDMA has moved data into the TX FIFO */
>> +#define XPKT_FINISH BIT(2)
>> +/* transmit buffer unavailable */
>> +#define NOTXBUF BIT(3)
>> +/* packets transmitted to ethernet successfully */
>> +#define XPKT_OK_INT_STS BIT(4)
>> +/* packets transmitted to ethernet lost due to late
>> + * collision or excessive collision
>> + */
>> +#define XPKT_LOST_INT_STS BIT(5)
>> +/* packets received into RX FIFO successfully */
>> +#define RPKT_SAV BIT(6)
>> +/* received packet lost due to RX FIFO full */
>> +#define RPKT_LOST_INT_STS BIT(7)
>> +#define AHB_ERR BIT(8) /* AHB error */
>> +#define PHYSTS_CHG BIT(9) /* PHY link status change */
>> + unsigned int isr; /* interrupt status, 0x0 */
>> +
>> +#define RPKT_FINISH_M BIT(0) /* interrupt mask of ISR[0] */
>> +#define NORXBUF_M BIT(1) /* interrupt mask of ISR[1] */
>> +#define XPKT_FINISH_M BIT(2) /* interrupt mask of ISR[2] */
>> +#define NOTXBUF_M BIT(3) /* interrupt mask of ISR[3] */
>> +#define XPKT_OK_M BIT(4) /* interrupt mask of ISR[4] */
>> +#define XPKT_LOST_M BIT(5) /* interrupt mask of ISR[5] */
>> +#define RPKT_SAV_M BIT(6) /* interrupt mask of ISR[6] */
>> +#define RPKT_LOST_M BIT(7) /* interrupt mask of ISR[7] */
>> +#define AHB_ERR_M BIT(8) /* interrupt mask of ISR[8] */
>> +#define PHYSTS_CHG_M BIT(9) /* interrupt mask of ISR[9] */
>> + unsigned int imr; /* interrupt mask, 0x4 */
>> +
>> +/* the most significant 2 bytes of MAC address */
>> +#define MAC_MADR_MASK 0xffff
>> + /* MAC most significant address, 0x8 */
>> + unsigned int mac_madr;
>> +
>> + /* MAC least significant address, 0xc */
>> + unsigned int mac_ldar;
>> +
>> + /* multicast address hash table 0, 0x10 */
>> + unsigned int matht0;
>> +
>> + /* multicast address hash table 1, 0x14 */
>> + unsigned int matht1;
>> +
>> + /* transmit poll demand, 0x18 */
>> + unsigned int txpd;
>> +
>> + /* receive poll demand, 0x1c */
>> + unsigned int rxpd;
>> +
>> + /* transmit ring base address, 0x20 */
>> + unsigned int txr_badr;
>> +
>> + /* receive ring base address, 0x24 */
>> + unsigned int rxr_badr;
>> +
>> +/* defines the period of TX cycle time */
>> +#define TXINT_TIME_SEL BIT(15)
>> +#define TXINT_THR_MASK 0x7000
>> +#define TXINT_CNT_MASK 0xf00
>> +/* defines the period of RX cycle time */
>> +#define RXINT_TIME_SEL BIT(7)
>> +#define RXINT_THR_MASK 0x70
>> +#define RXINT_CNT_MASK 0xF
>> + /* interrupt timer control, 0x28 */
>> + unsigned int itc;
>> +
>> +/* defines the period of TX poll time */
>> +#define TXPOLL_TIME_SEL BIT(12)
>> +#define TXPOLL_CNT_MASK 0xf00
>> +#define TXPOLL_CNT_SHIFT_BIT 8
>> +/* defines the period of RX poll time */
>> +#define RXPOLL_TIME_SEL BIT(4)
>> +#define RXPOLL_CNT_MASK 0xF
>> +#define RXPOLL_CNT_SHIFT_BIT 0
>> + /* automatic polling timer control, 0x2c */
>> + unsigned int aptc;
>> +
>> +/* enable RX FIFO threshold arbitration */
>> +#define RX_THR_EN BIT(9)
>> +#define RXFIFO_HTHR_MASK 0x1c0
>> +#define RXFIFO_LTHR_MASK 0x38
>> +/* use INCR16 burst command in AHB bus */
>> +#define INCR16_EN BIT(2)
>> +/* use INCR8 burst command in AHB bus */
>> +#define INCR8_EN BIT(1)
>> +/* use INCR4 burst command in AHB bus */
>> +#define INCR4_EN BIT(0)
>> + /* DMA burst length and arbitration control, 0x30 */
>> + unsigned int dblac;
>> +
>> + unsigned int reserved1[21]; /* 0x34 - 0x84 */
>> +
>> +#define RX_BROADPKT BIT(17) /* receive boradcast packet */
>> +/* receive all multicast packet */
>> +#define RX_MULTIPKT BIT(16)
>> +#define FULLDUP BIT(15) /* full duplex */
>> +/* append CRC to transmitted packet */
>> +#define CRC_APD BIT(14)
>> +/* do not check incoming packet's destination address */
>> +#define RCV_ALL BIT(12)
>> +/* store incoming packet even if its length is great than 1518 bytes */
>> +#define RX_FTL BIT(11)
>> +/* store incoming packet even if its length is less than 64 bytes */
>> +#define RX_RUNT BIT(10)
>> +/* enable storing incoming packet if the packet passes hash table
>> + * address filtering and is a multicast packet
>> + */
>> +#define HT_MULTI_EN BIT(9)
>> +#define RCV_EN BIT(8) /* receiver enable */
>> +/* enable packet reception when transmitting packet in half duplex mode */
>> +#define ENRX_IN_HALFTX BIT(6)
>> +#define XMT_EN BIT(5) /* transmitter enable */
>> +/* disable CRC check when receiving packets */
>> +#define CRC_DIS BIT(4)
>> +#define LOOP_EN BIT(3) /* internal loop-back */
>> +/* software reset, last 64 AHB bus clocks */
>> +#define SW_RST BIT(2)
>> +#define RDMA_EN BIT(1) /* enable receive DMA chan */
>> +#define XDMA_EN BIT(0) /* enable transmit DMA chan */
>> + unsigned int maccr; /* MAC control, 0x88 */
>> +
>> +#define COL_EXCEED BIT(11) /* collisions exceeds 16 */
>> +/* transmitter detects late collision */
>> +#define LATE_COL BIT(10)
>> +/* packet transmitted to ethernet lost due to late collision
>> + * or excessive collision
>> + */
>> +#define XPKT_LOST BIT(9)
>> +/* packets transmitted to ethernet successfully */
>> +#define XPKT_OK BIT(8)
>> +/* receiver detects a runt packet */
>> +#define RUNT_MAC_STS BIT(7)
>> +/* receiver detects a frame that is too long */
>> +#define FTL_MAC_STS BIT(6)
>> +#define CRC_ERR_MAC_STS BIT(5)
>> +/* received packets list due to RX FIFO full */
>> +#define RPKT_LOST BIT(4)
>> +/* packets received into RX FIFO successfully */
>> +#define RPKT_SAVE BIT(3)
>> +/* incoming packet dropped due to collision */
>> +#define COL BIT(2)
>> +#define MCPU_BROADCAST BIT(1)
>> +#define MCPU_MULTICAST BIT(0)
>> + unsigned int macsr; /* MAC status, 0x8c */
>> +
>> +/* initialize a write sequence to PHY by setting this bit to 1
>> + * this bit would be auto cleared after the write operation is finished.
>> + */
>> +#define MIIWR BIT(27)
>> +#define MIIRD BIT(26)
>> +#define REGAD_MASK 0x3e00000
>> +#define PHYAD_MASK 0x1f0000
>> +#define MIIRDATA_MASK 0xffff
>> + unsigned int phycr; /* PHY control, 0x90 */
>> +
>> +#define MIIWDATA_MASK 0xffff
>> + unsigned int phywdata; /* PHY write data, 0x94 */
>> +
>> +#define PAUSE_TIME_MASK 0xffff0000
>> +#define FC_HIGH_MASK 0xf000
>> +#define FC_LOW_MASK 0xf00
>> +#define RX_PAUSE BIT(4) /* receive pause frame */
>> +/* packet transmission is paused due to receive */
>> +#define TXPAUSED BIT(3)
>> + /* pause frame */
>> +/* enable flow control threshold mode. */
>> +#define FCTHR_EN BIT(2)
>> +#define TX_PAUSE BIT(1) /* transmit pause frame */
>> +#define FC_EN BIT(0) /* flow control mode enable */
>> + unsigned int fcr; /* flow control, 0x98 */
>> +
>> +#define BK_LOW_MASK 0xf00
>> +#define BKJAM_LEN_MASK 0xf0
>> +#define BK_MODE BIT(1) /* back pressure address mode */
>> +#define BK_EN BIT(0) /* back pressure mode enable */
>> + unsigned int bpr; /* back pressure, 0x9c */
>> +
>> + unsigned int reserved2[9]; /* 0xa0 - 0xc0 */
>> +
>> +#define TEST_SEED_MASK 0x3fff
>> + unsigned int ts; /* test seed, 0xc4 */
>> +
>> +#define TXD_REQ BIT(31) /* TXDMA request */
>> +#define RXD_REQ BIT(30) /* RXDMA request */
>> +#define DARB_TXGNT BIT(29) /* TXDMA grant */
>> +#define DARB_RXGNT BIT(28) /* RXDMA grant */
>> +#define TXFIFO_EMPTY BIT(27) /* TX FIFO is empty */
>> +#define RXFIFO_EMPTY BIT(26) /* RX FIFO is empty */
>> +#define TXDMA2_SM_MASK 0x7000
>> +#define TXDMA1_SM_MASK 0xf00
>> +#define RXDMA2_SM_MASK 0x70
>> +#define RXDMA1_SM_MASK 0xF
>> + unsigned int dmafifos; /* DMA/FIFO state, 0xc8 */
>> +
>> +#define SINGLE_PKT BIT(26) /* single packet mode */
>> +/* automatic polling timer test mode */
>> +#define PTIMER_TEST BIT(25)
>> +#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
>> +#define TEST_SEED_SEL BIT(22) /* test seed select */
>> +#define SEED_SEL BIT(21) /* seed select */
>> +#define TEST_MODE BIT(20) /* transmission test mode */
>> +#define TEST_TIME_MASK 0xffc00
>> +#define TEST_EXCEL_MASK 0x3e0
>> + unsigned int tm; /* test mode, 0xcc */
>> +
>> + unsigned int reserved3; /* 0xd0 */
>> +
>> +#define TX_MCOL_MASK 0xffff0000
>> +#define TX_MCOL_SHIFT_BIT 16
>> +#define TX_SCOL_MASK 0xffff
>> +#define TX_SCOL_SHIFT_BIT 0
>> + /* TX_MCOL and TX_SCOL counter, 0xd4 */
>> + unsigned int txmcol_xscol;
>> +
>> +#define RPF_MASK 0xffff0000
>> +#define RPF_SHIFT_BIT 16
>> +#define AEP_MASK 0xffff
>> +#define AEP_SHIFT_BIT 0
>> + unsigned int rpf_aep; /* RPF and AEP counter, 0xd8 */
>> +
>> +#define XM_MASK 0xffff0000
>> +#define XM_SHIFT_BIT 16
>> +#define PG_MASK 0xffff
>> +#define PG_SHIFT_BIT 0
>> + unsigned int xm_pg; /* XM and PG counter, 0xdc */
>> +
>> +#define RUNT_CNT_MASK 0xffff0000
>> +#define RUNT_CNT_SHIFT_BIT 16
>> +#define TLCC_MASK 0xffff
>> +#define TLCC_SHIFT_BIT 0
>> + /* RUNT_CNT and TLCC counter, 0xe0 */
>> + unsigned int runtcnt_tlcc;
>> +
>> +#define CRCER_CNT_MASK 0xffff0000
>> +#define CRCER_CNT_SHIFT_BIT 16
>> +#define FTL_CNT_MASK 0xffff
>> +#define FTL_CNT_SHIFT_BIT 0
>> + /* CRCER_CNT and FTL_CNT counter, 0xe4 */
>> + unsigned int crcercnt_ftlcnt;
>> +
>> +#define RLC_MASK 0xffff0000
>> +#define RLC_SHIFT_BIT 16
>> +#define RCC_MASK 0xffff
>> +#define RCC_SHIFT_BIT 0
>> + unsigned int rlc_rcc; /* RLC and RCC counter, 0xe8 */
>> +
>> + unsigned int broc; /* BROC counter, 0xec */
>> + unsigned int mulca; /* MULCA counter, 0xf0 */
>> + unsigned int rp; /* RP counter, 0xf4 */
>> + unsigned int xp; /* XP counter, 0xf8 */
>> +};
>
> I don't think you can rely on the fields to be the size you expect (int
> is not necessarily 32 bits), and nor can you expect them to have the
> padding you expect (a 64 bit arch could pad ints to 64 bits). I think it
> would be better to just #define the offsets explicitly, which will be
> safe and easy to verify.
>
> Thanks,
> Mark.



--
Florian

2013-08-08 11:35:27

by Jonas Jensen

[permalink] [raw]
Subject: [PATCH v5] net: Add MOXA ART SoCs ethernet driver

The MOXA UC-711X hardware(s) has an ethernet controller that seem
to be developed internally. The IC used is "RTL8201CP".

Since there is no public documentation, this driver is mostly the
one published by MOXA that has been heavily cleaned up / ported
from linux 2.6.9.

Signed-off-by: Jonas Jensen <[email protected]>
---

Notes:
The flash mapped in v4 is not part of the MAC controller,
leaving it to user-space per Florian and Mark's comments.

Changes since v4:

1. don't rely on structs for register offsets
2. don't map flash storage register
3. remove code related to flash storage
4. reformat header file, use single line comments
5. move devm_request_irq() earlier in probe path
6. save return values in probe fail path
7. check/handle irq_of_parse_and_map() return value

device tree bindings document:
8. remove references to flash storage register

Applies to next-20130808

.../devicetree/bindings/net/moxa,moxart-mac.txt | 21 +
drivers/net/ethernet/Kconfig | 1 +
drivers/net/ethernet/Makefile | 1 +
drivers/net/ethernet/moxa/Kconfig | 30 ++
drivers/net/ethernet/moxa/Makefile | 5 +
drivers/net/ethernet/moxa/moxart_ether.c | 559 +++++++++++++++++++++
drivers/net/ethernet/moxa/moxart_ether.h | 330 ++++++++++++
7 files changed, 947 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
create mode 100644 drivers/net/ethernet/moxa/Kconfig
create mode 100644 drivers/net/ethernet/moxa/Makefile
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.c
create mode 100644 drivers/net/ethernet/moxa/moxart_ether.h

diff --git a/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
new file mode 100644
index 0000000..583418b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/moxa,moxart-mac.txt
@@ -0,0 +1,21 @@
+MOXA ART Ethernet Controller
+
+Required properties:
+
+- compatible : Must be "moxa,moxart-mac"
+- reg : Should contain register location and length
+- interrupts : Should contain the mac interrupt number
+
+Example:
+
+ mac0: mac@90900000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x90900000 0x100>;
+ interrupts = <25 0>;
+ };
+
+ mac1: mac@92000000 {
+ compatible = "moxa,moxart-mac";
+ reg = <0x92000000 0x100>;
+ interrupts = <27 0>;
+ };
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index 2037080..506b024 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -90,6 +90,7 @@ source "drivers/net/ethernet/marvell/Kconfig"
source "drivers/net/ethernet/mellanox/Kconfig"
source "drivers/net/ethernet/micrel/Kconfig"
source "drivers/net/ethernet/microchip/Kconfig"
+source "drivers/net/ethernet/moxa/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"

config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 390bd0b..c0b8789 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
+obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/moxa/Kconfig b/drivers/net/ethernet/moxa/Kconfig
new file mode 100644
index 0000000..1731e05
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Kconfig
@@ -0,0 +1,30 @@
+#
+# MOXART device configuration
+#
+
+config NET_VENDOR_MOXART
+ bool "MOXA ART devices"
+ default y
+ depends on (ARM && ARCH_MOXART)
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ 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 MOXA ART devices. If you say Y, you will be asked
+ for your specific card in the following questions.
+
+if NET_VENDOR_MOXART
+
+config ARM_MOXART_ETHER
+ tristate "MOXART Ethernet support"
+ depends on ARM && ARCH_MOXART
+ select NET_CORE
+ ---help---
+ If you wish to compile a kernel for a hardware with MOXA ART SoC and
+ want to use the internal ethernet then you should answer Y to this.
+
+
+endif # NET_VENDOR_MOXART
diff --git a/drivers/net/ethernet/moxa/Makefile b/drivers/net/ethernet/moxa/Makefile
new file mode 100644
index 0000000..aa3c73e9
--- /dev/null
+++ b/drivers/net/ethernet/moxa/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the MOXART network device drivers.
+#
+
+obj-$(CONFIG_ARM_MOXART_ETHER) += moxart_ether.o
diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c
new file mode 100644
index 0000000..e1aa0f0
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.c
@@ -0,0 +1,559 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/crc32.h>
+#include <linux/crc32c.h>
+#include <linux/dma-mapping.h>
+
+#include "moxart_ether.h"
+
+static inline void moxart_emac_write(struct net_device *ndev,
+ unsigned int reg, unsigned long value)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(value, priv->base + reg);
+}
+
+static void moxart_update_mac_address(struct net_device *ndev)
+{
+ moxart_emac_write(ndev, REG_MAC_MS_ADDRESS,
+ ((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])));
+ moxart_emac_write(ndev, REG_MAC_MS_ADDRESS + 4,
+ ((ndev->dev_addr[2] << 24) |
+ (ndev->dev_addr[3] << 16) |
+ (ndev->dev_addr[4] << 8) |
+ (ndev->dev_addr[5])));
+}
+
+static int moxart_set_mac_address(struct net_device *ndev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+ moxart_update_mac_address(ndev);
+
+ return 0;
+}
+
+static void moxart_mac_free_memory(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ int i;
+
+ for (i = 0; i < RX_DESC_NUM; i++)
+ dma_unmap_single(&ndev->dev, priv->rx_mapping[i],
+ priv->rx_buf_size, DMA_FROM_DEVICE);
+
+ if (priv->tx_desc_base)
+ dma_free_coherent(NULL, TX_REG_DESC_SIZE * TX_DESC_NUM,
+ priv->tx_desc_base, priv->tx_base);
+
+ if (priv->rx_desc_base)
+ dma_free_coherent(NULL, RX_REG_DESC_SIZE * RX_DESC_NUM,
+ priv->rx_desc_base, priv->rx_base);
+
+ kfree(priv->tx_buf_base);
+ kfree(priv->rx_buf_base);
+}
+
+static void moxart_mac_reset(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(SW_RST, priv->base + REG_MAC_CTRL);
+ while (readl(priv->base + REG_MAC_CTRL) & SW_RST)
+ mdelay(10);
+
+ writel(0, priv->base + REG_INTERRUPT_MASK);
+
+ priv->reg_maccr = RX_BROADPKT | FULLDUP | CRC_APD | RX_FTL;
+}
+
+static void moxart_mac_enable(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ writel(0x00001010, priv->base + REG_INT_TIMER_CTRL);
+ writel(0x00000001, priv->base + REG_APOLL_TIMER_CTRL);
+ writel(0x00000390, priv->base + REG_DMA_BLEN_CTRL);
+
+ priv->reg_imr |= (RPKT_FINISH_M | XPKT_FINISH_M);
+ writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK);
+
+ priv->reg_maccr |= (RCV_EN | XMT_EN | RDMA_EN | XDMA_EN);
+ writel(priv->reg_maccr, priv->base + REG_MAC_CTRL);
+}
+
+static void moxart_mac_setup_desc_ring(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ void __iomem *desc;
+ int i;
+
+ for (i = 0; i < TX_DESC_NUM; i++) {
+ desc = priv->tx_desc_base + i * TX_REG_DESC_SIZE;
+ memset(desc, 0, TX_REG_DESC_SIZE);
+
+ priv->tx_buf[i] = priv->tx_buf_base + priv->tx_buf_size * i;
+ }
+ writel(TX_DESC1_END, desc + TX_REG_OFFSET_DESC1);
+
+ priv->tx_head = 0;
+ priv->tx_tail = 0;
+
+ for (i = 0; i < RX_DESC_NUM; i++) {
+ desc = priv->rx_desc_base + i * RX_REG_DESC_SIZE;
+ memset(desc, 0, RX_REG_DESC_SIZE);
+ writel(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0);
+ writel(RX_BUF_SIZE & RX_DESC1_BUF_SIZE_MASK,
+ desc + RX_REG_OFFSET_DESC1);
+
+ priv->rx_buf[i] = priv->rx_buf_base + priv->rx_buf_size * i;
+ priv->rx_mapping[i] = dma_map_single(&ndev->dev,
+ priv->rx_buf[i],
+ priv->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->rx_mapping[i]))
+ netdev_err(ndev, "DMA mapping error\n");
+
+ writel(priv->rx_mapping[i],
+ desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_PHYS);
+ writel(priv->rx_buf[i],
+ desc + RX_REG_OFFSET_DESC2 + RX_DESC2_ADDRESS_VIRT);
+ }
+ writel(RX_DESC1_END, desc + RX_REG_OFFSET_DESC1);
+
+ priv->rx_head = 0;
+
+ /* reset the MAC controler TX/RX desciptor base address */
+ writel(priv->tx_base, priv->base + REG_TXR_BASE_ADDRESS);
+ writel(priv->rx_base, priv->base + REG_RXR_BASE_ADDRESS);
+}
+
+static int moxart_mac_open(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ if (!is_valid_ether_addr(ndev->dev_addr))
+ return -EADDRNOTAVAIL;
+
+ napi_enable(&priv->napi);
+
+ moxart_mac_reset(ndev);
+ moxart_update_mac_address(ndev);
+ moxart_mac_setup_desc_ring(ndev);
+ moxart_mac_enable(ndev);
+ netif_start_queue(ndev);
+
+ netdev_dbg(ndev, "%s: IMR=0x%x, MACCR=0x%x\n",
+ __func__, readl(priv->base + REG_INTERRUPT_MASK),
+ readl(priv->base + REG_MAC_CTRL));
+
+ return 0;
+}
+
+static int moxart_mac_stop(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ napi_disable(&priv->napi);
+
+ netif_stop_queue(ndev);
+
+ /* disable all interrupts */
+ writel(0, priv->base + REG_INTERRUPT_MASK);
+
+ /* disable all functions */
+ writel(0, priv->base + REG_MAC_CTRL);
+
+ return 0;
+}
+
+static int moxart_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct moxart_mac_priv_t *priv = container_of(napi,
+ struct moxart_mac_priv_t,
+ napi);
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *skb;
+ void __iomem *desc;
+ unsigned int desc0, len;
+ int rx_head = priv->rx_head;
+ int rx = 0;
+
+ while (1) {
+ desc = priv->rx_desc_base + (RX_REG_DESC_SIZE * rx_head);
+ desc0 = readl(desc + RX_REG_OFFSET_DESC0);
+
+ if (desc0 & RX_DESC0_DMA_OWN)
+ break;
+
+ if (desc0 & (RX_DESC0_ERR | RX_DESC0_CRC_ERR | RX_DESC0_FTL |
+ RX_DESC0_RUNT | RX_DESC0_ODD_NB)) {
+ net_dbg_ratelimited("packet error\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ continue;
+ }
+
+ len = desc0 & RX_DESC0_FRAME_LEN_MASK;
+
+ if (len > RX_BUF_SIZE)
+ len = RX_BUF_SIZE;
+
+ skb = build_skb(priv->rx_buf[rx_head], priv->rx_buf_size);
+ if (unlikely(!skb)) {
+ net_dbg_ratelimited("build_skb failed\n");
+ priv->stats.rx_dropped++;
+ priv->stats.rx_errors++;
+ }
+
+ skb_put(skb, len);
+ skb->protocol = eth_type_trans(skb, ndev);
+ napi_gro_receive(&priv->napi, skb);
+ rx++;
+
+ ndev->last_rx = jiffies;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += len;
+ if (desc0 & RX_DESC0_MULTICAST)
+ priv->stats.multicast++;
+
+ writel(RX_DESC0_DMA_OWN, desc + RX_REG_OFFSET_DESC0);
+
+ rx_head = RX_NEXT(rx_head);
+ priv->rx_head = rx_head;
+
+ if (rx >= budget)
+ break;
+ }
+
+ if (rx < budget) {
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
+ }
+
+ priv->reg_imr |= RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK);
+
+ return rx;
+}
+
+static void moxart_tx_finished(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned tx_head = priv->tx_head;
+ unsigned tx_tail = priv->tx_tail;
+
+ while (tx_tail != tx_head) {
+ dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail],
+ priv->tx_len[tx_tail], DMA_TO_DEVICE);
+
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += priv->tx_skb[tx_tail]->len;
+
+ dev_kfree_skb_irq(priv->tx_skb[tx_tail]);
+ priv->tx_skb[tx_tail] = NULL;
+
+ tx_tail = TX_NEXT(tx_tail);
+ }
+ priv->tx_tail = tx_tail;
+}
+
+static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id)
+{
+ struct net_device *ndev = (struct net_device *) dev_id;
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ unsigned int ists = readl(priv->base + REG_INTERRUPT_STATUS);
+
+ if (ists & XPKT_OK_INT_STS)
+ moxart_tx_finished(ndev);
+
+ if (ists & RPKT_FINISH) {
+ if (napi_schedule_prep(&priv->napi)) {
+ priv->reg_imr &= ~RPKT_FINISH_M;
+ writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK);
+ __napi_schedule(&priv->napi);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int moxart_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ void __iomem *desc;
+ unsigned int len;
+ unsigned int tx_head = priv->tx_head;
+ u32 txdes1;
+
+ desc = priv->tx_desc_base + (TX_REG_DESC_SIZE * tx_head);
+
+ spin_lock_irq(&priv->txlock);
+ if (readl(desc + TX_REG_OFFSET_DESC0) & TX_DESC0_DMA_OWN) {
+ net_dbg_ratelimited("no TX space for packet\n");
+ priv->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+ }
+
+ len = skb->len > TX_BUF_SIZE ? TX_BUF_SIZE : skb->len;
+
+ priv->tx_mapping[tx_head] = dma_map_single(&ndev->dev, skb->data,
+ len, DMA_TO_DEVICE);
+ if (dma_mapping_error(&ndev->dev, priv->tx_mapping[tx_head])) {
+ netdev_err(ndev, "DMA mapping error\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ priv->tx_len[tx_head] = len;
+ priv->tx_skb[tx_head] = skb;
+
+ writel(priv->tx_mapping[tx_head],
+ desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_PHYS);
+ writel(skb->data,
+ desc + TX_REG_OFFSET_DESC2 + TX_DESC2_ADDRESS_VIRT);
+
+ if (skb->len < ETH_ZLEN) {
+ memset(&skb->data[skb->len],
+ 0, ETH_ZLEN - skb->len);
+ len = ETH_ZLEN;
+ }
+
+ txdes1 = readl(desc + TX_REG_OFFSET_DESC1);
+ txdes1 |= TX_DESC1_LTS | TX_DESC1_FTS;
+ txdes1 &= ~(TX_DESC1_FIFO_COMPLETE | TX_DESC1_INTR_COMPLETE);
+ txdes1 |= (len & TX_DESC1_BUF_SIZE_MASK);
+ writel(txdes1, desc + TX_REG_OFFSET_DESC1);
+ writel(TX_DESC0_DMA_OWN, desc + TX_REG_OFFSET_DESC0);
+
+ /* start to send packet */
+ writel(0xffffffff, priv->base + REG_TX_POLL_DEMAND);
+
+ priv->tx_head = TX_NEXT(tx_head);
+
+ ndev->trans_start = jiffies;
+
+ spin_unlock_irq(&priv->txlock);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *moxart_mac_get_stats(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ return &priv->stats;
+}
+
+static void moxart_mac_setmulticast(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *ha;
+ int crc_val;
+
+ netdev_for_each_mc_addr(ha, ndev) {
+ crc_val = crc32_le(~0, ha->addr, ETH_ALEN);
+ crc_val = (crc_val >> 26) & 0x3f;
+ if (crc_val >= 32) {
+ writel(readl(priv->base + REG_MCAST_HASH_TABLE1) |
+ (1UL << (crc_val - 32)),
+ priv->base + REG_MCAST_HASH_TABLE1);
+ } else {
+ writel(readl(priv->base + REG_MCAST_HASH_TABLE0) |
+ (1UL << crc_val),
+ priv->base + REG_MCAST_HASH_TABLE0);
+ }
+ }
+}
+
+static void moxart_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct moxart_mac_priv_t *priv = netdev_priv(ndev);
+
+ spin_lock_irq(&priv->txlock);
+
+ (ndev->flags & IFF_PROMISC) ? (priv->reg_maccr |= RCV_ALL) :
+ (priv->reg_maccr &= ~RCV_ALL);
+
+ (ndev->flags & IFF_ALLMULTI) ? (priv->reg_maccr |= RX_MULTIPKT) :
+ (priv->reg_maccr &= ~RX_MULTIPKT);
+
+ if ((ndev->flags & IFF_MULTICAST) && netdev_mc_count(ndev)) {
+ priv->reg_maccr |= HT_MULTI_EN;
+ moxart_mac_setmulticast(ndev);
+ } else {
+ priv->reg_maccr &= ~HT_MULTI_EN;
+ }
+
+ writel(priv->reg_maccr, priv->base + REG_MAC_CTRL);
+
+ spin_unlock_irq(&priv->txlock);
+}
+
+static struct net_device_ops moxart_netdev_ops = {
+ .ndo_open = moxart_mac_open,
+ .ndo_stop = moxart_mac_stop,
+ .ndo_start_xmit = moxart_mac_start_xmit,
+ .ndo_get_stats = moxart_mac_get_stats,
+ .ndo_set_rx_mode = moxart_mac_set_rx_mode,
+ .ndo_set_mac_address = moxart_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static int moxart_mac_probe(struct platform_device *pdev)
+{
+ struct device *p_dev = &pdev->dev;
+ struct device_node *node = p_dev->of_node;
+ struct net_device *ndev;
+ struct moxart_mac_priv_t *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct moxart_mac_priv_t));
+ if (!ndev)
+ return -ENOMEM;
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (irq <= 0) {
+ netdev_err(ndev, "irq_of_parse_and_map failed\n");
+ return -EINVAL;
+ }
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ndev->base_addr = res->start;
+ priv->base = devm_ioremap_resource(p_dev, res);
+ ret = IS_ERR(priv->base);
+ if (ret) {
+ dev_err(p_dev, "devm_ioremap_resource failed\n");
+ goto init_fail;
+ }
+
+ spin_lock_init(&priv->txlock);
+
+ priv->tx_buf_size = TX_BUF_SIZE;
+ priv->rx_buf_size = RX_BUF_SIZE +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE *
+ TX_DESC_NUM, &priv->tx_base,
+ GFP_DMA | GFP_KERNEL);
+ if (priv->tx_desc_base == NULL)
+ goto init_fail;
+
+ priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE *
+ RX_DESC_NUM, &priv->rx_base,
+ GFP_DMA | GFP_KERNEL);
+ if (priv->rx_desc_base == NULL)
+ goto init_fail;
+
+ priv->tx_buf_base = kmalloc(priv->tx_buf_size * TX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->tx_buf_base)
+ goto init_fail;
+
+ priv->rx_buf_base = kmalloc(priv->rx_buf_size * RX_DESC_NUM,
+ GFP_ATOMIC);
+ if (!priv->rx_buf_base)
+ goto init_fail;
+
+ platform_set_drvdata(pdev, ndev);
+
+ ret = devm_request_irq(p_dev, irq, moxart_mac_interrupt, 0,
+ pdev->name, ndev);
+ if (ret) {
+ netdev_err(ndev, "devm_request_irq failed\n");
+ goto init_fail;
+ }
+
+ ether_setup(ndev);
+ ndev->netdev_ops = &moxart_netdev_ops;
+ netif_napi_add(ndev, &priv->napi, moxart_rx_poll, RX_DESC_NUM);
+ ndev->priv_flags |= IFF_UNICAST_FLT;
+ ndev->irq = irq;
+
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ free_netdev(ndev);
+ goto init_fail;
+ }
+
+ netdev_dbg(ndev, "%s: IRQ=%d address=%pM\n",
+ __func__, ndev->irq, ndev->dev_addr);
+
+ return 0;
+
+init_fail:
+ netdev_err(ndev, "init failed\n");
+ moxart_mac_free_memory(ndev);
+
+ return ret;
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ unregister_netdev(ndev);
+ free_irq(ndev->irq, ndev);
+ moxart_mac_free_memory(ndev);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static const struct of_device_id moxart_mac_match[] = {
+ { .compatible = "moxa,moxart-mac" },
+ { }
+};
+
+struct __initdata platform_driver moxart_mac_driver = {
+ .probe = moxart_mac_probe,
+ .remove = moxart_remove,
+ .driver = {
+ .name = "moxart-ethernet",
+ .owner = THIS_MODULE,
+ .of_match_table = moxart_mac_match,
+ },
+};
+module_platform_driver(moxart_mac_driver);
+
+MODULE_DESCRIPTION("MOXART RTL8201CP Ethernet driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jonas Jensen <[email protected]>");
+
diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h
new file mode 100644
index 0000000..2be9280
--- /dev/null
+++ b/drivers/net/ethernet/moxa/moxart_ether.h
@@ -0,0 +1,330 @@
+/* MOXA ART Ethernet (RTL8201CP) driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <[email protected]>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <http://www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _MOXART_ETHERNET_H
+#define _MOXART_ETHERNET_H
+
+#define TX_REG_OFFSET_DESC0 0
+#define TX_REG_OFFSET_DESC1 4
+#define TX_REG_OFFSET_DESC2 8
+#define TX_REG_DESC_SIZE 16
+
+#define RX_REG_OFFSET_DESC0 0
+#define RX_REG_OFFSET_DESC1 4
+#define RX_REG_OFFSET_DESC2 8
+#define RX_REG_DESC_SIZE 16
+
+#define TX_DESC0_PKT_LATE_COL 0x1 /* abort, late collision */
+#define TX_DESC0_RX_PKT_EXS_COL 0x2 /* abort, >16 collisions */
+#define TX_DESC0_DMA_OWN 0x80000000 /* owned by controller */
+#define TX_DESC1_BUF_SIZE_MASK 0x7ff
+#define TX_DESC1_LTS 0x8000000 /* last TX packet */
+#define TX_DESC1_FTS 0x10000000 /* first TX packet */
+#define TX_DESC1_FIFO_COMPLETE 0x20000000
+#define TX_DESC1_INTR_COMPLETE 0x40000000
+#define TX_DESC1_END 0x80000000
+#define TX_DESC2_ADDRESS_PHYS 0
+#define TX_DESC2_ADDRESS_VIRT 4
+
+#define RX_DESC0_FRAME_LEN 0
+#define RX_DESC0_FRAME_LEN_MASK 0x7FF
+#define RX_DESC0_MULTICAST 0x10000
+#define RX_DESC0_BROADCAST 0x20000
+#define RX_DESC0_ERR 0x40000
+#define RX_DESC0_CRC_ERR 0x80000
+#define RX_DESC0_FTL 0x100000
+#define RX_DESC0_RUNT 0x200000 /* packet less than 64 bytes */
+#define RX_DESC0_ODD_NB 0x400000 /* receive odd nibbles */
+#define RX_DESC0_LRS 0x10000000 /* last receive segment */
+#define RX_DESC0_FRS 0x20000000 /* first receive segment */
+#define RX_DESC0_DMA_OWN 0x80000000
+#define RX_DESC1_BUF_SIZE_MASK 0x7FF
+#define RX_DESC1_END 0x80000000
+#define RX_DESC2_ADDRESS_PHYS 0
+#define RX_DESC2_ADDRESS_VIRT 4
+
+#define TX_DESC_NUM 64
+#define TX_DESC_NUM_MASK (TX_DESC_NUM-1)
+#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK))
+#define TX_BUF_SIZE 1600
+#define TX_BUF_SIZE_MAX (TX_DESC1_BUF_SIZE_MASK+1)
+
+#define RX_DESC_NUM 64
+#define RX_DESC_NUM_MASK (RX_DESC_NUM-1)
+#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK))
+#define RX_BUF_SIZE 1600
+#define RX_BUF_SIZE_MAX (RX_DESC1_BUF_SIZE_MASK+1)
+
+#define REG_INTERRUPT_STATUS 0
+#define REG_INTERRUPT_MASK 4
+#define REG_MAC_MS_ADDRESS 8
+#define REG_MAC_LS_ADDRESS 12
+#define REG_MCAST_HASH_TABLE0 16
+#define REG_MCAST_HASH_TABLE1 20
+#define REG_TX_POLL_DEMAND 24
+#define REG_RX_POLL_DEMAND 28
+#define REG_TXR_BASE_ADDRESS 32
+#define REG_RXR_BASE_ADDRESS 36
+#define REG_INT_TIMER_CTRL 40
+#define REG_APOLL_TIMER_CTRL 44
+#define REG_DMA_BLEN_CTRL 48
+#define REG_RESERVED1 52
+#define REG_MAC_CTRL 136
+#define REG_MAC_STATUS 140
+#define REG_PHY_CTRL 144
+#define REG_PHY_WRITE_DATA 148
+#define REG_FLOW_CTRL 152
+#define REG_BACK_PRESSURE 156
+#define REG_RESERVED2 160
+#define REG_TEST_SEED 196
+#define REG_DMA_FIFO_STATE 200
+#define REG_TEST_MODE 204
+#define REG_RESERVED3 208
+#define REG_TX_COL_COUNTER 212
+#define REG_RPF_AEP_COUNTER 216
+#define REG_XM_PG_COUNTER 220
+#define REG_RUNT_TLC_COUNTER 224
+#define REG_CRC_FTL_COUNTER 228
+#define REG_RLC_RCC_COUNTER 232
+#define REG_BROC_COUNTER 236
+#define REG_MULCA_COUNTER 240
+#define REG_RP_COUNTER 244
+#define REG_XP_COUNTER 248
+
+#define REG_PHY_CTRL_OFFSET 0x0
+#define REG_PHY_STATUS 0x1
+#define REG_PHY_ID1 0x2
+#define REG_PHY_ID2 0x3
+#define REG_PHY_ANA 0x4
+#define REG_PHY_ANLPAR 0x5
+#define REG_PHY_ANE 0x6
+#define REG_PHY_ECTRL1 0x10
+#define REG_PHY_QPDS 0x11
+#define REG_PHY_10BOP 0x12
+#define REG_PHY_ECTRL2 0x13
+#define REG_PHY_FTMAC100_WRITE 0x8000000
+#define REG_PHY_FTMAC100_READ 0x4000000
+
+/* REG_INTERRUPT_STATUS */
+#define RPKT_FINISH BIT(0) /* DMA data received */
+#define NORXBUF BIT(1) /* receive buffer unavailable */
+#define XPKT_FINISH BIT(2) /* DMA moved data to TX FIFO */
+#define NOTXBUF BIT(3) /* transmit buffer unavailable */
+#define XPKT_OK_INT_STS BIT(4) /* transmit to ethernet success */
+#define XPKT_LOST_INT_STS BIT(5) /* transmit ethernet lost (collision) */
+#define RPKT_SAV BIT(6) /* FIFO receive success */
+#define RPKT_LOST_INT_STS BIT(7) /* FIFO full, receive failed */
+#define AHB_ERR BIT(8) /* AHB error */
+#define PHYSTS_CHG BIT(9) /* PHY link status change */
+
+/* REG_INTERRUPT_MASK */
+#define RPKT_FINISH_M BIT(0)
+#define NORXBUF_M BIT(1)
+#define XPKT_FINISH_M BIT(2)
+#define NOTXBUF_M BIT(3)
+#define XPKT_OK_M BIT(4)
+#define XPKT_LOST_M BIT(5)
+#define RPKT_SAV_M BIT(6)
+#define RPKT_LOST_M BIT(7)
+#define AHB_ERR_M BIT(8)
+#define PHYSTS_CHG_M BIT(9)
+
+/* REG_MAC_MS_ADDRESS */
+#define MAC_MADR_MASK 0xffff /* 2 MSB MAC address */
+
+/* REG_INT_TIMER_CTRL */
+#define TXINT_TIME_SEL BIT(15) /* TX cycle time period */
+#define TXINT_THR_MASK 0x7000
+#define TXINT_CNT_MASK 0xf00
+#define RXINT_TIME_SEL BIT(7) /* RX cycle time period */
+#define RXINT_THR_MASK 0x70
+#define RXINT_CNT_MASK 0xF
+
+/* REG_APOLL_TIMER_CTRL */
+#define TXPOLL_TIME_SEL BIT(12) /* TX poll time period */
+#define TXPOLL_CNT_MASK 0xf00
+#define TXPOLL_CNT_SHIFT_BIT 8
+#define RXPOLL_TIME_SEL BIT(4) /* RX poll time period */
+#define RXPOLL_CNT_MASK 0xF
+#define RXPOLL_CNT_SHIFT_BIT 0
+
+/* REG_DMA_BLEN_CTRL */
+#define RX_THR_EN BIT(9) /* RX FIFO threshold arbitration */
+#define RXFIFO_HTHR_MASK 0x1c0
+#define RXFIFO_LTHR_MASK 0x38
+#define INCR16_EN BIT(2) /* AHB bus INCR16 burst command */
+#define INCR8_EN BIT(1) /* AHB bus INCR8 burst command */
+#define INCR4_EN BIT(0) /* AHB bus INCR4 burst command */
+
+/* REG_MAC_CTRL */
+#define RX_BROADPKT BIT(17) /* receive broadcast packets */
+#define RX_MULTIPKT BIT(16) /* receive all multicast packets */
+#define FULLDUP BIT(15) /* full duplex */
+#define CRC_APD BIT(14) /* append CRC to transmitted packet */
+#define RCV_ALL BIT(12) /* ignore incoming packet destination */
+#define RX_FTL BIT(11) /* accept packets larger than 1518 B */
+#define RX_RUNT BIT(10) /* accept packets smaller than 64 B */
+#define HT_MULTI_EN BIT(9) /* accept on hash and mcast pass */
+#define RCV_EN BIT(8) /* receiver enable */
+#define ENRX_IN_HALFTX BIT(6) /* enable receive in half duplex mode */
+#define XMT_EN BIT(5) /* transmit enable */
+#define CRC_DIS BIT(4) /* disable CRC check when receiving */
+#define LOOP_EN BIT(3) /* internal loop-back */
+#define SW_RST BIT(2) /* software reset, last 64 AHB clocks */
+#define RDMA_EN BIT(1) /* enable receive DMA chan */
+#define XDMA_EN BIT(0) /* enable transmit DMA chan */
+
+/* REG_MAC_STATUS */
+#define COL_EXCEED BIT(11) /* more than 16 collisions */
+#define LATE_COL BIT(10) /* transmit late collision detected */
+#define XPKT_LOST BIT(9) /* transmit to ethernet lost */
+#define XPKT_OK BIT(8) /* transmit to ethernet success */
+#define RUNT_MAC_STS BIT(7) /* receive runt detected */
+#define FTL_MAC_STS BIT(6) /* receive frame too long detected */
+#define CRC_ERR_MAC_STS BIT(5)
+#define RPKT_LOST BIT(4) /* RX FIFO full, receive failed */
+#define RPKT_SAVE BIT(3) /* RX FIFO receive success */
+#define COL BIT(2) /* collision, incoming packet dropped */
+#define MCPU_BROADCAST BIT(1)
+#define MCPU_MULTICAST BIT(0)
+
+/* REG_PHY_CTRL */
+#define MIIWR BIT(27) /* init write sequence (auto cleared)*/
+#define MIIRD BIT(26)
+#define REGAD_MASK 0x3e00000
+#define PHYAD_MASK 0x1f0000
+#define MIIRDATA_MASK 0xffff
+
+/* REG_PHY_WRITE_DATA */
+#define MIIWDATA_MASK 0xffff
+
+/* REG_FLOW_CTRL */
+#define PAUSE_TIME_MASK 0xffff0000
+#define FC_HIGH_MASK 0xf000
+#define FC_LOW_MASK 0xf00
+#define RX_PAUSE BIT(4) /* receive pause frame */
+#define TX_PAUSED BIT(3) /* transmit pause due to receive */
+#define FCTHR_EN BIT(2) /* enable threshold mode. */
+#define TX_PAUSE BIT(1) /* transmit pause frame */
+#define FC_EN BIT(0) /* flow control mode enable */
+
+/* REG_BACK_PRESSURE */
+#define BACKP_LOW_MASK 0xf00
+#define BACKP_JAM_LEN_MASK 0xf0
+#define BACKP_MODE BIT(1) /* address mode */
+#define BACKP_ENABLE BIT(0)
+
+/* REG_TEST_SEED */
+#define TEST_SEED_MASK 0x3fff
+
+/* REG_DMA_FIFO_STATE */
+#define TX_DMA_REQUEST BIT(31)
+#define RX_DMA_REQUEST BIT(30)
+#define TX_DMA_GRANT BIT(29)
+#define RX_DMA_GRANT BIT(28)
+#define TX_FIFO_EMPTY BIT(27)
+#define RX_FIFO_EMPTY BIT(26)
+#define TX_DMA2_SM_MASK 0x7000
+#define TX_DMA1_SM_MASK 0xf00
+#define RX_DMA2_SM_MASK 0x70
+#define RX_DMA1_SM_MASK 0xF
+
+/* REG_TEST_MODE */
+#define SINGLE_PKT BIT(26) /* single packet mode */
+#define PTIMER_TEST BIT(25) /* automatic polling timer test mode */
+#define ITIMER_TEST BIT(24) /* interrupt timer test mode */
+#define TEST_SEED_SELECT BIT(22)
+#define SEED_SELECT BIT(21)
+#define TEST_MODE BIT(20)
+#define TEST_TIME_MASK 0xffc00
+#define TEST_EXCEL_MASK 0x3e0
+
+/* REG_TX_COL_COUNTER */
+#define TX_MCOL_MASK 0xffff0000
+#define TX_MCOL_SHIFT_BIT 16
+#define TX_SCOL_MASK 0xffff
+#define TX_SCOL_SHIFT_BIT 0
+
+/* REG_RPF_AEP_COUNTER */
+#define RPF_MASK 0xffff0000
+#define RPF_SHIFT_BIT 16
+#define AEP_MASK 0xffff
+#define AEP_SHIFT_BIT 0
+
+/* REG_XM_PG_COUNTER */
+#define XM_MASK 0xffff0000
+#define XM_SHIFT_BIT 16
+#define PG_MASK 0xffff
+#define PG_SHIFT_BIT 0
+
+/* REG_RUNT_TLC_COUNTER */
+#define RUNT_CNT_MASK 0xffff0000
+#define RUNT_CNT_SHIFT_BIT 16
+#define TLCC_MASK 0xffff
+#define TLCC_SHIFT_BIT 0
+
+/* REG_CRC_FTL_COUNTER */
+#define CRCER_CNT_MASK 0xffff0000
+#define CRCER_CNT_SHIFT_BIT 16
+#define FTL_CNT_MASK 0xffff
+#define FTL_CNT_SHIFT_BIT 0
+
+/* REG_RLC_RCC_COUNTER */
+#define RLC_MASK 0xffff0000
+#define RLC_SHIFT_BIT 16
+#define RCC_MASK 0xffff
+#define RCC_SHIFT_BIT 0
+
+/* REG_PHY_STATUS */
+#define AN_COMPLETE 0x20
+#define LINK_STATUS 0x4
+
+struct moxart_mac_priv_t {
+ void __iomem *base;
+ struct net_device_stats stats;
+ unsigned int reg_maccr;
+ unsigned int reg_imr;
+ struct napi_struct napi;
+ struct net_device *ndev;
+
+ dma_addr_t rx_base;
+ dma_addr_t rx_mapping[RX_DESC_NUM];
+ void __iomem *rx_desc_base;
+ unsigned char *rx_buf_base;
+ unsigned char *rx_buf[RX_DESC_NUM];
+ unsigned int rx_head;
+ unsigned int rx_buf_size;
+
+ dma_addr_t tx_base;
+ dma_addr_t tx_mapping[TX_DESC_NUM];
+ void __iomem *tx_desc_base;
+ unsigned char *tx_buf_base;
+ unsigned char *tx_buf[RX_DESC_NUM];
+ unsigned int tx_head;
+ unsigned int tx_buf_size;
+
+ spinlock_t txlock;
+ unsigned int tx_len[TX_DESC_NUM];
+ struct sk_buff *tx_skb[TX_DESC_NUM];
+ unsigned int tx_tail;
+};
+
+#if TX_BUF_SIZE >= TX_BUF_SIZE_MAX
+#error MOXA ART Ethernet device driver TX buffer is too large!
+#endif
+#if RX_BUF_SIZE >= RX_BUF_SIZE_MAX
+#error MOXA ART Ethernet device driver RX buffer is too large!
+#endif
+
+#endif
--
1.8.2.1

2013-08-12 04:33:37

by David Miller

[permalink] [raw]
Subject: Re: [PATCH v5] net: Add MOXA ART SoCs ethernet driver

From: Jonas Jensen <[email protected]>
Date: Thu, 8 Aug 2013 13:34:54 +0200

> The MOXA UC-711X hardware(s) has an ethernet controller that seem
> to be developed internally. The IC used is "RTL8201CP".
>
> Since there is no public documentation, this driver is mostly the
> one published by MOXA that has been heavily cleaned up / ported
> from linux 2.6.9.
>
> Signed-off-by: Jonas Jensen <[email protected]>

Applied to net-next, thanks.