[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <67a7ada098b9a65aa078733c7bd373677711e5bb.camel@redhat.com>
Date: Thu, 08 Sep 2022 10:44:18 +0200
From: Paolo Abeni <pabeni@...hat.com>
To: Mattias Forsblad <mattias.forsblad@...il.com>,
netdev@...r.kernel.org
Cc: Andrew Lunn <andrew@...n.ch>,
Vivien Didelot <vivien.didelot@...il.com>,
Florian Fainelli <f.fainelli@...il.com>,
Vladimir Oltean <olteanv@...il.com>,
"David S . Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>
Subject: Re: [PATCH net-next v3 1/2] net: dsa: mv88e6xxx: Add functionality
for handling RMU frames.
On Mon, 2022-09-05 at 15:19 +0200, Mattias Forsblad wrote:
> The Marvell SOHO family has a secondary channel for sending
> control data other than the ordinary MDIO channel. The
> switch can process specially crafted ethernet frames
> as control frames. Add functionality for creating, sending,
> receiving and processing those frames. This channel is
> best suited for accessing larger data structures in the
> switch.
> Use this control channel for getting RMON counters.
>
> Signed-off-by: Mattias Forsblad <mattias.forsblad@...il.com>
> ---
> drivers/net/dsa/mv88e6xxx/Makefile | 1 +
> drivers/net/dsa/mv88e6xxx/chip.c | 73 +++++--
> drivers/net/dsa/mv88e6xxx/chip.h | 21 ++
> drivers/net/dsa/mv88e6xxx/global1.c | 76 +++++++
> drivers/net/dsa/mv88e6xxx/global1.h | 3 +
> drivers/net/dsa/mv88e6xxx/rmu.c | 310 ++++++++++++++++++++++++++++
> drivers/net/dsa/mv88e6xxx/rmu.h | 28 +++
> include/net/dsa.h | 20 +-
> net/dsa/dsa.c | 28 +++
> net/dsa/dsa2.c | 2 +
> net/dsa/tag_dsa.c | 32 ++-
> 11 files changed, 575 insertions(+), 19 deletions(-)
> create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.c
> create mode 100644 drivers/net/dsa/mv88e6xxx/rmu.h
>
> diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
> index c8eca2b6f959..105d7bd832c9 100644
> --- a/drivers/net/dsa/mv88e6xxx/Makefile
> +++ b/drivers/net/dsa/mv88e6xxx/Makefile
> @@ -15,3 +15,4 @@ mv88e6xxx-objs += port_hidden.o
> mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
> mv88e6xxx-objs += serdes.o
> mv88e6xxx-objs += smi.o
> +mv88e6xxx-objs += rmu.o
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
> index 6f4ea39ab466..81fd9b7a9afa 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.c
> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
> @@ -42,6 +42,7 @@
> #include "ptp.h"
> #include "serdes.h"
> #include "smi.h"
> +#include "rmu.h"
>
> static void assert_reg_lock(struct mv88e6xxx_chip *chip)
> {
> @@ -1233,16 +1234,30 @@ static int mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
> u16 bank1_select, u16 histogram)
> {
> struct mv88e6xxx_hw_stat *stat;
> + int offset = 0;
> + u64 high;
> int i, j;
>
> for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
> stat = &mv88e6xxx_hw_stats[i];
> if (stat->type & types) {
> - mv88e6xxx_reg_lock(chip);
> - data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
> - bank1_select,
> - histogram);
> - mv88e6xxx_reg_unlock(chip);
> + if (mv88e6xxx_rmu_available(chip) &&
> + !(stat->type & STATS_TYPE_PORT)) {
> + if (stat->type & STATS_TYPE_BANK1)
> + offset = 32;
> +
> + data[j] = chip->ports[port].rmu_raw_stats[stat->reg + offset];
> + if (stat->size == 8) {
> + high = chip->ports[port].rmu_raw_stats[stat->reg + offset
> + + 1];
> + data[j] += (high << 32);
> + }
> + } else {
> + mv88e6xxx_reg_lock(chip);
> + data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
> + bank1_select, histogram);
> + mv88e6xxx_reg_unlock(chip);
> + }
>
> j++;
> }
> @@ -1311,8 +1326,8 @@ static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
> mv88e6xxx_reg_unlock(chip);
> }
>
> -static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
> - uint64_t *data)
> +static void mv88e6xxx_get_ethtool_stats_mdio(struct dsa_switch *ds, int port,
> + uint64_t *data)
> {
> struct mv88e6xxx_chip *chip = ds->priv;
> int ret;
> @@ -1326,7 +1341,18 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
> return;
>
> mv88e6xxx_get_stats(chip, port, data);
> +}
> +
> +static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
> + uint64_t *data)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
>
> + /* If RMU isn't available fall back to MDIO access. */
> + if (mv88e6xxx_rmu_available(chip))
> + chip->rmu.ops->get_rmon(chip, port, data);
> + else
> + mv88e6xxx_get_ethtool_stats_mdio(ds, port, data);
> }
>
> static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
> @@ -1535,14 +1561,6 @@ static int mv88e6xxx_trunk_setup(struct mv88e6xxx_chip *chip)
> return 0;
> }
>
> -static int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
> -{
> - if (chip->info->ops->rmu_disable)
> - return chip->info->ops->rmu_disable(chip);
> -
> - return 0;
> -}
> -
> static int mv88e6xxx_pot_setup(struct mv88e6xxx_chip *chip)
> {
> if (chip->info->ops->pot_clear)
> @@ -4098,6 +4116,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = {
> .ppu_disable = mv88e6185_g1_ppu_disable,
> .reset = mv88e6185_g1_reset,
> .rmu_disable = mv88e6085_g1_rmu_disable,
> + .rmu_enable = mv88e6085_g1_rmu_enable,
> .vtu_getnext = mv88e6352_g1_vtu_getnext,
> .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
> .stu_getnext = mv88e6352_g1_stu_getnext,
> @@ -4181,6 +4200,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = {
> .pot_clear = mv88e6xxx_g2_pot_clear,
> .reset = mv88e6352_g1_reset,
> .rmu_disable = mv88e6085_g1_rmu_disable,
> + .rmu_enable = mv88e6085_g1_rmu_enable,
> .vtu_getnext = mv88e6352_g1_vtu_getnext,
> .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
> .phylink_get_caps = mv88e6095_phylink_get_caps,
> @@ -5300,6 +5320,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
> .pot_clear = mv88e6xxx_g2_pot_clear,
> .reset = mv88e6352_g1_reset,
> .rmu_disable = mv88e6352_g1_rmu_disable,
> + .rmu_enable = mv88e6352_g1_rmu_enable,
> .atu_get_hash = mv88e6165_g1_atu_get_hash,
> .atu_set_hash = mv88e6165_g1_atu_set_hash,
> .vtu_getnext = mv88e6352_g1_vtu_getnext,
> @@ -5367,6 +5388,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
> .pot_clear = mv88e6xxx_g2_pot_clear,
> .reset = mv88e6352_g1_reset,
> .rmu_disable = mv88e6390_g1_rmu_disable,
> + .rmu_enable = mv88e6390_g1_rmu_enable,
> .atu_get_hash = mv88e6165_g1_atu_get_hash,
> .atu_set_hash = mv88e6165_g1_atu_set_hash,
> .vtu_getnext = mv88e6390_g1_vtu_getnext,
> @@ -5434,6 +5456,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
> .pot_clear = mv88e6xxx_g2_pot_clear,
> .reset = mv88e6352_g1_reset,
> .rmu_disable = mv88e6390_g1_rmu_disable,
> + .rmu_enable = mv88e6390_g1_rmu_enable,
> .atu_get_hash = mv88e6165_g1_atu_get_hash,
> .atu_set_hash = mv88e6165_g1_atu_set_hash,
> .vtu_getnext = mv88e6390_g1_vtu_getnext,
> @@ -5504,6 +5527,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
> .pot_clear = mv88e6xxx_g2_pot_clear,
> .reset = mv88e6352_g1_reset,
> .rmu_disable = mv88e6390_g1_rmu_disable,
> + .rmu_enable = mv88e6390_g1_rmu_enable,
> .atu_get_hash = mv88e6165_g1_atu_get_hash,
> .atu_set_hash = mv88e6165_g1_atu_set_hash,
> .vtu_getnext = mv88e6390_g1_vtu_getnext,
> @@ -6861,6 +6885,23 @@ static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
> return err_sync ? : err_pvt;
> }
>
> +int mv88e6xxx_connect_tag_protocol(struct dsa_switch *ds,
> + enum dsa_tag_protocol proto)
> +{
> + struct dsa_tagger_data *tagger_data = ds->tagger_data;
> +
> + switch (proto) {
> + case DSA_TAG_PROTO_DSA:
> + case DSA_TAG_PROTO_EDSA:
> + tagger_data->decode_frame2reg = mv88e6xxx_decode_frame2reg_handler;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
> .get_tag_protocol = mv88e6xxx_get_tag_protocol,
> .change_tag_protocol = mv88e6xxx_change_tag_protocol,
> @@ -6926,6 +6967,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
> .crosschip_lag_change = mv88e6xxx_crosschip_lag_change,
> .crosschip_lag_join = mv88e6xxx_crosschip_lag_join,
> .crosschip_lag_leave = mv88e6xxx_crosschip_lag_leave,
> + .master_state_change = mv88e6xxx_master_change,
> + .connect_tag_protocol = mv88e6xxx_connect_tag_protocol,
> };
>
> static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
> diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
> index e693154cf803..c7477b716473 100644
> --- a/drivers/net/dsa/mv88e6xxx/chip.h
> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
> @@ -266,6 +266,7 @@ struct mv88e6xxx_vlan {
> struct mv88e6xxx_port {
> struct mv88e6xxx_chip *chip;
> int port;
> + u64 rmu_raw_stats[64];
> struct mv88e6xxx_vlan bridge_pvid;
> u64 serdes_stats[2];
> u64 atu_member_violation;
> @@ -282,6 +283,17 @@ struct mv88e6xxx_port {
> struct devlink_region *region;
> };
>
> +struct mv88e6xxx_rmu {
> + /* RMU resources */
> + struct net_device *master_netdev;
> + const struct mv88e6xxx_bus_ops *ops;
> + /* Mutex for RMU operations */
> + struct mutex mutex;
> + u16 got_id;
> + u8 request_cmd;
> + int inband_seqno;
> +};
> +
> enum mv88e6xxx_region_id {
> MV88E6XXX_REGION_GLOBAL1 = 0,
> MV88E6XXX_REGION_GLOBAL2,
> @@ -410,12 +422,16 @@ struct mv88e6xxx_chip {
>
> /* Bridge MST to SID mappings */
> struct list_head msts;
> +
> + /* RMU resources */
> + struct mv88e6xxx_rmu rmu;
> };
>
> struct mv88e6xxx_bus_ops {
> int (*read)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val);
> int (*write)(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val);
> int (*init)(struct mv88e6xxx_chip *chip);
> + int (*get_rmon)(struct mv88e6xxx_chip *chip, int port, uint64_t *data);
> };
>
> struct mv88e6xxx_mdio_bus {
> @@ -637,6 +653,7 @@ struct mv88e6xxx_ops {
>
> /* Remote Management Unit operations */
> int (*rmu_disable)(struct mv88e6xxx_chip *chip);
> + int (*rmu_enable)(struct mv88e6xxx_chip *chip, int port);
>
> /* Precision Time Protocol operations */
> const struct mv88e6xxx_ptp_ops *ptp_ops;
> @@ -804,4 +821,8 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip)
>
> int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap);
>
> +static inline bool mv88e6xxx_rmu_available(struct mv88e6xxx_chip *chip)
> +{
> + return chip->rmu.master_netdev ? 1 : 0;
> +}
> #endif /* _MV88E6XXX_CHIP_H */
> diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c
> index 5848112036b0..f6c288ece0ba 100644
> --- a/drivers/net/dsa/mv88e6xxx/global1.c
> +++ b/drivers/net/dsa/mv88e6xxx/global1.c
> @@ -466,18 +466,94 @@ int mv88e6085_g1_rmu_disable(struct mv88e6xxx_chip *chip)
> MV88E6085_G1_CTL2_RM_ENABLE, 0);
> }
>
> +int mv88e6085_g1_rmu_enable(struct mv88e6xxx_chip *chip, int upstream_port)
> +{
> + int val = MV88E6352_G1_CTL2_RMU_MODE_DISABLED;
> +
> + dev_dbg(chip->dev, "RMU: Enabling on port %d", upstream_port);
> +
> + switch (upstream_port) {
> + case 9:
> + val = MV88E6085_G1_CTL2_RM_ENABLE;
> + break;
> + case 10:
> + val = MV88E6085_G1_CTL2_RM_ENABLE | MV88E6085_G1_CTL2_P10RM;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return mv88e6xxx_g1_ctl2_mask(chip, MV88E6085_G1_CTL2_P10RM |
> + MV88E6085_G1_CTL2_RM_ENABLE, val);
> +}
> +
> int mv88e6352_g1_rmu_disable(struct mv88e6xxx_chip *chip)
> {
> return mv88e6xxx_g1_ctl2_mask(chip, MV88E6352_G1_CTL2_RMU_MODE_MASK,
> MV88E6352_G1_CTL2_RMU_MODE_DISABLED);
> }
>
> +int mv88e6352_g1_rmu_enable(struct mv88e6xxx_chip *chip, int port)
> +{
> + int val = MV88E6352_G1_CTL2_RMU_MODE_DISABLED;
> + int upstream_port;
> +
> + upstream_port = dsa_switch_upstream_port(chip->ds);
> + dev_dbg(chip->dev, "RMU: Enabling on port %d", upstream_port);
> + if (upstream_port < 0)
> + return -EOPNOTSUPP;
> +
> + switch (upstream_port) {
> + case 4:
> + val = MV88E6352_G1_CTL2_RMU_MODE_PORT_4;
> + break;
> + case 5:
> + val = MV88E6352_G1_CTL2_RMU_MODE_PORT_5;
> + break;
> + case 6:
> + val = MV88E6352_G1_CTL2_RMU_MODE_PORT_6;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return mv88e6xxx_g1_ctl2_mask(chip, MV88E6352_G1_CTL2_RMU_MODE_MASK,
> + val);
> +}
> +
> int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip)
> {
> return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_RMU_MODE_MASK,
> MV88E6390_G1_CTL2_RMU_MODE_DISABLED);
> }
>
> +int mv88e6390_g1_rmu_enable(struct mv88e6xxx_chip *chip, int upstream_port)
> +{
> + int val = MV88E6390_G1_CTL2_RMU_MODE_DISABLED;
> +
> + dev_dbg(chip->dev, "RMU: Enabling on port %d", upstream_port);
> +
> + switch (upstream_port) {
> + case 0:
> + val = MV88E6390_G1_CTL2_RMU_MODE_PORT_0;
> + break;
> + case 1:
> + val = MV88E6390_G1_CTL2_RMU_MODE_PORT_1;
> + break;
> + case 9:
> + val = MV88E6390_G1_CTL2_RMU_MODE_PORT_9;
> + break;
> + case 10:
> + val = MV88E6390_G1_CTL2_RMU_MODE_PORT_10;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_RMU_MODE_MASK,
> + val);
Minor nit: you should align 'val' with the open bracket or even better
you can use a single line for the whole statement.
> +}
> +
> int mv88e6390_g1_stats_set_histogram(struct mv88e6xxx_chip *chip)
> {
> return mv88e6xxx_g1_ctl2_mask(chip, MV88E6390_G1_CTL2_HIST_MODE_MASK,
> diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h
> index 65958b2a0d3a..29c0c8acb583 100644
> --- a/drivers/net/dsa/mv88e6xxx/global1.h
> +++ b/drivers/net/dsa/mv88e6xxx/global1.h
> @@ -313,8 +313,11 @@ int mv88e6250_g1_ieee_pri_map(struct mv88e6xxx_chip *chip);
> int mv88e6185_g1_set_cascade_port(struct mv88e6xxx_chip *chip, int port);
>
> int mv88e6085_g1_rmu_disable(struct mv88e6xxx_chip *chip);
> +int mv88e6085_g1_rmu_enable(struct mv88e6xxx_chip *chip, int upstream_port);
> int mv88e6352_g1_rmu_disable(struct mv88e6xxx_chip *chip);
> +int mv88e6352_g1_rmu_enable(struct mv88e6xxx_chip *chip, int upstream_port);
> int mv88e6390_g1_rmu_disable(struct mv88e6xxx_chip *chip);
> +int mv88e6390_g1_rmu_enable(struct mv88e6xxx_chip *chip, int upstream_port);
>
> int mv88e6xxx_g1_set_device_number(struct mv88e6xxx_chip *chip, int index);
>
> diff --git a/drivers/net/dsa/mv88e6xxx/rmu.c b/drivers/net/dsa/mv88e6xxx/rmu.c
> new file mode 100644
> index 000000000000..7bc5b1ea3e9b
> --- /dev/null
> +++ b/drivers/net/dsa/mv88e6xxx/rmu.c
> @@ -0,0 +1,310 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Marvell 88E6xxx Switch Remote Management Unit Support
> + *
> + * Copyright (c) 2022 Mattias Forsblad <mattias.forsblad@...il.com>
> + *
> + */
> +
> +#include <asm/unaligned.h>
> +#include "rmu.h"
> +#include "global1.h"
> +
> +#define MV88E6XXX_DSA_HLEN 4
> +
> +static const u8 rmu_dest_addr[ETH_ALEN] = { 0x01, 0x50, 0x43, 0x00, 0x00, 0x00 };
> +
> +#define MV88E6XXX_RMU_L2_BYTE1_RESV_VAL 0x3e
> +#define MV88E6XXX_RMU 1
> +#define MV88E6XXX_RMU_PRIO 6
> +#define MV88E6XXX_RMU_RESV2 0xf
> +
> +#define MV88E6XXX_SOURCE_PORT GENMASK(6, 3)
> +#define MV88E6XXX_SOURCE_DEV GENMASK(5, 0)
> +#define MV88E6XXX_CPU_CODE_MASK GENMASK(7, 6)
> +#define MV88E6XXX_TRG_DEV_MASK GENMASK(4, 0)
> +#define MV88E6XXX_RMU_CODE_MASK GENMASK(1, 1)
> +#define MV88E6XXX_RMU_PRIO_MASK GENMASK(7, 5)
> +#define MV88E6XXX_RMU_L2_BYTE1_RESV GENMASK(7, 2)
> +#define MV88E6XXX_RMU_L2_BYTE2_RESV GENMASK(3, 0)
> +
> +#define MV88E6XXX_RMU_RESP_FORMAT_1 0x0001
> +#define MV88E6XXX_RMU_RESP_FORMAT_2 0x0002
> +#define MV88E6XXX_RMU_RESP_ERROR 0xffff
> +
> +#define MV88E6XXX_RMU_RESP_CODE_GOT_ID 0x0000
> +#define MV88E6XXX_RMU_RESP_CODE_DUMP_MIB 0x1020
> +
> +static void mv88e6xxx_rmu_create_l2(struct sk_buff *skb, struct dsa_port *dp)
> +{
> + struct mv88e6xxx_chip *chip = dp->ds->priv;
> + struct ethhdr *eth;
> + u8 *edsa_header;
> + u8 *dsa_header;
> + u8 extra = 0;
> +
> + if (chip->tag_protocol == DSA_TAG_PROTO_EDSA)
> + extra = 4;
> +
> + /* Create RMU L2 header */
> + dsa_header = skb_push(skb, 6);
> + dsa_header[0] = FIELD_PREP(MV88E6XXX_CPU_CODE_MASK, MV88E6XXX_RMU);
> + dsa_header[0] |= FIELD_PREP(MV88E6XXX_TRG_DEV_MASK, dp->ds->index);
> + dsa_header[1] = FIELD_PREP(MV88E6XXX_RMU_CODE_MASK, 1);
> + dsa_header[1] |= FIELD_PREP(MV88E6XXX_RMU_L2_BYTE1_RESV, MV88E6XXX_RMU_L2_BYTE1_RESV_VAL);
> + dsa_header[2] = FIELD_PREP(MV88E6XXX_RMU_PRIO_MASK, MV88E6XXX_RMU_PRIO);
> + dsa_header[2] |= MV88E6XXX_RMU_L2_BYTE2_RESV;
> + dsa_header[3] = ++chip->rmu.inband_seqno;
> + dsa_header[4] = 0;
> + dsa_header[5] = 0;
> +
> + /* Insert RMU MAC destination address /w extra if needed */
> + skb_push(skb, ETH_ALEN * 2 + extra);
> + eth = (struct ethhdr *)skb->data;
> + memcpy(eth->h_dest, rmu_dest_addr, ETH_ALEN);
> + memcpy(eth->h_source, chip->rmu.master_netdev->dev_addr, ETH_ALEN);
> +
> + if (extra) {
> + edsa_header = (u8 *)ð->h_proto;
> + edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
> + edsa_header[1] = ETH_P_EDSA & 0xff;
> + edsa_header[2] = 0x00;
> + edsa_header[3] = 0x00;
> + }
> +}
> +
> +static int mv88e6xxx_rmu_send_wait(struct mv88e6xxx_chip *chip, int port,
> + int request, const char *msg, int len)
> +{
> + struct dsa_port *dp;
> + struct sk_buff *skb;
> + unsigned char *data;
> + int ret = 0;
> +
> + dp = dsa_to_port(chip->ds, port);
> + if (!dp)
> + return 0;
> +
> + skb = netdev_alloc_skb(chip->rmu.master_netdev, 64);
> + if (!skb)
> + return -ENOMEM;
> +
> + /* Take height for an eventual EDSA header */
> + skb_reserve(skb, 2 * ETH_HLEN + 4);
> + skb_reset_network_header(skb);
> +
> + /* Insert RMU L3 message */
> + data = skb_put(skb, len);
> + memcpy(data, msg, len);
> +
> + mv88e6xxx_rmu_create_l2(skb, dp);
> +
> + mutex_lock(&chip->rmu.mutex);
> +
> + chip->rmu.request_cmd = request;
> +
> + ret = dsa_switch_inband_tx(dp->ds, skb, NULL, MV88E6XXX_RMU_WAIT_TIME_MS);
> + if (ret < 0) {
Minor nit: bracket not needed here.
> + dev_err(chip->dev,
> + "RMU: timeout waiting for request %d (%pe) on port %d\n",
> + request, ERR_PTR(ret), port);
> + }
> +
> + mutex_unlock(&chip->rmu.mutex);
> +
> + return ret > 0 ? 0 : ret;
> +}
> +
> +static int mv88e6xxx_rmu_get_id(struct mv88e6xxx_chip *chip, int port)
> +{
> + const u8 get_id[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
> + int ret = -1;
> +
> + if (chip->rmu.got_id)
> + return 0;
> +
> + ret = mv88e6xxx_rmu_send_wait(chip, port, MV88E6XXX_RMU_REQ_GET_ID, get_id, 8);
> + if (ret) {
> + dev_dbg(chip->dev, "RMU: error for command GET_ID %pe\n", ERR_PTR(ret));
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mv88e6xxx_rmu_stats_get(struct mv88e6xxx_chip *chip, int port, uint64_t *data)
> +{
> + u8 dump_mib[8] = { 0x00, 0x01, 0x00, 0x00, 0x10, 0x20, 0x00, 0x00 };
> + int ret;
> +
> + ret = mv88e6xxx_rmu_get_id(chip, port);
> + if (ret)
> + return ret;
> +
> + /* Send a GET_MIB command */
> + dump_mib[7] = port;
> + ret = mv88e6xxx_rmu_send_wait(chip, port, MV88E6XXX_RMU_REQ_DUMP_MIB, dump_mib, 8);
> + if (ret) {
> + dev_dbg(chip->dev, "RMU: error for command DUMP_MIB %pe port %d\n",
> + ERR_PTR(ret), port);
> + return ret;
> + }
> +
> + /* Update MIB for port */
> + if (chip->info->ops->stats_get_stats)
> + return chip->info->ops->stats_get_stats(chip, port, data);
> +
> + return 0;
> +}
> +
> +void mv88e6xxx_master_change(struct dsa_switch *ds, const struct net_device *master,
> + bool operational)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> + struct dsa_port *cpu_dp;
> + int port;
> +
> + cpu_dp = master->dsa_ptr;
> + port = dsa_towards_port(ds, cpu_dp->ds->index, cpu_dp->index);
> +
> + mv88e6xxx_reg_lock(chip);
> +
> + if (operational) {
> + if (chip->info->ops->rmu_enable) {
> + if (!chip->info->ops->rmu_enable(chip, port))
> + chip->rmu.master_netdev = (struct net_device *)master;
> + else
> + dev_err(chip->dev, "RMU: Unable to enable on port %d", port);
> + }
> +
> + } else {
> + chip->rmu.master_netdev = NULL;
> + if (chip->info->ops->rmu_disable)
> + chip->info->ops->rmu_disable(chip);
> + }
> +
> + mv88e6xxx_reg_unlock(chip);
> +}
> +
> +static void mv88e6xxx_prod_id_handler(struct dsa_switch *ds, struct sk_buff *skb)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> + u16 prodnum;
> +
> + prodnum = get_unaligned_be16(&skb->data[2]);
> + chip->rmu.got_id = prodnum;
> + dev_dbg_ratelimited(chip->dev, "RMU: received id OK with product number: 0x%04x\n",
> + chip->rmu.got_id);
> +}
> +
> +static void mv88e6xxx_mib_handler(struct dsa_switch *ds, struct sk_buff *skb)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> + struct mv88e6xxx_port *p;
> + u8 port;
> + int i;
> +
> + port = FIELD_GET(MV88E6XXX_SOURCE_PORT, skb->data[7]);
> + p = &chip->ports[port];
> + if (!p) {
> + dev_err_ratelimited(chip->dev, "RMU: illegal port number in response: %d\n", port);
> + return;
> + }
> +
> + /* Copy whole array for further
> + * processing according to chip type
> + */
> + for (i = 0; i < MV88E6XXX_RMU_MAX_RMON; i++)
> + p->rmu_raw_stats[i] = get_unaligned_be32(&skb->data[12 + i * 4]);
> +}
> +
> +static int mv88e6xxx_validate_mac(struct dsa_switch *ds, struct sk_buff *skb)
> +{
> + struct mv88e6xxx_chip *chip = ds->priv;
> + unsigned char *ethhdr;
> +
> + /* Check matching MAC */
> + ethhdr = skb_mac_header(skb);
> + if (!ether_addr_equal(chip->rmu.master_netdev->dev_addr, ethhdr)) {
> + dev_dbg_ratelimited(ds->dev, "RMU: mismatching MAC address for request. Rx %pM expecting %pM\n",
> + ethhdr, chip->rmu.master_netdev->dev_addr);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +void mv88e6xxx_decode_frame2reg_handler(struct net_device *dev, struct sk_buff *skb)
> +{
> + struct dsa_tagger_data *tagger_data;
> + struct dsa_port *dp = dev->dsa_ptr;
> + struct dsa_switch *ds = dp->ds;
> + struct mv88e6xxx_chip *chip;
> + int source_device;
> + u8 *dsa_header;
> + u16 format;
> + u16 code;
> + u8 seqno;
> +
> + tagger_data = ds->tagger_data;
> +
> + if (mv88e6xxx_validate_mac(ds, skb))
> + return;
> +
> + /* Decode Frame2Reg DSA portion */
> + dsa_header = skb->data - 2;
> +
> + source_device = FIELD_GET(MV88E6XXX_SOURCE_DEV, dsa_header[0]);
> + ds = dsa_switch_find(ds->dst->index, source_device);
> + if (!ds) {
> + net_dbg_ratelimited("RMU: Didn't find switch with index %d", source_device);
> + return;
> + }
> +
> + chip = ds->priv;
> + seqno = dsa_header[3];
> + if (seqno != chip->rmu.inband_seqno) {
> + net_dbg_ratelimited("RMU: wrong seqno received. Was %d, expected %d",
> + seqno, chip->rmu.inband_seqno);
> + return;
> + }
> +
> + /* Pull DSA L2 data */
> + skb_pull(skb, MV88E6XXX_DSA_HLEN);
> +
> + format = get_unaligned_be16(&skb->data[0]);
> + if (format != MV88E6XXX_RMU_RESP_FORMAT_1 &&
> + format != MV88E6XXX_RMU_RESP_FORMAT_2) {
> + net_dbg_ratelimited("RMU: received unknown format 0x%04x", format);
> + return;
> + }
> +
> + code = get_unaligned_be16(&skb->data[4]);
> + if (code == MV88E6XXX_RMU_RESP_ERROR) {
> + net_dbg_ratelimited("RMU: error response code 0x%04x", code);
> + return;
> + }
> +
> + if (code == MV88E6XXX_RMU_RESP_CODE_GOT_ID)
> + mv88e6xxx_prod_id_handler(ds, skb);
> + else if (code == MV88E6XXX_RMU_RESP_CODE_DUMP_MIB)
> + mv88e6xxx_mib_handler(ds, skb);
> +
> + dsa_switch_inband_complete(ds, NULL);
> +}
> +
> +static const struct mv88e6xxx_bus_ops mv88e6xxx_bus_ops = {
> + .get_rmon = mv88e6xxx_rmu_stats_get,
> +};
> +
> +int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip)
> +{
> + mutex_init(&chip->rmu.mutex);
> +
> + chip->rmu.ops = &mv88e6xxx_bus_ops;
> +
> + if (chip->info->ops->rmu_disable)
> + return chip->info->ops->rmu_disable(chip);
> +
> + return 0;
> +}
> diff --git a/drivers/net/dsa/mv88e6xxx/rmu.h b/drivers/net/dsa/mv88e6xxx/rmu.h
> new file mode 100644
> index 000000000000..cf84b7005331
> --- /dev/null
> +++ b/drivers/net/dsa/mv88e6xxx/rmu.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Marvell 88E6xxx Switch Remote Management Unit Support
> + *
> + * Copyright (c) 2022 Mattias Forsblad <mattias.forsblad@...il.com>
> + *
> + */
> +
> +#ifndef _MV88E6XXX_RMU_H_
> +#define _MV88E6XXX_RMU_H_
> +
> +#include "chip.h"
> +
> +#define MV88E6XXX_RMU_MAX_RMON 64
> +
> +#define MV88E6XXX_RMU_REQ_GET_ID 1
> +#define MV88E6XXX_RMU_REQ_DUMP_MIB 2
> +
> +#define MV88E6XXX_RMU_WAIT_TIME_MS 20
> +
> +int mv88e6xxx_rmu_setup(struct mv88e6xxx_chip *chip);
> +
> +void mv88e6xxx_master_change(struct dsa_switch *ds, const struct net_device *master,
> + bool operational);
> +
> +void mv88e6xxx_decode_frame2reg_handler(struct net_device *dev, struct sk_buff *skb);
> +
> +#endif /* _MV88E6XXX_RMU_H_ */
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index f2ce12860546..5c8dccc8bc3e 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -130,6 +130,11 @@ struct dsa_lag {
> refcount_t refcount;
> };
>
> +struct dsa_tagger_data {
> + void (*decode_frame2reg)(struct net_device *netdev,
> + struct sk_buff *skb);
> +};
> +
> struct dsa_switch_tree {
> struct list_head list;
>
> @@ -495,6 +500,8 @@ struct dsa_switch {
> unsigned int max_num_bridges;
>
> unsigned int num_ports;
> +
> + struct completion inband_done;
> };
>
> static inline struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p)
> @@ -822,7 +829,7 @@ struct dsa_switch_ops {
> * in current use. The switch driver can provide handlers for certain
> * types of packets for switch management.
> */
> - int (*connect_tag_protocol)(struct dsa_switch *ds,
> + int (*connect_tag_protocol)(struct dsa_switch *ds,
> enum dsa_tag_protocol proto);
>
> /* Optional switch-wide initialization and destruction methods */
> @@ -1390,6 +1397,17 @@ void dsa_tag_drivers_register(struct dsa_tag_driver *dsa_tag_driver_array[],
> void dsa_tag_drivers_unregister(struct dsa_tag_driver *dsa_tag_driver_array[],
> unsigned int count);
>
> +int dsa_switch_inband_tx(struct dsa_switch *ds, struct sk_buff *skb,
> + struct completion *completion, unsigned long timeout);
> +static inline void dsa_switch_inband_complete(struct dsa_switch *ds, struct completion *completion)
> +{
> + /* Custom completion? */
> + if (completion)
> + complete(completion);
> + else
> + complete(&ds->inband_done);
> +}
> +
> #define dsa_tag_driver_module_drivers(__dsa_tag_drivers_array, __count) \
> static int __init dsa_tag_driver_module_init(void) \
> { \
> diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
> index be7b320cda76..2d7add779b6f 100644
> --- a/net/dsa/dsa.c
> +++ b/net/dsa/dsa.c
> @@ -324,6 +324,34 @@ int dsa_switch_resume(struct dsa_switch *ds)
> EXPORT_SYMBOL_GPL(dsa_switch_resume);
> #endif
>
> +int dsa_switch_inband_tx(struct dsa_switch *ds, struct sk_buff *skb,
> + struct completion *completion, unsigned long timeout)
> +{
> + int ret;
> + struct completion *com;
Minor nit: please apply the reverse x-mas tree order.
Cheers,
Paolo
Powered by blists - more mailing lists