Commit 1cf814a0 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-ethernet-ti-ale-add-static-configuration'

Grygorii Strashko says:

====================
net: ethernet: ti: ale: add static configuration

As existing, as newly introduced CPSW ALE versions have differences in
supported features and ALE table formats. Especially it's actual for the
recent AM65x/J721E/J7200 and future AM64x SoCs, which supports more
features like: auto-aging, classifiers, Link aggregation, additional HW
filtering, etc.

The existing ALE configuration interface is not practical in terms of
adding new features and requires consumers to program a lot static
parameters. And any attempt to add new features will case endless adding
and maintaining different combination of flags and options. Because CPSW
ALE configuration is static and fixed for SoC (or set of SoC), It is
reasonable to add support for static ALE configurations inside ALE module.

This series introduces static ALE configuration table for different ALE
variants and provides option for consumers to select required ALE
configuration by providing ALE const char *dev_id identifier (Patch 2).
And all existing driver have been switched to use new approach (Patches 3-6).

After this ALE HW auto-ageing feature can be enabled for AM65x CPSW ALE
variant (Patch 7).

Finally, Patches 8-9 introduces tables to describe the ALE VLAN entries
fields as the ALE VLAN entries are too much differ between different TI
CPSW ALE versions. So, handling them using flags, defines and get/set
functions are became over-complicated.

Patch 1 - is preparation patch

Changes in v3:
- fixed comment for Patch 2

Changes in v2:
- fixed sparse warnings

v2: https://lore.kernel.org/patchwork/cover/1301684/
v1: https://lore.kernel.org/patchwork/cover/1301048/


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c4f33417 06c5c912
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -572,13 +572,14 @@ static int am65_cpsw_nway_reset(struct net_device *ndev)
static int am65_cpsw_get_regs_len(struct net_device *ndev)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	u32 i, regdump_len = 0;
	u32 ale_entries, i, regdump_len = 0;

	ale_entries = cpsw_ale_get_num_entries(common->ale);
	for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) {
		if (am65_cpsw_regdump[i].hdr.module_id ==
		    AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) {
			regdump_len += sizeof(struct am65_cpsw_regdump_hdr);
			regdump_len += common->ale->params.ale_entries *
			regdump_len += ale_entries *
				       ALE_ENTRY_WORDS * sizeof(u32);
			continue;
		}
@@ -592,10 +593,11 @@ static void am65_cpsw_get_regs(struct net_device *ndev,
			       struct ethtool_regs *regs, void *p)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	u32 i, j, pos, *reg = p;
	u32 ale_entries, i, j, pos, *reg = p;

	/* update CPSW IP version */
	regs->version = AM65_CPSW_REGDUMP_VER;
	ale_entries = cpsw_ale_get_num_entries(common->ale);

	pos = 0;
	for (i = 0; i < ARRAY_SIZE(am65_cpsw_regdump); i++) {
@@ -603,7 +605,7 @@ static void am65_cpsw_get_regs(struct net_device *ndev,

		if (am65_cpsw_regdump[i].hdr.module_id ==
		    AM65_CPSW_REGDUMP_MOD_CPSW_ALE_TBL) {
			u32 ale_tbl_len = common->ale->params.ale_entries *
			u32 ale_tbl_len = ale_entries *
					  ALE_ENTRY_WORDS * sizeof(u32) +
					  sizeof(struct am65_cpsw_regdump_hdr);
			reg[pos++] = ale_tbl_len;
+14 −2
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) {
@@ -2131,10 +2143,10 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
	/* init common data */
	ale_params.dev = dev;
	ale_params.ale_ageout = AM65_CPSW_ALE_AGEOUT_DEFAULT;
	ale_params.ale_entries = 0;
	ale_params.ale_ports = common->port_num + 1;
	ale_params.ale_regs = common->cpsw_base + AM65_CPSW_NU_ALE_BASE;
	ale_params.nu_switch_ale = true;
	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;
+0 −6
Original line number Diff line number Diff line
@@ -1278,12 +1278,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
	}
	data->channels = prop;

	if (of_property_read_u32(node, "ale_entries", &prop)) {
		dev_err(&pdev->dev, "Missing ale_entries property in the DT.\n");
		return -EINVAL;
	}
	data->ale_entries = prop;

	if (of_property_read_u32(node, "bd_ram_size", &prop)) {
		dev_err(&pdev->dev, "Missing bd_ram_size property in the DT.\n");
		return -EINVAL;
+349 −72
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,46 @@

#define AM65_CPSW_ALE_THREAD_DEF_REG 0x134

/* ALE_AGING_TIMER */
#define ALE_AGING_TIMER_MASK	GENMASK(23, 0)

/**
 * struct ale_entry_fld - The ALE tbl entry field description
 * @start_bit: field start bit
 * @num_bits: field bit length
 * @flags: field flags
 */
struct ale_entry_fld {
	u8 start_bit;
	u8 num_bits;
	u8 flags;
};

enum {
	CPSW_ALE_F_STATUS_REG = BIT(0), /* Status register present */
	CPSW_ALE_F_HW_AUTOAGING = BIT(1), /* HW auto aging */

	CPSW_ALE_F_COUNT
};

/**
 * struct ale_dev_id - The ALE version/SoC specific configuration
 * @dev_id: ALE version/SoC id
 * @features: features supported by ALE
 * @tbl_entries: number of ALE entries
 * @major_ver_mask: mask of ALE Major Version Value in ALE_IDVER reg.
 * @nu_switch_ale: NU Switch ALE
 * @vlan_entry_tbl: ALE vlan entry fields description tbl
 */
struct cpsw_ale_dev_id {
	const char *dev_id;
	u32 features;
	u32 tbl_entries;
	u32 major_ver_mask;
	bool nu_switch_ale;
	const struct ale_entry_fld *vlan_entry_tbl;
};

#define ALE_TABLE_WRITE		BIT(31)

#define ALE_TYPE_FREE			0
@@ -60,7 +101,6 @@

#define ALE_TABLE_SIZE_MULTIPLIER	1024
#define ALE_STATUS_SIZE_MASK		0x1f
#define ALE_TABLE_SIZE_DEFAULT		64

static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits)
{
@@ -106,6 +146,59 @@ static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value, \
	cpsw_ale_set_field(ale_entry, start, bits, value);		\
}

enum {
	ALE_ENT_VID_MEMBER_LIST = 0,
	ALE_ENT_VID_UNREG_MCAST_MSK,
	ALE_ENT_VID_REG_MCAST_MSK,
	ALE_ENT_VID_FORCE_UNTAGGED_MSK,
	ALE_ENT_VID_UNREG_MCAST_IDX,
	ALE_ENT_VID_REG_MCAST_IDX,
	ALE_ENT_VID_LAST,
};

#define ALE_FLD_ALLOWED			BIT(0)
#define ALE_FLD_SIZE_PORT_MASK_BITS	BIT(1)
#define ALE_FLD_SIZE_PORT_NUM_BITS	BIT(2)

#define ALE_ENTRY_FLD(id, start, bits)	\
[id] = {				\
	.start_bit = start,		\
	.num_bits = bits,		\
	.flags = ALE_FLD_ALLOWED,	\
}

#define ALE_ENTRY_FLD_DYN_MSK_SIZE(id, start)	\
[id] = {					\
	.start_bit = start,			\
	.num_bits = 0,				\
	.flags = ALE_FLD_ALLOWED |		\
		 ALE_FLD_SIZE_PORT_MASK_BITS,	\
}

/* dm814x, am3/am4/am5, k2hk */
static const struct ale_entry_fld vlan_entry_cpsw[ALE_ENT_VID_LAST] = {
	ALE_ENTRY_FLD(ALE_ENT_VID_MEMBER_LIST, 0, 3),
	ALE_ENTRY_FLD(ALE_ENT_VID_UNREG_MCAST_MSK, 8, 3),
	ALE_ENTRY_FLD(ALE_ENT_VID_REG_MCAST_MSK, 16, 3),
	ALE_ENTRY_FLD(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24, 3),
};

/* k2e/k2l, k3 am65/j721e cpsw2g  */
static const struct ale_entry_fld vlan_entry_nu[ALE_ENT_VID_LAST] = {
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_MEMBER_LIST, 0),
	ALE_ENTRY_FLD(ALE_ENT_VID_UNREG_MCAST_IDX, 20, 3),
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24),
	ALE_ENTRY_FLD(ALE_ENT_VID_REG_MCAST_IDX, 44, 3),
};

/* K3 j721e/j7200 cpsw9g/5g, am64x cpsw3g  */
static const struct ale_entry_fld vlan_entry_k3_cpswxg[] = {
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_MEMBER_LIST, 0),
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_UNREG_MCAST_MSK, 12),
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_FORCE_UNTAGGED_MSK, 24),
	ALE_ENTRY_FLD_DYN_MSK_SIZE(ALE_ENT_VID_REG_MCAST_MSK, 36),
};

DEFINE_ALE_FIELD(entry_type,		60,	2)
DEFINE_ALE_FIELD(vlan_id,		48,	12)
DEFINE_ALE_FIELD(mcast_state,		62,	2)
@@ -115,17 +208,76 @@ DEFINE_ALE_FIELD(ucast_type, 62, 2)
DEFINE_ALE_FIELD1(port_num,		66)
DEFINE_ALE_FIELD(blocked,		65,     1)
DEFINE_ALE_FIELD(secure,		64,     1)
DEFINE_ALE_FIELD1(vlan_untag_force,	24)
DEFINE_ALE_FIELD1(vlan_reg_mcast,	16)
DEFINE_ALE_FIELD1(vlan_unreg_mcast,	8)
DEFINE_ALE_FIELD1(vlan_member_list,	0)
DEFINE_ALE_FIELD(mcast,			40,	1)
/* ALE NetCP nu switch specific */
DEFINE_ALE_FIELD(vlan_unreg_mcast_idx,	20,	3)
DEFINE_ALE_FIELD(vlan_reg_mcast_idx,	44,	3)

#define NU_VLAN_UNREG_MCAST_IDX	1

static int cpsw_ale_entry_get_fld(struct cpsw_ale *ale,
				  u32 *ale_entry,
				  const struct ale_entry_fld *entry_tbl,
				  int fld_id)
{
	const struct ale_entry_fld *entry_fld;
	u32 bits;

	if (!ale || !ale_entry)
		return -EINVAL;

	entry_fld = &entry_tbl[fld_id];
	if (!(entry_fld->flags & ALE_FLD_ALLOWED)) {
		dev_err(ale->params.dev, "get: wrong ale fld id %d\n", fld_id);
		return -ENOENT;
	}

	bits = entry_fld->num_bits;
	if (entry_fld->flags & ALE_FLD_SIZE_PORT_MASK_BITS)
		bits = ale->port_mask_bits;

	return cpsw_ale_get_field(ale_entry, entry_fld->start_bit, bits);
}

static void cpsw_ale_entry_set_fld(struct cpsw_ale *ale,
				   u32 *ale_entry,
				   const struct ale_entry_fld *entry_tbl,
				   int fld_id,
				   u32 value)
{
	const struct ale_entry_fld *entry_fld;
	u32 bits;

	if (!ale || !ale_entry)
		return;

	entry_fld = &entry_tbl[fld_id];
	if (!(entry_fld->flags & ALE_FLD_ALLOWED)) {
		dev_err(ale->params.dev, "set: wrong ale fld id %d\n", fld_id);
		return;
	}

	bits = entry_fld->num_bits;
	if (entry_fld->flags & ALE_FLD_SIZE_PORT_MASK_BITS)
		bits = ale->port_mask_bits;

	cpsw_ale_set_field(ale_entry, entry_fld->start_bit, bits, value);
}

static int cpsw_ale_vlan_get_fld(struct cpsw_ale *ale,
				 u32 *ale_entry,
				 int fld_id)
{
	return cpsw_ale_entry_get_fld(ale, ale_entry,
				      ale->vlan_entry_tbl, fld_id);
}

static void cpsw_ale_vlan_set_fld(struct cpsw_ale *ale,
				  u32 *ale_entry,
				  int fld_id,
				  u32 value)
{
	cpsw_ale_entry_set_fld(ale, ale_entry,
			       ale->vlan_entry_tbl, fld_id, value);
}

/* The MAC address field in the ALE entry cannot be macroized as above */
static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
{
@@ -420,19 +572,22 @@ static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry,
	int idx;

	/* Set VLAN registered multicast flood mask */
	idx = cpsw_ale_get_vlan_reg_mcast_idx(ale_entry);
	idx = cpsw_ale_vlan_get_fld(ale, ale_entry,
				    ALE_ENT_VID_REG_MCAST_IDX);
	writel(reg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));

	/* Set VLAN unregistered multicast flood mask */
	idx = cpsw_ale_get_vlan_unreg_mcast_idx(ale_entry);
	idx = cpsw_ale_vlan_get_fld(ale, ale_entry,
				    ALE_ENT_VID_UNREG_MCAST_IDX);
	writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));
}

static void cpsw_ale_set_vlan_untag(struct cpsw_ale *ale, u32 *ale_entry,
				    u16 vid, int untag_mask)
{
	cpsw_ale_set_vlan_untag_force(ale_entry,
				      untag_mask, ale->vlan_field_bits);
	cpsw_ale_vlan_set_fld(ale, ale_entry,
			      ALE_ENT_VID_FORCE_UNTAGGED_MSK,
			      untag_mask);
	if (untag_mask & ALE_PORT_HOST)
		bitmap_set(ale->p0_untag_vid_mask, vid, 1);
	else
@@ -454,17 +609,19 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port_mask, int untag,
	cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag);

	if (!ale->params.nu_switch_ale) {
		cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast,
					    ale->vlan_field_bits);
		cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast,
					      ale->vlan_field_bits);
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_REG_MCAST_MSK, reg_mcast);
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_UNREG_MCAST_MSK, unreg_mcast);
	} else {
		cpsw_ale_set_vlan_unreg_mcast_idx(ale_entry,
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_UNREG_MCAST_IDX,
				      NU_VLAN_UNREG_MCAST_IDX);
		cpsw_ale_set_vlan_mcast(ale, ale_entry, reg_mcast, unreg_mcast);
	}
	cpsw_ale_set_vlan_member_list(ale_entry, port_mask,
				      ale->vlan_field_bits);

	cpsw_ale_vlan_set_fld(ale, ale_entry,
			      ALE_ENT_VID_MEMBER_LIST, port_mask);

	if (idx < 0)
		idx = cpsw_ale_match_free(ale);
@@ -483,20 +640,20 @@ static void cpsw_ale_del_vlan_modify(struct cpsw_ale *ale, u32 *ale_entry,
	int reg_mcast, unreg_mcast;
	int members, untag;

	members = cpsw_ale_get_vlan_member_list(ale_entry,
						ale->vlan_field_bits);
	members = cpsw_ale_vlan_get_fld(ale, ale_entry,
					ALE_ENT_VID_MEMBER_LIST);
	members &= ~port_mask;
	if (!members) {
		cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE);
		return;
	}

	untag = cpsw_ale_get_vlan_untag_force(ale_entry,
					      ale->vlan_field_bits);
	reg_mcast = cpsw_ale_get_vlan_reg_mcast(ale_entry,
						ale->vlan_field_bits);
	unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry,
						    ale->vlan_field_bits);
	untag = cpsw_ale_vlan_get_fld(ale, ale_entry,
				      ALE_ENT_VID_FORCE_UNTAGGED_MSK);
	reg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry,
					  ALE_ENT_VID_REG_MCAST_MSK);
	unreg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry,
					    ALE_ENT_VID_UNREG_MCAST_MSK);
	untag &= members;
	reg_mcast &= members;
	unreg_mcast &= members;
@@ -504,16 +661,16 @@ static void cpsw_ale_del_vlan_modify(struct cpsw_ale *ale, u32 *ale_entry,
	cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag);

	if (!ale->params.nu_switch_ale) {
		cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast,
					    ale->vlan_field_bits);
		cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast,
					      ale->vlan_field_bits);
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_REG_MCAST_MSK, reg_mcast);
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_UNREG_MCAST_MSK, unreg_mcast);
	} else {
		cpsw_ale_set_vlan_mcast(ale, ale_entry, reg_mcast,
					unreg_mcast);
	}
	cpsw_ale_set_vlan_member_list(ale_entry, members,
				      ale->vlan_field_bits);
	cpsw_ale_vlan_set_fld(ale, ale_entry,
			      ALE_ENT_VID_MEMBER_LIST, members);
}

int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
@@ -551,15 +708,15 @@ int cpsw_ale_vlan_add_modify(struct cpsw_ale *ale, u16 vid, int port_mask,
	if (idx >= 0)
		cpsw_ale_read(ale, idx, ale_entry);

	vlan_members = cpsw_ale_get_vlan_member_list(ale_entry,
						     ale->vlan_field_bits);
	reg_mcast_members = cpsw_ale_get_vlan_reg_mcast(ale_entry,
							ale->vlan_field_bits);
	vlan_members = cpsw_ale_vlan_get_fld(ale, ale_entry,
					     ALE_ENT_VID_MEMBER_LIST);
	reg_mcast_members = cpsw_ale_vlan_get_fld(ale, ale_entry,
						  ALE_ENT_VID_REG_MCAST_MSK);
	unreg_mcast_members =
		cpsw_ale_get_vlan_unreg_mcast(ale_entry,
					      ale->vlan_field_bits);
	untag_members = cpsw_ale_get_vlan_untag_force(ale_entry,
						      ale->vlan_field_bits);
		cpsw_ale_vlan_get_fld(ale, ale_entry,
				      ALE_ENT_VID_UNREG_MCAST_MSK);
	untag_members = cpsw_ale_vlan_get_fld(ale, ale_entry,
					      ALE_ENT_VID_FORCE_UNTAGGED_MSK);

	vlan_members |= port_mask;
	untag_members = (untag_members & ~port_mask) | untag_mask;
@@ -592,14 +749,15 @@ void cpsw_ale_set_unreg_mcast(struct cpsw_ale *ale, int unreg_mcast_mask,
			continue;

		unreg_members =
			cpsw_ale_get_vlan_unreg_mcast(ale_entry,
						      ale->vlan_field_bits);
			cpsw_ale_vlan_get_fld(ale, ale_entry,
					      ALE_ENT_VID_UNREG_MCAST_MSK);
		if (add)
			unreg_members |= unreg_mcast_mask;
		else
			unreg_members &= ~unreg_mcast_mask;
		cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_members,
					      ale->vlan_field_bits);
		cpsw_ale_vlan_set_fld(ale, ale_entry,
				      ALE_ENT_VID_UNREG_MCAST_MSK,
				      unreg_members);
		cpsw_ale_write(ale, idx, ale_entry);
	}
}
@@ -609,15 +767,15 @@ static void cpsw_ale_vlan_set_unreg_mcast(struct cpsw_ale *ale, u32 *ale_entry,
{
	int unreg_mcast;

	unreg_mcast =
		cpsw_ale_get_vlan_unreg_mcast(ale_entry,
					      ale->vlan_field_bits);
	unreg_mcast = cpsw_ale_vlan_get_fld(ale, ale_entry,
					    ALE_ENT_VID_UNREG_MCAST_MSK);
	if (allmulti)
		unreg_mcast |= ALE_PORT_HOST;
	else
		unreg_mcast &= ~ALE_PORT_HOST;
	cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast,
				      ale->vlan_field_bits);

	cpsw_ale_vlan_set_fld(ale, ale_entry,
			      ALE_ENT_VID_UNREG_MCAST_MSK, unreg_mcast);
}

static void
@@ -627,7 +785,8 @@ cpsw_ale_vlan_set_unreg_mcast_idx(struct cpsw_ale *ale, u32 *ale_entry,
	int unreg_mcast;
	int idx;

	idx = cpsw_ale_get_vlan_unreg_mcast_idx(ale_entry);
	idx = cpsw_ale_vlan_get_fld(ale, ale_entry,
				    ALE_ENT_VID_UNREG_MCAST_IDX);

	unreg_mcast = readl(ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));

@@ -651,9 +810,9 @@ void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti, int port)
		type = cpsw_ale_get_entry_type(ale_entry);
		if (type != ALE_TYPE_VLAN)
			continue;
		vlan_members =
			cpsw_ale_get_vlan_member_list(ale_entry,
						      ale->vlan_field_bits);

		vlan_members = cpsw_ale_vlan_get_fld(ale, ale_entry,
						     ALE_ENT_VID_MEMBER_LIST);

		if (port != -1 && !(vlan_members & BIT(port)))
			continue;
@@ -960,30 +1119,146 @@ 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);
}

static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = {
	{
		/* am3/4/5, dra7. dm814x, 66ak2hk-gbe */
		.dev_id = "cpsw",
		.tbl_entries = 1024,
		.major_ver_mask = 0xff,
		.vlan_entry_tbl = vlan_entry_cpsw,
	},
	{
		/* 66ak2h_xgbe */
		.dev_id = "66ak2h-xgbe",
		.tbl_entries = 2048,
		.major_ver_mask = 0xff,
		.vlan_entry_tbl = vlan_entry_cpsw,
	},
	{
		.dev_id = "66ak2el",
		.features = CPSW_ALE_F_STATUS_REG,
		.major_ver_mask = 0x7,
		.nu_switch_ale = true,
		.vlan_entry_tbl = vlan_entry_nu,
	},
	{
		.dev_id = "66ak2g",
		.features = CPSW_ALE_F_STATUS_REG,
		.tbl_entries = 64,
		.major_ver_mask = 0x7,
		.nu_switch_ale = true,
		.vlan_entry_tbl = vlan_entry_nu,
	},
	{
		.dev_id = "am65x-cpsw2g",
		.features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING,
		.tbl_entries = 64,
		.major_ver_mask = 0x7,
		.nu_switch_ale = true,
		.vlan_entry_tbl = vlan_entry_nu,
	},
	{
		.dev_id = "j721e-cpswxg",
		.features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING,
		.major_ver_mask = 0x7,
		.vlan_entry_tbl = vlan_entry_k3_cpswxg,
	},
	{ },
};

static const struct
cpsw_ale_dev_id *cpsw_ale_match_id(const struct cpsw_ale_dev_id *id,
				   const char *dev_id)
{
	if (!dev_id)
		return NULL;

	while (id->dev_id) {
		if (strcmp(dev_id, id->dev_id) == 0)
			return id;
		id++;
	}
	return NULL;
}

struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
{
	const struct cpsw_ale_dev_id *ale_dev_id;
	struct cpsw_ale *ale;
	u32 rev, ale_entries;

	ale_dev_id = cpsw_ale_match_id(cpsw_ale_id_match, params->dev_id);
	if (!ale_dev_id)
		return ERR_PTR(-EINVAL);

	params->ale_entries = ale_dev_id->tbl_entries;
	params->major_ver_mask = ale_dev_id->major_ver_mask;
	params->nu_switch_ale = ale_dev_id->nu_switch_ale;

	ale = devm_kzalloc(params->dev, sizeof(*ale), GFP_KERNEL);
	if (!ale)
		return ERR_PTR(-ENOMEM);
@@ -997,10 +1272,10 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)

	ale->params = *params;
	ale->ageout = ale->params.ale_ageout * HZ;
	ale->features = ale_dev_id->features;
	ale->vlan_entry_tbl = ale_dev_id->vlan_entry_tbl;

	rev = readl_relaxed(ale->params.ale_regs + ALE_IDVER);
	if (!ale->params.major_ver_mask)
		ale->params.major_ver_mask = 0xff;
	ale->version =
		(ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask) << 8) |
		 ALE_VERSION_MINOR(rev);
@@ -1008,7 +1283,8 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
		 ALE_VERSION_MAJOR(rev, ale->params.major_ver_mask),
		 ALE_VERSION_MINOR(rev));

	if (!ale->params.ale_entries) {
	if (ale->features & CPSW_ALE_F_STATUS_REG &&
	    !ale->params.ale_entries) {
		ale_entries =
			readl_relaxed(ale->params.ale_regs + ALE_STATUS) &
			ALE_STATUS_SIZE_MASK;
@@ -1017,15 +1293,11 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
		 * table which shows the size as a multiple of 1024 entries.
		 * For these, params.ale_entries will be set to zero. So
		 * read the register and update the value of ale_entries.
		 * ALE table on NetCP lite, is much smaller and is indicated
		 * by a value of zero in ALE_STATUS. So use a default value
		 * of ALE_TABLE_SIZE_DEFAULT for this. Caller is expected
		 * to set the value of ale_entries for all other versions
		 * of ALE.
		 * return error if ale_entries is zero in ALE_STATUS.
		 */
		if (!ale_entries)
			ale_entries = ALE_TABLE_SIZE_DEFAULT;
		else
			return ERR_PTR(-EINVAL);

		ale_entries *= ALE_TABLE_SIZE_MULTIPLIER;
		ale->params.ale_entries = ale_entries;
	}
@@ -1079,3 +1351,8 @@ void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data)
		data += ALE_ENTRY_WORDS;
	}
}

u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale)
{
	return ale ? ale->params.ale_entries : 0;
}
Loading