From: John Jacques <john.jacq...@intel.com> Removes unused code and adds the second MDIO bus available on 6700.
Signed-off-by: John Jacques <john.jacq...@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@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@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@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.jacq...@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.jacq...@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 -- _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto