diff options
Diffstat (limited to 'package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch')
-rw-r--r-- | package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch b/package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch new file mode 100644 index 0000000000..de1ecd2833 --- /dev/null +++ b/package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch @@ -0,0 +1,559 @@ +From 7288414298b34dcda1216fee1fe38d05ea0027a2 Mon Sep 17 00:00:00 2001 +From: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> +Date: Mon, 17 Dec 2012 23:32:39 +0100 +Subject: net: add driver for Lantiq XWAY ARX100 switch + +Signed-off-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com> + +diff --git a/arch/mips/include/asm/arch-arx100/config.h b/arch/mips/include/asm/arch-arx100/config.h +index 1a6c9bc..8f955e8 100644 +--- a/arch/mips/include/asm/arch-arx100/config.h ++++ b/arch/mips/include/asm/arch-arx100/config.h +@@ -10,17 +10,21 @@ + * and drivers for this SoC: + * + * CONFIG_LTQ_SUPPORT_UART +- * - support the Danube ASC/UART interface and console ++ * - support the ARX100 ASC/UART interface and console + * + * CONFIG_LTQ_SUPPORT_NOR_FLASH + * - support a parallel NOR flash via the CFI interface in flash bank 0 + * + * CONFIG_LTQ_SUPPORT_ETHERNET +- * - support the Danube ETOP and MAC interface ++ * - support the ARX100 ETOP and MAC interface + * + * CONFIG_LTQ_SUPPORT_SPI_FLASH +- * - support the Danube SPI interface and serial flash drivers ++ * - support the ARX100 SPI interface and serial flash drivers + * - specific SPI flash drivers must be configured separately ++ * ++ * CONFIG_LTQ_SUPPORT_SPL_SPI_FLASH ++ * - build a preloader that runs in the internal SRAM and loads ++ * the U-Boot from SPI flash into RAM + */ + + #ifndef __ARX100_CONFIG_H__ +diff --git a/arch/mips/include/asm/arch-arx100/switch.h b/arch/mips/include/asm/arch-arx100/switch.h +new file mode 100644 +index 0000000..301056c +--- /dev/null ++++ b/arch/mips/include/asm/arch-arx100/switch.h +@@ -0,0 +1,86 @@ ++/* ++ * Copyright (C) 2012-2013 Daniel Schwierzeck <daniel.schwierzeck@gmail.com> ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#ifndef __ARX100_SWITCH_H__ ++#define __ARX100_SWITCH_H__ ++ ++struct ar9_switch_regs { ++ __be32 ps; /* Port status*/ ++ __be32 p0_ctl; /* Port 0 control */ ++ __be32 p1_ctl; /* Port 1 control */ ++ __be32 p2_ctl; /* Port 2 control */ ++ __be32 p0_vlan; /* Port 0 VLAN control */ ++ __be32 p1_vlan; /* Port 1 VLAN control */ ++ __be32 p2_vlan; /* Port 2 VLAN control */ ++ __be32 p0_inctl; /* Port 0 ingress control */ ++ __be32 p1_inctl; /* Port 1 ingress control */ ++ __be32 p2_inctl; /* Port 2 ingress control */ ++ u32 rsvd0[16]; ++ __be32 sw_gctl0; /* Switch global control 0 */ ++ __be32 sw_gctl1; /* Switch global control 1 */ ++ __be32 arp; /* ARP/RARP */ ++ __be32 strm_ctl; /* Storm control */ ++ __be32 rgmii_ctl; /* RGMII/GMII port control */ ++ u32 rsvd1[4]; ++ __be32 pmac_hd_ctl; /* PMAC header control */ ++ u32 rsvd2[15]; ++ __be32 mdio_ctrl; /* MDIO indirect access control */ ++ __be32 mdio_data; /* MDIO indirect read data */ ++}; ++ ++#define BUILD_CHECK_AR9_REG(name, offset) \ ++ BUILD_BUG_ON(offsetof(struct ar9_switch_regs, name) != (offset)) ++ ++static inline void build_check_ar9_registers(void) ++{ ++ BUILD_CHECK_AR9_REG(sw_gctl0, 0x68); ++ BUILD_CHECK_AR9_REG(rgmii_ctl, 0x78); ++ BUILD_CHECK_AR9_REG(pmac_hd_ctl, 0x8c); ++ BUILD_CHECK_AR9_REG(mdio_ctrl, 0xcc); ++ BUILD_CHECK_AR9_REG(mdio_data, 0xd0); ++} ++ ++#define P0_CTL_FLP (1 << 18) ++#define P0_CTL_FLD (1 << 17) ++ ++#define SW_GCTL0_SE (1 << 31) ++ ++#define RGMII_CTL_P1_SHIFT 10 ++#define RGMII_CTL_P1_MASK (0x3FF << RGMII_CTL_P1_SHIFT) ++#define RGMII_CTL_P0_MASK 0x3FF ++#define RGMII_CTL_P0IS_SHIFT 8 ++#define RGMII_CTL_P0IS_RGMII (0x0 << RGMII_CTL_P0IS_SHIFT) ++#define RGMII_CTL_P0IS_MII (0x1 << RGMII_CTL_P0IS_SHIFT) ++#define RGMII_CTL_P0IS_REVMII (0x2 << RGMII_CTL_P0IS_SHIFT) ++#define RGMII_CTL_P0IS_RMII (0x3 << RGMII_CTL_P0IS_SHIFT) ++#define RGMII_CTL_P0RDLY_SHIFT 6 ++#define RGMII_CTL_P0RDLY_0_0 (0x0 << RGMII_CTL_P0RDLY_SHIFT) ++#define RGMII_CTL_P0RDLY_1_5 (0x1 << RGMII_CTL_P0RDLY_SHIFT) ++#define RGMII_CTL_P0RDLY_1_75 (0x2 << RGMII_CTL_P0RDLY_SHIFT) ++#define RGMII_CTL_P0RDLY_2_0 (0x3 << RGMII_CTL_P0RDLY_SHIFT) ++#define RGMII_CTL_P0TDLY_SHIFT 4 ++#define RGMII_CTL_P0TDLY_0_0 (0x0 << RGMII_CTL_P0TDLY_SHIFT) ++#define RGMII_CTL_P0TDLY_1_5 (0x1 << RGMII_CTL_P0TDLY_SHIFT) ++#define RGMII_CTL_P0TDLY_1_75 (0x2 << RGMII_CTL_P0TDLY_SHIFT) ++#define RGMII_CTL_P0TDLY_2_0 (0x3 << RGMII_CTL_P0TDLY_SHIFT) ++#define RGMII_CTL_P0SPD_SHIFT 2 ++#define RGMII_CTL_P0SPD_10 (0x0 << RGMII_CTL_P0SPD_SHIFT) ++#define RGMII_CTL_P0SPD_100 (0x1 << RGMII_CTL_P0SPD_SHIFT) ++#define RGMII_CTL_P0SPD_1000 (0x2 << RGMII_CTL_P0SPD_SHIFT) ++#define RGMII_CTL_P0DUP_FULL (1 << 1) ++#define RGMII_CTL_P0FCE_EN (1 << 0) ++ ++#define PMAC_HD_CTL_AC (1 << 18) ++ ++#define MDIO_CTRL_WD_SHIFT 16 ++#define MDIO_CTRL_MBUSY (1 << 15) ++#define MDIO_CTRL_OP_READ (1 << 11) ++#define MDIO_CTRL_OP_WRITE (1 << 10) ++#define MDIO_CTRL_PHYAD_SHIFT 5 ++#define MDIO_CTRL_PHYAD_MASK (0x1f << MDIO_CTRL_PHYAD_SHIFT) ++#define MDIO_CTRL_REGAD_MASK 0x1f ++ ++#endif /* __ARX100_SWITCH_H__ */ +diff --git a/drivers/net/Makefile b/drivers/net/Makefile +index bbc2c92..926b8c2 100644 +--- a/drivers/net/Makefile ++++ b/drivers/net/Makefile +@@ -38,6 +38,7 @@ COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks8695eth.o + COBJS-$(CONFIG_KS8851_MLL) += ks8851_mll.o + COBJS-$(CONFIG_LAN91C96) += lan91c96.o + COBJS-$(CONFIG_LANTIQ_DANUBE_ETOP) += lantiq_danube_etop.o ++COBJS-$(CONFIG_LANTIQ_ARX100_SWITCH) += lantiq_arx100_switch.o + COBJS-$(CONFIG_LANTIQ_VRX200_SWITCH) += lantiq_vrx200_switch.o + COBJS-$(CONFIG_MACB) += macb.o + COBJS-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o +diff --git a/drivers/net/lantiq_arx100_switch.c b/drivers/net/lantiq_arx100_switch.c +new file mode 100644 +index 0000000..cc65249 +--- /dev/null ++++ b/drivers/net/lantiq_arx100_switch.c +@@ -0,0 +1,410 @@ ++/* ++ * Copyright (C) 2011-2013 Daniel Schwierzeck, daniel.schwierzeck@gmail.com ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++#define DEBUG ++#include <common.h> ++#include <malloc.h> ++#include <netdev.h> ++#include <miiphy.h> ++#include <switch.h> ++#include <linux/compiler.h> ++#include <asm/gpio.h> ++#include <asm/processor.h> ++#include <asm/lantiq/io.h> ++#include <asm/lantiq/eth.h> ++#include <asm/lantiq/pm.h> ++#include <asm/lantiq/reset.h> ++#include <asm/lantiq/dma.h> ++#include <asm/arch/soc.h> ++#include <asm/arch/switch.h> ++ ++#define LTQ_ETH_RX_BUFFER_CNT PKTBUFSRX ++#define LTQ_ETH_TX_BUFFER_CNT 8 ++#define LTQ_ETH_RX_DATA_SIZE PKTSIZE_ALIGN ++#define LTQ_ETH_IP_ALIGN 2 ++ ++#define LTQ_MDIO_DRV_NAME "ltq-mdio" ++#define LTQ_ETH_DRV_NAME "ltq-eth" ++ ++#define LTQ_ETHSW_MAX_GMAC 2 ++#define LTQ_ETHSW_PMAC 2 ++ ++struct ltq_eth_priv { ++ struct ltq_dma_device dma_dev; ++ struct mii_dev *bus; ++ struct eth_device *dev; ++ struct phy_device *phymap[LTQ_ETHSW_MAX_GMAC]; ++ int rx_num; ++ int tx_num; ++}; ++ ++static struct ar9_switch_regs *switch_regs = ++ (struct ar9_switch_regs *) CKSEG1ADDR(LTQ_SWITCH_BASE); ++ ++static int ltq_mdio_is_busy(void) ++{ ++ u32 mdio_ctrl = ltq_readl(&switch_regs->mdio_ctrl); ++ ++ return mdio_ctrl & MDIO_CTRL_MBUSY; ++} ++ ++static void ltq_mdio_poll(void) ++{ ++ while (ltq_mdio_is_busy()) ++ cpu_relax(); ++ ++ __udelay(1000); ++} ++ ++static int ltq_mdio_read(struct mii_dev *bus, int phyad, int devad, ++ int regad) ++{ ++ u32 mdio_ctrl; ++ int retval; ++ ++ mdio_ctrl = MDIO_CTRL_MBUSY | MDIO_CTRL_OP_READ | ++ ((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) | ++ (regad & MDIO_CTRL_REGAD_MASK); ++ ++ ltq_mdio_poll(); ++ ltq_writel(&switch_regs->mdio_ctrl, mdio_ctrl); ++ ltq_mdio_poll(); ++ retval = ltq_readl(&switch_regs->mdio_data); ++ ltq_writel(&switch_regs->mdio_data, 0xFFFF); ++ ++ debug("%s: phyad %02x, regad %02x, val %02x\n", __func__, phyad, regad, retval); ++ ++ return retval; ++} ++ ++static int ltq_mdio_write(struct mii_dev *bus, int phyad, int devad, ++ int regad, u16 val) ++{ ++ u32 mdio_ctrl; ++ ++ debug("%s: phyad %02x, regad %02x, val %02x\n", __func__, phyad, regad, val); ++ ++ mdio_ctrl = (val << MDIO_CTRL_WD_SHIFT) | MDIO_CTRL_MBUSY | ++ MDIO_CTRL_OP_WRITE | ++ ((phyad << MDIO_CTRL_PHYAD_SHIFT) & MDIO_CTRL_PHYAD_MASK) | ++ (regad & MDIO_CTRL_REGAD_MASK); ++ ++ ltq_mdio_poll(); ++ ltq_writel(&switch_regs->mdio_ctrl, mdio_ctrl); ++ ++ return 0; ++} ++ ++static void ltq_eth_gmac_update(struct phy_device *phydev, int num) ++{ ++} ++ ++static inline u8 *ltq_eth_rx_packet_align(int rx_num) ++{ ++ u8 *packet = (u8 *) NetRxPackets[rx_num]; ++ ++ /* ++ * IP header needs ++ */ ++ return packet + LTQ_ETH_IP_ALIGN; ++} ++ ++static int ltq_eth_init(struct eth_device *dev, bd_t *bis) ++{ ++ struct ltq_eth_priv *priv = dev->priv; ++ struct ltq_dma_device *dma_dev = &priv->dma_dev; ++ struct phy_device *phydev; ++ int i; ++ ++ for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) { ++ phydev = priv->phymap[i]; ++ if (!phydev) ++ continue; ++ ++ phy_startup(phydev); ++ ltq_eth_gmac_update(phydev, i); ++ } ++ ++ for (i = 0; i < LTQ_ETH_RX_BUFFER_CNT; i++) ++ ltq_dma_rx_map(dma_dev, i, ltq_eth_rx_packet_align(i), ++ LTQ_ETH_RX_DATA_SIZE); ++ ++ ltq_dma_enable(dma_dev); ++ ++ priv->rx_num = 0; ++ priv->tx_num = 0; ++ ++ return 0; ++} ++ ++static void ltq_eth_halt(struct eth_device *dev) ++{ ++ struct ltq_eth_priv *priv = dev->priv; ++ struct ltq_dma_device *dma_dev = &priv->dma_dev; ++ struct phy_device *phydev; ++ int i; ++ ++ ltq_dma_reset(dma_dev); ++ ++ for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) { ++ phydev = priv->phymap[i]; ++ if (!phydev) ++ continue; ++ ++ phy_shutdown(phydev); ++ phydev->link = 0; ++ ltq_eth_gmac_update(phydev, i); ++ } ++} ++ ++static int ltq_eth_send(struct eth_device *dev, void *packet, int length) ++{ ++ struct ltq_eth_priv *priv = dev->priv; ++ struct ltq_dma_device *dma_dev = &priv->dma_dev; ++ int err; ++ ++ err = ltq_dma_tx_map(dma_dev, priv->tx_num, packet, length, 10); ++ if (err) { ++ puts("NET: timeout on waiting for TX descriptor\n"); ++ return -1; ++ } ++ ++ priv->tx_num = (priv->tx_num + 1) % LTQ_ETH_TX_BUFFER_CNT; ++ ++ return err; ++} ++ ++static int ltq_eth_recv(struct eth_device *dev) ++{ ++ struct ltq_eth_priv *priv = dev->priv; ++ struct ltq_dma_device *dma_dev = &priv->dma_dev; ++ u8 *packet; ++ int len; ++ ++ if (!ltq_dma_rx_poll(dma_dev, priv->rx_num)) ++ return 0; ++ ++#if 0 ++ printf("%s: rx_num %d\n", __func__, priv->rx_num); ++#endif ++ ++ len = ltq_dma_rx_length(dma_dev, priv->rx_num); ++ packet = ltq_eth_rx_packet_align(priv->rx_num); ++ ++#if 0 ++ printf("%s: received: packet %p, len %u, rx_num %d\n", ++ __func__, packet, len, priv->rx_num); ++#endif ++ ++ if (len) ++ NetReceive(packet, len); ++ ++ ltq_dma_rx_map(dma_dev, priv->rx_num, packet, ++ LTQ_ETH_RX_DATA_SIZE); ++ ++ priv->rx_num = (priv->rx_num + 1) % LTQ_ETH_RX_BUFFER_CNT; ++ ++ return 0; ++} ++ ++static void ltq_eth_pmac_init(void) ++{ ++ /* Add CRC to packets from DMA to PMAC */ ++ ltq_setbits(&switch_regs->pmac_hd_ctl, PMAC_HD_CTL_AC); ++ ++ /* Force link up */ ++ ltq_setbits(&switch_regs->p2_ctl, P0_CTL_FLP); ++} ++ ++static void ltq_eth_hw_init(const struct ltq_eth_port_config *port) ++{ ++ /* Power up ethernet subsystems */ ++ ltq_pm_enable(LTQ_PM_ETH); ++ ++ /* Enable switch core */ ++ ltq_setbits(&switch_regs->sw_gctl0, SW_GCTL0_SE); ++ ++ /* MII/MDIO */ ++ gpio_set_altfunc(42, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT); ++ /* MII/MDC */ ++ gpio_set_altfunc(43, GPIO_ALTSEL_SET, GPIO_ALTSEL_CLR, GPIO_DIR_OUT); ++ ++ ltq_eth_pmac_init(); ++} ++ ++static void ltq_eth_port_config(struct ltq_eth_priv *priv, ++ const struct ltq_eth_port_config *port) ++{ ++ struct phy_device *phydev; ++ struct switch_device *sw; ++ u32 rgmii_ctl; ++ unsigned int port_ctl, port_xmii = 0; ++ ++ if (port->num > 1) ++ return; ++ ++ rgmii_ctl = ltq_readl(&switch_regs->rgmii_ctl); ++ ++ if (port->num == 1) ++ port_ctl = ltq_readl(&switch_regs->p1_ctl); ++ else ++ port_ctl = ltq_readl(&switch_regs->p0_ctl); ++ ++ switch (port->phy_if) { ++ case PHY_INTERFACE_MODE_RGMII: ++ port_xmii = RGMII_CTL_P0IS_RGMII; ++ ++ switch (port->rgmii_tx_delay) { ++ case 1: ++ port_xmii |= RGMII_CTL_P0TDLY_1_5; ++ break; ++ case 2: ++ port_xmii |= RGMII_CTL_P0TDLY_1_75; ++ break; ++ case 3: ++ port_xmii |= RGMII_CTL_P0TDLY_2_0; ++ break; ++ default: ++ break; ++ } ++ ++ switch (port->rgmii_rx_delay) { ++ case 1: ++ port_xmii |= RGMII_CTL_P0RDLY_1_5; ++ break; ++ case 2: ++ port_xmii |= RGMII_CTL_P0RDLY_1_75; ++ break; ++ case 3: ++ port_xmii |= RGMII_CTL_P0RDLY_2_0; ++ break; ++ default: ++ break; ++ } ++ ++ if (!(port->flags & LTQ_ETH_PORT_PHY)) { ++ port_xmii |= (RGMII_CTL_P0SPD_1000 | ++ RGMII_CTL_P0DUP_FULL); ++ port_ctl |= P0_CTL_FLP; ++ } ++ ++ break; ++ case PHY_INTERFACE_MODE_MII: ++ port_xmii = RGMII_CTL_P0IS_MII; ++ ++ if (!(port->flags & LTQ_ETH_PORT_PHY)) { ++ port_xmii |= (RGMII_CTL_P0SPD_100 | ++ RGMII_CTL_P0DUP_FULL); ++ port_ctl |= P0_CTL_FLP; ++ } ++ ++ break; ++ default: ++ break; ++ } ++ ++ if (port->num == 1) { ++ ltq_writel(&switch_regs->p1_ctl, port_ctl); ++ ++ rgmii_ctl &= ~RGMII_CTL_P1_MASK; ++ rgmii_ctl |= (port_xmii << RGMII_CTL_P1_SHIFT); ++ } else { ++ ltq_writel(&switch_regs->p0_ctl, port_ctl); ++ ++ rgmii_ctl &= ~RGMII_CTL_P0_MASK; ++ rgmii_ctl |= port_xmii; ++ } ++ ++ ltq_writel(&switch_regs->rgmii_ctl, rgmii_ctl); ++ ++ /* Connect to external switch */ ++ if (port->flags & LTQ_ETH_PORT_SWITCH) { ++ sw = switch_connect(priv->bus); ++ if (sw) ++ switch_setup(sw); ++ } ++ ++ /* Connect to internal/external PHYs */ ++ if (port->flags & LTQ_ETH_PORT_PHY) { ++ phydev = phy_connect(priv->bus, port->phy_addr, priv->dev, ++ port->phy_if); ++ if (phydev) ++ phy_config(phydev); ++ ++ priv->phymap[port->num] = phydev; ++ } ++} ++ ++int ltq_eth_initialize(const struct ltq_eth_board_config *board_config) ++{ ++ struct eth_device *dev; ++ struct mii_dev *bus; ++ struct ltq_eth_priv *priv; ++ struct ltq_dma_device *dma_dev; ++ const struct ltq_eth_port_config *port = &board_config->ports[0]; ++ int i, ret; ++ ++ build_check_ar9_registers(); ++ ++ ltq_dma_init(); ++ ltq_eth_hw_init(port); ++ ++ dev = calloc(1, sizeof(*dev)); ++ if (!dev) ++ return -1; ++ ++ priv = calloc(1, sizeof(*priv)); ++ if (!priv) ++ return -1; ++ ++ bus = mdio_alloc(); ++ if (!bus) ++ return -1; ++ ++ sprintf(dev->name, LTQ_ETH_DRV_NAME); ++ dev->priv = priv; ++ dev->init = ltq_eth_init; ++ dev->halt = ltq_eth_halt; ++ dev->recv = ltq_eth_recv; ++ dev->send = ltq_eth_send; ++ ++ sprintf(bus->name, LTQ_MDIO_DRV_NAME); ++ bus->read = ltq_mdio_read; ++ bus->write = ltq_mdio_write; ++ bus->priv = priv; ++ ++ dma_dev = &priv->dma_dev; ++ dma_dev->port = 0; ++ dma_dev->rx_chan.chan_no = 0; ++ dma_dev->rx_chan.class = 0; ++ dma_dev->rx_chan.num_desc = LTQ_ETH_RX_BUFFER_CNT; ++ dma_dev->rx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; ++ dma_dev->rx_burst_len = LTQ_DMA_BURST_2WORDS; ++ dma_dev->tx_chan.chan_no = 1; ++ dma_dev->tx_chan.class = 0; ++ dma_dev->tx_chan.num_desc = LTQ_ETH_TX_BUFFER_CNT; ++ dma_dev->tx_endian_swap = LTQ_DMA_ENDIANESS_B3_B2_B1_B0; ++ dma_dev->tx_burst_len = LTQ_DMA_BURST_2WORDS; ++ ++ priv->bus = bus; ++ priv->dev = dev; ++ ++ ret = ltq_dma_register(dma_dev); ++ if (ret) ++ return ret; ++ ++ ret = mdio_register(bus); ++ if (ret) ++ return ret; ++ ++ ret = eth_register(dev); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < board_config->num_ports; i++) ++ ltq_eth_port_config(priv, &board_config->ports[i]); ++ ++ return 0; ++} +-- +1.8.3.2 + |