aboutsummaryrefslogtreecommitdiffstats
path: root/package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch
diff options
context:
space:
mode:
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.patch546
1 files changed, 546 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 0000000..16e16d0
--- /dev/null
+++ b/package/boot/uboot-lantiq/patches/0016-net-add-driver-for-Lantiq-XWAY-ARX100-switch.patch
@@ -0,0 +1,546 @@
+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>
+
+--- 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__
+--- /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__ */
+--- a/drivers/net/Makefile
++++ b/drivers/net/Makefile
+@@ -38,6 +38,7 @@ COBJS-$(CONFIG_DRIVER_KS8695ETH) += ks86
+ 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
+--- /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;
++}