From 204df03dd8524ab3ee8261feab44397dc890a840 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 10 Aug 2011 15:32:16 +0200 Subject: [PATCH 15/70] NET: MIPS: lantiq: make etop ethernet work on ase/ar9 Extend the driver to handle the different DMA channel layout for AR9 and Amazon-SE SoCs. The patch also adds support for the integrated PHY found on Amazon-SE and the gigabit switch found inside the AR9. Signed-off-by: John Crispin Cc: netdev@vger.kernel.org --- .../mips/include/asm/mach-lantiq/xway/lantiq_irq.h | 22 +--- .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 10 ++ arch/mips/lantiq/xway/devices.c | 11 +- drivers/net/ethernet/lantiq_etop.c | 171 ++++++++++++++++++-- 4 files changed, 174 insertions(+), 40 deletions(-) --- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h @@ -38,26 +38,8 @@ #define MIPS_CPU_TIMER_IRQ 7 -#define LTQ_DMA_CH0_INT (INT_NUM_IM2_IRL0) -#define LTQ_DMA_CH1_INT (INT_NUM_IM2_IRL0 + 1) -#define LTQ_DMA_CH2_INT (INT_NUM_IM2_IRL0 + 2) -#define LTQ_DMA_CH3_INT (INT_NUM_IM2_IRL0 + 3) -#define LTQ_DMA_CH4_INT (INT_NUM_IM2_IRL0 + 4) -#define LTQ_DMA_CH5_INT (INT_NUM_IM2_IRL0 + 5) -#define LTQ_DMA_CH6_INT (INT_NUM_IM2_IRL0 + 6) -#define LTQ_DMA_CH7_INT (INT_NUM_IM2_IRL0 + 7) -#define LTQ_DMA_CH8_INT (INT_NUM_IM2_IRL0 + 8) -#define LTQ_DMA_CH9_INT (INT_NUM_IM2_IRL0 + 9) -#define LTQ_DMA_CH10_INT (INT_NUM_IM2_IRL0 + 10) -#define LTQ_DMA_CH11_INT (INT_NUM_IM2_IRL0 + 11) -#define LTQ_DMA_CH12_INT (INT_NUM_IM2_IRL0 + 25) -#define LTQ_DMA_CH13_INT (INT_NUM_IM2_IRL0 + 26) -#define LTQ_DMA_CH14_INT (INT_NUM_IM2_IRL0 + 27) -#define LTQ_DMA_CH15_INT (INT_NUM_IM2_IRL0 + 28) -#define LTQ_DMA_CH16_INT (INT_NUM_IM2_IRL0 + 29) -#define LTQ_DMA_CH17_INT (INT_NUM_IM2_IRL0 + 30) -#define LTQ_DMA_CH18_INT (INT_NUM_IM2_IRL0 + 16) -#define LTQ_DMA_CH19_INT (INT_NUM_IM2_IRL0 + 21) +#define LTQ_DMA_ETOP ((ltq_is_ase()) ? \ + (INT_NUM_IM3_IRL0) : (INT_NUM_IM2_IRL0)) #define LTQ_PPE_MBOX_INT (INT_NUM_IM2_IRL0 + 24) --- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h @@ -82,6 +82,7 @@ #define LTQ_PMU_SIZE 0x1000 #define PMU_DMA 0x0020 +#define PMU_EPHY 0x0080 #define PMU_USB 0x8041 #define PMU_LED 0x0800 #define PMU_GPT 0x1000 @@ -93,6 +94,10 @@ #define LTQ_ETOP_BASE_ADDR 0x1E180000 #define LTQ_ETOP_SIZE 0x40000 +/* GBIT - gigabit switch */ +#define LTQ_GBIT_BASE_ADDR 0x1E108000 +#define LTQ_GBIT_SIZE 0x200 + /* DMA */ #define LTQ_DMA_BASE_ADDR 0x1E104100 #define LTQ_DMA_SIZE 0x800 @@ -147,6 +152,11 @@ extern void ltq_pmu_enable(unsigned int extern void ltq_pmu_disable(unsigned int module); extern void ltq_cgu_enable(unsigned int clk); +static inline int ltq_is_ase(void) +{ + return (ltq_get_soc_type() == SOC_TYPE_AMAZON_SE); +} + static inline int ltq_is_ar9(void) { return (ltq_get_soc_type() == SOC_TYPE_AR9); --- a/arch/mips/lantiq/xway/devices.c +++ b/arch/mips/lantiq/xway/devices.c @@ -74,18 +74,23 @@ void __init ltq_register_ase_asc(void) } /* ethernet */ -static struct resource ltq_etop_resources = - MEM_RES("etop", LTQ_ETOP_BASE_ADDR, LTQ_ETOP_SIZE); +static struct resource ltq_etop_resources[] = { + MEM_RES("etop", LTQ_ETOP_BASE_ADDR, LTQ_ETOP_SIZE), + MEM_RES("gbit", LTQ_GBIT_BASE_ADDR, LTQ_GBIT_SIZE), +}; static struct platform_device ltq_etop = { .name = "ltq_etop", - .resource = <q_etop_resources, + .resource = ltq_etop_resources, .num_resources = 1, }; void __init ltq_register_etop(struct ltq_eth_data *eth) { + /* only register the gphy on socs that have one */ + if (ltq_is_ar9() | ltq_is_vr9()) + ltq_etop.num_resources = 2; if (eth) { ltq_etop.dev.platform_data = eth; platform_device_register(<q_etop); --- a/drivers/net/ethernet/lantiq_etop.c +++ b/drivers/net/ethernet/lantiq_etop.c @@ -71,10 +71,43 @@ #define ETOP_MII_REVERSE 0xe #define ETOP_PLEN_UNDER 0x40 #define ETOP_CGEN 0x800 +#define ETOP_CFG_MII0 0x01 -/* use 2 static channels for TX/RX */ +#define LTQ_GBIT_MDIO_CTL 0xCC +#define LTQ_GBIT_MDIO_DATA 0xd0 +#define LTQ_GBIT_GCTL0 0x68 +#define LTQ_GBIT_PMAC_HD_CTL 0x8c +#define LTQ_GBIT_P0_CTL 0x4 +#define LTQ_GBIT_PMAC_RX_IPG 0xa8 + +#define PMAC_HD_CTL_AS (1 << 19) +#define PMAC_HD_CTL_RXSH (1 << 22) + +/* Switch Enable (0=disable, 1=enable) */ +#define GCTL0_SE 0x80000000 +/* Disable MDIO auto polling (0=disable, 1=enable) */ +#define PX_CTL_DMDIO 0x00400000 + +/* register information for the gbit's MDIO bus */ +#define MDIO_XR9_REQUEST 0x00008000 +#define MDIO_XR9_READ 0x00000800 +#define MDIO_XR9_WRITE 0x00000400 +#define MDIO_XR9_REG_MASK 0x1f +#define MDIO_XR9_ADDR_MASK 0x1f +#define MDIO_XR9_RD_MASK 0xffff +#define MDIO_XR9_REG_OFFSET 0 +#define MDIO_XR9_ADDR_OFFSET 5 +#define MDIO_XR9_WR_OFFSET 16 + +/* the newer xway socks have a embedded 3/7 port gbit multiplexer */ +#define ltq_has_gbit() (ltq_is_ar9() || ltq_is_vr9()) + +/* use 2 static channels for TX/RX + depending on the SoC we need to use different DMA channels for ethernet */ #define LTQ_ETOP_TX_CHANNEL 1 -#define LTQ_ETOP_RX_CHANNEL 6 +#define LTQ_ETOP_RX_CHANNEL ((ltq_is_ase()) ? (5) : \ + ((ltq_has_gbit()) ? (0) : (6))) + #define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL) #define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL) @@ -83,9 +116,15 @@ #define ltq_etop_w32_mask(x, y, z) \ ltq_w32_mask(x, y, ltq_etop_membase + (z)) +#define ltq_gbit_r32(x) ltq_r32(ltq_gbit_membase + (x)) +#define ltq_gbit_w32(x, y) ltq_w32(x, ltq_gbit_membase + (y)) +#define ltq_gbit_w32_mask(x, y, z) \ + ltq_w32_mask(x, y, ltq_gbit_membase + (z)) + #define DRV_VERSION "1.0" static void __iomem *ltq_etop_membase; +static void __iomem *ltq_gbit_membase; struct ltq_etop_chan { int idx; @@ -111,6 +150,9 @@ struct ltq_etop_priv { spinlock_t lock; }; +static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, + int phy_reg, u16 phy_data); + static int ltq_etop_alloc_skb(struct ltq_etop_chan *ch) { @@ -212,7 +254,7 @@ static irqreturn_t ltq_etop_dma_irq(int irq, void *_priv) { struct ltq_etop_priv *priv = _priv; - int ch = irq - LTQ_DMA_CH0_INT; + int ch = irq - LTQ_DMA_ETOP; napi_schedule(&priv->ch[ch].napi); return IRQ_HANDLED; @@ -245,15 +287,43 @@ ltq_etop_hw_exit(struct net_device *dev) ltq_etop_free_channel(dev, &priv->ch[i]); } +static void +ltq_etop_gbit_init(void) +{ + ltq_pmu_enable(PMU_SWITCH); + + ltq_gpio_request(42, 2, 1, "MDIO"); + ltq_gpio_request(43, 2, 1, "MDC"); + + ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0); + /** Disable MDIO auto polling mode */ + ltq_gbit_w32_mask(0, PX_CTL_DMDIO, LTQ_GBIT_P0_CTL); + /* set 1522 packet size */ + ltq_gbit_w32_mask(0x300, 0, LTQ_GBIT_GCTL0); + /* disable pmac & dmac headers */ + ltq_gbit_w32_mask(PMAC_HD_CTL_AS | PMAC_HD_CTL_RXSH, 0, + LTQ_GBIT_PMAC_HD_CTL); + /* Due to traffic halt when burst length 8, + replace default IPG value with 0x3B */ + ltq_gbit_w32(0x3B, LTQ_GBIT_PMAC_RX_IPG); +} + static int ltq_etop_hw_init(struct net_device *dev) { struct ltq_etop_priv *priv = netdev_priv(dev); + unsigned int mii_mode = priv->pldata->mii_mode; int i; ltq_pmu_enable(PMU_PPE); - switch (priv->pldata->mii_mode) { + if (ltq_has_gbit()) { + ltq_etop_gbit_init(); + /* force the etops link to the gbit to MII */ + mii_mode = PHY_INTERFACE_MODE_MII; + } + + switch (mii_mode) { case PHY_INTERFACE_MODE_RMII: ltq_etop_w32_mask(ETOP_MII_MASK, ETOP_MII_REVERSE, LTQ_ETOP_CFG); @@ -265,6 +335,18 @@ ltq_etop_hw_init(struct net_device *dev) break; default: + if (ltq_is_ase()) { + ltq_pmu_enable(PMU_EPHY); + /* disable external MII */ + ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG); + /* enable clock for internal PHY */ + ltq_cgu_enable(CGU_EPHY); + /* we need to write this magic to the internal phy to + make it work */ + ltq_etop_mdio_wr(NULL, 0x8, 0x12, 0xC020); + pr_info("Selected EPHY mode\n"); + break; + } netdev_err(dev, "unknown mii mode %d\n", priv->pldata->mii_mode); return -ENOTSUPP; @@ -276,7 +358,7 @@ ltq_etop_hw_init(struct net_device *dev) ltq_dma_init_port(DMA_PORT_ETOP); for (i = 0; i < MAX_DMA_CHAN; i++) { - int irq = LTQ_DMA_CH0_INT + i; + int irq = LTQ_DMA_ETOP + i; struct ltq_etop_chan *ch = &priv->ch[i]; ch->idx = ch->dma.nr = i; @@ -340,6 +422,39 @@ static const struct ethtool_ops ltq_etop }; static int +ltq_etop_mdio_wr_xr9(struct mii_bus *bus, int phy_addr, + int phy_reg, u16 phy_data) +{ + u32 val = MDIO_XR9_REQUEST | MDIO_XR9_WRITE | + (phy_data << MDIO_XR9_WR_OFFSET) | + ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | + ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); + + while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) + ; + ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); + while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) + ; + return 0; +} + +static int +ltq_etop_mdio_rd_xr9(struct mii_bus *bus, int phy_addr, int phy_reg) +{ + u32 val = MDIO_XR9_REQUEST | MDIO_XR9_READ | + ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | + ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); + + while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) + ; + ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); + while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) + ; + val = ltq_gbit_r32(LTQ_GBIT_MDIO_DATA) & MDIO_XR9_RD_MASK; + return val; +} + +static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, int phy_reg, u16 phy_data) { u32 val = MDIO_REQUEST | @@ -380,14 +495,11 @@ ltq_etop_mdio_probe(struct net_device *d { struct ltq_etop_priv *priv = netdev_priv(dev); struct phy_device *phydev = NULL; - int phy_addr; - for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { - if (priv->mii_bus->phy_map[phy_addr]) { - phydev = priv->mii_bus->phy_map[phy_addr]; - break; - } - } + if (ltq_is_ase()) + phydev = priv->mii_bus->phy_map[8]; + else + phydev = priv->mii_bus->phy_map[0]; if (!phydev) { netdev_err(dev, "no PHY found\n"); @@ -409,6 +521,9 @@ ltq_etop_mdio_probe(struct net_device *d | SUPPORTED_Autoneg | SUPPORTED_MII | SUPPORTED_TP); + if (ltq_has_gbit()) + phydev->supported &= SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full; phydev->advertising = phydev->supported; priv->phydev = phydev; @@ -434,8 +549,13 @@ ltq_etop_mdio_init(struct net_device *de } priv->mii_bus->priv = dev; - priv->mii_bus->read = ltq_etop_mdio_rd; - priv->mii_bus->write = ltq_etop_mdio_wr; + if (ltq_has_gbit()) { + priv->mii_bus->read = ltq_etop_mdio_rd_xr9; + priv->mii_bus->write = ltq_etop_mdio_wr_xr9; + } else { + priv->mii_bus->read = ltq_etop_mdio_rd; + priv->mii_bus->write = ltq_etop_mdio_wr; + } priv->mii_bus->name = "ltq_mii"; snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", priv->pdev->name, priv->pdev->id); @@ -526,9 +646,9 @@ ltq_etop_tx(struct sk_buff *skb, struct struct ltq_etop_priv *priv = netdev_priv(dev); struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1]; struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; - int len; unsigned long flags; u32 byte_offset; + int len; len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; @@ -702,7 +822,7 @@ ltq_etop_probe(struct platform_device *p { struct net_device *dev; struct ltq_etop_priv *priv; - struct resource *res; + struct resource *res, *gbit_res; int err; int i; @@ -730,6 +850,23 @@ ltq_etop_probe(struct platform_device *p goto err_out; } + if (ltq_has_gbit()) { + gbit_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!gbit_res) { + dev_err(&pdev->dev, "failed to get gbit resource\n"); + err = -ENOENT; + goto err_out; + } + ltq_gbit_membase = devm_ioremap_nocache(&pdev->dev, + gbit_res->start, resource_size(gbit_res)); + if (!ltq_gbit_membase) { + dev_err(&pdev->dev, "failed to remap gigabit switch %d\n", + pdev->id); + err = -ENOMEM; + goto err_out; + } + } + dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); strcpy(dev->name, "eth%d"); dev->netdev_ops = <q_eth_netdev_ops;