Merge branch 'net-sparx5-prepare-for-lan969x-switch-driver'

Daniel Machon says:

====================
net: sparx5: prepare for lan969x switch driver

== Description:

This series is the first of a multi-part series, that prepares and adds
support for the new lan969x switch driver.

The upstreaming efforts is split into multiple series (might change a
bit as we go along):

    1) Prepare the Sparx5 driver for lan969x (this series)
    2) Add support lan969x (same basic features as Sparx5 provides +
       RGMII, excl.  FDMA and VCAP)
    3) Add support for lan969x FDMA
    4) Add support for lan969x VCAP

== Lan969x in short:

The lan969x Ethernet switch family [1] provides a rich set of
switching features and port configurations (up to 30 ports) from 10Mbps
to 10Gbps, with support for RGMII, SGMII, QSGMII, USGMII, and USXGMII,
ideal for industrial & process automation infrastructure applications,
transport, grid automation, power substation automation, and ring &
intra-ring topologies. The LAN969x family is hardware and software
compatible and scalable supporting 46Gbps to 102Gbps switch bandwidths.

== Preparing Sparx5 for lan969x:

The lan969x switch chip reuses many of the IP's of the Sparx5 switch
chip, therefore it has been decided to add support through the existing
Sparx5 driver, in order to avoid a bunch of duplicate code. However, in
order to reuse the Sparx5 switch driver, we have to introduce some
mechanisms to handle the chip differences that are there.  These
mechanisms are:

    - Platform match data to contain all the differences that needs to
      be handled (constants, ops etc.)

    - Register macro indirection layer so that we can reuse the existing
      register macros.

    - Function for branching out on platform type where required.

In some places we ops out functions and in other places we branch on the
chip type. Exactly when we choose one over the other, is an estimate in
each case.

After this series is applied, the Sparx5 driver will be prepared for
lan969x and still function exactly as before.

== Patch breakdown:

Patch #1        adds private match data

Patch #2        adds register macro indirection layer

Patch #3-#4     does some preparation work

Patch #5-#7     adds chip constants and updates the code to use them

Patch #8-#13    adds and uses ops for handling functions differently on the
                two platforms.

Patch #14       adds and uses a macro for branching out on the chip type.

Patch #15 (NEW) redefines macros for internal ports and PGID's.

[1] https://www.microchip.com/en-us/product/lan9698

To: David S. Miller <davem@davemloft.net>
To: Eric Dumazet <edumazet@google.com>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Lars Povlsen <lars.povlsen@microchip.com>
To: Steen Hegelund <Steen.Hegelund@microchip.com>
To: horatiu.vultur@microchip.com
To: jensemil.schulzostergaard@microchip.com
To: UNGLinuxDriver@microchip.com
To: Richard Cochran <richardcochran@gmail.com>
To: horms@kernel.org
To: justinstitt@google.com
To: gal@nvidia.com
To: aakash.r.menon@gmail.com
To: jacob.e.keller@intel.com
To: ast@fiberby.net
Cc: netdev@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org

Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
====================

Link: https://patch.msgid.link/20241004-b4-sparx5-lan969x-switch-driver-v2-0-d3290f581663@microchip.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2024-10-08 12:07:06 +02:00
commit 5d6a8aeabd
26 changed files with 3525 additions and 2220 deletions

View File

@ -11,7 +11,7 @@ sparx5-switch-y := sparx5_main.o sparx5_packet.o \
sparx5_ptp.o sparx5_pgid.o sparx5_tc.o sparx5_qos.o \
sparx5_vcap_impl.o sparx5_vcap_ag_api.o sparx5_tc_flower.o \
sparx5_tc_matchall.o sparx5_pool.o sparx5_sdlb.o sparx5_police.o \
sparx5_psfp.o sparx5_mirror.o
sparx5_psfp.o sparx5_mirror.o sparx5_regs.o
sparx5-switch-$(CONFIG_SPARX5_DCB) += sparx5_dcb.o
sparx5-switch-$(CONFIG_DEBUG_FS) += sparx5_vcap_debugfs.o

View File

@ -15,9 +15,7 @@
#define SPX5_CALBITS_PER_PORT 3 /* Bit per port in calendar register */
/* DSM calendar information */
#define SPX5_DSM_CAL_LEN 64
#define SPX5_DSM_CAL_EMPTY 0xFFFF
#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
#define SPX5_DSM_CAL_TAXIS 8
#define SPX5_DSM_CAL_BW_LOSS 553
@ -37,19 +35,6 @@ static u32 sparx5_taxi_ports[SPX5_DSM_CAL_TAXIS][SPX5_DSM_CAL_MAX_DEVS_PER_TAXI]
{64, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
};
struct sparx5_calendar_data {
u32 schedule[SPX5_DSM_CAL_LEN];
u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 new_slots[SPX5_DSM_CAL_LEN];
u32 temp_sched[SPX5_DSM_CAL_LEN];
u32 indices[SPX5_DSM_CAL_LEN];
u32 short_list[SPX5_DSM_CAL_LEN];
u32 long_list[SPX5_DSM_CAL_LEN];
};
static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
{
switch (sparx5->target_ct) {
@ -131,18 +116,24 @@ static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
{
struct sparx5_port *port;
if (portno >= SPX5_PORTS) {
if (portno >= sparx5->data->consts->n_ports) {
/* Internal ports */
if (portno == SPX5_PORT_CPU_0 || portno == SPX5_PORT_CPU_1) {
if (portno ==
sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0) ||
portno ==
sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1)) {
/* Equals 1.25G */
return SPX5_CAL_SPEED_2G5;
} else if (portno == SPX5_PORT_VD0) {
} else if (portno ==
sparx5_get_internal_port(sparx5, SPX5_PORT_VD0)) {
/* IPMC only idle BW */
return SPX5_CAL_SPEED_NONE;
} else if (portno == SPX5_PORT_VD1) {
} else if (portno ==
sparx5_get_internal_port(sparx5, SPX5_PORT_VD1)) {
/* OAM only idle BW */
return SPX5_CAL_SPEED_NONE;
} else if (portno == SPX5_PORT_VD2) {
} else if (portno ==
sparx5_get_internal_port(sparx5, SPX5_PORT_VD2)) {
/* IPinIP gets only idle BW */
return SPX5_CAL_SPEED_NONE;
}
@ -159,6 +150,7 @@ static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
/* Auto configure the QSYS calendar based on port configuration */
int sparx5_config_auto_calendar(struct sparx5 *sparx5)
{
const struct sparx5_consts *consts = sparx5->data->consts;
u32 cal[7], value, idx, portno;
u32 max_core_bw;
u32 total_bw = 0, used_port_bw = 0;
@ -174,7 +166,7 @@ int sparx5_config_auto_calendar(struct sparx5 *sparx5)
}
/* Setup the calendar with the bandwidth to each port */
for (portno = 0; portno < SPX5_PORTS_ALL; portno++) {
for (portno = 0; portno < consts->n_ports_all; portno++) {
u64 reg, offset, this_bw;
spd = sparx5_get_port_cal_speed(sparx5, portno);
@ -182,7 +174,7 @@ int sparx5_config_auto_calendar(struct sparx5 *sparx5)
continue;
this_bw = sparx5_cal_speed_to_value(spd);
if (portno < SPX5_PORTS)
if (portno < consts->n_ports)
used_port_bw += this_bw;
else
/* Internal ports are granted half the value */
@ -208,12 +200,13 @@ int sparx5_config_auto_calendar(struct sparx5 *sparx5)
}
/* Halt the calendar while changing it */
spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
QSYS_CAL_CTRL_CAL_MODE,
sparx5, QSYS_CAL_CTRL);
if (is_sparx5(sparx5))
spx5_rmw(QSYS_CAL_CTRL_CAL_MODE_SET(10),
QSYS_CAL_CTRL_CAL_MODE,
sparx5, QSYS_CAL_CTRL);
/* Assign port bandwidth to auto calendar */
for (idx = 0; idx < ARRAY_SIZE(cal); idx++)
for (idx = 0; idx < consts->n_auto_cals; idx++)
spx5_wr(cal[idx], sparx5, QSYS_CAL_AUTO(idx));
/* Increase grant rate of all ports to account for
@ -278,8 +271,8 @@ static u32 sparx5_dsm_cp_cal(u32 *sched)
return SPX5_DSM_CAL_EMPTY;
}
static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
struct sparx5_calendar_data *data)
int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
struct sparx5_calendar_data *data)
{
bool slow_mode;
u32 gcd, idx, sum, min, factor;
@ -304,7 +297,7 @@ static int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
for (idx = 0; idx < SPX5_DSM_CAL_MAX_DEVS_PER_TAXI; idx++) {
u32 portno = data->taxi_ports[idx];
if (portno < SPX5_TAXI_PORT_MAX) {
if (portno < sparx5->data->consts->n_ports_all) {
data->taxi_speeds[idx] = sparx5_cal_speed_to_value
(sparx5_get_port_cal_speed(sparx5, portno));
} else {
@ -565,6 +558,7 @@ static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
/* Configure the DSM calendar based on port configuration */
int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
{
const struct sparx5_ops *ops = sparx5->data->ops;
int taxi;
struct sparx5_calendar_data *data;
int err = 0;
@ -573,8 +567,8 @@ int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
if (!data)
return -ENOMEM;
for (taxi = 0; taxi < SPX5_DSM_CAL_TAXIS; ++taxi) {
err = sparx5_dsm_calendar_calc(sparx5, taxi, data);
for (taxi = 0; taxi < sparx5->data->consts->n_dsm_cal_taxis; ++taxi) {
err = ops->dsm_calendar_calc(sparx5, taxi, data);
if (err) {
dev_err(sparx5->dev, "DSM calendar calculation failed\n");
goto cal_out;

View File

@ -234,10 +234,11 @@ static int sparx5_dcb_ieee_dscp_setdel(struct net_device *dev,
struct dcb_app *))
{
struct sparx5_port *port = netdev_priv(dev);
struct sparx5 *sparx5 = port->sparx5;
struct sparx5_port *port_itr;
int err, i;
for (i = 0; i < SPX5_PORTS; i++) {
for (i = 0; i < sparx5->data->consts->n_ports; i++) {
port_itr = port->sparx5->ports[i];
if (!port_itr)
continue;
@ -386,7 +387,7 @@ int sparx5_dcb_init(struct sparx5 *sparx5)
struct sparx5_port *port;
int i;
for (i = 0; i < SPX5_PORTS; i++) {
for (i = 0; i < sparx5->data->consts->n_ports; i++) {
port = sparx5->ports[i];
if (!port)
continue;

View File

@ -505,8 +505,8 @@ static void sparx5_get_dev_misc_stats(u64 *portstats, void __iomem *inst, u32
static void sparx5_get_device_stats(struct sparx5 *sparx5, int portno)
{
u64 *portstats = &sparx5->stats[portno * sparx5->num_stats];
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
void __iomem *inst;
inst = spx5_inst_get(sparx5, dev, tinst);
@ -819,8 +819,8 @@ static void sparx5_get_eth_phy_stats(struct net_device *ndev,
portstats = &sparx5->stats[portno * sparx5->num_stats];
if (sparx5_is_baser(port->conf.portmode)) {
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
sparx5_get_dev_phy_stats(portstats, inst, tinst);
@ -844,8 +844,8 @@ static void sparx5_get_eth_mac_stats(struct net_device *ndev,
portstats = &sparx5->stats[portno * sparx5->num_stats];
if (sparx5_is_baser(port->conf.portmode)) {
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
sparx5_get_dev_mac_stats(portstats, inst, tinst);
@ -912,8 +912,8 @@ static void sparx5_get_eth_mac_ctrl_stats(struct net_device *ndev,
portstats = &sparx5->stats[portno * sparx5->num_stats];
if (sparx5_is_baser(port->conf.portmode)) {
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
sparx5_get_dev_mac_ctrl_stats(portstats, inst, tinst);
@ -944,8 +944,8 @@ static void sparx5_get_eth_rmon_stats(struct net_device *ndev,
portstats = &sparx5->stats[portno * sparx5->num_stats];
if (sparx5_is_baser(port->conf.portmode)) {
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
sparx5_get_dev_rmon_stats(portstats, inst, tinst);
@ -1027,8 +1027,8 @@ static void sparx5_get_sset_data(struct net_device *ndev,
portstats = &sparx5->stats[portno * sparx5->num_stats];
if (sparx5_is_baser(port->conf.portmode)) {
u32 tinst = sparx5_port_dev_index(portno);
u32 dev = sparx5_to_high_dev(portno);
u32 tinst = sparx5_port_dev_index(sparx5, portno);
u32 dev = sparx5_to_high_dev(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
sparx5_get_dev_misc_stats(portstats, inst, tinst);
@ -1122,7 +1122,7 @@ static void sparx5_update_stats(struct sparx5 *sparx5)
{
int idx;
for (idx = 0; idx < SPX5_PORTS; idx++)
for (idx = 0; idx < sparx5->data->consts->n_ports; idx++)
if (sparx5->ports[idx])
sparx5_update_port_stats(sparx5, idx);
}
@ -1189,7 +1189,7 @@ static int sparx5_get_ts_info(struct net_device *dev,
struct sparx5 *sparx5 = port->sparx5;
struct sparx5_phc *phc;
if (!sparx5->ptp)
if (!sparx5->ptp && is_sparx5(sparx5))
return ethtool_op_get_ts_info(dev, info);
phc = &sparx5->phc[SPARX5_PHC_PORT];
@ -1228,6 +1228,7 @@ const struct ethtool_ops sparx5_ethtool_ops = {
int sparx_stats_init(struct sparx5 *sparx5)
{
const struct sparx5_consts *consts = sparx5->data->consts;
char queue_name[32];
int portno;
@ -1235,14 +1236,15 @@ int sparx_stats_init(struct sparx5 *sparx5)
sparx5->num_stats = spx5_stats_count;
sparx5->num_ethtool_stats = ARRAY_SIZE(sparx5_stats_layout);
sparx5->stats = devm_kcalloc(sparx5->dev,
SPX5_PORTS_ALL * sparx5->num_stats,
consts->n_ports_all *
sparx5->num_stats,
sizeof(u64), GFP_KERNEL);
if (!sparx5->stats)
return -ENOMEM;
mutex_init(&sparx5->queue_stats_lock);
sparx5_config_stats(sparx5);
for (portno = 0; portno < SPX5_PORTS; portno++)
for (portno = 0; portno < consts->n_ports; portno++)
if (sparx5->ports[portno])
sparx5_config_port_stats(sparx5, portno);

View File

@ -156,7 +156,9 @@ static bool sparx5_fdma_rx_get_frame(struct sparx5 *sparx5, struct sparx5_rx *rx
/* Now do the normal processing of the skb */
sparx5_ifh_parse((u32 *)skb->data, &fi);
/* Map to port netdev */
port = fi.src_port < SPX5_PORTS ? sparx5->ports[fi.src_port] : NULL;
port = fi.src_port < sparx5->data->consts->n_ports ?
sparx5->ports[fi.src_port] :
NULL;
if (!port || !port->ndev) {
dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
sparx5_xtr_flush(sparx5, XTR_QUEUE);
@ -296,7 +298,7 @@ static void sparx5_fdma_rx_init(struct sparx5 *sparx5,
fdma->ops.dataptr_cb = &sparx5_fdma_rx_dataptr_cb;
fdma->ops.nextptr_cb = &fdma_nextptr_cb;
/* Fetch a netdev for SKB and NAPI use, any will do */
for (idx = 0; idx < SPX5_PORTS; ++idx) {
for (idx = 0; idx < sparx5->data->consts->n_ports; ++idx) {
struct sparx5_port *port = sparx5->ports[idx];
if (port && port->ndev) {
@ -362,7 +364,9 @@ static void sparx5_fdma_injection_mode(struct sparx5 *sparx5)
sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
/* CPU ports capture setup */
for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
for (portno = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0);
portno <= sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1);
portno++) {
/* ASM CPU port: No preamble, IFH, enable padding */
spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |

View File

@ -80,15 +80,16 @@ static void sparx5_mact_select(struct sparx5 *sparx5,
int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
const unsigned char mac[ETH_ALEN], u16 vid)
{
const struct sparx5_consts *consts = sparx5->data->consts;
int addr, type, ret;
if (pgid < SPX5_PORTS) {
if (pgid < consts->n_ports) {
type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
addr = pgid % 32;
addr += (pgid / 32) << 5; /* Add upsid */
} else {
type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
addr = pgid - SPX5_PORTS;
addr = pgid - consts->n_ports;
}
mutex_lock(&sparx5->lock);
@ -128,7 +129,8 @@ int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
struct sparx5_port *port = netdev_priv(dev);
struct sparx5 *sparx5 = port->sparx5;
return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
return sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU),
addr, port->pvid);
}
static int sparx5_mact_get(struct sparx5 *sparx5,
@ -371,7 +373,7 @@ static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
return;
port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
if (port >= SPX5_PORTS)
if (port >= sparx5->data->consts->n_ports)
return;
if (!test_bit(port, sparx5->bridge_mask))

View File

@ -29,8 +29,8 @@
#include "sparx5_port.h"
#include "sparx5_qos.h"
#define QLIM_WM(fraction) \
((SPX5_BUFFER_MEMORY / SPX5_BUFFER_CELL_SZ - 100) * (fraction) / 100)
const struct sparx5_regs *regs;
#define IO_RANGES 3
struct initial_port_config {
@ -45,12 +45,6 @@ struct sparx5_ram_config {
u32 init_val;
};
struct sparx5_main_io_resource {
enum sparx5_target id;
phys_addr_t offset;
int range;
};
static const struct sparx5_main_io_resource sparx5_main_iomap[] = {
{ TARGET_CPU, 0, 0 }, /* 0x600000000 */
{ TARGET_FDMA, 0x80000, 0 }, /* 0x600080000 */
@ -214,23 +208,45 @@ static const struct sparx5_main_io_resource sparx5_main_iomap[] = {
{ TARGET_VOP, 0x11a00000, 2 }, /* 0x611a00000 */
};
bool is_sparx5(struct sparx5 *sparx5)
{
switch (sparx5->target_ct) {
case SPX5_TARGET_CT_7546:
case SPX5_TARGET_CT_7549:
case SPX5_TARGET_CT_7552:
case SPX5_TARGET_CT_7556:
case SPX5_TARGET_CT_7558:
case SPX5_TARGET_CT_7546TSN:
case SPX5_TARGET_CT_7549TSN:
case SPX5_TARGET_CT_7552TSN:
case SPX5_TARGET_CT_7556TSN:
case SPX5_TARGET_CT_7558TSN:
return true;
default:
return false;
}
}
static int sparx5_create_targets(struct sparx5 *sparx5)
{
const struct sparx5_main_io_resource *iomap = sparx5->data->iomap;
int iomap_size = sparx5->data->iomap_size;
int ioranges = sparx5->data->ioranges;
struct resource *iores[IO_RANGES];
void __iomem *iomem[IO_RANGES];
void __iomem *begin[IO_RANGES];
int range_id[IO_RANGES];
int idx, jdx;
for (idx = 0, jdx = 0; jdx < ARRAY_SIZE(sparx5_main_iomap); jdx++) {
const struct sparx5_main_io_resource *iomap = &sparx5_main_iomap[jdx];
for (idx = 0, jdx = 0; jdx < iomap_size; jdx++) {
const struct sparx5_main_io_resource *io = &iomap[jdx];
if (idx == iomap->range) {
if (idx == io->range) {
range_id[idx] = jdx;
idx++;
}
}
for (idx = 0; idx < IO_RANGES; idx++) {
for (idx = 0; idx < ioranges; idx++) {
iores[idx] = platform_get_resource(sparx5->pdev, IORESOURCE_MEM,
idx);
if (!iores[idx]) {
@ -245,12 +261,12 @@ static int sparx5_create_targets(struct sparx5 *sparx5)
iores[idx]->name);
return -ENOMEM;
}
begin[idx] = iomem[idx] - sparx5_main_iomap[range_id[idx]].offset;
begin[idx] = iomem[idx] - iomap[range_id[idx]].offset;
}
for (jdx = 0; jdx < ARRAY_SIZE(sparx5_main_iomap); jdx++) {
const struct sparx5_main_io_resource *iomap = &sparx5_main_iomap[jdx];
for (jdx = 0; jdx < iomap_size; jdx++) {
const struct sparx5_main_io_resource *io = &iomap[jdx];
sparx5->regs[iomap->id] = begin[iomap->range] + iomap->offset;
sparx5->regs[io->id] = begin[io->range] + io->offset;
}
return 0;
}
@ -465,44 +481,45 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5)
return -ENODEV;
}
switch (freq) {
case SPX5_CORE_CLOCK_250MHZ:
clk_div = 10;
pol_upd_int = 312;
break;
case SPX5_CORE_CLOCK_500MHZ:
clk_div = 5;
pol_upd_int = 624;
break;
case SPX5_CORE_CLOCK_625MHZ:
clk_div = 4;
pol_upd_int = 780;
break;
default:
dev_err(sparx5->dev, "%d coreclock not supported on (%#04x)\n",
sparx5->coreclock, sparx5->target_ct);
return -EINVAL;
if (is_sparx5(sparx5)) {
switch (freq) {
case SPX5_CORE_CLOCK_250MHZ:
clk_div = 10;
pol_upd_int = 312;
break;
case SPX5_CORE_CLOCK_500MHZ:
clk_div = 5;
pol_upd_int = 624;
break;
case SPX5_CORE_CLOCK_625MHZ:
clk_div = 4;
pol_upd_int = 780;
break;
default:
dev_err(sparx5->dev,
"%d coreclock not supported on (%#04x)\n",
sparx5->coreclock, sparx5->target_ct);
return -EINVAL;
}
/* Configure the LCPLL */
spx5_rmw(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_SET(clk_div) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_SET(1),
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA,
sparx5, CLKGEN_LCPLL1_CORE_CLK_CFG);
}
/* Update state with chosen frequency */
sparx5->coreclock = freq;
/* Configure the LCPLL */
spx5_rmw(CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV_SET(clk_div) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA_SET(0) |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA_SET(1),
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_DIV |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_PRE_DIV |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_DIR |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_SEL |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_ROT_ENA |
CLKGEN_LCPLL1_CORE_CLK_CFG_CORE_CLK_ENA,
sparx5,
CLKGEN_LCPLL1_CORE_CLK_CFG);
clk_period = sparx5_clk_period(freq);
spx5_rmw(HSCH_SYS_CLK_PER_100PS_SET(clk_period / 100),
@ -525,7 +542,7 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5)
sparx5,
LRN_AUTOAGE_CFG_1);
for (idx = 0; idx < 3; idx++)
for (idx = 0; idx < sparx5->data->consts->n_sio_clks; idx++)
spx5_rmw(GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(clk_period / 100),
GCB_SIO_CLOCK_SYS_CLK_PERIOD,
sparx5,
@ -545,25 +562,36 @@ static int sparx5_init_coreclock(struct sparx5 *sparx5)
return 0;
}
static u32 qlim_wm(struct sparx5 *sparx5, int fraction)
{
return (sparx5->data->consts->buf_size / SPX5_BUFFER_CELL_SZ - 100) *
fraction / 100;
}
static int sparx5_qlim_set(struct sparx5 *sparx5)
{
const struct sparx5_consts *consts = sparx5->data->consts;
u32 res, dp, prio;
for (res = 0; res < 2; res++) {
for (prio = 0; prio < 8; prio++)
spx5_wr(0xFFF, sparx5,
QRES_RES_CFG(prio + 630 + res * 1024));
QRES_RES_CFG(prio +
consts->qres_max_prio_idx +
res * 1024));
for (dp = 0; dp < 4; dp++)
spx5_wr(0xFFF, sparx5,
QRES_RES_CFG(dp + 638 + res * 1024));
QRES_RES_CFG(dp +
consts->qres_max_colour_idx +
res * 1024));
}
/* Set 80,90,95,100% of memory size for top watermarks */
spx5_wr(QLIM_WM(80), sparx5, XQS_QLIMIT_SHR_QLIM_CFG(0));
spx5_wr(QLIM_WM(90), sparx5, XQS_QLIMIT_SHR_CTOP_CFG(0));
spx5_wr(QLIM_WM(95), sparx5, XQS_QLIMIT_SHR_ATOP_CFG(0));
spx5_wr(QLIM_WM(100), sparx5, XQS_QLIMIT_SHR_TOP_CFG(0));
spx5_wr(qlim_wm(sparx5, 80), sparx5, XQS_QLIMIT_SHR_QLIM_CFG(0));
spx5_wr(qlim_wm(sparx5, 90), sparx5, XQS_QLIMIT_SHR_CTOP_CFG(0));
spx5_wr(qlim_wm(sparx5, 95), sparx5, XQS_QLIMIT_SHR_ATOP_CFG(0));
spx5_wr(qlim_wm(sparx5, 100), sparx5, XQS_QLIMIT_SHR_TOP_CFG(0));
return 0;
}
@ -585,7 +613,7 @@ static void sparx5_board_init(struct sparx5 *sparx5)
GCB_HW_SGPIO_SD_CFG);
/* Refer to LOS SGPIO */
for (idx = 0; idx < SPX5_PORTS; idx++)
for (idx = 0; idx < sparx5->data->consts->n_ports; idx++)
if (sparx5->ports[idx])
if (sparx5->ports[idx]->conf.sd_sgpio != ~0)
spx5_wr(sparx5->ports[idx]->conf.sd_sgpio,
@ -596,12 +624,14 @@ static void sparx5_board_init(struct sparx5 *sparx5)
static int sparx5_start(struct sparx5 *sparx5)
{
u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const struct sparx5_consts *consts = sparx5->data->consts;
const struct sparx5_ops *ops = sparx5->data->ops;
char queue_name[32];
u32 idx;
int err;
/* Setup own UPSIDs */
for (idx = 0; idx < 3; idx++) {
for (idx = 0; idx < consts->n_own_upsids; idx++) {
spx5_wr(idx, sparx5, ANA_AC_OWN_UPSID(idx));
spx5_wr(idx, sparx5, ANA_CL_OWN_UPSID(idx));
spx5_wr(idx, sparx5, ANA_L2_OWN_UPSID(idx));
@ -609,7 +639,7 @@ static int sparx5_start(struct sparx5 *sparx5)
}
/* Enable CPU ports */
for (idx = SPX5_PORTS; idx < SPX5_PORTS_ALL; idx++)
for (idx = consts->n_ports; idx < consts->n_ports_all; idx++)
spx5_rmw(QFWD_SWITCH_PORT_MODE_PORT_ENA_SET(1),
QFWD_SWITCH_PORT_MODE_PORT_ENA,
sparx5,
@ -619,13 +649,14 @@ static int sparx5_start(struct sparx5 *sparx5)
sparx5_update_fwd(sparx5);
/* CPU copy CPU pgids */
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
sparx5, ANA_AC_PGID_MISC_CFG(PGID_CPU));
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
sparx5, ANA_AC_PGID_MISC_CFG(PGID_BCAST));
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5,
ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_CPU)));
spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5,
ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_BCAST)));
/* Recalc injected frame FCS */
for (idx = SPX5_PORT_CPU_0; idx <= SPX5_PORT_CPU_1; idx++)
for (idx = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0);
idx <= sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1); idx++)
spx5_rmw(ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA_SET(1),
ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
sparx5, ANA_CL_FILTER_CTRL(idx));
@ -640,7 +671,8 @@ static int sparx5_start(struct sparx5 *sparx5)
sparx5_vlan_init(sparx5);
/* Add host mode BC address (points only to CPU) */
sparx5_mact_learn(sparx5, PGID_CPU, broadcast, NULL_VID);
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), broadcast,
NULL_VID);
/* Enable queue limitation watermarks */
sparx5_qlim_set(sparx5);
@ -720,7 +752,7 @@ static int sparx5_start(struct sparx5 *sparx5)
if (sparx5->ptp_irq >= 0) {
err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq,
NULL, sparx5_ptp_irq_handler,
NULL, ops->ptp_irq_handler,
IRQF_ONESHOT, "sparx5-ptp",
sparx5);
if (err)
@ -759,6 +791,12 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
sparx5->dev = &pdev->dev;
spin_lock_init(&sparx5->tx_lock);
sparx5->data = device_get_match_data(sparx5->dev);
if (!sparx5->data)
return -EINVAL;
regs = sparx5->data->regs;
/* Do switch core reset if available */
reset = devm_reset_control_get_optional_shared(&pdev->dev, "switch");
if (IS_ERR(reset))
@ -937,8 +975,62 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
destroy_workqueue(sparx5->mact_queue);
}
static const struct sparx5_regs sparx5_regs = {
.tsize = sparx5_tsize,
.gaddr = sparx5_gaddr,
.gcnt = sparx5_gcnt,
.gsize = sparx5_gsize,
.raddr = sparx5_raddr,
.rcnt = sparx5_rcnt,
.fpos = sparx5_fpos,
.fsize = sparx5_fsize,
};
static const struct sparx5_consts sparx5_consts = {
.n_ports = 65,
.n_ports_all = 70,
.n_hsch_l1_elems = 64,
.n_hsch_queues = 8,
.n_lb_groups = 10,
.n_pgids = 2113, /* (2048 + n_ports) */
.n_sio_clks = 3,
.n_own_upsids = 3,
.n_auto_cals = 7,
.n_filters = 1024,
.n_gates = 1024,
.n_sdlbs = 4096,
.n_dsm_cal_taxis = 8,
.buf_size = 4194280,
.qres_max_prio_idx = 630,
.qres_max_colour_idx = 638,
.tod_pin = 4,
};
static const struct sparx5_ops sparx5_ops = {
.is_port_2g5 = &sparx5_port_is_2g5,
.is_port_5g = &sparx5_port_is_5g,
.is_port_10g = &sparx5_port_is_10g,
.is_port_25g = &sparx5_port_is_25g,
.get_port_dev_index = &sparx5_port_dev_mapping,
.get_port_dev_bit = &sparx5_port_dev_mapping,
.get_hsch_max_group_rate = &sparx5_get_hsch_max_group_rate,
.get_sdlb_group = &sparx5_get_sdlb_group,
.set_port_mux = &sparx5_port_mux_set,
.ptp_irq_handler = &sparx5_ptp_irq_handler,
.dsm_calendar_calc = &sparx5_dsm_calendar_calc,
};
static const struct sparx5_match_data sparx5_desc = {
.iomap = sparx5_main_iomap,
.iomap_size = ARRAY_SIZE(sparx5_main_iomap),
.ioranges = 3,
.regs = &sparx5_regs,
.consts = &sparx5_consts,
.ops = &sparx5_ops,
};
static const struct of_device_id mchp_sparx5_match[] = {
{ .compatible = "microchip,sparx5-switch" },
{ .compatible = "microchip,sparx5-switch", .data = &sparx5_desc },
{ }
};
MODULE_DEVICE_TABLE(of, mchp_sparx5_match);

View File

@ -52,24 +52,23 @@ enum sparx5_vlan_port_type {
};
#define SPX5_PORTS 65
#define SPX5_PORT_CPU (SPX5_PORTS) /* Next port is CPU port */
#define SPX5_PORT_CPU_0 (SPX5_PORT_CPU + 0) /* CPU Port 65 */
#define SPX5_PORT_CPU_1 (SPX5_PORT_CPU + 1) /* CPU Port 66 */
#define SPX5_PORT_VD0 (SPX5_PORT_CPU + 2) /* VD0/Port 67 used for IPMC */
#define SPX5_PORT_VD1 (SPX5_PORT_CPU + 3) /* VD1/Port 68 used for AFI/OAM */
#define SPX5_PORT_VD2 (SPX5_PORT_CPU + 4) /* VD2/Port 69 used for IPinIP*/
#define SPX5_PORTS_ALL (SPX5_PORT_CPU + 5) /* Total number of ports */
#define SPX5_PORTS_ALL 70 /* Total number of ports */
#define PGID_BASE SPX5_PORTS /* Starts after port PGIDs */
#define PGID_UC_FLOOD (PGID_BASE + 0)
#define PGID_MC_FLOOD (PGID_BASE + 1)
#define PGID_IPV4_MC_DATA (PGID_BASE + 2)
#define PGID_IPV4_MC_CTRL (PGID_BASE + 3)
#define PGID_IPV6_MC_DATA (PGID_BASE + 4)
#define PGID_IPV6_MC_CTRL (PGID_BASE + 5)
#define PGID_BCAST (PGID_BASE + 6)
#define PGID_CPU (PGID_BASE + 7)
#define PGID_MCAST_START (PGID_BASE + 8)
#define SPX5_PORT_CPU_0 0 /* CPU Port 0 */
#define SPX5_PORT_CPU_1 1 /* CPU Port 1 */
#define SPX5_PORT_VD0 2 /* VD0/Port used for IPMC */
#define SPX5_PORT_VD1 3 /* VD1/Port used for AFI/OAM */
#define SPX5_PORT_VD2 4 /* VD2/Port used for IPinIP*/
#define PGID_UC_FLOOD 0
#define PGID_MC_FLOOD 1
#define PGID_IPV4_MC_DATA 2
#define PGID_IPV4_MC_CTRL 3
#define PGID_IPV6_MC_DATA 4
#define PGID_IPV6_MC_CTRL 5
#define PGID_BCAST 6
#define PGID_CPU 7
#define PGID_MCAST_START 8
#define PGID_TABLE_SIZE 3290
@ -100,8 +99,24 @@ enum sparx5_vlan_port_type {
#define IFH_PDU_TYPE_IPV4_UDP_PTP 0x6
#define IFH_PDU_TYPE_IPV6_UDP_PTP 0x7
#define SPX5_DSM_CAL_LEN 64
#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
struct sparx5;
struct sparx5_calendar_data {
u32 schedule[SPX5_DSM_CAL_LEN];
u32 avg_dist[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 taxi_ports[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 taxi_speeds[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 dev_slots[SPX5_DSM_CAL_MAX_DEVS_PER_TAXI];
u32 new_slots[SPX5_DSM_CAL_LEN];
u32 temp_sched[SPX5_DSM_CAL_LEN];
u32 indices[SPX5_DSM_CAL_LEN];
u32 short_list[SPX5_DSM_CAL_LEN];
u32 long_list[SPX5_DSM_CAL_LEN];
};
/* Frame DMA receive state:
* For each DB, there is a SKB, and the skb data pointer is mapped in
* the DB. Once a frame is received the skb is given to the upper layers
@ -226,6 +241,69 @@ struct sparx5_mall_entry {
#define SPARX5_SKB_CB(skb) \
((struct sparx5_skb_cb *)((skb)->cb))
struct sparx5_regs {
const unsigned int *tsize;
const unsigned int *gaddr;
const unsigned int *gcnt;
const unsigned int *gsize;
const unsigned int *raddr;
const unsigned int *rcnt;
const unsigned int *fpos;
const unsigned int *fsize;
};
struct sparx5_consts {
u32 n_ports; /* Number of front ports */
u32 n_ports_all; /* Number of front ports + internal ports */
u32 n_hsch_l1_elems; /* Number of HSCH layer 1 elements */
u32 n_hsch_queues; /* Number of HSCH queues */
u32 n_lb_groups; /* Number of leacky bucket groupd */
u32 n_pgids; /* Number of PGID's */
u32 n_sio_clks; /* Number of serial IO clocks */
u32 n_own_upsids; /* Number of own UPSID's */
u32 n_auto_cals; /* Number of auto calendars */
u32 n_filters; /* Number of PSFP filters */
u32 n_gates; /* Number of PSFP gates */
u32 n_sdlbs; /* Number of service dual leaky buckets */
u32 n_dsm_cal_taxis; /* Number of DSM calendar taxis */
u32 buf_size; /* Amount of QLIM watermark memory */
u32 qres_max_prio_idx; /* Maximum QRES prio index */
u32 qres_max_colour_idx; /* Maximum QRES colour index */
u32 tod_pin; /* PTP TOD pin */
};
struct sparx5_ops {
bool (*is_port_2g5)(int portno);
bool (*is_port_5g)(int portno);
bool (*is_port_10g)(int portno);
bool (*is_port_25g)(int portno);
u32 (*get_port_dev_index)(struct sparx5 *sparx5, int port);
u32 (*get_port_dev_bit)(struct sparx5 *sparx5, int port);
u32 (*get_hsch_max_group_rate)(int grp);
struct sparx5_sdlb_group *(*get_sdlb_group)(int idx);
int (*set_port_mux)(struct sparx5 *sparx5, struct sparx5_port *port,
struct sparx5_port_config *conf);
irqreturn_t (*ptp_irq_handler)(int irq, void *args);
int (*dsm_calendar_calc)(struct sparx5 *sparx5, u32 taxi,
struct sparx5_calendar_data *data);
};
struct sparx5_main_io_resource {
enum sparx5_target id;
phys_addr_t offset;
int range;
};
struct sparx5_match_data {
const struct sparx5_regs *regs;
const struct sparx5_consts *consts;
const struct sparx5_ops *ops;
const struct sparx5_main_io_resource *iomap;
int ioranges;
int iomap_size;
};
struct sparx5 {
struct platform_device *pdev;
struct device *dev;
@ -293,8 +371,12 @@ struct sparx5 {
struct list_head mall_entries;
/* Common root for debugfs */
struct dentry *debugfs_root;
const struct sparx5_match_data *data;
};
/* sparx5_main.c */
bool is_sparx5(struct sparx5 *sparx5);
/* sparx5_switchdev.c */
int sparx5_register_notifier_blocks(struct sparx5 *sparx5);
void sparx5_unregister_notifier_blocks(struct sparx5 *sparx5);
@ -355,6 +437,9 @@ void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
/* sparx5_calendar.c */
int sparx5_config_auto_calendar(struct sparx5 *sparx5);
int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
struct sparx5_calendar_data *data);
/* sparx5_ethtool.c */
void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats);
@ -375,7 +460,7 @@ void sparx5_set_port_ifh_timestamp(void *ifh_hdr, u64 timestamp);
void sparx5_set_port_ifh_rew_op(void *ifh_hdr, u32 rew_op);
void sparx5_set_port_ifh_pdu_type(void *ifh_hdr, u32 pdu_type);
void sparx5_set_port_ifh_pdu_w16_offset(void *ifh_hdr, u32 pdu_w16_offset);
void sparx5_set_port_ifh(void *ifh_hdr, u16 portno);
void sparx5_set_port_ifh(struct sparx5 *sparx5, void *ifh_hdr, u16 portno);
bool sparx5_netdevice_check(const struct net_device *dev);
struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno);
int sparx5_register_netdevs(struct sparx5 *sparx5);
@ -413,6 +498,7 @@ enum sparx5_pgid_type {
void sparx5_pgid_init(struct sparx5 *spx5);
int sparx5_pgid_alloc_mcast(struct sparx5 *spx5, u16 *idx);
int sparx5_pgid_free(struct sparx5 *spx5, u16 idx);
int sparx5_get_pgid(struct sparx5 *sparx5, int pgid);
/* sparx5_pool.c */
struct sparx5_pool_entry {
@ -426,6 +512,11 @@ int sparx5_pool_get(struct sparx5_pool_entry *pool, int size, u32 *id);
int sparx5_pool_get_with_idx(struct sparx5_pool_entry *pool, int size, u32 idx,
u32 *id);
/* sparx5_port.c */
int sparx5_port_mux_set(struct sparx5 *sparx5, struct sparx5_port *port,
struct sparx5_port_config *conf);
int sparx5_get_internal_port(struct sparx5 *sparx5, int port);
/* sparx5_sdlb.c */
#define SPX5_SDLB_PUP_TOKEN_DISABLE 0x1FFF
#define SPX5_SDLB_PUP_TOKEN_MAX (SPX5_SDLB_PUP_TOKEN_DISABLE - 1)
@ -444,6 +535,7 @@ struct sparx5_sdlb_group {
};
extern struct sparx5_sdlb_group sdlb_groups[SPX5_SDLB_GROUP_CNT];
struct sparx5_sdlb_group *sparx5_get_sdlb_group(int idx);
int sparx5_sdlb_pup_token_get(struct sparx5 *sparx5, u32 pup_interval,
u64 rate);

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,7 @@ static void __ifh_encode_bitfield(void *ifh, u64 value, u32 pos, u32 width)
ifh_hdr[byte - 5] |= (u8)((encode & 0xFF0000000000) >> 40);
}
void sparx5_set_port_ifh(void *ifh_hdr, u16 portno)
void sparx5_set_port_ifh(struct sparx5 *sparx5, void *ifh_hdr, u16 portno)
{
/* VSTAX.RSV = 1. MSBit must be 1 */
ifh_encode_bitfield(ifh_hdr, 1, VSTAX + 79, 1);
@ -68,7 +68,8 @@ void sparx5_set_port_ifh(void *ifh_hdr, u16 portno)
/* MISC.PIPELINE_ACT */
ifh_encode_bitfield(ifh_hdr, 1, 42, 3);
/* FWD.SRC_PORT = CPU */
ifh_encode_bitfield(ifh_hdr, SPX5_PORT_CPU, 46, 7);
ifh_encode_bitfield(ifh_hdr, sparx5_get_pgid(sparx5, SPX5_PORT_CPU_0),
46, 7);
/* FWD.SFLOW_ID (disable SFlow sampling) */
ifh_encode_bitfield(ifh_hdr, 124, 57, 7);
/* FWD.UPDATE_FCS = Enable. Enforce update of FCS. */
@ -190,7 +191,8 @@ static int sparx5_set_mac_address(struct net_device *dev, void *p)
sparx5_mact_forget(sparx5, dev->dev_addr, port->pvid);
/* Add new */
sparx5_mact_learn(sparx5, PGID_CPU, addr->sa_data, port->pvid);
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU),
addr->sa_data, port->pvid);
/* Record the address */
eth_hw_addr_set(dev, addr->sa_data);
@ -290,7 +292,7 @@ int sparx5_register_netdevs(struct sparx5 *sparx5)
int portno;
int err;
for (portno = 0; portno < SPX5_PORTS; portno++)
for (portno = 0; portno < sparx5->data->consts->n_ports; portno++)
if (sparx5->ports[portno]) {
err = register_netdev(sparx5->ports[portno]->ndev);
if (err) {
@ -309,7 +311,7 @@ void sparx5_destroy_netdevs(struct sparx5 *sparx5)
struct sparx5_port *port;
int portno;
for (portno = 0; portno < SPX5_PORTS; portno++) {
for (portno = 0; portno < sparx5->data->consts->n_ports; portno++) {
port = sparx5->ports[portno];
if (port && port->phylink) {
/* Disconnect the phy */
@ -327,8 +329,7 @@ void sparx5_unregister_netdevs(struct sparx5 *sparx5)
{
int portno;
for (portno = 0; portno < SPX5_PORTS; portno++)
for (portno = 0; portno < sparx5->data->consts->n_ports; portno++)
if (sparx5->ports[portno])
unregister_netdev(sparx5->ports[portno]->ndev);
}

View File

@ -75,7 +75,7 @@ static void sparx5_xtr_grp(struct sparx5 *sparx5, u8 grp, bool byte_swap)
sparx5_ifh_parse(ifh, &fi);
/* Map to port netdev */
port = fi.src_port < SPX5_PORTS ?
port = fi.src_port < sparx5->data->consts->n_ports ?
sparx5->ports[fi.src_port] : NULL;
if (!port || !port->ndev) {
dev_err(sparx5->dev, "Data on inactive port %d\n", fi.src_port);
@ -235,7 +235,7 @@ netdev_tx_t sparx5_port_xmit_impl(struct sk_buff *skb, struct net_device *dev)
netdev_tx_t ret;
memset(ifh, 0, IFH_LEN * 4);
sparx5_set_port_ifh(ifh, port->portno);
sparx5_set_port_ifh(sparx5, ifh, port->portno);
if (sparx5->ptp && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) {
if (sparx5_ptp_txtstamp_request(port, skb) < 0)
@ -317,7 +317,9 @@ int sparx5_manual_injection_mode(struct sparx5 *sparx5)
sparx5, QS_INJ_GRP_CFG(INJ_QUEUE));
/* CPU ports capture setup */
for (portno = SPX5_PORT_CPU_0; portno <= SPX5_PORT_CPU_1; portno++) {
for (portno = sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_0);
portno <= sparx5_get_internal_port(sparx5, SPX5_PORT_CPU_1);
portno++) {
/* ASM CPU port: No preamble, IFH, enable padding */
spx5_wr(ASM_PORT_CFG_PAD_ENA_SET(1) |
ASM_PORT_CFG_NO_PREAMBLE_ENA_SET(1) |

View File

@ -5,13 +5,13 @@ void sparx5_pgid_init(struct sparx5 *spx5)
{
int i;
for (i = 0; i < PGID_TABLE_SIZE; i++)
for (i = 0; i < spx5->data->consts->n_pgids; i++)
spx5->pgid_map[i] = SPX5_PGID_FREE;
/* Reserved for unicast, flood control, broadcast, and CPU.
* These cannot be freed.
*/
for (i = 0; i <= PGID_CPU; i++)
for (i = 0; i <= sparx5_get_pgid(spx5, PGID_CPU); i++)
spx5->pgid_map[i] = SPX5_PGID_RESERVED;
}
@ -22,7 +22,8 @@ int sparx5_pgid_alloc_mcast(struct sparx5 *spx5, u16 *idx)
/* The multicast area starts at index 65, but the first 7
* are reserved for flood masks and CPU. Start alloc after that.
*/
for (i = PGID_MCAST_START; i < PGID_TABLE_SIZE; i++) {
for (i = sparx5_get_pgid(spx5, PGID_MCAST_START);
i < spx5->data->consts->n_pgids; i++) {
if (spx5->pgid_map[i] == SPX5_PGID_FREE) {
spx5->pgid_map[i] = SPX5_PGID_MULTICAST;
*idx = i;
@ -35,7 +36,8 @@ int sparx5_pgid_alloc_mcast(struct sparx5 *spx5, u16 *idx)
int sparx5_pgid_free(struct sparx5 *spx5, u16 idx)
{
if (idx <= PGID_CPU || idx >= PGID_TABLE_SIZE)
if (idx <= sparx5_get_pgid(spx5, PGID_CPU) ||
idx >= spx5->data->consts->n_pgids)
return -EINVAL;
if (spx5->pgid_map[idx] == SPX5_PGID_FREE)
@ -44,3 +46,8 @@ int sparx5_pgid_free(struct sparx5 *spx5, u16 idx)
spx5->pgid_map[idx] = SPX5_PGID_FREE;
return 0;
}
int sparx5_get_pgid(struct sparx5 *sparx5, int pgid)
{
return sparx5->data->consts->n_ports + pgid;
}

View File

@ -11,10 +11,11 @@ static int sparx5_policer_service_conf_set(struct sparx5 *sparx5,
struct sparx5_policer *pol)
{
u32 idx, pup_tokens, max_pup_tokens, burst, thres;
const struct sparx5_ops *ops = sparx5->data->ops;
struct sparx5_sdlb_group *g;
u64 rate;
g = &sdlb_groups[pol->group];
g = ops->get_sdlb_group(pol->group);
idx = pol->idx;
rate = pol->rate * 1000;

View File

@ -132,8 +132,8 @@ static int sparx5_get_sfi_status(struct sparx5 *sparx5,
return -EINVAL;
}
dev = sparx5_to_high_dev(portno);
tinst = sparx5_port_dev_index(portno);
dev = sparx5_to_high_dev(sparx5, portno);
tinst = sparx5_port_dev_index(sparx5, portno);
inst = spx5_inst_get(sparx5, dev, tinst);
value = spx5_inst_rd(inst, DEV10G_MAC_TX_MONITOR_STICKY(0));
@ -213,11 +213,13 @@ static int sparx5_port_verify_speed(struct sparx5 *sparx5,
struct sparx5_port *port,
struct sparx5_port_config *conf)
{
if ((sparx5_port_is_2g5(port->portno) &&
const struct sparx5_ops *ops = sparx5->data->ops;
if ((ops->is_port_2g5(port->portno) &&
conf->speed > SPEED_2500) ||
(sparx5_port_is_5g(port->portno) &&
(ops->is_port_5g(port->portno) &&
conf->speed > SPEED_5000) ||
(sparx5_port_is_10g(port->portno) &&
(ops->is_port_10g(port->portno) &&
conf->speed > SPEED_10000))
return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
@ -226,14 +228,14 @@ static int sparx5_port_verify_speed(struct sparx5 *sparx5,
return -EINVAL;
case PHY_INTERFACE_MODE_1000BASEX:
if (conf->speed != SPEED_1000 ||
sparx5_port_is_2g5(port->portno))
ops->is_port_2g5(port->portno))
return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
if (sparx5_port_is_2g5(port->portno))
if (ops->is_port_2g5(port->portno))
return sparx5_port_error(port, conf, SPX5_PERR_IFTYPE);
break;
case PHY_INTERFACE_MODE_2500BASEX:
if (conf->speed != SPEED_2500 ||
sparx5_port_is_2g5(port->portno))
ops->is_port_2g5(port->portno))
return sparx5_port_error(port, conf, SPX5_PERR_SPEED);
break;
case PHY_INTERFACE_MODE_QSGMII:
@ -316,10 +318,11 @@ static int sparx5_port_flush_poll(struct sparx5 *sparx5, u32 portno)
static int sparx5_port_disable(struct sparx5 *sparx5, struct sparx5_port *port, bool high_spd_dev)
{
u32 tinst = high_spd_dev ?
sparx5_port_dev_index(port->portno) : port->portno;
sparx5_port_dev_index(sparx5, port->portno) : port->portno;
u32 dev = high_spd_dev ?
sparx5_to_high_dev(port->portno) : TARGET_DEV2G5;
sparx5_to_high_dev(sparx5, port->portno) : TARGET_DEV2G5;
void __iomem *devinst = spx5_inst_get(sparx5, dev, tinst);
const struct sparx5_ops *ops = sparx5->data->ops;
u32 spd = port->conf.speed;
u32 spd_prm;
int err;
@ -427,7 +430,7 @@ static int sparx5_port_disable(struct sparx5 *sparx5, struct sparx5_port *port,
HSCH_FLUSH_CTRL);
if (high_spd_dev) {
u32 pcs = sparx5_to_pcs_dev(port->portno);
u32 pcs = sparx5_to_pcs_dev(sparx5, port->portno);
void __iomem *pcsinst = spx5_inst_get(sparx5, pcs, tinst);
/* 12: Disable 5G/10G/25 BaseR PCS */
@ -436,7 +439,7 @@ static int sparx5_port_disable(struct sparx5 *sparx5, struct sparx5_port *port,
pcsinst,
PCS10G_BR_PCS_CFG(0));
if (sparx5_port_is_25g(port->portno))
if (ops->is_port_25g(port->portno))
/* Disable 25G PCS */
spx5_rmw(DEV25G_PCS25G_CFG_PCS25G_ENA_SET(0),
DEV25G_PCS25G_CFG_PCS25G_ENA,
@ -513,9 +516,8 @@ static int sparx5_port_fifo_sz(struct sparx5 *sparx5,
/* Configure port muxing:
* QSGMII: 4x2G5 devices
*/
static int sparx5_port_mux_set(struct sparx5 *sparx5,
struct sparx5_port *port,
struct sparx5_port_config *conf)
int sparx5_port_mux_set(struct sparx5 *sparx5, struct sparx5_port *port,
struct sparx5_port_config *conf)
{
u32 portno = port->portno;
u32 inst;
@ -558,9 +560,10 @@ static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
bool dtag = max_tags == SPX5_PORT_MAX_TAGS_TWO;
enum sparx5_vlan_port_type vlan_type = port->vlan_type;
bool dotag = max_tags != SPX5_PORT_MAX_TAGS_NONE;
u32 dev = sparx5_to_high_dev(port->portno);
u32 tinst = sparx5_port_dev_index(port->portno);
u32 dev = sparx5_to_high_dev(sparx5, port->portno);
u32 tinst = sparx5_port_dev_index(sparx5, port->portno);
void __iomem *inst = spx5_inst_get(sparx5, dev, tinst);
const struct sparx5_ops *ops = sparx5->data->ops;
u32 etype;
etype = (vlan_type == SPX5_VLAN_PORT_TYPE_S_CUSTOM ?
@ -575,7 +578,7 @@ static int sparx5_port_max_tags_set(struct sparx5 *sparx5,
sparx5,
DEV2G5_MAC_TAGS_CFG(port->portno));
if (sparx5_port_is_2g5(port->portno))
if (ops->is_port_2g5(port->portno))
return 0;
spx5_inst_rmw(DEV10G_MAC_TAGS_CFG_TAG_ID_SET(etype) |
@ -789,9 +792,9 @@ static int sparx5_port_pcs_high_set(struct sparx5 *sparx5,
struct sparx5_port_config *conf)
{
u32 clk_spd = conf->portmode == PHY_INTERFACE_MODE_5GBASER ? 1 : 0;
u32 pix = sparx5_port_dev_index(port->portno);
u32 dev = sparx5_to_high_dev(port->portno);
u32 pcs = sparx5_to_pcs_dev(port->portno);
u32 pix = sparx5_port_dev_index(sparx5, port->portno);
u32 dev = sparx5_to_high_dev(sparx5, port->portno);
u32 pcs = sparx5_to_pcs_dev(sparx5, port->portno);
void __iomem *devinst;
void __iomem *pcsinst;
int err;
@ -843,19 +846,22 @@ static int sparx5_port_pcs_high_set(struct sparx5 *sparx5,
/* Switch between 1G/2500 and 5G/10G/25G devices */
static void sparx5_dev_switch(struct sparx5 *sparx5, int port, bool hsd)
{
int bt_indx = BIT(sparx5_port_dev_index(port));
const struct sparx5_ops *ops = sparx5->data->ops;
int bt_indx;
if (sparx5_port_is_5g(port)) {
bt_indx = BIT(ops->get_port_dev_bit(sparx5, port));
if (ops->is_port_5g(port)) {
spx5_rmw(hsd ? 0 : bt_indx,
bt_indx,
sparx5,
PORT_CONF_DEV5G_MODES);
} else if (sparx5_port_is_10g(port)) {
} else if (ops->is_port_10g(port)) {
spx5_rmw(hsd ? 0 : bt_indx,
bt_indx,
sparx5,
PORT_CONF_DEV10G_MODES);
} else if (sparx5_port_is_25g(port)) {
} else if (ops->is_port_25g(port)) {
spx5_rmw(hsd ? 0 : bt_indx,
bt_indx,
sparx5,
@ -1016,9 +1022,10 @@ int sparx5_port_init(struct sparx5 *sparx5,
{
u32 pause_start = sparx5_wm_enc(6 * (ETH_MAXLEN / SPX5_BUFFER_CELL_SZ));
u32 atop = sparx5_wm_enc(20 * (ETH_MAXLEN / SPX5_BUFFER_CELL_SZ));
u32 devhigh = sparx5_to_high_dev(port->portno);
u32 pix = sparx5_port_dev_index(port->portno);
u32 pcs = sparx5_to_pcs_dev(port->portno);
const struct sparx5_ops *ops = sparx5->data->ops;
u32 devhigh = sparx5_to_high_dev(sparx5, port->portno);
u32 pix = sparx5_port_dev_index(sparx5, port->portno);
u32 pcs = sparx5_to_pcs_dev(sparx5, port->portno);
bool sd_pol = port->signd_active_high;
bool sd_sel = !port->signd_internal;
bool sd_ena = port->signd_enable;
@ -1031,7 +1038,7 @@ int sparx5_port_init(struct sparx5 *sparx5,
pcsinst = spx5_inst_get(sparx5, pcs, pix);
/* Set the mux port mode */
err = sparx5_port_mux_set(sparx5, port, conf);
err = ops->set_port_mux(sparx5, port, conf);
if (err)
return err;
@ -1082,7 +1089,7 @@ int sparx5_port_init(struct sparx5 *sparx5,
if (err)
return err;
if (!sparx5_port_is_2g5(port->portno))
if (!ops->is_port_2g5(port->portno))
/* Enable shadow device */
spx5_rmw(DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA_SET(1),
DSM_DEV_TX_STOP_WM_CFG_DEV10G_SHADOW_ENA,
@ -1105,7 +1112,7 @@ int sparx5_port_init(struct sparx5 *sparx5,
sparx5,
DEV2G5_MAC_IFG_CFG(port->portno));
if (sparx5_port_is_2g5(port->portno))
if (ops->is_port_2g5(port->portno))
return 0; /* Low speed device only - return */
/* Now setup the high speed device */
@ -1128,7 +1135,7 @@ int sparx5_port_init(struct sparx5 *sparx5,
pcsinst,
PCS10G_BR_PCS_SD_CFG(0));
if (sparx5_port_is_25g(port->portno)) {
if (ops->is_port_25g(port->portno)) {
/* Handle Signal Detect in 25G PCS */
spx5_wr(DEV25G_PCS25G_SD_CFG_SD_POL_SET(sd_pol) |
DEV25G_PCS25G_SD_CFG_SD_SEL_SET(sd_sel) |
@ -1345,3 +1352,8 @@ int sparx5_port_qos_default_set(const struct sparx5_port *port,
return 0;
}
int sparx5_get_internal_port(struct sparx5 *sparx5, int port)
{
return sparx5->data->consts->n_ports + port;
}

View File

@ -40,25 +40,29 @@ static inline bool sparx5_port_is_25g(int portno)
return portno >= 56 && portno <= 63;
}
static inline u32 sparx5_to_high_dev(int port)
static inline u32 sparx5_to_high_dev(struct sparx5 *sparx5, int port)
{
if (sparx5_port_is_5g(port))
const struct sparx5_ops *ops = sparx5->data->ops;
if (ops->is_port_5g(port))
return TARGET_DEV5G;
if (sparx5_port_is_10g(port))
if (ops->is_port_10g(port))
return TARGET_DEV10G;
return TARGET_DEV25G;
}
static inline u32 sparx5_to_pcs_dev(int port)
static inline u32 sparx5_to_pcs_dev(struct sparx5 *sparx5, int port)
{
if (sparx5_port_is_5g(port))
const struct sparx5_ops *ops = sparx5->data->ops;
if (ops->is_port_5g(port))
return TARGET_PCS5G_BR;
if (sparx5_port_is_10g(port))
if (ops->is_port_10g(port))
return TARGET_PCS10G_BR;
return TARGET_PCS25G_BR;
}
static inline int sparx5_port_dev_index(int port)
static inline u32 sparx5_port_dev_mapping(struct sparx5 *sparx5, int port)
{
if (sparx5_port_is_2g5(port))
return port;
@ -70,6 +74,11 @@ static inline int sparx5_port_dev_index(int port)
return (port - 56);
}
static inline u32 sparx5_port_dev_index(struct sparx5 *sparx5, int port)
{
return sparx5->data->ops->get_port_dev_index(sparx5, port);
}
int sparx5_port_init(struct sparx5 *sparx5,
struct sparx5_port *spx5_port,
struct sparx5_port_config *conf);

View File

@ -20,36 +20,40 @@ static struct sparx5_pool_entry sparx5_psfp_sg_pool[SPX5_PSFP_SG_CNT];
/* Pool of available stream filters */
static struct sparx5_pool_entry sparx5_psfp_sf_pool[SPX5_PSFP_SF_CNT];
static int sparx5_psfp_sf_get(u32 *id)
static int sparx5_psfp_sf_get(struct sparx5 *sparx5, u32 *id)
{
return sparx5_pool_get(sparx5_psfp_sf_pool, SPX5_PSFP_SF_CNT, id);
return sparx5_pool_get(sparx5_psfp_sf_pool,
sparx5->data->consts->n_filters, id);
}
static int sparx5_psfp_sf_put(u32 id)
static int sparx5_psfp_sf_put(struct sparx5 *sparx5, u32 id)
{
return sparx5_pool_put(sparx5_psfp_sf_pool, SPX5_PSFP_SF_CNT, id);
return sparx5_pool_put(sparx5_psfp_sf_pool,
sparx5->data->consts->n_filters, id);
}
static int sparx5_psfp_sg_get(u32 idx, u32 *id)
static int sparx5_psfp_sg_get(struct sparx5 *sparx5, u32 idx, u32 *id)
{
return sparx5_pool_get_with_idx(sparx5_psfp_sg_pool, SPX5_PSFP_SG_CNT,
idx, id);
return sparx5_pool_get_with_idx(sparx5_psfp_sg_pool,
sparx5->data->consts->n_gates, idx, id);
}
static int sparx5_psfp_sg_put(u32 id)
static int sparx5_psfp_sg_put(struct sparx5 *sparx5, u32 id)
{
return sparx5_pool_put(sparx5_psfp_sg_pool, SPX5_PSFP_SG_CNT, id);
return sparx5_pool_put(sparx5_psfp_sg_pool,
sparx5->data->consts->n_gates, id);
}
static int sparx5_psfp_fm_get(u32 idx, u32 *id)
static int sparx5_psfp_fm_get(struct sparx5 *sparx5, u32 idx, u32 *id)
{
return sparx5_pool_get_with_idx(sparx5_psfp_fm_pool, SPX5_SDLB_CNT, idx,
id);
return sparx5_pool_get_with_idx(sparx5_psfp_fm_pool,
sparx5->data->consts->n_sdlbs, idx, id);
}
static int sparx5_psfp_fm_put(u32 id)
static int sparx5_psfp_fm_put(struct sparx5 *sparx5, u32 id)
{
return sparx5_pool_put(sparx5_psfp_fm_pool, SPX5_SDLB_CNT, id);
return sparx5_pool_put(sparx5_psfp_fm_pool,
sparx5->data->consts->n_sdlbs, id);
}
u32 sparx5_psfp_isdx_get_sf(struct sparx5 *sparx5, u32 isdx)
@ -205,7 +209,7 @@ int sparx5_psfp_sf_add(struct sparx5 *sparx5, const struct sparx5_psfp_sf *sf,
{
int ret;
ret = sparx5_psfp_sf_get(id);
ret = sparx5_psfp_sf_get(sparx5, id);
if (ret < 0)
return ret;
@ -220,7 +224,7 @@ int sparx5_psfp_sf_del(struct sparx5 *sparx5, u32 id)
sparx5_psfp_sf_set(sparx5, id, &sf);
return sparx5_psfp_sf_put(id);
return sparx5_psfp_sf_put(sparx5, id);
}
int sparx5_psfp_sg_add(struct sparx5 *sparx5, u32 uidx,
@ -229,7 +233,7 @@ int sparx5_psfp_sg_add(struct sparx5 *sparx5, u32 uidx,
ktime_t basetime;
int ret;
ret = sparx5_psfp_sg_get(uidx, id);
ret = sparx5_psfp_sg_get(sparx5, uidx, id);
if (ret < 0)
return ret;
/* Was already in use, no need to reconfigure */
@ -253,7 +257,7 @@ int sparx5_psfp_sg_del(struct sparx5 *sparx5, u32 id)
const struct sparx5_psfp_sg sg = { 0 };
int ret;
ret = sparx5_psfp_sg_put(id);
ret = sparx5_psfp_sg_put(sparx5, id);
if (ret < 0)
return ret;
/* Stream gate still in use ? */
@ -270,7 +274,7 @@ int sparx5_psfp_fm_add(struct sparx5 *sparx5, u32 uidx,
int ret;
/* Get flow meter */
ret = sparx5_psfp_fm_get(uidx, &fm->pol.idx);
ret = sparx5_psfp_fm_get(sparx5, uidx, &fm->pol.idx);
if (ret < 0)
return ret;
/* Was already in use, no need to reconfigure */
@ -303,7 +307,7 @@ int sparx5_psfp_fm_del(struct sparx5 *sparx5, u32 id)
if (ret < 0)
return ret;
ret = sparx5_psfp_fm_put(id);
ret = sparx5_psfp_fm_put(sparx5, id);
if (ret < 0)
return ret;
/* Do not reset flow-meter if still in use. */
@ -315,11 +319,12 @@ int sparx5_psfp_fm_del(struct sparx5 *sparx5, u32 id)
void sparx5_psfp_init(struct sparx5 *sparx5)
{
const struct sparx5_ops *ops = sparx5->data->ops;
const struct sparx5_sdlb_group *group;
int i;
for (i = 0; i < SPX5_SDLB_GROUP_CNT; i++) {
group = &sdlb_groups[i];
for (i = 0; i < sparx5->data->consts->n_lb_groups; i++) {
group = ops->get_sdlb_group(i);
sparx5_sdlb_group_init(sparx5, group->max_rate,
group->min_burst, group->frame_size, i);
}

View File

@ -274,6 +274,7 @@ static void sparx5_get_hwtimestamp(struct sparx5 *sparx5,
u32 nsec)
{
/* Read current PTP time to get seconds */
const struct sparx5_consts *consts = sparx5->data->consts;
unsigned long flags;
u32 curr_nsec;
@ -285,10 +286,10 @@ static void sparx5_get_hwtimestamp(struct sparx5 *sparx5,
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
ts->tv_sec = spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN));
curr_nsec = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN));
ts->tv_sec = spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(consts->tod_pin));
curr_nsec = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(consts->tod_pin));
ts->tv_nsec = nsec;
@ -440,8 +441,11 @@ static int sparx5_ptp_settime64(struct ptp_clock_info *ptp,
{
struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info);
struct sparx5 *sparx5 = phc->sparx5;
const struct sparx5_consts *consts;
unsigned long flags;
consts = sparx5->data->consts;
spin_lock_irqsave(&sparx5->ptp_clock_lock, flags);
/* Must be in IDLE mode before the time can be loaded */
@ -451,14 +455,14 @@ static int sparx5_ptp_settime64(struct ptp_clock_info *ptp,
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
/* Set new value */
spx5_wr(PTP_PTP_TOD_SEC_MSB_PTP_TOD_SEC_MSB_SET(upper_32_bits(ts->tv_sec)),
sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN));
sparx5, PTP_PTP_TOD_SEC_MSB(consts->tod_pin));
spx5_wr(lower_32_bits(ts->tv_sec),
sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN));
spx5_wr(ts->tv_nsec, sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN));
sparx5, PTP_PTP_TOD_SEC_LSB(consts->tod_pin));
spx5_wr(ts->tv_nsec, sparx5, PTP_PTP_TOD_NSEC(consts->tod_pin));
/* Apply new values */
spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_LOAD) |
@ -467,7 +471,7 @@ static int sparx5_ptp_settime64(struct ptp_clock_info *ptp,
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags);
@ -478,10 +482,13 @@ int sparx5_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info);
struct sparx5 *sparx5 = phc->sparx5;
const struct sparx5_consts *consts;
unsigned long flags;
time64_t s;
s64 ns;
consts = sparx5->data->consts;
spin_lock_irqsave(&sparx5->ptp_clock_lock, flags);
spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) |
@ -490,12 +497,12 @@ int sparx5_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
s = spx5_rd(sparx5, PTP_PTP_TOD_SEC_MSB(TOD_ACC_PIN));
s = spx5_rd(sparx5, PTP_PTP_TOD_SEC_MSB(consts->tod_pin));
s <<= 32;
s |= spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(TOD_ACC_PIN));
ns = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN));
s |= spx5_rd(sparx5, PTP_PTP_TOD_SEC_LSB(consts->tod_pin));
ns = spx5_rd(sparx5, PTP_PTP_TOD_NSEC(consts->tod_pin));
ns &= PTP_PTP_TOD_NSEC_PTP_TOD_NSEC;
spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags);
@ -515,6 +522,9 @@ static int sparx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct sparx5_phc *phc = container_of(ptp, struct sparx5_phc, info);
struct sparx5 *sparx5 = phc->sparx5;
const struct sparx5_consts *consts;
consts = sparx5->data->consts;
if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
unsigned long flags;
@ -528,10 +538,10 @@ static int sparx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
spx5_wr(PTP_PTP_TOD_NSEC_PTP_TOD_NSEC_SET(delta),
sparx5, PTP_PTP_TOD_NSEC(TOD_ACC_PIN));
sparx5, PTP_PTP_TOD_NSEC(consts->tod_pin));
/* Adjust time with the value of PTP_TOD_NSEC */
spx5_rmw(PTP_PTP_PIN_CFG_PTP_PIN_ACTION_SET(PTP_PIN_ACTION_DELTA) |
@ -540,7 +550,7 @@ static int sparx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
PTP_PTP_PIN_CFG_PTP_PIN_ACTION |
PTP_PTP_PIN_CFG_PTP_PIN_DOM |
PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
sparx5, PTP_PTP_PIN_CFG(TOD_ACC_PIN));
sparx5, PTP_PTP_PIN_CFG(consts->tod_pin));
spin_unlock_irqrestore(&sparx5->ptp_clock_lock, flags);
} else {
@ -630,7 +640,7 @@ int sparx5_ptp_init(struct sparx5 *sparx5)
/* Enable master counters */
spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0x7), sparx5, PTP_PTP_DOM_CFG);
for (i = 0; i < SPX5_PORTS; i++) {
for (i = 0; i < sparx5->data->consts->n_ports; i++) {
port = sparx5->ports[i];
if (!port)
continue;
@ -646,7 +656,7 @@ void sparx5_ptp_deinit(struct sparx5 *sparx5)
struct sparx5_port *port;
int i;
for (i = 0; i < SPX5_PORTS; i++) {
for (i = 0; i < sparx5->data->consts->n_ports; i++) {
port = sparx5->ports[i];
if (!port)
continue;

View File

@ -74,6 +74,11 @@ static const u32 spx5_hsch_max_group_rate[SPX5_HSCH_LEAK_GRP_CNT] = {
26214200 /* 26.214 Gbps */
};
u32 sparx5_get_hsch_max_group_rate(int grp)
{
return spx5_hsch_max_group_rate[grp];
}
static struct sparx5_layer layers[SPX5_HSCH_LAYER_CNT];
static u32 sparx5_lg_get_leak_time(struct sparx5 *sparx5, u32 layer, u32 group)
@ -385,6 +390,7 @@ static int sparx5_dwrr_conf_set(struct sparx5_port *port,
static int sparx5_leak_groups_init(struct sparx5 *sparx5)
{
const struct sparx5_ops *ops = sparx5->data->ops;
struct sparx5_layer *layer;
u32 sys_clk_per_100ps;
struct sparx5_lg *lg;
@ -397,7 +403,7 @@ static int sparx5_leak_groups_init(struct sparx5 *sparx5)
layer = &layers[i];
for (ii = 0; ii < SPX5_HSCH_LEAK_GRP_CNT; ii++) {
lg = &layer->leak_groups[ii];
lg->max_rate = spx5_hsch_max_group_rate[ii];
lg->max_rate = ops->get_hsch_max_group_rate(i);
/* Calculate the leak time in us, to serve a maximum
* rate of 'max_rate' for this group

View File

@ -79,4 +79,6 @@ int sparx5_tc_ets_add(struct sparx5_port *port,
int sparx5_tc_ets_del(struct sparx5_port *port);
u32 sparx5_get_hsch_max_group_rate(int grp);
#endif /* __SPARX5_QOS_H__ */

View File

@ -0,0 +1,219 @@
// SPDX-License-Identifier: GPL-2.0+
/* Microchip Sparx5 Switch driver
*
* Copyright (c) 2024 Microchip Technology Inc.
*/
/* This file is autogenerated by cml-utils 2024-09-24 14:02:24 +0200.
* Commit ID: 9d07b8d19363f3cd3590ddb3f7a2e2768e16524b
*/
#include "sparx5_regs.h"
const unsigned int sparx5_tsize[TSIZE_LAST] = {
[TC_DEV10G] = 12,
[TC_DEV2G5] = 65,
[TC_DEV5G] = 13,
[TC_PCS10G_BR] = 12,
[TC_PCS5G_BR] = 13,
};
const unsigned int sparx5_raddr[RADDR_LAST] = {
[RA_CPU_PROC_CTRL] = 176,
[RA_GCB_SOFT_RST] = 8,
[RA_GCB_HW_SGPIO_TO_SD_MAP_CFG] = 24,
};
const unsigned int sparx5_rcnt[RCNT_LAST] = {
[RC_ANA_AC_OWN_UPSID] = 3,
[RC_ANA_ACL_VCAP_S2_CFG] = 70,
[RC_ANA_ACL_OWN_UPSID] = 3,
[RC_ANA_CL_OWN_UPSID] = 3,
[RC_ANA_L2_OWN_UPSID] = 3,
[RC_ASM_PORT_CFG] = 67,
[RC_DSM_BUF_CFG] = 67,
[RC_DSM_DEV_TX_STOP_WM_CFG] = 67,
[RC_DSM_RX_PAUSE_CFG] = 67,
[RC_DSM_MAC_CFG] = 67,
[RC_DSM_MAC_ADDR_BASE_HIGH_CFG] = 65,
[RC_DSM_MAC_ADDR_BASE_LOW_CFG] = 65,
[RC_DSM_TAXI_CAL_CFG] = 9,
[RC_GCB_HW_SGPIO_TO_SD_MAP_CFG] = 65,
[RC_HSCH_PORT_MODE] = 70,
[RC_QFWD_SWITCH_PORT_MODE] = 70,
[RC_QSYS_PAUSE_CFG] = 70,
[RC_QSYS_ATOP] = 70,
[RC_QSYS_FWD_PRESSURE] = 70,
[RC_QSYS_CAL_AUTO] = 7,
[RC_REW_OWN_UPSID] = 3,
[RC_REW_RTAG_ETAG_CTRL] = 70,
};
const unsigned int sparx5_gaddr[GADDR_LAST] = {
[GA_ANA_AC_RAM_CTRL] = 839108,
[GA_ANA_AC_PS_COMMON] = 894472,
[GA_ANA_AC_MIRROR_PROBE] = 893696,
[GA_ANA_AC_SRC] = 849920,
[GA_ANA_AC_PGID] = 786432,
[GA_ANA_AC_TSN_SF] = 839136,
[GA_ANA_AC_TSN_SF_CFG] = 839680,
[GA_ANA_AC_TSN_SF_STATUS] = 839072,
[GA_ANA_AC_SG_ACCESS] = 839140,
[GA_ANA_AC_SG_CONFIG] = 851584,
[GA_ANA_AC_SG_STATUS] = 839088,
[GA_ANA_AC_SG_STATUS_STICKY] = 839152,
[GA_ANA_AC_STAT_GLOBAL_CFG_PORT] = 851552,
[GA_ANA_AC_STAT_CNT_CFG_PORT] = 843776,
[GA_ANA_AC_STAT_GLOBAL_CFG_ACL] = 893792,
[GA_ANA_ACL_COMMON] = 32768,
[GA_ANA_ACL_KEY_SEL] = 34200,
[GA_ANA_ACL_CNT_B] = 16384,
[GA_ANA_ACL_STICKY] = 36408,
[GA_ANA_AC_POL_POL_ALL_CFG] = 75968,
[GA_ANA_AC_POL_COMMON_BDLB] = 79048,
[GA_ANA_AC_POL_COMMON_BUM_SLB] = 79056,
[GA_ANA_AC_SDLB_LBGRP_TBL] = 295468,
[GA_ANA_CL_PORT] = 131072,
[GA_ANA_CL_COMMON] = 166912,
[GA_ANA_L2_COMMON] = 566024,
[GA_ANA_L3_COMMON] = 493632,
[GA_ANA_L3_VLAN_ARP_L3MC_STICKY] = 491460,
[GA_ASM_CFG] = 33280,
[GA_ASM_PFC_TIMER_CFG] = 34716,
[GA_ASM_LBK_WM_CFG] = 34744,
[GA_ASM_LBK_MISC_CFG] = 34756,
[GA_ASM_RAM_CTRL] = 34832,
[GA_EACL_ES2_KEY_SELECT_PROFILE] = 149504,
[GA_EACL_CNT_TBL] = 122880,
[GA_EACL_POL_CFG] = 150608,
[GA_EACL_ES2_STICKY] = 118696,
[GA_EACL_RAM_CTRL] = 118736,
[GA_GCB_SIO_CTRL] = 876,
[GA_HSCH_HSCH_DWRR] = 162816,
[GA_HSCH_HSCH_MISC] = 163104,
[GA_HSCH_HSCH_LEAK_LISTS] = 161664,
[GA_HSCH_SYSTEM] = 184000,
[GA_HSCH_MMGT] = 162368,
[GA_HSCH_TAS_CONFIG] = 162384,
[GA_PTP_PTP_CFG] = 320,
[GA_PTP_PTP_TOD_DOMAINS] = 336,
[GA_PTP_PHASE_DETECTOR_CTRL] = 420,
[GA_QSYS_CALCFG] = 2304,
[GA_QSYS_RAM_CTRL] = 2344,
[GA_REW_COMMON] = 387264,
[GA_REW_PORT] = 360448,
[GA_REW_VOE_PORT_LM_CNT] = 393216,
[GA_REW_RAM_CTRL] = 378696,
[GA_VOP_RAM_CTRL] = 279176,
[GA_XQS_SYSTEM] = 6768,
[GA_XQS_QLIMIT_SHR] = 7936,
};
const unsigned int sparx5_gcnt[GCNT_LAST] = {
[GC_ANA_AC_SRC] = 102,
[GC_ANA_AC_PGID] = 3290,
[GC_ANA_AC_TSN_SF_CFG] = 1024,
[GC_ANA_AC_STAT_CNT_CFG_PORT] = 70,
[GC_ANA_ACL_KEY_SEL] = 134,
[GC_ANA_ACL_CNT_A] = 4096,
[GC_ANA_ACL_CNT_B] = 4096,
[GC_ANA_AC_SDLB_LBGRP_TBL] = 10,
[GC_ANA_AC_SDLB_LBSET_TBL] = 4616,
[GC_ANA_CL_PORT] = 70,
[GC_ANA_L2_ISDX_LIMIT] = 1536,
[GC_ANA_L2_ISDX] = 4096,
[GC_ANA_L3_VLAN] = 5120,
[GC_ASM_DEV_STATISTICS] = 65,
[GC_EACL_ES2_KEY_SELECT_PROFILE] = 138,
[GC_EACL_CNT_TBL] = 2048,
[GC_GCB_SIO_CTRL] = 3,
[GC_HSCH_HSCH_CFG] = 5040,
[GC_HSCH_HSCH_DWRR] = 72,
[GC_PTP_PTP_PINS] = 5,
[GC_PTP_PHASE_DETECTOR_CTRL] = 5,
[GC_REW_PORT] = 70,
[GC_REW_VOE_PORT_LM_CNT] = 520,
};
const unsigned int sparx5_gsize[GSIZE_LAST] = {
[GW_ANA_AC_SRC] = 16,
[GW_ANA_L2_COMMON] = 700,
[GW_ASM_CFG] = 1088,
[GW_CPU_CPU_REGS] = 204,
[GW_FDMA_FDMA] = 428,
[GW_GCB_CHIP_REGS] = 424,
[GW_HSCH_TAS_CONFIG] = 12,
[GW_PTP_PHASE_DETECTOR_CTRL] = 8,
[GW_QSYS_PAUSE_CFG] = 1128,
};
const unsigned int sparx5_fpos[FPOS_LAST] = {
[FP_CPU_PROC_CTRL_AARCH64_MODE_ENA] = 12,
[FP_CPU_PROC_CTRL_L2_RST_INVALIDATE_DIS] = 11,
[FP_CPU_PROC_CTRL_L1_RST_INVALIDATE_DIS] = 10,
[FP_CPU_PROC_CTRL_BE_EXCEP_MODE] = 9,
[FP_CPU_PROC_CTRL_VINITHI] = 8,
[FP_CPU_PROC_CTRL_CFGTE] = 7,
[FP_CPU_PROC_CTRL_CP15S_DISABLE] = 6,
[FP_CPU_PROC_CTRL_PROC_CRYPTO_DISABLE] = 5,
[FP_CPU_PROC_CTRL_L2_FLUSH_REQ] = 1,
[FP_FDMA_CH_CFG_CH_XTR_STATUS_MODE] = 7,
[FP_FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY] = 6,
[FP_FDMA_CH_CFG_CH_INJ_PORT] = 5,
[FP_PTP_PTP_PIN_CFG_PTP_PIN_ACTION] = 26,
[FP_PTP_PTP_PIN_CFG_PTP_PIN_SYNC] = 24,
[FP_PTP_PTP_PIN_CFG_PTP_PIN_INV_POL] = 23,
[FP_PTP_PHAD_CTRL_PHAD_ENA] = 7,
[FP_PTP_PHAD_CTRL_PHAD_FAILED] = 6,
};
const unsigned int sparx5_fsize[FSIZE_LAST] = {
[FW_ANA_AC_PROBE_PORT_CFG_PROBE_PORT_MASK] = 32,
[FW_ANA_AC_SRC_CFG_PORT_MASK] = 32,
[FW_ANA_AC_PGID_CFG_PORT_MASK] = 32,
[FW_ANA_AC_TSN_SF_PORT_NUM] = 9,
[FW_ANA_AC_TSN_SF_CFG_TSN_SGID] = 10,
[FW_ANA_AC_TSN_SF_STATUS_TSN_SFID] = 10,
[FW_ANA_AC_SG_ACCESS_CTRL_SGID] = 10,
[FW_ANA_AC_PORT_SGE_CFG_MASK] = 16,
[FW_ANA_AC_SDLB_XLB_START_LBSET_START] = 13,
[FW_ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT] = 5,
[FW_ANA_AC_SDLB_LBGRP_STATE_TBL_PUP_LBSET_NEXT] = 13,
[FW_ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT] = 13,
[FW_ANA_AC_SDLB_XLB_NEXT_LBGRP] = 4,
[FW_ANA_AC_SDLB_INH_LBSET_ADDR_INH_LBSET_ADDR] = 13,
[FW_ANA_L2_AUTO_LRN_CFG_AUTO_LRN_ENA] = 32,
[FW_ANA_L2_DLB_CFG_DLB_IDX] = 13,
[FW_ANA_L2_TSN_CFG_TSN_SFID] = 10,
[FW_ANA_L3_VLAN_MASK_CFG_VLAN_PORT_MASK] = 32,
[FW_FDMA_CH_CFG_CH_DCB_DB_CNT] = 4,
[FW_GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL] = 9,
[FW_HSCH_SE_CFG_SE_DWRR_CNT] = 7,
[FW_HSCH_SE_CONNECT_SE_LEAK_LINK] = 16,
[FW_HSCH_SE_DLB_SENSE_SE_DLB_DPORT] = 7,
[FW_HSCH_HSCH_CFG_CFG_CFG_SE_IDX] = 13,
[FW_HSCH_HSCH_LEAK_CFG_LEAK_FIRST] = 16,
[FW_HSCH_FLUSH_CTRL_FLUSH_PORT] = 7,
[FW_HSCH_FLUSH_CTRL_FLUSH_HIER] = 16,
[FW_LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW] = 14,
[FW_LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX] = 11,
[FW_LRN_AUTOAGE_CFG_2_NEXT_ROW] = 14,
[FW_PTP_PTP_PIN_INTR_INTR_PTP] = 5,
[FW_PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA] = 5,
[FW_PTP_PTP_INTR_IDENT_INTR_PTP_IDENT] = 5,
[FW_PTP_PTP_PIN_CFG_PTP_PIN_SELECT] = 2,
[FW_QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL] = 7,
[FW_QRES_RES_CFG_WM_HIGH] = 12,
[FW_QRES_RES_STAT_MAXUSE] = 21,
[FW_QRES_RES_STAT_CUR_INUSE] = 21,
[FW_QSYS_PAUSE_CFG_PAUSE_START] = 12,
[FW_QSYS_PAUSE_CFG_PAUSE_STOP] = 12,
[FW_QSYS_ATOP_ATOP] = 12,
[FW_QSYS_ATOP_TOT_CFG_ATOP_TOT] = 12,
[FW_REW_RTAG_ETAG_CTRL_IPE_TBL] = 7,
[FW_XQS_STAT_CFG_STAT_VIEW] = 13,
[FW_XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP] = 15,
[FW_XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP] = 15,
[FW_XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP] = 15,
[FW_XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM] = 15,
};

View File

@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Microchip Sparx5 Switch driver
*
* Copyright (c) 2024 Microchip Technology Inc.
*/
/* This file is autogenerated by cml-utils 2024-09-24 14:02:24 +0200.
* Commit ID: 9d07b8d19363f3cd3590ddb3f7a2e2768e16524b
*/
#ifndef _SPARX5_REGS_H_
#define _SPARX5_REGS_H_
/* These enumerated values are used to index the platform specific structs
* containing the addresses, counts, size and positions, of register groups,
* registers and fields.
*/
enum sparx5_tsize_enum {
TC_DEV10G,
TC_DEV2G5,
TC_DEV5G,
TC_PCS10G_BR,
TC_PCS5G_BR,
TSIZE_LAST,
};
enum sparx5_raddr_enum {
RA_CPU_PROC_CTRL,
RA_GCB_SOFT_RST,
RA_GCB_HW_SGPIO_TO_SD_MAP_CFG,
RADDR_LAST,
};
enum sparx5_rcnt_enum {
RC_ANA_AC_OWN_UPSID,
RC_ANA_ACL_VCAP_S2_CFG,
RC_ANA_ACL_OWN_UPSID,
RC_ANA_CL_OWN_UPSID,
RC_ANA_L2_OWN_UPSID,
RC_ASM_PORT_CFG,
RC_DSM_BUF_CFG,
RC_DSM_DEV_TX_STOP_WM_CFG,
RC_DSM_RX_PAUSE_CFG,
RC_DSM_MAC_CFG,
RC_DSM_MAC_ADDR_BASE_HIGH_CFG,
RC_DSM_MAC_ADDR_BASE_LOW_CFG,
RC_DSM_TAXI_CAL_CFG,
RC_GCB_HW_SGPIO_TO_SD_MAP_CFG,
RC_HSCH_PORT_MODE,
RC_QFWD_SWITCH_PORT_MODE,
RC_QSYS_PAUSE_CFG,
RC_QSYS_ATOP,
RC_QSYS_FWD_PRESSURE,
RC_QSYS_CAL_AUTO,
RC_REW_OWN_UPSID,
RC_REW_RTAG_ETAG_CTRL,
RCNT_LAST,
};
enum sparx5_gaddr_enum {
GA_ANA_AC_RAM_CTRL,
GA_ANA_AC_PS_COMMON,
GA_ANA_AC_MIRROR_PROBE,
GA_ANA_AC_SRC,
GA_ANA_AC_PGID,
GA_ANA_AC_TSN_SF,
GA_ANA_AC_TSN_SF_CFG,
GA_ANA_AC_TSN_SF_STATUS,
GA_ANA_AC_SG_ACCESS,
GA_ANA_AC_SG_CONFIG,
GA_ANA_AC_SG_STATUS,
GA_ANA_AC_SG_STATUS_STICKY,
GA_ANA_AC_STAT_GLOBAL_CFG_PORT,
GA_ANA_AC_STAT_CNT_CFG_PORT,
GA_ANA_AC_STAT_GLOBAL_CFG_ACL,
GA_ANA_ACL_COMMON,
GA_ANA_ACL_KEY_SEL,
GA_ANA_ACL_CNT_B,
GA_ANA_ACL_STICKY,
GA_ANA_AC_POL_POL_ALL_CFG,
GA_ANA_AC_POL_COMMON_BDLB,
GA_ANA_AC_POL_COMMON_BUM_SLB,
GA_ANA_AC_SDLB_LBGRP_TBL,
GA_ANA_CL_PORT,
GA_ANA_CL_COMMON,
GA_ANA_L2_COMMON,
GA_ANA_L3_COMMON,
GA_ANA_L3_VLAN_ARP_L3MC_STICKY,
GA_ASM_CFG,
GA_ASM_PFC_TIMER_CFG,
GA_ASM_LBK_WM_CFG,
GA_ASM_LBK_MISC_CFG,
GA_ASM_RAM_CTRL,
GA_EACL_ES2_KEY_SELECT_PROFILE,
GA_EACL_CNT_TBL,
GA_EACL_POL_CFG,
GA_EACL_ES2_STICKY,
GA_EACL_RAM_CTRL,
GA_GCB_SIO_CTRL,
GA_HSCH_HSCH_DWRR,
GA_HSCH_HSCH_MISC,
GA_HSCH_HSCH_LEAK_LISTS,
GA_HSCH_SYSTEM,
GA_HSCH_MMGT,
GA_HSCH_TAS_CONFIG,
GA_PTP_PTP_CFG,
GA_PTP_PTP_TOD_DOMAINS,
GA_PTP_PHASE_DETECTOR_CTRL,
GA_QSYS_CALCFG,
GA_QSYS_RAM_CTRL,
GA_REW_COMMON,
GA_REW_PORT,
GA_REW_VOE_PORT_LM_CNT,
GA_REW_RAM_CTRL,
GA_VOP_RAM_CTRL,
GA_XQS_SYSTEM,
GA_XQS_QLIMIT_SHR,
GADDR_LAST,
};
enum sparx5_gcnt_enum {
GC_ANA_AC_SRC,
GC_ANA_AC_PGID,
GC_ANA_AC_TSN_SF_CFG,
GC_ANA_AC_STAT_CNT_CFG_PORT,
GC_ANA_ACL_KEY_SEL,
GC_ANA_ACL_CNT_A,
GC_ANA_ACL_CNT_B,
GC_ANA_AC_SDLB_LBGRP_TBL,
GC_ANA_AC_SDLB_LBSET_TBL,
GC_ANA_CL_PORT,
GC_ANA_L2_ISDX_LIMIT,
GC_ANA_L2_ISDX,
GC_ANA_L3_VLAN,
GC_ASM_DEV_STATISTICS,
GC_EACL_ES2_KEY_SELECT_PROFILE,
GC_EACL_CNT_TBL,
GC_GCB_SIO_CTRL,
GC_HSCH_HSCH_CFG,
GC_HSCH_HSCH_DWRR,
GC_PTP_PTP_PINS,
GC_PTP_PHASE_DETECTOR_CTRL,
GC_REW_PORT,
GC_REW_VOE_PORT_LM_CNT,
GCNT_LAST,
};
enum sparx5_gsize_enum {
GW_ANA_AC_SRC,
GW_ANA_L2_COMMON,
GW_ASM_CFG,
GW_CPU_CPU_REGS,
GW_FDMA_FDMA,
GW_GCB_CHIP_REGS,
GW_HSCH_TAS_CONFIG,
GW_PTP_PHASE_DETECTOR_CTRL,
GW_QSYS_PAUSE_CFG,
GSIZE_LAST,
};
enum sparx5_fpos_enum {
FP_CPU_PROC_CTRL_AARCH64_MODE_ENA,
FP_CPU_PROC_CTRL_L2_RST_INVALIDATE_DIS,
FP_CPU_PROC_CTRL_L1_RST_INVALIDATE_DIS,
FP_CPU_PROC_CTRL_BE_EXCEP_MODE,
FP_CPU_PROC_CTRL_VINITHI,
FP_CPU_PROC_CTRL_CFGTE,
FP_CPU_PROC_CTRL_CP15S_DISABLE,
FP_CPU_PROC_CTRL_PROC_CRYPTO_DISABLE,
FP_CPU_PROC_CTRL_L2_FLUSH_REQ,
FP_FDMA_CH_CFG_CH_XTR_STATUS_MODE,
FP_FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY,
FP_FDMA_CH_CFG_CH_INJ_PORT,
FP_PTP_PTP_PIN_CFG_PTP_PIN_ACTION,
FP_PTP_PTP_PIN_CFG_PTP_PIN_SYNC,
FP_PTP_PTP_PIN_CFG_PTP_PIN_INV_POL,
FP_PTP_PHAD_CTRL_PHAD_ENA,
FP_PTP_PHAD_CTRL_PHAD_FAILED,
FPOS_LAST,
};
enum sparx5_fsize_enum {
FW_ANA_AC_PROBE_PORT_CFG_PROBE_PORT_MASK,
FW_ANA_AC_SRC_CFG_PORT_MASK,
FW_ANA_AC_PGID_CFG_PORT_MASK,
FW_ANA_AC_TSN_SF_PORT_NUM,
FW_ANA_AC_TSN_SF_CFG_TSN_SGID,
FW_ANA_AC_TSN_SF_STATUS_TSN_SFID,
FW_ANA_AC_SG_ACCESS_CTRL_SGID,
FW_ANA_AC_PORT_SGE_CFG_MASK,
FW_ANA_AC_SDLB_XLB_START_LBSET_START,
FW_ANA_AC_SDLB_LBGRP_MISC_THRES_SHIFT,
FW_ANA_AC_SDLB_LBGRP_STATE_TBL_PUP_LBSET_NEXT,
FW_ANA_AC_SDLB_XLB_NEXT_LBSET_NEXT,
FW_ANA_AC_SDLB_XLB_NEXT_LBGRP,
FW_ANA_AC_SDLB_INH_LBSET_ADDR_INH_LBSET_ADDR,
FW_ANA_L2_AUTO_LRN_CFG_AUTO_LRN_ENA,
FW_ANA_L2_DLB_CFG_DLB_IDX,
FW_ANA_L2_TSN_CFG_TSN_SFID,
FW_ANA_L3_VLAN_MASK_CFG_VLAN_PORT_MASK,
FW_FDMA_CH_CFG_CH_DCB_DB_CNT,
FW_GCB_HW_SGPIO_TO_SD_MAP_CFG_SGPIO_TO_SD_SEL,
FW_HSCH_SE_CFG_SE_DWRR_CNT,
FW_HSCH_SE_CONNECT_SE_LEAK_LINK,
FW_HSCH_SE_DLB_SENSE_SE_DLB_DPORT,
FW_HSCH_HSCH_CFG_CFG_CFG_SE_IDX,
FW_HSCH_HSCH_LEAK_CFG_LEAK_FIRST,
FW_HSCH_FLUSH_CTRL_FLUSH_PORT,
FW_HSCH_FLUSH_CTRL_FLUSH_HIER,
FW_LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_DIRECT_ROW,
FW_LRN_MAC_ACCESS_CFG_3_MAC_ENTRY_ISDX_LIMIT_IDX,
FW_LRN_AUTOAGE_CFG_2_NEXT_ROW,
FW_PTP_PTP_PIN_INTR_INTR_PTP,
FW_PTP_PTP_PIN_INTR_ENA_INTR_PTP_ENA,
FW_PTP_PTP_INTR_IDENT_INTR_PTP_IDENT,
FW_PTP_PTP_PIN_CFG_PTP_PIN_SELECT,
FW_QFWD_FRAME_COPY_CFG_FRMC_PORT_VAL,
FW_QRES_RES_CFG_WM_HIGH,
FW_QRES_RES_STAT_MAXUSE,
FW_QRES_RES_STAT_CUR_INUSE,
FW_QSYS_PAUSE_CFG_PAUSE_START,
FW_QSYS_PAUSE_CFG_PAUSE_STOP,
FW_QSYS_ATOP_ATOP,
FW_QSYS_ATOP_TOT_CFG_ATOP_TOT,
FW_REW_RTAG_ETAG_CTRL_IPE_TBL,
FW_XQS_STAT_CFG_STAT_VIEW,
FW_XQS_QLIMIT_SHR_TOP_CFG_QLIMIT_SHR_TOP,
FW_XQS_QLIMIT_SHR_ATOP_CFG_QLIMIT_SHR_ATOP,
FW_XQS_QLIMIT_SHR_CTOP_CFG_QLIMIT_SHR_CTOP,
FW_XQS_QLIMIT_SHR_QLIM_CFG_QLIMIT_SHR_QLIM,
FSIZE_LAST,
};
extern const unsigned int sparx5_tsize[TSIZE_LAST];
extern const unsigned int sparx5_raddr[RADDR_LAST];
extern const unsigned int sparx5_rcnt[RCNT_LAST];
extern const unsigned int sparx5_gaddr[GADDR_LAST];
extern const unsigned int sparx5_gcnt[GCNT_LAST];
extern const unsigned int sparx5_gsize[GSIZE_LAST];
extern const unsigned int sparx5_fpos[FPOS_LAST];
extern const unsigned int sparx5_fsize[FSIZE_LAST];
#endif /* _SPARX5_REGS_H_ */

View File

@ -20,6 +20,11 @@ struct sparx5_sdlb_group sdlb_groups[SPX5_SDLB_GROUP_CNT] = {
{ 5000000ULL, 8192 / 8, 64 } /* 5 M */
};
struct sparx5_sdlb_group *sparx5_get_sdlb_group(int idx)
{
return &sdlb_groups[idx];
}
int sparx5_sdlb_clk_hz_get(struct sparx5 *sparx5)
{
u32 clk_per_100ps;
@ -178,14 +183,15 @@ static int sparx5_sdlb_group_get_count(struct sparx5 *sparx5, u32 group)
int sparx5_sdlb_group_get_by_rate(struct sparx5 *sparx5, u32 rate, u32 burst)
{
const struct sparx5_ops *ops = sparx5->data->ops;
const struct sparx5_sdlb_group *group;
u64 rate_bps;
int i, count;
rate_bps = rate * 1000;
for (i = SPX5_SDLB_GROUP_CNT - 1; i >= 0; i--) {
group = &sdlb_groups[i];
for (i = sparx5->data->consts->n_lb_groups - 1; i >= 0; i--) {
group = ops->get_sdlb_group(i);
count = sparx5_sdlb_group_get_count(sparx5, i);
@ -208,7 +214,7 @@ int sparx5_sdlb_group_get_by_index(struct sparx5 *sparx5, u32 idx, u32 *group)
u32 itr, next;
int i;
for (i = 0; i < SPX5_SDLB_GROUP_CNT; i++) {
for (i = 0; i < sparx5->data->consts->n_lb_groups; i++) {
if (sparx5_sdlb_group_is_empty(sparx5, i))
continue;
@ -303,11 +309,12 @@ int sparx5_sdlb_group_del(struct sparx5 *sparx5, u32 group, u32 idx)
void sparx5_sdlb_group_init(struct sparx5 *sparx5, u64 max_rate, u32 min_burst,
u32 frame_size, u32 idx)
{
const struct sparx5_ops *ops = sparx5->data->ops;
u32 thres_shift, mask = 0x01, power = 0;
struct sparx5_sdlb_group *group;
u64 max_token;
group = &sdlb_groups[idx];
group = ops->get_sdlb_group(idx);
/* Number of positions to right-shift LB's threshold value. */
while ((min_burst & mask) == 0) {

View File

@ -32,24 +32,34 @@ static int sparx5_port_attr_pre_bridge_flags(struct sparx5_port *port,
static void sparx5_port_update_mcast_ip_flood(struct sparx5_port *port, bool flood_flag)
{
bool should_flood = flood_flag || port->is_mrouter;
struct sparx5 *sparx5 = port->sparx5;
int pgid;
for (pgid = PGID_IPV4_MC_DATA; pgid <= PGID_IPV6_MC_CTRL; pgid++)
for (pgid = sparx5_get_pgid(sparx5, PGID_IPV4_MC_DATA);
pgid <= sparx5_get_pgid(sparx5, PGID_IPV6_MC_CTRL); pgid++)
sparx5_pgid_update_mask(port, pgid, should_flood);
}
static void sparx5_port_attr_bridge_flags(struct sparx5_port *port,
struct switchdev_brport_flags flags)
{
struct sparx5 *sparx5 = port->sparx5;
if (flags.mask & BR_MCAST_FLOOD) {
sparx5_pgid_update_mask(port, PGID_MC_FLOOD, !!(flags.val & BR_MCAST_FLOOD));
sparx5_pgid_update_mask(port,
sparx5_get_pgid(sparx5, PGID_MC_FLOOD),
!!(flags.val & BR_MCAST_FLOOD));
sparx5_port_update_mcast_ip_flood(port, !!(flags.val & BR_MCAST_FLOOD));
}
if (flags.mask & BR_FLOOD)
sparx5_pgid_update_mask(port, PGID_UC_FLOOD, !!(flags.val & BR_FLOOD));
sparx5_pgid_update_mask(port,
sparx5_get_pgid(sparx5, PGID_UC_FLOOD),
!!(flags.val & BR_FLOOD));
if (flags.mask & BR_BCAST_FLOOD)
sparx5_pgid_update_mask(port, PGID_BCAST, !!(flags.val & BR_BCAST_FLOOD));
sparx5_pgid_update_mask(port,
sparx5_get_pgid(sparx5, PGID_BCAST),
!!(flags.val & BR_BCAST_FLOOD));
}
static void sparx5_attr_stp_state_set(struct sparx5_port *port,
@ -219,7 +229,8 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port,
port->vid = NULL_VID;
/* Forward frames to CPU */
sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, 0);
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU),
port->ndev->dev_addr, 0);
/* Port enters in host more therefore restore mc list */
__dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync);
@ -254,7 +265,8 @@ static int sparx5_port_add_addr(struct net_device *dev, bool up)
u16 vid = port->pvid;
if (up)
sparx5_mact_learn(sparx5, PGID_CPU, port->ndev->dev_addr, vid);
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU),
port->ndev->dev_addr, vid);
else
sparx5_mact_forget(sparx5, port->ndev->dev_addr, vid);
@ -330,7 +342,8 @@ static void sparx5_switchdev_bridge_fdb_event_work(struct work_struct *work)
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
if (host_addr)
sparx5_add_mact_entry(sparx5, dev, PGID_CPU,
sparx5_add_mact_entry(sparx5, dev,
sparx5_get_pgid(sparx5, PGID_CPU),
fdb_info->addr, vid);
else
sparx5_add_mact_entry(sparx5, port->ndev, port->portno,
@ -418,8 +431,8 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev,
switchdev_blocking_nb);
/* Flood broadcast to CPU */
sparx5_mact_learn(sparx5, PGID_BCAST, dev->broadcast,
v->vid);
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_BCAST),
dev->broadcast, v->vid);
return 0;
}
@ -547,7 +560,7 @@ static int sparx5_handle_port_mdb_add(struct net_device *dev,
/* Add any mrouter ports to the new entry */
if (is_new && ether_addr_is_ip_mcast(v->addr))
for (i = 0; i < SPX5_PORTS; i++)
for (i = 0; i < spx5->data->consts->n_ports; i++)
if (spx5->ports[i] && spx5->ports[i]->is_mrouter)
sparx5_pgid_update_mask(spx5->ports[i],
entry->pgid_idx,

View File

@ -60,8 +60,8 @@ static int sparx5_tc_setup_block(struct net_device *ndev,
cb, ndev, ndev, false);
}
static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
u32 *idx)
static void sparx5_tc_get_layer_and_idx(struct sparx5 *sparx5, u32 parent,
u32 portno, u32 *layer, u32 *idx)
{
if (parent == TC_H_ROOT) {
*layer = 2;
@ -90,8 +90,8 @@ static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
struct sparx5_port *port = netdev_priv(ndev);
u32 layer, se_idx;
sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
&se_idx);
sparx5_tc_get_layer_and_idx(port->sparx5, qopt->parent, port->portno,
&layer, &se_idx);
switch (qopt->command) {
case TC_TBF_REPLACE:

View File

@ -785,7 +785,9 @@ static int sparx5_tc_flower_psfp_setup(struct sparx5 *sparx5,
* allocate a stream gate that is always open.
*/
if (sg_idx < 0) {
sg_idx = sparx5_pool_idx_to_id(SPX5_PSFP_SG_OPEN);
/* Always-open stream gate is always the last */
sg_idx = sparx5_pool_idx_to_id(sparx5->data->consts->n_gates -
1);
sg->ipv = 0; /* Disabled */
sg->cycletime = SPX5_PSFP_SG_CYCLE_TIME_DEFAULT;
sg->num_entries = 1;

View File

@ -16,8 +16,10 @@ static int sparx5_vlant_set_mask(struct sparx5 *sparx5, u16 vid)
/* Output mask to respective registers */
spx5_wr(mask[0], sparx5, ANA_L3_VLAN_MASK_CFG(vid));
spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
if (is_sparx5(sparx5)) {
spx5_wr(mask[1], sparx5, ANA_L3_VLAN_MASK_CFG1(vid));
spx5_wr(mask[2], sparx5, ANA_L3_VLAN_MASK_CFG2(vid));
}
return 0;
}
@ -141,15 +143,19 @@ void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable)
void sparx5_pgid_clear(struct sparx5 *spx5, int pgid)
{
spx5_wr(0, spx5, ANA_AC_PGID_CFG(pgid));
spx5_wr(0, spx5, ANA_AC_PGID_CFG1(pgid));
spx5_wr(0, spx5, ANA_AC_PGID_CFG2(pgid));
if (is_sparx5(spx5)) {
spx5_wr(0, spx5, ANA_AC_PGID_CFG1(pgid));
spx5_wr(0, spx5, ANA_AC_PGID_CFG2(pgid));
}
}
void sparx5_pgid_read_mask(struct sparx5 *spx5, int pgid, u32 portmask[3])
{
portmask[0] = spx5_rd(spx5, ANA_AC_PGID_CFG(pgid));
portmask[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid));
portmask[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid));
if (is_sparx5(spx5)) {
portmask[1] = spx5_rd(spx5, ANA_AC_PGID_CFG1(pgid));
portmask[2] = spx5_rd(spx5, ANA_AC_PGID_CFG2(pgid));
}
}
void sparx5_update_fwd(struct sparx5 *sparx5)
@ -162,26 +168,33 @@ void sparx5_update_fwd(struct sparx5 *sparx5)
bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
/* Update flood masks */
for (port = PGID_UC_FLOOD; port <= PGID_BCAST; port++) {
for (port = sparx5_get_pgid(sparx5, PGID_UC_FLOOD);
port <= sparx5_get_pgid(sparx5, PGID_BCAST); port++) {
spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port));
spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
if (is_sparx5(sparx5)) {
spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port));
spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port));
}
}
/* Update SRC masks */
for (port = 0; port < SPX5_PORTS; port++) {
for (port = 0; port < sparx5->data->consts->n_ports; port++) {
if (test_bit(port, sparx5->bridge_fwd_mask)) {
/* Allow to send to all bridged but self */
bitmap_copy(workmask, sparx5->bridge_fwd_mask, SPX5_PORTS);
clear_bit(port, workmask);
bitmap_to_arr32(mask, workmask, SPX5_PORTS);
spx5_wr(mask[0], sparx5, ANA_AC_SRC_CFG(port));
spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
if (is_sparx5(sparx5)) {
spx5_wr(mask[1], sparx5, ANA_AC_SRC_CFG1(port));
spx5_wr(mask[2], sparx5, ANA_AC_SRC_CFG2(port));
}
} else {
spx5_wr(0, sparx5, ANA_AC_SRC_CFG(port));
spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
if (is_sparx5(sparx5)) {
spx5_wr(0, sparx5, ANA_AC_SRC_CFG1(port));
spx5_wr(0, sparx5, ANA_AC_SRC_CFG2(port));
}
}
}
@ -192,8 +205,10 @@ void sparx5_update_fwd(struct sparx5 *sparx5)
/* Apply learning mask */
spx5_wr(mask[0], sparx5, ANA_L2_AUTO_LRN_CFG);
spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
if (is_sparx5(sparx5)) {
spx5_wr(mask[1], sparx5, ANA_L2_AUTO_LRN_CFG1);
spx5_wr(mask[2], sparx5, ANA_L2_AUTO_LRN_CFG2);
}
}
void sparx5_vlan_port_apply(struct sparx5 *sparx5,