net: ethernet: ti: am65-cpsw: enable hw auto ageing
authorGrygorii Strashko <grygorii.strashko@ti.com>
Thu, 10 Sep 2020 20:28:05 +0000 (23:28 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sat, 12 Sep 2020 00:34:39 +0000 (17:34 -0700)
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: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h

index bec47e7..501d676 100644 (file)
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/clk.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
 #include <linux/interrupt.h>
@@ -2038,6 +2039,7 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
        struct am65_cpsw_common *common;
        struct device_node *node;
        struct resource *res;
+       struct clk *clk;
        int ret, i;
 
        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)
        if (!common->ports)
                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);
        ret = pm_runtime_get_sync(dev);
        if (ret < 0) {
@@ -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_regs = common->cpsw_base + AM65_CPSW_NU_ALE_BASE;
        ale_params.dev_id = "am65x-cpsw2g";
+       ale_params.bus_freq = common->bus_freq;
 
        common->ale = cpsw_ale_create(&ale_params);
        if (IS_ERR(common->ale)) {
index 94f666e..993e1d4 100644 (file)
@@ -106,6 +106,7 @@ struct am65_cpsw_common {
 
        u32                     nuss_ver;
        u32                     cpsw_ver;
+       unsigned long           bus_freq;
        bool                    pf_p0_rx_ptype_rrobin;
        struct am65_cpts        *cpts;
        int                     est_enabled;
index 524920a..7b54e99 100644 (file)
@@ -32,6 +32,7 @@
 #define ALE_STATUS             0x04
 #define ALE_CONTROL            0x08
 #define ALE_PRESCALE           0x10
+#define ALE_AGING_TIMER                0x14
 #define ALE_UNKNOWNVLAN                0x18
 #define ALE_TABLE_CONTROL      0x20
 #define ALE_TABLE              0x34
@@ -46,6 +47,9 @@
 
 #define AM65_CPSW_ALE_THREAD_DEF_REG 0x134
 
+/* ALE_AGING_TIMER */
+#define ALE_AGING_TIMER_MASK   GENMASK(23, 0)
+
 enum {
        CPSW_ALE_F_STATUS_REG = BIT(0), /* Status register present */
        CPSW_ALE_F_HW_AUTOAGING = BIT(1), /* HW auto aging */
@@ -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)
 {
        cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
        cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
 
-       timer_setup(&ale->timer, cpsw_ale_timer, 0);
-       if (ale->ageout) {
-               ale->timer.expires = jiffies + ale->ageout;
-               add_timer(&ale->timer);
-       }
+       cpsw_ale_aging_start(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_ENABLE, 0);
 }
index 27b3080..9c6da58 100644 (file)
@@ -25,6 +25,7 @@ struct cpsw_ale_params {
         */
        u32                     major_ver_mask;
        const char              *dev_id;
+       unsigned long           bus_freq;
 };
 
 struct cpsw_ale {