Re: [PATCH net-next v1 6/8] net: dsa: realtek: rtl8365mb: add QoS baseline and DCB default priority

From: Oleksij Rempel

Date: Tue Nov 10 2026 - 11:18:46 EST


Hi Linus,

On Tue, Oct 06, 2026 at 01:18:11AM +0200, Linus Walleij wrote:
> Hi Oleksij,
>
> thanks for your patch!
>
> As mentioned I think the main logic of this DCB stuff needs to go into
> common code.
>
> Whether to use rtl83xx.c or create a new file rtl83xx-dcb.c with just this
> QoS DCB stuff is up to you.
>
> Some detailed comments:
>
> On Fri, Oct 2, 2026 at 1:58 PM Oleksij Rempel <o.rempel@xxxxxxxxxxxxxx> wrote:
>
> > +#define RTL8365MB_QOS_QMAP_IDX(_nq) ((_nq) & 0x7)
> > +
> > +/* Internal priority -> queue-id map. Eight tables (one per queue count),
> > + * indexed [table][prio]; four priorities per register, 3-bit qid. An N-queue
> > + * configuration uses table N-1 (1Q is table 0 at 0x0904, 8Q is table 7).
> > + */
> > +#define RTL8365MB_QOS_PRI_TO_QID_TABLE(_nq) ((_nq) - 1)
> > +#define RTL8365MB_QOS_PRI_TO_QID_REG(_t, _pri) \
> > + (0x0904 + ((_t) << 1) + ((_pri) >> 2))
>
> (...)
>
> > +static int rtl8365mb_qos_setup_queues(struct realtek_priv *priv,
> > + unsigned int nq)
> > +{
> > + int table = RTL8365MB_QOS_PRI_TO_QID_TABLE(nq);
>
> Prio to qid table is nq-1
>
> > + struct dsa_switch *ds = &priv->ds;
> > + struct dsa_port *dp;
> > + int tt, ret;
> > +
> > + dsa_switch_for_each_port(dp, ds) {
> > + u32 reg = RTL8365MB_QOS_PORT_QUEUE_NUMBER_REG(dp->index);
> > + int off = RTL8365MB_QOS_PORT_QUEUE_NUMBER_OFFSET(dp->index);
> > +
> > + ret = rtl8365mb_set_field(priv, reg,
> > + rtl8365mb_qos_sel_field_mask(off),
> > + RTL8365MB_QOS_QMAP_IDX(nq));
>
> This sets qos port queue number to RTL8365MB_QOS_QMAP_IDX(nq)
> which is nq & 7.
>
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + for (tt = 0; tt < IEEE8021Q_TT_MAX; tt++) {
> > + u32 reg = RTL8365MB_QOS_PRI_TO_QID_REG(table, tt);
>
> This finds the reg for table nq-1
>
> > + int off = RTL8365MB_QOS_PRI_TO_QID_OFFSET(tt);
> > + int tc = ieee8021q_tt_to_tc(tt, nq);
> > +
> > + if (tc < 0)
> > + return tc;
> > +
> > + /* QID field holds the traffic class */
> > + ret = rtl8365mb_set_field(priv, reg,
> > + rtl8365mb_qos_sel_field_mask(off), tc);
> > + if (ret)
> > + return ret;
> > + }
>
> And sets it.
>
> In a code dump I have for RTL8370 writes the same index into both registers,
> so something like:
>
> {
> int qmapidx = RTL8365MB_QOS_QMAP_IDX(nq);
>
> ret = rtl8365mb_set_field(priv, reg,
> rtl8365mb_qos_sel_field_mask(off),
> qmapidx);
>
> (...)
>
> for (tt = 0; tt < IEEE8021Q_TT_MAX; tt++) {
> u32 reg = RTL8365MB_QOS_PRI_TO_QID_REG(qmapidx, tt);
> int off = RTL8365MB_QOS_PRI_TO_QID_OFFSET(tt);
> int tc = ieee8021q_tt_to_tc(tt, nq);
> (...)
>
> So we compute index once and use it both for port selection and
> table programming.
>
> Can you check this in the RTL8365MB manual, this old RTL8370 code
> could be wrong but it's also the way RTL8366RB does things.

Hm... good question. After some investigation, I think the register
naming is misleading, and maybe the RTL8370 code too.

The switch has a pool of 8 prio-to-qid tables, and the 3-bit port
field is an index into it, not a queue count: field value v selects
table v - 1, and 0 selects table 7. I tested all field values on HW
(RTL8365MB-VC). The field does not limit the queues either: with field
value 1 the port uses whatever queues table 0 holds.

See e.g.:
https://github.com/openwrt/openwrt/blob/c46ccb69d17e584479df849a107423175a143c83/target/linux/mediatek/files-5.10/drivers/net/phy/rtk/rtl8367c/qos.c#L45-L92

In v2 I'll treat the field as a table index.

Best Regards,
Oleksij
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |