[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250927070724.734933-5-lukeh@padl.com>
Date: Sat, 27 Sep 2025 17:07:07 +1000
From: Luke Howard <lukeh@...l.com>
To: netdev@...r.kernel.org
Cc: andrew@...n.ch, vladimir.oltean@....com, kieran@...nda.com,
jcschroeder@...il.com, max@...tershome.org,
Luke Howard <lukeh@...l.com>
Subject: [RFC net-next 4/5] net: dsa: mv88e6xxx: CBS support
Add support for the 802.1Qav Credit Based Shaper (CBS) to Marvell switches
that support AVB. CBS policies can be configured per-port, but are subject
to the global policy limitations imposed by the Marvell MQPRIO
implementation.
Signed-off-by: Luke Howard <lukeh@...l.com>
---
drivers/net/dsa/mv88e6xxx/avb.c | 115 ++++++++++++++++++++++++++++++-
drivers/net/dsa/mv88e6xxx/avb.h | 33 +++++++++
drivers/net/dsa/mv88e6xxx/chip.c | 27 ++++++++
drivers/net/dsa/mv88e6xxx/chip.h | 2 +
4 files changed, 176 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/mv88e6xxx/avb.c b/drivers/net/dsa/mv88e6xxx/avb.c
index 361e7ff821567..513e0504735a6 100644
--- a/drivers/net/dsa/mv88e6xxx/avb.c
+++ b/drivers/net/dsa/mv88e6xxx/avb.c
@@ -107,7 +107,16 @@ static int mv88e6xxx_tc_disable(struct mv88e6xxx_chip *chip)
return chip->info->ops->tc_ops->tc_disable(chip);
}
-/* MQPRIO helpers */
+/* MQPRIO and CBS helpers */
+
+int mv88e6xxx_qav_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ if (!chip->info->ops->tc_ops->set_port_cbs_qopt)
+ return -EOPNOTSUPP;
+
+ return chip->info->ops->tc_ops->set_port_cbs_qopt(chip, port, cbs_qopt);
+}
/* Set the AVB global policy limit registers. Caller must acquired register
* lock.
@@ -330,6 +339,26 @@ int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip)
return 0;
}
+static int mv88e6xxx_qav_set_port_config(struct mv88e6xxx_chip *chip, int port,
+ int queue, u16 rate, u16 hilimit)
+{
+ int err;
+
+ err = mv88e6xxx_port_qav_write(chip, port,
+ MV88E6XXX_PORT_QAV_CFG_RATE(queue),
+ rate);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_port_qav_write(chip, port,
+ MV88E6XXX_PORT_QAV_CFG_HI_LIMIT(queue),
+ hilimit);
+ if (err)
+ return err;
+
+ return 0;
+}
+
/* Assign FPri to QPri mappings for each traffic class
*
* @param chip Marvell switch chip instance
@@ -456,14 +485,77 @@ static int mv88e6352_tc_disable(struct mv88e6xxx_chip *chip)
return 0;
}
+static int mv88e6341_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ u16 rate, hilimit;
+
+ if (cbs_qopt->enable) {
+ rate = DIV_ROUND_UP(cbs_qopt->idleslope, MV88E6341_AVB_CFG_RATE_UNITS);
+ rate = clamp_t(u16, rate, 1, MV88E6341_AVB_CFG_RATE_MASK);
+
+ hilimit = cbs_qopt->hicredit;
+ hilimit = clamp_t(u16, hilimit, 1, MV88E6341_AVB_CFG_HI_LIMIT_MASK);
+ } else {
+ rate = 0;
+ hilimit = MV88E6341_AVB_CFG_HI_LIMIT_MASK;
+ }
+
+ return mv88e6xxx_qav_set_port_config(chip, port, cbs_qopt->queue,
+ rate, hilimit);
+}
+
const struct mv88e6xxx_tc_ops mv88e6341_tc_ops = {
.tc_enable = mv88e6352_tc_enable,
.tc_disable = mv88e6352_tc_disable,
+ .set_port_cbs_qopt = mv88e6341_set_port_cbs_qopt,
};
+static int mv88e6352_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ u16 rate, hilimit;
+ u16 cfg;
+ int err;
+
+ if (cbs_qopt->enable) {
+ rate = DIV_ROUND_UP(cbs_qopt->idleslope, MV88E6352_AVB_CFG_RATE_UNITS);
+ rate = clamp_t(u16, rate, 1, MV88E6352_AVB_CFG_RATE_MASK);
+
+ hilimit = cbs_qopt->hicredit;
+ hilimit = clamp_t(u16, hilimit, 1, MV88E6352_AVB_CFG_HI_LIMIT_MASK);
+ } else {
+ rate = 0;
+ hilimit = MV88E6352_AVB_CFG_HI_LIMIT_MASK;
+ }
+
+ err = mv88e6xxx_qav_set_port_config(chip, port, cbs_qopt->queue,
+ rate, hilimit);
+ if (err)
+ return err;
+
+ /* Set undocumented enable register */
+
+ err = mv88e6xxx_port_qav_read(chip, port, MV88E6352_PORT_QAV_CFG, &cfg, 1);
+ if (err)
+ return err;
+
+ if (cbs_qopt->enable)
+ cfg |= MV88E6352_PORT_QAV_CFG_ENABLE;
+ else
+ cfg &= ~(MV88E6352_PORT_QAV_CFG_ENABLE);
+
+ err = mv88e6xxx_port_qav_write(chip, port, MV88E6352_PORT_QAV_CFG, cfg);
+ if (err)
+ return err;
+
+ return 0;
+}
+
const struct mv88e6xxx_tc_ops mv88e6352_tc_ops = {
.tc_enable = mv88e6352_tc_enable,
.tc_disable = mv88e6352_tc_disable,
+ .set_port_cbs_qopt = mv88e6352_set_port_cbs_qopt,
};
static inline u16 mv88e6390_avb_pri_map_to_reg(const struct mv88e6xxx_avb_priority_map map[])
@@ -544,7 +636,28 @@ static int mv88e6390_tc_disable(struct mv88e6xxx_chip *chip)
return err;
}
+static int mv88e6390_set_port_cbs_qopt(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ u16 rate, hilimit;
+
+ if (cbs_qopt->enable) {
+ rate = DIV_ROUND_UP(cbs_qopt->idleslope, MV88E6390_AVB_CFG_RATE_UNITS);
+ rate = clamp_t(u16, rate, 1, MV88E6390_AVB_CFG_RATE_MASK);
+
+ hilimit = cbs_qopt->hicredit;
+ hilimit = clamp_t(u16, hilimit, 1, MV88E6390_AVB_CFG_HI_LIMIT_MASK);
+ } else {
+ rate = 0;
+ hilimit = MV88E6390_AVB_CFG_HI_LIMIT_MASK;
+ }
+
+ return mv88e6xxx_qav_set_port_config(chip, port, cbs_qopt->queue,
+ rate, hilimit);
+}
+
const struct mv88e6xxx_tc_ops mv88e6390_tc_ops = {
.tc_enable = mv88e6390_tc_enable,
.tc_disable = mv88e6390_tc_disable,
+ .set_port_cbs_qopt = mv88e6390_set_port_cbs_qopt,
};
diff --git a/drivers/net/dsa/mv88e6xxx/avb.h b/drivers/net/dsa/mv88e6xxx/avb.h
index d049e30c5c0e2..b83db1e9878bf 100644
--- a/drivers/net/dsa/mv88e6xxx/avb.h
+++ b/drivers/net/dsa/mv88e6xxx/avb.h
@@ -94,6 +94,9 @@
#define MV88E6XXX_AVB_CFG_OUI_HI 0x0C
#define MV88E6XXX_AVB_CFG_OUI_LO 0x0D
+#define MV88E6XXX_PORT_QAV_CFG_RATE(queue) (((queue) & 0x7) << 1)
+#define MV88E6XXX_PORT_QAV_CFG_HI_LIMIT(queue) ((((queue) & 0x7) << 1) + 1)
+
/* 6352 Family AVB Global Config (4 TX queues) */
#define MV88E6352_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
@@ -110,6 +113,18 @@
#define MV88E6352_AVB_CFG_AVB_LO_QPRI_GET(p) FIELD_GET(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
#define MV88E6352_AVB_CFG_AVB_LO_QPRI_SET(p) FIELD_PREP(MV88E6352_AVB_CFG_AVB_LO_QPRI_MASK, p)
+#define MV88E6352_AVB_CFG_RATE_UNITS 32 /* 32Kbps */
+#define MV88E6352_AVB_CFG_RATE_MASK GENMASK(14, 0) /* 1Gbps */
+#define MV88E6352_AVB_CFG_HI_LIMIT_MASK GENMASK(14, 0) /* 32k */
+
+#define MV88E6352_PORT_QAV_CFG 0x08
+#define MV88E6352_PORT_QAV_CFG_ENABLE 0x8000
+
+/* 6341 Family AVB Global Config (4 TX queues) */
+#define MV88E6341_AVB_CFG_RATE_UNITS 64 /* 64Kbps */
+#define MV88E6341_AVB_CFG_RATE_MASK GENMASK(15, 0) /* 4Gbps */
+#define MV88E6341_AVB_CFG_HI_LIMIT_MASK GENMASK(13, 0) /* 16k */
+
/* 6390 Family AVB Global Config (8 TX queues) */
#define MV88E6390_AVB_CFG_AVB_HI_FPRI_GET(p) MV88E6XXX_AVB_CFG_AVB_HI_FPRI_GET(p)
@@ -126,6 +141,10 @@
#define MV88E6390_AVB_CFG_AVB_LO_QPRI_GET(p) FIELD_GET(MV88E6390_AVB_CFG_AVB_LO_QPRI_MASK, p)
#define MV88E6390_AVB_CFG_AVB_LO_QPRI_SET(p) FIELD_PREP(MV88E6390_AVB_CFG_AVB_LO_QPRI_MASK, p)
+#define MV88E6390_AVB_CFG_RATE_UNITS 64 /* 64Kbps */
+#define MV88E6390_AVB_CFG_RATE_MASK GENMASK(15, 0) /* 4Gbps */
+#define MV88E6390_AVB_CFG_HI_LIMIT_MASK GENMASK(13, 0) /* 16k */
+
#define MV88E6352_AVB_QUEUE_MIN(tc) (tc)
#define MV88E6352_AVB_QUEUE_MAX(tc) ((tc) + 1)
@@ -194,6 +213,20 @@ int mv88e6xxx_avb_tc_enable(struct mv88e6xxx_chip *chip,
*/
int mv88e6xxx_avb_tc_disable(struct mv88e6xxx_chip *chip);
+struct tc_cbs_qopt_offload;
+
+/* Set AVB credit based shaper policy. Caller must acquire register lock.
+ *
+ * @param chip Marvell switch chip instance
+ * @param port Switch port
+ * @param cbs_qopt CBS policy to apply
+ *
+ * @return 0 on success, or a negative error value otherwise
+ */
+int mv88e6xxx_qav_set_port_cbs_qopt(struct mv88e6xxx_chip *chip,
+ int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt);
+
/* The MAAP address range is 91:E0:F0:00:00:00 thru 91:E0:F0:00:FF:FF
* (IEEE 1722 Annex D)
*/
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 6ba2179d1c4ee..16f41604b932b 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -7003,6 +7003,31 @@ static int mv88e6xxx_qos_port_mqprio(struct dsa_switch *ds, int port,
return err;
}
+static int mv88e6xxx_qos_port_cbs_set(struct dsa_switch *ds, int port,
+ struct tc_cbs_qopt_offload *cbs_qopt)
+{
+ struct mv88e6xxx_chip *chip = ds->priv;
+ int err;
+
+ if (cbs_qopt->queue >= chip->info->num_tx_queues) {
+ dev_info(ds->dev, "p%d: invalid AVB queue %d\n", port, cbs_qopt->queue);
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->reg_lock);
+
+ err = mv88e6xxx_qav_set_port_cbs_qopt(chip, port, cbs_qopt);
+
+ mutex_unlock(&chip->reg_lock);
+
+ if (err) {
+ dev_info(ds->dev, "p%d: failed to %s AVB CBS policy: %d\n",
+ port, cbs_qopt->enable ? "enable" : "disable", err);
+ }
+
+ return err;
+}
+
static int mv88e6xxx_port_setup_tc(struct dsa_switch *ds, int port,
enum tc_setup_type type,
void *type_data)
@@ -7017,6 +7042,8 @@ static int mv88e6xxx_port_setup_tc(struct dsa_switch *ds, int port,
return mv88e6xxx_qos_query_caps(type_data);
case TC_SETUP_QDISC_MQPRIO:
return mv88e6xxx_qos_port_mqprio(ds, port, type_data);
+ case TC_SETUP_QDISC_CBS:
+ return mv88e6xxx_qos_port_cbs_set(ds, port, type_data);
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
index 71e536fbd2d24..6bbfb503b237f 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.h
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
@@ -833,6 +833,8 @@ struct mv88e6xxx_tc_ops {
int (*tc_enable)(struct mv88e6xxx_chip *chip,
const struct mv88e6xxx_avb_tc_policy *policy);
int (*tc_disable)(struct mv88e6xxx_chip *chip);
+ int (*set_port_cbs_qopt)(struct mv88e6xxx_chip *chip, int port,
+ const struct tc_cbs_qopt_offload *cbs_qopt);
};
static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip)
--
2.43.0
Powered by blists - more mailing lists