Implement {get,add,del}_dscp_prio for Marvell switches that support it. On switches that do not support setting the QPri independently of FPri, FPri is set to the default for the DSCP value. On 6352-class switches the DSCP-to-QPri mapping is a global table with no per-entry "unmapped" state: every DSCP is always mapped, so the DCB app table comes up fully populated and deleting an entry restores its reset-default QPri rather than removing it. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Luke Howard --- drivers/net/dsa/mv88e6xxx/chip.c | 6 + drivers/net/dsa/mv88e6xxx/chip.h | 14 ++ drivers/net/dsa/mv88e6xxx/dcb.c | 294 ++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/dcb.h | 6 + drivers/net/dsa/mv88e6xxx/global1.h | 8 + drivers/net/dsa/mv88e6xxx/port.h | 10 ++ 6 files changed, 338 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index cefe6be2f0ce9..3bdf8c7b36612 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -7434,6 +7434,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_get_pcp_prio = mv88e6xxx_port_get_pcp_prio, .port_add_pcp_prio = mv88e6xxx_port_add_pcp_prio, .port_del_pcp_prio = mv88e6xxx_port_del_pcp_prio, + .port_get_dscp_prio = mv88e6xxx_port_get_dscp_prio, + .port_add_dscp_prio = mv88e6xxx_port_add_dscp_prio, + .port_del_dscp_prio = mv88e6xxx_port_del_dscp_prio, .port_setup_tc = mv88e6xxx_port_setup_tc, .cls_flower_add = mv88e6xxx_cls_flower_add, .cls_flower_del = mv88e6xxx_cls_flower_del, @@ -7471,6 +7474,9 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) ds->pcp_prio_mapping_is_global = dcb_ops && dcb_ops->global_get_pcp_prio; + ds->dscp_prio_mapping_is_global = dcb_ops && + dcb_ops->global_get_dscp_prio; + /* Some chips support up to 32, but that requires enabling the * 5-bit port mode, which we do not support. 640k^W16 ought to * be enough for anyone. diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8c4e8b4890907..d6265e925574f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -794,6 +794,20 @@ struct mv88e6xxx_dcb_ops { u8 pcp); int (*port_set_pcp_prio)(struct mv88e6xxx_chip *chip, int port, u8 pcp, u8 prio); + + /* Get/set the chip's global DSCP to queue priority (QPri) mapping */ + int (*global_get_dscp_prio)(struct mv88e6xxx_chip *chip, u8 dscp); + int (*global_set_dscp_prio)(struct mv88e6xxx_chip *chip, u8 dscp, + u8 prio); + int (*global_del_dscp_prio)(struct mv88e6xxx_chip *chip, u8 dscp); + + /* Get/set a port's DSCP to queue priority (QPri) mapping */ + int (*port_get_dscp_prio)(struct mv88e6xxx_chip *chip, int port, + u8 dscp); + int (*port_set_dscp_prio)(struct mv88e6xxx_chip *chip, int port, + u8 dscp, u8 prio); + int (*port_del_dscp_prio)(struct mv88e6xxx_chip *chip, int port, + u8 dscp); }; struct mv88e6xxx_ptp_ops { diff --git a/drivers/net/dsa/mv88e6xxx/dcb.c b/drivers/net/dsa/mv88e6xxx/dcb.c index 0a3099cdf3882..c26f43b27f09e 100644 --- a/drivers/net/dsa/mv88e6xxx/dcb.c +++ b/drivers/net/dsa/mv88e6xxx/dcb.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Copyright (c) 2026 Luminex Network Intelligence +#include + +#include + #include "chip.h" #include "dcb.h" #include "global1.h" @@ -134,19 +138,201 @@ static int mv88e6393x_port_set_pcp_prio(struct mv88e6xxx_chip *chip, int port, return mv88e639x_port_set_pcp_prio(chip, port, pcp, prio, true); } +/* The DCB priority maps the DSCP to an egress queue priority (QPri); the frame + * priority (FPri) is left untouched. These chips always map every DSCP, so + * there is no per-entry "unmapped" state on the global (6352) table: deleting + * an entry restores its reset-default QPri rather than removing it. + */ + +/* G1 offset 0x19 is the IP Mapping Table only on these families; elsewhere it is + * the Core Tag Type register, so report DSCP mapping as unsupported there. + */ +static bool mv88e6352_has_ip_pri_map(struct mv88e6xxx_chip *chip) +{ + switch (chip->info->family) { + case MV88E6XXX_FAMILY_6320: + case MV88E6XXX_FAMILY_6341: + case MV88E6XXX_FAMILY_6352: + return true; + default: + return false; + } +} + +static int mv88e6352_get_dscp_prio(struct mv88e6xxx_chip *chip, u8 dscp) +{ + u16 val; + int err; + + if (!mv88e6352_has_ip_pri_map(chip)) + return -EOPNOTSUPP; + + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* load the table pointer (Update clear), then read the entry back */ + err = mv88e6xxx_g1_write(chip, MV88E6352_G1_IP_PRI_MAP, + FIELD_PREP(MV88E6352_G1_IP_PRI_MAP_PTR_MASK, + dscp)); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_IP_PRI_MAP, &val); + if (err) + return err; + + return FIELD_GET(MV88E6352_G1_IP_PRI_MAP_QPRI_MASK, val); +} + +/* Update only the QPri of a DSCP entry, preserving the global UseIpFPri bit and + * the per-entry FPri so the frame priority handling is left untouched. + */ +static int mv88e6352_write_dscp_qpri(struct mv88e6xxx_chip *chip, u8 dscp, + u8 prio) +{ + u16 val; + int err; + + /* load the table pointer (Update clear), then read the entry back */ + err = mv88e6xxx_g1_write(chip, MV88E6352_G1_IP_PRI_MAP, + FIELD_PREP(MV88E6352_G1_IP_PRI_MAP_PTR_MASK, + dscp)); + if (err) + return err; + + err = mv88e6xxx_g1_read(chip, MV88E6352_G1_IP_PRI_MAP, &val); + if (err) + return err; + + val &= ~(MV88E6352_G1_IP_PRI_MAP_PTR_MASK | + MV88E6352_G1_IP_PRI_MAP_QPRI_MASK); + val |= MV88E6352_G1_IP_PRI_MAP_UPDATE | + FIELD_PREP(MV88E6352_G1_IP_PRI_MAP_PTR_MASK, dscp) | + FIELD_PREP(MV88E6352_G1_IP_PRI_MAP_QPRI_MASK, prio); + + return mv88e6xxx_g1_write(chip, MV88E6352_G1_IP_PRI_MAP, val); +} + +static int mv88e6352_set_dscp_prio(struct mv88e6xxx_chip *chip, u8 dscp, + u8 prio) +{ + if (!mv88e6352_has_ip_pri_map(chip)) + return -EOPNOTSUPP; + + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* QPri range as for the PCP-to-priority mapping */ + if (prio >= 4) + return -EINVAL; + + return mv88e6352_write_dscp_qpri(chip, dscp, prio); +} + +static int mv88e6352_del_dscp_prio(struct mv88e6xxx_chip *chip, u8 dscp) +{ + if (!mv88e6352_has_ip_pri_map(chip)) + return -EOPNOTSUPP; + + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* No per-entry disable exists, so restore the reset-default QPri, which + * is the top two DSCP bits (DSCP >> 4: 0x00-0x0f->0, 0x10-0x1f->1, + * 0x20-0x2f->2, 0x30-0x3f->3). + */ + return mv88e6352_write_dscp_qpri(chip, dscp, dscp >> 4); +} + +static int mv88e6390_port_get_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp) +{ + u16 val; + int err; + + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* load the table pointer (Update clear), then read the entry back */ + err = mv88e6xxx_port_write(chip, port, MV88E6390_PORT_IP_PRI_MAP, + FIELD_PREP(MV88E6390_PORT_IP_PRI_MAP_PTR_MASK, + dscp)); + if (err) + return err; + + err = mv88e6xxx_port_read(chip, port, MV88E6390_PORT_IP_PRI_MAP, &val); + if (err) + return err; + + if (val & MV88E6390_PORT_IP_PRI_MAP_DIS_IP_QPRI) + return -EOPNOTSUPP; + + return FIELD_GET(MV88E6390_PORT_IP_PRI_MAP_QPRI_MASK, val); +} + +static int mv88e6390_port_set_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp, u8 prio) +{ + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* QPri range as for the PCP-to-priority mapping */ + if (prio >= 8) + return -EINVAL; + + /* This write defines the whole entry: enable and set the QPri, disable + * the FPri override so frame priority is left unchanged, and leave + * IP_YELLOW clear. The entry is owned solely by this API, so a blind + * write is sufficient and no read-modify-write is needed. + */ + return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_IP_PRI_MAP, + MV88E6390_PORT_IP_PRI_MAP_UPDATE | + MV88E6390_PORT_IP_PRI_MAP_DIS_IP_FPRI | + FIELD_PREP(MV88E6390_PORT_IP_PRI_MAP_PTR_MASK, + dscp) | + FIELD_PREP(MV88E6390_PORT_IP_PRI_MAP_QPRI_MASK, + prio)); +} + +static int mv88e6390_port_del_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp) +{ + if (dscp >= DSCP_MAX) + return -EINVAL; + + /* Disable both the QPri and FPri overrides so the DSCP falls back to the + * port default; get_dscp_prio then reports the entry as removed. + */ + return mv88e6xxx_port_write(chip, port, MV88E6390_PORT_IP_PRI_MAP, + MV88E6390_PORT_IP_PRI_MAP_UPDATE | + MV88E6390_PORT_IP_PRI_MAP_DIS_IP_QPRI | + MV88E6390_PORT_IP_PRI_MAP_DIS_IP_FPRI | + FIELD_PREP(MV88E6390_PORT_IP_PRI_MAP_PTR_MASK, + dscp)); +} + const struct mv88e6xxx_dcb_ops mv88e6352_dcb_ops = { .global_get_pcp_prio = mv88e6352_get_pcp_prio, .global_set_pcp_prio = mv88e6352_set_pcp_prio, + .global_get_dscp_prio = mv88e6352_get_dscp_prio, + .global_set_dscp_prio = mv88e6352_set_dscp_prio, + .global_del_dscp_prio = mv88e6352_del_dscp_prio, }; const struct mv88e6xxx_dcb_ops mv88e6390_dcb_ops = { .port_get_pcp_prio = mv88e6390_port_get_pcp_prio, .port_set_pcp_prio = mv88e6390_port_set_pcp_prio, + .port_get_dscp_prio = mv88e6390_port_get_dscp_prio, + .port_set_dscp_prio = mv88e6390_port_set_dscp_prio, + .port_del_dscp_prio = mv88e6390_port_del_dscp_prio, }; const struct mv88e6xxx_dcb_ops mv88e6393x_dcb_ops = { .port_get_pcp_prio = mv88e6393x_port_get_pcp_prio, .port_set_pcp_prio = mv88e6393x_port_set_pcp_prio, + .port_get_dscp_prio = mv88e6390_port_get_dscp_prio, + .port_set_dscp_prio = mv88e6390_port_set_dscp_prio, + .port_del_dscp_prio = mv88e6390_port_del_dscp_prio, }; static int mv88e6xxx_dcb_get_pcp_prio(struct mv88e6xxx_chip *chip, int port, @@ -227,3 +413,111 @@ int mv88e6xxx_port_del_pcp_prio(struct dsa_switch *ds, int port, u8 pcp, mv88e6xxx_reg_unlock(chip); return err; } + +static int mv88e6xxx_dcb_get_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp) +{ + const struct mv88e6xxx_dcb_ops *dcb_ops = chip->info->ops->dcb_ops; + + if (!dcb_ops) + return -EOPNOTSUPP; + + if (dcb_ops->port_get_dscp_prio) + return dcb_ops->port_get_dscp_prio(chip, port, dscp); + + if (dcb_ops->global_get_dscp_prio) + return dcb_ops->global_get_dscp_prio(chip, dscp); + + return -EOPNOTSUPP; +} + +static int mv88e6xxx_dcb_set_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp, u8 prio) +{ + const struct mv88e6xxx_dcb_ops *dcb_ops = chip->info->ops->dcb_ops; + + if (!dcb_ops) + return -EOPNOTSUPP; + + if (dcb_ops->port_set_dscp_prio) + return dcb_ops->port_set_dscp_prio(chip, port, dscp, prio); + + if (dcb_ops->global_set_dscp_prio) + return dcb_ops->global_set_dscp_prio(chip, dscp, prio); + + return -EOPNOTSUPP; +} + +static int mv88e6xxx_dcb_del_dscp_prio(struct mv88e6xxx_chip *chip, int port, + u8 dscp) +{ + const struct mv88e6xxx_dcb_ops *dcb_ops = chip->info->ops->dcb_ops; + + if (!dcb_ops) + return -EOPNOTSUPP; + + if (dcb_ops->port_del_dscp_prio) + return dcb_ops->port_del_dscp_prio(chip, port, dscp); + + if (dcb_ops->global_del_dscp_prio) + return dcb_ops->global_del_dscp_prio(chip, dscp); + + return -EOPNOTSUPP; +} + +int mv88e6xxx_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_dcb_get_dscp_prio(chip, port, dscp); + mv88e6xxx_reg_unlock(chip); + + return err; +} + +int mv88e6xxx_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + if (prio >= IEEE_8021Q_MAX_PRIORITIES) + return -EINVAL; + + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_dcb_set_dscp_prio(chip, port, dscp, prio); + mv88e6xxx_reg_unlock(chip); + + return err; +} + +int mv88e6xxx_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio) +{ + struct mv88e6xxx_chip *chip = ds->priv; + int err; + + mv88e6xxx_reg_lock(chip); + + err = mv88e6xxx_dcb_get_dscp_prio(chip, port, dscp); + if (err == -EOPNOTSUPP) { + /* No explicit mapping for this DSCP, so nothing to remove */ + err = 0; + goto unlock; + } + if (err < 0) + goto unlock; + if (err != prio) { + /* Mapping was changed in the meantime; leave it alone */ + err = 0; + goto unlock; + } + + err = mv88e6xxx_dcb_del_dscp_prio(chip, port, dscp); + +unlock: + mv88e6xxx_reg_unlock(chip); + return err; +} diff --git a/drivers/net/dsa/mv88e6xxx/dcb.h b/drivers/net/dsa/mv88e6xxx/dcb.h index 7a2124ec23ba9..e1cf92bbd8729 100644 --- a/drivers/net/dsa/mv88e6xxx/dcb.h +++ b/drivers/net/dsa/mv88e6xxx/dcb.h @@ -18,4 +18,10 @@ int mv88e6xxx_port_add_pcp_prio(struct dsa_switch *ds, int port, u8 pcp, int mv88e6xxx_port_del_pcp_prio(struct dsa_switch *ds, int port, u8 pcp, u8 prio); +int mv88e6xxx_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp); +int mv88e6xxx_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); +int mv88e6xxx_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, + u8 prio); + #endif /* _MV88E6XXX_DCB_H_ */ diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 3dbb7a1b8fe11..fdc9795fa5c6d 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -195,6 +195,14 @@ /* Offset 0x19: Core Tag Type */ #define MV88E6185_G1_CORE_TAG_TYPE 0x19 +/* Offset 0x19: IP Mapping Table (6172/6176/6240/6320/6341/6352) */ +#define MV88E6352_G1_IP_PRI_MAP 0x19 +#define MV88E6352_G1_IP_PRI_MAP_UPDATE 0x8000 +#define MV88E6352_G1_IP_PRI_MAP_USE_IP_FPRI 0x4000 +#define MV88E6352_G1_IP_PRI_MAP_PTR_MASK 0x3f00 +#define MV88E6352_G1_IP_PRI_MAP_FPRI_MASK 0x0070 +#define MV88E6352_G1_IP_PRI_MAP_QPRI_MASK 0x0003 + /* Offset 0x1A: Monitor Control */ #define MV88E6185_G1_MONITOR_CTL 0x1a #define MV88E6185_G1_MONITOR_CTL_INGRESS_DEST_MASK 0xf000 diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 56143fa534c16..230a99f404606 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -448,6 +448,16 @@ /* Control for Special LED (Index 0x7 of LED Control on Port 2) */ #define MV88E6XXX_PORT_LED_CONTROL_0x07_P2_PTP_ACT 0 /* bits 6:0 PTP Activity */ +/* Offset 0x17: IP Priority Mapping Table */ +#define MV88E6390_PORT_IP_PRI_MAP 0x17 +#define MV88E6390_PORT_IP_PRI_MAP_UPDATE BIT(15) +#define MV88E6390_PORT_IP_PRI_MAP_PTR_MASK GENMASK(14, 9) +#define MV88E6390_PORT_IP_PRI_MAP_IP_YELLOW BIT(8) +#define MV88E6390_PORT_IP_PRI_MAP_DIS_IP_QPRI BIT(7) +#define MV88E6390_PORT_IP_PRI_MAP_QPRI_MASK GENMASK(6, 4) +#define MV88E6390_PORT_IP_PRI_MAP_DIS_IP_FPRI BIT(3) +#define MV88E6390_PORT_IP_PRI_MAP_FPRI_MASK GENMASK(2, 0) + /* Offset 0x18: IEEE Priority Mapping Table */ #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18 #define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000 -- 2.43.0