提交 1cf814a0 编写于 作者: D David S. Miller

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: NDavid S. Miller <davem@davemloft.net>
......@@ -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;
......
......@@ -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)) {
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,
NU_VLAN_UNREG_MCAST_IDX);
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)
}
}
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);
}
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,16 +1293,12 @@ 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
ale_entries *= ALE_TABLE_SIZE_MULTIPLIER;
return ERR_PTR(-EINVAL);
ale_entries *= ALE_TABLE_SIZE_MULTIPLIER;
ale->params.ale_entries = ale_entries;
}
dev_info(ale->params.dev,
......@@ -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;
}
......@@ -24,18 +24,24 @@ struct cpsw_ale_params {
* pass it from caller.
*/
u32 major_ver_mask;
const char *dev_id;
unsigned long bus_freq;
};
struct ale_entry_fld;
struct cpsw_ale {
struct cpsw_ale_params params;
struct timer_list timer;
unsigned long ageout;
u32 version;
u32 features;
/* These bits are different on NetCP NU Switch ALE */
u32 port_mask_bits;
u32 port_num_bits;
u32 vlan_field_bits;
unsigned long *p0_untag_vid_mask;
const struct ale_entry_fld *vlan_entry_tbl;
};
enum cpsw_ale_control {
......@@ -119,6 +125,7 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
int control, int value);
void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data);
u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale);
static inline int cpsw_ale_get_vlan_p0_untag(struct cpsw_ale *ale, u16 vid)
{
......
......@@ -339,7 +339,8 @@ int cpsw_get_regs_len(struct net_device *ndev)
{
struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
return cpsw->data.ale_entries * ALE_ENTRY_WORDS * sizeof(u32);
return cpsw_ale_get_num_entries(cpsw->ale) *
ALE_ENTRY_WORDS * sizeof(u32);
}
void cpsw_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *p)
......
......@@ -1243,7 +1243,6 @@ static int cpsw_probe_dt(struct cpsw_common *cpsw)
data->active_slave = 0;
data->channels = CPSW_MAX_QUEUES;
data->ale_entries = CPSW_ALE_NUM_ENTRIES;
data->dual_emac = true;
data->bd_ram_size = CPSW_BD_RAM_SIZE;
data->mac_control = 0;
......
......@@ -500,8 +500,8 @@ int cpsw_init_common(struct cpsw_common *cpsw, void __iomem *ss_regs,
ale_params.dev = dev;
ale_params.ale_ageout = ale_ageout;
ale_params.ale_entries = data->ale_entries;
ale_params.ale_ports = CPSW_ALE_PORTS_NUM;
ale_params.dev_id = "cpsw";
cpsw->ale = cpsw_ale_create(&ale_params);
if (IS_ERR(cpsw->ale)) {
......
......@@ -117,7 +117,6 @@ do { \
#define CPSW_MAX_QUEUES 8
#define CPSW_CPDMA_DESCS_POOL_SIZE_DEFAULT 256
#define CPSW_ALE_AGEOUT_DEFAULT 10 /* sec */
#define CPSW_ALE_NUM_ENTRIES 1024
#define CPSW_FIFO_QUEUE_TYPE_SHIFT 16
#define CPSW_FIFO_SHAPE_EN_SHIFT 16
#define CPSW_FIFO_RATE_EN_SHIFT 20
......@@ -294,7 +293,6 @@ struct cpsw_platform_data {
u32 channels; /* number of cpdma channels (symmetric) */
u32 slaves; /* number of slave cpgmac ports */
u32 active_slave;/* time stamping, ethtool and SIOCGMIIPHY slave */
u32 ale_entries; /* ale table size */
u32 bd_ram_size; /*buffer descriptor ram size */
u32 mac_control; /* Mac control register */
u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/
......
......@@ -51,7 +51,6 @@
#define GBE13_CPTS_OFFSET 0x500
#define GBE13_ALE_OFFSET 0x600
#define GBE13_HOST_PORT_NUM 0
#define GBE13_NUM_ALE_ENTRIES 1024
/* 1G Ethernet NU SS defines */
#define GBENU_MODULE_NAME "netcp-gbenu"
......@@ -101,7 +100,6 @@
#define XGBE10_ALE_OFFSET 0x700
#define XGBE10_HW_STATS_OFFSET 0x800
#define XGBE10_HOST_PORT_NUM 0
#define XGBE10_NUM_ALE_ENTRIES 2048
#define GBE_TIMER_INTERVAL (HZ / 2)
......@@ -711,7 +709,6 @@ struct gbe_priv {
struct netcp_device *netcp_device;
struct timer_list timer;
u32 num_slaves;
u32 ale_entries;
u32 ale_ports;
bool enable_ale;
u8 max_num_slaves;
......@@ -3309,7 +3306,6 @@ static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev,
gbe_dev->cpts_reg = gbe_dev->switch_regs + XGBE10_CPTS_OFFSET;
gbe_dev->ale_ports = gbe_dev->max_num_ports;
gbe_dev->host_port = XGBE10_HOST_PORT_NUM;
gbe_dev->ale_entries = XGBE10_NUM_ALE_ENTRIES;
gbe_dev->stats_en_mask = (1 << (gbe_dev->max_num_ports)) - 1;
/* Subsystem registers */
......@@ -3433,7 +3429,6 @@ static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev,
gbe_dev->ale_reg = gbe_dev->switch_regs + GBE13_ALE_OFFSET;
gbe_dev->ale_ports = gbe_dev->max_num_ports;
gbe_dev->host_port = GBE13_HOST_PORT_NUM;
gbe_dev->ale_entries = GBE13_NUM_ALE_ENTRIES;
gbe_dev->stats_en_mask = GBE13_REG_VAL_STAT_ENABLE_ALL;
/* Subsystem registers */
......@@ -3697,12 +3692,15 @@ static int gbe_probe(struct netcp_device *netcp_device, struct device *dev,
ale_params.dev = gbe_dev->dev;
ale_params.ale_regs = gbe_dev->ale_reg;
ale_params.ale_ageout = GBE_DEFAULT_ALE_AGEOUT;
ale_params.ale_entries = gbe_dev->ale_entries;
ale_params.ale_ports = gbe_dev->ale_ports;
if (IS_SS_ID_MU(gbe_dev)) {
ale_params.major_ver_mask = 0x7;
ale_params.nu_switch_ale = true;
}
ale_params.dev_id = "cpsw";
if (IS_SS_ID_NU(gbe_dev))
ale_params.dev_id = "66ak2el";
else if (IS_SS_ID_2U(gbe_dev))
ale_params.dev_id = "66ak2g";
else if (IS_SS_ID_XGBE(gbe_dev))
ale_params.dev_id = "66ak2h-xgbe";
gbe_dev->ale = cpsw_ale_create(&ale_params);
if (IS_ERR(gbe_dev->ale)) {
dev_err(gbe_dev->dev, "error initializing ale engine\n");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册