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, hash: &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, hash: 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, params: 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, params: 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, fid: 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, stats: &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: &size_params,
183 size_min: mv88e6xxx_num_macs(chip),
184 size_max: mv88e6xxx_num_macs(chip),
185 size_granularity: 1, unit: DEVLINK_RESOURCE_UNIT_ENTRY);
186
187 err = dsa_devlink_resource_register(ds, resource_name: "ATU",
188 resource_size: mv88e6xxx_num_macs(chip),
189 resource_id: MV88E6XXX_RESOURCE_ID_ATU,
190 DEVLINK_RESOURCE_ID_PARENT_TOP,
191 size_params: &size_params);
192 if (err)
193 goto out;
194
195 devlink_resource_size_params_init(size_params: &size_params,
196 size_min: mv88e6xxx_num_macs(chip) / 4,
197 size_max: mv88e6xxx_num_macs(chip) / 4,
198 size_granularity: 1, unit: DEVLINK_RESOURCE_UNIT_ENTRY);
199
200 err = dsa_devlink_resource_register(ds, resource_name: "ATU_bin_0",
201 resource_size: mv88e6xxx_num_macs(chip) / 4,
202 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
203 parent_resource_id: MV88E6XXX_RESOURCE_ID_ATU,
204 size_params: &size_params);
205 if (err)
206 goto out;
207
208 err = dsa_devlink_resource_register(ds, resource_name: "ATU_bin_1",
209 resource_size: mv88e6xxx_num_macs(chip) / 4,
210 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
211 parent_resource_id: MV88E6XXX_RESOURCE_ID_ATU,
212 size_params: &size_params);
213 if (err)
214 goto out;
215
216 err = dsa_devlink_resource_register(ds, resource_name: "ATU_bin_2",
217 resource_size: mv88e6xxx_num_macs(chip) / 4,
218 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
219 parent_resource_id: MV88E6XXX_RESOURCE_ID_ATU,
220 size_params: &size_params);
221 if (err)
222 goto out;
223
224 err = dsa_devlink_resource_register(ds, resource_name: "ATU_bin_3",
225 resource_size: mv88e6xxx_num_macs(chip) / 4,
226 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
227 parent_resource_id: MV88E6XXX_RESOURCE_ID_ATU,
228 size_params: &size_params);
229 if (err)
230 goto out;
231
232 dsa_devlink_resource_occ_get_register(ds,
233 resource_id: MV88E6XXX_RESOURCE_ID_ATU,
234 occ_get: mv88e6xxx_devlink_atu_get,
235 occ_get_priv: chip);
236
237 dsa_devlink_resource_occ_get_register(ds,
238 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_0,
239 occ_get: mv88e6xxx_devlink_atu_bin_0_get,
240 occ_get_priv: chip);
241
242 dsa_devlink_resource_occ_get_register(ds,
243 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_1,
244 occ_get: mv88e6xxx_devlink_atu_bin_1_get,
245 occ_get_priv: chip);
246
247 dsa_devlink_resource_occ_get_register(ds,
248 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_2,
249 occ_get: mv88e6xxx_devlink_atu_bin_2_get,
250 occ_get_priv: chip);
251
252 dsa_devlink_resource_occ_get_register(ds,
253 resource_id: MV88E6XXX_RESOURCE_ID_ATU_BIN_3,
254 occ_get: mv88e6xxx_devlink_atu_bin_3_get,
255 occ_get_priv: 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(n: 32, size: 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, reg: i, val: &registers[i]);
284 break;
285 case MV88E6XXX_REGION_GLOBAL2:
286 err = mv88e6xxx_g2_read(chip, reg: i, val: &registers[i]);
287 break;
288 default:
289 err = -EOPNOTSUPP;
290 }
291
292 if (err) {
293 kfree(objp: 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: addr.mac);
330
331 do {
332 err = mv88e6xxx_g1_atu_getnext(chip, fid, entry: &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, val: &atu_op);
340 if (err)
341 return err;
342
343 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_DATA, val: &atu_data);
344 if (err)
345 return err;
346
347 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC01, val: &atu_01);
348 if (err)
349 return err;
350
351 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC23, val: &atu_23);
352 if (err)
353 return err;
354
355 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_MAC45, val: &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: 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 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
378 struct mv88e6xxx_devlink_atu_entry *table;
379 struct mv88e6xxx_chip *chip = ds->priv;
380 int fid = -1, count, err;
381
382 table = kmalloc_array(n: mv88e6xxx_num_databases(chip),
383 size: sizeof(struct mv88e6xxx_devlink_atu_entry),
384 GFP_KERNEL);
385 if (!table)
386 return -ENOMEM;
387
388 memset(table, 0, mv88e6xxx_num_databases(chip) *
389 sizeof(struct mv88e6xxx_devlink_atu_entry));
390
391 count = 0;
392
393 mv88e6xxx_reg_lock(chip);
394
395 err = mv88e6xxx_fid_map(chip, bitmap: fid_bitmap);
396 if (err) {
397 kfree(objp: table);
398 goto out;
399 }
400
401 while (1) {
402 fid = find_next_bit(addr: fid_bitmap, MV88E6XXX_N_FID, offset: fid + 1);
403 if (fid == MV88E6XXX_N_FID)
404 break;
405
406 err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, table,
407 count: &count);
408 if (err) {
409 kfree(objp: table);
410 goto out;
411 }
412 }
413 *data = (u8 *)table;
414out:
415 mv88e6xxx_reg_unlock(chip);
416
417 return err;
418}
419
420/**
421 * struct mv88e6xxx_devlink_vtu_entry - Devlink VTU entry
422 * @fid: Global1/2: FID and VLAN policy.
423 * @sid: Global1/3: SID, unknown filters and learning.
424 * @op: Global1/5: FID (old chipsets).
425 * @vid: Global1/6: VID, valid, and page.
426 * @data: Global1/7-9: Membership data and priority override.
427 * @resvd: Reserved. Also happens to align the size to 16B.
428 *
429 * The VTU entry format varies between chipset generations, the
430 * descriptions above represent the superset of all possible
431 * information, not all fields are valid on all devices. Since this is
432 * a low-level debug interface, copy all data verbatim and defer
433 * parsing to the consumer.
434 */
435struct mv88e6xxx_devlink_vtu_entry {
436 u16 fid;
437 u16 sid;
438 u16 op;
439 u16 vid;
440 u16 data[3];
441 u16 resvd;
442};
443
444static int mv88e6xxx_region_vtu_snapshot(struct devlink *dl,
445 const struct devlink_region_ops *ops,
446 struct netlink_ext_ack *extack,
447 u8 **data)
448{
449 struct mv88e6xxx_devlink_vtu_entry *table, *entry;
450 struct dsa_switch *ds = dsa_devlink_to_ds(dl);
451 struct mv88e6xxx_chip *chip = ds->priv;
452 struct mv88e6xxx_vtu_entry vlan;
453 int err;
454
455 table = kcalloc(n: mv88e6xxx_max_vid(chip) + 1,
456 size: sizeof(struct mv88e6xxx_devlink_vtu_entry),
457 GFP_KERNEL);
458 if (!table)
459 return -ENOMEM;
460
461 entry = table;
462 vlan.vid = mv88e6xxx_max_vid(chip);
463 vlan.valid = false;
464
465 mv88e6xxx_reg_lock(chip);
466
467 do {
468 err = mv88e6xxx_g1_vtu_getnext(chip, entry: &vlan);
469 if (err)
470 break;
471
472 if (!vlan.valid)
473 break;
474
475 err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID,
476 val: &entry->fid);
477 err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
478 val: &entry->sid);
479 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP,
480 val: &entry->op);
481 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
482 val: &entry->vid);
483 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
484 val: &entry->data[0]);
485 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
486 val: &entry->data[1]);
487 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
488 val: &entry->data[2]);
489 if (err)
490 break;
491
492 entry++;
493 } while (vlan.vid < mv88e6xxx_max_vid(chip));
494
495 mv88e6xxx_reg_unlock(chip);
496
497 if (err) {
498 kfree(objp: table);
499 return err;
500 }
501
502 *data = (u8 *)table;
503 return 0;
504}
505
506/**
507 * struct mv88e6xxx_devlink_stu_entry - Devlink STU entry
508 * @sid: Global1/3: SID, unknown filters and learning.
509 * @vid: Global1/6: Valid bit.
510 * @data: Global1/7-9: Membership data and priority override.
511 * @resvd: Reserved. In case we forgot something.
512 *
513 * The STU entry format varies between chipset generations. Peridot
514 * and Amethyst packs the STU data into Global1/7-8. Older silicon
515 * spreads the information across all three VTU data registers -
516 * inheriting the layout of even older hardware that had no STU at
517 * all. Since this is a low-level debug interface, copy all data
518 * verbatim and defer parsing to the consumer.
519 */
520struct mv88e6xxx_devlink_stu_entry {
521 u16 sid;
522 u16 vid;
523 u16 data[3];
524 u16 resvd;
525};
526
527static int mv88e6xxx_region_stu_snapshot(struct devlink *dl,
528 const struct devlink_region_ops *ops,
529 struct netlink_ext_ack *extack,
530 u8 **data)
531{
532 struct mv88e6xxx_devlink_stu_entry *table, *entry;
533 struct dsa_switch *ds = dsa_devlink_to_ds(dl);
534 struct mv88e6xxx_chip *chip = ds->priv;
535 struct mv88e6xxx_stu_entry stu;
536 int err;
537
538 table = kcalloc(n: mv88e6xxx_max_sid(chip) + 1,
539 size: sizeof(struct mv88e6xxx_devlink_stu_entry),
540 GFP_KERNEL);
541 if (!table)
542 return -ENOMEM;
543
544 entry = table;
545 stu.sid = mv88e6xxx_max_sid(chip);
546 stu.valid = false;
547
548 mv88e6xxx_reg_lock(chip);
549
550 do {
551 err = mv88e6xxx_g1_stu_getnext(chip, entry: &stu);
552 if (err)
553 break;
554
555 if (!stu.valid)
556 break;
557
558 err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID,
559 val: &entry->sid);
560 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID,
561 val: &entry->vid);
562 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1,
563 val: &entry->data[0]);
564 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2,
565 val: &entry->data[1]);
566 err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3,
567 val: &entry->data[2]);
568 if (err)
569 break;
570
571 entry++;
572 } while (stu.sid < mv88e6xxx_max_sid(chip));
573
574 mv88e6xxx_reg_unlock(chip);
575
576 if (err) {
577 kfree(objp: table);
578 return err;
579 }
580
581 *data = (u8 *)table;
582 return 0;
583}
584
585static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl,
586 const struct devlink_region_ops *ops,
587 struct netlink_ext_ack *extack,
588 u8 **data)
589{
590 struct dsa_switch *ds = dsa_devlink_to_ds(dl);
591 struct mv88e6xxx_chip *chip = ds->priv;
592 int dev, port, err;
593 u16 *pvt, *cur;
594
595 pvt = kcalloc(MV88E6XXX_MAX_PVT_ENTRIES, size: sizeof(*pvt), GFP_KERNEL);
596 if (!pvt)
597 return -ENOMEM;
598
599 mv88e6xxx_reg_lock(chip);
600
601 cur = pvt;
602 for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; dev++) {
603 for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; port++) {
604 err = mv88e6xxx_g2_pvt_read(chip, src_dev: dev, src_port: port, data: cur);
605 if (err)
606 break;
607
608 cur++;
609 }
610 }
611
612 mv88e6xxx_reg_unlock(chip);
613
614 if (err) {
615 kfree(objp: pvt);
616 return err;
617 }
618
619 *data = (u8 *)pvt;
620 return 0;
621}
622
623static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port,
624 const struct devlink_port_region_ops *ops,
625 struct netlink_ext_ack *extack,
626 u8 **data)
627{
628 struct dsa_switch *ds = dsa_devlink_port_to_ds(port: devlink_port);
629 int port = dsa_devlink_port_to_port(port: devlink_port);
630 struct mv88e6xxx_chip *chip = ds->priv;
631 u16 *registers;
632 int i, err;
633
634 registers = kmalloc_array(n: 32, size: sizeof(u16), GFP_KERNEL);
635 if (!registers)
636 return -ENOMEM;
637
638 mv88e6xxx_reg_lock(chip);
639 for (i = 0; i < 32; i++) {
640 err = mv88e6xxx_port_read(chip, port, reg: i, val: &registers[i]);
641 if (err) {
642 kfree(objp: registers);
643 goto out;
644 }
645 }
646 *data = (u8 *)registers;
647out:
648 mv88e6xxx_reg_unlock(chip);
649
650 return err;
651}
652
653static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = {
654 .id = MV88E6XXX_REGION_GLOBAL1,
655};
656
657static struct devlink_region_ops mv88e6xxx_region_global1_ops = {
658 .name = "global1",
659 .snapshot = mv88e6xxx_region_global_snapshot,
660 .destructor = kfree,
661 .priv = &mv88e6xxx_region_global1_priv,
662};
663
664static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = {
665 .id = MV88E6XXX_REGION_GLOBAL2,
666};
667
668static struct devlink_region_ops mv88e6xxx_region_global2_ops = {
669 .name = "global2",
670 .snapshot = mv88e6xxx_region_global_snapshot,
671 .destructor = kfree,
672 .priv = &mv88e6xxx_region_global2_priv,
673};
674
675static struct devlink_region_ops mv88e6xxx_region_atu_ops = {
676 .name = "atu",
677 .snapshot = mv88e6xxx_region_atu_snapshot,
678 .destructor = kfree,
679};
680
681static struct devlink_region_ops mv88e6xxx_region_vtu_ops = {
682 .name = "vtu",
683 .snapshot = mv88e6xxx_region_vtu_snapshot,
684 .destructor = kfree,
685};
686
687static struct devlink_region_ops mv88e6xxx_region_stu_ops = {
688 .name = "stu",
689 .snapshot = mv88e6xxx_region_stu_snapshot,
690 .destructor = kfree,
691};
692
693static struct devlink_region_ops mv88e6xxx_region_pvt_ops = {
694 .name = "pvt",
695 .snapshot = mv88e6xxx_region_pvt_snapshot,
696 .destructor = kfree,
697};
698
699static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = {
700 .name = "port",
701 .snapshot = mv88e6xxx_region_port_snapshot,
702 .destructor = kfree,
703};
704
705struct mv88e6xxx_region {
706 struct devlink_region_ops *ops;
707 u64 size;
708
709 bool (*cond)(struct mv88e6xxx_chip *chip);
710};
711
712static struct mv88e6xxx_region mv88e6xxx_regions[] = {
713 [MV88E6XXX_REGION_GLOBAL1] = {
714 .ops = &mv88e6xxx_region_global1_ops,
715 .size = 32 * sizeof(u16)
716 },
717 [MV88E6XXX_REGION_GLOBAL2] = {
718 .ops = &mv88e6xxx_region_global2_ops,
719 .size = 32 * sizeof(u16) },
720 [MV88E6XXX_REGION_ATU] = {
721 .ops = &mv88e6xxx_region_atu_ops
722 /* calculated at runtime */
723 },
724 [MV88E6XXX_REGION_VTU] = {
725 .ops = &mv88e6xxx_region_vtu_ops
726 /* calculated at runtime */
727 },
728 [MV88E6XXX_REGION_STU] = {
729 .ops = &mv88e6xxx_region_stu_ops,
730 .cond = mv88e6xxx_has_stu,
731 /* calculated at runtime */
732 },
733 [MV88E6XXX_REGION_PVT] = {
734 .ops = &mv88e6xxx_region_pvt_ops,
735 .size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16),
736 .cond = mv88e6xxx_has_pvt,
737 },
738};
739
740void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds)
741{
742 struct mv88e6xxx_chip *chip = ds->priv;
743 int i;
744
745 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++)
746 dsa_devlink_region_destroy(region: chip->regions[i]);
747}
748
749void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port)
750{
751 struct mv88e6xxx_chip *chip = ds->priv;
752
753 dsa_devlink_region_destroy(region: chip->ports[port].region);
754}
755
756int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port)
757{
758 struct mv88e6xxx_chip *chip = ds->priv;
759 struct devlink_region *region;
760
761 region = dsa_devlink_port_region_create(ds,
762 port,
763 ops: &mv88e6xxx_region_port_ops, region_max_snapshots: 1,
764 region_size: 32 * sizeof(u16));
765 if (IS_ERR(ptr: region))
766 return PTR_ERR(ptr: region);
767
768 chip->ports[port].region = region;
769
770 return 0;
771}
772
773int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds)
774{
775 bool (*cond)(struct mv88e6xxx_chip *chip);
776 struct mv88e6xxx_chip *chip = ds->priv;
777 struct devlink_region_ops *ops;
778 struct devlink_region *region;
779 u64 size;
780 int i, j;
781
782 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) {
783 ops = mv88e6xxx_regions[i].ops;
784 size = mv88e6xxx_regions[i].size;
785 cond = mv88e6xxx_regions[i].cond;
786
787 if (cond && !cond(chip))
788 continue;
789
790 switch (i) {
791 case MV88E6XXX_REGION_ATU:
792 size = mv88e6xxx_num_databases(chip) *
793 sizeof(struct mv88e6xxx_devlink_atu_entry);
794 break;
795 case MV88E6XXX_REGION_VTU:
796 size = (mv88e6xxx_max_vid(chip) + 1) *
797 sizeof(struct mv88e6xxx_devlink_vtu_entry);
798 break;
799 case MV88E6XXX_REGION_STU:
800 size = (mv88e6xxx_max_sid(chip) + 1) *
801 sizeof(struct mv88e6xxx_devlink_stu_entry);
802 break;
803 }
804
805 region = dsa_devlink_region_create(ds, ops, region_max_snapshots: 1, region_size: size);
806 if (IS_ERR(ptr: region))
807 goto out;
808 chip->regions[i] = region;
809 }
810 return 0;
811
812out:
813 for (j = 0; j < i; j++)
814 dsa_devlink_region_destroy(region: chip->regions[j]);
815
816 return PTR_ERR(ptr: region);
817}
818
819int mv88e6xxx_devlink_info_get(struct dsa_switch *ds,
820 struct devlink_info_req *req,
821 struct netlink_ext_ack *extack)
822{
823 struct mv88e6xxx_chip *chip = ds->priv;
824
825 return devlink_info_version_fixed_put(req,
826 DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
827 version_value: chip->info->name);
828}
829

source code of linux/drivers/net/dsa/mv88e6xxx/devlink.c