From: Wei Fang <wei.fang@nxp.com>
To: claudiu.manoil@nxp.com, vladimir.oltean@nxp.com,
xiaoning.wang@nxp.com, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, f.fainelli@gmail.com, frank.li@nxp.com,
chleroy@kernel.org, horms@kernel.org, linux@armlinux.org.uk,
maxime.chevallier@bootlin.com, andrew@lunn.ch, olteanv@gmail.com
Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org, imx@lists.linux.dev
Subject: [PATCH v7 net-next 13/15] net: dsa: netc: initialize buffer pool table and implement flow-control
Date: Wed, 13 May 2026 11:04:52 +0800 [thread overview]
Message-ID: <20260513030454.1666570-14-wei.fang@nxp.com> (raw)
In-Reply-To: <20260513030454.1666570-1-wei.fang@nxp.com>
The buffer pool is a quantity of memory available for buffering a group
of flows (e.g. frames having the same priority, frames received from the
same port), while waiting to be transmitted on a port. The buffer pool
tracks internal memory consumption with upper bound limits and optionally
a non-shared portion when associated with a shared buffer pool. Currently
the shared buffer pool is not supported, it will be added in the future.
For i.MX94, the switch has 4 ports and 8 buffer pools, so each port is
allocated two buffer pools. For frames with priorities of 0 to 3, they
will be mapped to the first buffer pool; For frames with priorities of
4 to 7, they will be mapped to the second buffer pool. Each buffer pool
has a flow control on threshold and a flow control off threshold. By
setting these threshold, add the flow control support to each port.
Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
drivers/net/dsa/netc/netc_main.c | 130 ++++++++++++++++++++++++++
drivers/net/dsa/netc/netc_platform.c | 2 +-
drivers/net/dsa/netc/netc_switch.h | 19 ++++
drivers/net/dsa/netc/netc_switch_hw.h | 10 ++
4 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/netc/netc_main.c b/drivers/net/dsa/netc/netc_main.c
index 34b5e655d1c9..7a4064849693 100644
--- a/drivers/net/dsa/netc/netc_main.c
+++ b/drivers/net/dsa/netc/netc_main.c
@@ -212,6 +212,9 @@ static void netc_get_switch_capabilities(struct netc_switch *priv)
val = netc_base_rd(regs, NETC_FDBHTCAPR);
priv->num_fdb_gmac = FIELD_GET(FDBHTCAPR_NUM_GMAC, val);
+
+ val = netc_base_rd(regs, NETC_BPCAPR);
+ priv->num_bp = FIELD_GET(BPCAPR_NUM_BP, val);
}
static int netc_init_all_ports(struct netc_switch *priv)
@@ -403,6 +406,15 @@ static void netc_port_fixed_config(struct netc_port *np)
/* Enable L2 and L3 DOS */
netc_port_rmw(np, NETC_PCR, PCR_L2DOSE | PCR_L3DOSE,
PCR_L2DOSE | PCR_L3DOSE);
+
+ /* Set the quanta value of TX PAUSE frame */
+ netc_mac_port_wr(np, NETC_PM_PAUSE_QUANTA(0), NETC_PAUSE_QUANTA);
+
+ /* When a quanta timer counts down and reaches this value,
+ * the MAC sends a refresh PAUSE frame with the programmed
+ * full quanta value if a pause condition still exists.
+ */
+ netc_mac_port_wr(np, NETC_PM_PAUSE_THRESH(0), NETC_PAUSE_THRESH);
}
static void netc_port_default_config(struct netc_port *np)
@@ -636,6 +648,79 @@ static int netc_add_standalone_fdb_bcast_entry(struct netc_switch *priv)
bcast, NETC_STANDALONE_PVID);
}
+static void netc_port_set_pbpmcr(struct netc_port *np, u64 mapping)
+{
+ u32 pbpmcr0 = lower_32_bits(mapping);
+ u32 pbpmcr1 = upper_32_bits(mapping);
+
+ netc_port_wr(np, NETC_PBPMCR0, pbpmcr0);
+ netc_port_wr(np, NETC_PBPMCR1, pbpmcr1);
+}
+
+static void netc_ipv_to_buffer_pool_mapping(struct netc_switch *priv)
+{
+ int bp_per_port = priv->num_bp / priv->info->num_ports;
+ int q = NETC_IPV_NUM / bp_per_port;
+ int r = NETC_IPV_NUM % bp_per_port;
+ int num = q + r;
+
+ /* IPV-to-buffer-pool mapping per port:
+ * Each port is allocated 'bp_per_port' buffer pools and supports 8
+ * IPVs, where a higher IPV indicates a higher frame priority. Each
+ * IPV can be mapped to only one buffer pool, from hardware design
+ * perspective, bp_per_port will not be greater than 8. So 'q' will
+ * not be 0.
+ *
+ * The mapping rule is as follows:
+ * - The first 'num' IPVs share the port's first buffer pool (index
+ * 'base_id').
+ * - After that, every 'q' IPVs share one buffer pool, with pool
+ * indices increasing sequentially.
+ */
+ for (int i = 0; i < priv->info->num_ports; i++) {
+ u32 base_id = i * bp_per_port;
+ u32 bp_id = base_id;
+ u64 mapping = 0;
+
+ for (int ipv = 0; ipv < NETC_IPV_NUM; ipv++) {
+ /* Update the buffer pool index */
+ if (ipv >= num)
+ bp_id = base_id + ((ipv - num) / q) + 1;
+
+ mapping |= (u64)bp_id << (ipv * 8);
+ }
+
+ netc_port_set_pbpmcr(priv->ports[i], mapping);
+ }
+}
+
+static int netc_switch_bpt_default_config(struct netc_switch *priv)
+{
+ if (priv->num_bp < priv->info->num_ports)
+ return -EINVAL;
+
+ priv->bpt_list = devm_kcalloc(priv->dev, priv->num_bp,
+ sizeof(struct bpt_cfge_data),
+ GFP_KERNEL);
+ if (!priv->bpt_list)
+ return -ENOMEM;
+
+ /* Initialize the maximum threshold of each buffer pool entry */
+ for (int i = 0; i < priv->num_bp; i++) {
+ struct bpt_cfge_data *cfge = &priv->bpt_list[i];
+ int err;
+
+ cfge->max_thresh = cpu_to_le16(NETC_BP_THRESH);
+ err = ntmp_bpt_update_entry(&priv->ntmp, i, cfge);
+ if (err)
+ return err;
+ }
+
+ netc_ipv_to_buffer_pool_mapping(priv);
+
+ return 0;
+}
+
static int netc_setup(struct dsa_switch *ds)
{
struct netc_switch *priv = ds->priv;
@@ -665,6 +750,10 @@ static int netc_setup(struct dsa_switch *ds)
dsa_switch_for_each_available_port(dp, ds)
netc_port_default_config(priv->ports[dp->index]);
+ err = netc_switch_bpt_default_config(priv);
+ if (err)
+ goto free_lock_and_ntmp_user;
+
err = netc_add_standalone_vlan_entry(priv);
if (err)
goto free_lock_and_ntmp_user;
@@ -1218,6 +1307,45 @@ static void netc_port_set_rmii_mii_mac(struct netc_port *np,
netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val);
}
+static void netc_port_set_tx_pause(struct netc_port *np, bool tx_pause)
+{
+ struct netc_switch *priv = np->switch_priv;
+ int port = np->dp->index;
+ int i, j, num_bp;
+
+ num_bp = priv->num_bp / priv->info->num_ports;
+ for (i = 0, j = port * num_bp; i < num_bp; i++, j++) {
+ struct bpt_cfge_data *cfge = &priv->bpt_list[j];
+ struct bpt_cfge_data old_cfge = *cfge;
+
+ if (tx_pause) {
+ cfge->fc_on_thresh = cpu_to_le16(NETC_FC_THRESH_ON);
+ cfge->fc_off_thresh = cpu_to_le16(NETC_FC_THRESH_OFF);
+ cfge->fccfg_sbpen = FIELD_PREP(BPT_FC_CFG,
+ BPT_FC_CFG_EN_BPFC);
+ cfge->fc_ports = cpu_to_le32(BIT(port));
+ } else {
+ cfge->fc_on_thresh = cpu_to_le16(0);
+ cfge->fc_off_thresh = cpu_to_le16(0);
+ cfge->fccfg_sbpen = 0;
+ cfge->fc_ports = cpu_to_le32(0);
+ }
+
+ if (ntmp_bpt_update_entry(&priv->ntmp, j, cfge)) {
+ *cfge = old_cfge;
+ dev_warn(priv->dev,
+ "Failed to %s TX pause of buffer pool %d (swp%d)\n",
+ tx_pause ? "enable" : "disable", j, port);
+ }
+ }
+}
+
+static void netc_port_set_rx_pause(struct netc_port *np, bool rx_pause)
+{
+ netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_PAUSE_IGN,
+ rx_pause ? 0 : PM_CMD_CFG_PAUSE_IGN);
+}
+
static void netc_port_mac_rx_enable(struct netc_port *np)
{
netc_port_rmw(np, NETC_POR, POR_RXDIS, 0);
@@ -1319,6 +1447,8 @@ static void netc_mac_link_up(struct phylink_config *config,
interface == PHY_INTERFACE_MODE_MII)
netc_port_set_rmii_mii_mac(np, speed, duplex);
+ netc_port_set_tx_pause(np, tx_pause);
+ netc_port_set_rx_pause(np, rx_pause);
netc_port_mac_tx_enable(np);
netc_port_mac_rx_enable(np);
}
diff --git a/drivers/net/dsa/netc/netc_platform.c b/drivers/net/dsa/netc/netc_platform.c
index bb4f92d238cb..34aeb6fceb3c 100644
--- a/drivers/net/dsa/netc/netc_platform.c
+++ b/drivers/net/dsa/netc/netc_platform.c
@@ -14,7 +14,7 @@ struct netc_switch_platform {
static void imx94_switch_phylink_get_caps(int port,
struct phylink_config *config)
{
- config->mac_capabilities = MAC_1000FD;
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_1000FD;
switch (port) {
case 0 ... 1:
diff --git a/drivers/net/dsa/netc/netc_switch.h b/drivers/net/dsa/netc/netc_switch.h
index cc278a862623..4a9bf69907e9 100644
--- a/drivers/net/dsa/netc/netc_switch.h
+++ b/drivers/net/dsa/netc/netc_switch.h
@@ -28,11 +28,27 @@
#define NETC_TC_NUM 8
#define NETC_CBDR_NUM 2
+#define NETC_IPV_NUM 8
#define NETC_MAX_FRAME_LEN 9600
#define NETC_STANDALONE_PVID 0
+/* Threshold format: MANT (bits 11:4) * 2^EXP (bits 3:0)
+ * Unit: Memory words (average of 20 bytes each)
+ * NETC_BP_THRESH = 0x334, MANT = 0x33, EXP = 4. Threshold: 816 words
+ * NETC_FC_THRESH_ON = 0x533, MANT = 0x53, EXP = 3. Threshold: 664 words
+ * NETC_FC_THRESH_OFF = 0x3c3, MANT = 0x3c, EXP = 3. Threshold: 480 words
+ */
+#define NETC_BP_THRESH 0x334
+#define NETC_FC_THRESH_ON 0x533
+#define NETC_FC_THRESH_OFF 0x3c3
+
+/* PAUSE quanta: 0xFFFF = 65535 quanta (each quanta = 512 bit times) */
+#define NETC_PAUSE_QUANTA 0xFFFF
+/* PAUSE refresh threshold: send refresh when timer reaches this value */
+#define NETC_PAUSE_THRESH 0xFF00
+
struct netc_switch;
struct netc_switch_info {
@@ -94,6 +110,9 @@ struct netc_switch {
/* Switch hardware capabilities */
u32 num_fdb_gmac;
+ u32 num_bp;
+
+ struct bpt_cfge_data *bpt_list;
};
#define NETC_PRIV(ds) ((struct netc_switch *)((ds)->priv))
diff --git a/drivers/net/dsa/netc/netc_switch_hw.h b/drivers/net/dsa/netc/netc_switch_hw.h
index b04e9866d72a..caf5977c5100 100644
--- a/drivers/net/dsa/netc/netc_switch_hw.h
+++ b/drivers/net/dsa/netc/netc_switch_hw.h
@@ -12,6 +12,12 @@
#define NETC_SWITCH_DEVICE_ID 0xeef2
/* Definition of Switch base registers */
+#define NETC_BPCAPR 0x0008
+#define BPCAPR_NUM_BP GENMASK(7, 0)
+
+#define NETC_PBPMCR0 0x0400
+#define NETC_PBPMCR1 0x0404
+
#define NETC_CBDRMR(a) (0x0800 + (a) * 0x30)
#define NETC_CBDRBAR0(a) (0x0810 + (a) * 0x30)
#define NETC_CBDRBAR1(a) (0x0814 + (a) * 0x30)
@@ -141,6 +147,7 @@ enum netc_stg_stage {
#define NETC_PM_CMD_CFG(a) (0x1008 + (a) * 0x400)
#define PM_CMD_CFG_TX_EN BIT(0)
#define PM_CMD_CFG_RX_EN BIT(1)
+#define PM_CMD_CFG_PAUSE_IGN BIT(8)
#define NETC_PM_MAXFRM(a) (0x1014 + (a) * 0x400)
#define PM_MAXFRAM GENMASK(15, 0)
@@ -149,6 +156,9 @@ enum netc_stg_stage {
#define PM_IEVENT_TX_EMPTY BIT(5)
#define PM_IEVENT_RX_EMPTY BIT(6)
+#define NETC_PM_PAUSE_QUANTA(a) (0x1054 + (a) * 0x400)
+#define NETC_PM_PAUSE_THRESH(a) (0x1064 + (a) * 0x400)
+
#define NETC_PM_IF_MODE(a) (0x1300 + (a) * 0x400)
#define PM_IF_MODE_IFMODE GENMASK(2, 0)
#define IFMODE_MII 1
--
2.34.1
next prev parent reply other threads:[~2026-05-13 3:04 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-13 3:04 [PATCH v7 net-next 00/15] Add preliminary NETC switch support for i.MX94 Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 01/15] dt-bindings: net: dsa: update the description of 'dsa,member' property Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 02/15] dt-bindings: net: dsa: add NETC switch Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 03/15] net: enetc: add pre-boot initialization for i.MX94 switch Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 04/15] net: enetc: add basic operations to the FDB table Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 05/15] net: enetc: add support for the "Add" operation to VLAN filter table Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 06/15] net: enetc: add support for the "Update" operation to buffer pool table Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 07/15] net: enetc: add support for "Add" and "Delete" operations to IPFT Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 08/15] net: enetc: add multiple command BD rings support Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 09/15] net: dsa: add NETC switch tag support Wei Fang
2026-05-14 5:22 ` sashiko-bot
2026-05-15 2:55 ` Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 10/15] net: dsa: netc: introduce NXP NETC switch driver for i.MX94 Wei Fang
2026-05-14 5:57 ` sashiko-bot
2026-05-15 3:36 ` Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 11/15] net: dsa: netc: add phylink MAC operations Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 12/15] net: dsa: netc: add FDB, STP, MTU, port setup and host flooding support Wei Fang
2026-05-14 8:21 ` sashiko-bot
2026-05-13 3:04 ` Wei Fang [this message]
2026-05-14 8:51 ` [PATCH v7 net-next 13/15] net: dsa: netc: initialize buffer pool table and implement flow-control sashiko-bot
2026-05-13 3:04 ` [PATCH v7 net-next 14/15] net: dsa: netc: add support for the standardized counters Wei Fang
2026-05-13 3:04 ` [PATCH v7 net-next 15/15] net: dsa: netc: add support for ethtool private statistics Wei Fang
2026-05-14 10:27 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260513030454.1666570-14-wei.fang@nxp.com \
--to=wei.fang@nxp.com \
--cc=andrew+netdev@lunn.ch \
--cc=andrew@lunn.ch \
--cc=chleroy@kernel.org \
--cc=claudiu.manoil@nxp.com \
--cc=conor+dt@kernel.org \
--cc=davem@davemloft.net \
--cc=devicetree@vger.kernel.org \
--cc=edumazet@google.com \
--cc=f.fainelli@gmail.com \
--cc=frank.li@nxp.com \
--cc=horms@kernel.org \
--cc=imx@lists.linux.dev \
--cc=krzk+dt@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=maxime.chevallier@bootlin.com \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
--cc=robh@kernel.org \
--cc=vladimir.oltean@nxp.com \
--cc=xiaoning.wang@nxp.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.