[linux-yocto] [PATCH 02/17] axxia: Clean Up the MDIO Access Code
Daniel Dragomir
daniel.dragomir at windriver.com
Tue May 16 11:38:54 PDT 2017
From: John Jacques <john.jacques at intel.com>
Removes unused code and adds the second MDIO bus
available on 6700.
Signed-off-by: John Jacques <john.jacques at intel.com>
---
arch/arm64/boot/dts/intel/axc6732-waco.dts | 7 +
arch/arm64/boot/dts/intel/axc67xx.dtsi | 4 +-
arch/arm64/boot/dts/intel/axm56xx.dtsi | 2 +-
drivers/misc/Kconfig | 6 +
drivers/misc/Makefile | 1 +
drivers/misc/axxia-mdio.c | 228 ++++++
drivers/net/ethernet/lsi/Kconfig | 34 -
drivers/net/ethernet/lsi/Makefile | 2 -
drivers/net/ethernet/lsi/lsi-femac.c | 1198 ----------------------------
drivers/net/ethernet/lsi/lsi-mdio.c | 205 -----
include/linux/axxia-mdio.h | 21 +
11 files changed, 266 insertions(+), 1442 deletions(-)
create mode 100644 drivers/misc/axxia-mdio.c
delete mode 100644 drivers/net/ethernet/lsi/lsi-femac.c
delete mode 100644 drivers/net/ethernet/lsi/lsi-mdio.c
create mode 100644 include/linux/axxia-mdio.h
diff --git a/arch/arm64/boot/dts/intel/axc6732-waco.dts b/arch/arm64/boot/dts/intel/axc6732-waco.dts
index 056efc4..c600dbe 100644
--- a/arch/arm64/boot/dts/intel/axc6732-waco.dts
+++ b/arch/arm64/boot/dts/intel/axc6732-waco.dts
@@ -55,6 +55,13 @@
};
};
+&mdio1 {
+ status = "okay";
+ lsi,mdio-clk-offset = <0x1c>;
+ lsi,mdio-clk-period = <0xf0>;
+ max-speed = <10>;
+};
+
&spi0 {
status = "okay";
diff --git a/arch/arm64/boot/dts/intel/axc67xx.dtsi b/arch/arm64/boot/dts/intel/axc67xx.dtsi
index 3554dd5..f6712fb 100644
--- a/arch/arm64/boot/dts/intel/axc67xx.dtsi
+++ b/arch/arm64/boot/dts/intel/axc67xx.dtsi
@@ -120,7 +120,7 @@
};
mdio0: mdio at 8080260000 {
- compatible = "lsi,axm-mdio";
+ compatible = "lsi,axm-mdio", "intel,axxia-mdio0";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x80 0x80260000 0 0x1000>;
@@ -128,7 +128,7 @@
};
mdio1: mdio at 8080270000 {
- compatible = "lsi,axm-mdio";
+ compatible = "intel,axxia-mdio1";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x80 0x80270000 0 0x1000>;
diff --git a/arch/arm64/boot/dts/intel/axm56xx.dtsi b/arch/arm64/boot/dts/intel/axm56xx.dtsi
index ff140b7..08ebbe9 100644
--- a/arch/arm64/boot/dts/intel/axm56xx.dtsi
+++ b/arch/arm64/boot/dts/intel/axm56xx.dtsi
@@ -130,7 +130,7 @@
};
mdio: mdio at 8080200000 {
- compatible = "lsi,axm-mdio";
+ compatible = "lsi,axm-mdio", "intel,axxia-mdio0";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x80 0x80200000 0 0x1000>;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c7be89f..13f8e17 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -530,6 +530,12 @@ config AXXIA_PEI
help
Set up the PEI controllers in Linux instead of the boot loader.
+config AXXIA_MDIO
+ bool "Axxia MDIO Driver"
+ depends on ARCH_AXXIA
+ help
+ Provide access to the Axxia MDIO bus.
+
config VEXPRESS_SYSCFG
bool "Versatile Express System Configuration driver"
depends on VEXPRESS_CONFIG
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 00cdb96..438adf2 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_LSI_MTC) += lsi-mtc.o
obj-$(CONFIG_LSI_SMMON) += lsi-smmon.o
obj-$(CONFIG_AXXIA_OEM) += axxia-oem.o
obj-$(CONFIG_AXXIA_PEI) += axxia-pei.o
+obj-$(CONFIG_AXXIA_MDIO) += axxia-mdio.o
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
diff --git a/drivers/misc/axxia-mdio.c b/drivers/misc/axxia-mdio.c
new file mode 100644
index 0000000..acaca53
--- /dev/null
+++ b/drivers/misc/axxia-mdio.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 Intel <john.jacques at intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+/* MDIO Registers */
+#define MDIO_CONTROL 0x00
+#define CONTROL_BUSY (1<<31) /* MDIO cycle in progress (RO) */
+#define CONTROL_NOPRE (1<<30) /* Suppress preamble */
+#define CONTROL_CL22 (0<<29) /* Clause-22 */
+#define CONTROL_CL45 (1<<29) /* Clause-45 */
+#define CONTROL_READ (2<<27) /* Read operation */
+#define CONTROL_WRITE (1<<27) /* Write operation */
+#define CONTROL_IFSEL (1<<26) /* Interface select */
+#define CONTROL_PHYREG(_n) (((_n) & 0x1F) << 21)
+#define CONTROL_PHYID(_n) (((_n) & 0x1F) << 16)
+#define CONTROL_DATA(_n) (((_n) & 0xFFFF) << 0)
+#define MDIO_STATUS 0x04
+#define STATUS_BUSY (1<<31)
+#define STATUS_DONE (1<<30)
+#define MDIO_CLK_OFFSET 0x08
+#define MDIO_CLK_PERIOD 0x0c
+
+/* MDIO bus driver private data */
+struct axxia_mdio_priv {
+ void __iomem *base;
+ struct mii_bus *bus;
+};
+
+static inline void __iomem *
+bus_to_regs(struct mii_bus *bus)
+{
+ return ((struct axxia_mdio_priv *)bus->priv)->base;
+}
+
+static int
+axxia_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+ void __iomem *base = bus_to_regs(bus);
+ u32 ctrl;
+ u32 data;
+
+ /* Set the mdio_done (status) bit. */
+ writel(STATUS_DONE | readl(base + MDIO_STATUS), base + MDIO_STATUS);
+
+ /* Write the command. */
+ ctrl = (CONTROL_READ |
+ CONTROL_PHYID(mii_id) |
+ CONTROL_PHYREG(regnum));
+
+ if (regnum & MII_ADDR_C45)
+ ctrl |= CONTROL_CL45;
+
+ writel(ctrl, base + MDIO_CONTROL);
+
+ /* Wait for the mdio_done (status) bit to clear. */
+ while ((readl(base + MDIO_STATUS) & STATUS_DONE) != 0)
+ cpu_relax();
+
+ /* Wait for the mdio_busy (control) bit to clear. */
+ do {
+ data = readl(base + MDIO_CONTROL);
+ } while ((data & CONTROL_BUSY) != 0);
+
+ return data & 0xFFFF;
+}
+
+static int
+axxia_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
+{
+ void __iomem *base = bus_to_regs(bus);
+ u32 ctrl;
+
+ printk("%s:%d - base=0x%p\n", __FILE__, __LINE__, base);
+
+ /* Wait for mdio_busy (control) to be clear. */
+ while ((readl(base + MDIO_CONTROL) & CONTROL_BUSY) != 0)
+ cpu_relax();
+
+ /* Set the mdio_busy (status) bit. */
+ writel(STATUS_DONE | readl(base + MDIO_STATUS), base + MDIO_STATUS);
+
+ /* Write the command. */
+ ctrl = (CONTROL_WRITE |
+ CONTROL_PHYID(mii_id) |
+ CONTROL_PHYREG(regnum) |
+ CONTROL_DATA(value));
+
+ if (regnum & MII_ADDR_C45)
+ ctrl |= CONTROL_CL45;
+
+ writel(ctrl, base + MDIO_CONTROL);
+
+ /* Wait for the mdio_done (status) bit to clear. */
+ while ((readl(base + MDIO_STATUS) & STATUS_DONE) != 0)
+ cpu_relax();
+
+ /* Wait for the mdio_busy (control) bit to clear. */
+ while ((readl(base + MDIO_CONTROL) & CONTROL_BUSY) != 0)
+ cpu_relax();
+
+ return 0;
+}
+
+static int
+axxia_mdio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct axxia_mdio_priv *priv = NULL;
+ struct resource *res;
+ int err;
+ u32 clk_offset = 0x10;
+ u32 clk_period = 0x2c;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENODEV;
+
+ priv->bus = mdiobus_alloc();
+
+ if (!priv->bus)
+ return -ENOMEM;
+
+ priv->bus->name = "Axxia MDIO",
+ priv->bus->read = axxia_mdio_read,
+ priv->bus->write = axxia_mdio_write,
+ priv->bus->priv = priv;
+ snprintf(priv->bus->id, MII_BUS_ID_SIZE, pdev->name);
+ printk("%s:%d - pdev->name=%s\n", __FILE__, __LINE__, pdev->name);
+
+ priv->base = devm_ioremap_resource(&pdev->dev, res);
+
+ if (!priv->base) {
+ dev_err(&pdev->dev, "Failed to map registers\n");
+ err = -ENODEV;
+ goto err_ret;
+ }
+
+ priv->bus->parent = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, priv->bus);
+
+ of_property_read_u32(np, "lsi,mdio-clk-offset", &clk_offset);
+ of_property_read_u32(np, "lsi,mdio-clk-period", &clk_period);
+
+ writel(clk_offset, priv->base + MDIO_CLK_OFFSET);
+ writel(clk_period, priv->base + MDIO_CLK_PERIOD);
+ writel(0, priv->base + MDIO_CONTROL);
+
+ err = of_mdiobus_register(priv->bus, np);
+
+ if (err) {
+ dev_err(&pdev->dev, "Failed to register MDIO bus\n");
+ goto err_ret;
+ }
+
+ return 0;
+
+err_ret:
+
+ if (priv && priv->bus)
+ kfree(priv->bus);
+
+ return err;
+}
+
+static int
+axxia_mdio_remove(struct platform_device *pdev)
+{
+ struct mii_bus *bus = dev_get_drvdata(&pdev->dev);
+
+ mdiobus_unregister(bus);
+ dev_set_drvdata(&pdev->dev, NULL);
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static struct of_device_id axxia_mdio_match[] = {
+ { .compatible = "lsi,axm-mdio", },
+ { .compatible = "intel,axxia-mdio0", },
+ { .compatible = "intel,axxia-mdio1", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, axxia_mdio_match);
+
+static struct platform_driver axxia_mdio_driver = {
+ .driver = {
+ .name = "axxia-mdio",
+ .owner = THIS_MODULE,
+ .of_match_table = axxia_mdio_match,
+ },
+ .probe = axxia_mdio_probe,
+ .remove = axxia_mdio_remove,
+};
+
+module_platform_driver(axxia_mdio_driver);
+
+MODULE_AUTHOR("John Jacques");
+MODULE_DESCRIPTION("Axxia MDIO Bus Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/lsi/Kconfig b/drivers/net/ethernet/lsi/Kconfig
index 408e643..a78867d 100644
--- a/drivers/net/ethernet/lsi/Kconfig
+++ b/drivers/net/ethernet/lsi/Kconfig
@@ -1,37 +1,3 @@
-config NET_VENDOR_LSI
- bool "LSI AXM55xx Ethernet support"
- select MII
- select PHYLIB
- default y if ARCH_AXXIA
- ---help---
- If you have a network interface driver for LSI AXXIA series, say Y
-
- 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 LSI devices. If you say Y, you will be
- asked for your specific card in the following questions.
-
-if NET_VENDOR_LSI
-
-config NET_LSI_MDIO
- bool "LSI AXM55xx MDIO bus driver"
- default y
- ---help---
- The MDIO controller in AXM55xx devices.
-
- If your board uses an external PHY connected to FEMAC, say Y.
-
-config NET_LSI_FEMAC
- tristate "LSI FEMAC Ethernet controller"
- select LSI_MDIO
- default y
- ---help---
- Say Y here if you want to use the built-in 10/100 Fast ethernet
- controller (FEMAC) on the AXM55xx devices.
-
-endif # NET_VENDOR_LSI
-
-
config LSI_NET
bool "LSI ACP34XX Ethernet support"
select MII
diff --git a/drivers/net/ethernet/lsi/Makefile b/drivers/net/ethernet/lsi/Makefile
index 5ebc246..a48a239 100644
--- a/drivers/net/ethernet/lsi/Makefile
+++ b/drivers/net/ethernet/lsi/Makefile
@@ -1,6 +1,4 @@
# Makefile for the LSI FEMAC network interface.
-obj-$(CONFIG_NET_LSI_MDIO) += lsi-mdio.o
-obj-$(CONFIG_NET_LSI_FEMAC) += lsi-femac.o
obj-$(CONFIG_LSI_NET) += lsi_acp_mdio.o
obj-$(CONFIG_LSI_NET) += lsi_acp_net.o
diff --git a/drivers/net/ethernet/lsi/lsi-femac.c b/drivers/net/ethernet/lsi/lsi-femac.c
deleted file mode 100644
index 734ea03..0000000
--- a/drivers/net/ethernet/lsi/lsi-femac.c
+++ /dev/null
@@ -1,1198 +0,0 @@
-/* drivers/net/ethernet/lsi/lsi-femac.c
- *
- * Network device driver for LSI Fast-Ethernet controller (FEMAC).
- *
- * Copyright (C) 2013 LSI
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/version.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/errno.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_mdio.h>
-#include <linux/dmapool.h>
-
-#define DRVNAME "lsi-femac"
-#define DESCRIPTOR_GRANULARITY 64
-#define MAX_FRAME_SIZE 1600
-#define NAPI_WEIGHT 64
-
-static unsigned char macaddr[ETH_ALEN];
-module_param_array(macaddr, byte, NULL, S_IRUSR);
-MODULE_PARM_DESC(macaddr, "Ethernet address");
-
-static int rx_num_desc = 128; /* Must be multiple of DESCRIPTOR_GRANULARITY */
-module_param(rx_num_desc, int, S_IRUSR);
-MODULE_PARM_DESC(rx_num_desc, "Number of receive descriptors");
-
-static int tx_num_desc = 128; /* Must be multiple of DESCRIPTOR_GRANULARITY */
-module_param(tx_num_desc, int, S_IRUSR);
-MODULE_PARM_DESC(tx_num_desc, "Number of transmit descriptors");
-
-/**
- * struct dma_desc - Hardware DMA descriptor
- */
-struct dma_desc {
- __le32 flags;
-#define DMADESC_FILL (0 << 0)
-#define DMADESC_BLOCK (1 << 0)
-#define DMADESC_SCATTER (2 << 0)
-#define DMADESC_READ (0 << 2)
-#define DMADESC_WRITE (1 << 2)
-#define DMADESC_SOP (1 << 3)
-#define DMADESC_EOP (1 << 4)
-#define DMADESC_INTR (1 << 5)
-#define DMADESC_ERROR (1 << 6)
-#define DMADESC_SWAP (1 << 7)
- __le16 pdu_len;
- __le16 buf_len;
- __le32 cookie;
- __le32 buf_ptr;
-};
-
-/**
- * struct queue_ptr - Holds the state of the RX or TX queue
- * @hw_tail: Tail pointer (written by hardware, pointer to this field is
- * programmed into the DMAREG_(RX|TX)_TAIL_ADDR). Points to the next
- * descriptor to be used for reception or transmission.
- * @tail: Driver tail pointer. Follows hw_tail and points to next descriptor
- * to be completed.
- * @head: Head pointer where the drivers puts the new buffers queued for
- * transmission, and the where fresh RX buffers are added.
- * @size: Size in bytes of the descriptor ring.
- * @phys: Physical address of descriptor ring.
- */
-struct queue_ptr {
- __le32 hw_tail;
- u32 tail;
- u32 head;
- size_t size;
- dma_addr_t phys;
-};
-
-struct dbg_counters {
- unsigned long tx_interrupt;
- unsigned long rx_interrupt;
- unsigned long tx_nodesc;
- unsigned long tx_nobuf;
-};
-
-#define DBG_INC(_priv, _member) (++(_priv)->counters._member)
-
-/* Device private data */
-struct femac_dev {
- struct net_device *ndev;
- struct device *dev;
- struct napi_struct napi;
- void __iomem *base;
- /* MAC address (from parameter, device-tree or randomized) */
- unsigned char mac_addr[ETH_ALEN] __aligned(2);
- /* PHY */
- struct phy_device *phy_dev;
- int link;
- int speed;
- int duplex;
- /* RX/TX ring */
- size_t rx_ring_size;
- dma_addr_t rx_ring_phys;
- unsigned rx_num_desc;
- struct dma_desc *rx_ring;
- struct queue_ptr *rxq;
- /* TX ring */
- size_t tx_ring_size;
- dma_addr_t tx_ring_phys;
- unsigned tx_num_desc;
- struct dma_desc *tx_ring;
- struct queue_ptr *txq;
- /* Lock protecting the TX ring */
- spinlock_t lock;
- /* DMA pool for tx buffers */
- struct dma_pool *tx_pool;
- /* Debug counters */
- struct dbg_counters counters;
-};
-
-#define napi_to_priv(_napi) container_of(napi, struct femac_dev, napi)
-
-/* FEMAC Registers
- */
-
-/* SMII Status */
-#define RXREG_SMII_STATUS 0x0010
-#define RX_SMII_STATUS_SPEED 0x01
-#define RX_SMII_STATUS_DUPLEX 0x02
-#define RX_SMII_STATUS_LINK 0x04
-#define RX_SMII_STATUS_JABBER 0x08
-#define RX_SMII_STATUS_FCD 0x10 /* False Carrier Detect */
-/* Receive Configuration */
-#define RXREG_CONF 0x004c
-#define RX_CONF_ENABLE 0x0001
-#define RX_CONF_PAP 0x0002 /* Pass Any Packet */
-#define RX_CONF_JUMBO9K 0x0008
-#define RX_CONF_STRIPCRC 0x0010
-#define RX_CONF_AMT 0x0020 /* Accept All MAC Types */
-#define RX_CONF_AFC 0x0040 /* Accept Flow Control */
-#define RX_CONF_VLAN 0x0200 /* Enable VLAN */
-#define RX_CONF_SPEED 0x0800 /* RX MAC Speed, 0=10M, 1=100M */
-#define RX_CONF_DUPLEX 0x1000 /* 1=Full-duplex */
-#define RX_CONF_LINK 0x2000 /* 1=Enable */
-#define RX_CONF_RXFCE 0x4000 /* RX flow-control */
-#define RX_CONF_TXFCE 0x8000 /* TX flow-control */
-/* Receive Statistics */
-#define RXREG_STAT_OVERFLOW 0x0278
-#define RXREG_STAT_UNDERSIZE 0x00280
-#define RXREG_STAT_OVERSIZE 0x002b8
-#define RXREG_STAT_PACKET_OK 0x002c0
-#define RXREG_STAT_CRC_ERR 0x002c8
-#define RXREG_STAT_MULTICAST 0x002d0
-#define RXREG_STAT_BROADCAST 0x002d8
-#define RXREG_STAT_MACTYPE 0x002e0
-#define RXREG_STAT_ALIGN_ERR 0x002e8
-#define RXREG_STAT_BYTES_LO 0x002f0
-#define RXREG_STAT_BYTES_HI 0x002f8
-/* Receive Ethernet Mode */
-#define RXREG_MODE 0x0800
-#define RX_MODE_ETHERNET_ENABLE 0x01
-/* Receive Soft Reset */
-#define RXREG_SOFT_RESET 0x0808
-#define RX_SOFT_RESET_MAC_0 0x01
-/* Transmit Watermark */
-#define TXREG_WATERMARK 0x1018
-#define TX_WATERMARK_DTPA_ASSERT 0x8000
-#define TX_WATERMARK_DTPA_DISABLE 0x4000
-#define TX_WATERMARK_DTPA_HIGH(_x) (((_x) & 0x7f) << 7)
-#define TX_WATERMARK_DTPA_LOW(_x) (((_x) & 0x7f) << 0)
-/* Swap Source Address Registers */
-#define TXREG_SOURCE_ADDRESS_2 0x1020
-#define TXREG_SOURCE_ADDRESS_1 0x1024
-#define TXREG_SOURCE_ADDRESS_0 0x1028
-/* Transmit Extended Configuration */
-#define TXREG_EXTENDED_CONF 0x1030
-#define TX_EXTCONF_TXCOLL_WATERMARK 0xe000
-#define TX_EXTCONF_EX_DEFFERED_DROP 0x0200
-#define TX_EXTCONF_JUMBO9K 0x0100
-#define TX_EXTCONF_LATE_COLL_WDW 0x00ff
-/* Transmit Configuration */
-#define TXREG_CONF 0x1050
-#define TX_CONF_ENABLE_SWAP_SA 0x8000
-#define TX_CONF_LINK 0x2000
-#define TX_CONF_DUPLEX 0x1000
-#define TX_CONF_SPEED 0x0800
-#define TX_CONF_XBK_RST_RX_NTX 0x0600
-#define TX_CONF_IFG_MASK 0x01f0
-#define TX_CONF_IFG(_x) (((_x) & 0x1f) << 4)
-#define TX_CONF_APP_CRC_ENABLE 0x0004
-#define TX_CONF_PAD_ENABLE 0x0002
-#define TX_CONF_ENABLE 0x0001
-/* Transmit Statistics */
-#define TXREG_STAT_UNDERRUN 0x1300
-#define TXREG_STAT_DEFERRED 0x1308
-#define TXREG_STAT_PAUSE 0x1310
-#define TXREG_STAT_PACKET_OK 0x1318
-#define TXREG_STAT_UNDERSIZE 0x1350
-#define TXREG_STAT_BYTES_LO 0x1358
-#define TXREG_STAT_BYTES_HI 0x1360
-#define TXREG_STAT_LATECOLL 0x1368
-#define TXREG_STAT_EXCECOLL 0x1370
-#define TXREG_STAT_EXCEDEFERRED 0x1378
-#define TXREG_STAT_COLLISION_LIMIT 0x1380
-/* Transmit Mode */
-#define TXREG_MODE 0x1800
-#define TX_MODE_ETHERNET_ENABLE 0x01
-/* Transmit Soft Reset */
-#define TXREG_SOFT_RESET 0x1808
-#define TX_SOFT_RESET_MAC_0 0x01
-/* DMA Control */
-#define DMAREG_CONTROL 0x2000
-#define DMA_CTRL_RST 0x80000000
-#define DMA_CTRL_ERR_CLEAR 0x40000000
-#define DMA_CTRL_ENABLE 0x00010000
-/* DMA Enable */
-#define DMAREG_ENABLE 0x2008
-#define DMA_TX_ENABLE 0x00010000
-#define DMA_RX_ENABLE 0x00020000
-/* DMA Interrupt Enable/Status */
-#define DMAREG_INT_STATUS 0x2018
-#define DMAREG_INT_ENABLE 0x201c
-#define DMA_INT_TX 0x01
-#define DMA_INT_RX 0x02
-/* DMA RX/TX Queue Base/Size */
-#define DMAREG_RX_QUEUE_BASE 0x2030
-#define DMAREG_RX_QUEUE_SIZE 0x2034
-#define DMAREG_TX_QUEUE_BASE 0x2038
-#define DMAREG_TX_QUEUE_SIZE 0x203c
-/* DMA Tail Pointer Address */
-#define DMAREG_RX_TAIL_ADDR 0x2048
-#define DMAREG_TX_TAIL_ADDR 0x204c
-/* DMA Head/Tail Pointers */
-#define DMAREG_RX_HEAD 0x2050
-#define DMAREG_RX_TAIL 0x2054
-#define DMAREG_TX_HEAD 0x2058
-#define DMAREG_TX_TAIL 0x205c
-#define DMA_POINTER_GEN 0x100000
-#define DMA_POINTER_MASK 0x0fffff
-#define DMAREG_DTPA_LOW 0x2060
-#define DTPA_LOW_MARK(_x) (((_x) & 0x1ff) << 0)
-#define DTPA_BP_COUNT(_x) (((_x) & 0xff) << 16)
-#define DMAREG_DTPA_HIGH 0x2064
-#define DTPA_HIGH_MARK(_x) (((_x) & 0x1ff) << 0)
-#define DTPA_START_THRES(_x) (((_x) & 0x3) << 16)
-
-#define dmaptr_idx(_val) (((_val) & DMA_POINTER_MASK) / sizeof(struct dma_desc))
-#define dmaptr_gen(_val) (!!((_val) & DMA_POINTER_GEN))
-
-/* RX/TX-ring
- *
- * tail - Oldest descriptor, i.e. the next descriptor to be processed by RX/TX
- * interrupt. This pointer is only used by the driver (no corresponding
- * hardware register). The interrupt handler will process descriptors from tail
- * to hw_tail.
- *
- * hw_tail - Next descriptor to be processed by hardware. The memory location
- * is updated by the hardware when it switches descriptor (via DMA). A copy of
- * this value is also available in the DMAREG_[RX|TX]_TAIL register.
- *
- * head - Newest descriptor. This is where the driver adds new descriptors
- * (either fresh rx buffers or tx buffers queued for transmission) and the
- * pointer is updated in hardware via the DMAREG_[RX|TX]_HEAD register. The
- * hardware will process descriptors from hw_tail to head. When hw_tail ==
- * head, the ring is empty.
- *
- * tail hw_tail head
- * | | |
- * V V V
- * +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
- * | | | | | | | | | | | |
- * +-----+ +-----+ +-----+ +-----+ +-----+ +-----+
- *
- */
-
-/**
- * queue_get_head - Return next DMA descriptor from head of queue.
- */
-static inline struct dma_desc *
-queue_get_head(struct dma_desc *ring, const struct queue_ptr *q)
-{
- if ((q->head ^ q->tail) == DMA_POINTER_GEN)
- return NULL;
- return &ring[dmaptr_idx(q->head)];
-}
-
-/**
- * queue_get_tail - Return next DMA descriptor from tail of queue.
- */
-static inline struct dma_desc *
-queue_get_tail(struct dma_desc *ring, const struct queue_ptr *q)
-{
- if (q->tail == le32_to_cpu(q->hw_tail))
- return NULL;
- return &ring[dmaptr_idx(q->tail)];
-}
-
-/**
- * inc_pointer - Helper function to increment a DMA pointer. The counter is in
- * the lower bits and is incremented modulo the size of the ring. The bit
- * DMA_POINTER_GEN is toggled when the counter wraps.
- */
-static inline u32
-inc_pointer(u32 ptr, u32 size)
-{
- u32 newptr = (ptr & DMA_POINTER_MASK) + sizeof(struct dma_desc);
-
- /* When counter wraps (on size), reset and toggle generation bit.
- * Otherwise preserve generation bit
- */
- if (newptr >= size)
- newptr = (ptr & DMA_POINTER_GEN) ^ DMA_POINTER_GEN;
- else
- newptr |= ptr & DMA_POINTER_GEN;
-
- return newptr;
-}
-
-static inline u32
-queue_inc_head(struct queue_ptr *q)
-{
- q->head = inc_pointer(q->head, q->size);
- return q->head;
-}
-
-static inline u32
-queue_inc_tail(struct queue_ptr *q)
-{
- q->tail = inc_pointer(q->tail, q->size);
- return q->tail;
-}
-
-static inline void
-pr_queue(const char *tag, const struct queue_ptr *q)
-{
- pr_debug("%s tail=%d.%d hw_tail=%d.%d head=%d.%d\n",
- tag,
- dmaptr_gen(q->tail), dmaptr_idx(q->tail),
- dmaptr_gen(q->hw_tail), dmaptr_idx(q->hw_tail),
- dmaptr_gen(q->head), dmaptr_idx(q->head));
-}
-
-/**
- * clear_statistics - Counters are cleared on read.
- */
-static void
-clear_statistics(const struct femac_dev *priv)
-{
- int waste;
-
- waste = readl(priv->base + RXREG_STAT_OVERFLOW);
- waste = readl(priv->base + RXREG_STAT_UNDERSIZE);
- waste = readl(priv->base + RXREG_STAT_OVERSIZE);
- waste = readl(priv->base + RXREG_STAT_PACKET_OK);
- waste = readl(priv->base + RXREG_STAT_CRC_ERR);
- waste = readl(priv->base + RXREG_STAT_MULTICAST);
- waste = readl(priv->base + RXREG_STAT_BROADCAST);
- waste = readl(priv->base + RXREG_STAT_MACTYPE);
- waste = readl(priv->base + RXREG_STAT_ALIGN_ERR);
- waste = readl(priv->base + RXREG_STAT_BYTES_LO);
- waste = readl(priv->base + RXREG_STAT_BYTES_HI);
-
- waste = readl(priv->base + TXREG_STAT_UNDERRUN);
- waste = readl(priv->base + TXREG_STAT_DEFERRED);
- waste = readl(priv->base + TXREG_STAT_PAUSE);
- waste = readl(priv->base + TXREG_STAT_PACKET_OK);
- waste = readl(priv->base + TXREG_STAT_UNDERSIZE);
- waste = readl(priv->base + TXREG_STAT_BYTES_LO);
- waste = readl(priv->base + TXREG_STAT_BYTES_HI);
- waste = readl(priv->base + TXREG_STAT_LATECOLL);
- waste = readl(priv->base + TXREG_STAT_EXCECOLL);
- waste = readl(priv->base + TXREG_STAT_EXCEDEFERRED);
- waste = readl(priv->base + TXREG_STAT_COLLISION_LIMIT);
-}
-
-static int
-enable_rx_tx(struct net_device *device)
-{
- struct femac_dev *priv = netdev_priv(device);
- unsigned long rxcfg;
- unsigned long txcfg;
-
- rxcfg = (RX_CONF_STRIPCRC |
- RX_CONF_RXFCE |
- RX_CONF_TXFCE);
-
- txcfg = (TX_CONF_ENABLE_SWAP_SA |
- TX_CONF_APP_CRC_ENABLE |
- TX_CONF_IFG(0xf) |
- TX_CONF_PAD_ENABLE);
-
- /* Setup the receive and transmit configuration registers according to
- * status from PHY.
- */
- if (priv->phy_dev->speed == SPEED_100) {
- rxcfg |= RX_CONF_SPEED;
- txcfg |= TX_CONF_SPEED;
- }
- if (priv->phy_dev->duplex == DUPLEX_FULL) {
- rxcfg |= RX_CONF_DUPLEX;
- txcfg |= TX_CONF_DUPLEX;
- }
- if (priv->phy_dev->link) {
- rxcfg |= (RX_CONF_ENABLE | RX_CONF_LINK);
- txcfg |= (TX_CONF_LINK | TX_CONF_ENABLE);
- }
-
- writel(rxcfg, priv->base + RXREG_CONF);
- writel(txcfg, priv->base + TXREG_CONF);
-
- if (priv->phy_dev->link) {
- netif_start_queue(device);
- netif_carrier_on(device);
- } else {
- netif_carrier_off(device);
- netif_stop_queue(device);
- }
-
- return 0;
-}
-
-static void
-disable_rx_tx(struct femac_dev *priv)
-{
- unsigned long txcfg;
- unsigned long rxcfg;
-
- rxcfg = readl(priv->base + RXREG_CONF);
- rxcfg &= ~RX_CONF_ENABLE;
- writel(rxcfg, priv->base + RXREG_CONF);
-
- txcfg = readl(priv->base + TXREG_CONF);
- txcfg &= ~TX_CONF_ENABLE;
- writel(txcfg, priv->base + TXREG_CONF);
-}
-
-static ssize_t
-femac_show_counters(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct net_device *ndev = container_of(dev, struct net_device, dev);
- struct femac_dev *priv = netdev_priv(ndev);
- ssize_t n = 0;
-
- n += snprintf(&buf[n], PAGE_SIZE,
- "tx_interrupt: %lu\n"
- "rx_interrupt: %lu\n"
- "tx_nodesc: %lu\n"
- "tx_nobuf: %lu\n",
- priv->counters.tx_interrupt,
- priv->counters.rx_interrupt,
- priv->counters.tx_nodesc,
- priv->counters.tx_nobuf);
-
- n += snprintf(&buf[n], PAGE_SIZE,
- "rx_queue: %u.%u / %u.%u / %u.%u\n",
- dmaptr_gen(priv->rxq->tail),
- dmaptr_idx(priv->rxq->tail),
- dmaptr_gen(le32_to_cpu(priv->rxq->hw_tail)),
- dmaptr_idx(le32_to_cpu(priv->rxq->hw_tail)),
- dmaptr_gen(priv->rxq->head),
- dmaptr_idx(priv->rxq->head));
-
- n += snprintf(&buf[n], PAGE_SIZE,
- "tx_queue: %u.%u / %u.%u / %u.%u\n",
- dmaptr_gen(priv->txq->tail),
- dmaptr_idx(priv->txq->tail),
- dmaptr_gen(le32_to_cpu(priv->txq->hw_tail)),
- dmaptr_idx(le32_to_cpu(priv->txq->hw_tail)),
- dmaptr_gen(priv->txq->head),
- dmaptr_idx(priv->txq->head));
-
- return n;
-}
-static DEVICE_ATTR(counters, S_IRUSR, femac_show_counters, NULL);
-
-static void
-set_macaddr(struct femac_dev *priv, u8 *addr)
-{
- writel((addr[4] << 8) | addr[5], priv->base + TXREG_SOURCE_ADDRESS_2);
- writel((addr[2] << 8) | addr[3], priv->base + TXREG_SOURCE_ADDRESS_1);
- writel((addr[0] << 8) | addr[1], priv->base + TXREG_SOURCE_ADDRESS_0);
-}
-
-/**
- * femac_adjust_link - Called by the PHY driver to update MAC with changes in
- * link state.
- */
-static void
-femac_adjust_link(struct net_device *ndev)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- struct phy_device *phy_dev = priv->phy_dev;
- int status_change = 0;
-
- /* Link on or off change */
- if (phy_dev->link != priv->link) {
- priv->link = phy_dev->link;
- if (phy_dev->link)
- enable_rx_tx(ndev);
- else
- disable_rx_tx(priv);
- status_change = 1;
- }
-
- if (status_change)
- phy_print_status(phy_dev);
-}
-
-/**
- * alloc_rx_buf - Initialize a dma descritor with a new rx buffer.
- */
-static int
-alloc_rx_buf(struct femac_dev *priv, struct dma_desc *d)
-{
- struct sk_buff *skb;
- dma_addr_t dma_addr;
-
- skb = netdev_alloc_skb(priv->ndev, MAX_FRAME_SIZE);
- if (!skb)
- return -ENOMEM;
- dma_addr = dma_map_single(priv->dev, skb->data,
- MAX_FRAME_SIZE, DMA_FROM_DEVICE);
- if (dma_mapping_error(priv->dev, dma_addr)) {
- dev_kfree_skb_any(skb);
- return -ENOMEM;
- }
- d->flags = cpu_to_le32(DMADESC_WRITE | DMADESC_INTR);
- d->buf_len = cpu_to_le16(MAX_FRAME_SIZE);
- d->pdu_len = d->buf_len;
- d->buf_ptr = cpu_to_le32((u32)dma_addr);
- d->cookie = (u32)skb;
- return 0;
-}
-
-static void
-femac_tx_complete(struct femac_dev *priv)
-{
- struct dma_desc *desc;
- int complete = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&priv->lock, flags);
- while ((desc = queue_get_tail(priv->tx_ring, priv->txq)) != NULL) {
- void *buf = (void *)desc->cookie;
-
- dma_pool_free(priv->tx_pool, buf, le32_to_cpu(desc->buf_ptr));
- queue_inc_tail(priv->txq);
- pr_queue("femac: TX complete", priv->txq);
- ++complete;
- }
- spin_unlock_irqrestore(&priv->lock, flags);
-
- if (complete)
- netif_wake_queue(priv->ndev);
-}
-
-static inline unsigned compare_ether_addr(const u8 *addr1, const u8 *addr2)
-{
- const u16 *a = (const u16 *)addr1;
- const u16 *b = (const u16 *)addr2;
-
- BUILD_BUG_ON(ETH_ALEN != 6);
- return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
-}
-
-static int
-femac_rx_packets(struct femac_dev *priv, int max)
-{
- struct sk_buff *skb;
- struct dma_desc *desc;
- int num_rx = 0;
-
- while (num_rx < max) {
- desc = queue_get_tail(priv->rx_ring, priv->rxq);
- if (!desc)
- break;
- queue_inc_tail(priv->rxq);
-
- pr_debug(DRVNAME " (RX) desc=%p flags=%#x len=%u/%u buf=%#x cookie=%#x\n",
- desc,
- le32_to_cpu(desc->flags),
- le16_to_cpu(desc->buf_len),
- le16_to_cpu(desc->pdu_len),
- le32_to_cpu(desc->buf_ptr),
- desc->cookie);
-
- dma_unmap_single(priv->dev, le32_to_cpu(desc->buf_ptr),
- MAX_FRAME_SIZE, DMA_FROM_DEVICE);
- skb = (struct sk_buff *)desc->cookie;
-
- if (!(le32_to_cpu(desc->flags) & DMADESC_ERROR)) {
- struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
-
- if (is_multicast_ether_addr(ðhdr->h_dest[0]) ||
- is_broadcast_ether_addr(ðhdr->h_dest[0]) ||
- compare_ether_addr(ðhdr->h_dest[0],
- &priv->mac_addr[0]) == 0) {
- /* No error, pass sk_buff to upper layer */
- skb_put(skb, le16_to_cpu(desc->buf_len));
- skb->protocol = eth_type_trans(skb, priv->ndev);
- netif_receive_skb(skb);
- } else {
- dev_kfree_skb_any(skb);
- }
- } else {
- struct net_device_stats *s = &priv->ndev->stats;
-
- /* Error, free skb and update counters */
- dev_kfree_skb_any(skb);
-
- s->rx_fifo_errors +=
- readl(priv->base + RXREG_STAT_OVERFLOW);
- s->rx_crc_errors +=
- readl(priv->base + RXREG_STAT_CRC_ERR);
- s->rx_frame_errors +=
- readl(priv->base + RXREG_STAT_ALIGN_ERR);
- }
-
- /* Add new RX buffers */
- desc = queue_get_head(priv->rx_ring, priv->rxq);
- if (alloc_rx_buf(priv, desc)) {
- dev_warn(priv->dev, "femac: Failed to alloc RX buffer\n");
- break;
- }
- /* Writes to desc must complete before head is advanced */
- wmb();
- writel(queue_inc_head(priv->rxq), priv->base + DMAREG_RX_HEAD);
-
- ++num_rx;
- }
-
- return num_rx;
-}
-
-static irqreturn_t
-femac_isr(int irq, void *device_id)
-{
- struct femac_dev *priv = (struct femac_dev *)device_id;
- u32 int_status, int_enable;
-
- /* Read interrupt status */
- int_status = readl(priv->base + DMAREG_INT_STATUS);
- int_enable = readl(priv->base + DMAREG_INT_ENABLE);
- int_status &= int_enable;
-
- if ((int_status & (DMA_INT_TX | DMA_INT_RX)) == 0)
- return IRQ_NONE;
-
- if (int_status & DMA_INT_TX)
- DBG_INC(priv, tx_interrupt);
- else
- DBG_INC(priv, rx_interrupt);
-
- if (napi_schedule_prep(&priv->napi)) {
- /* Disable interrupts */
- writel(0, priv->base + DMAREG_INT_ENABLE);
- __napi_schedule(&priv->napi);
- } else {
- /* Clear interrupt status */
- writel(0, priv->base + DMAREG_INT_STATUS);
- }
-
- return IRQ_HANDLED;
-}
-
-static int
-femac_poll(struct napi_struct *napi, int budget)
-{
- struct femac_dev *priv = napi_to_priv(napi);
- int rcvd;
-
- /* Clear interrupt status */
- writel(0, priv->base + DMAREG_INT_STATUS);
-
- WARN_ON(priv->rxq->head != readl(priv->base + DMAREG_RX_HEAD));
-
- /* Clean TX ring */
- femac_tx_complete(priv);
-
- /* Process rx_ring */
- rcvd = femac_rx_packets(priv, budget);
- if (rcvd < budget) {
- /* Exit polling mode */
- napi_complete(napi);
- /* Re-enable receive interrupts */
- writel(DMA_INT_RX | DMA_INT_TX, priv->base + DMAREG_INT_ENABLE);
- }
-
- return rcvd;
-}
-
-/**
- * femac_open - Opens the interface.
- *
- * The interface is opened whenever ifconfig activates it. The open method
- * should register any system resource it needs (I/O ports, IRQ, DMA, etc.)
- * turn on the hardware, and increment the module usage count.
- */
-static int
-femac_open(struct net_device *ndev)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- struct device_node *phy_np;
- struct phy_device *phy_dev;
- int rc;
-
- phy_np = of_parse_phandle(priv->dev->of_node, "phy-handle", 0);
- if (!phy_np) {
- dev_err(&ndev->dev, "Missing phy-handle\n");
- return -ENODEV;
- }
-
- phy_dev = of_phy_connect(ndev, phy_np, femac_adjust_link,
- 0, PHY_INTERFACE_MODE_MII);
- if (IS_ERR_OR_NULL(phy_dev)) {
- dev_err(&ndev->dev, "Could not attach to PHY\n");
- return -ENODEV;
- }
-
- phy_dev->advertising = phy_dev->supported;
- priv->link = 0;
- priv->phy_dev = phy_dev;
-
- phy_attached_info(phy_dev);
-
- napi_enable(&priv->napi);
-
- /* Install the interrupt handlers. */
- rc = request_irq(ndev->irq, femac_isr, 0, DRVNAME, priv);
- if (rc) {
- dev_err(&ndev->dev, "Request IRQ%d failed\n", ndev->irq);
- return rc;
- }
-
- /* enable interrupts */
- writel(DMA_INT_RX | DMA_INT_TX, priv->base + DMAREG_INT_ENABLE);
-
- phy_start(priv->phy_dev);
-
- return 0;
-}
-
-/**
- * femac_stop - Stops the interface.
- *
- * The interface is stopped when it is brought down; operations performed at
- * open time should be reversed.
- */
-static int
-femac_stop(struct net_device *device)
-{
- struct femac_dev *priv = netdev_priv(device);
-
- /* Indicate to the OS that no more packets should be sent. */
- netif_stop_queue(device);
-
- phy_disconnect(priv->phy_dev);
-
- /* Stop the receiver and transmitter. */
- disable_rx_tx(priv);
-
- /* Disable NAPI. */
- napi_disable(&priv->napi);
-
- /* Free the interrupts. */
- free_irq(device->irq, priv);
-
- return 0;
-}
-
-/**
- * femac_hard_start_xmit - The method initiates the transmission of a packet.
- *
- * The full packet (protocol headers and all) is contained in a socket buffer
- * (sk_buff) structure.
- *
- * Since the FEMAC DMA engine needs 32-bit aligned data, we allocate a new TX
- * buffer from a dma_pool and copy the payload before passing it on to the
- * hardware.
- */
-static int
-femac_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- struct dma_desc *desc;
- void *tx_buf;
- dma_addr_t dma_addr;
- unsigned long flags;
- int length;
-
- if (skb->len > MAX_FRAME_SIZE) {
- ++ndev->stats.tx_dropped;
- goto drop;
- }
-
- tx_buf = dma_pool_alloc(priv->tx_pool, GFP_ATOMIC, &dma_addr);
- if (!tx_buf) {
- netif_stop_queue(ndev);
- DBG_INC(priv, tx_nobuf);
- ++ndev->stats.tx_dropped;
- dev_err_ratelimited(&ndev->dev, "No TX buffers!\n");
- goto drop;
- }
-
- length = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
- skb_copy_from_linear_data(skb, tx_buf, length);
-
- spin_lock_irqsave(&priv->lock, flags);
-
- desc = queue_get_head(priv->tx_ring, priv->txq);
- pr_debug(DRVNAME " (TX) desc=%p len=%u\n", desc, length);
- if (!desc) {
- DBG_INC(priv, tx_nodesc);
- spin_unlock_irqrestore(&priv->lock, flags);
- dma_pool_free(priv->tx_pool, tx_buf, dma_addr);
- netif_stop_queue(ndev);
- dev_kfree_skb_any(skb);
- ++ndev->stats.tx_dropped;
- dev_err_ratelimited(&ndev->dev, "No TX descriptors!\n");
- return NETDEV_TX_BUSY;
- }
-
- desc->flags = cpu_to_le32(DMADESC_WRITE | DMADESC_INTR |
- DMADESC_SOP | DMADESC_EOP);
- desc->buf_ptr = cpu_to_le32((u32)dma_addr);
- desc->buf_len = cpu_to_le16(length);
- desc->pdu_len = desc->buf_len;
- desc->cookie = (u32)tx_buf;
- /* Make sure writes to descriptor completed before starting TX */
- wmb();
- WARN_ON(priv->txq->head != readl(priv->base + DMAREG_TX_HEAD));
- writel(queue_inc_head(priv->txq), priv->base + DMAREG_TX_HEAD);
- netif_trans_update(ndev); /* prevent tx timeout */
- pr_queue("XMIT", priv->txq);
-
- spin_unlock_irqrestore(&priv->lock, flags);
-
-drop:
- dev_kfree_skb_any(skb);
- return NETDEV_TX_OK;
-}
-
-/**
- * femac_get_stats - Read hardware counters.
- *
- * Whenever an application needs to get statistics for the interface, this
- * method is called.
- */
-static struct net_device_stats *
-femac_get_stats(struct net_device *ndev)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- struct net_device_stats *s = &ndev->stats;
-
- s->rx_packets += readl(priv->base + RXREG_STAT_PACKET_OK);
- s->tx_packets += readl(priv->base + TXREG_STAT_PACKET_OK);
- s->rx_bytes += readl(priv->base + RXREG_STAT_BYTES_LO);
- s->tx_bytes += readl(priv->base + TXREG_STAT_BYTES_LO);
- s->multicast += readl(priv->base + RXREG_STAT_MULTICAST);
- s->multicast += readl(priv->base + RXREG_STAT_BROADCAST);
- s->collisions += readl(priv->base + TXREG_STAT_LATECOLL);
- s->collisions += readl(priv->base + TXREG_STAT_EXCECOLL);
- s->rx_length_errors += readl(priv->base + RXREG_STAT_UNDERSIZE);
- s->rx_length_errors += readl(priv->base + RXREG_STAT_OVERSIZE);
- s->tx_fifo_errors += readl(priv->base + TXREG_STAT_UNDERRUN);
-
- s->rx_errors =
- (s->rx_length_errors +
- s->rx_fifo_errors +
- s->rx_crc_errors +
- s->rx_frame_errors);
-
- s->tx_errors =
- (s->tx_fifo_errors +
- s->tx_aborted_errors);
-
- return &ndev->stats;
-}
-
-/**
- * femac_set_mac_address
- */
-static int
-femac_set_mac_address(struct net_device *ndev, void *data)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- struct sockaddr *addr = data;
-
- if (netif_running(ndev))
- return -EBUSY;
-
- memcpy(ndev->dev_addr, addr->sa_data, ndev->addr_len);
- set_macaddr(priv, addr->sa_data);
-
- return 0;
-}
-
-static const struct net_device_ops femac_netdev_ops = {
- .ndo_open = femac_open,
- .ndo_stop = femac_stop,
- .ndo_get_stats = femac_get_stats,
- .ndo_set_mac_address = femac_set_mac_address,
- .ndo_start_xmit = femac_hard_start_xmit,
-};
-
-/* Ethtool operations */
-
-static int
-femac_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
-{
- struct femac_dev *priv = netdev_priv(ndev);
-
- if (!priv->phy_dev)
- return -ENODEV;
-
- return phy_ethtool_gset(priv->phy_dev, cmd);
-}
-
-static int
-femac_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
-{
- struct femac_dev *priv = netdev_priv(ndev);
-
- if (!priv->phy_dev)
- return -ENODEV;
-
- return phy_ethtool_sset(priv->phy_dev, cmd);
-}
-
-static const struct ethtool_ops femac_ethtool_ops = {
- .get_settings = femac_get_settings,
- .set_settings = femac_set_settings
-};
-
-/**
- * femac_init - Allocate memory and initialize the hardware
- */
-static int
-femac_init(struct net_device *ndev)
-{
- struct femac_dev *priv = netdev_priv(ndev);
- dma_addr_t dma_phys;
- int i;
-
- /* Reset the MAC */
- writel(0x80000000, priv->base + DMAREG_CONTROL);
-
- /* The number of rx and tx descriptors must be an even multiple of
- * DESCRIPTOR_GRANULARITY.
- */
- priv->rx_num_desc = ALIGN(rx_num_desc, DESCRIPTOR_GRANULARITY);
- priv->tx_num_desc = ALIGN(tx_num_desc, DESCRIPTOR_GRANULARITY);
-
- /* This needs to be set to something sane for dma_alloc_coherent() */
- ndev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
- ndev->dev.dma_mask = &ndev->dev.coherent_dma_mask;
-
- priv->tx_pool = dma_pool_create("femac_dma", priv->dev,
- MAX_FRAME_SIZE, 4, 0);
- if (!priv->tx_pool) {
- dev_err(&ndev->dev, "Failed to allocate DMA buffer pool\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&priv->lock);
-
- /* Take MAC out of reset */
- writel(0x0, priv->base + RXREG_SOFT_RESET);
- writel(RX_MODE_ETHERNET_ENABLE, priv->base + RXREG_MODE);
- writel(0x0, priv->base + TXREG_SOFT_RESET);
- writel(TX_MODE_ETHERNET_ENABLE, priv->base + TXREG_MODE);
- writel(TX_WATERMARK_DTPA_HIGH(96) | TX_WATERMARK_DTPA_LOW(10),
- priv->base + TXREG_WATERMARK);
-
- writel(DMA_CTRL_ERR_CLEAR | DMA_CTRL_ENABLE,
- priv->base + DMAREG_CONTROL);
- writel(DMA_TX_ENABLE | DMA_RX_ENABLE, priv->base + DMAREG_ENABLE);
- writel(DTPA_BP_COUNT(0x28) | DTPA_LOW_MARK(0x44),
- priv->base + DMAREG_DTPA_LOW);
- writel(DTPA_HIGH_MARK(0xc0) | DTPA_START_THRES(0),
- priv->base + DMAREG_DTPA_HIGH);
-
- set_macaddr(priv, ndev->dev_addr);
-
- /* Initialize RX queue */
- priv->rx_ring_size = priv->rx_num_desc * sizeof(struct dma_desc);
- priv->rx_ring = dma_alloc_coherent(&ndev->dev, priv->rx_ring_size,
- &priv->rx_ring_phys, GFP_KERNEL);
- priv->rxq = dma_alloc_coherent(&ndev->dev, 2 * sizeof(struct queue_ptr),
- &dma_phys, GFP_KERNEL);
- priv->rxq->phys = dma_phys;
- writel(priv->rx_ring_phys, priv->base + DMAREG_RX_QUEUE_BASE);
- writel(priv->rx_ring_size / 1024, priv->base + DMAREG_RX_QUEUE_SIZE);
- writel(priv->rxq->phys, priv->base + DMAREG_RX_TAIL_ADDR);
- priv->rxq->size = priv->rx_ring_size;
- priv->rxq->hw_tail = readl(priv->base + DMAREG_RX_TAIL);
- priv->rxq->tail = priv->rxq->hw_tail;
- priv->rxq->head = priv->rxq->hw_tail;
- writel(priv->rxq->head, priv->base + DMAREG_RX_HEAD);
- pr_debug("femac: rx_ring %p rxq %p\n", priv->rx_ring, priv->rxq);
-
- /* Initialize the descriptors */
- for (i = 0; i < priv->rx_num_desc - 1; ++i) {
- struct dma_desc *desc = &priv->rx_ring[i];
-
- alloc_rx_buf(priv, desc);
- writel(queue_inc_head(priv->rxq), priv->base + DMAREG_RX_HEAD);
- }
-
- /* Initialize TX queue */
- priv->tx_ring_size = priv->tx_num_desc * sizeof(struct dma_desc);
- priv->tx_ring = dma_alloc_coherent(&ndev->dev, priv->tx_ring_size,
- &priv->tx_ring_phys, GFP_KERNEL);
- priv->txq = &priv->rxq[1];
- priv->txq->phys = dma_phys + sizeof(struct queue_ptr);
- writel(priv->tx_ring_phys, priv->base + DMAREG_TX_QUEUE_BASE);
- writel(priv->tx_ring_size / 1024, priv->base + DMAREG_TX_QUEUE_SIZE);
- writel(priv->txq->phys, priv->base + DMAREG_TX_TAIL_ADDR);
- priv->txq->size = priv->tx_ring_size;
- priv->txq->hw_tail = readl(priv->base + DMAREG_RX_TAIL);
- priv->txq->head = priv->txq->hw_tail;
- priv->txq->tail = priv->txq->hw_tail;
- writel(priv->txq->head, priv->base + DMAREG_TX_HEAD);
- pr_debug("femac: tx_ring %p txq %p\n", priv->tx_ring, priv->txq);
-
- /* Clear statistics */
- clear_statistics(priv);
-
- ether_setup(ndev);
- ndev->netdev_ops = &femac_netdev_ops;
- ndev->ethtool_ops = &femac_ethtool_ops;
- ndev->watchdog_timeo = msecs_to_jiffies(1000);
- memset(&priv->napi, 0, sizeof(struct napi_struct));
- netif_napi_add(ndev, &priv->napi, femac_poll, NAPI_WEIGHT);
-
- return 0;
-}
-
-/**
- * femac_probe - Create, initialize and register the net_device.
- */
-static int
-femac_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct net_device *ndev = NULL;
- struct femac_dev *priv = NULL;
- struct resource *res;
- const u32 *field;
- int length;
- int rc = 0;
-
- /* Allocate space for the net_device. */
- ndev = alloc_etherdev(sizeof(*priv));
- if (!ndev) {
- rc = -ENOMEM;
- goto out;
- }
-
- priv = netdev_priv(ndev);
- priv->ndev = ndev;
- priv->dev = &pdev->dev;
- strcpy(ndev->name, "eth%d");
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- priv->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(priv->base)) {
- rc = PTR_ERR(priv->base);
- goto out;
- }
-
- ndev->irq = platform_get_irq(pdev, 0);
- if (ndev->irq < 0) {
- rc = ndev->irq;
- goto out;
- }
-
- /* Check for mac address property in device-tree */
- field = of_get_property(np, "mac-address", &length);
- if (!field)
- field = of_get_property(np, "local-mac-address", &length);
- if (field && length == ETH_ALEN)
- ether_addr_copy(priv->mac_addr, (const u8 *)field);
- /* MAC address may be overridden via module/cmdline parameter */
- if (is_valid_ether_addr(macaddr))
- ether_addr_copy(priv->mac_addr, macaddr);
- /* Still no valid MAC address, randomize it */
- if (!is_valid_ether_addr(priv->mac_addr))
- random_ether_addr(priv->mac_addr);
-
- ether_addr_copy(ndev->dev_addr, &priv->mac_addr[0]);
- ether_addr_copy(ndev->perm_addr, &priv->mac_addr[0]);
- ndev->addr_len = ETH_ALEN;
-
- /* Initialize the device. */
- rc = femac_init(ndev);
- if (rc != 0)
- goto out;
-
- /* Register the device. */
- rc = register_netdev(ndev);
- if (rc != 0)
- goto out;
-
- device_create_file(&ndev->dev, &dev_attr_counters);
-
- netif_carrier_off(ndev);
-
-out:
- if (rc) {
- dev_err(&pdev->dev, "Failed to initialize, error %d\n", rc);
- if (ndev)
- free_netdev(ndev);
- }
- return rc;
-}
-
-/**
- * femac_remove - Handle device removal.
- */
-static int
-femac_remove(struct platform_device *pdev)
-{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct femac_dev *priv = netdev_priv(ndev);
-
- device_remove_file(&ndev->dev, &dev_attr_counters);
- dma_pool_destroy(priv->tx_pool);
- dma_free_coherent(&ndev->dev,
- priv->rx_ring_size,
- priv->rx_ring,
- priv->rx_ring_phys);
- dma_free_coherent(&ndev->dev,
- priv->tx_ring_size,
- priv->tx_ring,
- priv->tx_ring_phys);
- dma_free_coherent(&ndev->dev,
- 2 * sizeof(struct queue_ptr),
- priv->rxq,
- priv->rxq->phys);
- unregister_netdev(ndev);
- free_netdev(ndev);
- platform_set_drvdata(pdev, NULL);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int
-femac_suspend(struct device *dev)
-{
- return -ENOSYS;
-}
-
-static int
-femac_resume(struct device *dev)
-{
- return -ENOSYS;
-}
-
-static const struct dev_pm_ops femac_pm_ops = {
- .suspend = femac_suspend,
- .resume = femac_resume,
-};
-#endif
-
-static const struct of_device_id femac_id_table[] = {
- { .compatible = "lsi,femac" },
- { }
-};
-MODULE_DEVICE_TABLE(of, femac_id_table);
-
-static struct platform_driver femac_driver = {
- .driver = {
- .name = DRVNAME,
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &femac_pm_ops,
-#endif
- .of_match_table = femac_id_table,
- },
- .probe = femac_probe,
- .remove = femac_remove,
-};
-
-module_platform_driver(femac_driver);
-
-MODULE_AUTHOR("LSI Corporation");
-MODULE_DESCRIPTION("LSI FEMAC Ethernet Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ethernet/lsi/lsi-mdio.c b/drivers/net/ethernet/lsi/lsi-mdio.c
deleted file mode 100644
index 56decf6..0000000
--- a/drivers/net/ethernet/lsi/lsi-mdio.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Driver for MDIO controller found in LSI Axxia devices.
- *
- * Copyright (C) 2013 LSI Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_mdio.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-
-/* MDIO Registers */
-#define MDIO_CONTROL 0x00
-#define CONTROL_BUSY (1<<31) /* MDIO cycle in progress (RO) */
-#define CONTROL_NOPRE (1<<30) /* Suppress preamble */
-#define CONTROL_CL22 (0<<29) /* Clause-22 */
-#define CONTROL_CL45 (1<<29) /* Clause-45 */
-#define CONTROL_READ (2<<27) /* Read operation */
-#define CONTROL_WRITE (1<<27) /* Write operation */
-#define CONTROL_IFSEL (1<<26) /* Interface select */
-#define CONTROL_PHYREG(_n) (((_n) & 0x1F) << 21)
-#define CONTROL_PHYID(_n) (((_n) & 0x1F) << 16)
-#define CONTROL_DATA(_n) (((_n) & 0xFFFF) << 0)
-#define MDIO_STATUS 0x04
-#define STATUS_BUSY (1<<31)
-#define STATUS_DONE (1<<30)
-#define MDIO_CLK_OFFSET 0x08
-#define MDIO_CLK_PERIOD 0x0c
-
-/* MDIO bus driver private data */
-struct lsi_mdio_priv {
- void __iomem *base;
- struct mii_bus *bus;
-};
-
-static inline void __iomem *
-bus_to_regs(struct mii_bus *bus)
-{
- return ((struct lsi_mdio_priv *)bus->priv)->base;
-}
-
-static int
-lsi_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
-{
- void __iomem *base = bus_to_regs(bus);
- u32 ctrl;
- u32 data;
-
- /* Set the mdio_done (status) bit. */
- writel(STATUS_DONE | readl(base + MDIO_STATUS), base + MDIO_STATUS);
-
- /* Write the command. */
- ctrl = (CONTROL_READ |
- CONTROL_PHYID(mii_id) |
- CONTROL_PHYREG(regnum));
- if (regnum & MII_ADDR_C45)
- ctrl |= CONTROL_CL45;
- writel(ctrl, base + MDIO_CONTROL);
-
- /* Wait for the mdio_done (status) bit to clear. */
- while ((readl(base + MDIO_STATUS) & STATUS_DONE) != 0)
- cpu_relax();
-
- /* Wait for the mdio_busy (control) bit to clear. */
- do {
- data = readl(base + MDIO_CONTROL);
- } while ((data & CONTROL_BUSY) != 0);
-
- return data & 0xFFFF;
-}
-
-static int
-lsi_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
-{
- void __iomem *base = bus_to_regs(bus);
- u32 ctrl;
-
- /* Wait for mdio_busy (control) to be clear. */
- while ((readl(base + MDIO_CONTROL) & CONTROL_BUSY) != 0)
- cpu_relax();
-
- /* Set the mdio_busy (status) bit. */
- writel(STATUS_DONE | readl(base + MDIO_STATUS), base + MDIO_STATUS);
-
- /* Write the command. */
- ctrl = (CONTROL_WRITE |
- CONTROL_PHYID(mii_id) |
- CONTROL_PHYREG(regnum) |
- CONTROL_DATA(value));
- if (regnum & MII_ADDR_C45)
- ctrl |= CONTROL_CL45;
- writel(ctrl, base + MDIO_CONTROL);
-
- /* Wait for the mdio_done (status) bit to clear. */
- while ((readl(base + MDIO_STATUS) & STATUS_DONE) != 0)
- cpu_relax();
-
- /* Wait for the mdio_busy (control) bit to clear. */
- while ((readl(base + MDIO_CONTROL) & CONTROL_BUSY) != 0)
- cpu_relax();
-
- return 0;
-}
-
-static int
-lsi_mdio_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct lsi_mdio_priv *priv = NULL;
- struct resource *res;
- int err;
- u32 clk_offset = 0x10;
- u32 clk_period = 0x2c;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENODEV;
-
- priv->bus = mdiobus_alloc();
- if (!priv->bus)
- return -ENOMEM;
-
- priv->bus->name = "Axxia MDIO",
- priv->bus->read = lsi_mdio_read,
- priv->bus->write = lsi_mdio_write,
- priv->bus->priv = priv;
- snprintf(priv->bus->id, MII_BUS_ID_SIZE, pdev->name);
-
- priv->base = devm_ioremap_resource(&pdev->dev, res);
- if (!priv->base) {
- dev_err(&pdev->dev, "Failed to map registers\n");
- err = -ENODEV;
- goto err_ret;
- }
-
- priv->bus->parent = &pdev->dev;
- dev_set_drvdata(&pdev->dev, priv->bus);
-
- of_property_read_u32(np, "lsi,mdio-clk-offset", &clk_offset);
- of_property_read_u32(np, "lsi,mdio-clk-period", &clk_period);
-
- writel(clk_offset, priv->base + MDIO_CLK_OFFSET);
- writel(clk_period, priv->base + MDIO_CLK_PERIOD);
- writel(0, priv->base + MDIO_CONTROL);
-
- err = of_mdiobus_register(priv->bus, np);
- if (err) {
- dev_err(&pdev->dev, "Failed to register MDIO bus\n");
- goto err_ret;
- }
-
- return 0;
-
-err_ret:
- if (priv && priv->bus)
- kfree(priv->bus);
- return err;
-}
-
-static int
-lsi_mdio_remove(struct platform_device *pdev)
-{
- struct mii_bus *bus = dev_get_drvdata(&pdev->dev);
-
- mdiobus_unregister(bus);
- dev_set_drvdata(&pdev->dev, NULL);
- mdiobus_free(bus);
-
- return 0;
-}
-
-static struct of_device_id lsi_mdio_match[] = {
- { .compatible = "lsi,axm-mdio", },
- {},
-};
-MODULE_DEVICE_TABLE(of, lsi_mdio_match);
-
-static struct platform_driver lsi_mdio_driver = {
- .driver = {
- .name = "lsi-mdio",
- .owner = THIS_MODULE,
- .of_match_table = lsi_mdio_match,
- },
- .probe = lsi_mdio_probe,
- .remove = lsi_mdio_remove,
-};
-
-module_platform_driver(lsi_mdio_driver);
-
-MODULE_AUTHOR("LSI Corporation");
-MODULE_DESCRIPTION("LSI MDIO Bus Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/include/linux/axxia-mdio.h b/include/linux/axxia-mdio.h
new file mode 100644
index 0000000..3550349
--- /dev/null
+++ b/include/linux/axxia-mdio.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 Intel <john.jacques at intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#ifndef __DRIVERS_MISC_AXXIA_MDIO_H
+#define __DRIVERS_MISC_AXXIA_MDIO_H
+
+#endif /* __DRIVERS_MISC_AXXIA_MDIO_H */
--
2.7.4
More information about the linux-yocto
mailing list