Commit 23015ff1 authored by Grygorii Strashko's avatar Grygorii Strashko Committed by David S. Miller

net: ethernet: ti: am65-cpsw: enable hw auto ageing

The AM65x ALE supports HW auto-ageing which can be enabled by programming
ageing interval in ALE_AGING_TIMER register. For this CPSW fck_clk
frequency has to be know by ALE.

This patch extends cpsw_ale_params with bus_freq field and enables ALE HW
auto ageing for AM65x CPSW2G ALE version.
Signed-off-by: default avatarGrygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 186f5c99
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* *
*/ */
#include <linux/clk.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -2038,6 +2039,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) ...@@ -2038,6 +2039,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
struct am65_cpsw_common *common; struct am65_cpsw_common *common;
struct device_node *node; struct device_node *node;
struct resource *res; struct resource *res;
struct clk *clk;
int ret, i; int ret, i;
common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL); common = devm_kzalloc(dev, sizeof(struct am65_cpsw_common), GFP_KERNEL);
...@@ -2086,6 +2088,16 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) ...@@ -2086,6 +2088,16 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
if (!common->ports) if (!common->ports)
return -ENOMEM; return -ENOMEM;
clk = devm_clk_get(dev, "fck");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "error getting fck clock %d\n", ret);
return ret;
}
common->bus_freq = clk_get_rate(clk);
pm_runtime_enable(dev); pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev); ret = pm_runtime_get_sync(dev);
if (ret < 0) { if (ret < 0) {
...@@ -2134,6 +2146,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev) ...@@ -2134,6 +2146,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
ale_params.ale_ports = common->port_num + 1; ale_params.ale_ports = common->port_num + 1;
ale_params.ale_regs = common->cpsw_base + AM65_CPSW_NU_ALE_BASE; ale_params.ale_regs = common->cpsw_base + AM65_CPSW_NU_ALE_BASE;
ale_params.dev_id = "am65x-cpsw2g"; ale_params.dev_id = "am65x-cpsw2g";
ale_params.bus_freq = common->bus_freq;
common->ale = cpsw_ale_create(&ale_params); common->ale = cpsw_ale_create(&ale_params);
if (IS_ERR(common->ale)) { if (IS_ERR(common->ale)) {
......
...@@ -106,6 +106,7 @@ struct am65_cpsw_common { ...@@ -106,6 +106,7 @@ struct am65_cpsw_common {
u32 nuss_ver; u32 nuss_ver;
u32 cpsw_ver; u32 cpsw_ver;
unsigned long bus_freq;
bool pf_p0_rx_ptype_rrobin; bool pf_p0_rx_ptype_rrobin;
struct am65_cpts *cpts; struct am65_cpts *cpts;
int est_enabled; int est_enabled;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define ALE_STATUS 0x04 #define ALE_STATUS 0x04
#define ALE_CONTROL 0x08 #define ALE_CONTROL 0x08
#define ALE_PRESCALE 0x10 #define ALE_PRESCALE 0x10
#define ALE_AGING_TIMER 0x14
#define ALE_UNKNOWNVLAN 0x18 #define ALE_UNKNOWNVLAN 0x18
#define ALE_TABLE_CONTROL 0x20 #define ALE_TABLE_CONTROL 0x20
#define ALE_TABLE 0x34 #define ALE_TABLE 0x34
...@@ -46,6 +47,9 @@ ...@@ -46,6 +47,9 @@
#define AM65_CPSW_ALE_THREAD_DEF_REG 0x134 #define AM65_CPSW_ALE_THREAD_DEF_REG 0x134
/* ALE_AGING_TIMER */
#define ALE_AGING_TIMER_MASK GENMASK(23, 0)
enum { enum {
CPSW_ALE_F_STATUS_REG = BIT(0), /* Status register present */ CPSW_ALE_F_STATUS_REG = BIT(0), /* Status register present */
CPSW_ALE_F_HW_AUTOAGING = BIT(1), /* HW auto aging */ CPSW_ALE_F_HW_AUTOAGING = BIT(1), /* HW auto aging */
...@@ -982,21 +986,66 @@ static void cpsw_ale_timer(struct timer_list *t) ...@@ -982,21 +986,66 @@ static void cpsw_ale_timer(struct timer_list *t)
} }
} }
static void cpsw_ale_hw_aging_timer_start(struct cpsw_ale *ale)
{
u32 aging_timer;
aging_timer = ale->params.bus_freq / 1000000;
aging_timer *= ale->params.ale_ageout;
if (aging_timer & ~ALE_AGING_TIMER_MASK) {
aging_timer = ALE_AGING_TIMER_MASK;
dev_warn(ale->params.dev,
"ALE aging timer overflow, set to max\n");
}
writel(aging_timer, ale->params.ale_regs + ALE_AGING_TIMER);
}
static void cpsw_ale_hw_aging_timer_stop(struct cpsw_ale *ale)
{
writel(0, ale->params.ale_regs + ALE_AGING_TIMER);
}
static void cpsw_ale_aging_start(struct cpsw_ale *ale)
{
if (!ale->params.ale_ageout)
return;
if (ale->features & CPSW_ALE_F_HW_AUTOAGING) {
cpsw_ale_hw_aging_timer_start(ale);
return;
}
timer_setup(&ale->timer, cpsw_ale_timer, 0);
ale->timer.expires = jiffies + ale->ageout;
add_timer(&ale->timer);
}
static void cpsw_ale_aging_stop(struct cpsw_ale *ale)
{
if (!ale->params.ale_ageout)
return;
if (ale->features & CPSW_ALE_F_HW_AUTOAGING) {
cpsw_ale_hw_aging_timer_stop(ale);
return;
}
del_timer_sync(&ale->timer);
}
void cpsw_ale_start(struct cpsw_ale *ale) void cpsw_ale_start(struct cpsw_ale *ale)
{ {
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1); cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
timer_setup(&ale->timer, cpsw_ale_timer, 0); cpsw_ale_aging_start(ale);
if (ale->ageout) {
ale->timer.expires = jiffies + ale->ageout;
add_timer(&ale->timer);
}
} }
void cpsw_ale_stop(struct cpsw_ale *ale) void cpsw_ale_stop(struct cpsw_ale *ale)
{ {
del_timer_sync(&ale->timer); cpsw_ale_aging_stop(ale);
cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0); cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
} }
......
...@@ -25,6 +25,7 @@ struct cpsw_ale_params { ...@@ -25,6 +25,7 @@ struct cpsw_ale_params {
*/ */
u32 major_ver_mask; u32 major_ver_mask;
const char *dev_id; const char *dev_id;
unsigned long bus_freq;
}; };
struct cpsw_ale { struct cpsw_ale {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment