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