Files
linux/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x.c
Daniel Machon d84ad2c0d8 net: lan969x: add FDMA implementation
The lan969x switch device supports manual frame injection and extraction
to and from the switch core, using a number of injection and extraction
queues.  This technique is currently supported, but delivers poor
performance compared to Frame DMA (FDMA).

This lan969x implementation of FDMA, hooks into the existing FDMA for
Sparx5, but requires its own RX and TX handling, as lan969x does not
support the same native cache coherency that Sparx5 does. Effectively,
this means that we are going to use the DMA mapping API for mapping and
unmapping TX buffers. The RX loop will utilize the page pool API for
efficient RX handling. Other than that, the implementation is largely
the same, and utilizes the FDMA library for DCB and DB handling.

Some numbers:

Manual injection/extraction (before this series):

// iperf3 -c 1.0.1.1

[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.02  sec   345 MBytes   289 Mbits/sec  sender
[  5]   0.00-10.06  sec   345 MBytes   288 Mbits/sec  receiver

FDMA (after this series):

// iperf3 -c 1.0.1.1

[ ID] Interval           Transfer     Bitrate
[  5]   0.00-10.03  sec  1.10 GBytes   940 Mbits/sec  sender
[  5]   0.00-10.07  sec  1.10 GBytes   936 Mbits/sec  receiver

Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com>
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
Link: https://patch.msgid.link/20250113-sparx5-lan969x-switch-driver-5-v2-5-c468f02fd623@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-01-15 14:13:33 -08:00

358 lines
12 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/* Microchip lan969x Switch driver
*
* Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
*/
#include "lan969x.h"
#define LAN969X_SDLB_GRP_CNT 5
#define LAN969X_HSCH_LEAK_GRP_CNT 4
static const struct sparx5_main_io_resource lan969x_main_iomap[] = {
{ TARGET_CPU, 0xc0000, 0 }, /* 0xe00c0000 */
{ TARGET_FDMA, 0xc0400, 0 }, /* 0xe00c0400 */
{ TARGET_GCB, 0x2010000, 1 }, /* 0xe2010000 */
{ TARGET_QS, 0x2030000, 1 }, /* 0xe2030000 */
{ TARGET_PTP, 0x2040000, 1 }, /* 0xe2040000 */
{ TARGET_ANA_ACL, 0x2050000, 1 }, /* 0xe2050000 */
{ TARGET_LRN, 0x2060000, 1 }, /* 0xe2060000 */
{ TARGET_VCAP_SUPER, 0x2080000, 1 }, /* 0xe2080000 */
{ TARGET_QSYS, 0x20a0000, 1 }, /* 0xe20a0000 */
{ TARGET_QFWD, 0x20b0000, 1 }, /* 0xe20b0000 */
{ TARGET_XQS, 0x20c0000, 1 }, /* 0xe20c0000 */
{ TARGET_VCAP_ES2, 0x20d0000, 1 }, /* 0xe20d0000 */
{ TARGET_VCAP_ES0, 0x20e0000, 1 }, /* 0xe20e0000 */
{ TARGET_ANA_AC_POL, 0x2200000, 1 }, /* 0xe2200000 */
{ TARGET_QRES, 0x2280000, 1 }, /* 0xe2280000 */
{ TARGET_EACL, 0x22c0000, 1 }, /* 0xe22c0000 */
{ TARGET_ANA_CL, 0x2400000, 1 }, /* 0xe2400000 */
{ TARGET_ANA_L3, 0x2480000, 1 }, /* 0xe2480000 */
{ TARGET_ANA_AC_SDLB, 0x2500000, 1 }, /* 0xe2500000 */
{ TARGET_HSCH, 0x2580000, 1 }, /* 0xe2580000 */
{ TARGET_REW, 0x2600000, 1 }, /* 0xe2600000 */
{ TARGET_ANA_L2, 0x2800000, 1 }, /* 0xe2800000 */
{ TARGET_ANA_AC, 0x2900000, 1 }, /* 0xe2900000 */
{ TARGET_VOP, 0x2a00000, 1 }, /* 0xe2a00000 */
{ TARGET_DEV2G5, 0x3004000, 1 }, /* 0xe3004000 */
{ TARGET_DEV10G, 0x3008000, 1 }, /* 0xe3008000 */
{ TARGET_PCS10G_BR, 0x300c000, 1 }, /* 0xe300c000 */
{ TARGET_DEV2G5 + 1, 0x3010000, 1 }, /* 0xe3010000 */
{ TARGET_DEV2G5 + 2, 0x3014000, 1 }, /* 0xe3014000 */
{ TARGET_DEV2G5 + 3, 0x3018000, 1 }, /* 0xe3018000 */
{ TARGET_DEV2G5 + 4, 0x301c000, 1 }, /* 0xe301c000 */
{ TARGET_DEV10G + 1, 0x3020000, 1 }, /* 0xe3020000 */
{ TARGET_PCS10G_BR + 1, 0x3024000, 1 }, /* 0xe3024000 */
{ TARGET_DEV2G5 + 5, 0x3028000, 1 }, /* 0xe3028000 */
{ TARGET_DEV2G5 + 6, 0x302c000, 1 }, /* 0xe302c000 */
{ TARGET_DEV2G5 + 7, 0x3030000, 1 }, /* 0xe3030000 */
{ TARGET_DEV2G5 + 8, 0x3034000, 1 }, /* 0xe3034000 */
{ TARGET_DEV10G + 2, 0x3038000, 1 }, /* 0xe3038000 */
{ TARGET_PCS10G_BR + 2, 0x303c000, 1 }, /* 0xe303c000 */
{ TARGET_DEV2G5 + 9, 0x3040000, 1 }, /* 0xe3040000 */
{ TARGET_DEV5G, 0x3044000, 1 }, /* 0xe3044000 */
{ TARGET_PCS5G_BR, 0x3048000, 1 }, /* 0xe3048000 */
{ TARGET_DEV2G5 + 10, 0x304c000, 1 }, /* 0xe304c000 */
{ TARGET_DEV2G5 + 11, 0x3050000, 1 }, /* 0xe3050000 */
{ TARGET_DEV2G5 + 12, 0x3054000, 1 }, /* 0xe3054000 */
{ TARGET_DEV10G + 3, 0x3058000, 1 }, /* 0xe3058000 */
{ TARGET_PCS10G_BR + 3, 0x305c000, 1 }, /* 0xe305c000 */
{ TARGET_DEV2G5 + 13, 0x3060000, 1 }, /* 0xe3060000 */
{ TARGET_DEV5G + 1, 0x3064000, 1 }, /* 0xe3064000 */
{ TARGET_PCS5G_BR + 1, 0x3068000, 1 }, /* 0xe3068000 */
{ TARGET_DEV2G5 + 14, 0x306c000, 1 }, /* 0xe306c000 */
{ TARGET_DEV2G5 + 15, 0x3070000, 1 }, /* 0xe3070000 */
{ TARGET_DEV2G5 + 16, 0x3074000, 1 }, /* 0xe3074000 */
{ TARGET_DEV10G + 4, 0x3078000, 1 }, /* 0xe3078000 */
{ TARGET_PCS10G_BR + 4, 0x307c000, 1 }, /* 0xe307c000 */
{ TARGET_DEV2G5 + 17, 0x3080000, 1 }, /* 0xe3080000 */
{ TARGET_DEV5G + 2, 0x3084000, 1 }, /* 0xe3084000 */
{ TARGET_PCS5G_BR + 2, 0x3088000, 1 }, /* 0xe3088000 */
{ TARGET_DEV2G5 + 18, 0x308c000, 1 }, /* 0xe308c000 */
{ TARGET_DEV2G5 + 19, 0x3090000, 1 }, /* 0xe3090000 */
{ TARGET_DEV2G5 + 20, 0x3094000, 1 }, /* 0xe3094000 */
{ TARGET_DEV10G + 5, 0x3098000, 1 }, /* 0xe3098000 */
{ TARGET_PCS10G_BR + 5, 0x309c000, 1 }, /* 0xe309c000 */
{ TARGET_DEV2G5 + 21, 0x30a0000, 1 }, /* 0xe30a0000 */
{ TARGET_DEV5G + 3, 0x30a4000, 1 }, /* 0xe30a4000 */
{ TARGET_PCS5G_BR + 3, 0x30a8000, 1 }, /* 0xe30a8000 */
{ TARGET_DEV2G5 + 22, 0x30ac000, 1 }, /* 0xe30ac000 */
{ TARGET_DEV2G5 + 23, 0x30b0000, 1 }, /* 0xe30b0000 */
{ TARGET_DEV2G5 + 24, 0x30b4000, 1 }, /* 0xe30b4000 */
{ TARGET_DEV10G + 6, 0x30b8000, 1 }, /* 0xe30b8000 */
{ TARGET_PCS10G_BR + 6, 0x30bc000, 1 }, /* 0xe30bc000 */
{ TARGET_DEV2G5 + 25, 0x30c0000, 1 }, /* 0xe30c0000 */
{ TARGET_DEV10G + 7, 0x30c4000, 1 }, /* 0xe30c4000 */
{ TARGET_PCS10G_BR + 7, 0x30c8000, 1 }, /* 0xe30c8000 */
{ TARGET_DEV2G5 + 26, 0x30cc000, 1 }, /* 0xe30cc000 */
{ TARGET_DEV10G + 8, 0x30d0000, 1 }, /* 0xe30d0000 */
{ TARGET_PCS10G_BR + 8, 0x30d4000, 1 }, /* 0xe30d4000 */
{ TARGET_DEV2G5 + 27, 0x30d8000, 1 }, /* 0xe30d8000 */
{ TARGET_DEV10G + 9, 0x30dc000, 1 }, /* 0xe30dc000 */
{ TARGET_PCS10G_BR + 9, 0x30e0000, 1 }, /* 0xe30e0000 */
{ TARGET_DEVRGMII, 0x30e4000, 1 }, /* 0xe30e4000 */
{ TARGET_DEVRGMII + 1, 0x30e8000, 1 }, /* 0xe30e8000 */
{ TARGET_DSM, 0x30ec000, 1 }, /* 0xe30ec000 */
{ TARGET_PORT_CONF, 0x30f0000, 1 }, /* 0xe30f0000 */
{ TARGET_ASM, 0x3200000, 1 }, /* 0xe3200000 */
{ TARGET_HSIO_WRAP, 0x3408000, 1 }, /* 0xe3408000 */
};
static struct sparx5_sdlb_group lan969x_sdlb_groups[LAN969X_SDLB_GRP_CNT] = {
{ 1000000000, 8192 / 2, 64 }, /* 1 G */
{ 500000000, 8192 / 2, 64 }, /* 500 M */
{ 100000000, 8192 / 4, 64 }, /* 100 M */
{ 50000000, 8192 / 4, 64 }, /* 50 M */
{ 5000000, 8192 / 8, 64 }, /* 10 M */
};
static u32 lan969x_hsch_max_group_rate[LAN969X_HSCH_LEAK_GRP_CNT] = {
655355, 1048568, 6553550, 10485680
};
static struct sparx5_sdlb_group *lan969x_get_sdlb_group(int idx)
{
return &lan969x_sdlb_groups[idx];
}
static u32 lan969x_get_hsch_max_group_rate(int grp)
{
return lan969x_hsch_max_group_rate[grp];
}
static u32 lan969x_get_dev_mode_bit(struct sparx5 *sparx5, int port)
{
if (lan969x_port_is_2g5(port) || lan969x_port_is_5g(port))
return port;
/* 10G */
switch (port) {
case 0:
return 12;
case 4:
return 13;
case 8:
return 14;
case 12:
return 0;
default:
return port;
}
}
static u32 lan969x_port_dev_mapping(struct sparx5 *sparx5, int port)
{
if (lan969x_port_is_5g(port)) {
switch (port) {
case 9:
return 0;
case 13:
return 1;
case 17:
return 2;
case 21:
return 3;
}
}
if (lan969x_port_is_10g(port)) {
switch (port) {
case 0:
return 0;
case 4:
return 1;
case 8:
return 2;
case 12:
return 3;
case 16:
return 4;
case 20:
return 5;
case 24:
return 6;
case 25:
return 7;
case 26:
return 8;
case 27:
return 9;
}
}
/* 2g5 port */
return port;
}
static int lan969x_port_mux_set(struct sparx5 *sparx5, struct sparx5_port *port,
struct sparx5_port_config *conf)
{
u32 portno = port->portno;
u32 inst;
if (port->conf.portmode == conf->portmode)
return 0; /* Nothing to do */
switch (conf->portmode) {
case PHY_INTERFACE_MODE_QSGMII: /* QSGMII: 4x2G5 devices. Mode Q' */
inst = (portno - portno % 4) / 4;
spx5_rmw(BIT(inst), BIT(inst), sparx5, PORT_CONF_QSGMII_ENA);
break;
default:
break;
}
return 0;
}
static irqreturn_t lan969x_ptp_irq_handler(int irq, void *args)
{
int budget = SPARX5_MAX_PTP_ID;
struct sparx5 *sparx5 = args;
while (budget--) {
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
struct skb_shared_hwtstamps shhwtstamps;
struct sparx5_port *port;
struct timespec64 ts;
unsigned long flags;
u32 val, id, txport;
u32 delay;
val = spx5_rd(sparx5, PTP_TWOSTEP_CTRL);
/* Check if a timestamp can be retrieved */
if (!(val & PTP_TWOSTEP_CTRL_PTP_VLD))
break;
WARN_ON(val & PTP_TWOSTEP_CTRL_PTP_OVFL);
if (!(val & PTP_TWOSTEP_CTRL_STAMP_TX))
continue;
/* Retrieve the ts Tx port */
txport = PTP_TWOSTEP_CTRL_STAMP_PORT_GET(val);
/* Retrieve its associated skb */
port = sparx5->ports[txport];
/* Retrieve the delay */
delay = spx5_rd(sparx5, PTP_TWOSTEP_STAMP_NSEC);
delay = PTP_TWOSTEP_STAMP_NSEC_NS_GET(delay);
/* Get next timestamp from fifo, which needs to be the
* rx timestamp which represents the id of the frame
*/
spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1),
PTP_TWOSTEP_CTRL_PTP_NXT,
sparx5, PTP_TWOSTEP_CTRL);
val = spx5_rd(sparx5, PTP_TWOSTEP_CTRL);
/* Check if a timestamp can be retrieved */
if (!(val & PTP_TWOSTEP_CTRL_PTP_VLD))
break;
/* Read RX timestamping to get the ID */
id = spx5_rd(sparx5, PTP_TWOSTEP_STAMP_NSEC);
id <<= 8;
id |= spx5_rd(sparx5, PTP_TWOSTEP_STAMP_SUBNS);
spin_lock_irqsave(&port->tx_skbs.lock, flags);
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
if (SPARX5_SKB_CB(skb)->ts_id != id)
continue;
__skb_unlink(skb, &port->tx_skbs);
skb_match = skb;
break;
}
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
/* Next ts */
spx5_rmw(PTP_TWOSTEP_CTRL_PTP_NXT_SET(1),
PTP_TWOSTEP_CTRL_PTP_NXT,
sparx5, PTP_TWOSTEP_CTRL);
if (WARN_ON(!skb_match))
continue;
spin_lock_irqsave(&sparx5->ptp_ts_id_lock, flags);
sparx5->ptp_skbs--;
spin_unlock_irqrestore(&sparx5->ptp_ts_id_lock, flags);
/* Get the h/w timestamp */
sparx5_get_hwtimestamp(sparx5, &ts, delay);
/* Set the timestamp in the skb */
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
skb_tstamp_tx(skb_match, &shhwtstamps);
dev_kfree_skb_any(skb_match);
}
return IRQ_HANDLED;
}
static const struct sparx5_regs lan969x_regs = {
.tsize = lan969x_tsize,
.gaddr = lan969x_gaddr,
.gcnt = lan969x_gcnt,
.gsize = lan969x_gsize,
.raddr = lan969x_raddr,
.rcnt = lan969x_rcnt,
.fpos = lan969x_fpos,
.fsize = lan969x_fsize,
};
static const struct sparx5_consts lan969x_consts = {
.n_ports = 30,
.n_ports_all = 35,
.n_hsch_l1_elems = 32,
.n_hsch_queues = 4,
.n_lb_groups = 5,
.n_pgids = 1054, /* (1024 + n_ports) */
.n_sio_clks = 1,
.n_own_upsids = 1,
.n_auto_cals = 4,
.n_filters = 256,
.n_gates = 256,
.n_sdlbs = 496,
.n_dsm_cal_taxis = 5,
.buf_size = 1572864,
.qres_max_prio_idx = 315,
.qres_max_colour_idx = 323,
.tod_pin = 4,
.vcaps = lan969x_vcaps,
.vcap_stats = &lan969x_vcap_stats,
.vcaps_cfg = lan969x_vcap_inst_cfg,
};
static const struct sparx5_ops lan969x_ops = {
.is_port_2g5 = &lan969x_port_is_2g5,
.is_port_5g = &lan969x_port_is_5g,
.is_port_10g = &lan969x_port_is_10g,
.is_port_25g = &lan969x_port_is_25g,
.is_port_rgmii = &lan969x_port_is_rgmii,
.get_port_dev_index = &lan969x_port_dev_mapping,
.get_port_dev_bit = &lan969x_get_dev_mode_bit,
.get_hsch_max_group_rate = &lan969x_get_hsch_max_group_rate,
.get_sdlb_group = &lan969x_get_sdlb_group,
.set_port_mux = &lan969x_port_mux_set,
.ptp_irq_handler = &lan969x_ptp_irq_handler,
.dsm_calendar_calc = &lan969x_dsm_calendar_calc,
.port_config_rgmii = &lan969x_port_config_rgmii,
.fdma_init = &lan969x_fdma_init,
.fdma_deinit = &lan969x_fdma_deinit,
.fdma_poll = &lan969x_fdma_napi_poll,
.fdma_xmit = &lan969x_fdma_xmit,
};
const struct sparx5_match_data lan969x_desc = {
.iomap = lan969x_main_iomap,
.iomap_size = ARRAY_SIZE(lan969x_main_iomap),
.ioranges = 2,
.regs = &lan969x_regs,
.consts = &lan969x_consts,
.ops = &lan969x_ops,
};