Commit a5a3303e authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mv88e6xxx-Add-devlink-regions-support'



Andrew Lunn says:

====================
mv88e6xxx: Add devlink regions support

Make use of devlink regions to allow read access to some of the
internal of the switches. Currently access to global1, global2 and the
ATU is provided.

The switch itself will never trigger a region snapshot, it is assumed
it is performed from user space as needed.

v2:
Remove left of debug print
Comment ATU format is generic to mv88e6xxx
Combine declaration and the assignment on a single line.

v3:
Drop support for port regions
Improve the devlink API with a priv member and passing the region to
the snapshot function
Make the helper to convert from devlink to ds an inline function

v4:
Add missing kerneldoc in ICE driver
Fix typo for global2 reading global1 registers
Make use of enum to make code more readable
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0db0561d 93157307
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
# 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
+31 −259
Original line number Diff line number Diff line
@@ -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)
{
@@ -2837,248 +2834,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,6 +2971,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)

	err = mv88e6xxx_setup_devlink_params(ds);
	if (err)
		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;
@@ -5607,6 +5378,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)
+18 −0
Original line number Diff line number Diff line
@@ -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 */
+532 −0
Original line number Diff line number Diff line
// 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, &registers[i]);
			break;
		case MV88E6XXX_REGION_GLOBAL2:
			err = mv88e6xxx_g2_read(chip, i, &registers[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 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,
};

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]);
}

void mv88e6xxx_teardown_devlink_regions(struct dsa_switch *ds)
{
	struct mv88e6xxx_chip *chip = ds->priv;

	mv88e6xxx_teardown_devlink_regions_global(chip);
}

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;

	return mv88e6xxx_setup_devlink_regions_global(ds, 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);
}
+21 −0
Original line number Diff line number Diff line
/* 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 */
Loading