File Index
Files and directories
Sign in
FILE

devlink.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/dsa/mv88e6xxx/devlink.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2#include <net/dsa.h>
  3
  4#include "chip.h"
  5#include "devlink.h"
  6#include "global1.h"
  7#include "global2.h"
  8#include "port.h"
  9
 10static int mv88e6xxx_atu_get_hash(struct mv88e6xxx_chip *chip, u8 *hash)
 11{
 12	if (chip->info->ops->atu_get_hash)
 13		return chip->info->ops->atu_get_hash(chip, hash);
 14
 15	return -EOPNOTSUPP;
 16}
 17
 18static int mv88e6xxx_atu_set_hash(struct mv88e6xxx_chip *chip, u8 hash)
 19{
 20	if (chip->info->ops->atu_set_hash)
 21		return chip->info->ops->atu_set_hash(chip, hash);
 22
 23	return -EOPNOTSUPP;
 24}
 25
 26enum mv88e6xxx_devlink_param_id {
 27	MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
 28	MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
 29};
 30
 31int mv88e6xxx_devlink_param_get(struct dsa_switch *ds, u32 id,
 32				struct devlink_param_gset_ctx *ctx)
 33{
 34	struct mv88e6xxx_chip *chip = ds->priv;
 35	int err;
 36
 37	mv88e6xxx_reg_lock(chip);
 38
 39	switch (id) {
 40	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
 41		err = mv88e6xxx_atu_get_hash(chip, &ctx->val.vu8);
 42		break;
 43	default:
 44		err = -EOPNOTSUPP;
 45		break;
 46	}
 47
 48	mv88e6xxx_reg_unlock(chip);
 49
 50	return err;
 51}
 52
 53int mv88e6xxx_devlink_param_set(struct dsa_switch *ds, u32 id,
 54				struct devlink_param_gset_ctx *ctx)
 55{
 56	struct mv88e6xxx_chip *chip = ds->priv;
 57	int err;
 58
 59	mv88e6xxx_reg_lock(chip);
 60
 61	switch (id) {
 62	case MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH:
 63		err = mv88e6xxx_atu_set_hash(chip, ctx->val.vu8);
 64		break;
 65	default:
 66		err = -EOPNOTSUPP;
 67		break;
 68	}
 69
 70	mv88e6xxx_reg_unlock(chip);
 71
 72	return err;
 73}
 74
 75static const struct devlink_param mv88e6xxx_devlink_params[] = {
 76	DSA_DEVLINK_PARAM_DRIVER(MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH,
 77				 "ATU_hash", DEVLINK_PARAM_TYPE_U8,
 78				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
 79};
 80
 81int mv88e6xxx_setup_devlink_params(struct dsa_switch *ds)
 82{
 83	return dsa_devlink_params_register(ds, mv88e6xxx_devlink_params,
 84					   ARRAY_SIZE(mv88e6xxx_devlink_params));
 85}
 86
 87void mv88e6xxx_teardown_devlink_params(struct dsa_switch *ds)
 88{
 89	dsa_devlink_params_unregister(ds, mv88e6xxx_devlink_params,
 90				      ARRAY_SIZE(mv88e6xxx_devlink_params));
 91}
 92
 93enum mv88e6xxx_devlink_resource_id {
 94	MV88E6XXX_RESOURCE_ID_ATU,
 95	MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
 96	MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
 97	MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
 98	MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
 99};
100
101static u64 mv88e6xxx_devlink_atu_bin_get(struct mv88e6xxx_chip *chip,
102					 u16 bin)
103{
104	u16 occupancy = 0;
105	int err;
106
107	mv88e6xxx_reg_lock(chip);
108
109	err = mv88e6xxx_g2_atu_stats_set(chip, MV88E6XXX_G2_ATU_STATS_MODE_ALL,
110					 bin);
111	if (err) {
112		dev_err(chip->dev, "failed to set ATU stats kind/bin\n");
113		goto unlock;
114	}
115
116	err = mv88e6xxx_g1_atu_get_next(chip, 0);
117	if (err) {
118		dev_err(chip->dev, "failed to perform ATU get next\n");
119		goto unlock;
120	}
121
122	err = mv88e6xxx_g2_atu_stats_get(chip, &occupancy);
123	if (err) {
124		dev_err(chip->dev, "failed to get ATU stats\n");
125		goto unlock;
126	}
127
128	occupancy &= MV88E6XXX_G2_ATU_STATS_MASK;
129
130unlock:
131	mv88e6xxx_reg_unlock(chip);
132
133	return occupancy;
134}
135
136static u64 mv88e6xxx_devlink_atu_bin_0_get(void *priv)
137{
138	struct mv88e6xxx_chip *chip = priv;
139
140	return mv88e6xxx_devlink_atu_bin_get(chip,
141					     MV88E6XXX_G2_ATU_STATS_BIN_0);
142}
143
144static u64 mv88e6xxx_devlink_atu_bin_1_get(void *priv)
145{
146	struct mv88e6xxx_chip *chip = priv;
147
148	return mv88e6xxx_devlink_atu_bin_get(chip,
149					     MV88E6XXX_G2_ATU_STATS_BIN_1);
150}
151
152static u64 mv88e6xxx_devlink_atu_bin_2_get(void *priv)
153{
154	struct mv88e6xxx_chip *chip = priv;
155
156	return mv88e6xxx_devlink_atu_bin_get(chip,
157					     MV88E6XXX_G2_ATU_STATS_BIN_2);
158}
159
160static u64 mv88e6xxx_devlink_atu_bin_3_get(void *priv)
161{
162	struct mv88e6xxx_chip *chip = priv;
163
164	return mv88e6xxx_devlink_atu_bin_get(chip,
165					     MV88E6XXX_G2_ATU_STATS_BIN_3);
166}
167
168static u64 mv88e6xxx_devlink_atu_get(void *priv)
169{
170	return mv88e6xxx_devlink_atu_bin_0_get(priv) +
171		mv88e6xxx_devlink_atu_bin_1_get(priv) +
172		mv88e6xxx_devlink_atu_bin_2_get(priv) +
173		mv88e6xxx_devlink_atu_bin_3_get(priv);
174}
175
176int mv88e6xxx_setup_devlink_resources(struct dsa_switch *ds)
177{
178	struct devlink_resource_size_params size_params;
179	struct mv88e6xxx_chip *chip = ds->priv;
180	int err;
181
182	devlink_resource_size_params_init(&size_params,
183					  mv88e6xxx_num_macs(chip),
184					  mv88e6xxx_num_macs(chip),
185					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
186
187	err = dsa_devlink_resource_register(ds, "ATU",
188					    mv88e6xxx_num_macs(chip),
189					    MV88E6XXX_RESOURCE_ID_ATU,
190					    DEVLINK_RESOURCE_ID_PARENT_TOP,
191					    &size_params);
192	if (err)
193		goto out;
194
195	devlink_resource_size_params_init(&size_params,
196					  mv88e6xxx_num_macs(chip) / 4,
197					  mv88e6xxx_num_macs(chip) / 4,
198					  1, DEVLINK_RESOURCE_UNIT_ENTRY);
199
200	err = dsa_devlink_resource_register(ds, "ATU_bin_0",
201					    mv88e6xxx_num_macs(chip) / 4,
202					    MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
203					    MV88E6XXX_RESOURCE_ID_ATU,
204					    &size_params);
205	if (err)
206		goto out;
207
208	err = dsa_devlink_resource_register(ds, "ATU_bin_1",
209					    mv88e6xxx_num_macs(chip) / 4,
210					    MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
211					    MV88E6XXX_RESOURCE_ID_ATU,
212					    &size_params);
213	if (err)
214		goto out;
215
216	err = dsa_devlink_resource_register(ds, "ATU_bin_2",
217					    mv88e6xxx_num_macs(chip) / 4,
218					    MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
219					    MV88E6XXX_RESOURCE_ID_ATU,
220					    &size_params);
221	if (err)
222		goto out;
223
224	err = dsa_devlink_resource_register(ds, "ATU_bin_3",
225					    mv88e6xxx_num_macs(chip) / 4,
226					    MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
227					    MV88E6XXX_RESOURCE_ID_ATU,
228					    &size_params);
229	if (err)
230		goto out;
231
232	dsa_devlink_resource_occ_get_register(ds,
233					      MV88E6XXX_RESOURCE_ID_ATU,
234					      mv88e6xxx_devlink_atu_get,
235					      chip);
236
237	dsa_devlink_resource_occ_get_register(ds,
238					      MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
239					      mv88e6xxx_devlink_atu_bin_0_get,
240					      chip);
241
242	dsa_devlink_resource_occ_get_register(ds,
243					      MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
244					      mv88e6xxx_devlink_atu_bin_1_get,
245					      chip);
246
247	dsa_devlink_resource_occ_get_register(ds,
248					      MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
249					      mv88e6xxx_devlink_atu_bin_2_get,
250					      chip);
251
252	dsa_devlink_resource_occ_get_register(ds,
253					      MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
254					      mv88e6xxx_devlink_atu_bin_3_get,
255					      chip);
256
257	return 0;
258
259out:
260	dsa_devlink_resources_unregister(ds);
261	return err;
262}
263
264static int mv88e6xxx_region_global_snapshot(struct devlink *dl,
265					    const struct devlink_region_ops *ops,
266					    struct netlink_ext_ack *extack,
267					    u8 **data)
268{
269	struct mv88e6xxx_region_priv *region_priv = ops->priv;
270	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
271	struct mv88e6xxx_chip *chip = ds->priv;
272	u16 *registers;
273	int i, err;
274
275	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
276	if (!registers)
277		return -ENOMEM;
278
279	mv88e6xxx_reg_lock(chip);
280	for (i = 0; i < 32; i++) {
281		switch (region_priv->id) {
282		case MV88E6XXX_REGION_GLOBAL1:
283			err = mv88e6xxx_g1_read(chip, i, &registers[i]);
284			break;
285		case MV88E6XXX_REGION_GLOBAL2:
286			err = mv88e6xxx_g2_read(chip, i, &registers[i]);
287			break;
288		default:
289			err = -EOPNOTSUPP;
290		}
291
292		if (err) {
293			kfree(registers);
294			goto out;
295		}
296	}
297	*data = (u8 *)registers;
298out:
299	mv88e6xxx_reg_unlock(chip);
300
301	return err;
302}
303
304/* The ATU entry varies between mv88e6xxx chipset generations. Define
305 * a generic format which covers all the current and hopefully future
306 * mv88e6xxx generations
307 */
308
309struct mv88e6xxx_devlink_atu_entry {
310	/* The FID is scattered over multiple registers. */
311	u16 fid;
312	u16 atu_op;
313	u16 atu_data;
314	u16 atu_01;
315	u16 atu_23;
316	u16 atu_45;
317};
318
319static int mv88e6xxx_region_atu_snapshot_fid(struct mv88e6xxx_chip *chip,
320					     int fid,
321					     struct mv88e6xxx_devlink_atu_entry *table,
322					     int *count)
323{
324	u16 atu_op, atu_data, atu_01, atu_23, atu_45;
325	struct mv88e6xxx_atu_entry addr;
326	int err;
327
328	addr.state = 0;
329	eth_broadcast_addr(addr.mac);
330
331	do {
332		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
333		if (err)
334			return err;
335
336		if (!addr.state)
337			break;
338
339		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &atu_op);
340		if (err)
341			return err;
342
343		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, &atu_data);
344		if (err)
345			return err;
346
347		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, &atu_01);
348		if (err)
349			return err;
350
351		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, &atu_23);
352		if (err)
353			return err;
354
355		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, &atu_45);
356		if (err)
357			return err;
358
359		table[*count].fid = fid;
360		table[*count].atu_op = atu_op;
361		table[*count].atu_data = atu_data;
362		table[*count].atu_01 = atu_01;
363		table[*count].atu_23 = atu_23;
364		table[*count].atu_45 = atu_45;
365		(*count)++;
366	} while (!is_broadcast_ether_addr(addr.mac));
367
368	return 0;
369}
370
371static int mv88e6xxx_region_atu_snapshot(struct devlink *dl,
372					 const struct devlink_region_ops *ops,
373					 struct netlink_ext_ack *extack,
374					 u8 **data)
375{
376	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
377	struct mv88e6xxx_devlink_atu_entry *table;
378	struct mv88e6xxx_chip *chip = ds->priv;
379	int fid = -1, err = 0, count = 0;
380
381	table = kcalloc(mv88e6xxx_num_databases(chip),
382			sizeof(struct mv88e6xxx_devlink_atu_entry),
383			GFP_KERNEL);
384	if (!table)
385		return -ENOMEM;
386
387	mv88e6xxx_reg_lock(chip);
388
389	while (1) {
390		fid = find_next_bit(chip->fid_bitmap, MV88E6XXX_N_FID, fid + 1);
391		if (fid == MV88E6XXX_N_FID)
392			break;
393
394		err =  mv88e6xxx_region_atu_snapshot_fid(chip, fid, table,
395							 &count);
396		if (err) {
397			kfree(table);
398			goto out;
399		}
400	}
401	*data = (u8 *)table;
402out:
403	mv88e6xxx_reg_unlock(chip);
404
405	return err;
406}
407
408/**
409 * struct mv88e6xxx_devlink_vtu_entry - Devlink VTU entry
410 * @fid:   Global1/2:   FID and VLAN policy.
411 * @sid:   Global1/3:   SID, unknown filters and learning.
412 * @op:    Global1/5:   FID (old chipsets).
413 * @vid:   Global1/6:   VID, valid, and page.
414 * @data:  Global1/7-9: Membership data and priority override.
415 * @resvd: Reserved. Also happens to align the size to 16B.
416 *
417 * The VTU entry format varies between chipset generations, the
418 * descriptions above represent the superset of all possible
419 * information, not all fields are valid on all devices. Since this is
420 * a low-level debug interface, copy all data verbatim and defer
421 * parsing to the consumer.
422 */
423struct mv88e6xxx_devlink_vtu_entry {
424	u16 fid;
425	u16 sid;
426	u16 op;
427	u16 vid;
428	u16 data[3];
429	u16 resvd;
430};
431
432static int mv88e6xxx_region_vtu_snapshot(struct devlink *dl,
433					 const struct devlink_region_ops *ops,
434					 struct netlink_ext_ack *extack,
435					 u8 **data)
436{
437	struct mv88e6xxx_devlink_vtu_entry *table, *entry;
438	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
439	struct mv88e6xxx_chip *chip = ds->priv;
440	struct mv88e6xxx_vtu_entry vlan;
441	int err;
442
443	table = kcalloc(mv88e6xxx_max_vid(chip) + 1,
444			sizeof(struct mv88e6xxx_devlink_vtu_entry),
445			GFP_KERNEL);
446	if (!table)
447		return -ENOMEM;
448
449	entry = table;
450	vlan.vid = mv88e6xxx_max_vid(chip);
451	vlan.valid = false;
452
453	mv88e6xxx_reg_lock(chip);
454
455	do {
456		err = mv88e6xxx_g1_vtu_getnext(chip, &vlan);
457		if (err)
458			break;
459
460		if (!vlan.valid)
461			break;
462
463		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID,
464						&entry->fid);
465		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
466						&entry->sid);
467		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP,
468						&entry->op);
469		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
470						&entry->vid);
471		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
472						&entry->data[0]);
473		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
474						&entry->data[1]);
475		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
476						&entry->data[2]);
477		if (err)
478			break;
479
480		entry++;
481	} while (vlan.vid < mv88e6xxx_max_vid(chip));
482
483	mv88e6xxx_reg_unlock(chip);
484
485	if (err) {
486		kfree(table);
487		return err;
488	}
489
490	*data = (u8 *)table;
491	return 0;
492}
493
494/**
495 * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry
496 * @sid:   Global1/3:   SID, unknown filters and learning.
497 * @vid:   Global1/6:   Valid bit.
498 * @data:  Global1/7-9: Membership data and priority override.
499 * @resvd: Reserved. In case we forgot something.
500 *
501 * The STU entry format varies between chipset generations. Peridot
502 * and Amethyst packs the STU data into Global1/7-8. Older silicon
503 * spreads the information across all three VTU data registers -
504 * inheriting the layout of even older hardware that had no STU at
505 * all. Since this is a low-level debug interface, copy all data
506 * verbatim and defer parsing to the consumer.
507 */
508struct mv88e6xxx_devlink_stu_entry {
509	u16 sid;
510	u16 vid;
511	u16 data[3];
512	u16 resvd;
513};
514
515static int mv88e6xxx_region_stu_snapshot(struct devlink *dl,
516					 const struct devlink_region_ops *ops,
517					 struct netlink_ext_ack *extack,
518					 u8 **data)
519{
520	struct mv88e6xxx_devlink_stu_entry *table, *entry;
521	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
522	struct mv88e6xxx_chip *chip = ds->priv;
523	struct mv88e6xxx_stu_entry stu;
524	int err;
525
526	table = kcalloc(mv88e6xxx_max_sid(chip) + 1,
527			sizeof(struct mv88e6xxx_devlink_stu_entry),
528			GFP_KERNEL);
529	if (!table)
530		return -ENOMEM;
531
532	entry = table;
533	stu.sid = mv88e6xxx_max_sid(chip);
534	stu.valid = false;
535
536	mv88e6xxx_reg_lock(chip);
537
538	do {
539		err = mv88e6xxx_g1_stu_getnext(chip, &stu);
540		if (err)
541			break;
542
543		if (!stu.valid)
544			break;
545
546		err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
547						&entry->sid);
548		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
549						&entry->vid);
550		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
551						&entry->data[0]);
552		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
553						&entry->data[1]);
554		err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
555						&entry->data[2]);
556		if (err)
557			break;
558
559		entry++;
560	} while (stu.sid < mv88e6xxx_max_sid(chip));
561
562	mv88e6xxx_reg_unlock(chip);
563
564	if (err) {
565		kfree(table);
566		return err;
567	}
568
569	*data = (u8 *)table;
570	return 0;
571}
572
573static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl,
574					 const struct devlink_region_ops *ops,
575					 struct netlink_ext_ack *extack,
576					 u8 **data)
577{
578	struct dsa_switch *ds = dsa_devlink_to_ds(dl);
579	struct mv88e6xxx_chip *chip = ds->priv;
580	int dev, port, err;
581	u16 *pvt, *cur;
582
583	pvt = kcalloc(MV88E6XXX_MAX_PVT_ENTRIES, sizeof(*pvt), GFP_KERNEL);
584	if (!pvt)
585		return -ENOMEM;
586
587	mv88e6xxx_reg_lock(chip);
588
589	cur = pvt;
590	for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; dev++) {
591		for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; port++) {
592			err = mv88e6xxx_g2_pvt_read(chip, dev, port, cur);
593			if (err)
594				break;
595
596			cur++;
597		}
598	}
599
600	mv88e6xxx_reg_unlock(chip);
601
602	if (err) {
603		kfree(pvt);
604		return err;
605	}
606
607	*data = (u8 *)pvt;
608	return 0;
609}
610
611static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port,
612					  const struct devlink_port_region_ops *ops,
613					  struct netlink_ext_ack *extack,
614					  u8 **data)
615{
616	struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port);
617	int port = dsa_devlink_port_to_port(devlink_port);
618	struct mv88e6xxx_chip *chip = ds->priv;
619	u16 *registers;
620	int i, err;
621
622	registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL);
623	if (!registers)
624		return -ENOMEM;
625
626	mv88e6xxx_reg_lock(chip);
627	for (i = 0; i < 32; i++) {
628		err = mv88e6xxx_port_read(chip, port, i, &registers[i]);
629		if (err) {
630			kfree(registers);
631			goto out;
632		}
633	}
634	*data = (u8 *)registers;
635out:
636	mv88e6xxx_reg_unlock(chip);
637
638	return err;
639}
640
641static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = {
642	.id = MV88E6XXX_REGION_GLOBAL1,
643};
644
645static const struct devlink_region_ops mv88e6xxx_region_global1_ops = {
646	.name = "global1",
647	.snapshot = mv88e6xxx_region_global_snapshot,
648	.destructor = kfree,
649	.priv = &mv88e6xxx_region_global1_priv,
650};
651
652static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = {
653	.id = MV88E6XXX_REGION_GLOBAL2,
654};
655
656static const struct devlink_region_ops mv88e6xxx_region_global2_ops = {
657	.name = "global2",
658	.snapshot = mv88e6xxx_region_global_snapshot,
659	.destructor = kfree,
660	.priv = &mv88e6xxx_region_global2_priv,
661};
662
663static const struct devlink_region_ops mv88e6xxx_region_atu_ops = {
664	.name = "atu",
665	.snapshot = mv88e6xxx_region_atu_snapshot,
666	.destructor = kfree,
667};
668
669static const struct devlink_region_ops mv88e6xxx_region_vtu_ops = {
670	.name = "vtu",
671	.snapshot = mv88e6xxx_region_vtu_snapshot,
672	.destructor = kfree,
673};
674
675static const struct devlink_region_ops mv88e6xxx_region_stu_ops = {
676	.name = "stu",
677	.snapshot = mv88e6xxx_region_stu_snapshot,
678	.destructor = kfree,
679};
680
681static const struct devlink_region_ops mv88e6xxx_region_pvt_ops = {
682	.name = "pvt",
683	.snapshot = mv88e6xxx_region_pvt_snapshot,
684	.destructor = kfree,
685};
686
687static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = {
688	.name = "port",
689	.snapshot = mv88e6xxx_region_port_snapshot,
690	.destructor = kfree,
691};
692
693struct mv88e6xxx_region {
694	const struct devlink_region_ops *ops;
695	u64 size;
696
697	bool (*cond)(struct mv88e6xxx_chip *chip);
698};
699
700static const struct mv88e6xxx_region mv88e6xxx_regions[] = {
701	[MV88E6XXX_REGION_GLOBAL1] = {
702		.ops = &mv88e6xxx_region_global1_ops,
703		.size = 32 * sizeof(u16)
704	},
705	[MV88E6XXX_REGION_GLOBAL2] = {
706		.ops = &mv88e6xxx_region_global2_ops,
707		.size = 32 * sizeof(u16) },
708	[MV88E6XXX_REGION_ATU] = {
709		.ops = &mv88e6xxx_region_atu_ops
710	  /* calculated at runtime */
711	},
712	[MV88E6XXX_REGION_VTU] = {
713		.ops = &mv88e6xxx_region_vtu_ops
714	  /* calculated at runtime */
715	},
716	[MV88E6XXX_REGION_STU] = {
717		.ops = &mv88e6xxx_region_stu_ops,
718		.cond = mv88e6xxx_has_stu,
719	  /* calculated at runtime */
720	},
721	[MV88E6XXX_REGION_PVT] = {
722		.ops = &mv88e6xxx_region_pvt_ops,
723		.size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16),
724		.cond = mv88e6xxx_has_pvt,
725	},
726};
727
728void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)
729{
730	struct mv88e6xxx_chip *chip = ds->priv;
731	int i;
732
733	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++)
734		if (chip->regions[i])
735			dsa_devlink_region_destroy(chip->regions[i]);
736}
737
738void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)
739{
740	struct mv88e6xxx_chip *chip = ds->priv;
741
742	dsa_devlink_region_destroy(chip->ports[port].region);
743}
744
745int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)
746{
747	struct mv88e6xxx_chip *chip = ds->priv;
748	struct devlink_region *region;
749
750	region = dsa_devlink_port_region_create(ds,
751						port,
752						&mv88e6xxx_region_port_ops, 1,
753						32 * sizeof(u16));
754	if (IS_ERR(region))
755		return PTR_ERR(region);
756
757	chip->ports[port].region = region;
758
759	return 0;
760}
761
762int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
763{
764	bool (*cond)(struct mv88e6xxx_chip *chip);
765	struct mv88e6xxx_chip *chip = ds->priv;
766	const struct devlink_region_ops *ops;
767	struct devlink_region *region;
768	u64 size;
769	int i, j;
770
771	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) {
772		ops = mv88e6xxx_regions[i].ops;
773		size = mv88e6xxx_regions[i].size;
774		cond = mv88e6xxx_regions[i].cond;
775
776		if (cond && !cond(chip))
777			continue;
778
779		switch (i) {
780		case MV88E6XXX_REGION_ATU:
781			size = mv88e6xxx_num_databases(chip) *
782				sizeof(struct mv88e6xxx_devlink_atu_entry);
783			break;
784		case MV88E6XXX_REGION_VTU:
785			size = (mv88e6xxx_max_vid(chip) + 1) *
786				sizeof(struct mv88e6xxx_devlink_vtu_entry);
787			break;
788		case MV88E6XXX_REGION_STU:
789			size = (mv88e6xxx_max_sid(chip) + 1) *
790				sizeof(struct mv88e6xxx_devlink_stu_entry);
791			break;
792		}
793
794		region = dsa_devlink_region_create(ds, ops, 1, size);
795		if (IS_ERR(region))
796			goto out;
797		chip->regions[i] = region;
798	}
799	return 0;
800
801out:
802	for (j = 0; j < i; j++)
803		dsa_devlink_region_destroy(chip->regions[j]);
804
805	return PTR_ERR(region);
806}
807
808int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
809			       struct devlink_info_req *req,
810			       struct netlink_ext_ack *extack)
811{
812	struct mv88e6xxx_chip *chip = ds->priv;
813
814	return devlink_info_version_fixed_put(req,
815					      DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
816					      chip->info->name);
817}
~/ Read-only file preview.