[PATCH net-next 7/9] net: ethernet: ti: cpsw_ale: add policer save restore for PM sleep

From: Roger Quadros
Date: Wed Mar 19 2025 - 09:44:56 EST


On some K3 platforms CPSW context is lost during PM sleep.

Add cpsw_ale_policer_save() and cpsw_ale_policer_restore() helpers.

In am65-cpsw driver, save the policer context during PM suspend and
restore it during PM resume.

Signed-off-by: Roger Quadros <rogerq@xxxxxxxxxx>
---
drivers/net/ethernet/ti/am65-cpsw-nuss.c | 23 ++++++++++++++---
drivers/net/ethernet/ti/am65-cpsw-nuss.h | 1 +
drivers/net/ethernet/ti/cpsw_ale.c | 42 ++++++++++++++++++++++++++++++++
drivers/net/ethernet/ti/cpsw_ale.h | 4 +++
4 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
index 1c0eedf884ce..405944013521 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
@@ -3487,7 +3487,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
struct device_node *node;
struct resource *res;
struct clk *clk;
- int ale_entries;
+ int tbl_entries;
__be64 id_temp;
int ret, i;

@@ -3590,10 +3590,25 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
goto err_of_clear;
}

- ale_entries = common->ale->params.ale_entries;
+ tbl_entries = common->ale->params.ale_entries;
common->ale_context = devm_kzalloc(dev,
- ale_entries * ALE_ENTRY_WORDS * sizeof(u32),
+ tbl_entries * ALE_ENTRY_WORDS * sizeof(u32),
GFP_KERNEL);
+ if (!common->ale_context) {
+ ret = -ENOMEM;
+ goto err_of_clear;
+ }
+
+ tbl_entries = common->ale->params.num_policers;
+ i = CPSW_ALE_POLICER_ENTRY_WORDS + 1; /* 8 CFG + 1 Thread_val */
+ i *= tbl_entries; /* for all policers */
+ i += 1; /* thread_def register */
+ common->policer_context = devm_kzalloc(dev, i * sizeof(u32), GFP_KERNEL);
+ if (!common->policer_context) {
+ ret = -ENOMEM;
+ goto err_of_clear;
+ }
+
ret = am65_cpsw_init_cpts(common);
if (ret)
goto err_of_clear;
@@ -3677,6 +3692,7 @@ static int am65_cpsw_nuss_suspend(struct device *dev)
int i, ret;

cpsw_ale_dump(common->ale, common->ale_context);
+ cpsw_ale_policer_save(common->ale, common->policer_context);
host_p->vid_context = readl(host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
for (i = 0; i < common->port_num; i++) {
port = &common->ports[i];
@@ -3754,6 +3770,7 @@ static int am65_cpsw_nuss_resume(struct device *dev)

writel(host_p->vid_context, host_p->port_base + AM65_CPSW_PORT_VLAN_REG_OFFSET);
cpsw_ale_restore(common->ale, common->ale_context);
+ cpsw_ale_policer_restore(common->ale, common->policer_context);

return 0;
}
diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.h b/drivers/net/ethernet/ti/am65-cpsw-nuss.h
index 917c37e4e89b..61daa5db12e6 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.h
+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.h
@@ -190,6 +190,7 @@ struct am65_cpsw_common {
unsigned char switch_id[MAX_PHYS_ITEM_ID_LEN];
/* only for suspend/resume context restore */
u32 *ale_context;
+ u32 *policer_context;
};

struct am65_cpsw_ndev_priv {
diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c
index f5ca18d4ea6a..48592441085a 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.c
+++ b/drivers/net/ethernet/ti/cpsw_ale.c
@@ -1830,3 +1830,45 @@ int cpsw_ale_policer_set_entry(struct cpsw_ale *ale, u32 policer_idx,

return 0;
}
+
+void cpsw_ale_policer_save(struct cpsw_ale *ale, u32 *data)
+{
+ int i, idx;
+
+ for (idx = 0; idx < ale->params.num_policers; idx++) {
+ cpsw_ale_policer_read_idx(ale, idx);
+
+ for (i = 0; i < CPSW_ALE_POLICER_ENTRY_WORDS; i++)
+ data[i] = readl_relaxed(ale->params.ale_regs +
+ ALE_POLICER_PORT_OUI + 4 * i);
+
+ regmap_field_write(ale->fields[ALE_THREAD_CLASS_INDEX], idx);
+ data[i++] = readl_relaxed(ale->params.ale_regs +
+ ALE_THREAD_VAL);
+ data += i * 4;
+ }
+
+ data[0] = readl_relaxed(ale->params.ale_regs + ALE_THREAD_DEF);
+}
+
+void cpsw_ale_policer_restore(struct cpsw_ale *ale, u32 *data)
+{
+ int i, idx;
+
+ for (idx = 0; idx < ale->params.num_policers; idx++) {
+ cpsw_ale_policer_read_idx(ale, idx);
+
+ for (i = 0; i < CPSW_ALE_POLICER_ENTRY_WORDS; i++)
+ writel_relaxed(data[i], ale->params.ale_regs +
+ ALE_POLICER_PORT_OUI + 4 * i);
+
+ cpsw_ale_policer_write_idx(ale, idx);
+
+ regmap_field_write(ale->fields[ALE_THREAD_CLASS_INDEX], idx);
+ writel_relaxed(data[i++], ale->params.ale_regs +
+ ALE_THREAD_VAL);
+ data += i * 4;
+ }
+
+ writel_relaxed(data[0], ale->params.ale_regs + ALE_THREAD_DEF);
+}
diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h
index 11d333bf5a52..dbc095397389 100644
--- a/drivers/net/ethernet/ti/cpsw_ale.h
+++ b/drivers/net/ethernet/ti/cpsw_ale.h
@@ -171,6 +171,8 @@ enum cpsw_ale_port_state {
#define CPSW_ALE_POLICER_MATCH_IPSRC BIT(8)
#define CPSW_ALE_POLICER_MATCH_IPDST BIT(9)

+#define CPSW_ALE_POLICER_ENTRY_WORDS 8
+
struct cpsw_ale_policer_cfg {
u32 match_flags;
u16 ether_type;
@@ -227,5 +229,7 @@ int cpsw_ale_policer_set_entry(struct cpsw_ale *ale, u32 policer_idx,
struct cpsw_ale_policer_cfg *cfg);
void cpsw_ale_policer_clr_entry(struct cpsw_ale *ale, u32 policer_idx,
struct cpsw_ale_policer_cfg *cfg);
+void cpsw_ale_policer_save(struct cpsw_ale *ale, u32 *data);
+void cpsw_ale_policer_restore(struct cpsw_ale *ale, u32 *data);

#endif

--
2.34.1