openwrt-owl/target/linux/lantiq/patches-3.2/0018-NET-MIPS-lantiq-make-e...

397 lines
12 KiB
Diff

From a1a9dcf7554df9bbf23ca0933e645b2327abbb8b Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Wed, 10 Aug 2011 15:32:16 +0200
Subject: [PATCH 18/73] 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 <blogic@openwrt.org>
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 = &ltq_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(&ltq_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;
@@ -110,6 +149,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)
{
@@ -211,7 +253,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;
@@ -244,15 +286,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);
@@ -264,6 +334,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;
@@ -275,7 +357,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;
@@ -339,6 +421,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 |
@@ -379,14 +494,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");
@@ -408,6 +520,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;
@@ -433,8 +548,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, "%x", 0);
priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
@@ -524,9 +644,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;
@@ -700,7 +820,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;
@@ -728,6 +848,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 = &ltq_eth_netdev_ops;