19dd43aa2SAndrew Lunn // SPDX-License-Identifier: GPL-2.0-or-later
29dd43aa2SAndrew Lunn #include <net/dsa.h>
39dd43aa2SAndrew Lunn 
49dd43aa2SAndrew Lunn #include "chip.h"
59dd43aa2SAndrew Lunn #include "devlink.h"
69dd43aa2SAndrew Lunn #include "global1.h"
79dd43aa2SAndrew Lunn #include "global2.h"
8bfb25542SAndrew Lunn #include "port.h"
99dd43aa2SAndrew Lunn 
mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip * chip,u8 * hash)109dd43aa2SAndrew Lunn static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash)
119dd43aa2SAndrew Lunn {
129dd43aa2SAndrew Lunn 	if (chip->info->ops->atu_get_hash)
139dd43aa2SAndrew Lunn 		return chip->info->ops->atu_get_hash(chip, hash);
149dd43aa2SAndrew Lunn 
159dd43aa2SAndrew Lunn 	return -EOPNOTSUPP;
169dd43aa2SAndrew Lunn }
179dd43aa2SAndrew Lunn 
mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip * chip,u8 hash)189dd43aa2SAndrew Lunn static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash)
199dd43aa2SAndrew Lunn {
209dd43aa2SAndrew Lunn 	if (chip->info->ops->atu_set_hash)
219dd43aa2SAndrew Lunn 		return chip->info->ops->atu_set_hash(chip, hash);
229dd43aa2SAndrew Lunn 
239dd43aa2SAndrew Lunn 	return -EOPNOTSUPP;
249dd43aa2SAndrew Lunn }
259dd43aa2SAndrew Lunn 
269dd43aa2SAndrew Lunn enum mv88e6xxx_devlink_param_id {
279dd43aa2SAndrew Lunn 	MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
289dd43aa2SAndrew Lunn 	MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
299dd43aa2SAndrew Lunn };
309dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_param_get(struct dsa_switch * ds,u32 id,struct devlink_param_gset_ctx * ctx)319dd43aa2SAndrew Lunn int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id,
329dd43aa2SAndrew Lunn 				struct devlink_param_gset_ctx *ctx)
339dd43aa2SAndrew Lunn {
349dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
359dd43aa2SAndrew Lunn 	int err;
369dd43aa2SAndrew Lunn 
379dd43aa2SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
389dd43aa2SAndrew Lunn 
399dd43aa2SAndrew Lunn 	switch (id) {
409dd43aa2SAndrew Lunn 	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
419dd43aa2SAndrew Lunn 		err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8);
429dd43aa2SAndrew Lunn 		break;
439dd43aa2SAndrew Lunn 	default:
449dd43aa2SAndrew Lunn 		err = -EOPNOTSUPP;
459dd43aa2SAndrew Lunn 		break;
469dd43aa2SAndrew Lunn 	}
479dd43aa2SAndrew Lunn 
489dd43aa2SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
499dd43aa2SAndrew Lunn 
509dd43aa2SAndrew Lunn 	return err;
519dd43aa2SAndrew Lunn }
529dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_param_set(struct dsa_switch * ds,u32 id,struct devlink_param_gset_ctx * ctx)539dd43aa2SAndrew Lunn int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id,
549dd43aa2SAndrew Lunn 				struct devlink_param_gset_ctx *ctx)
559dd43aa2SAndrew Lunn {
569dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
579dd43aa2SAndrew Lunn 	int err;
589dd43aa2SAndrew Lunn 
599dd43aa2SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
609dd43aa2SAndrew Lunn 
619dd43aa2SAndrew Lunn 	switch (id) {
629dd43aa2SAndrew Lunn 	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
639dd43aa2SAndrew Lunn 		err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8);
649dd43aa2SAndrew Lunn 		break;
659dd43aa2SAndrew Lunn 	default:
669dd43aa2SAndrew Lunn 		err = -EOPNOTSUPP;
679dd43aa2SAndrew Lunn 		break;
689dd43aa2SAndrew Lunn 	}
699dd43aa2SAndrew Lunn 
709dd43aa2SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
719dd43aa2SAndrew Lunn 
729dd43aa2SAndrew Lunn 	return err;
739dd43aa2SAndrew Lunn }
749dd43aa2SAndrew Lunn 
759dd43aa2SAndrew Lunn static const struct devlink_param mv88e6xxx_devlink_params[] = {
769dd43aa2SAndrew Lunn 	DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
779dd43aa2SAndrew Lunn 				 "ATU_hash", DEVLINK_PARAM_TYPE_U8,
789dd43aa2SAndrew Lunn 				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
799dd43aa2SAndrew Lunn };
809dd43aa2SAndrew Lunn 
mv88e6xxx_setup_devlink_params(struct dsa_switch * ds)819dd43aa2SAndrew Lunn int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds)
829dd43aa2SAndrew Lunn {
839dd43aa2SAndrew Lunn 	return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params,
849dd43aa2SAndrew Lunn 					   ARRAY_SIZE(mv88e6xxx_devlink_params));
859dd43aa2SAndrew Lunn }
869dd43aa2SAndrew Lunn 
mv88e6xxx_teardown_devlink_params(struct dsa_switch * ds)879dd43aa2SAndrew Lunn void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds)
889dd43aa2SAndrew Lunn {
899dd43aa2SAndrew Lunn 	dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params,
909dd43aa2SAndrew Lunn 				      ARRAY_SIZE(mv88e6xxx_devlink_params));
919dd43aa2SAndrew Lunn }
929dd43aa2SAndrew Lunn 
939dd43aa2SAndrew Lunn enum mv88e6xxx_devlink_resource_id {
949dd43aa2SAndrew Lunn 	MV88E6XXX_RESOURCE_ID_ATU,
959dd43aa2SAndrew Lunn 	MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
969dd43aa2SAndrew Lunn 	MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
979dd43aa2SAndrew Lunn 	MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
989dd43aa2SAndrew Lunn 	MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
999dd43aa2SAndrew Lunn };
1009dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip * chip,u16 bin)1019dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip,
1029dd43aa2SAndrew Lunn 					 u16 bin)
1039dd43aa2SAndrew Lunn {
1049dd43aa2SAndrew Lunn 	u16 occupancy = 0;
1059dd43aa2SAndrew Lunn 	int err;
1069dd43aa2SAndrew Lunn 
1079dd43aa2SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
1089dd43aa2SAndrew Lunn 
1099dd43aa2SAndrew Lunn 	err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL,
1109dd43aa2SAndrew Lunn 					 bin);
1119dd43aa2SAndrew Lunn 	if (err) {
1129dd43aa2SAndrew Lunn 		dev_err(chip->dev, "failed to set ATU stats kind/bin\n");
1139dd43aa2SAndrew Lunn 		goto unlock;
1149dd43aa2SAndrew Lunn 	}
1159dd43aa2SAndrew Lunn 
1169dd43aa2SAndrew Lunn 	err = mv88e6xxx_g1_atu_get_next(chip, 0);
1179dd43aa2SAndrew Lunn 	if (err) {
1189dd43aa2SAndrew Lunn 		dev_err(chip->dev, "failed to perform ATU get next\n");
1199dd43aa2SAndrew Lunn 		goto unlock;
1209dd43aa2SAndrew Lunn 	}
1219dd43aa2SAndrew Lunn 
1229dd43aa2SAndrew Lunn 	err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy);
1239dd43aa2SAndrew Lunn 	if (err) {
1249dd43aa2SAndrew Lunn 		dev_err(chip->dev, "failed to get ATU stats\n");
1259dd43aa2SAndrew Lunn 		goto unlock;
1269dd43aa2SAndrew Lunn 	}
1279dd43aa2SAndrew Lunn 
1289dd43aa2SAndrew Lunn 	occupancy &= MV88E6XXX_G2_ATU_STATS_MASK;
1299dd43aa2SAndrew Lunn 
1309dd43aa2SAndrew Lunn unlock:
1319dd43aa2SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
1329dd43aa2SAndrew Lunn 
1339dd43aa2SAndrew Lunn 	return occupancy;
1349dd43aa2SAndrew Lunn }
1359dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_bin_0_get(void * priv)1369dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv)
1379dd43aa2SAndrew Lunn {
1389dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = priv;
1399dd43aa2SAndrew Lunn 
1409dd43aa2SAndrew Lunn 	return mv88e6xxx_devlink_atu_bin_get(chip,
1419dd43aa2SAndrew Lunn 					     MV88E6XXX_G2_ATU_STATS_BIN_0);
1429dd43aa2SAndrew Lunn }
1439dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_bin_1_get(void * priv)1449dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv)
1459dd43aa2SAndrew Lunn {
1469dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = priv;
1479dd43aa2SAndrew Lunn 
1489dd43aa2SAndrew Lunn 	return mv88e6xxx_devlink_atu_bin_get(chip,
1499dd43aa2SAndrew Lunn 					     MV88E6XXX_G2_ATU_STATS_BIN_1);
1509dd43aa2SAndrew Lunn }
1519dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_bin_2_get(void * priv)1529dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv)
1539dd43aa2SAndrew Lunn {
1549dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = priv;
1559dd43aa2SAndrew Lunn 
1569dd43aa2SAndrew Lunn 	return mv88e6xxx_devlink_atu_bin_get(chip,
1579dd43aa2SAndrew Lunn 					     MV88E6XXX_G2_ATU_STATS_BIN_2);
1589dd43aa2SAndrew Lunn }
1599dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_bin_3_get(void * priv)1609dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv)
1619dd43aa2SAndrew Lunn {
1629dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = priv;
1639dd43aa2SAndrew Lunn 
1649dd43aa2SAndrew Lunn 	return mv88e6xxx_devlink_atu_bin_get(chip,
1659dd43aa2SAndrew Lunn 					     MV88E6XXX_G2_ATU_STATS_BIN_3);
1669dd43aa2SAndrew Lunn }
1679dd43aa2SAndrew Lunn 
mv88e6xxx_devlink_atu_get(void * priv)1689dd43aa2SAndrew Lunn static u64 mv88e6xxx_devlink_atu_get(void *priv)
1699dd43aa2SAndrew Lunn {
1709dd43aa2SAndrew Lunn 	return mv88e6xxx_devlink_atu_bin_0_get(priv) +
1719dd43aa2SAndrew Lunn 		mv88e6xxx_devlink_atu_bin_1_get(priv) +
1729dd43aa2SAndrew Lunn 		mv88e6xxx_devlink_atu_bin_2_get(priv) +
1739dd43aa2SAndrew Lunn 		mv88e6xxx_devlink_atu_bin_3_get(priv);
1749dd43aa2SAndrew Lunn }
1759dd43aa2SAndrew Lunn 
mv88e6xxx_setup_devlink_resources(struct dsa_switch * ds)1769dd43aa2SAndrew Lunn int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds)
1779dd43aa2SAndrew Lunn {
1789dd43aa2SAndrew Lunn 	struct devlink_resource_size_params size_params;
1799dd43aa2SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
1809dd43aa2SAndrew Lunn 	int err;
1819dd43aa2SAndrew Lunn 
1829dd43aa2SAndrew Lunn 	devlink_resource_size_params_init(&size_params,
1839dd43aa2SAndrew Lunn 					  mv88e6xxx_num_macs(chip),
1849dd43aa2SAndrew Lunn 					  mv88e6xxx_num_macs(chip),
1859dd43aa2SAndrew Lunn 					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
1869dd43aa2SAndrew Lunn 
1879dd43aa2SAndrew Lunn 	err = dsa_devlink_resource_register(ds, "ATU",
1889dd43aa2SAndrew Lunn 					    mv88e6xxx_num_macs(chip),
1899dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU,
1909dd43aa2SAndrew Lunn 					    DEVLINK_RESOURCE_ID_PARENT_TOP,
1919dd43aa2SAndrew Lunn 					    &size_params);
1929dd43aa2SAndrew Lunn 	if (err)
1939dd43aa2SAndrew Lunn 		goto out;
1949dd43aa2SAndrew Lunn 
1959dd43aa2SAndrew Lunn 	devlink_resource_size_params_init(&size_params,
1969dd43aa2SAndrew Lunn 					  mv88e6xxx_num_macs(chip) / 4,
1979dd43aa2SAndrew Lunn 					  mv88e6xxx_num_macs(chip) / 4,
1989dd43aa2SAndrew Lunn 					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
1999dd43aa2SAndrew Lunn 
2009dd43aa2SAndrew Lunn 	err = dsa_devlink_resource_register(ds, "ATU_bin_0",
2019dd43aa2SAndrew Lunn 					    mv88e6xxx_num_macs(chip) / 4,
2029dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
2039dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU,
2049dd43aa2SAndrew Lunn 					    &size_params);
2059dd43aa2SAndrew Lunn 	if (err)
2069dd43aa2SAndrew Lunn 		goto out;
2079dd43aa2SAndrew Lunn 
2089dd43aa2SAndrew Lunn 	err = dsa_devlink_resource_register(ds, "ATU_bin_1",
2099dd43aa2SAndrew Lunn 					    mv88e6xxx_num_macs(chip) / 4,
2109dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
2119dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU,
2129dd43aa2SAndrew Lunn 					    &size_params);
2139dd43aa2SAndrew Lunn 	if (err)
2149dd43aa2SAndrew Lunn 		goto out;
2159dd43aa2SAndrew Lunn 
2169dd43aa2SAndrew Lunn 	err = dsa_devlink_resource_register(ds, "ATU_bin_2",
2179dd43aa2SAndrew Lunn 					    mv88e6xxx_num_macs(chip) / 4,
2189dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
2199dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU,
2209dd43aa2SAndrew Lunn 					    &size_params);
2219dd43aa2SAndrew Lunn 	if (err)
2229dd43aa2SAndrew Lunn 		goto out;
2239dd43aa2SAndrew Lunn 
2249dd43aa2SAndrew Lunn 	err = dsa_devlink_resource_register(ds, "ATU_bin_3",
2259dd43aa2SAndrew Lunn 					    mv88e6xxx_num_macs(chip) / 4,
2269dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
2279dd43aa2SAndrew Lunn 					    MV88E6XXX_RESOURCE_ID_ATU,
2289dd43aa2SAndrew Lunn 					    &size_params);
2299dd43aa2SAndrew Lunn 	if (err)
2309dd43aa2SAndrew Lunn 		goto out;
2319dd43aa2SAndrew Lunn 
2329dd43aa2SAndrew Lunn 	dsa_devlink_resource_occ_get_register(ds,
2339dd43aa2SAndrew Lunn 					      MV88E6XXX_RESOURCE_ID_ATU,
2349dd43aa2SAndrew Lunn 					      mv88e6xxx_devlink_atu_get,
2359dd43aa2SAndrew Lunn 					      chip);
2369dd43aa2SAndrew Lunn 
2379dd43aa2SAndrew Lunn 	dsa_devlink_resource_occ_get_register(ds,
2389dd43aa2SAndrew Lunn 					      MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
2399dd43aa2SAndrew Lunn 					      mv88e6xxx_devlink_atu_bin_0_get,
2409dd43aa2SAndrew Lunn 					      chip);
2419dd43aa2SAndrew Lunn 
2429dd43aa2SAndrew Lunn 	dsa_devlink_resource_occ_get_register(ds,
2439dd43aa2SAndrew Lunn 					      MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
2449dd43aa2SAndrew Lunn 					      mv88e6xxx_devlink_atu_bin_1_get,
2459dd43aa2SAndrew Lunn 					      chip);
2469dd43aa2SAndrew Lunn 
2479dd43aa2SAndrew Lunn 	dsa_devlink_resource_occ_get_register(ds,
2489dd43aa2SAndrew Lunn 					      MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
2499dd43aa2SAndrew Lunn 					      mv88e6xxx_devlink_atu_bin_2_get,
2509dd43aa2SAndrew Lunn 					      chip);
2519dd43aa2SAndrew Lunn 
2529dd43aa2SAndrew Lunn 	dsa_devlink_resource_occ_get_register(ds,
2539dd43aa2SAndrew Lunn 					      MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
2549dd43aa2SAndrew Lunn 					      mv88e6xxx_devlink_atu_bin_3_get,
2559dd43aa2SAndrew Lunn 					      chip);
2569dd43aa2SAndrew Lunn 
2579dd43aa2SAndrew Lunn 	return 0;
2589dd43aa2SAndrew Lunn 
2599dd43aa2SAndrew Lunn out:
2609dd43aa2SAndrew Lunn 	dsa_devlink_resources_unregister(ds);
2619dd43aa2SAndrew Lunn 	return err;
2629dd43aa2SAndrew Lunn }
263bfb25542SAndrew Lunn 
mv88e6xxx_region_global_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)264bfb25542SAndrew Lunn static int mv88e6xxx_region_global_snapshot(struct devlink *dl,
265bfb25542SAndrew Lunn 					    const struct devlink_region_ops *ops,
266bfb25542SAndrew Lunn 					    struct netlink_ext_ack *extack,
267bfb25542SAndrew Lunn 					    u8 **data)
268bfb25542SAndrew Lunn {
269bfb25542SAndrew Lunn 	struct mv88e6xxx_region_priv *region_priv = ops->priv;
270bfb25542SAndrew Lunn 	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
271bfb25542SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
272bfb25542SAndrew Lunn 	u16 *registers;
273bfb25542SAndrew Lunn 	int i, err;
274bfb25542SAndrew Lunn 
275bfb25542SAndrew Lunn 	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
276bfb25542SAndrew Lunn 	if (!registers)
277bfb25542SAndrew Lunn 		return -ENOMEM;
278bfb25542SAndrew Lunn 
279bfb25542SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
280bfb25542SAndrew Lunn 	for (i = 0; i < 32; i++) {
281bfb25542SAndrew Lunn 		switch (region_priv->id) {
282bfb25542SAndrew Lunn 		case MV88E6XXX_REGION_GLOBAL1:
283bfb25542SAndrew Lunn 			err = mv88e6xxx_g1_read(chip, i, &registers[i]);
284bfb25542SAndrew Lunn 			break;
285bfb25542SAndrew Lunn 		case MV88E6XXX_REGION_GLOBAL2:
286bfb25542SAndrew Lunn 			err = mv88e6xxx_g2_read(chip, i, &registers[i]);
287bfb25542SAndrew Lunn 			break;
288bfb25542SAndrew Lunn 		default:
289bfb25542SAndrew Lunn 			err = -EOPNOTSUPP;
290bfb25542SAndrew Lunn 		}
291bfb25542SAndrew Lunn 
292bfb25542SAndrew Lunn 		if (err) {
293bfb25542SAndrew Lunn 			kfree(registers);
294bfb25542SAndrew Lunn 			goto out;
295bfb25542SAndrew Lunn 		}
296bfb25542SAndrew Lunn 	}
297bfb25542SAndrew Lunn 	*data = (u8 *)registers;
298bfb25542SAndrew Lunn out:
299bfb25542SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
300bfb25542SAndrew Lunn 
301bfb25542SAndrew Lunn 	return err;
302bfb25542SAndrew Lunn }
303bfb25542SAndrew Lunn 
304bfb25542SAndrew Lunn /* The ATU entry varies between mv88e6xxx chipset generations. Define
305bfb25542SAndrew Lunn  * a generic format which covers all the current and hopefully future
306bfb25542SAndrew Lunn  * mv88e6xxx generations
307bfb25542SAndrew Lunn  */
308bfb25542SAndrew Lunn 
309bfb25542SAndrew Lunn struct mv88e6xxx_devlink_atu_entry {
310bfb25542SAndrew Lunn 	/* The FID is scattered over multiple registers. */
311bfb25542SAndrew Lunn 	u16 fid;
312bfb25542SAndrew Lunn 	u16 atu_op;
313bfb25542SAndrew Lunn 	u16 atu_data;
314bfb25542SAndrew Lunn 	u16 atu_01;
315bfb25542SAndrew Lunn 	u16 atu_23;
316bfb25542SAndrew Lunn 	u16 atu_45;
317bfb25542SAndrew Lunn };
318bfb25542SAndrew Lunn 
mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip * chip,int fid,struct mv88e6xxx_devlink_atu_entry * table,int * count)319bfb25542SAndrew Lunn static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip,
320bfb25542SAndrew Lunn 					     int fid,
321bfb25542SAndrew Lunn 					     struct mv88e6xxx_devlink_atu_entry *table,
322bfb25542SAndrew Lunn 					     int *count)
323bfb25542SAndrew Lunn {
324bfb25542SAndrew Lunn 	u16 atu_op, atu_data, atu_01, atu_23, atu_45;
325bfb25542SAndrew Lunn 	struct mv88e6xxx_atu_entry addr;
326bfb25542SAndrew Lunn 	int err;
327bfb25542SAndrew Lunn 
328bfb25542SAndrew Lunn 	addr.state = 0;
329bfb25542SAndrew Lunn 	eth_broadcast_addr(addr.mac);
330bfb25542SAndrew Lunn 
331bfb25542SAndrew Lunn 	do {
332bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
333bfb25542SAndrew Lunn 		if (err)
334bfb25542SAndrew Lunn 			return err;
335bfb25542SAndrew Lunn 
336bfb25542SAndrew Lunn 		if (!addr.state)
337bfb25542SAndrew Lunn 			break;
338bfb25542SAndrew Lunn 
339bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &atu_op);
340bfb25542SAndrew Lunn 		if (err)
341bfb25542SAndrew Lunn 			return err;
342bfb25542SAndrew Lunn 
343bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &atu_data);
344bfb25542SAndrew Lunn 		if (err)
345bfb25542SAndrew Lunn 			return err;
346bfb25542SAndrew Lunn 
347bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, &atu_01);
348bfb25542SAndrew Lunn 		if (err)
349bfb25542SAndrew Lunn 			return err;
350bfb25542SAndrew Lunn 
351bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, &atu_23);
352bfb25542SAndrew Lunn 		if (err)
353bfb25542SAndrew Lunn 			return err;
354bfb25542SAndrew Lunn 
355bfb25542SAndrew Lunn 		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, &atu_45);
356bfb25542SAndrew Lunn 		if (err)
357bfb25542SAndrew Lunn 			return err;
358bfb25542SAndrew Lunn 
359bfb25542SAndrew Lunn 		table[*count].fid = fid;
360bfb25542SAndrew Lunn 		table[*count].atu_op = atu_op;
361bfb25542SAndrew Lunn 		table[*count].atu_data = atu_data;
362bfb25542SAndrew Lunn 		table[*count].atu_01 = atu_01;
363bfb25542SAndrew Lunn 		table[*count].atu_23 = atu_23;
364bfb25542SAndrew Lunn 		table[*count].atu_45 = atu_45;
365bfb25542SAndrew Lunn 		(*count)++;
366bfb25542SAndrew Lunn 	} while (!is_broadcast_ether_addr(addr.mac));
367bfb25542SAndrew Lunn 
368bfb25542SAndrew Lunn 	return 0;
369bfb25542SAndrew Lunn }
370bfb25542SAndrew Lunn 
mv88e6xxx_region_atu_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)371bfb25542SAndrew Lunn static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
372bfb25542SAndrew Lunn 					 const struct devlink_region_ops *ops,
373bfb25542SAndrew Lunn 					 struct netlink_ext_ack *extack,
374bfb25542SAndrew Lunn 					 u8 **data)
375bfb25542SAndrew Lunn {
376bfb25542SAndrew Lunn 	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
377bfb25542SAndrew Lunn 	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
378bfb25542SAndrew Lunn 	struct mv88e6xxx_devlink_atu_entry *table;
379bfb25542SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
380bfb25542SAndrew Lunn 	int fid = -1, count, err;
381bfb25542SAndrew Lunn 
382bfb25542SAndrew Lunn 	table = kmalloc_array(mv88e6xxx_num_databases(chip),
383bfb25542SAndrew Lunn 			      sizeof(struct mv88e6xxx_devlink_atu_entry),
384bfb25542SAndrew Lunn 			      GFP_KERNEL);
385bfb25542SAndrew Lunn 	if (!table)
386bfb25542SAndrew Lunn 		return -ENOMEM;
387bfb25542SAndrew Lunn 
388bfb25542SAndrew Lunn 	memset(table, 0, mv88e6xxx_num_databases(chip) *
389bfb25542SAndrew Lunn 	       sizeof(struct mv88e6xxx_devlink_atu_entry));
390bfb25542SAndrew Lunn 
391bfb25542SAndrew Lunn 	count = 0;
392bfb25542SAndrew Lunn 
393bfb25542SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
394bfb25542SAndrew Lunn 
395bfb25542SAndrew Lunn 	err = mv88e6xxx_fid_map(chip, fid_bitmap);
3962bae900bSzhangxiaoxu 	if (err) {
3972bae900bSzhangxiaoxu 		kfree(table);
398bfb25542SAndrew Lunn 		goto out;
3992bae900bSzhangxiaoxu 	}
400bfb25542SAndrew Lunn 
401bfb25542SAndrew Lunn 	while (1) {
402bfb25542SAndrew Lunn 		fid = find_next_bit(fid_bitmap, MV88E6XXX_N_FID, fid + 1);
403bfb25542SAndrew Lunn 		if (fid == MV88E6XXX_N_FID)
404bfb25542SAndrew Lunn 			break;
405bfb25542SAndrew Lunn 
406bfb25542SAndrew Lunn 		err =  mv88e6xxx_region_atu_snapshot_fid(chip, fid, table,
407bfb25542SAndrew Lunn 							 &count);
408bfb25542SAndrew Lunn 		if (err) {
409bfb25542SAndrew Lunn 			kfree(table);
410bfb25542SAndrew Lunn 			goto out;
411bfb25542SAndrew Lunn 		}
412bfb25542SAndrew Lunn 	}
413bfb25542SAndrew Lunn 	*data = (u8 *)table;
414bfb25542SAndrew Lunn out:
415bfb25542SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
416bfb25542SAndrew Lunn 
417bfb25542SAndrew Lunn 	return err;
418bfb25542SAndrew Lunn }
419bfb25542SAndrew Lunn 
420ca4d632aSTobias Waldekranz /**
421ca4d632aSTobias Waldekranz  * struct mv88e6xxx_devlink_vtu_entry - Devlink VTU entry
422ca4d632aSTobias Waldekranz  * @fid:   Global1/2:   FID and VLAN policy.
423ca4d632aSTobias Waldekranz  * @sid:   Global1/3:   SID, unknown filters and learning.
424ca4d632aSTobias Waldekranz  * @op:    Global1/5:   FID (old chipsets).
425ca4d632aSTobias Waldekranz  * @vid:   Global1/6:   VID, valid, and page.
426ca4d632aSTobias Waldekranz  * @data:  Global1/7-9: Membership data and priority override.
427ca4d632aSTobias Waldekranz  * @resvd: Reserved. Also happens to align the size to 16B.
428ca4d632aSTobias Waldekranz  *
429ca4d632aSTobias Waldekranz  * The VTU entry format varies between chipset generations, the
430ca4d632aSTobias Waldekranz  * descriptions above represent the superset of all possible
431ca4d632aSTobias Waldekranz  * information, not all fields are valid on all devices. Since this is
432ca4d632aSTobias Waldekranz  * a low-level debug interface, copy all data verbatim and defer
433ca4d632aSTobias Waldekranz  * parsing to the consumer.
434ca4d632aSTobias Waldekranz  */
435ca4d632aSTobias Waldekranz struct mv88e6xxx_devlink_vtu_entry {
436ca4d632aSTobias Waldekranz 	u16 fid;
437ca4d632aSTobias Waldekranz 	u16 sid;
438ca4d632aSTobias Waldekranz 	u16 op;
439ca4d632aSTobias Waldekranz 	u16 vid;
440ca4d632aSTobias Waldekranz 	u16 data[3];
441ca4d632aSTobias Waldekranz 	u16 resvd;
442ca4d632aSTobias Waldekranz };
443ca4d632aSTobias Waldekranz 
mv88e6xxx_region_vtu_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)444ca4d632aSTobias Waldekranz static int mv88e6xxx_region_vtu_snapshot(struct devlink *dl,
445ca4d632aSTobias Waldekranz 					 const struct devlink_region_ops *ops,
446ca4d632aSTobias Waldekranz 					 struct netlink_ext_ack *extack,
447ca4d632aSTobias Waldekranz 					 u8 **data)
448ca4d632aSTobias Waldekranz {
449ca4d632aSTobias Waldekranz 	struct mv88e6xxx_devlink_vtu_entry *table, *entry;
450ca4d632aSTobias Waldekranz 	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
451ca4d632aSTobias Waldekranz 	struct mv88e6xxx_chip *chip = ds->priv;
452ca4d632aSTobias Waldekranz 	struct mv88e6xxx_vtu_entry vlan;
453ca4d632aSTobias Waldekranz 	int err;
454ca4d632aSTobias Waldekranz 
455e545f865STobias Waldekranz 	table = kcalloc(mv88e6xxx_max_vid(chip) + 1,
456ca4d632aSTobias Waldekranz 			sizeof(struct mv88e6xxx_devlink_vtu_entry),
457ca4d632aSTobias Waldekranz 			GFP_KERNEL);
458ca4d632aSTobias Waldekranz 	if (!table)
459ca4d632aSTobias Waldekranz 		return -ENOMEM;
460ca4d632aSTobias Waldekranz 
461ca4d632aSTobias Waldekranz 	entry = table;
462e545f865STobias Waldekranz 	vlan.vid = mv88e6xxx_max_vid(chip);
463ca4d632aSTobias Waldekranz 	vlan.valid = false;
464ca4d632aSTobias Waldekranz 
465ca4d632aSTobias Waldekranz 	mv88e6xxx_reg_lock(chip);
466ca4d632aSTobias Waldekranz 
467ca4d632aSTobias Waldekranz 	do {
468ca4d632aSTobias Waldekranz 		err = mv88e6xxx_g1_vtu_getnext(chip, &vlan);
469ca4d632aSTobias Waldekranz 		if (err)
470ca4d632aSTobias Waldekranz 			break;
471ca4d632aSTobias Waldekranz 
472ca4d632aSTobias Waldekranz 		if (!vlan.valid)
473ca4d632aSTobias Waldekranz 			break;
474ca4d632aSTobias Waldekranz 
475ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID,
476ca4d632aSTobias Waldekranz 						&entry->fid);
477ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
478ca4d632aSTobias Waldekranz 						&entry->sid);
479ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP,
480ca4d632aSTobias Waldekranz 						&entry->op);
481ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
482ca4d632aSTobias Waldekranz 						&entry->vid);
483ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
484ca4d632aSTobias Waldekranz 						&entry->data[0]);
485ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
486ca4d632aSTobias Waldekranz 						&entry->data[1]);
487ca4d632aSTobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
488ca4d632aSTobias Waldekranz 						&entry->data[2]);
489ca4d632aSTobias Waldekranz 		if (err)
490ca4d632aSTobias Waldekranz 			break;
491ca4d632aSTobias Waldekranz 
492ca4d632aSTobias Waldekranz 		entry++;
493e545f865STobias Waldekranz 	} while (vlan.vid < mv88e6xxx_max_vid(chip));
494ca4d632aSTobias Waldekranz 
495ca4d632aSTobias Waldekranz 	mv88e6xxx_reg_unlock(chip);
496ca4d632aSTobias Waldekranz 
497ca4d632aSTobias Waldekranz 	if (err) {
498ca4d632aSTobias Waldekranz 		kfree(table);
499ca4d632aSTobias Waldekranz 		return err;
500ca4d632aSTobias Waldekranz 	}
501ca4d632aSTobias Waldekranz 
502ca4d632aSTobias Waldekranz 	*data = (u8 *)table;
503ca4d632aSTobias Waldekranz 	return 0;
504ca4d632aSTobias Waldekranz }
505ca4d632aSTobias Waldekranz 
506*7dc96039STobias Waldekranz /**
507*7dc96039STobias Waldekranz  * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry
508*7dc96039STobias Waldekranz  * @sid:   Global1/3:   SID, unknown filters and learning.
509*7dc96039STobias Waldekranz  * @vid:   Global1/6:   Valid bit.
510*7dc96039STobias Waldekranz  * @data:  Global1/7-9: Membership data and priority override.
511*7dc96039STobias Waldekranz  * @resvd: Reserved. In case we forgot something.
512*7dc96039STobias Waldekranz  *
513*7dc96039STobias Waldekranz  * The STU entry format varies between chipset generations. Peridot
514*7dc96039STobias Waldekranz  * and Amethyst packs the STU data into Global1/7-8. Older silicon
515*7dc96039STobias Waldekranz  * spreads the information across all three VTU data registers -
516*7dc96039STobias Waldekranz  * inheriting the layout of even older hardware that had no STU at
517*7dc96039STobias Waldekranz  * all. Since this is a low-level debug interface, copy all data
518*7dc96039STobias Waldekranz  * verbatim and defer parsing to the consumer.
519*7dc96039STobias Waldekranz  */
520*7dc96039STobias Waldekranz struct mv88e6xxx_devlink_stu_entry {
521*7dc96039STobias Waldekranz 	u16 sid;
522*7dc96039STobias Waldekranz 	u16 vid;
523*7dc96039STobias Waldekranz 	u16 data[3];
524*7dc96039STobias Waldekranz 	u16 resvd;
525*7dc96039STobias Waldekranz };
526*7dc96039STobias Waldekranz 
mv88e6xxx_region_stu_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)527*7dc96039STobias Waldekranz static int mv88e6xxx_region_stu_snapshot(struct devlink *dl,
528*7dc96039STobias Waldekranz 					 const struct devlink_region_ops *ops,
529*7dc96039STobias Waldekranz 					 struct netlink_ext_ack *extack,
530*7dc96039STobias Waldekranz 					 u8 **data)
531*7dc96039STobias Waldekranz {
532*7dc96039STobias Waldekranz 	struct mv88e6xxx_devlink_stu_entry *table, *entry;
533*7dc96039STobias Waldekranz 	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
534*7dc96039STobias Waldekranz 	struct mv88e6xxx_chip *chip = ds->priv;
535*7dc96039STobias Waldekranz 	struct mv88e6xxx_stu_entry stu;
536*7dc96039STobias Waldekranz 	int err;
537*7dc96039STobias Waldekranz 
538*7dc96039STobias Waldekranz 	table = kcalloc(mv88e6xxx_max_sid(chip) + 1,
539*7dc96039STobias Waldekranz 			sizeof(struct mv88e6xxx_devlink_stu_entry),
540*7dc96039STobias Waldekranz 			GFP_KERNEL);
541*7dc96039STobias Waldekranz 	if (!table)
542*7dc96039STobias Waldekranz 		return -ENOMEM;
543*7dc96039STobias Waldekranz 
544*7dc96039STobias Waldekranz 	entry = table;
545*7dc96039STobias Waldekranz 	stu.sid = mv88e6xxx_max_sid(chip);
546*7dc96039STobias Waldekranz 	stu.valid = false;
547*7dc96039STobias Waldekranz 
548*7dc96039STobias Waldekranz 	mv88e6xxx_reg_lock(chip);
549*7dc96039STobias Waldekranz 
550*7dc96039STobias Waldekranz 	do {
551*7dc96039STobias Waldekranz 		err = mv88e6xxx_g1_stu_getnext(chip, &stu);
552*7dc96039STobias Waldekranz 		if (err)
553*7dc96039STobias Waldekranz 			break;
554*7dc96039STobias Waldekranz 
555*7dc96039STobias Waldekranz 		if (!stu.valid)
556*7dc96039STobias Waldekranz 			break;
557*7dc96039STobias Waldekranz 
558*7dc96039STobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
559*7dc96039STobias Waldekranz 						&entry->sid);
560*7dc96039STobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
561*7dc96039STobias Waldekranz 						&entry->vid);
562*7dc96039STobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
563*7dc96039STobias Waldekranz 						&entry->data[0]);
564*7dc96039STobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
565*7dc96039STobias Waldekranz 						&entry->data[1]);
566*7dc96039STobias Waldekranz 		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
567*7dc96039STobias Waldekranz 						&entry->data[2]);
568*7dc96039STobias Waldekranz 		if (err)
569*7dc96039STobias Waldekranz 			break;
570*7dc96039STobias Waldekranz 
571*7dc96039STobias Waldekranz 		entry++;
572*7dc96039STobias Waldekranz 	} while (stu.sid < mv88e6xxx_max_sid(chip));
573*7dc96039STobias Waldekranz 
574*7dc96039STobias Waldekranz 	mv88e6xxx_reg_unlock(chip);
575*7dc96039STobias Waldekranz 
576*7dc96039STobias Waldekranz 	if (err) {
577*7dc96039STobias Waldekranz 		kfree(table);
578*7dc96039STobias Waldekranz 		return err;
579*7dc96039STobias Waldekranz 	}
580*7dc96039STobias Waldekranz 
581*7dc96039STobias Waldekranz 	*data = (u8 *)table;
582*7dc96039STobias Waldekranz 	return 0;
583*7dc96039STobias Waldekranz }
584*7dc96039STobias Waldekranz 
mv88e6xxx_region_pvt_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)585836021a2STobias Waldekranz static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl,
586836021a2STobias Waldekranz 					 const struct devlink_region_ops *ops,
587836021a2STobias Waldekranz 					 struct netlink_ext_ack *extack,
588836021a2STobias Waldekranz 					 u8 **data)
589836021a2STobias Waldekranz {
590836021a2STobias Waldekranz 	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
591836021a2STobias Waldekranz 	struct mv88e6xxx_chip *chip = ds->priv;
592836021a2STobias Waldekranz 	int dev, port, err;
593836021a2STobias Waldekranz 	u16 *pvt, *cur;
594836021a2STobias Waldekranz 
595836021a2STobias Waldekranz 	pvt = kcalloc(MV88E6XXX_MAX_PVT_ENTRIES, sizeof(*pvt), GFP_KERNEL);
596836021a2STobias Waldekranz 	if (!pvt)
597836021a2STobias Waldekranz 		return -ENOMEM;
598836021a2STobias Waldekranz 
599836021a2STobias Waldekranz 	mv88e6xxx_reg_lock(chip);
600836021a2STobias Waldekranz 
601836021a2STobias Waldekranz 	cur = pvt;
602836021a2STobias Waldekranz 	for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; dev++) {
603836021a2STobias Waldekranz 		for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; port++) {
604836021a2STobias Waldekranz 			err = mv88e6xxx_g2_pvt_read(chip, dev, port, cur);
605836021a2STobias Waldekranz 			if (err)
606836021a2STobias Waldekranz 				break;
607836021a2STobias Waldekranz 
608836021a2STobias Waldekranz 			cur++;
609836021a2STobias Waldekranz 		}
610836021a2STobias Waldekranz 	}
611836021a2STobias Waldekranz 
612836021a2STobias Waldekranz 	mv88e6xxx_reg_unlock(chip);
613836021a2STobias Waldekranz 
614836021a2STobias Waldekranz 	if (err) {
615836021a2STobias Waldekranz 		kfree(pvt);
616836021a2STobias Waldekranz 		return err;
617836021a2STobias Waldekranz 	}
618836021a2STobias Waldekranz 
619836021a2STobias Waldekranz 	*data = (u8 *)pvt;
620836021a2STobias Waldekranz 	return 0;
621836021a2STobias Waldekranz }
622836021a2STobias Waldekranz 
mv88e6xxx_region_port_snapshot(struct devlink_port * devlink_port,const struct devlink_port_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)623b71a8d60SAndrew Lunn static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port,
624b71a8d60SAndrew Lunn 					  const struct devlink_port_region_ops *ops,
625b71a8d60SAndrew Lunn 					  struct netlink_ext_ack *extack,
626b71a8d60SAndrew Lunn 					  u8 **data)
627b71a8d60SAndrew Lunn {
628b71a8d60SAndrew Lunn 	struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port);
629b71a8d60SAndrew Lunn 	int port = dsa_devlink_port_to_port(devlink_port);
630b71a8d60SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
631b71a8d60SAndrew Lunn 	u16 *registers;
632b71a8d60SAndrew Lunn 	int i, err;
633b71a8d60SAndrew Lunn 
634b71a8d60SAndrew Lunn 	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
635b71a8d60SAndrew Lunn 	if (!registers)
636b71a8d60SAndrew Lunn 		return -ENOMEM;
637b71a8d60SAndrew Lunn 
638b71a8d60SAndrew Lunn 	mv88e6xxx_reg_lock(chip);
639b71a8d60SAndrew Lunn 	for (i = 0; i < 32; i++) {
640b71a8d60SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, i, &registers[i]);
641b71a8d60SAndrew Lunn 		if (err) {
642b71a8d60SAndrew Lunn 			kfree(registers);
643b71a8d60SAndrew Lunn 			goto out;
644b71a8d60SAndrew Lunn 		}
645b71a8d60SAndrew Lunn 	}
646b71a8d60SAndrew Lunn 	*data = (u8 *)registers;
647b71a8d60SAndrew Lunn out:
648b71a8d60SAndrew Lunn 	mv88e6xxx_reg_unlock(chip);
649b71a8d60SAndrew Lunn 
650b71a8d60SAndrew Lunn 	return err;
651b71a8d60SAndrew Lunn }
652b71a8d60SAndrew Lunn 
653bfb25542SAndrew Lunn static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = {
654bfb25542SAndrew Lunn 	.id = MV88E6XXX_REGION_GLOBAL1,
655bfb25542SAndrew Lunn };
656bfb25542SAndrew Lunn 
657bfb25542SAndrew Lunn static struct devlink_region_ops mv88e6xxx_region_global1_ops = {
658bfb25542SAndrew Lunn 	.name = "global1",
659bfb25542SAndrew Lunn 	.snapshot = mv88e6xxx_region_global_snapshot,
660bfb25542SAndrew Lunn 	.destructor = kfree,
661bfb25542SAndrew Lunn 	.priv = &mv88e6xxx_region_global1_priv,
662bfb25542SAndrew Lunn };
663bfb25542SAndrew Lunn 
664bfb25542SAndrew Lunn static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = {
665bfb25542SAndrew Lunn 	.id = MV88E6XXX_REGION_GLOBAL2,
666bfb25542SAndrew Lunn };
667bfb25542SAndrew Lunn 
668bfb25542SAndrew Lunn static struct devlink_region_ops mv88e6xxx_region_global2_ops = {
669bfb25542SAndrew Lunn 	.name = "global2",
670bfb25542SAndrew Lunn 	.snapshot = mv88e6xxx_region_global_snapshot,
671bfb25542SAndrew Lunn 	.destructor = kfree,
672bfb25542SAndrew Lunn 	.priv = &mv88e6xxx_region_global2_priv,
673bfb25542SAndrew Lunn };
674bfb25542SAndrew Lunn 
675bfb25542SAndrew Lunn static struct devlink_region_ops mv88e6xxx_region_atu_ops = {
676bfb25542SAndrew Lunn 	.name = "atu",
677bfb25542SAndrew Lunn 	.snapshot = mv88e6xxx_region_atu_snapshot,
678bfb25542SAndrew Lunn 	.destructor = kfree,
679bfb25542SAndrew Lunn };
680bfb25542SAndrew Lunn 
681ca4d632aSTobias Waldekranz static struct devlink_region_ops mv88e6xxx_region_vtu_ops = {
682ca4d632aSTobias Waldekranz 	.name = "vtu",
683ca4d632aSTobias Waldekranz 	.snapshot = mv88e6xxx_region_vtu_snapshot,
684ca4d632aSTobias Waldekranz 	.destructor = kfree,
685ca4d632aSTobias Waldekranz };
686ca4d632aSTobias Waldekranz 
687*7dc96039STobias Waldekranz static struct devlink_region_ops mv88e6xxx_region_stu_ops = {
688*7dc96039STobias Waldekranz 	.name = "stu",
689*7dc96039STobias Waldekranz 	.snapshot = mv88e6xxx_region_stu_snapshot,
690*7dc96039STobias Waldekranz 	.destructor = kfree,
691*7dc96039STobias Waldekranz };
692*7dc96039STobias Waldekranz 
693836021a2STobias Waldekranz static struct devlink_region_ops mv88e6xxx_region_pvt_ops = {
694836021a2STobias Waldekranz 	.name = "pvt",
695836021a2STobias Waldekranz 	.snapshot = mv88e6xxx_region_pvt_snapshot,
696836021a2STobias Waldekranz 	.destructor = kfree,
697836021a2STobias Waldekranz };
698836021a2STobias Waldekranz 
699b71a8d60SAndrew Lunn static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = {
700b71a8d60SAndrew Lunn 	.name = "port",
701b71a8d60SAndrew Lunn 	.snapshot = mv88e6xxx_region_port_snapshot,
702b71a8d60SAndrew Lunn 	.destructor = kfree,
703b71a8d60SAndrew Lunn };
704b71a8d60SAndrew Lunn 
705bfb25542SAndrew Lunn struct mv88e6xxx_region {
706bfb25542SAndrew Lunn 	struct devlink_region_ops *ops;
707bfb25542SAndrew Lunn 	u64 size;
708836021a2STobias Waldekranz 
709836021a2STobias Waldekranz 	bool (*cond)(struct mv88e6xxx_chip *chip);
710bfb25542SAndrew Lunn };
711bfb25542SAndrew Lunn 
712bfb25542SAndrew Lunn static struct mv88e6xxx_region mv88e6xxx_regions[] = {
713bfb25542SAndrew Lunn 	[MV88E6XXX_REGION_GLOBAL1] = {
714bfb25542SAndrew Lunn 		.ops = &mv88e6xxx_region_global1_ops,
715bfb25542SAndrew Lunn 		.size = 32 * sizeof(u16)
716bfb25542SAndrew Lunn 	},
717bfb25542SAndrew Lunn 	[MV88E6XXX_REGION_GLOBAL2] = {
718bfb25542SAndrew Lunn 		.ops = &mv88e6xxx_region_global2_ops,
719bfb25542SAndrew Lunn 		.size = 32 * sizeof(u16) },
720bfb25542SAndrew Lunn 	[MV88E6XXX_REGION_ATU] = {
721bfb25542SAndrew Lunn 		.ops = &mv88e6xxx_region_atu_ops
722bfb25542SAndrew Lunn 	  /* calculated at runtime */
723bfb25542SAndrew Lunn 	},
724ca4d632aSTobias Waldekranz 	[MV88E6XXX_REGION_VTU] = {
725ca4d632aSTobias Waldekranz 		.ops = &mv88e6xxx_region_vtu_ops
726ca4d632aSTobias Waldekranz 	  /* calculated at runtime */
727ca4d632aSTobias Waldekranz 	},
728*7dc96039STobias Waldekranz 	[MV88E6XXX_REGION_STU] = {
729*7dc96039STobias Waldekranz 		.ops = &mv88e6xxx_region_stu_ops,
730*7dc96039STobias Waldekranz 		.cond = mv88e6xxx_has_stu,
731*7dc96039STobias Waldekranz 	  /* calculated at runtime */
732*7dc96039STobias Waldekranz 	},
733836021a2STobias Waldekranz 	[MV88E6XXX_REGION_PVT] = {
734836021a2STobias Waldekranz 		.ops = &mv88e6xxx_region_pvt_ops,
735836021a2STobias Waldekranz 		.size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16),
736836021a2STobias Waldekranz 		.cond = mv88e6xxx_has_pvt,
737836021a2STobias Waldekranz 	},
738bfb25542SAndrew Lunn };
739bfb25542SAndrew Lunn 
mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch * ds)740fd292c18SVladimir Oltean void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)
741bfb25542SAndrew Lunn {
742fd292c18SVladimir Oltean 	struct mv88e6xxx_chip *chip = ds->priv;
743bfb25542SAndrew Lunn 	int i;
744bfb25542SAndrew Lunn 
745bfb25542SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++)
746bfb25542SAndrew Lunn 		dsa_devlink_region_destroy(chip->regions[i]);
747bfb25542SAndrew Lunn }
748bfb25542SAndrew Lunn 
mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch * ds,int port)749fd292c18SVladimir Oltean void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)
750bfb25542SAndrew Lunn {
751fd292c18SVladimir Oltean 	struct mv88e6xxx_chip *chip = ds->priv;
752fd292c18SVladimir Oltean 
753b71a8d60SAndrew Lunn 	dsa_devlink_region_destroy(chip->ports[port].region);
754b71a8d60SAndrew Lunn }
755bfb25542SAndrew Lunn 
mv88e6xxx_setup_devlink_regions_port(struct dsa_switch * ds,int port)756fd292c18SVladimir Oltean int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)
757b71a8d60SAndrew Lunn {
758fd292c18SVladimir Oltean 	struct mv88e6xxx_chip *chip = ds->priv;
759b71a8d60SAndrew Lunn 	struct devlink_region *region;
760b71a8d60SAndrew Lunn 
761b71a8d60SAndrew Lunn 	region = dsa_devlink_port_region_create(ds,
762b71a8d60SAndrew Lunn 						port,
763b71a8d60SAndrew Lunn 						&mv88e6xxx_region_port_ops, 1,
764b71a8d60SAndrew Lunn 						32 * sizeof(u16));
765b71a8d60SAndrew Lunn 	if (IS_ERR(region))
766b71a8d60SAndrew Lunn 		return PTR_ERR(region);
767b71a8d60SAndrew Lunn 
768b71a8d60SAndrew Lunn 	chip->ports[port].region = region;
769b71a8d60SAndrew Lunn 
770b71a8d60SAndrew Lunn 	return 0;
771b71a8d60SAndrew Lunn }
772b71a8d60SAndrew Lunn 
mv88e6xxx_setup_devlink_regions_global(struct dsa_switch * ds)773fd292c18SVladimir Oltean int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
774bfb25542SAndrew Lunn {
775836021a2STobias Waldekranz 	bool (*cond)(struct mv88e6xxx_chip *chip);
776fd292c18SVladimir Oltean 	struct mv88e6xxx_chip *chip = ds->priv;
777bfb25542SAndrew Lunn 	struct devlink_region_ops *ops;
778bfb25542SAndrew Lunn 	struct devlink_region *region;
779bfb25542SAndrew Lunn 	u64 size;
780bfb25542SAndrew Lunn 	int i, j;
781bfb25542SAndrew Lunn 
782bfb25542SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) {
783bfb25542SAndrew Lunn 		ops = mv88e6xxx_regions[i].ops;
784bfb25542SAndrew Lunn 		size = mv88e6xxx_regions[i].size;
785836021a2STobias Waldekranz 		cond = mv88e6xxx_regions[i].cond;
786836021a2STobias Waldekranz 
787836021a2STobias Waldekranz 		if (cond && !cond(chip))
788836021a2STobias Waldekranz 			continue;
789bfb25542SAndrew Lunn 
790ca4d632aSTobias Waldekranz 		switch (i) {
791ca4d632aSTobias Waldekranz 		case MV88E6XXX_REGION_ATU:
792bfb25542SAndrew Lunn 			size = mv88e6xxx_num_databases(chip) *
793bfb25542SAndrew Lunn 				sizeof(struct mv88e6xxx_devlink_atu_entry);
794ca4d632aSTobias Waldekranz 			break;
795ca4d632aSTobias Waldekranz 		case MV88E6XXX_REGION_VTU:
796281140a0STobias Waldekranz 			size = (mv88e6xxx_max_vid(chip) + 1) *
797ca4d632aSTobias Waldekranz 				sizeof(struct mv88e6xxx_devlink_vtu_entry);
798ca4d632aSTobias Waldekranz 			break;
799*7dc96039STobias Waldekranz 		case MV88E6XXX_REGION_STU:
800*7dc96039STobias Waldekranz 			size = (mv88e6xxx_max_sid(chip) + 1) *
801*7dc96039STobias Waldekranz 				sizeof(struct mv88e6xxx_devlink_stu_entry);
802*7dc96039STobias Waldekranz 			break;
803ca4d632aSTobias Waldekranz 		}
804bfb25542SAndrew Lunn 
805bfb25542SAndrew Lunn 		region = dsa_devlink_region_create(ds, ops, 1, size);
806bfb25542SAndrew Lunn 		if (IS_ERR(region))
807bfb25542SAndrew Lunn 			goto out;
808bfb25542SAndrew Lunn 		chip->regions[i] = region;
809bfb25542SAndrew Lunn 	}
810bfb25542SAndrew Lunn 	return 0;
811bfb25542SAndrew Lunn 
812bfb25542SAndrew Lunn out:
813bfb25542SAndrew Lunn 	for (j = 0; j < i; j++)
814bfb25542SAndrew Lunn 		dsa_devlink_region_destroy(chip->regions[j]);
815bfb25542SAndrew Lunn 
816bfb25542SAndrew Lunn 	return PTR_ERR(region);
817bfb25542SAndrew Lunn }
818bfb25542SAndrew Lunn 
mv88e6xxx_devlink_info_get(struct dsa_switch * ds,struct devlink_info_req * req,struct netlink_ext_ack * extack)81993157307SAndrew Lunn int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
82093157307SAndrew Lunn 			       struct devlink_info_req *req,
82193157307SAndrew Lunn 			       struct netlink_ext_ack *extack)
82293157307SAndrew Lunn {
82393157307SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
82493157307SAndrew Lunn 
82593157307SAndrew Lunn 	return devlink_info_version_fixed_put(req,
82693157307SAndrew Lunn 					      DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
82793157307SAndrew Lunn 					      chip->info->name);
82893157307SAndrew Lunn }
829