diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 308 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 18 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/devlink.c | 633 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/devlink.h | 21 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/hwtstamp.c | 59 |
6 files changed, 723 insertions, 317 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index aa645ff86f64..4b080b448ce7 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o mv88e6xxx-objs := chip.o +mv88e6xxx-objs += devlink.o mv88e6xxx-objs += global1.o mv88e6xxx-objs += global1_atu.o mv88e6xxx-objs += global1_vtu.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index f0dbc05e30a4..bd297ae7cf9e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -32,6 +32,7 @@ #include <net/dsa.h> #include "chip.h" +#include "devlink.h" #include "global1.h" #include "global2.h" #include "hwtstamp.h" @@ -1465,21 +1466,21 @@ static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip, return chip->info->ops->vtu_loadpurge(chip, entry); } -static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *fid_bitmap) { - DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); struct mv88e6xxx_vtu_entry vlan; int i, err; + u16 fid; bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); /* Set every FID bit used by the (un)bridged ports */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { - err = mv88e6xxx_port_get_fid(chip, i, fid); + err = mv88e6xxx_port_get_fid(chip, i, &fid); if (err) return err; - set_bit(*fid, fid_bitmap); + set_bit(fid, fid_bitmap); } /* Set every FID bit used by the VLAN entries */ @@ -1497,6 +1498,18 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) set_bit(vlan.fid, fid_bitmap); } while (vlan.vid < chip->info->max_vid); + return 0; +} + +static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) +{ + DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); + int err; + + err = mv88e6xxx_fid_map(chip, fid_bitmap); + if (err) + return err; + /* The reset value 0x000 is used to indicate that multiple address * databases are not needed. Return the next positive available. */ @@ -1508,22 +1521,6 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid) return mv88e6xxx_g1_atu_flush(chip, *fid, true); } -static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash) -{ - if (chip->info->ops->atu_get_hash) - return chip->info->ops->atu_get_hash(chip, hash); - - return -EOPNOTSUPP; -} - -static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash) -{ - if (chip->info->ops->atu_set_hash) - return chip->info->ops->atu_set_hash(chip, hash); - - return -EOPNOTSUPP; -} - static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, u16 vid_begin, u16 vid_end) { @@ -1581,15 +1578,16 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering) + bool vlan_filtering, + struct switchdev_trans *trans) { struct mv88e6xxx_chip *chip = ds->priv; u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE : MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED; int err; - if (!chip->info->max_vid) - return -EOPNOTSUPP; + if (switchdev_trans_ph_prepare(trans)) + return chip->info->max_vid ? 0 : -EOPNOTSUPP; mv88e6xxx_reg_lock(chip); err = mv88e6xxx_port_set_8021q_mode(chip, port, mode); @@ -2837,248 +2835,11 @@ static int mv88e6390_setup_errata(struct mv88e6xxx_chip *chip) return mv88e6xxx_software_reset(chip); } -enum mv88e6xxx_devlink_param_id { - MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, - MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, -}; - -static int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, - struct devlink_param_gset_ctx *ctx) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mv88e6xxx_reg_lock(chip); - - switch (id) { - case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: - err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8); - break; - default: - err = -EOPNOTSUPP; - break; - } - - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, - struct devlink_param_gset_ctx *ctx) -{ - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - mv88e6xxx_reg_lock(chip); - - switch (id) { - case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: - err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8); - break; - default: - err = -EOPNOTSUPP; - break; - } - - mv88e6xxx_reg_unlock(chip); - - return err; -} - -static const struct devlink_param mv88e6xxx_devlink_params[] = { - DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, - "ATU_hash", DEVLINK_PARAM_TYPE_U8, - BIT(DEVLINK_PARAM_CMODE_RUNTIME)), -}; - -static int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds) -{ - return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params, - ARRAY_SIZE(mv88e6xxx_devlink_params)); -} - -static void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds) -{ - dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params, - ARRAY_SIZE(mv88e6xxx_devlink_params)); -} - -enum mv88e6xxx_devlink_resource_id { - MV88E6XXX_RESOURCE_ID_ATU, - MV88E6XXX_RESOURCE_ID_ATU_BIN_0, - MV88E6XXX_RESOURCE_ID_ATU_BIN_1, - MV88E6XXX_RESOURCE_ID_ATU_BIN_2, - MV88E6XXX_RESOURCE_ID_ATU_BIN_3, -}; - -static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip, - u16 bin) -{ - u16 occupancy = 0; - int err; - - mv88e6xxx_reg_lock(chip); - - err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL, - bin); - if (err) { - dev_err(chip->dev, "failed to set ATU stats kind/bin\n"); - goto unlock; - } - - err = mv88e6xxx_g1_atu_get_next(chip, 0); - if (err) { - dev_err(chip->dev, "failed to perform ATU get next\n"); - goto unlock; - } - - err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy); - if (err) { - dev_err(chip->dev, "failed to get ATU stats\n"); - goto unlock; - } - - occupancy &= MV88E6XXX_G2_ATU_STATS_MASK; - -unlock: - mv88e6xxx_reg_unlock(chip); - - return occupancy; -} - -static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv) -{ - struct mv88e6xxx_chip *chip = priv; - - return mv88e6xxx_devlink_atu_bin_get(chip, - MV88E6XXX_G2_ATU_STATS_BIN_0); -} - -static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv) -{ - struct mv88e6xxx_chip *chip = priv; - - return mv88e6xxx_devlink_atu_bin_get(chip, - MV88E6XXX_G2_ATU_STATS_BIN_1); -} - -static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv) -{ - struct mv88e6xxx_chip *chip = priv; - - return mv88e6xxx_devlink_atu_bin_get(chip, - MV88E6XXX_G2_ATU_STATS_BIN_2); -} - -static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv) -{ - struct mv88e6xxx_chip *chip = priv; - - return mv88e6xxx_devlink_atu_bin_get(chip, - MV88E6XXX_G2_ATU_STATS_BIN_3); -} - -static u64 mv88e6xxx_devlink_atu_get(void *priv) -{ - return mv88e6xxx_devlink_atu_bin_0_get(priv) + - mv88e6xxx_devlink_atu_bin_1_get(priv) + - mv88e6xxx_devlink_atu_bin_2_get(priv) + - mv88e6xxx_devlink_atu_bin_3_get(priv); -} - -static int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) -{ - struct devlink_resource_size_params size_params; - struct mv88e6xxx_chip *chip = ds->priv; - int err; - - devlink_resource_size_params_init(&size_params, - mv88e6xxx_num_macs(chip), - mv88e6xxx_num_macs(chip), - 1, DEVLINK_RESOURCE_UNIT_ENTRY); - - err = dsa_devlink_resource_register(ds, "ATU", - mv88e6xxx_num_macs(chip), - MV88E6XXX_RESOURCE_ID_ATU, - DEVLINK_RESOURCE_ID_PARENT_TOP, - &size_params); - if (err) - goto out; - - devlink_resource_size_params_init(&size_params, - mv88e6xxx_num_macs(chip) / 4, - mv88e6xxx_num_macs(chip) / 4, - 1, DEVLINK_RESOURCE_UNIT_ENTRY); - - err = dsa_devlink_resource_register(ds, "ATU_bin_0", - mv88e6xxx_num_macs(chip) / 4, - MV88E6XXX_RESOURCE_ID_ATU_BIN_0, - MV88E6XXX_RESOURCE_ID_ATU, - &size_params); - if (err) - goto out; - - err = dsa_devlink_resource_register(ds, "ATU_bin_1", - mv88e6xxx_num_macs(chip) / 4, - MV88E6XXX_RESOURCE_ID_ATU_BIN_1, - MV88E6XXX_RESOURCE_ID_ATU, - &size_params); - if (err) - goto out; - - err = dsa_devlink_resource_register(ds, "ATU_bin_2", - mv88e6xxx_num_macs(chip) / 4, - MV88E6XXX_RESOURCE_ID_ATU_BIN_2, - MV88E6XXX_RESOURCE_ID_ATU, - &size_params); - if (err) - goto out; - - err = dsa_devlink_resource_register(ds, "ATU_bin_3", - mv88e6xxx_num_macs(chip) / 4, - MV88E6XXX_RESOURCE_ID_ATU_BIN_3, - MV88E6XXX_RESOURCE_ID_ATU, - &size_params); - if (err) - goto out; - - dsa_devlink_resource_occ_get_register(ds, - MV88E6XXX_RESOURCE_ID_ATU, - mv88e6xxx_devlink_atu_get, - chip); - - dsa_devlink_resource_occ_get_register(ds, - MV88E6XXX_RESOURCE_ID_ATU_BIN_0, - mv88e6xxx_devlink_atu_bin_0_get, - chip); - - dsa_devlink_resource_occ_get_register(ds, - MV88E6XXX_RESOURCE_ID_ATU_BIN_1, - mv88e6xxx_devlink_atu_bin_1_get, - chip); - - dsa_devlink_resource_occ_get_register(ds, - MV88E6XXX_RESOURCE_ID_ATU_BIN_2, - mv88e6xxx_devlink_atu_bin_2_get, - chip); - - dsa_devlink_resource_occ_get_register(ds, - MV88E6XXX_RESOURCE_ID_ATU_BIN_3, - mv88e6xxx_devlink_atu_bin_3_get, - chip); - - return 0; - -out: - dsa_devlink_resources_unregister(ds); - return err; -} - static void mv88e6xxx_teardown(struct dsa_switch *ds) { mv88e6xxx_teardown_devlink_params(ds); dsa_devlink_resources_unregister(ds); + mv88e6xxx_teardown_devlink_regions(ds); } static int mv88e6xxx_setup(struct dsa_switch *ds) @@ -3211,7 +2972,18 @@ unlock: err = mv88e6xxx_setup_devlink_params(ds); if (err) - dsa_devlink_resources_unregister(ds); + goto out_resources; + + err = mv88e6xxx_setup_devlink_regions(ds); + if (err) + goto out_params; + + return 0; + +out_params: + mv88e6xxx_teardown_devlink_params(ds); +out_resources: + dsa_devlink_resources_unregister(ds); return err; } @@ -3329,12 +3101,6 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip, return 0; } -static const struct of_device_id mv88e6xxx_mdio_external_match[] = { - { .compatible = "marvell,mv88e6xxx-mdio-external", - .data = (void *)true }, - { }, -}; - static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) { @@ -3354,7 +3120,6 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip) static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, struct device_node *np) { - const struct of_device_id *match; struct device_node *child; int err; @@ -3372,8 +3137,8 @@ static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip, * bus. */ for_each_available_child_of_node(np, child) { - match = of_match_node(mv88e6xxx_mdio_external_match, child); - if (match) { + if (of_device_is_compatible( + child, "marvell,mv88e6xxx-mdio-external")) { err = mv88e6xxx_mdio_register(chip, child, true); if (err) { mv88e6xxx_mdios_unregister(chip); @@ -5614,6 +5379,7 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_ts_info = mv88e6xxx_get_ts_info, .devlink_param_get = mv88e6xxx_devlink_param_get, .devlink_param_set = mv88e6xxx_devlink_param_set, + .devlink_info_get = mv88e6xxx_devlink_info_get, }; 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 823ae89e5fca..81c244fc0419 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -238,6 +238,19 @@ struct mv88e6xxx_port { bool mirror_egress; unsigned int serdes_irq; char serdes_irq_name[64]; + struct devlink_region *region; +}; + +enum mv88e6xxx_region_id { + MV88E6XXX_REGION_GLOBAL1 = 0, + MV88E6XXX_REGION_GLOBAL2, + MV88E6XXX_REGION_ATU, + + _MV88E6XXX_REGION_MAX, +}; + +struct mv88e6xxx_region_priv { + enum mv88e6xxx_region_id id; }; struct mv88e6xxx_chip { @@ -334,6 +347,9 @@ struct mv88e6xxx_chip { /* Array of port structures. */ struct mv88e6xxx_port ports[DSA_MAX_PORTS]; + + /* devlink regions */ + struct devlink_region *regions[_MV88E6XXX_REGION_MAX]; }; struct mv88e6xxx_bus_ops { @@ -689,4 +705,6 @@ static inline void mv88e6xxx_reg_unlock(struct mv88e6xxx_chip *chip) mutex_unlock(&chip->reg_lock); } +int mv88e6xxx_fid_map(struct mv88e6xxx_chip *chip, unsigned long *bitmap); + #endif /* _MV88E6XXX_CHIP_H */ diff --git a/drivers/net/dsa/mv88e6xxx/devlink.c b/drivers/net/dsa/mv88e6xxx/devlink.c new file mode 100644 index 000000000000..10cd1bfd81a0 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/devlink.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <net/dsa.h> + +#include "chip.h" +#include "devlink.h" +#include "global1.h" +#include "global2.h" +#include "port.h" + +static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash) +{ + if (chip->info->ops->atu_get_hash) + return chip->info->ops->atu_get_hash(chip, hash); + + return -EOPNOTSUPP; +} + +static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash) +{ + if (chip->info->ops->atu_set_hash) + return chip->info->ops->atu_set_hash(chip, hash); + + return -EOPNOTSUPP; +} + +enum mv88e6xxx_devlink_param_id { + MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, + MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, +}; + +int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + + switch (id) { + case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: + err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8); + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; +} + +int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + + switch (id) { + case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH: + err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8); + break; + default: + err = -EOPNOTSUPP; + break; + } + + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static const struct devlink_param mv88e6xxx_devlink_params[] = { + DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, + "ATU_hash", DEVLINK_PARAM_TYPE_U8, + BIT(DEVLINK_PARAM_CMODE_RUNTIME)), +}; + +int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds) +{ + return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params, + ARRAY_SIZE(mv88e6xxx_devlink_params)); +} + +void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds) +{ + dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params, + ARRAY_SIZE(mv88e6xxx_devlink_params)); +} + +enum mv88e6xxx_devlink_resource_id { + MV88E6XXX_RESOURCE_ID_ATU, + MV88E6XXX_RESOURCE_ID_ATU_BIN_0, + MV88E6XXX_RESOURCE_ID_ATU_BIN_1, + MV88E6XXX_RESOURCE_ID_ATU_BIN_2, + MV88E6XXX_RESOURCE_ID_ATU_BIN_3, +}; + +static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip, + u16 bin) +{ + u16 occupancy = 0; + int err; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL, + bin); + if (err) { + dev_err(chip->dev, "failed to set ATU stats kind/bin\n"); + goto unlock; + } + + err = mv88e6xxx_g1_atu_get_next(chip, 0); + if (err) { + dev_err(chip->dev, "failed to perform ATU get next\n"); + goto unlock; + } + + err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy); + if (err) { + dev_err(chip->dev, "failed to get ATU stats\n"); + goto unlock; + } + + occupancy &= MV88E6XXX_G2_ATU_STATS_MASK; + +unlock: + mv88e6xxx_reg_unlock(chip); + + return occupancy; +} + +static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv) +{ + struct mv88e6xxx_chip *chip = priv; + + return mv88e6xxx_devlink_atu_bin_get(chip, + MV88E6XXX_G2_ATU_STATS_BIN_0); +} + +static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv) +{ + struct mv88e6xxx_chip *chip = priv; + + return mv88e6xxx_devlink_atu_bin_get(chip, + MV88E6XXX_G2_ATU_STATS_BIN_1); +} + +static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv) +{ + struct mv88e6xxx_chip *chip = priv; + + return mv88e6xxx_devlink_atu_bin_get(chip, + MV88E6XXX_G2_ATU_STATS_BIN_2); +} + +static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv) +{ + struct mv88e6xxx_chip *chip = priv; + + return mv88e6xxx_devlink_atu_bin_get(chip, + MV88E6XXX_G2_ATU_STATS_BIN_3); +} + +static u64 mv88e6xxx_devlink_atu_get(void *priv) +{ + return mv88e6xxx_devlink_atu_bin_0_get(priv) + + mv88e6xxx_devlink_atu_bin_1_get(priv) + + mv88e6xxx_devlink_atu_bin_2_get(priv) + + mv88e6xxx_devlink_atu_bin_3_get(priv); +} + +int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds) +{ + struct devlink_resource_size_params size_params; + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + devlink_resource_size_params_init(&size_params, + mv88e6xxx_num_macs(chip), + mv88e6xxx_num_macs(chip), + 1, DEVLINK_RESOURCE_UNIT_ENTRY); + + err = dsa_devlink_resource_register(ds, "ATU", + mv88e6xxx_num_macs(chip), + MV88E6XXX_RESOURCE_ID_ATU, + DEVLINK_RESOURCE_ID_PARENT_TOP, + &size_params); + if (err) + goto out; + + devlink_resource_size_params_init(&size_params, + mv88e6xxx_num_macs(chip) / 4, + mv88e6xxx_num_macs(chip) / 4, + 1, DEVLINK_RESOURCE_UNIT_ENTRY); + + err = dsa_devlink_resource_register(ds, "ATU_bin_0", + mv88e6xxx_num_macs(chip) / 4, + MV88E6XXX_RESOURCE_ID_ATU_BIN_0, + MV88E6XXX_RESOURCE_ID_ATU, + &size_params); + if (err) + goto out; + + err = dsa_devlink_resource_register(ds, "ATU_bin_1", + mv88e6xxx_num_macs(chip) / 4, + MV88E6XXX_RESOURCE_ID_ATU_BIN_1, + MV88E6XXX_RESOURCE_ID_ATU, + &size_params); + if (err) + goto out; + + err = dsa_devlink_resource_register(ds, "ATU_bin_2", + mv88e6xxx_num_macs(chip) / 4, + MV88E6XXX_RESOURCE_ID_ATU_BIN_2, + MV88E6XXX_RESOURCE_ID_ATU, + &size_params); + if (err) + goto out; + + err = dsa_devlink_resource_register(ds, "ATU_bin_3", + mv88e6xxx_num_macs(chip) / 4, + MV88E6XXX_RESOURCE_ID_ATU_BIN_3, + MV88E6XXX_RESOURCE_ID_ATU, + &size_params); + if (err) + goto out; + + dsa_devlink_resource_occ_get_register(ds, + MV88E6XXX_RESOURCE_ID_ATU, + mv88e6xxx_devlink_atu_get, + chip); + + dsa_devlink_resource_occ_get_register(ds, + MV88E6XXX_RESOURCE_ID_ATU_BIN_0, + mv88e6xxx_devlink_atu_bin_0_get, + chip); + + dsa_devlink_resource_occ_get_register(ds, + MV88E6XXX_RESOURCE_ID_ATU_BIN_1, + mv88e6xxx_devlink_atu_bin_1_get, + chip); + + dsa_devlink_resource_occ_get_register(ds, + MV88E6XXX_RESOURCE_ID_ATU_BIN_2, + mv88e6xxx_devlink_atu_bin_2_get, + chip); + + dsa_devlink_resource_occ_get_register(ds, + MV88E6XXX_RESOURCE_ID_ATU_BIN_3, + mv88e6xxx_devlink_atu_bin_3_get, + chip); + + return 0; + +out: + dsa_devlink_resources_unregister(ds); + return err; +} + +static int mv88e6xxx_region_global_snapshot(struct devlink *dl, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct mv88e6xxx_region_priv *region_priv = ops->priv; + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + struct mv88e6xxx_chip *chip = ds->priv; + u16 *registers; + int i, err; + + registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL); + if (!registers) + return -ENOMEM; + + mv88e6xxx_reg_lock(chip); + for (i = 0; i < 32; i++) { + switch (region_priv->id) { + case MV88E6XXX_REGION_GLOBAL1: + err = mv88e6xxx_g1_read(chip, i, ®isters[i]); + break; + case MV88E6XXX_REGION_GLOBAL2: + err = mv88e6xxx_g2_read(chip, i, ®isters[i]); + break; + default: + err = -EOPNOTSUPP; + } + + if (err) { + kfree(registers); + goto out; + } + } + *data = (u8 *)registers; +out: + mv88e6xxx_reg_unlock(chip); + + return err; +} + +/* The ATU entry varies between mv88e6xxx chipset generations. Define + * a generic format which covers all the current and hopefully future + * mv88e6xxx generations + */ + +struct mv88e6xxx_devlink_atu_entry { + /* The FID is scattered over multiple registers. */ + u16 fid; + u16 atu_op; + u16 atu_data; + u16 atu_01; + u16 atu_23; + u16 atu_45; +}; + +static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip, + int fid, + struct mv88e6xxx_devlink_atu_entry *table, + int *count) +{ + u16 atu_op, atu_data, atu_01, atu_23, atu_45; + struct mv88e6xxx_atu_entry addr; + int err; + + addr.state = 0; + eth_broadcast_addr(addr.mac); + + do { + err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr); + if (err) + return err; + + if (!addr.state) + break; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &atu_op); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &atu_data); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, &atu_01); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, &atu_23); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, &atu_45); + if (err) + return err; + + table[*count].fid = fid; + table[*count].atu_op = atu_op; + table[*count].atu_data = atu_data; + table[*count].atu_01 = atu_01; + table[*count].atu_23 = atu_23; + table[*count].atu_45 = atu_45; + (*count)++; + } while (!is_broadcast_ether_addr(addr.mac)); + + return 0; +} + +static int mv88e6xxx_region_atu_snapshot(struct devlink *dl, + const struct devlink_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct dsa_switch *ds = dsa_devlink_to_ds(dl); + DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); + struct mv88e6xxx_devlink_atu_entry *table; + struct mv88e6xxx_chip *chip = ds->priv; + int fid = -1, count, err; + + table = kmalloc_array(mv88e6xxx_num_databases(chip), + sizeof(struct mv88e6xxx_devlink_atu_entry), + GFP_KERNEL); + if (!table) + return -ENOMEM; + + memset(table, 0, mv88e6xxx_num_databases(chip) * + sizeof(struct mv88e6xxx_devlink_atu_entry)); + + count = 0; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_fid_map(chip, fid_bitmap); + if (err) + goto out; + + while (1) { + fid = find_next_bit(fid_bitmap, MV88E6XXX_N_FID, fid + 1); + if (fid == MV88E6XXX_N_FID) + break; + + err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, table, + &count); + if (err) { + kfree(table); + goto out; + } + } + *data = (u8 *)table; +out: + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port, + const struct devlink_port_region_ops *ops, + struct netlink_ext_ack *extack, + u8 **data) +{ + struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port); + int port = dsa_devlink_port_to_port(devlink_port); + struct mv88e6xxx_chip *chip = ds->priv; + u16 *registers; + int i, err; + + registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL); + if (!registers) + return -ENOMEM; + + mv88e6xxx_reg_lock(chip); + for (i = 0; i < 32; i++) { + err = mv88e6xxx_port_read(chip, port, i, ®isters[i]); + if (err) { + kfree(registers); + goto out; + } + } + *data = (u8 *)registers; +out: + mv88e6xxx_reg_unlock(chip); + + return err; +} + +static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = { + .id = MV88E6XXX_REGION_GLOBAL1, +}; + +static struct devlink_region_ops mv88e6xxx_region_global1_ops = { + .name = "global1", + .snapshot = mv88e6xxx_region_global_snapshot, + .destructor = kfree, + .priv = &mv88e6xxx_region_global1_priv, +}; + +static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = { + .id = MV88E6XXX_REGION_GLOBAL2, +}; + +static struct devlink_region_ops mv88e6xxx_region_global2_ops = { + .name = "global2", + .snapshot = mv88e6xxx_region_global_snapshot, + .destructor = kfree, + .priv = &mv88e6xxx_region_global2_priv, +}; + +static struct devlink_region_ops mv88e6xxx_region_atu_ops = { + .name = "atu", + .snapshot = mv88e6xxx_region_atu_snapshot, + .destructor = kfree, +}; + +static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = { + .name = "port", + .snapshot = mv88e6xxx_region_port_snapshot, + .destructor = kfree, +}; + +struct mv88e6xxx_region { + struct devlink_region_ops *ops; + u64 size; +}; + +static struct mv88e6xxx_region mv88e6xxx_regions[] = { + [MV88E6XXX_REGION_GLOBAL1] = { + .ops = &mv88e6xxx_region_global1_ops, + .size = 32 * sizeof(u16) + }, + [MV88E6XXX_REGION_GLOBAL2] = { + .ops = &mv88e6xxx_region_global2_ops, + .size = 32 * sizeof(u16) }, + [MV88E6XXX_REGION_ATU] = { + .ops = &mv88e6xxx_region_atu_ops + /* calculated at runtime */ + }, +}; + +static void +mv88e6xxx_teardown_devlink_regions_global(struct mv88e6xxx_chip *chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) + dsa_devlink_region_destroy(chip->regions[i]); +} + +static void +mv88e6xxx_teardown_devlink_regions_port(struct mv88e6xxx_chip *chip, + int port) +{ + dsa_devlink_region_destroy(chip->ports[port].region); +} + +static int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, + struct mv88e6xxx_chip *chip, + int port) +{ + struct devlink_region *region; + + region = dsa_devlink_port_region_create(ds, + port, + &mv88e6xxx_region_port_ops, 1, + 32 * sizeof(u16)); + if (IS_ERR(region)) + return PTR_ERR(region); + + chip->ports[port].region = region; + + return 0; +} + +static void +mv88e6xxx_teardown_devlink_regions_ports(struct mv88e6xxx_chip *chip) +{ + int port; + + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) + mv88e6xxx_teardown_devlink_regions_port(chip, port); +} + +static int mv88e6xxx_setup_devlink_regions_ports(struct dsa_switch *ds, + struct mv88e6xxx_chip *chip) +{ + int port; + int err; + + for (port = 0; port < mv88e6xxx_num_ports(chip); port++) { + err = mv88e6xxx_setup_devlink_regions_port(ds, chip, port); + if (err) + goto out; + } + + return 0; + +out: + while (port-- > 0) + mv88e6xxx_teardown_devlink_regions_port(chip, port); + + return err; +} + +static int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds, + struct mv88e6xxx_chip *chip) +{ + struct devlink_region_ops *ops; + struct devlink_region *region; + u64 size; + int i, j; + + for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) { + ops = mv88e6xxx_regions[i].ops; + size = mv88e6xxx_regions[i].size; + + if (i == MV88E6XXX_REGION_ATU) + size = mv88e6xxx_num_databases(chip) * + sizeof(struct mv88e6xxx_devlink_atu_entry); + + region = dsa_devlink_region_create(ds, ops, 1, size); + if (IS_ERR(region)) + goto out; + chip->regions[i] = region; + } + return 0; + +out: + for (j = 0; j < i; j++) + dsa_devlink_region_destroy(chip->regions[j]); + + return PTR_ERR(region); +} + +int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + err = mv88e6xxx_setup_devlink_regions_global(ds, chip); + if (err) + return err; + + err = mv88e6xxx_setup_devlink_regions_ports(ds, chip); + if (err) + mv88e6xxx_teardown_devlink_regions_global(chip); + + return err; +} + +void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds) +{ + struct mv88e6xxx_chip *chip = ds->priv; + + mv88e6xxx_teardown_devlink_regions_ports(chip); + mv88e6xxx_teardown_devlink_regions_global(chip); +} + +int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + err = devlink_info_driver_name_put(req, "mv88e6xxx"); + if (err) + return err; + + return devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + chip->info->name); +} diff --git a/drivers/net/dsa/mv88e6xxx/devlink.h b/drivers/net/dsa/mv88e6xxx/devlink.h new file mode 100644 index 000000000000..3d72db3dcf95 --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/devlink.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* Marvell 88E6xxx Switch devlink support. */ + +#ifndef _MV88E6XXX_DEVLINK_H +#define _MV88E6XXX_DEVLINK_H + +int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds); +void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds); +int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds); +int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx); +int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id, + struct devlink_param_gset_ctx *ctx); +int mv88e6xxx_setup_devlink_regions(struct dsa_switch *ds); +void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds); + +int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, + struct devlink_info_req *req, + struct netlink_ext_ack *extack); +#endif /* _MV88E6XXX_DEVLINK_H */ diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index a4c488b12e8f..094d17a1d037 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -211,49 +211,20 @@ int mv88e6xxx_port_hwtstamp_get(struct dsa_switch *ds, int port, -EFAULT : 0; } -/* Get the start of the PTP header in this skb */ -static u8 *parse_ptp_header(struct sk_buff *skb, unsigned int type) -{ - u8 *data = skb_mac_header(skb); - unsigned int offset = 0; - - if (type & PTP_CLASS_VLAN) - offset += VLAN_HLEN; - - switch (type & PTP_CLASS_PMASK) { - case PTP_CLASS_IPV4: - offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN; - break; - case PTP_CLASS_IPV6: - offset += ETH_HLEN + IP6_HLEN + UDP_HLEN; - break; - case PTP_CLASS_L2: - offset += ETH_HLEN; - break; - default: - return NULL; - } - - /* Ensure that the entire header is present in this packet. */ - if (skb->len + ETH_HLEN < offset + 34) - return NULL; - - return data + offset; -} - /* Returns a pointer to the PTP header if the caller should time stamp, * or NULL if the caller should not. */ -static u8 *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, int port, - struct sk_buff *skb, unsigned int type) +static struct ptp_header *mv88e6xxx_should_tstamp(struct mv88e6xxx_chip *chip, + int port, struct sk_buff *skb, + unsigned int type) { struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; - u8 *hdr; + struct ptp_header *hdr; if (!chip->info->ptp_support) return NULL; - hdr = parse_ptp_header(skb, type); + hdr = ptp_parse_header(skb, type); if (!hdr) return NULL; @@ -275,12 +246,11 @@ static int mv88e6xxx_ts_valid(u16 status) static int seq_match(struct sk_buff *skb, u16 ts_seqid) { unsigned int type = SKB_PTP_TYPE(skb); - u8 *hdr = parse_ptp_header(skb, type); - __be16 *seqid; + struct ptp_header *hdr; - seqid = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); + hdr = ptp_parse_header(skb, type); - return ts_seqid == ntohs(*seqid); + return ts_seqid == ntohs(hdr->sequence_id); } static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, @@ -357,9 +327,9 @@ static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip, &ps->rx_queue2); } -static int is_pdelay_resp(u8 *msgtype) +static int is_pdelay_resp(const struct ptp_header *hdr) { - return (*msgtype & 0xf) == 3; + return (hdr->tsmt & 0xf) == 3; } bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, @@ -367,7 +337,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, { struct mv88e6xxx_port_hwtstamp *ps; struct mv88e6xxx_chip *chip; - u8 *hdr; + struct ptp_header *hdr; chip = ds->priv; ps = &chip->port_hwtstamp[port]; @@ -503,8 +473,7 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, { struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; - __be16 *seq_ptr; - u8 *hdr; + struct ptp_header *hdr; if (!(skb_shinfo(clone)->tx_flags & SKBTX_HW_TSTAMP)) return false; @@ -513,15 +482,13 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, if (!hdr) return false; - seq_ptr = (__be16 *)(hdr + OFF_PTP_SEQUENCE_ID); - if (test_and_set_bit_lock(MV88E6XXX_HWTSTAMP_TX_IN_PROGRESS, &ps->state)) return false; ps->tx_skb = clone; ps->tx_tstamp_start = jiffies; - ps->tx_seq_id = be16_to_cpup(seq_ptr); + ps->tx_seq_id = be16_to_cpu(hdr->sequence_id); ptp_schedule_worker(chip->ptp_clock, 0); return true; |