Andrew Lunn | 9dd43aa | 2020-09-18 21:11:05 +0200 | [diff] [blame] | 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" |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 8 | #include "port.h" |
Andrew Lunn | 9dd43aa | 2020-09-18 21:11:05 +0200 | [diff] [blame] | 9 | |
| 10 | static 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 | |
| 18 | static 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 | |
| 26 | enum mv88e6xxx_devlink_param_id { |
| 27 | MV88E6XXX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, |
| 28 | MV88E6XXX_DEVLINK_PARAM_ID_ATU_HASH, |
| 29 | }; |
| 30 | |
| 31 | int 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 | |
| 53 | int 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 | |
| 75 | static 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 | |
| 81 | int 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 | |
| 87 | void 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 | |
| 93 | enum 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 | |
| 101 | static 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 | |
| 130 | unlock: |
| 131 | mv88e6xxx_reg_unlock(chip); |
| 132 | |
| 133 | return occupancy; |
| 134 | } |
| 135 | |
| 136 | static 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 | |
| 144 | static 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 | |
| 152 | static 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 | |
| 160 | static 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 | |
| 168 | static 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 | |
| 176 | int 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 | |
| 259 | out: |
| 260 | dsa_devlink_resources_unregister(ds); |
| 261 | return err; |
| 262 | } |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 263 | |
| 264 | static 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, ®isters[i]); |
| 284 | break; |
| 285 | case MV88E6XXX_REGION_GLOBAL2: |
| 286 | err = mv88e6xxx_g2_read(chip, i, ®isters[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; |
| 298 | out: |
| 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 | |
| 309 | struct 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 | |
| 319 | static 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 | |
| 371 | static 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(mv88e6xxx_num_databases(chip), |
| 383 | 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, fid_bitmap); |
zhangxiaoxu | 2bae900 | 2020-11-09 09:44:16 -0500 | [diff] [blame] | 396 | if (err) { |
| 397 | kfree(table); |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 398 | goto out; |
zhangxiaoxu | 2bae900 | 2020-11-09 09:44:16 -0500 | [diff] [blame] | 399 | } |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 400 | |
| 401 | while (1) { |
| 402 | fid = find_next_bit(fid_bitmap, MV88E6XXX_N_FID, fid + 1); |
| 403 | if (fid == MV88E6XXX_N_FID) |
| 404 | break; |
| 405 | |
| 406 | err = mv88e6xxx_region_atu_snapshot_fid(chip, fid, table, |
| 407 | &count); |
| 408 | if (err) { |
| 409 | kfree(table); |
| 410 | goto out; |
| 411 | } |
| 412 | } |
| 413 | *data = (u8 *)table; |
| 414 | out: |
| 415 | mv88e6xxx_reg_unlock(chip); |
| 416 | |
| 417 | return err; |
| 418 | } |
| 419 | |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 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 | */ |
| 435 | struct 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 | |
| 444 | static 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 | |
Tobias Waldekranz | e545f86 | 2020-11-10 19:57:20 +0100 | [diff] [blame] | 455 | table = kcalloc(mv88e6xxx_max_vid(chip) + 1, |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 456 | sizeof(struct mv88e6xxx_devlink_vtu_entry), |
| 457 | GFP_KERNEL); |
| 458 | if (!table) |
| 459 | return -ENOMEM; |
| 460 | |
| 461 | entry = table; |
Tobias Waldekranz | e545f86 | 2020-11-10 19:57:20 +0100 | [diff] [blame] | 462 | vlan.vid = mv88e6xxx_max_vid(chip); |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 463 | vlan.valid = false; |
| 464 | |
| 465 | mv88e6xxx_reg_lock(chip); |
| 466 | |
| 467 | do { |
| 468 | err = mv88e6xxx_g1_vtu_getnext(chip, &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 | &entry->fid); |
| 477 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, |
| 478 | &entry->sid); |
| 479 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, |
| 480 | &entry->op); |
| 481 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, |
| 482 | &entry->vid); |
| 483 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1, |
| 484 | &entry->data[0]); |
| 485 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA2, |
| 486 | &entry->data[1]); |
| 487 | err = err ? : mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA3, |
| 488 | &entry->data[2]); |
| 489 | if (err) |
| 490 | break; |
| 491 | |
| 492 | entry++; |
Tobias Waldekranz | e545f86 | 2020-11-10 19:57:20 +0100 | [diff] [blame] | 493 | } while (vlan.vid < mv88e6xxx_max_vid(chip)); |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 494 | |
| 495 | mv88e6xxx_reg_unlock(chip); |
| 496 | |
| 497 | if (err) { |
| 498 | kfree(table); |
| 499 | return err; |
| 500 | } |
| 501 | |
| 502 | *data = (u8 *)table; |
| 503 | return 0; |
| 504 | } |
| 505 | |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 506 | static int mv88e6xxx_region_pvt_snapshot(struct devlink *dl, |
| 507 | const struct devlink_region_ops *ops, |
| 508 | struct netlink_ext_ack *extack, |
| 509 | u8 **data) |
| 510 | { |
| 511 | struct dsa_switch *ds = dsa_devlink_to_ds(dl); |
| 512 | struct mv88e6xxx_chip *chip = ds->priv; |
| 513 | int dev, port, err; |
| 514 | u16 *pvt, *cur; |
| 515 | |
| 516 | pvt = kcalloc(MV88E6XXX_MAX_PVT_ENTRIES, sizeof(*pvt), GFP_KERNEL); |
| 517 | if (!pvt) |
| 518 | return -ENOMEM; |
| 519 | |
| 520 | mv88e6xxx_reg_lock(chip); |
| 521 | |
| 522 | cur = pvt; |
| 523 | for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; dev++) { |
| 524 | for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; port++) { |
| 525 | err = mv88e6xxx_g2_pvt_read(chip, dev, port, cur); |
| 526 | if (err) |
| 527 | break; |
| 528 | |
| 529 | cur++; |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | mv88e6xxx_reg_unlock(chip); |
| 534 | |
| 535 | if (err) { |
| 536 | kfree(pvt); |
| 537 | return err; |
| 538 | } |
| 539 | |
| 540 | *data = (u8 *)pvt; |
| 541 | return 0; |
| 542 | } |
| 543 | |
Andrew Lunn | b71a8d6 | 2020-10-04 18:12:57 +0200 | [diff] [blame] | 544 | static int mv88e6xxx_region_port_snapshot(struct devlink_port *devlink_port, |
| 545 | const struct devlink_port_region_ops *ops, |
| 546 | struct netlink_ext_ack *extack, |
| 547 | u8 **data) |
| 548 | { |
| 549 | struct dsa_switch *ds = dsa_devlink_port_to_ds(devlink_port); |
| 550 | int port = dsa_devlink_port_to_port(devlink_port); |
| 551 | struct mv88e6xxx_chip *chip = ds->priv; |
| 552 | u16 *registers; |
| 553 | int i, err; |
| 554 | |
| 555 | registers = kmalloc_array(32, sizeof(u16), GFP_KERNEL); |
| 556 | if (!registers) |
| 557 | return -ENOMEM; |
| 558 | |
| 559 | mv88e6xxx_reg_lock(chip); |
| 560 | for (i = 0; i < 32; i++) { |
| 561 | err = mv88e6xxx_port_read(chip, port, i, ®isters[i]); |
| 562 | if (err) { |
| 563 | kfree(registers); |
| 564 | goto out; |
| 565 | } |
| 566 | } |
| 567 | *data = (u8 *)registers; |
| 568 | out: |
| 569 | mv88e6xxx_reg_unlock(chip); |
| 570 | |
| 571 | return err; |
| 572 | } |
| 573 | |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 574 | static struct mv88e6xxx_region_priv mv88e6xxx_region_global1_priv = { |
| 575 | .id = MV88E6XXX_REGION_GLOBAL1, |
| 576 | }; |
| 577 | |
| 578 | static struct devlink_region_ops mv88e6xxx_region_global1_ops = { |
| 579 | .name = "global1", |
| 580 | .snapshot = mv88e6xxx_region_global_snapshot, |
| 581 | .destructor = kfree, |
| 582 | .priv = &mv88e6xxx_region_global1_priv, |
| 583 | }; |
| 584 | |
| 585 | static struct mv88e6xxx_region_priv mv88e6xxx_region_global2_priv = { |
| 586 | .id = MV88E6XXX_REGION_GLOBAL2, |
| 587 | }; |
| 588 | |
| 589 | static struct devlink_region_ops mv88e6xxx_region_global2_ops = { |
| 590 | .name = "global2", |
| 591 | .snapshot = mv88e6xxx_region_global_snapshot, |
| 592 | .destructor = kfree, |
| 593 | .priv = &mv88e6xxx_region_global2_priv, |
| 594 | }; |
| 595 | |
| 596 | static struct devlink_region_ops mv88e6xxx_region_atu_ops = { |
| 597 | .name = "atu", |
| 598 | .snapshot = mv88e6xxx_region_atu_snapshot, |
| 599 | .destructor = kfree, |
| 600 | }; |
| 601 | |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 602 | static struct devlink_region_ops mv88e6xxx_region_vtu_ops = { |
| 603 | .name = "vtu", |
| 604 | .snapshot = mv88e6xxx_region_vtu_snapshot, |
| 605 | .destructor = kfree, |
| 606 | }; |
| 607 | |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 608 | static struct devlink_region_ops mv88e6xxx_region_pvt_ops = { |
| 609 | .name = "pvt", |
| 610 | .snapshot = mv88e6xxx_region_pvt_snapshot, |
| 611 | .destructor = kfree, |
| 612 | }; |
| 613 | |
Andrew Lunn | b71a8d6 | 2020-10-04 18:12:57 +0200 | [diff] [blame] | 614 | static const struct devlink_port_region_ops mv88e6xxx_region_port_ops = { |
| 615 | .name = "port", |
| 616 | .snapshot = mv88e6xxx_region_port_snapshot, |
| 617 | .destructor = kfree, |
| 618 | }; |
| 619 | |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 620 | struct mv88e6xxx_region { |
| 621 | struct devlink_region_ops *ops; |
| 622 | u64 size; |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 623 | |
| 624 | bool (*cond)(struct mv88e6xxx_chip *chip); |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 625 | }; |
| 626 | |
| 627 | static struct mv88e6xxx_region mv88e6xxx_regions[] = { |
| 628 | [MV88E6XXX_REGION_GLOBAL1] = { |
| 629 | .ops = &mv88e6xxx_region_global1_ops, |
| 630 | .size = 32 * sizeof(u16) |
| 631 | }, |
| 632 | [MV88E6XXX_REGION_GLOBAL2] = { |
| 633 | .ops = &mv88e6xxx_region_global2_ops, |
| 634 | .size = 32 * sizeof(u16) }, |
| 635 | [MV88E6XXX_REGION_ATU] = { |
| 636 | .ops = &mv88e6xxx_region_atu_ops |
| 637 | /* calculated at runtime */ |
| 638 | }, |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 639 | [MV88E6XXX_REGION_VTU] = { |
| 640 | .ops = &mv88e6xxx_region_vtu_ops |
| 641 | /* calculated at runtime */ |
| 642 | }, |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 643 | [MV88E6XXX_REGION_PVT] = { |
| 644 | .ops = &mv88e6xxx_region_pvt_ops, |
| 645 | .size = MV88E6XXX_MAX_PVT_ENTRIES * sizeof(u16), |
| 646 | .cond = mv88e6xxx_has_pvt, |
| 647 | }, |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 648 | }; |
| 649 | |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 650 | void mv88e6xxx_teardown_devlink_regions_global(struct dsa_switch *ds) |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 651 | { |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 652 | struct mv88e6xxx_chip *chip = ds->priv; |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 653 | int i; |
| 654 | |
| 655 | for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) |
| 656 | dsa_devlink_region_destroy(chip->regions[i]); |
| 657 | } |
| 658 | |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 659 | void mv88e6xxx_teardown_devlink_regions_port(struct dsa_switch *ds, int port) |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 660 | { |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 661 | struct mv88e6xxx_chip *chip = ds->priv; |
| 662 | |
Andrew Lunn | b71a8d6 | 2020-10-04 18:12:57 +0200 | [diff] [blame] | 663 | dsa_devlink_region_destroy(chip->ports[port].region); |
| 664 | } |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 665 | |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 666 | int mv88e6xxx_setup_devlink_regions_port(struct dsa_switch *ds, int port) |
Andrew Lunn | b71a8d6 | 2020-10-04 18:12:57 +0200 | [diff] [blame] | 667 | { |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 668 | struct mv88e6xxx_chip *chip = ds->priv; |
Andrew Lunn | b71a8d6 | 2020-10-04 18:12:57 +0200 | [diff] [blame] | 669 | struct devlink_region *region; |
| 670 | |
| 671 | region = dsa_devlink_port_region_create(ds, |
| 672 | port, |
| 673 | &mv88e6xxx_region_port_ops, 1, |
| 674 | 32 * sizeof(u16)); |
| 675 | if (IS_ERR(region)) |
| 676 | return PTR_ERR(region); |
| 677 | |
| 678 | chip->ports[port].region = region; |
| 679 | |
| 680 | return 0; |
| 681 | } |
| 682 | |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 683 | int mv88e6xxx_setup_devlink_regions_global(struct dsa_switch *ds) |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 684 | { |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 685 | bool (*cond)(struct mv88e6xxx_chip *chip); |
Vladimir Oltean | fd292c1 | 2021-09-17 17:29:16 +0300 | [diff] [blame] | 686 | struct mv88e6xxx_chip *chip = ds->priv; |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 687 | struct devlink_region_ops *ops; |
| 688 | struct devlink_region *region; |
| 689 | u64 size; |
| 690 | int i, j; |
| 691 | |
| 692 | for (i = 0; i < ARRAY_SIZE(mv88e6xxx_regions); i++) { |
| 693 | ops = mv88e6xxx_regions[i].ops; |
| 694 | size = mv88e6xxx_regions[i].size; |
Tobias Waldekranz | 836021a | 2021-04-21 14:04:54 +0200 | [diff] [blame] | 695 | cond = mv88e6xxx_regions[i].cond; |
| 696 | |
| 697 | if (cond && !cond(chip)) |
| 698 | continue; |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 699 | |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 700 | switch (i) { |
| 701 | case MV88E6XXX_REGION_ATU: |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 702 | size = mv88e6xxx_num_databases(chip) * |
| 703 | sizeof(struct mv88e6xxx_devlink_atu_entry); |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 704 | break; |
| 705 | case MV88E6XXX_REGION_VTU: |
Tobias Waldekranz | 281140a | 2021-04-21 14:04:53 +0200 | [diff] [blame] | 706 | size = (mv88e6xxx_max_vid(chip) + 1) * |
Tobias Waldekranz | ca4d632a | 2020-11-09 09:29:27 +0100 | [diff] [blame] | 707 | sizeof(struct mv88e6xxx_devlink_vtu_entry); |
| 708 | break; |
| 709 | } |
Andrew Lunn | bfb2554 | 2020-09-18 21:11:07 +0200 | [diff] [blame] | 710 | |
| 711 | region = dsa_devlink_region_create(ds, ops, 1, size); |
| 712 | if (IS_ERR(region)) |
| 713 | goto out; |
| 714 | chip->regions[i] = region; |
| 715 | } |
| 716 | return 0; |
| 717 | |
| 718 | out: |
| 719 | for (j = 0; j < i; j++) |
| 720 | dsa_devlink_region_destroy(chip->regions[j]); |
| 721 | |
| 722 | return PTR_ERR(region); |
| 723 | } |
| 724 | |
Andrew Lunn | 9315730 | 2020-09-18 21:11:09 +0200 | [diff] [blame] | 725 | int mv88e6xxx_devlink_info_get(struct dsa_switch *ds, |
| 726 | struct devlink_info_req *req, |
| 727 | struct netlink_ext_ack *extack) |
| 728 | { |
| 729 | struct mv88e6xxx_chip *chip = ds->priv; |
| 730 | int err; |
| 731 | |
| 732 | err = devlink_info_driver_name_put(req, "mv88e6xxx"); |
| 733 | if (err) |
| 734 | return err; |
| 735 | |
| 736 | return devlink_info_version_fixed_put(req, |
| 737 | DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, |
| 738 | chip->info->name); |
| 739 | } |