mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 01:28:59 +00:00
6bf179b270
Switch to the mainline Lantiq PCIe PHY driver and update the vr9.dtsi accordingly. The Lantiq IRQ SMP support added upstream required changes to the SoC dtsi as well. Following changes are made to the Lantiq kernel patches: 0005-lantiq_etop-pass-struct-device-to-DMA-API-functions.patch 0006-MIPS-lantiq-pass-struct-device-to-DMA-API-functions.patch applied upstream 0008-MIPS-lantiq-backport-old-timer-code.patch access_ok API update because it lost it's type (which was the first) parameter in upstream commit 96d4f267e40f95 ("Remove 'type' argument from access_ok() function") 0024-MIPS-lantiq-autoselect-soc-rev-matching-fw.patch merged into 0026-MIPS-lantiq-Add-GPHY-Firmware-loader.patch 0024-MIPS-lantiq-revert-DSA-switch-driver-PMU-clock-chang.patch revert upstream changes required for upstream xrx200 ethernet and xrx200 (DSA) switch driver but breaking our driver 0026-MIPS-lantiq-Add-GPHY-Firmware-loader.patch required for our driver but dropped upstream, add former upstream version 0028-NET-lantiq-various-etop-fixes.patch now has to use the phy_set_max_speed API instead of modifying phydev->supported. Also call ltq_dma_enable_irq() in ltq_etop_open() based on upstream commit cc973aecf0b054 ("MIPS: lantiq: Do not enable IRQs in dma open") Signed-off-by: Mathias Kresin <dev@kresin.me> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
867 lines
24 KiB
Diff
867 lines
24 KiB
Diff
From 870ed9cae083ff8a60a739ef7e74c5a1800533be Mon Sep 17 00:00:00 2001
|
|
From: John Crispin <blogic@openwrt.org>
|
|
Date: Tue, 9 Sep 2014 22:45:34 +0200
|
|
Subject: [PATCH 28/36] NET: lantiq: various etop fixes
|
|
|
|
Signed-off-by: John Crispin <blogic@openwrt.org>
|
|
---
|
|
drivers/net/ethernet/lantiq_etop.c | 555 +++++++++++++++++++++++++-----------
|
|
1 file changed, 389 insertions(+), 166 deletions(-)
|
|
|
|
--- a/drivers/net/ethernet/lantiq_etop.c
|
|
+++ b/drivers/net/ethernet/lantiq_etop.c
|
|
@@ -1,7 +1,7 @@
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
*
|
|
- * Copyright (C) 2011 John Crispin <blogic@openwrt.org>
|
|
+ * Copyright (C) 2011-12 John Crispin <blogic@openwrt.org>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
@@ -20,11 +20,16 @@
|
|
#include <linux/mm.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/ethtool.h>
|
|
+#include <linux/if_vlan.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/io.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/module.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/of_net.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/of_platform.h>
|
|
|
|
#include <asm/checksum.h>
|
|
|
|
@@ -32,7 +37,7 @@
|
|
#include <xway_dma.h>
|
|
#include <lantiq_platform.h>
|
|
|
|
-#define LTQ_ETOP_MDIO 0x11804
|
|
+#define LTQ_ETOP_MDIO_ACC 0x11804
|
|
#define MDIO_REQUEST 0x80000000
|
|
#define MDIO_READ 0x40000000
|
|
#define MDIO_ADDR_MASK 0x1f
|
|
@@ -41,44 +46,91 @@
|
|
#define MDIO_REG_OFFSET 0x10
|
|
#define MDIO_VAL_MASK 0xffff
|
|
|
|
-#define PPE32_CGEN 0x800
|
|
-#define LQ_PPE32_ENET_MAC_CFG 0x1840
|
|
+#define LTQ_ETOP_MDIO_CFG 0x11800
|
|
+#define MDIO_CFG_MASK 0x6
|
|
+
|
|
+#define LTQ_ETOP_CFG 0x11808
|
|
+#define LTQ_ETOP_IGPLEN 0x11820
|
|
+#define LTQ_ETOP_MAC_CFG 0x11840
|
|
|
|
#define LTQ_ETOP_ENETS0 0x11850
|
|
#define LTQ_ETOP_MAC_DA0 0x1186C
|
|
#define LTQ_ETOP_MAC_DA1 0x11870
|
|
-#define LTQ_ETOP_CFG 0x16020
|
|
-#define LTQ_ETOP_IGPLEN 0x16080
|
|
+
|
|
+#define MAC_CFG_MASK 0xfff
|
|
+#define MAC_CFG_CGEN (1 << 11)
|
|
+#define MAC_CFG_DUPLEX (1 << 2)
|
|
+#define MAC_CFG_SPEED (1 << 1)
|
|
+#define MAC_CFG_LINK (1 << 0)
|
|
|
|
#define MAX_DMA_CHAN 0x8
|
|
#define MAX_DMA_CRC_LEN 0x4
|
|
#define MAX_DMA_DATA_LEN 0x600
|
|
|
|
#define ETOP_FTCU BIT(28)
|
|
-#define ETOP_MII_MASK 0xf
|
|
-#define ETOP_MII_NORMAL 0xd
|
|
-#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_ETOP_TX_CHANNEL 1
|
|
-#define LTQ_ETOP_RX_CHANNEL 6
|
|
-#define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL)
|
|
-#define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL)
|
|
+#define ETOP_CFG_MASK 0xfff
|
|
+#define ETOP_CFG_FEN0 (1 << 8)
|
|
+#define ETOP_CFG_SEN0 (1 << 6)
|
|
+#define ETOP_CFG_OFF1 (1 << 3)
|
|
+#define ETOP_CFG_REMII0 (1 << 1)
|
|
+#define ETOP_CFG_OFF0 (1 << 0)
|
|
+
|
|
+#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 LTQ_GBIT_RGMII_CTL 0x78
|
|
+
|
|
+#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
|
|
+
|
|
+/* MDC clock divider, clock = 25MHz/((MDC_CLOCK + 1) * 2) */
|
|
+#define MDC_CLOCK_MASK 0xff000000
|
|
+#define MDC_CLOCK_OFFSET 24
|
|
+
|
|
+/* 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
|
|
|
|
+#define LTQ_DMA_ETOP ((of_machine_is_compatible("lantiq,ase")) ? \
|
|
+ (INT_NUM_IM3_IRL0) : (INT_NUM_IM2_IRL0))
|
|
+
|
|
+/* the newer xway socks have a embedded 3/7 port gbit multiplexer */
|
|
#define ltq_etop_r32(x) ltq_r32(ltq_etop_membase + (x))
|
|
#define ltq_etop_w32(x, y) ltq_w32(x, ltq_etop_membase + (y))
|
|
#define ltq_etop_w32_mask(x, y, z) \
|
|
ltq_w32_mask(x, y, ltq_etop_membase + (z))
|
|
|
|
-#define DRV_VERSION "1.0"
|
|
+#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.2"
|
|
|
|
static void __iomem *ltq_etop_membase;
|
|
+static void __iomem *ltq_gbit_membase;
|
|
|
|
struct ltq_etop_chan {
|
|
- int idx;
|
|
int tx_free;
|
|
+ int irq;
|
|
struct net_device *netdev;
|
|
struct napi_struct napi;
|
|
struct ltq_dma_channel dma;
|
|
@@ -88,23 +140,36 @@ struct ltq_etop_chan {
|
|
struct ltq_etop_priv {
|
|
struct net_device *netdev;
|
|
struct platform_device *pdev;
|
|
- struct ltq_eth_data *pldata;
|
|
struct resource *res;
|
|
|
|
struct mii_bus *mii_bus;
|
|
|
|
- struct ltq_etop_chan ch[MAX_DMA_CHAN];
|
|
- int tx_free[MAX_DMA_CHAN >> 1];
|
|
+ struct ltq_etop_chan txch;
|
|
+ struct ltq_etop_chan rxch;
|
|
|
|
- spinlock_t lock;
|
|
+ int tx_irq;
|
|
+ int rx_irq;
|
|
+
|
|
+ unsigned char mac[6];
|
|
+ int mii_mode;
|
|
+
|
|
+ spinlock_t lock;
|
|
+
|
|
+ struct clk *clk_ppe;
|
|
+ struct clk *clk_switch;
|
|
+ struct clk *clk_ephy;
|
|
+ struct clk *clk_ephycgu;
|
|
};
|
|
|
|
+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)
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
|
|
|
|
- ch->skb[ch->dma.desc] = netdev_alloc_skb(ch->netdev, MAX_DMA_DATA_LEN);
|
|
+ ch->skb[ch->dma.desc] = dev_alloc_skb(MAX_DMA_DATA_LEN);
|
|
if (!ch->skb[ch->dma.desc])
|
|
return -ENOMEM;
|
|
ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(&priv->pdev->dev,
|
|
@@ -139,8 +204,11 @@ ltq_etop_hw_receive(struct ltq_etop_chan
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
skb_put(skb, len);
|
|
+ skb->dev = ch->netdev;
|
|
skb->protocol = eth_type_trans(skb, ch->netdev);
|
|
netif_receive_skb(skb);
|
|
+ ch->netdev->stats.rx_packets++;
|
|
+ ch->netdev->stats.rx_bytes += len;
|
|
}
|
|
|
|
static int
|
|
@@ -148,7 +216,9 @@ ltq_etop_poll_rx(struct napi_struct *nap
|
|
{
|
|
struct ltq_etop_chan *ch = container_of(napi,
|
|
struct ltq_etop_chan, napi);
|
|
+ struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
|
|
int work_done = 0;
|
|
+ unsigned long flags;
|
|
|
|
while (work_done < budget) {
|
|
struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc];
|
|
@@ -160,7 +230,9 @@ ltq_etop_poll_rx(struct napi_struct *nap
|
|
}
|
|
if (work_done < budget) {
|
|
napi_complete_done(&ch->napi, work_done);
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
ltq_dma_ack_irq(&ch->dma);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
}
|
|
return work_done;
|
|
}
|
|
@@ -172,12 +244,14 @@ ltq_etop_poll_tx(struct napi_struct *nap
|
|
container_of(napi, struct ltq_etop_chan, napi);
|
|
struct ltq_etop_priv *priv = netdev_priv(ch->netdev);
|
|
struct netdev_queue *txq =
|
|
- netdev_get_tx_queue(ch->netdev, ch->idx >> 1);
|
|
+ netdev_get_tx_queue(ch->netdev, ch->dma.nr >> 1);
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
while ((ch->dma.desc_base[ch->tx_free].ctl &
|
|
(LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) {
|
|
+ ch->netdev->stats.tx_packets++;
|
|
+ ch->netdev->stats.tx_bytes += ch->skb[ch->tx_free]->len;
|
|
dev_kfree_skb_any(ch->skb[ch->tx_free]);
|
|
ch->skb[ch->tx_free] = NULL;
|
|
memset(&ch->dma.desc_base[ch->tx_free], 0,
|
|
@@ -190,7 +264,9 @@ ltq_etop_poll_tx(struct napi_struct *nap
|
|
if (netif_tx_queue_stopped(txq))
|
|
netif_tx_start_queue(txq);
|
|
napi_complete(&ch->napi);
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
ltq_dma_ack_irq(&ch->dma);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
return 1;
|
|
}
|
|
|
|
@@ -198,9 +274,10 @@ static irqreturn_t
|
|
ltq_etop_dma_irq(int irq, void *_priv)
|
|
{
|
|
struct ltq_etop_priv *priv = _priv;
|
|
- int ch = irq - LTQ_DMA_CH0_INT;
|
|
-
|
|
- napi_schedule(&priv->ch[ch].napi);
|
|
+ if (irq == priv->txch.dma.irq)
|
|
+ napi_schedule(&priv->txch.napi);
|
|
+ else
|
|
+ napi_schedule(&priv->rxch.napi);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
@@ -212,7 +289,7 @@ ltq_etop_free_channel(struct net_device
|
|
ltq_dma_free(&ch->dma);
|
|
if (ch->dma.irq)
|
|
free_irq(ch->dma.irq, priv);
|
|
- if (IS_RX(ch->idx)) {
|
|
+ if (ch == &priv->txch) {
|
|
int desc;
|
|
for (desc = 0; desc < LTQ_DESC_NUM; desc++)
|
|
dev_kfree_skb_any(ch->skb[ch->dma.desc]);
|
|
@@ -223,66 +300,135 @@ static void
|
|
ltq_etop_hw_exit(struct net_device *dev)
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
- int i;
|
|
|
|
- ltq_pmu_disable(PMU_PPE);
|
|
- for (i = 0; i < MAX_DMA_CHAN; i++)
|
|
- if (IS_TX(i) || IS_RX(i))
|
|
- ltq_etop_free_channel(dev, &priv->ch[i]);
|
|
+ clk_disable(priv->clk_ppe);
|
|
+
|
|
+ if (of_machine_is_compatible("lantiq,ar9"))
|
|
+ clk_disable(priv->clk_switch);
|
|
+
|
|
+ if (of_machine_is_compatible("lantiq,ase")) {
|
|
+ clk_disable(priv->clk_ephy);
|
|
+ clk_disable(priv->clk_ephycgu);
|
|
+ }
|
|
+
|
|
+ ltq_etop_free_channel(dev, &priv->txch);
|
|
+ ltq_etop_free_channel(dev, &priv->rxch);
|
|
+}
|
|
+
|
|
+static void
|
|
+ltq_etop_gbit_init(struct net_device *dev)
|
|
+{
|
|
+ struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ clk_enable(priv->clk_switch);
|
|
+
|
|
+ /* enable gbit port0 on the SoC */
|
|
+ ltq_gbit_w32_mask((1 << 17), (1 << 18), LTQ_GBIT_P0_CTL);
|
|
+
|
|
+ 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);
|
|
+ /* set mdc clock to 2.5 MHz */
|
|
+ ltq_gbit_w32_mask(MDC_CLOCK_MASK, 4 << MDC_CLOCK_OFFSET,
|
|
+ LTQ_GBIT_RGMII_CTL);
|
|
}
|
|
|
|
static int
|
|
ltq_etop_hw_init(struct net_device *dev)
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
- int i;
|
|
+ int mii_mode = priv->mii_mode;
|
|
|
|
- ltq_pmu_enable(PMU_PPE);
|
|
+ clk_enable(priv->clk_ppe);
|
|
|
|
- switch (priv->pldata->mii_mode) {
|
|
+ if (of_machine_is_compatible("lantiq,ar9")) {
|
|
+ ltq_etop_gbit_init(dev);
|
|
+ /* force the etops link to the gbit to MII */
|
|
+ mii_mode = PHY_INTERFACE_MODE_MII;
|
|
+ }
|
|
+ ltq_etop_w32_mask(MDIO_CFG_MASK, 0, LTQ_ETOP_MDIO_CFG);
|
|
+ ltq_etop_w32_mask(MAC_CFG_MASK, MAC_CFG_CGEN | MAC_CFG_DUPLEX |
|
|
+ MAC_CFG_SPEED | MAC_CFG_LINK, LTQ_ETOP_MAC_CFG);
|
|
+
|
|
+ switch (mii_mode) {
|
|
case PHY_INTERFACE_MODE_RMII:
|
|
- ltq_etop_w32_mask(ETOP_MII_MASK,
|
|
- ETOP_MII_REVERSE, LTQ_ETOP_CFG);
|
|
+ ltq_etop_w32_mask(ETOP_CFG_MASK, ETOP_CFG_REMII0 | ETOP_CFG_OFF1 |
|
|
+ ETOP_CFG_SEN0 | ETOP_CFG_FEN0, LTQ_ETOP_CFG);
|
|
break;
|
|
|
|
case PHY_INTERFACE_MODE_MII:
|
|
- ltq_etop_w32_mask(ETOP_MII_MASK,
|
|
- ETOP_MII_NORMAL, LTQ_ETOP_CFG);
|
|
+ ltq_etop_w32_mask(ETOP_CFG_MASK, ETOP_CFG_OFF1 |
|
|
+ ETOP_CFG_SEN0 | ETOP_CFG_FEN0, LTQ_ETOP_CFG);
|
|
break;
|
|
|
|
default:
|
|
+ if (of_machine_is_compatible("lantiq,ase")) {
|
|
+ clk_enable(priv->clk_ephy);
|
|
+ /* disable external MII */
|
|
+ ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG);
|
|
+ /* enable clock for internal PHY */
|
|
+ clk_enable(priv->clk_ephycgu);
|
|
+ /* 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);
|
|
+ mii_mode);
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
- /* enable crc generation */
|
|
- ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+ltq_etop_dma_init(struct net_device *dev)
|
|
+{
|
|
+ struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
+ int tx = priv->tx_irq - LTQ_DMA_ETOP;
|
|
+ int rx = priv->rx_irq - LTQ_DMA_ETOP;
|
|
+ int err;
|
|
|
|
ltq_dma_init_port(DMA_PORT_ETOP);
|
|
|
|
- for (i = 0; i < MAX_DMA_CHAN; i++) {
|
|
- int irq = LTQ_DMA_CH0_INT + i;
|
|
- struct ltq_etop_chan *ch = &priv->ch[i];
|
|
-
|
|
- ch->idx = ch->dma.nr = i;
|
|
- ch->dma.dev = &priv->pdev->dev;
|
|
-
|
|
- if (IS_TX(i)) {
|
|
- ltq_dma_alloc_tx(&ch->dma);
|
|
- request_irq(irq, ltq_etop_dma_irq, 0, "etop_tx", priv);
|
|
- } else if (IS_RX(i)) {
|
|
- ltq_dma_alloc_rx(&ch->dma);
|
|
- for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM;
|
|
- ch->dma.desc++)
|
|
- if (ltq_etop_alloc_skb(ch))
|
|
- return -ENOMEM;
|
|
- ch->dma.desc = 0;
|
|
- request_irq(irq, ltq_etop_dma_irq, 0, "etop_rx", priv);
|
|
+ priv->txch.dma.nr = tx;
|
|
+ priv->txch.dma.dev = &priv->pdev->dev;
|
|
+ ltq_dma_alloc_tx(&priv->txch.dma);
|
|
+ err = request_irq(priv->tx_irq, ltq_etop_dma_irq, 0, "eth_tx", priv);
|
|
+ if (err) {
|
|
+ netdev_err(dev, "failed to allocate tx irq\n");
|
|
+ goto err_out;
|
|
+ }
|
|
+ priv->txch.dma.irq = priv->tx_irq;
|
|
+
|
|
+ priv->rxch.dma.nr = rx;
|
|
+ priv->rxch.dma.dev = &priv->pdev->dev;
|
|
+ ltq_dma_alloc_rx(&priv->rxch.dma);
|
|
+ for (priv->rxch.dma.desc = 0; priv->rxch.dma.desc < LTQ_DESC_NUM;
|
|
+ priv->rxch.dma.desc++) {
|
|
+ if (ltq_etop_alloc_skb(&priv->rxch)) {
|
|
+ netdev_err(dev, "failed to allocate skbs\n");
|
|
+ err = -ENOMEM;
|
|
+ goto err_out;
|
|
}
|
|
- ch->dma.irq = irq;
|
|
}
|
|
- return 0;
|
|
+ priv->rxch.dma.desc = 0;
|
|
+ err = request_irq(priv->rx_irq, ltq_etop_dma_irq, 0, "eth_rx", priv);
|
|
+ if (err)
|
|
+ netdev_err(dev, "failed to allocate rx irq\n");
|
|
+ else
|
|
+ priv->rxch.dma.irq = priv->rx_irq;
|
|
+err_out:
|
|
+ return err;
|
|
}
|
|
|
|
static void
|
|
@@ -301,6 +447,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 |
|
|
@@ -308,9 +487,9 @@ ltq_etop_mdio_wr(struct mii_bus *bus, in
|
|
((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET) |
|
|
phy_data;
|
|
|
|
- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
|
|
+ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST)
|
|
;
|
|
- ltq_etop_w32(val, LTQ_ETOP_MDIO);
|
|
+ ltq_etop_w32(val, LTQ_ETOP_MDIO_ACC);
|
|
return 0;
|
|
}
|
|
|
|
@@ -321,12 +500,12 @@ ltq_etop_mdio_rd(struct mii_bus *bus, in
|
|
((phy_addr & MDIO_ADDR_MASK) << MDIO_ADDR_OFFSET) |
|
|
((phy_reg & MDIO_REG_MASK) << MDIO_REG_OFFSET);
|
|
|
|
- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
|
|
+ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST)
|
|
;
|
|
- ltq_etop_w32(val, LTQ_ETOP_MDIO);
|
|
- while (ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_REQUEST)
|
|
+ ltq_etop_w32(val, LTQ_ETOP_MDIO_ACC);
|
|
+ while (ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_REQUEST)
|
|
;
|
|
- val = ltq_etop_r32(LTQ_ETOP_MDIO) & MDIO_VAL_MASK;
|
|
+ val = ltq_etop_r32(LTQ_ETOP_MDIO_ACC) & MDIO_VAL_MASK;
|
|
return val;
|
|
}
|
|
|
|
@@ -342,7 +521,10 @@ ltq_etop_mdio_probe(struct net_device *d
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
struct phy_device *phydev;
|
|
|
|
- phydev = phy_find_first(priv->mii_bus);
|
|
+ if (of_machine_is_compatible("lantiq,ase"))
|
|
+ phydev = mdiobus_get_phy(priv->mii_bus, 8);
|
|
+ else
|
|
+ phydev = mdiobus_get_phy(priv->mii_bus, 0);
|
|
|
|
if (!phydev) {
|
|
netdev_err(dev, "no PHY found\n");
|
|
@@ -350,14 +532,17 @@ ltq_etop_mdio_probe(struct net_device *d
|
|
}
|
|
|
|
phydev = phy_connect(dev, phydev_name(phydev),
|
|
- <q_etop_mdio_link, priv->pldata->mii_mode);
|
|
+ <q_etop_mdio_link, priv->mii_mode);
|
|
|
|
if (IS_ERR(phydev)) {
|
|
netdev_err(dev, "Could not attach to PHY\n");
|
|
return PTR_ERR(phydev);
|
|
}
|
|
|
|
- phy_set_max_speed(phydev, SPEED_100);
|
|
+ if (of_machine_is_compatible("lantiq,ar9"))
|
|
+ phy_set_max_speed(phydev, SPEED_1000);
|
|
+ else
|
|
+ phy_set_max_speed(phydev, SPEED_100);
|
|
|
|
phy_attached_info(phydev);
|
|
|
|
@@ -378,8 +563,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 (of_machine_is_compatible("lantiq,ar9")) {
|
|
+ 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);
|
|
@@ -416,18 +606,21 @@ static int
|
|
ltq_etop_open(struct net_device *dev)
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
- int i;
|
|
+ unsigned long flags;
|
|
|
|
- for (i = 0; i < MAX_DMA_CHAN; i++) {
|
|
- struct ltq_etop_chan *ch = &priv->ch[i];
|
|
+ napi_enable(&priv->txch.napi);
|
|
+ napi_enable(&priv->rxch.napi);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ ltq_dma_open(&priv->txch.dma);
|
|
+ ltq_dma_enable_irq(&priv->txch.dma);
|
|
+ ltq_dma_open(&priv->rxch.dma);
|
|
+ ltq_dma_enable_irq(&priv->rxch.dma);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
+ if (dev->phydev)
|
|
+ phy_start(dev->phydev);
|
|
|
|
- if (!IS_TX(i) && (!IS_RX(i)))
|
|
- continue;
|
|
- ltq_dma_open(&ch->dma);
|
|
- ltq_dma_enable_irq(&ch->dma);
|
|
- napi_enable(&ch->napi);
|
|
- }
|
|
- phy_start(dev->phydev);
|
|
netif_tx_start_all_queues(dev);
|
|
return 0;
|
|
}
|
|
@@ -436,18 +629,19 @@ static int
|
|
ltq_etop_stop(struct net_device *dev)
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
- int i;
|
|
+ unsigned long flags;
|
|
|
|
netif_tx_stop_all_queues(dev);
|
|
- phy_stop(dev->phydev);
|
|
- for (i = 0; i < MAX_DMA_CHAN; i++) {
|
|
- struct ltq_etop_chan *ch = &priv->ch[i];
|
|
-
|
|
- if (!IS_RX(i) && !IS_TX(i))
|
|
- continue;
|
|
- napi_disable(&ch->napi);
|
|
- ltq_dma_close(&ch->dma);
|
|
- }
|
|
+ if (dev->phydev)
|
|
+ phy_stop(dev->phydev);
|
|
+ napi_disable(&priv->txch.napi);
|
|
+ napi_disable(&priv->rxch.napi);
|
|
+
|
|
+ spin_lock_irqsave(&priv->lock, flags);
|
|
+ ltq_dma_close(&priv->txch.dma);
|
|
+ ltq_dma_close(&priv->rxch.dma);
|
|
+ spin_unlock_irqrestore(&priv->lock, flags);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -457,16 +651,16 @@ ltq_etop_tx(struct sk_buff *skb, struct
|
|
int queue = skb_get_queue_mapping(skb);
|
|
struct netdev_queue *txq = netdev_get_tx_queue(dev, queue);
|
|
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;
|
|
+ struct ltq_dma_desc *desc =
|
|
+ &priv->txch.dma.desc_base[priv->txch.dma.desc];
|
|
unsigned long flags;
|
|
u32 byte_offset;
|
|
+ int len;
|
|
|
|
len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
|
|
|
|
- if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) {
|
|
- dev_kfree_skb_any(skb);
|
|
+ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) ||
|
|
+ priv->txch.skb[priv->txch.dma.desc]) {
|
|
netdev_err(dev, "tx ring full\n");
|
|
netif_tx_stop_queue(txq);
|
|
return NETDEV_TX_BUSY;
|
|
@@ -474,7 +668,7 @@ ltq_etop_tx(struct sk_buff *skb, struct
|
|
|
|
/* dma needs to start on a 16 byte aligned address */
|
|
byte_offset = CPHYSADDR(skb->data) % 16;
|
|
- ch->skb[ch->dma.desc] = skb;
|
|
+ priv->txch.skb[priv->txch.dma.desc] = skb;
|
|
|
|
netif_trans_update(dev);
|
|
|
|
@@ -484,11 +678,11 @@ ltq_etop_tx(struct sk_buff *skb, struct
|
|
wmb();
|
|
desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP |
|
|
LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK);
|
|
- ch->dma.desc++;
|
|
- ch->dma.desc %= LTQ_DESC_NUM;
|
|
+ priv->txch.dma.desc++;
|
|
+ priv->txch.dma.desc %= LTQ_DESC_NUM;
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
- if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN)
|
|
+ if (priv->txch.dma.desc_base[priv->txch.dma.desc].ctl & LTQ_DMA_OWN)
|
|
netif_tx_stop_queue(txq);
|
|
|
|
return NETDEV_TX_OK;
|
|
@@ -499,11 +693,14 @@ ltq_etop_change_mtu(struct net_device *d
|
|
{
|
|
struct ltq_etop_priv *priv = netdev_priv(dev);
|
|
unsigned long flags;
|
|
+ int max;
|
|
|
|
dev->mtu = new_mtu;
|
|
|
|
+ max = ETH_HLEN + VLAN_HLEN + new_mtu + ETH_FCS_LEN;
|
|
+
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
- ltq_etop_w32((ETOP_PLEN_UNDER << 16) | new_mtu, LTQ_ETOP_IGPLEN);
|
|
+ ltq_etop_w32((ETOP_PLEN_UNDER << 16) | max, LTQ_ETOP_IGPLEN);
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
return 0;
|
|
@@ -563,6 +760,9 @@ ltq_etop_init(struct net_device *dev)
|
|
if (err)
|
|
goto err_hw;
|
|
ltq_etop_change_mtu(dev, 1500);
|
|
+ err = ltq_etop_dma_init(dev);
|
|
+ if (err)
|
|
+ goto err_hw;
|
|
|
|
memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr));
|
|
if (!is_valid_ether_addr(mac.sa_data)) {
|
|
@@ -580,9 +780,10 @@ ltq_etop_init(struct net_device *dev)
|
|
dev->addr_assign_type = NET_ADDR_RANDOM;
|
|
|
|
ltq_etop_set_multicast_list(dev);
|
|
- err = ltq_etop_mdio_init(dev);
|
|
- if (err)
|
|
- goto err_netdev;
|
|
+ if (!ltq_etop_mdio_init(dev))
|
|
+ dev->ethtool_ops = <q_etop_ethtool_ops;
|
|
+ else
|
|
+ pr_warn("etop: mdio probe failed\n");;
|
|
return 0;
|
|
|
|
err_netdev:
|
|
@@ -602,6 +803,9 @@ ltq_etop_tx_timeout(struct net_device *d
|
|
err = ltq_etop_hw_init(dev);
|
|
if (err)
|
|
goto err_hw;
|
|
+ err = ltq_etop_dma_init(dev);
|
|
+ if (err)
|
|
+ goto err_hw;
|
|
netif_trans_update(dev);
|
|
netif_wake_queue(dev);
|
|
return;
|
|
@@ -625,14 +829,19 @@ static const struct net_device_ops ltq_e
|
|
.ndo_tx_timeout = ltq_etop_tx_timeout,
|
|
};
|
|
|
|
-static int __init
|
|
-ltq_etop_probe(struct platform_device *pdev)
|
|
+static int ltq_etop_probe(struct platform_device *pdev)
|
|
{
|
|
struct net_device *dev;
|
|
struct ltq_etop_priv *priv;
|
|
- struct resource *res;
|
|
+ struct resource *res, *gbit_res, irqres[2];
|
|
+ const u8 *mac;
|
|
int err;
|
|
- int i;
|
|
+
|
|
+ err = of_irq_to_resource_table(pdev->dev.of_node, irqres, 2);
|
|
+ if (err != 2) {
|
|
+ dev_err(&pdev->dev, "failed to get etop irqs\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
@@ -658,31 +867,62 @@ ltq_etop_probe(struct platform_device *p
|
|
goto err_out;
|
|
}
|
|
|
|
- dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4);
|
|
- if (!dev) {
|
|
- err = -ENOMEM;
|
|
- goto err_out;
|
|
+ if (of_machine_is_compatible("lantiq,ar9")) {
|
|
+ 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;
|
|
- dev->ethtool_ops = <q_etop_ethtool_ops;
|
|
priv = netdev_priv(dev);
|
|
priv->res = res;
|
|
priv->pdev = pdev;
|
|
- priv->pldata = dev_get_platdata(&pdev->dev);
|
|
priv->netdev = dev;
|
|
+ priv->tx_irq = irqres[0].start;
|
|
+ priv->rx_irq = irqres[1].start;
|
|
+ priv->mii_mode = of_get_phy_mode(pdev->dev.of_node);
|
|
+
|
|
+ mac = of_get_mac_address(pdev->dev.of_node);
|
|
+ if (mac)
|
|
+ memcpy(priv->mac, mac, ETH_ALEN);
|
|
+
|
|
+ priv->clk_ppe = clk_get(&pdev->dev, NULL);
|
|
+ if (IS_ERR(priv->clk_ppe))
|
|
+ return PTR_ERR(priv->clk_ppe);
|
|
+ if (of_machine_is_compatible("lantiq,ar9")) {
|
|
+ priv->clk_switch = clk_get(&pdev->dev, "switch");
|
|
+ if (IS_ERR(priv->clk_switch))
|
|
+ return PTR_ERR(priv->clk_switch);
|
|
+ }
|
|
+ if (of_machine_is_compatible("lantiq,ase")) {
|
|
+ priv->clk_ephy = clk_get(&pdev->dev, "ephy");
|
|
+ if (IS_ERR(priv->clk_ephy))
|
|
+ return PTR_ERR(priv->clk_ephy);
|
|
+ priv->clk_ephycgu = clk_get(&pdev->dev, "ephycgu");
|
|
+ if (IS_ERR(priv->clk_ephycgu))
|
|
+ return PTR_ERR(priv->clk_ephycgu);
|
|
+ }
|
|
+
|
|
spin_lock_init(&priv->lock);
|
|
SET_NETDEV_DEV(dev, &pdev->dev);
|
|
|
|
- for (i = 0; i < MAX_DMA_CHAN; i++) {
|
|
- if (IS_TX(i))
|
|
- netif_napi_add(dev, &priv->ch[i].napi,
|
|
- ltq_etop_poll_tx, 8);
|
|
- else if (IS_RX(i))
|
|
- netif_napi_add(dev, &priv->ch[i].napi,
|
|
- ltq_etop_poll_rx, 32);
|
|
- priv->ch[i].netdev = dev;
|
|
- }
|
|
+ netif_napi_add(dev, &priv->txch.napi, ltq_etop_poll_tx, 8);
|
|
+ netif_napi_add(dev, &priv->rxch.napi, ltq_etop_poll_rx, 32);
|
|
+ priv->txch.netdev = dev;
|
|
+ priv->rxch.netdev = dev;
|
|
|
|
err = register_netdev(dev);
|
|
if (err)
|
|
@@ -711,31 +951,22 @@ ltq_etop_remove(struct platform_device *
|
|
return 0;
|
|
}
|
|
|
|
+static const struct of_device_id ltq_etop_match[] = {
|
|
+ { .compatible = "lantiq,etop-xway" },
|
|
+ {},
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ltq_etop_match);
|
|
+
|
|
static struct platform_driver ltq_mii_driver = {
|
|
+ .probe = ltq_etop_probe,
|
|
.remove = ltq_etop_remove,
|
|
.driver = {
|
|
.name = "ltq_etop",
|
|
+ .of_match_table = ltq_etop_match,
|
|
},
|
|
};
|
|
|
|
-int __init
|
|
-init_ltq_etop(void)
|
|
-{
|
|
- int ret = platform_driver_probe(<q_mii_driver, ltq_etop_probe);
|
|
-
|
|
- if (ret)
|
|
- pr_err("ltq_etop: Error registering platform driver!");
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void __exit
|
|
-exit_ltq_etop(void)
|
|
-{
|
|
- platform_driver_unregister(<q_mii_driver);
|
|
-}
|
|
-
|
|
-module_init(init_ltq_etop);
|
|
-module_exit(exit_ltq_etop);
|
|
+module_platform_driver(ltq_mii_driver);
|
|
|
|
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
|
|
MODULE_DESCRIPTION("Lantiq SoC ETOP");
|