mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-21 22:04:43 +04:00
qualcommax: qca_ppe: flush a port's queues on port_disable
Disabling a port leaves its queued frames holding buffers charged to the admission and shared groups, so every other port is admitted against a smaller pool. On port_disable, stop each unicast and multicast queue's enqueue and dequeue, flush it through FLUSH_CFG and wait for completion, one queue at a time with the QM clock gate dropped around the pass. The gates stay closed until port_enable, and the CPU port is skipped. On IPQ8074 a full queue drains to empty when its port goes down and fills again once the port is back up. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de> Link: https://github.com/openwrt/openwrt/pull/25700 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
1 parent
cfaf5b3e4c
commit
478da5ef34
3 files changed
+95
No files matched your search
@@ -19,6 +19,9 @@
|
||||
#define QCA_PPE_MAX_BRIDGES 8
|
||||
|
||||
/* --- Global --- */
|
||||
#define PPE_CLK_GATING_CTRL 0x8
|
||||
#define PPE_QM_CLK_GATE_EN BIT(4)
|
||||
|
||||
/* GMAC LPI: an enable bit per port from port 1, a wake and an idle timer per
|
||||
* port in microseconds, and the PPE clock cycles that make a microsecond.
|
||||
* qca-ssdk uses the same layout for IPQ807x and IPQ60xx.
|
||||
@@ -611,6 +614,9 @@
|
||||
|
||||
#define PPE_TM_RING_Q_MAP(r) (PPE_TM_BASE + 0x2a000 + (r) * 0x40)
|
||||
|
||||
#define PPE_TM_DEQ_DIS(q) (PPE_TM_BASE + 0x30000 + (q) * 0x10)
|
||||
#define PPE_DEQ_DIS BIT(0)
|
||||
|
||||
#define PPE_TM_L1_FLOW_MAP(i) (PPE_TM_BASE + 0x40000 + (i) * 0x10)
|
||||
#define PPE_L1_SP_ID GENMASK(3, 0)
|
||||
#define PPE_L1_C_PRI GENMASK(6, 4)
|
||||
@@ -711,6 +717,20 @@
|
||||
/* --- Queue Manager (base 0x800000) --- */
|
||||
#define PPE_QM_BASE 0x800000
|
||||
|
||||
/* Draining a queue: the gate that stops it filling, and the flush that hands
|
||||
* its buffers back to the group. FLUSH_BUSY is written to start the flush and
|
||||
* the hardware clears it; FLUSH_STATUS says whether it worked.
|
||||
*/
|
||||
#define PPE_QM_FLUSH_CFG (PPE_QM_BASE + 0x0)
|
||||
#define PPE_FLUSH_QID GENMASK(8, 0)
|
||||
#define PPE_FLUSH_STATUS BIT(10)
|
||||
#define PPE_FLUSH_DST_PORT GENMASK(23, 21)
|
||||
#define PPE_FLUSH_ALL_QUEUES BIT(24)
|
||||
#define PPE_FLUSH_BUSY BIT(31)
|
||||
|
||||
#define PPE_QM_ENQ_OPR(q) (PPE_QM_BASE + 0x5c000 + (q) * 0x10)
|
||||
#define PPE_ENQ_DISABLE BIT(0)
|
||||
|
||||
#define PPE_QM_UCAST_MAP(i) (PPE_QM_BASE + 0x10000 + (i) * 0x10)
|
||||
#define PPE_QM_PROFILE_ID GENMASK(3, 0)
|
||||
#define PPE_QM_QUEUE_ID GENMASK(11, 4)
|
||||
@@ -1045,6 +1065,7 @@ struct flow_cls_offload;
|
||||
#define PPE_DEVLINK_SB 0
|
||||
|
||||
void ppe_scheduler_init(struct qca_ppe_priv *priv);
|
||||
void ppe_port_queues_enable(struct qca_ppe_priv *priv, int port, bool en);
|
||||
int qca_ppe_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type,
|
||||
void *type_data);
|
||||
int qca_ppe_devlink_sb_setup(struct dsa_switch *ds);
|
||||
|
||||
@@ -769,6 +769,8 @@ static int qca_ppe_port_enable(struct dsa_switch *ds, int port,
|
||||
{
|
||||
struct qca_ppe_priv *priv = ds_to_priv(ds);
|
||||
|
||||
ppe_port_queues_enable(priv, port, true);
|
||||
|
||||
/* A user port's gate is opened by qca_ppe_mac_link_up() once its MAC
|
||||
* is up. DSA calls this before phylink_start(), so opening it here
|
||||
* would aim the fabric at a MAC that is still down and about to be
|
||||
@@ -785,6 +787,7 @@ static void qca_ppe_port_disable(struct dsa_switch *ds, int port)
|
||||
struct qca_ppe_priv *priv = ds_to_priv(ds);
|
||||
|
||||
ppe_port_bridge_txmac_set(priv, port, false);
|
||||
ppe_port_queues_enable(priv, port, false);
|
||||
}
|
||||
|
||||
static struct qca_ppe_bridge_vsi *
|
||||
|
||||
@@ -907,6 +907,77 @@ static void ppe_l0_scheduler_init(struct qca_ppe_priv *priv)
|
||||
}
|
||||
}
|
||||
|
||||
static void ppe_queues_gate(struct qca_ppe_priv *priv, u16 base, u8 count,
|
||||
bool en)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
regmap_write(priv->regmap, PPE_QM_ENQ_OPR(base + i),
|
||||
en ? 0 : PPE_ENQ_DISABLE);
|
||||
regmap_write(priv->regmap, PPE_TM_DEQ_DIS(base + i),
|
||||
en ? 0 : PPE_DEQ_DIS);
|
||||
}
|
||||
}
|
||||
|
||||
/* The flush is refused unless the queue's enqueue is already stopped; the
|
||||
* vendor stops its dequeue with it.
|
||||
*/
|
||||
static void ppe_queue_flush(struct qca_ppe_priv *priv, int port, u16 queue)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
regmap_update_bits(priv->regmap, PPE_QM_FLUSH_CFG,
|
||||
PPE_FLUSH_QID | PPE_FLUSH_DST_PORT |
|
||||
PPE_FLUSH_ALL_QUEUES | PPE_FLUSH_BUSY,
|
||||
FIELD_PREP(PPE_FLUSH_QID, queue) |
|
||||
FIELD_PREP(PPE_FLUSH_DST_PORT, port) |
|
||||
PPE_FLUSH_BUSY);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv->regmap, PPE_QM_FLUSH_CFG, val,
|
||||
!(val & PPE_FLUSH_BUSY), 10, 10000);
|
||||
if (ret || !(val & PPE_FLUSH_STATUS))
|
||||
dev_warn(priv->ds.dev, "port %d: queue %u did not flush\n",
|
||||
port, queue);
|
||||
}
|
||||
|
||||
/* What a port that goes down leaves behind: the frames already queued for it
|
||||
* stay charged to its admission group, and with the fabric gated nothing
|
||||
* dequeues them. Stop each of the port's queues at both ends and flush it, and
|
||||
* the buffers come back at once; port_enable is what reopens the gates.
|
||||
*/
|
||||
void ppe_port_queues_enable(struct qca_ppe_priv *priv, int port, bool en)
|
||||
{
|
||||
const struct port_l0_params *p = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(port_l0); i++)
|
||||
if (port_l0[i].port == port)
|
||||
p = &port_l0[i];
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
ppe_queues_gate(priv, p->ucast_base, p->ucast_count, en);
|
||||
ppe_queues_gate(priv, p->mcast_base, p->mcast_count, en);
|
||||
|
||||
if (en)
|
||||
return;
|
||||
|
||||
/* The vendor drops the QM clock gate around its own flush to
|
||||
* accelerate it, and restores it after.
|
||||
*/
|
||||
regmap_clear_bits(priv->regmap, PPE_CLK_GATING_CTRL,
|
||||
PPE_QM_CLK_GATE_EN);
|
||||
|
||||
for (i = 0; i < p->ucast_count; i++)
|
||||
ppe_queue_flush(priv, port, p->ucast_base + i);
|
||||
for (i = 0; i < p->mcast_count; i++)
|
||||
ppe_queue_flush(priv, port, p->mcast_base + i);
|
||||
|
||||
regmap_set_bits(priv->regmap, PPE_CLK_GATING_CTRL, PPE_QM_CLK_GATE_EN);
|
||||
}
|
||||
|
||||
static void ppe_edma_ring_map_init(struct qca_ppe_priv *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
Reference in new issue
Block a user