diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/global1_vtu.c')
| -rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_vtu.c | 353 |
1 files changed, 222 insertions, 131 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 058326924f3e..b524f27a2f0d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -1,21 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support * * Copyright (c) 2008 Marvell Semiconductor * Copyright (c) 2015 CMC Electronics, Inc. * Copyright (c) 2017 Savoir-faire Linux, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ +#include <linux/bitfield.h> #include <linux/interrupt.h> #include <linux/irqdomain.h> #include "chip.h" #include "global1.h" +#include "trace.h" /* Offset 0x02: VTU FID Register */ @@ -30,7 +28,7 @@ static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip, return err; entry->fid = val & MV88E6352_G1_VTU_FID_MASK; - + entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY); return 0; } @@ -39,13 +37,15 @@ static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip, { u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK; + if (entry->policy) + val |= MV88E6352_G1_VTU_FID_VID_POLICY; + return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val); } /* Offset 0x03: VTU SID Register */ -static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, u8 *sid) { u16 val; int err; @@ -54,15 +54,14 @@ static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, if (err) return err; - entry->sid = val & MV88E6352_G1_VTU_SID_MASK; + *sid = val & MV88E6352_G1_VTU_SID_MASK; return 0; } -static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, u8 sid) { - u16 val = entry->sid & MV88E6352_G1_VTU_SID_MASK; + u16 val = sid & MV88E6352_G1_VTU_SID_MASK; return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val); } @@ -71,8 +70,9 @@ static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip) { - return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_VTU_OP, - MV88E6XXX_G1_VTU_OP_BUSY); + int bit = __bf_shf(MV88E6XXX_G1_VTU_OP_BUSY); + + return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_VTU_OP, bit, 0); } static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) @@ -90,7 +90,7 @@ static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op) /* Offset 0x06: VTU VID Register */ static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + bool *valid, u16 *vid) { u16 val; int err; @@ -99,25 +99,28 @@ static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip, if (err) return err; - entry->vid = val & 0xfff; + if (vid) { + *vid = val & 0xfff; - if (val & MV88E6390_G1_VTU_VID_PAGE) - entry->vid |= 0x1000; + if (val & MV88E6390_G1_VTU_VID_PAGE) + *vid |= 0x1000; + } - entry->valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID); + if (valid) + *valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID); return 0; } static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + bool valid, u16 vid) { - u16 val = entry->vid & 0xfff; + u16 val = vid & 0xfff; - if (entry->vid & 0x1000) + if (vid & 0x1000) val |= MV88E6390_G1_VTU_VID_PAGE; - if (entry->valid) + if (valid) val |= MV88E6XXX_G1_VTU_VID_VALID; return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val); @@ -127,11 +130,9 @@ static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip, * Offset 0x08: VTU/STU Data Register 2 * Offset 0x09: VTU/STU Data Register 3 */ - -static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +static int mv88e6185_g1_vtu_stu_data_read(struct mv88e6xxx_chip *chip, + u16 *regs) { - u16 regs[3]; int i; /* Read all 3 VTU/STU Data registers */ @@ -144,20 +145,37 @@ static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, return err; } - /* Extract MemberTag and PortState data */ + return 0; +} + +static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip, + u8 *member, u8 *state) +{ + u16 regs[3]; + int err; + int i; + + err = mv88e6185_g1_vtu_stu_data_read(chip, regs); + if (err) + return err; + + /* Extract MemberTag data */ for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) { unsigned int member_offset = (i % 4) * 4; unsigned int state_offset = member_offset + 2; - entry->member[i] = (regs[i / 4] >> member_offset) & 0x3; - entry->state[i] = (regs[i / 4] >> state_offset) & 0x3; + if (member) + member[i] = (regs[i / 4] >> member_offset) & 0x3; + + if (state) + state[i] = (regs[i / 4] >> state_offset) & 0x3; } return 0; } static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) + u8 *member, u8 *state) { u16 regs[3] = { 0 }; int i; @@ -167,8 +185,11 @@ static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip, unsigned int member_offset = (i % 4) * 4; unsigned int state_offset = member_offset + 2; - regs[i / 4] |= (entry->member[i] & 0x3) << member_offset; - regs[i / 4] |= (entry->state[i] & 0x3) << state_offset; + if (member) + regs[i / 4] |= (member[i] & 0x3) << member_offset; + + if (state) + regs[i / 4] |= (state[i] & 0x3) << state_offset; } /* Write all 3 VTU/STU Data registers */ @@ -236,50 +257,8 @@ static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data) /* VLAN Translation Unit Operations */ -static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) -{ - int err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_read(chip, entry); - if (err) - return err; - - return mv88e6xxx_g1_vtu_vid_read(chip, entry); -} - -static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *vtu) -{ - struct mv88e6xxx_vtu_entry stu; - int err; - - err = mv88e6xxx_g1_vtu_sid_read(chip, vtu); - if (err) - return err; - - stu.sid = vtu->sid - 1; - - err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu); - if (err) - return err; - - if (stu.sid != vtu->sid || !stu.valid) - return -EINVAL; - - return 0; -} - -static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, - struct mv88e6xxx_vtu_entry *entry) +int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_vtu_entry *entry) { int err; @@ -295,7 +274,7 @@ static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, * write the VID only once, when the entry is given as invalid. */ if (!entry->valid) { - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, false, entry->vid); if (err) return err; } @@ -304,7 +283,7 @@ static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - return mv88e6xxx_g1_vtu_vid_read(chip, entry); + return mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, &entry->vid); } int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, @@ -318,12 +297,12 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (entry->valid) { - err = mv88e6185_g1_vtu_data_read(chip, entry); + err = mv88e6185_g1_vtu_data_read(chip, entry->member, entry->state); if (err) return err; /* VTU DBNum[3:0] are located in VTU Operation 3:0 - * VTU DBNum[7:4] are located in VTU Operation 11:8 + * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8) */ err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); if (err) @@ -331,6 +310,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip, entry->fid = val & 0x000f; entry->fid |= (val & 0x0f00) >> 4; + entry->fid &= mv88e6xxx_num_databases(chip) - 1; } return 0; @@ -347,16 +327,15 @@ int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip, return err; if (entry->valid) { - /* Fetch (and mask) VLAN PortState data from the STU */ - err = mv88e6xxx_g1_vtu_stu_get(chip, entry); + err = mv88e6185_g1_vtu_data_read(chip, entry->member, NULL); if (err) return err; - err = mv88e6185_g1_vtu_data_read(chip, entry); + err = mv88e6xxx_g1_vtu_fid_read(chip, entry); if (err) return err; - err = mv88e6xxx_g1_vtu_fid_read(chip, entry); + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); if (err) return err; } @@ -379,16 +358,11 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, if (err) return err; - /* Fetch VLAN PortState data from the STU */ - err = mv88e6xxx_g1_vtu_stu_get(chip, entry); - if (err) - return err; - - err = mv88e6390_g1_vtu_data_read(chip, entry->state); + err = mv88e6xxx_g1_vtu_fid_read(chip, entry); if (err) return err; - err = mv88e6xxx_g1_vtu_fid_read(chip, entry); + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); if (err) return err; } @@ -406,20 +380,24 @@ int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - err = mv88e6185_g1_vtu_data_write(chip, entry); + err = mv88e6185_g1_vtu_data_write(chip, entry->member, entry->state); if (err) return err; /* VTU DBNum[3:0] are located in VTU Operation 3:0 * VTU DBNum[7:4] are located in VTU Operation 11:8 + * + * For the 6250/6220, the latter are really [5:4] and + * 9:8, but in those cases bits 7:6 of entry->fid are + * 0 since they have num_databases = 64. */ op |= entry->fid & 0x000f; - op |= (entry->fid & 0x00f0) << 8; + op |= (entry->fid & 0x00f0) << 4; } return mv88e6xxx_g1_vtu_op(chip, op); @@ -434,27 +412,21 @@ int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - /* Write MemberTag and PortState data */ - err = mv88e6185_g1_vtu_data_write(chip, entry); - if (err) - return err; - - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); + /* Write MemberTag data */ + err = mv88e6185_g1_vtu_data_write(chip, entry->member, NULL); if (err) return err; - /* Load STU entry */ - err = mv88e6xxx_g1_vtu_op(chip, - MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); + err = mv88e6xxx_g1_vtu_fid_write(chip, entry); if (err) return err; - err = mv88e6xxx_g1_vtu_fid_write(chip, entry); + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); if (err) return err; } @@ -472,41 +444,116 @@ int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, if (err) return err; - err = mv88e6xxx_g1_vtu_vid_write(chip, entry); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid); if (err) return err; if (entry->valid) { - /* Write PortState data */ - err = mv88e6390_g1_vtu_data_write(chip, entry->state); + /* Write MemberTag data */ + err = mv88e6390_g1_vtu_data_write(chip, entry->member); if (err) return err; - err = mv88e6xxx_g1_vtu_sid_write(chip, entry); + err = mv88e6xxx_g1_vtu_fid_write(chip, entry); if (err) return err; - /* Load STU entry */ - err = mv88e6xxx_g1_vtu_op(chip, - MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); if (err) return err; + } - /* Write MemberTag data */ - err = mv88e6390_g1_vtu_data_write(chip, entry->member); + /* Load/Purge VTU entry */ + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE); +} + +int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) +{ + int err; + + /* As part of the VTU flush, refresh FID map */ + bitmap_zero(chip->fid_bitmap, MV88E6XXX_N_FID); + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL); +} + +/* Spanning Tree Unit Operations */ + +int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + /* To get the next higher active SID, the STU GetNext operation can be + * started again without setting the SID registers since it already + * contains the last SID. + * + * To save a few hardware accesses and abstract this to the caller, + * write the SID only once, when the entry is given as invalid. + */ + if (!entry->valid) { + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); if (err) return err; + } - err = mv88e6xxx_g1_vtu_fid_write(chip, entry); + err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, NULL); + if (err) + return err; + + if (entry->valid) { + err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid); if (err) return err; } - /* Load/Purge VTU entry */ - return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE); + return 0; } -int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) +int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_stu_getnext(chip, entry); + if (err) + return err; + + if (!entry->valid) + return 0; + + return mv88e6185_g1_vtu_data_read(chip, NULL, entry->state); +} + +int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_stu_getnext(chip, entry); + if (err) + return err; + + if (!entry->valid) + return 0; + + return mv88e6390_g1_vtu_data_read(chip, entry->state); +} + +int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) { int err; @@ -514,18 +561,61 @@ int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) if (err) return err; - return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL); + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; + + if (entry->valid) { + err = mv88e6185_g1_vtu_data_write(chip, NULL, entry->state); + if (err) + return err; + } + + /* Load/Purge STU entry */ + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); } +int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip, + struct mv88e6xxx_stu_entry *entry) +{ + int err; + + err = mv88e6xxx_g1_vtu_op_wait(chip); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0); + if (err) + return err; + + err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid); + if (err) + return err; + + if (entry->valid) { + err = mv88e6390_g1_vtu_data_write(chip, entry->state); + if (err) + return err; + } + + /* Load/Purge STU entry */ + return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE); +} + +/* VTU Violation Management */ + static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) { struct mv88e6xxx_chip *chip = dev_id; - struct mv88e6xxx_vtu_entry entry; + u16 val, vid; int spid; int err; - u16 val; - mutex_lock(&chip->reg_lock); + mv88e6xxx_reg_lock(chip); err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION); if (err) @@ -535,30 +625,28 @@ static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) if (err) goto out; - err = mv88e6xxx_g1_vtu_vid_read(chip, &entry); + err = mv88e6xxx_g1_vtu_vid_read(chip, NULL, &vid); if (err) goto out; spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { - dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", - entry.vid, spid); + trace_mv88e6xxx_vtu_member_violation(chip->dev, spid, vid); chip->ports[spid].vtu_member_violation++; } if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) { - dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", - entry.vid, spid); + trace_mv88e6xxx_vtu_miss_violation(chip->dev, spid, vid); chip->ports[spid].vtu_miss_violation++; } - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); return IRQ_HANDLED; out: - mutex_unlock(&chip->reg_lock); + mv88e6xxx_reg_unlock(chip); dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n", err); @@ -575,9 +663,12 @@ int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip) if (chip->vtu_prob_irq < 0) return chip->vtu_prob_irq; + snprintf(chip->vtu_prob_irq_name, sizeof(chip->vtu_prob_irq_name), + "mv88e6xxx-%s-g1-vtu-prob", dev_name(chip->dev)); + err = request_threaded_irq(chip->vtu_prob_irq, NULL, mv88e6xxx_g1_vtu_prob_irq_thread_fn, - IRQF_ONESHOT, "mv88e6xxx-g1-vtu-prob", + IRQF_ONESHOT, chip->vtu_prob_irq_name, chip); if (err) irq_dispose_mapping(chip->vtu_prob_irq); |
