681 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support4 *5 * Copyright (c) 2008 Marvell Semiconductor6 * Copyright (c) 2015 CMC Electronics, Inc.7 * Copyright (c) 2017 Savoir-faire Linux, Inc.8 */9 10#include <linux/bitfield.h>11#include <linux/interrupt.h>12#include <linux/irqdomain.h>13 14#include "chip.h"15#include "global1.h"16#include "trace.h"17 18/* Offset 0x02: VTU FID Register */19 20static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,21 struct mv88e6xxx_vtu_entry *entry)22{23 u16 val;24 int err;25 26 err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, &val);27 if (err)28 return err;29 30 entry->fid = val & MV88E6352_G1_VTU_FID_MASK;31 entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY);32 return 0;33}34 35static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,36 struct mv88e6xxx_vtu_entry *entry)37{38 u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK;39 40 if (entry->policy)41 val |= MV88E6352_G1_VTU_FID_VID_POLICY;42 43 return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val);44}45 46/* Offset 0x03: VTU SID Register */47 48static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, u8 *sid)49{50 u16 val;51 int err;52 53 err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, &val);54 if (err)55 return err;56 57 *sid = val & MV88E6352_G1_VTU_SID_MASK;58 59 return 0;60}61 62static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, u8 sid)63{64 u16 val = sid & MV88E6352_G1_VTU_SID_MASK;65 66 return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val);67}68 69/* Offset 0x05: VTU Operation Register */70 71static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)72{73 int bit = __bf_shf(MV88E6XXX_G1_VTU_OP_BUSY);74 75 return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_VTU_OP, bit, 0);76}77 78static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)79{80 int err;81 82 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP,83 MV88E6XXX_G1_VTU_OP_BUSY | op);84 if (err)85 return err;86 87 return mv88e6xxx_g1_vtu_op_wait(chip);88}89 90/* Offset 0x06: VTU VID Register */91 92static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,93 bool *valid, u16 *vid)94{95 u16 val;96 int err;97 98 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, &val);99 if (err)100 return err;101 102 if (vid) {103 *vid = val & 0xfff;104 105 if (val & MV88E6390_G1_VTU_VID_PAGE)106 *vid |= 0x1000;107 }108 109 if (valid)110 *valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID);111 112 return 0;113}114 115static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,116 bool valid, u16 vid)117{118 u16 val = vid & 0xfff;119 120 if (vid & 0x1000)121 val |= MV88E6390_G1_VTU_VID_PAGE;122 123 if (valid)124 val |= MV88E6XXX_G1_VTU_VID_VALID;125 126 return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val);127}128 129/* Offset 0x07: VTU/STU Data Register 1130 * Offset 0x08: VTU/STU Data Register 2131 * Offset 0x09: VTU/STU Data Register 3132 */133static int mv88e6185_g1_vtu_stu_data_read(struct mv88e6xxx_chip *chip,134 u16 *regs)135{136 int i;137 138 /* Read all 3 VTU/STU Data registers */139 for (i = 0; i < 3; ++i) {140 u16 *reg = ®s[i];141 int err;142 143 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);144 if (err)145 return err;146 }147 148 return 0;149}150 151static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,152 u8 *member, u8 *state)153{154 u16 regs[3];155 int err;156 int i;157 158 err = mv88e6185_g1_vtu_stu_data_read(chip, regs);159 if (err)160 return err;161 162 /* Extract MemberTag data */163 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {164 unsigned int member_offset = (i % 4) * 4;165 unsigned int state_offset = member_offset + 2;166 167 if (member)168 member[i] = (regs[i / 4] >> member_offset) & 0x3;169 170 if (state)171 state[i] = (regs[i / 4] >> state_offset) & 0x3;172 }173 174 return 0;175}176 177static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,178 u8 *member, u8 *state)179{180 u16 regs[3] = { 0 };181 int i;182 183 /* Insert MemberTag and PortState data */184 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {185 unsigned int member_offset = (i % 4) * 4;186 unsigned int state_offset = member_offset + 2;187 188 if (member)189 regs[i / 4] |= (member[i] & 0x3) << member_offset;190 191 if (state)192 regs[i / 4] |= (state[i] & 0x3) << state_offset;193 }194 195 /* Write all 3 VTU/STU Data registers */196 for (i = 0; i < 3; ++i) {197 u16 reg = regs[i];198 int err;199 200 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);201 if (err)202 return err;203 }204 205 return 0;206}207 208static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)209{210 u16 regs[2];211 int i;212 213 /* Read the 2 VTU/STU Data registers */214 for (i = 0; i < 2; ++i) {215 u16 *reg = ®s[i];216 int err;217 218 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);219 if (err)220 return err;221 }222 223 /* Extract data */224 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {225 unsigned int offset = (i % 8) * 2;226 227 data[i] = (regs[i / 8] >> offset) & 0x3;228 }229 230 return 0;231}232 233static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)234{235 u16 regs[2] = { 0 };236 int i;237 238 /* Insert data */239 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {240 unsigned int offset = (i % 8) * 2;241 242 regs[i / 8] |= (data[i] & 0x3) << offset;243 }244 245 /* Write the 2 VTU/STU Data registers */246 for (i = 0; i < 2; ++i) {247 u16 reg = regs[i];248 int err;249 250 err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);251 if (err)252 return err;253 }254 255 return 0;256}257 258/* VLAN Translation Unit Operations */259 260int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,261 struct mv88e6xxx_vtu_entry *entry)262{263 int err;264 265 err = mv88e6xxx_g1_vtu_op_wait(chip);266 if (err)267 return err;268 269 /* To get the next higher active VID, the VTU GetNext operation can be270 * started again without setting the VID registers since it already271 * contains the last VID.272 *273 * To save a few hardware accesses and abstract this to the caller,274 * write the VID only once, when the entry is given as invalid.275 */276 if (!entry->valid) {277 err = mv88e6xxx_g1_vtu_vid_write(chip, false, entry->vid);278 if (err)279 return err;280 }281 282 err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT);283 if (err)284 return err;285 286 return mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, &entry->vid);287}288 289int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,290 struct mv88e6xxx_vtu_entry *entry)291{292 u16 val;293 int err;294 295 err = mv88e6xxx_g1_vtu_getnext(chip, entry);296 if (err)297 return err;298 299 if (entry->valid) {300 err = mv88e6185_g1_vtu_data_read(chip, entry->member, entry->state);301 if (err)302 return err;303 304 /* VTU DBNum[3:0] are located in VTU Operation 3:0305 * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8)306 */307 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);308 if (err)309 return err;310 311 entry->fid = val & 0x000f;312 entry->fid |= (val & 0x0f00) >> 4;313 entry->fid &= mv88e6xxx_num_databases(chip) - 1;314 }315 316 return 0;317}318 319int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,320 struct mv88e6xxx_vtu_entry *entry)321{322 int err;323 324 /* Fetch VLAN MemberTag data from the VTU */325 err = mv88e6xxx_g1_vtu_getnext(chip, entry);326 if (err)327 return err;328 329 if (entry->valid) {330 err = mv88e6185_g1_vtu_data_read(chip, entry->member, NULL);331 if (err)332 return err;333 334 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);335 if (err)336 return err;337 338 err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);339 if (err)340 return err;341 }342 343 return 0;344}345 346int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,347 struct mv88e6xxx_vtu_entry *entry)348{349 int err;350 351 /* Fetch VLAN MemberTag data from the VTU */352 err = mv88e6xxx_g1_vtu_getnext(chip, entry);353 if (err)354 return err;355 356 if (entry->valid) {357 err = mv88e6390_g1_vtu_data_read(chip, entry->member);358 if (err)359 return err;360 361 err = mv88e6xxx_g1_vtu_fid_read(chip, entry);362 if (err)363 return err;364 365 err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);366 if (err)367 return err;368 }369 370 return 0;371}372 373int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,374 struct mv88e6xxx_vtu_entry *entry)375{376 u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;377 int err;378 379 err = mv88e6xxx_g1_vtu_op_wait(chip);380 if (err)381 return err;382 383 err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);384 if (err)385 return err;386 387 if (entry->valid) {388 err = mv88e6185_g1_vtu_data_write(chip, entry->member, entry->state);389 if (err)390 return err;391 392 /* VTU DBNum[3:0] are located in VTU Operation 3:0393 * VTU DBNum[7:4] are located in VTU Operation 11:8394 *395 * For the 6250/6220, the latter are really [5:4] and396 * 9:8, but in those cases bits 7:6 of entry->fid are397 * 0 since they have num_databases = 64.398 */399 op |= entry->fid & 0x000f;400 op |= (entry->fid & 0x00f0) << 4;401 }402 403 return mv88e6xxx_g1_vtu_op(chip, op);404}405 406int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,407 struct mv88e6xxx_vtu_entry *entry)408{409 int err;410 411 err = mv88e6xxx_g1_vtu_op_wait(chip);412 if (err)413 return err;414 415 err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);416 if (err)417 return err;418 419 if (entry->valid) {420 /* Write MemberTag data */421 err = mv88e6185_g1_vtu_data_write(chip, entry->member, NULL);422 if (err)423 return err;424 425 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);426 if (err)427 return err;428 429 err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);430 if (err)431 return err;432 }433 434 /* Load/Purge VTU entry */435 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);436}437 438int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,439 struct mv88e6xxx_vtu_entry *entry)440{441 int err;442 443 err = mv88e6xxx_g1_vtu_op_wait(chip);444 if (err)445 return err;446 447 err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);448 if (err)449 return err;450 451 if (entry->valid) {452 /* Write MemberTag data */453 err = mv88e6390_g1_vtu_data_write(chip, entry->member);454 if (err)455 return err;456 457 err = mv88e6xxx_g1_vtu_fid_write(chip, entry);458 if (err)459 return err;460 461 err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);462 if (err)463 return err;464 }465 466 /* Load/Purge VTU entry */467 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);468}469 470int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)471{472 int err;473 474 err = mv88e6xxx_g1_vtu_op_wait(chip);475 if (err)476 return err;477 478 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);479}480 481/* Spanning Tree Unit Operations */482 483int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip,484 struct mv88e6xxx_stu_entry *entry)485{486 int err;487 488 err = mv88e6xxx_g1_vtu_op_wait(chip);489 if (err)490 return err;491 492 /* To get the next higher active SID, the STU GetNext operation can be493 * started again without setting the SID registers since it already494 * contains the last SID.495 *496 * To save a few hardware accesses and abstract this to the caller,497 * write the SID only once, when the entry is given as invalid.498 */499 if (!entry->valid) {500 err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);501 if (err)502 return err;503 }504 505 err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT);506 if (err)507 return err;508 509 err = mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, NULL);510 if (err)511 return err;512 513 if (entry->valid) {514 err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);515 if (err)516 return err;517 }518 519 return 0;520}521 522int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip,523 struct mv88e6xxx_stu_entry *entry)524{525 int err;526 527 err = mv88e6xxx_g1_stu_getnext(chip, entry);528 if (err)529 return err;530 531 if (!entry->valid)532 return 0;533 534 return mv88e6185_g1_vtu_data_read(chip, NULL, entry->state);535}536 537int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip,538 struct mv88e6xxx_stu_entry *entry)539{540 int err;541 542 err = mv88e6xxx_g1_stu_getnext(chip, entry);543 if (err)544 return err;545 546 if (!entry->valid)547 return 0;548 549 return mv88e6390_g1_vtu_data_read(chip, entry->state);550}551 552int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip,553 struct mv88e6xxx_stu_entry *entry)554{555 int err;556 557 err = mv88e6xxx_g1_vtu_op_wait(chip);558 if (err)559 return err;560 561 err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0);562 if (err)563 return err;564 565 err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);566 if (err)567 return err;568 569 if (entry->valid) {570 err = mv88e6185_g1_vtu_data_write(chip, NULL, entry->state);571 if (err)572 return err;573 }574 575 /* Load/Purge STU entry */576 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);577}578 579int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip,580 struct mv88e6xxx_stu_entry *entry)581{582 int err;583 584 err = mv88e6xxx_g1_vtu_op_wait(chip);585 if (err)586 return err;587 588 err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0);589 if (err)590 return err;591 592 err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);593 if (err)594 return err;595 596 if (entry->valid) {597 err = mv88e6390_g1_vtu_data_write(chip, entry->state);598 if (err)599 return err;600 }601 602 /* Load/Purge STU entry */603 return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);604}605 606/* VTU Violation Management */607 608static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id)609{610 struct mv88e6xxx_chip *chip = dev_id;611 u16 val, vid;612 int spid;613 int err;614 615 mv88e6xxx_reg_lock(chip);616 617 err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION);618 if (err)619 goto out;620 621 err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);622 if (err)623 goto out;624 625 err = mv88e6xxx_g1_vtu_vid_read(chip, NULL, &vid);626 if (err)627 goto out;628 629 spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK;630 631 if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) {632 trace_mv88e6xxx_vtu_member_violation(chip->dev, spid, vid);633 chip->ports[spid].vtu_member_violation++;634 }635 636 if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) {637 trace_mv88e6xxx_vtu_miss_violation(chip->dev, spid, vid);638 chip->ports[spid].vtu_miss_violation++;639 }640 641 mv88e6xxx_reg_unlock(chip);642 643 return IRQ_HANDLED;644 645out:646 mv88e6xxx_reg_unlock(chip);647 648 dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n",649 err);650 651 return IRQ_HANDLED;652}653 654int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip)655{656 int err;657 658 chip->vtu_prob_irq = irq_find_mapping(chip->g1_irq.domain,659 MV88E6XXX_G1_STS_IRQ_VTU_PROB);660 if (chip->vtu_prob_irq < 0)661 return chip->vtu_prob_irq;662 663 snprintf(chip->vtu_prob_irq_name, sizeof(chip->vtu_prob_irq_name),664 "mv88e6xxx-%s-g1-vtu-prob", dev_name(chip->dev));665 666 err = request_threaded_irq(chip->vtu_prob_irq, NULL,667 mv88e6xxx_g1_vtu_prob_irq_thread_fn,668 IRQF_ONESHOT, chip->vtu_prob_irq_name,669 chip);670 if (err)671 irq_dispose_mapping(chip->vtu_prob_irq);672 673 return err;674}675 676void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip)677{678 free_irq(chip->vtu_prob_irq, chip);679 irq_dispose_mapping(chip->vtu_prob_irq);680}681