[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20210622144111.19647-4-lukma@denx.de>
Date: Tue, 22 Jun 2021 16:41:11 +0200
From: Lukasz Majewski <lukma@...x.de>
To: "David S . Miller" <davem@...emloft.net>,
Jakub Kicinski <kuba@...nel.org>,
Madalin Bucur <madalin.bucur@....nxp.com>,
Nicolas Ferre <nicolas.ferre@...rochip.com>,
Joakim Zhang <qiangqing.zhang@....com>,
Andrew Lunn <andrew@...n.ch>,
Florian Fainelli <f.fainelli@...il.com>,
Vladimir Oltean <olteanv@...il.com>
Cc: netdev@...r.kernel.org, Arnd Bergmann <arnd@...db.de>,
Mark Einon <mark.einon@...il.com>,
NXP Linux Team <linux-imx@....com>,
linux-kernel@...ts.infradead.org, Lukasz Majewski <lukma@...x.de>
Subject: [RFC 3/3] net: imx: Adjust fec_main.c to provide support for L2 switch
This patch provides the code for re-using generic FEC i.MX code for
More Than IP L2 switch.
The trick here is to reconfigure FEC driver to use DMA0 as the one
connected to switch's port 0. Port 1 and 2 are then connected to
ENET-MAC.
The internal connection diagram can be found here [0].
This code has been developed on i.MX287 device, but this switch (from
More ThanIP) can be also found on e.g. NXP's Vybrid VF610 SoC.
To reuse as much code as possible it was also necessary to introduce
fec_hwp() wrapper, which either returns pointer to MAC or L2 switch
interrupt and buffer descriptors.
Following registers shall be used with it:
FEC_{IEVENT|IMASK}, FEC_MTIP_R_DES_ACTIVE_0, FEC_MTIP_X_DES_ACTIVE_0,
FEC_MTIP_R_DES_START_0, FEC_MTIP_X_DES_START_0
This driver introduces special wrappers to map on fly DMA0 to either
ENET-MAC0 or L2 switch port 0.
The most intrusive changes when L2 switch is used:
- Disable RACC (Receive Data Accelerator)
When this feature is enabled the data is shifted by 16 bits and
hence received packets are malformed when we try to pass data from
one switch port to another.
- L2 switch shall pass the whole pkt_len packet. Current FEC code
subtract 4 to remove FCS
The control of FEC clock and promisc is handled by bridge driver, so
there is no need for adjustments.
This driver forward ports the legacy driver [1][2] into contemporary
Linux.
Last but not least - some functions had to be exported to allow proper
configuration of ethernet interfaces when bridging was enabled/disabled.
Links:
[0] - Chapter 29.3.1 in
"i.MX28 Applications Processor Reference Manual, Rev. 2, 08/2013"
[1]
>From 2.6 i.MX kernel:
Repo: git://git.freescale.com/imx/linux-2.6-imx.git
Branch: imx_2.6.35_maintain
SHA1: b3912bb8a4caf3ec50909135e88af959982c43ca
[2] - https://github.com/lmajewski/linux-imx28-l2switch/commits/master
Signed-off-by: Lukasz Majewski <lukma@...x.de>
---
drivers/net/ethernet/freescale/fec.h | 36 ++++++
drivers/net/ethernet/freescale/fec_main.c | 139 ++++++++++++++++++----
2 files changed, 151 insertions(+), 24 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 0602d5d5d2ee..dc2d31321cbd 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -29,6 +29,10 @@
*/
#define FEC_IEVENT 0x004 /* Interrupt event reg */
#define FEC_IMASK 0x008 /* Interrupt mask reg */
+#ifdef CONFIG_FEC_MTIP_L2SW
+#define FEC_MTIP_R_DES_ACTIVE_0 0x018 /* L2 switch Receive descriptor reg */
+#define FEC_MTIP_X_DES_ACTIVE_0 0x01C /* L2 switch Transmit descriptor reg */
+#endif
#define FEC_R_DES_ACTIVE_0 0x010 /* Receive descriptor reg */
#define FEC_X_DES_ACTIVE_0 0x014 /* Transmit descriptor reg */
#define FEC_ECNTRL 0x024 /* Ethernet control reg */
@@ -59,6 +63,10 @@
#define FEC_R_DES_START_2 0x16c /* Receive descriptor ring 2 */
#define FEC_X_DES_START_2 0x170 /* Transmit descriptor ring 2 */
#define FEC_R_BUFF_SIZE_2 0x174 /* Maximum receive buff ring2 size */
+#ifdef CONFIG_FEC_MTIP_L2SW
+#define FEC_MTIP_R_DES_START_0 0x0C /* L2 switch Receive descriptor ring */
+#define FEC_MTIP_X_DES_START_0 0x10 /* L2 switch Transmit descriptor ring */
+#endif
#define FEC_R_DES_START_0 0x180 /* Receive descriptor ring */
#define FEC_X_DES_START_0 0x184 /* Transmit descriptor ring */
#define FEC_R_BUFF_SIZE_0 0x188 /* Maximum receive buff size */
@@ -376,6 +384,12 @@ struct bufdesc_ex {
#define FEC_ENET_TS_AVAIL ((uint)0x00010000)
#define FEC_ENET_TS_TIMER ((uint)0x00008000)
+#ifdef CONFIG_FEC_MTIP_L2SW
+#define FEC_MTIP_ENET_TXF ((uint)0x00000010) /* Full frame transmitted */
+#define FEC_MTIP_ENET_RXF ((uint)0x00000004) /* Full frame received */
+#define FEC_MTIP_DEFAULT_IMASK (FEC_MTIP_ENET_TXF | FEC_MTIP_ENET_RXF)
+#define FEC_MTIP_RX_DISABLED_IMASK (FEC_MTIP_DEFAULT_IMASK & (~FEC_MTIP_ENET_RXF))
+#endif
#define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF)
#define FEC_RX_DISABLED_IMASK (FEC_DEFAULT_IMASK & (~FEC_ENET_RXF))
@@ -595,9 +609,31 @@ struct fec_enet_private {
int pps_enable;
unsigned int next_counter;
+#ifdef CONFIG_FEC_MTIP_L2SW
+ /* More Than IP L2 switch */
+ void __iomem *hwpsw;
+ struct mtipl2sw_priv *mtipl2;
+ bool mtip_l2sw;
+#endif
u64 ethtool_stats[];
};
+/* MTIP L2 switch */
+/* Get proper base address for either L2 switch or MAC ENET */
+static inline void __iomem *fec_hwp(struct fec_enet_private *fep)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return fep->hwpsw;
+#endif
+ return fep->hwp;
+}
+
+int fec_enet_close(struct net_device *ndev);
+int fec_enet_open(struct net_device *ndev);
+const unsigned short fec_offset_des_active_rxq(struct fec_enet_private *, int);
+const unsigned short fec_offset_des_active_txq(struct fec_enet_private *, int);
+struct fec_enet_private *fec_get_priv(const struct net_device *ndev);
void fec_ptp_init(struct platform_device *pdev, int irq_idx);
void fec_ptp_stop(struct platform_device *pdev);
void fec_ptp_start_cyclecounter(struct net_device *ndev);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 3db882322b2b..797ed7e443ee 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -69,6 +69,9 @@
#include <asm/cacheflush.h>
#include "fec.h"
+#ifdef CONFIG_FEC_MTIP_L2SW
+#include "./mtipsw/fec_mtip.h"
+#endif
static void set_multicast_list(struct net_device *ndev);
static void fec_enet_itr_coal_init(struct net_device *ndev);
@@ -99,9 +102,11 @@ static const struct fec_devinfo fec_imx27_info = {
static const struct fec_devinfo fec_imx28_info = {
.quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
- FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
- FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII |
- FEC_QUIRK_NO_HARD_RESET,
+ FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_FRREG |
+#ifndef CONFIG_FEC_MTIP_L2SW
+ FEC_QUIRK_HAS_RACC |
+#endif
+ FEC_QUIRK_CLEAR_SETUP_MII | FEC_QUIRK_NO_HARD_RESET,
};
static const struct fec_devinfo fec_imx6q_info = {
@@ -278,6 +283,46 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
static int mii_cnt;
+static inline const unsigned short
+offset_des_start_rxq(struct fec_enet_private *fep, int i)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_R_DES_START_0;
+#endif
+ return FEC_R_DES_START(i);
+}
+
+static inline const u32
+fec_rx_disabled_imask(struct fec_enet_private *fep)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_RX_DISABLED_IMASK;
+#endif
+ return FEC_RX_DISABLED_IMASK;
+}
+
+static inline const u32
+fec_default_imask(struct fec_enet_private *fep)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_DEFAULT_IMASK;
+#endif
+ return FEC_DEFAULT_IMASK;
+}
+
+static inline const unsigned short
+offset_des_start_txq(struct fec_enet_private *fep, int i)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_X_DES_START_0;
+#endif
+ return FEC_X_DES_START(i);
+}
+
static struct bufdesc *fec_enet_get_nextdesc(struct bufdesc *bdp,
struct bufdesc_prop *bd)
{
@@ -898,8 +943,9 @@ static void fec_enet_enable_ring(struct net_device *ndev)
for (i = 0; i < fep->num_rx_queues; i++) {
rxq = fep->rx_queue[i];
- writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
- writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
+ writel(rxq->bd.dma, fec_hwp(fep) +
+ offset_des_start_rxq(fep, i));
+ writel(PKT_MAXBUF_SIZE, fec_hwp(fep) + FEC_R_BUFF_SIZE(i));
/* enable DMA1/2 */
if (i)
@@ -909,8 +955,8 @@ static void fec_enet_enable_ring(struct net_device *ndev)
for (i = 0; i < fep->num_tx_queues; i++) {
txq = fep->tx_queue[i];
- writel(txq->bd.dma, fep->hwp + FEC_X_DES_START(i));
-
+ writel(txq->bd.dma, fec_hwp(fep) +
+ offset_des_start_txq(fep, i));
/* enable DMA1/2 */
if (i)
writel(DMA_CLASS_EN | IDLE_SLOPE(i),
@@ -1117,9 +1163,9 @@ fec_restart(struct net_device *ndev)
/* Enable interrupts we wish to service */
if (fep->link)
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK);
else
- writel(0, fep->hwp + FEC_IMASK);
+ writel(0, fec_hwp(fep) + FEC_IMASK);
/* Init the interrupt coalescing */
fec_enet_itr_coal_init(ndev);
@@ -1170,9 +1216,10 @@ fec_stop(struct net_device *ndev)
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
}
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK);
} else {
- writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
+ writel(fec_default_imask(fep) | FEC_ENET_WAKEUP,
+ fec_hwp(fep) + FEC_IMASK);
val = readl(fep->hwp + FEC_ECNTRL);
val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);
@@ -1413,7 +1460,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
unsigned short status;
struct sk_buff *skb_new = NULL;
struct sk_buff *skb;
- ushort pkt_len;
+ ushort pkt_len, l2pl;
__u8 *data;
int pkt_received = 0;
struct bufdesc_ex *ebdp = NULL;
@@ -1439,7 +1486,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
break;
pkt_received++;
- writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT);
+ writel(FEC_ENET_RXF, fec_hwp(fep) + FEC_IEVENT);
/* Check for errors. */
status ^= BD_ENET_RX_LAST;
@@ -1479,7 +1526,17 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
* include that when passing upstream as it messes up
* bridging applications.
*/
- is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4,
+ /*
+ * When the L2 switch support is enabled the FCS is removed
+ * by it and hence the pkt_len shall be passed without
+ * substracting 4 bytes.
+ */
+ l2pl = pkt_len - 4;
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ l2pl = pkt_len;
+#endif
+ is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, l2pl,
need_swap);
if (!is_copybreak) {
skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
@@ -1494,7 +1551,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
}
prefetch(skb->data - NET_IP_ALIGN);
- skb_put(skb, pkt_len - 4);
+ skb_put(skb, l2pl);
data = skb->data;
if (!is_copybreak && need_swap)
@@ -1654,7 +1711,7 @@ static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
if (done < budget) {
napi_complete_done(napi, done);
- writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+ writel(fec_default_imask(fep), fec_hwp(fep) + FEC_IMASK);
}
return done;
@@ -2971,7 +3028,7 @@ static int fec_enet_alloc_buffers(struct net_device *ndev)
return 0;
}
-static int
+int
fec_enet_open(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
@@ -3042,8 +3099,9 @@ fec_enet_open(struct net_device *ndev)
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
return ret;
}
+EXPORT_SYMBOL_GPL(fec_enet_open)
-static int
+int
fec_enet_close(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
@@ -3072,6 +3130,7 @@ fec_enet_close(struct net_device *ndev)
return 0;
}
+EXPORT_SYMBOL_GPL(fec_enet_close);
/* Set or clear the multicast filter for this adaptor.
* Skeleton taken from sunlance driver.
@@ -3240,14 +3299,45 @@ static const struct net_device_ops fec_netdev_ops = {
.ndo_set_features = fec_set_features,
};
-static const unsigned short offset_des_active_rxq[] = {
+#ifdef CONFIG_FEC_MTIP_L2SW
+struct fec_enet_private *fec_get_priv(const struct net_device *ndev)
+{
+ if (ndev->netdev_ops == &fec_netdev_ops)
+ return netdev_priv(ndev);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fec_get_priv)
+#endif
+
+static const unsigned short __offset_des_active_rxq[] = {
FEC_R_DES_ACTIVE_0, FEC_R_DES_ACTIVE_1, FEC_R_DES_ACTIVE_2
};
-static const unsigned short offset_des_active_txq[] = {
+static const unsigned short __offset_des_active_txq[] = {
FEC_X_DES_ACTIVE_0, FEC_X_DES_ACTIVE_1, FEC_X_DES_ACTIVE_2
};
+const unsigned short
+fec_offset_des_active_rxq(struct fec_enet_private *fep, int i)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_R_DES_ACTIVE_0;
+#endif
+ return __offset_des_active_rxq[i];
+}
+
+const unsigned short
+fec_offset_des_active_txq(struct fec_enet_private *fep, int i)
+{
+#ifdef CONFIG_FEC_MTIP_L2SW
+ if (fep->mtip_l2sw)
+ return FEC_MTIP_X_DES_ACTIVE_0;
+#endif
+ return __offset_des_active_txq[i];
+}
+
/*
* XXX: We need to clean up on failure exits here.
*
@@ -3307,7 +3397,8 @@ static int fec_enet_init(struct net_device *ndev)
rxq->bd.dma = bd_dma;
rxq->bd.dsize = dsize;
rxq->bd.dsize_log2 = dsize_log2;
- rxq->bd.reg_desc_active = fep->hwp + offset_des_active_rxq[i];
+ rxq->bd.reg_desc_active =
+ fec_hwp(fep) + fec_offset_des_active_rxq(fep, i);
bd_dma += size;
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
rxq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
@@ -3323,7 +3414,8 @@ static int fec_enet_init(struct net_device *ndev)
txq->bd.dma = bd_dma;
txq->bd.dsize = dsize;
txq->bd.dsize_log2 = dsize_log2;
- txq->bd.reg_desc_active = fep->hwp + offset_des_active_txq[i];
+ txq->bd.reg_desc_active =
+ fec_hwp(fep) + fec_offset_des_active_txq(fep, i);
bd_dma += size;
cbd_base = (struct bufdesc *)(((void *)cbd_base) + size);
txq->bd.last = (struct bufdesc *)(((void *)cbd_base) - dsize);
@@ -3334,8 +3426,7 @@ static int fec_enet_init(struct net_device *ndev)
ndev->watchdog_timeo = TX_TIMEOUT;
ndev->netdev_ops = &fec_netdev_ops;
ndev->ethtool_ops = &fec_enet_ethtool_ops;
-
- writel(FEC_RX_DISABLED_IMASK, fep->hwp + FEC_IMASK);
+ writel(fec_rx_disabled_imask(fep), fec_hwp(fep) + FEC_IMASK);
netif_napi_add(ndev, &fep->napi, fec_enet_rx_napi, NAPI_POLL_WEIGHT);
if (fep->quirks & FEC_QUIRK_HAS_VLAN)
--
2.20.1
Powered by blists - more mailing lists