162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci#include <net/dsa.h>
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "chip.h"
562306a36Sopenharmony_ci#include "devlink.h"
662306a36Sopenharmony_ci#include "global1.h"
762306a36Sopenharmony_ci#include "global2.h"
862306a36Sopenharmony_ci#include "port.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistatic int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	if (chip->info->ops->atu_get_hash)
1362306a36Sopenharmony_ci		return chip->info->ops->atu_get_hash(chip, hash);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	return -EOPNOTSUPP;
1662306a36Sopenharmony_ci}
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	if (chip->info->ops->atu_set_hash)
2162306a36Sopenharmony_ci		return chip->info->ops->atu_set_hash(chip, hash);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	return -EOPNOTSUPP;
2462306a36Sopenharmony_ci}
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cienum mv88e6xxx_devlink_param_id {
2762306a36Sopenharmony_ci	MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
2862306a36Sopenharmony_ci	MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id,
3262306a36Sopenharmony_ci				struct devlink_param_gset_ctx *ctx)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
3562306a36Sopenharmony_ci	int err;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	switch (id) {
4062306a36Sopenharmony_ci	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
4162306a36Sopenharmony_ci		err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8);
4262306a36Sopenharmony_ci		break;
4362306a36Sopenharmony_ci	default:
4462306a36Sopenharmony_ci		err = -EOPNOTSUPP;
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return err;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciint mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id,
5462306a36Sopenharmony_ci				struct devlink_param_gset_ctx *ctx)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
5762306a36Sopenharmony_ci	int err;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	switch (id) {
6262306a36Sopenharmony_ci	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
6362306a36Sopenharmony_ci		err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8);
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	default:
6662306a36Sopenharmony_ci		err = -EOPNOTSUPP;
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return err;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const struct devlink_param mv88e6xxx_devlink_params[] = {
7662306a36Sopenharmony_ci	DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
7762306a36Sopenharmony_ci				 "ATU_hash", DEVLINK_PARAM_TYPE_U8,
7862306a36Sopenharmony_ci				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciint mv88e6xxx_setup_devlink_params(struct dsa_switch *ds)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params,
8462306a36Sopenharmony_ci					   ARRAY_SIZE(mv88e6xxx_devlink_params));
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params,
9062306a36Sopenharmony_ci				      ARRAY_SIZE(mv88e6xxx_devlink_params));
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cienum mv88e6xxx_devlink_resource_id {
9462306a36Sopenharmony_ci	MV88E6XXX_RESOURCE_ID_ATU,
9562306a36Sopenharmony_ci	MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
9662306a36Sopenharmony_ci	MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
9762306a36Sopenharmony_ci	MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
9862306a36Sopenharmony_ci	MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip,
10262306a36Sopenharmony_ci					 u16 bin)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	u16 occupancy = 0;
10562306a36Sopenharmony_ci	int err;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL,
11062306a36Sopenharmony_ci					 bin);
11162306a36Sopenharmony_ci	if (err) {
11262306a36Sopenharmony_ci		dev_err(chip->dev, "failed to set ATU stats kind/bin\n");
11362306a36Sopenharmony_ci		goto unlock;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	err = mv88e6xxx_g1_atu_get_next(chip, 0);
11762306a36Sopenharmony_ci	if (err) {
11862306a36Sopenharmony_ci		dev_err(chip->dev, "failed to perform ATU get next\n");
11962306a36Sopenharmony_ci		goto unlock;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy);
12362306a36Sopenharmony_ci	if (err) {
12462306a36Sopenharmony_ci		dev_err(chip->dev, "failed to get ATU stats\n");
12562306a36Sopenharmony_ci		goto unlock;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	occupancy &= MV88E6XXX_G2_ATU_STATS_MASK;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciunlock:
13162306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	return occupancy;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = priv;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return mv88e6xxx_devlink_atu_bin_get(chip,
14162306a36Sopenharmony_ci					     MV88E6XXX_G2_ATU_STATS_BIN_0);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = priv;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return mv88e6xxx_devlink_atu_bin_get(chip,
14962306a36Sopenharmony_ci					     MV88E6XXX_G2_ATU_STATS_BIN_1);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = priv;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return mv88e6xxx_devlink_atu_bin_get(chip,
15762306a36Sopenharmony_ci					     MV88E6XXX_G2_ATU_STATS_BIN_2);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = priv;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return mv88e6xxx_devlink_atu_bin_get(chip,
16562306a36Sopenharmony_ci					     MV88E6XXX_G2_ATU_STATS_BIN_3);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic u64 mv88e6xxx_devlink_atu_get(void *priv)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return mv88e6xxx_devlink_atu_bin_0_get(priv) +
17162306a36Sopenharmony_ci		mv88e6xxx_devlink_atu_bin_1_get(priv) +
17262306a36Sopenharmony_ci		mv88e6xxx_devlink_atu_bin_2_get(priv) +
17362306a36Sopenharmony_ci		mv88e6xxx_devlink_atu_bin_3_get(priv);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciint mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct devlink_resource_size_params size_params;
17962306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
18062306a36Sopenharmony_ci	int err;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	devlink_resource_size_params_init(&size_params,
18362306a36Sopenharmony_ci					  mv88e6xxx_num_macs(chip),
18462306a36Sopenharmony_ci					  mv88e6xxx_num_macs(chip),
18562306a36Sopenharmony_ci					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	err = dsa_devlink_resource_register(ds, "ATU",
18862306a36Sopenharmony_ci					    mv88e6xxx_num_macs(chip),
18962306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU,
19062306a36Sopenharmony_ci					    DEVLINK_RESOURCE_ID_PARENT_TOP,
19162306a36Sopenharmony_ci					    &size_params);
19262306a36Sopenharmony_ci	if (err)
19362306a36Sopenharmony_ci		goto out;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	devlink_resource_size_params_init(&size_params,
19662306a36Sopenharmony_ci					  mv88e6xxx_num_macs(chip) / 4,
19762306a36Sopenharmony_ci					  mv88e6xxx_num_macs(chip) / 4,
19862306a36Sopenharmony_ci					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	err = dsa_devlink_resource_register(ds, "ATU_bin_0",
20162306a36Sopenharmony_ci					    mv88e6xxx_num_macs(chip) / 4,
20262306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
20362306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU,
20462306a36Sopenharmony_ci					    &size_params);
20562306a36Sopenharmony_ci	if (err)
20662306a36Sopenharmony_ci		goto out;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	err = dsa_devlink_resource_register(ds, "ATU_bin_1",
20962306a36Sopenharmony_ci					    mv88e6xxx_num_macs(chip) / 4,
21062306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
21162306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU,
21262306a36Sopenharmony_ci					    &size_params);
21362306a36Sopenharmony_ci	if (err)
21462306a36Sopenharmony_ci		goto out;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	err = dsa_devlink_resource_register(ds, "ATU_bin_2",
21762306a36Sopenharmony_ci					    mv88e6xxx_num_macs(chip) / 4,
21862306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
21962306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU,
22062306a36Sopenharmony_ci					    &size_params);
22162306a36Sopenharmony_ci	if (err)
22262306a36Sopenharmony_ci		goto out;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	err = dsa_devlink_resource_register(ds, "ATU_bin_3",
22562306a36Sopenharmony_ci					    mv88e6xxx_num_macs(chip) / 4,
22662306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
22762306a36Sopenharmony_ci					    MV88E6XXX_RESOURCE_ID_ATU,
22862306a36Sopenharmony_ci					    &size_params);
22962306a36Sopenharmony_ci	if (err)
23062306a36Sopenharmony_ci		goto out;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	dsa_devlink_resource_occ_get_register(ds,
23362306a36Sopenharmony_ci					      MV88E6XXX_RESOURCE_ID_ATU,
23462306a36Sopenharmony_ci					      mv88e6xxx_devlink_atu_get,
23562306a36Sopenharmony_ci					      chip);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	dsa_devlink_resource_occ_get_register(ds,
23862306a36Sopenharmony_ci					      MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
23962306a36Sopenharmony_ci					      mv88e6xxx_devlink_atu_bin_0_get,
24062306a36Sopenharmony_ci					      chip);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	dsa_devlink_resource_occ_get_register(ds,
24362306a36Sopenharmony_ci					      MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
24462306a36Sopenharmony_ci					      mv88e6xxx_devlink_atu_bin_1_get,
24562306a36Sopenharmony_ci					      chip);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	dsa_devlink_resource_occ_get_register(ds,
24862306a36Sopenharmony_ci					      MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
24962306a36Sopenharmony_ci					      mv88e6xxx_devlink_atu_bin_2_get,
25062306a36Sopenharmony_ci					      chip);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	dsa_devlink_resource_occ_get_register(ds,
25362306a36Sopenharmony_ci					      MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
25462306a36Sopenharmony_ci					      mv88e6xxx_devlink_atu_bin_3_get,
25562306a36Sopenharmony_ci					      chip);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ciout:
26062306a36Sopenharmony_ci	dsa_devlink_resources_unregister(ds);
26162306a36Sopenharmony_ci	return err;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int mv88e6xxx_region_global_snapshot(struct devlink *dl,
26562306a36Sopenharmony_ci					    const struct devlink_region_ops *ops,
26662306a36Sopenharmony_ci					    struct netlink_ext_ack *extack,
26762306a36Sopenharmony_ci					    u8 **data)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct mv88e6xxx_region_priv *region_priv = ops->priv;
27062306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
27162306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
27262306a36Sopenharmony_ci	u16 *registers;
27362306a36Sopenharmony_ci	int i, err;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
27662306a36Sopenharmony_ci	if (!registers)
27762306a36Sopenharmony_ci		return -ENOMEM;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
28062306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
28162306a36Sopenharmony_ci		switch (region_priv->id) {
28262306a36Sopenharmony_ci		case MV88E6XXX_REGION_GLOBAL1:
28362306a36Sopenharmony_ci			err = mv88e6xxx_g1_read(chip, i, &registers[i]);
28462306a36Sopenharmony_ci			break;
28562306a36Sopenharmony_ci		case MV88E6XXX_REGION_GLOBAL2:
28662306a36Sopenharmony_ci			err = mv88e6xxx_g2_read(chip, i, &registers[i]);
28762306a36Sopenharmony_ci			break;
28862306a36Sopenharmony_ci		default:
28962306a36Sopenharmony_ci			err = -EOPNOTSUPP;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		if (err) {
29362306a36Sopenharmony_ci			kfree(registers);
29462306a36Sopenharmony_ci			goto out;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci	*data = (u8 *)registers;
29862306a36Sopenharmony_ciout:
29962306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return err;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/* The ATU entry varies between mv88e6xxx chipset generations. Define
30562306a36Sopenharmony_ci * a generic format which covers all the current and hopefully future
30662306a36Sopenharmony_ci * mv88e6xxx generations
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistruct mv88e6xxx_devlink_atu_entry {
31062306a36Sopenharmony_ci	/* The FID is scattered over multiple registers. */
31162306a36Sopenharmony_ci	u16 fid;
31262306a36Sopenharmony_ci	u16 atu_op;
31362306a36Sopenharmony_ci	u16 atu_data;
31462306a36Sopenharmony_ci	u16 atu_01;
31562306a36Sopenharmony_ci	u16 atu_23;
31662306a36Sopenharmony_ci	u16 atu_45;
31762306a36Sopenharmony_ci};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip,
32062306a36Sopenharmony_ci					     int fid,
32162306a36Sopenharmony_ci					     struct mv88e6xxx_devlink_atu_entry *table,
32262306a36Sopenharmony_ci					     int *count)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	u16 atu_op, atu_data, atu_01, atu_23, atu_45;
32562306a36Sopenharmony_ci	struct mv88e6xxx_atu_entry addr;
32662306a36Sopenharmony_ci	int err;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	addr.state = 0;
32962306a36Sopenharmony_ci	eth_broadcast_addr(addr.mac);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	do {
33262306a36Sopenharmony_ci		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
33362306a36Sopenharmony_ci		if (err)
33462306a36Sopenharmony_ci			return err;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (!addr.state)
33762306a36Sopenharmony_ci			break;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &atu_op);
34062306a36Sopenharmony_ci		if (err)
34162306a36Sopenharmony_ci			return err;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &atu_data);
34462306a36Sopenharmony_ci		if (err)
34562306a36Sopenharmony_ci			return err;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, &atu_01);
34862306a36Sopenharmony_ci		if (err)
34962306a36Sopenharmony_ci			return err;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, &atu_23);
35262306a36Sopenharmony_ci		if (err)
35362306a36Sopenharmony_ci			return err;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, &atu_45);
35662306a36Sopenharmony_ci		if (err)
35762306a36Sopenharmony_ci			return err;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		table[*count].fid = fid;
36062306a36Sopenharmony_ci		table[*count].atu_op = atu_op;
36162306a36Sopenharmony_ci		table[*count].atu_data = atu_data;
36262306a36Sopenharmony_ci		table[*count].atu_01 = atu_01;
36362306a36Sopenharmony_ci		table[*count].atu_23 = atu_23;
36462306a36Sopenharmony_ci		table[*count].atu_45 = atu_45;
36562306a36Sopenharmony_ci		(*count)++;
36662306a36Sopenharmony_ci	} while (!is_broadcast_ether_addr(addr.mac));
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
37262306a36Sopenharmony_ci					 const struct devlink_region_ops *ops,
37362306a36Sopenharmony_ci					 struct netlink_ext_ack *extack,
37462306a36Sopenharmony_ci					 u8 **data)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
37762306a36Sopenharmony_ci	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
37862306a36Sopenharmony_ci	struct mv88e6xxx_devlink_atu_entry *table;
37962306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
38062306a36Sopenharmony_ci	int fid = -1, count, err;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	table = kmalloc_array(mv88e6xxx_num_databases(chip),
38362306a36Sopenharmony_ci			      sizeof(struct mv88e6xxx_devlink_atu_entry),
38462306a36Sopenharmony_ci			      GFP_KERNEL);
38562306a36Sopenharmony_ci	if (!table)
38662306a36Sopenharmony_ci		return -ENOMEM;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	memset(table, 0, mv88e6xxx_num_databases(chip) *
38962306a36Sopenharmony_ci	       sizeof(struct mv88e6xxx_devlink_atu_entry));
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	count = 0;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	err = mv88e6xxx_fid_map(chip, fid_bitmap);
39662306a36Sopenharmony_ci	if (err) {
39762306a36Sopenharmony_ci		kfree(table);
39862306a36Sopenharmony_ci		goto out;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	while (1) {
40262306a36Sopenharmony_ci		fid = find_next_bit(fid_bitmap, MV88E6XXX_N_FID, fid + 1);
40362306a36Sopenharmony_ci		if (fid == MV88E6XXX_N_FID)
40462306a36Sopenharmony_ci			break;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		err =  mv88e6xxx_region_atu_snapshot_fid(chip, fid, table,
40762306a36Sopenharmony_ci							 &count);
40862306a36Sopenharmony_ci		if (err) {
40962306a36Sopenharmony_ci			kfree(table);
41062306a36Sopenharmony_ci			goto out;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	*data = (u8 *)table;
41462306a36Sopenharmony_ciout:
41562306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return err;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/**
42162306a36Sopenharmony_ci * struct mv88e6xxx_devlink_vtu_entry - Devlink VTU entry
42262306a36Sopenharmony_ci * @fid:   Global1/2:   FID and VLAN policy.
42362306a36Sopenharmony_ci * @sid:   Global1/3:   SID, unknown filters and learning.
42462306a36Sopenharmony_ci * @op:    Global1/5:   FID (old chipsets).
42562306a36Sopenharmony_ci * @vid:   Global1/6:   VID, valid, and page.
42662306a36Sopenharmony_ci * @data:  Global1/7-9: Membership data and priority override.
42762306a36Sopenharmony_ci * @resvd: Reserved. Also happens to align the size to 16B.
42862306a36Sopenharmony_ci *
42962306a36Sopenharmony_ci * The VTU entry format varies between chipset generations, the
43062306a36Sopenharmony_ci * descriptions above represent the superset of all possible
43162306a36Sopenharmony_ci * information, not all fields are valid on all devices. Since this is
43262306a36Sopenharmony_ci * a low-level debug interface, copy all data verbatim and defer
43362306a36Sopenharmony_ci * parsing to the consumer.
43462306a36Sopenharmony_ci */
43562306a36Sopenharmony_cistruct mv88e6xxx_devlink_vtu_entry {
43662306a36Sopenharmony_ci	u16 fid;
43762306a36Sopenharmony_ci	u16 sid;
43862306a36Sopenharmony_ci	u16 op;
43962306a36Sopenharmony_ci	u16 vid;
44062306a36Sopenharmony_ci	u16 data[3];
44162306a36Sopenharmony_ci	u16 resvd;
44262306a36Sopenharmony_ci};
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int mv88e6xxx_region_vtu_snapshot(struct devlink *dl,
44562306a36Sopenharmony_ci					 const struct devlink_region_ops *ops,
44662306a36Sopenharmony_ci					 struct netlink_ext_ack *extack,
44762306a36Sopenharmony_ci					 u8 **data)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct mv88e6xxx_devlink_vtu_entry *table, *entry;
45062306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
45162306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
45262306a36Sopenharmony_ci	struct mv88e6xxx_vtu_entry vlan;
45362306a36Sopenharmony_ci	int err;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	table = kcalloc(mv88e6xxx_max_vid(chip) + 1,
45662306a36Sopenharmony_ci			sizeof(struct mv88e6xxx_devlink_vtu_entry),
45762306a36Sopenharmony_ci			GFP_KERNEL);
45862306a36Sopenharmony_ci	if (!table)
45962306a36Sopenharmony_ci		return -ENOMEM;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	entry = table;
46262306a36Sopenharmony_ci	vlan.vid = mv88e6xxx_max_vid(chip);
46362306a36Sopenharmony_ci	vlan.valid = false;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	do {
46862306a36Sopenharmony_ci		err = mv88e6xxx_g1_vtu_getnext(chip, &vlan);
46962306a36Sopenharmony_ci		if (err)
47062306a36Sopenharmony_ci			break;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		if (!vlan.valid)
47362306a36Sopenharmony_ci			break;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID,
47662306a36Sopenharmony_ci						&entry->fid);
47762306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
47862306a36Sopenharmony_ci						&entry->sid);
47962306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP,
48062306a36Sopenharmony_ci						&entry->op);
48162306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
48262306a36Sopenharmony_ci						&entry->vid);
48362306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
48462306a36Sopenharmony_ci						&entry->data[0]);
48562306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
48662306a36Sopenharmony_ci						&entry->data[1]);
48762306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
48862306a36Sopenharmony_ci						&entry->data[2]);
48962306a36Sopenharmony_ci		if (err)
49062306a36Sopenharmony_ci			break;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		entry++;
49362306a36Sopenharmony_ci	} while (vlan.vid < mv88e6xxx_max_vid(chip));
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (err) {
49862306a36Sopenharmony_ci		kfree(table);
49962306a36Sopenharmony_ci		return err;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	*data = (u8 *)table;
50362306a36Sopenharmony_ci	return 0;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/**
50762306a36Sopenharmony_ci * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry
50862306a36Sopenharmony_ci * @sid:   Global1/3:   SID, unknown filters and learning.
50962306a36Sopenharmony_ci * @vid:   Global1/6:   Valid bit.
51062306a36Sopenharmony_ci * @data:  Global1/7-9: Membership data and priority override.
51162306a36Sopenharmony_ci * @resvd: Reserved. In case we forgot something.
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci * The STU entry format varies between chipset generations. Peridot
51462306a36Sopenharmony_ci * and Amethyst packs the STU data into Global1/7-8. Older silicon
51562306a36Sopenharmony_ci * spreads the information across all three VTU data registers -
51662306a36Sopenharmony_ci * inheriting the layout of even older hardware that had no STU at
51762306a36Sopenharmony_ci * all. Since this is a low-level debug interface, copy all data
51862306a36Sopenharmony_ci * verbatim and defer parsing to the consumer.
51962306a36Sopenharmony_ci */
52062306a36Sopenharmony_cistruct mv88e6xxx_devlink_stu_entry {
52162306a36Sopenharmony_ci	u16 sid;
52262306a36Sopenharmony_ci	u16 vid;
52362306a36Sopenharmony_ci	u16 data[3];
52462306a36Sopenharmony_ci	u16 resvd;
52562306a36Sopenharmony_ci};
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int mv88e6xxx_region_stu_snapshot(struct devlink *dl,
52862306a36Sopenharmony_ci					 const struct devlink_region_ops *ops,
52962306a36Sopenharmony_ci					 struct netlink_ext_ack *extack,
53062306a36Sopenharmony_ci					 u8 **data)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct mv88e6xxx_devlink_stu_entry *table, *entry;
53362306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
53462306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
53562306a36Sopenharmony_ci	struct mv88e6xxx_stu_entry stu;
53662306a36Sopenharmony_ci	int err;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	table = kcalloc(mv88e6xxx_max_sid(chip) + 1,
53962306a36Sopenharmony_ci			sizeof(struct mv88e6xxx_devlink_stu_entry),
54062306a36Sopenharmony_ci			GFP_KERNEL);
54162306a36Sopenharmony_ci	if (!table)
54262306a36Sopenharmony_ci		return -ENOMEM;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	entry = table;
54562306a36Sopenharmony_ci	stu.sid = mv88e6xxx_max_sid(chip);
54662306a36Sopenharmony_ci	stu.valid = false;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	do {
55162306a36Sopenharmony_ci		err = mv88e6xxx_g1_stu_getnext(chip, &stu);
55262306a36Sopenharmony_ci		if (err)
55362306a36Sopenharmony_ci			break;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		if (!stu.valid)
55662306a36Sopenharmony_ci			break;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
55962306a36Sopenharmony_ci						&entry->sid);
56062306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
56162306a36Sopenharmony_ci						&entry->vid);
56262306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
56362306a36Sopenharmony_ci						&entry->data[0]);
56462306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
56562306a36Sopenharmony_ci						&entry->data[1]);
56662306a36Sopenharmony_ci		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
56762306a36Sopenharmony_ci						&entry->data[2]);
56862306a36Sopenharmony_ci		if (err)
56962306a36Sopenharmony_ci			break;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		entry++;
57262306a36Sopenharmony_ci	} while (stu.sid < mv88e6xxx_max_sid(chip));
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (err) {
57762306a36Sopenharmony_ci		kfree(table);
57862306a36Sopenharmony_ci		return err;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	*data = (u8 *)table;
58262306a36Sopenharmony_ci	return 0;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_cistatic int mv88e6xxx_region_pvt_snapshot(struct devlink *dl,
58662306a36Sopenharmony_ci					 const struct devlink_region_ops *ops,
58762306a36Sopenharmony_ci					 struct netlink_ext_ack *extack,
58862306a36Sopenharmony_ci					 u8 **data)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
59162306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
59262306a36Sopenharmony_ci	int dev, port, err;
59362306a36Sopenharmony_ci	u16 *pvt, *cur;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	pvt = kcalloc(MV88E6XXX_MAX_PVT_ENTRIES, sizeof(*pvt), GFP_KERNEL);
59662306a36Sopenharmony_ci	if (!pvt)
59762306a36Sopenharmony_ci		return -ENOMEM;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	cur = pvt;
60262306a36Sopenharmony_ci	for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; dev++) {
60362306a36Sopenharmony_ci		for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; port++) {
60462306a36Sopenharmony_ci			err = mv88e6xxx_g2_pvt_read(chip, dev, port, cur);
60562306a36Sopenharmony_ci			if (err)
60662306a36Sopenharmony_ci				break;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci			cur++;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (err) {
61562306a36Sopenharmony_ci		kfree(pvt);
61662306a36Sopenharmony_ci		return err;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	*data = (u8 *)pvt;
62062306a36Sopenharmony_ci	return 0;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_cistatic int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port,
62462306a36Sopenharmony_ci					  const struct devlink_port_region_ops *ops,
62562306a36Sopenharmony_ci					  struct netlink_ext_ack *extack,
62662306a36Sopenharmony_ci					  u8 **data)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port);
62962306a36Sopenharmony_ci	int port = dsa_devlink_port_to_port(devlink_port);
63062306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
63162306a36Sopenharmony_ci	u16 *registers;
63262306a36Sopenharmony_ci	int i, err;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
63562306a36Sopenharmony_ci	if (!registers)
63662306a36Sopenharmony_ci		return -ENOMEM;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	mv88e6xxx_reg_lock(chip);
63962306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
64062306a36Sopenharmony_ci		err = mv88e6xxx_port_read(chip, port, i, &registers[i]);
64162306a36Sopenharmony_ci		if (err) {
64262306a36Sopenharmony_ci			kfree(registers);
64362306a36Sopenharmony_ci			goto out;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	*data = (u8 *)registers;
64762306a36Sopenharmony_ciout:
64862306a36Sopenharmony_ci	mv88e6xxx_reg_unlock(chip);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	return err;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = {
65462306a36Sopenharmony_ci	.id = MV88E6XXX_REGION_GLOBAL1,
65562306a36Sopenharmony_ci};
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_global1_ops = {
65862306a36Sopenharmony_ci	.name = "global1",
65962306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_global_snapshot,
66062306a36Sopenharmony_ci	.destructor = kfree,
66162306a36Sopenharmony_ci	.priv = &mv88e6xxx_region_global1_priv,
66262306a36Sopenharmony_ci};
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = {
66562306a36Sopenharmony_ci	.id = MV88E6XXX_REGION_GLOBAL2,
66662306a36Sopenharmony_ci};
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_global2_ops = {
66962306a36Sopenharmony_ci	.name = "global2",
67062306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_global_snapshot,
67162306a36Sopenharmony_ci	.destructor = kfree,
67262306a36Sopenharmony_ci	.priv = &mv88e6xxx_region_global2_priv,
67362306a36Sopenharmony_ci};
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_atu_ops = {
67662306a36Sopenharmony_ci	.name = "atu",
67762306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_atu_snapshot,
67862306a36Sopenharmony_ci	.destructor = kfree,
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_vtu_ops = {
68262306a36Sopenharmony_ci	.name = "vtu",
68362306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_vtu_snapshot,
68462306a36Sopenharmony_ci	.destructor = kfree,
68562306a36Sopenharmony_ci};
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_stu_ops = {
68862306a36Sopenharmony_ci	.name = "stu",
68962306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_stu_snapshot,
69062306a36Sopenharmony_ci	.destructor = kfree,
69162306a36Sopenharmony_ci};
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic struct devlink_region_ops mv88e6xxx_region_pvt_ops = {
69462306a36Sopenharmony_ci	.name = "pvt",
69562306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_pvt_snapshot,
69662306a36Sopenharmony_ci	.destructor = kfree,
69762306a36Sopenharmony_ci};
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic const struct devlink_port_region_ops mv88e6xxx_region_port_ops = {
70062306a36Sopenharmony_ci	.name = "port",
70162306a36Sopenharmony_ci	.snapshot = mv88e6xxx_region_port_snapshot,
70262306a36Sopenharmony_ci	.destructor = kfree,
70362306a36Sopenharmony_ci};
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistruct mv88e6xxx_region {
70662306a36Sopenharmony_ci	struct devlink_region_ops *ops;
70762306a36Sopenharmony_ci	u64 size;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	bool (*cond)(struct mv88e6xxx_chip *chip);
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic struct mv88e6xxx_region mv88e6xxx_regions[] = {
71362306a36Sopenharmony_ci	[MV88E6XXX_REGION_GLOBAL1] = {
71462306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_global1_ops,
71562306a36Sopenharmony_ci		.size = 32 * sizeof(u16)
71662306a36Sopenharmony_ci	},
71762306a36Sopenharmony_ci	[MV88E6XXX_REGION_GLOBAL2] = {
71862306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_global2_ops,
71962306a36Sopenharmony_ci		.size = 32 * sizeof(u16) },
72062306a36Sopenharmony_ci	[MV88E6XXX_REGION_ATU] = {
72162306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_atu_ops
72262306a36Sopenharmony_ci	  /* calculated at runtime */
72362306a36Sopenharmony_ci	},
72462306a36Sopenharmony_ci	[MV88E6XXX_REGION_VTU] = {
72562306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_vtu_ops
72662306a36Sopenharmony_ci	  /* calculated at runtime */
72762306a36Sopenharmony_ci	},
72862306a36Sopenharmony_ci	[MV88E6XXX_REGION_STU] = {
72962306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_stu_ops,
73062306a36Sopenharmony_ci		.cond = mv88e6xxx_has_stu,
73162306a36Sopenharmony_ci	  /* calculated at runtime */
73262306a36Sopenharmony_ci	},
73362306a36Sopenharmony_ci	[MV88E6XXX_REGION_PVT] = {
73462306a36Sopenharmony_ci		.ops = &mv88e6xxx_region_pvt_ops,
73562306a36Sopenharmony_ci		.size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16),
73662306a36Sopenharmony_ci		.cond = mv88e6xxx_has_pvt,
73762306a36Sopenharmony_ci	},
73862306a36Sopenharmony_ci};
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_civoid mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
74362306a36Sopenharmony_ci	int i;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++)
74662306a36Sopenharmony_ci		dsa_devlink_region_destroy(chip->regions[i]);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_civoid mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	dsa_devlink_region_destroy(chip->ports[port].region);
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ciint mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
75962306a36Sopenharmony_ci	struct devlink_region *region;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	region = dsa_devlink_port_region_create(ds,
76262306a36Sopenharmony_ci						port,
76362306a36Sopenharmony_ci						&mv88e6xxx_region_port_ops, 1,
76462306a36Sopenharmony_ci						32 * sizeof(u16));
76562306a36Sopenharmony_ci	if (IS_ERR(region))
76662306a36Sopenharmony_ci		return PTR_ERR(region);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	chip->ports[port].region = region;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return 0;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ciint mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	bool (*cond)(struct mv88e6xxx_chip *chip);
77662306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
77762306a36Sopenharmony_ci	struct devlink_region_ops *ops;
77862306a36Sopenharmony_ci	struct devlink_region *region;
77962306a36Sopenharmony_ci	u64 size;
78062306a36Sopenharmony_ci	int i, j;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) {
78362306a36Sopenharmony_ci		ops = mv88e6xxx_regions[i].ops;
78462306a36Sopenharmony_ci		size = mv88e6xxx_regions[i].size;
78562306a36Sopenharmony_ci		cond = mv88e6xxx_regions[i].cond;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (cond && !cond(chip))
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		switch (i) {
79162306a36Sopenharmony_ci		case MV88E6XXX_REGION_ATU:
79262306a36Sopenharmony_ci			size = mv88e6xxx_num_databases(chip) *
79362306a36Sopenharmony_ci				sizeof(struct mv88e6xxx_devlink_atu_entry);
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci		case MV88E6XXX_REGION_VTU:
79662306a36Sopenharmony_ci			size = (mv88e6xxx_max_vid(chip) + 1) *
79762306a36Sopenharmony_ci				sizeof(struct mv88e6xxx_devlink_vtu_entry);
79862306a36Sopenharmony_ci			break;
79962306a36Sopenharmony_ci		case MV88E6XXX_REGION_STU:
80062306a36Sopenharmony_ci			size = (mv88e6xxx_max_sid(chip) + 1) *
80162306a36Sopenharmony_ci				sizeof(struct mv88e6xxx_devlink_stu_entry);
80262306a36Sopenharmony_ci			break;
80362306a36Sopenharmony_ci		}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci		region = dsa_devlink_region_create(ds, ops, 1, size);
80662306a36Sopenharmony_ci		if (IS_ERR(region))
80762306a36Sopenharmony_ci			goto out;
80862306a36Sopenharmony_ci		chip->regions[i] = region;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	return 0;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ciout:
81362306a36Sopenharmony_ci	for (j = 0; j < i; j++)
81462306a36Sopenharmony_ci		dsa_devlink_region_destroy(chip->regions[j]);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return PTR_ERR(region);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ciint mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
82062306a36Sopenharmony_ci			       struct devlink_info_req *req,
82162306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct mv88e6xxx_chip *chip = ds->priv;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return devlink_info_version_fixed_put(req,
82662306a36Sopenharmony_ci					      DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
82762306a36Sopenharmony_ci					      chip->info->name);
82862306a36Sopenharmony_ci}
829