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

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
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -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)) {
+1 −0
Original line number Diff line number Diff line
@@ -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;
+55 −6
Original line number Diff line number Diff line
@@ -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)
	}
}

void cpsw_ale_start(struct cpsw_ale *ale)
static void cpsw_ale_hw_aging_timer_start(struct cpsw_ale *ale)
{
	cpsw_ale_control_set(ale, 0, ALE_ENABLE, 1);
	cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1);
	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);
	if (ale->ageout) {
	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);

	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);
}
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ struct cpsw_ale_params {
	 */
	u32			major_ver_mask;
	const char		*dev_id;
	unsigned long		bus_freq;
};

struct cpsw_ale {