123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- 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;
- +}
|