Commit 753a026c authored by Clément Léger's avatar Clément Léger Committed by Jakub Kicinski

net: ocelot: add FDMA support

Ethernet frames can be extracted or injected autonomously to or from
the device’s DDR3/DDR3L memory and/or PCIe memory space. Linked list
data structures in memory are used for injecting or extracting Ethernet
frames. The FDMA generates interrupts when frame extraction or
injection is done and when the linked lists need updating.

The FDMA is shared between all the ethernet ports of the switch and
uses a linked list of descriptors (DCB) to inject and extract packets.
Before adding descriptors, the FDMA channels must be stopped. It would
be inefficient to do that each time a descriptor would be added so the
channels are restarted only once they stopped.

Both channels uses ring-like structure to feed the DCBs to the FDMA.
head and tail are never touched by hardware and are completely handled
by the driver. On top of that, page recycling has been added and is
mostly taken from gianfar driver.
Reviewed-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Co-developed-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: default avatarClément Léger <clement.leger@bootlin.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent de5841e1
...@@ -12,5 +12,6 @@ mscc_ocelot_switch_lib-y := \ ...@@ -12,5 +12,6 @@ mscc_ocelot_switch_lib-y := \
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \ mscc_ocelot-y := \
ocelot_fdma.o \
ocelot_vsc7514.o \ ocelot_vsc7514.o \
ocelot_net.o ocelot_net.o
This diff is collapsed.
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Microsemi SoCs FDMA driver
*
* Copyright (c) 2021 Microchip
*/
#ifndef _MSCC_OCELOT_FDMA_H_
#define _MSCC_OCELOT_FDMA_H_
#include "ocelot.h"
#define MSCC_FDMA_DCB_STAT_BLOCKO(x) (((x) << 20) & GENMASK(31, 20))
#define MSCC_FDMA_DCB_STAT_BLOCKO_M GENMASK(31, 20)
#define MSCC_FDMA_DCB_STAT_BLOCKO_X(x) (((x) & GENMASK(31, 20)) >> 20)
#define MSCC_FDMA_DCB_STAT_PD BIT(19)
#define MSCC_FDMA_DCB_STAT_ABORT BIT(18)
#define MSCC_FDMA_DCB_STAT_EOF BIT(17)
#define MSCC_FDMA_DCB_STAT_SOF BIT(16)
#define MSCC_FDMA_DCB_STAT_BLOCKL_M GENMASK(15, 0)
#define MSCC_FDMA_DCB_STAT_BLOCKL(x) ((x) & GENMASK(15, 0))
#define MSCC_FDMA_DCB_LLP(x) ((x) * 4 + 0x0)
#define MSCC_FDMA_DCB_LLP_PREV(x) ((x) * 4 + 0xA0)
#define MSCC_FDMA_CH_SAFE 0xcc
#define MSCC_FDMA_CH_ACTIVATE 0xd0
#define MSCC_FDMA_CH_DISABLE 0xd4
#define MSCC_FDMA_CH_FORCEDIS 0xd8
#define MSCC_FDMA_EVT_ERR 0x164
#define MSCC_FDMA_EVT_ERR_CODE 0x168
#define MSCC_FDMA_INTR_LLP 0x16c
#define MSCC_FDMA_INTR_LLP_ENA 0x170
#define MSCC_FDMA_INTR_FRM 0x174
#define MSCC_FDMA_INTR_FRM_ENA 0x178
#define MSCC_FDMA_INTR_ENA 0x184
#define MSCC_FDMA_INTR_IDENT 0x188
#define MSCC_FDMA_INJ_CHAN 2
#define MSCC_FDMA_XTR_CHAN 0
#define OCELOT_FDMA_WEIGHT 32
#define OCELOT_FDMA_CH_SAFE_TIMEOUT_US 10
#define OCELOT_FDMA_RX_RING_SIZE 512
#define OCELOT_FDMA_TX_RING_SIZE 128
#define OCELOT_FDMA_RX_DCB_SIZE (OCELOT_FDMA_RX_RING_SIZE * \
sizeof(struct ocelot_fdma_dcb))
#define OCELOT_FDMA_TX_DCB_SIZE (OCELOT_FDMA_TX_RING_SIZE * \
sizeof(struct ocelot_fdma_dcb))
/* +4 allows for word alignment after allocation */
#define OCELOT_DCBS_HW_ALLOC_SIZE (OCELOT_FDMA_RX_DCB_SIZE + \
OCELOT_FDMA_TX_DCB_SIZE + \
4)
#define OCELOT_FDMA_RX_SIZE (PAGE_SIZE / 2)
#define OCELOT_FDMA_SKBFRAG_OVR (4 + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define OCELOT_FDMA_RXB_SIZE ALIGN_DOWN(OCELOT_FDMA_RX_SIZE - OCELOT_FDMA_SKBFRAG_OVR, 4)
#define OCELOT_FDMA_SKBFRAG_SIZE (OCELOT_FDMA_RXB_SIZE + OCELOT_FDMA_SKBFRAG_OVR)
DECLARE_STATIC_KEY_FALSE(ocelot_fdma_enabled);
struct ocelot_fdma_dcb {
u32 llp;
u32 datap;
u32 datal;
u32 stat;
} __packed;
/**
* struct ocelot_fdma_tx_buf - TX buffer structure
* @skb: SKB currently used in the corresponding DCB.
* @dma_addr: SKB DMA mapped address.
*/
struct ocelot_fdma_tx_buf {
struct sk_buff *skb;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
};
/**
* struct ocelot_fdma_tx_ring - TX ring description of DCBs
*
* @dcbs: DCBs allocated for the ring
* @dcbs_dma: DMA base address of the DCBs
* @bufs: List of TX buffer associated to the DCBs
* @xmit_lock: lock for concurrent xmit access
* @next_to_clean: Next DCB to be cleaned in tx_cleanup
* @next_to_use: Next available DCB to send SKB
*/
struct ocelot_fdma_tx_ring {
struct ocelot_fdma_dcb *dcbs;
dma_addr_t dcbs_dma;
struct ocelot_fdma_tx_buf bufs[OCELOT_FDMA_TX_RING_SIZE];
/* Protect concurrent xmit calls */
spinlock_t xmit_lock;
u16 next_to_clean;
u16 next_to_use;
};
/**
* struct ocelot_fdma_rx_buf - RX buffer structure
* @page: Struct page used in this buffer
* @page_offset: Current page offset (either 0 or PAGE_SIZE/2)
* @dma_addr: DMA address of the page
*/
struct ocelot_fdma_rx_buf {
struct page *page;
u32 page_offset;
dma_addr_t dma_addr;
};
/**
* struct ocelot_fdma_rx_ring - TX ring description of DCBs
*
* @dcbs: DCBs allocated for the ring
* @dcbs_dma: DMA base address of the DCBs
* @bufs: List of RX buffer associated to the DCBs
* @skb: SKB currently received by the netdev
* @next_to_clean: Next DCB to be cleaned NAPI polling
* @next_to_use: Next available DCB to send SKB
* @next_to_alloc: Next buffer that needs to be allocated (page reuse or alloc)
*/
struct ocelot_fdma_rx_ring {
struct ocelot_fdma_dcb *dcbs;
dma_addr_t dcbs_dma;
struct ocelot_fdma_rx_buf bufs[OCELOT_FDMA_RX_RING_SIZE];
struct sk_buff *skb;
u16 next_to_clean;
u16 next_to_use;
u16 next_to_alloc;
};
/**
* struct ocelot_fdma - FDMA context
*
* @irq: FDMA interrupt
* @ndev: Net device used to initialize NAPI
* @dcbs_base: Memory coherent DCBs
* @dcbs_dma_base: DMA base address of memory coherent DCBs
* @tx_ring: Injection ring
* @rx_ring: Extraction ring
* @napi: NAPI context
* @ocelot: Back-pointer to ocelot struct
*/
struct ocelot_fdma {
int irq;
struct net_device *ndev;
struct ocelot_fdma_dcb *dcbs_base;
dma_addr_t dcbs_dma_base;
struct ocelot_fdma_tx_ring tx_ring;
struct ocelot_fdma_rx_ring rx_ring;
struct napi_struct napi;
struct ocelot *ocelot;
};
void ocelot_fdma_init(struct platform_device *pdev, struct ocelot *ocelot);
void ocelot_fdma_start(struct ocelot *ocelot);
void ocelot_fdma_deinit(struct ocelot *ocelot);
int ocelot_fdma_inject_frame(struct ocelot *fdma, int port, u32 rew_op,
struct sk_buff *skb, struct net_device *dev);
void ocelot_fdma_netdev_init(struct ocelot *ocelot, struct net_device *dev);
void ocelot_fdma_netdev_deinit(struct ocelot *ocelot,
struct net_device *dev);
#endif
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
#include "ocelot.h" #include "ocelot.h"
#include "ocelot_vcap.h" #include "ocelot_vcap.h"
#include "ocelot_fdma.h"
#define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP #define OCELOT_MAC_QUIRKS OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP
...@@ -457,7 +458,8 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -457,7 +458,8 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
int port = priv->chip_port; int port = priv->chip_port;
u32 rew_op = 0; u32 rew_op = 0;
if (!ocelot_can_inject(ocelot, 0)) if (!static_branch_unlikely(&ocelot_fdma_enabled) &&
!ocelot_can_inject(ocelot, 0))
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
/* Check if timestamping is needed */ /* Check if timestamping is needed */
...@@ -475,9 +477,13 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -475,9 +477,13 @@ static netdev_tx_t ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev)
rew_op = ocelot_ptp_rew_op(skb); rew_op = ocelot_ptp_rew_op(skb);
} }
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); if (static_branch_unlikely(&ocelot_fdma_enabled)) {
ocelot_fdma_inject_frame(ocelot, port, rew_op, skb, dev);
} else {
ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb);
kfree_skb(skb); consume_skb(skb);
}
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
...@@ -1699,14 +1705,20 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target, ...@@ -1699,14 +1705,20 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
if (err) if (err)
goto out; goto out;
if (ocelot->fdma)
ocelot_fdma_netdev_init(ocelot, dev);
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
dev_err(ocelot->dev, "register_netdev failed\n"); dev_err(ocelot->dev, "register_netdev failed\n");
goto out; goto out_fdma_deinit;
} }
return 0; return 0;
out_fdma_deinit:
if (ocelot->fdma)
ocelot_fdma_netdev_deinit(ocelot, dev);
out: out:
ocelot->ports[port] = NULL; ocelot->ports[port] = NULL;
free_netdev(dev); free_netdev(dev);
...@@ -1719,9 +1731,14 @@ void ocelot_release_port(struct ocelot_port *ocelot_port) ...@@ -1719,9 +1731,14 @@ void ocelot_release_port(struct ocelot_port *ocelot_port)
struct ocelot_port_private *priv = container_of(ocelot_port, struct ocelot_port_private *priv = container_of(ocelot_port,
struct ocelot_port_private, struct ocelot_port_private,
port); port);
struct ocelot *ocelot = ocelot_port->ocelot;
struct ocelot_fdma *fdma = ocelot->fdma;
unregister_netdev(priv->dev); unregister_netdev(priv->dev);
if (fdma)
ocelot_fdma_netdev_deinit(ocelot, priv->dev);
if (priv->phylink) { if (priv->phylink) {
rtnl_lock(); rtnl_lock();
phylink_disconnect_phy(priv->phylink); phylink_disconnect_phy(priv->phylink);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <soc/mscc/ocelot_vcap.h> #include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h> #include <soc/mscc/ocelot_hsio.h>
#include <soc/mscc/vsc7514_regs.h> #include <soc/mscc/vsc7514_regs.h>
#include "ocelot_fdma.h"
#include "ocelot.h" #include "ocelot.h"
#define VSC7514_VCAP_POLICER_BASE 128 #define VSC7514_VCAP_POLICER_BASE 128
...@@ -550,6 +551,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -550,6 +551,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
{ S1, "s1" }, { S1, "s1" },
{ S2, "s2" }, { S2, "s2" },
{ PTP, "ptp", 1 }, { PTP, "ptp", 1 },
{ FDMA, "fdma", 1 },
}; };
if (!np && !pdev->dev.platform_data) if (!np && !pdev->dev.platform_data)
...@@ -585,6 +587,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -585,6 +587,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
ocelot->targets[io_target[i].id] = target; ocelot->targets[io_target[i].id] = target;
} }
if (ocelot->targets[FDMA])
ocelot_fdma_init(pdev, ocelot);
hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio"); hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio");
if (IS_ERR(hsio)) { if (IS_ERR(hsio)) {
dev_err(&pdev->dev, "missing hsio syscon\n"); dev_err(&pdev->dev, "missing hsio syscon\n");
...@@ -648,6 +653,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ...@@ -648,6 +653,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
if (err) if (err)
goto out_ocelot_devlink_unregister; goto out_ocelot_devlink_unregister;
if (ocelot->fdma)
ocelot_fdma_start(ocelot);
err = ocelot_devlink_sb_register(ocelot); err = ocelot_devlink_sb_register(ocelot);
if (err) if (err)
goto out_ocelot_release_ports; goto out_ocelot_release_ports;
...@@ -688,6 +696,8 @@ static int mscc_ocelot_remove(struct platform_device *pdev) ...@@ -688,6 +696,8 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
{ {
struct ocelot *ocelot = platform_get_drvdata(pdev); struct ocelot *ocelot = platform_get_drvdata(pdev);
if (ocelot->fdma)
ocelot_fdma_deinit(ocelot);
devlink_unregister(ocelot->devlink); devlink_unregister(ocelot->devlink);
ocelot_deinit_timestamp(ocelot); ocelot_deinit_timestamp(ocelot);
ocelot_devlink_sb_unregister(ocelot); ocelot_devlink_sb_unregister(ocelot);
......
...@@ -118,6 +118,7 @@ enum ocelot_target { ...@@ -118,6 +118,7 @@ enum ocelot_target {
S2, S2,
HSIO, HSIO,
PTP, PTP,
FDMA,
GCB, GCB,
DEV_GMII, DEV_GMII,
TARGET_MAX, TARGET_MAX,
...@@ -732,6 +733,8 @@ struct ocelot { ...@@ -732,6 +733,8 @@ struct ocelot {
/* Protects the PTP clock */ /* Protects the PTP clock */
spinlock_t ptp_clock_lock; spinlock_t ptp_clock_lock;
struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM]; struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM];
struct ocelot_fdma *fdma;
}; };
struct ocelot_policer { struct ocelot_policer {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment