Commit 77ab714f authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'add-fdma-support-on-ocelot-switch-driver'

Clément Léger says:

====================
Add FDMA support on ocelot switch driver

This series adds support for the Frame DMA present on the VSC7514
switch. The FDMA is able to extract and inject packets on the various
ethernet interfaces present on the switch.
====================

Link: https://lore.kernel.org/r/20211209154911.3152830-1-clement.leger@bootlin.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1868d997 753a026c
......@@ -12,5 +12,6 @@ mscc_ocelot_switch_lib-y := \
mscc_ocelot_switch_lib-$(CONFIG_BRIDGE_MRP) += ocelot_mrp.o
obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
mscc_ocelot-y := \
ocelot_fdma.o \
ocelot_vsc7514.o \
ocelot_net.o
......@@ -1054,14 +1054,34 @@ static int ocelot_xtr_poll_xfh(struct ocelot *ocelot, int grp, u32 *xfh)
return 0;
}
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
u64 timestamp)
{
struct skb_shared_hwtstamps *shhwtstamps;
u64 tod_in_ns, full_ts_in_ns;
struct timespec64 ts;
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
if ((tod_in_ns & 0xffffffff) < timestamp)
full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
timestamp;
else
full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
timestamp;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamps->hwtstamp = full_ts_in_ns;
}
EXPORT_SYMBOL(ocelot_ptp_rx_timestamp);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
{
u64 timestamp, src_port, len;
u32 xfh[OCELOT_TAG_LEN / 4];
struct net_device *dev;
struct timespec64 ts;
struct sk_buff *skb;
int sz, buf_len;
u32 val, *buf;
......@@ -1117,21 +1137,8 @@ int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **nskb)
*buf = val;
}
if (ocelot->ptp) {
ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
tod_in_ns = ktime_set(ts.tv_sec, ts.tv_nsec);
if ((tod_in_ns & 0xffffffff) < timestamp)
full_ts_in_ns = (((tod_in_ns >> 32) - 1) << 32) |
timestamp;
else
full_ts_in_ns = (tod_in_ns & GENMASK_ULL(63, 32)) |
timestamp;
shhwtstamps = skb_hwtstamps(skb);
memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps));
shhwtstamps->hwtstamp = full_ts_in_ns;
}
if (ocelot->ptp)
ocelot_ptp_rx_timestamp(ocelot, skb, timestamp);
/* Everything we see on an interface that is in the HW bridge
* has already been forwarded.
......@@ -1164,6 +1171,18 @@ bool ocelot_can_inject(struct ocelot *ocelot, int grp)
}
EXPORT_SYMBOL(ocelot_can_inject);
void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag)
{
ocelot_ifh_set_bypass(ifh, 1);
ocelot_ifh_set_dest(ifh, BIT_ULL(port));
ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
if (vlan_tag)
ocelot_ifh_set_vlan_tci(ifh, vlan_tag);
if (rew_op)
ocelot_ifh_set_rew_op(ifh, rew_op);
}
EXPORT_SYMBOL(ocelot_ifh_port_set);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb)
{
......@@ -1173,11 +1192,7 @@ void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) |
QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp);
ocelot_ifh_set_bypass(ifh, 1);
ocelot_ifh_set_dest(ifh, BIT_ULL(port));
ocelot_ifh_set_tag_type(ifh, IFH_TAG_TYPE_C);
ocelot_ifh_set_vlan_tci(ifh, skb_vlan_tag_get(skb));
ocelot_ifh_set_rew_op(ifh, rew_op);
ocelot_ifh_port_set(ifh, port, rew_op, skb_vlan_tag_get(skb));
for (i = 0; i < OCELOT_TAG_LEN / 4; i++)
ocelot_write_rix(ocelot, ifh[i], QS_INJ_WR, grp);
......
......@@ -32,6 +32,8 @@
#define OCELOT_PTP_QUEUE_SZ 128
#define OCELOT_JUMBO_MTU 9000
struct ocelot_port_tc {
bool block_shared;
unsigned long offload_cnt;
......
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 @@
#include <net/pkt_cls.h>
#include "ocelot.h"
#include "ocelot_vcap.h"
#include "ocelot_fdma.h"
#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)
int port = priv->chip_port;
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;
/* Check if timestamping is needed */
......@@ -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);
}
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;
}
......@@ -764,10 +770,23 @@ static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return phy_mii_ioctl(dev->phydev, ifr, cmd);
}
static int ocelot_change_mtu(struct net_device *dev, int new_mtu)
{
struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot;
ocelot_port_set_maxlen(ocelot, priv->chip_port, new_mtu);
WRITE_ONCE(dev->mtu, new_mtu);
return 0;
}
static const struct net_device_ops ocelot_port_netdev_ops = {
.ndo_open = ocelot_port_open,
.ndo_stop = ocelot_port_stop,
.ndo_start_xmit = ocelot_port_xmit,
.ndo_change_mtu = ocelot_change_mtu,
.ndo_set_rx_mode = ocelot_set_rx_mode,
.ndo_set_mac_address = ocelot_port_set_mac_address,
.ndo_get_stats64 = ocelot_get_stats64,
......@@ -1670,6 +1689,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
dev->netdev_ops = &ocelot_port_netdev_ops;
dev->ethtool_ops = &ocelot_ethtool_ops;
dev->max_mtu = OCELOT_JUMBO_MTU;
dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_RXFCS |
NETIF_F_HW_TC;
......@@ -1685,14 +1705,20 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
if (err)
goto out;
if (ocelot->fdma)
ocelot_fdma_netdev_init(ocelot, dev);
err = register_netdev(dev);
if (err) {
dev_err(ocelot->dev, "register_netdev failed\n");
goto out;
goto out_fdma_deinit;
}
return 0;
out_fdma_deinit:
if (ocelot->fdma)
ocelot_fdma_netdev_deinit(ocelot, dev);
out:
ocelot->ports[port] = NULL;
free_netdev(dev);
......@@ -1705,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,
port);
struct ocelot *ocelot = ocelot_port->ocelot;
struct ocelot_fdma *fdma = ocelot->fdma;
unregister_netdev(priv->dev);
if (fdma)
ocelot_fdma_netdev_deinit(ocelot, priv->dev);
if (priv->phylink) {
rtnl_lock();
phylink_disconnect_phy(priv->phylink);
......
......@@ -19,6 +19,7 @@
#include <soc/mscc/ocelot_vcap.h>
#include <soc/mscc/ocelot_hsio.h>
#include <soc/mscc/vsc7514_regs.h>
#include "ocelot_fdma.h"
#include "ocelot.h"
#define VSC7514_VCAP_POLICER_BASE 128
......@@ -550,6 +551,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
{ S1, "s1" },
{ S2, "s2" },
{ PTP, "ptp", 1 },
{ FDMA, "fdma", 1 },
};
if (!np && !pdev->dev.platform_data)
......@@ -585,6 +587,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
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");
if (IS_ERR(hsio)) {
dev_err(&pdev->dev, "missing hsio syscon\n");
......@@ -648,6 +653,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
if (err)
goto out_ocelot_devlink_unregister;
if (ocelot->fdma)
ocelot_fdma_start(ocelot);
err = ocelot_devlink_sb_register(ocelot);
if (err)
goto out_ocelot_release_ports;
......@@ -688,6 +696,8 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
{
struct ocelot *ocelot = platform_get_drvdata(pdev);
if (ocelot->fdma)
ocelot_fdma_deinit(ocelot);
devlink_unregister(ocelot->devlink);
ocelot_deinit_timestamp(ocelot);
ocelot_devlink_sb_unregister(ocelot);
......
......@@ -118,6 +118,7 @@ enum ocelot_target {
S2,
HSIO,
PTP,
FDMA,
GCB,
DEV_GMII,
TARGET_MAX,
......@@ -732,6 +733,8 @@ struct ocelot {
/* Protects the PTP clock */
spinlock_t ptp_clock_lock;
struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM];
struct ocelot_fdma *fdma;
};
struct ocelot_policer {
......@@ -794,8 +797,11 @@ void __ocelot_target_write_ix(struct ocelot *ocelot, enum ocelot_target target,
bool ocelot_can_inject(struct ocelot *ocelot, int grp);
void ocelot_port_inject_frame(struct ocelot *ocelot, int port, int grp,
u32 rew_op, struct sk_buff *skb);
void ocelot_ifh_port_set(void *ifh, int port, u32 rew_op, u32 vlan_tag);
int ocelot_xtr_poll_frame(struct ocelot *ocelot, int grp, struct sk_buff **skb);
void ocelot_drain_cpu_queue(struct ocelot *ocelot, int grp);
void ocelot_ptp_rx_timestamp(struct ocelot *ocelot, struct sk_buff *skb,
u64 timestamp);
/* Hardware initialization */
int ocelot_regfields_init(struct ocelot *ocelot,
......
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