Merge branch 'net-ethernet-ti-ale-add-static-configuration'
authorDavid S. Miller <davem@davemloft.net>
Sat, 12 Sep 2020 00:34:40 +0000 (17:34 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 12 Sep 2020 00:34:40 +0000 (17:34 -0700)
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: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/am65-cpsw-ethtool.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h
drivers/net/ethernet/ti/cpsw_ethtool.c
drivers/net/ethernet/ti/cpsw_new.c
drivers/net/ethernet/ti/cpsw_priv.c
drivers/net/ethernet/ti/cpsw_priv.h
drivers/net/ethernet/ti/netcp_ethss.c

index 496dafb..6e4d4f9 100644 (file)
@@ -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;
index 9baf3f3..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) {
@@ -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)) {
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 4a65edc..9b425f1 100644 (file)
@@ -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;
index 9ad872b..a6a455c 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
 
 #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
 
 #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;
+}
index 6a3cb68..5e4a696 100644 (file)
@@ -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)
 {
index fa54efe..4d02c51 100644 (file)
@@ -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)
index 8ed7857..a3528c5 100644 (file)
@@ -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;
index 482a1a4..51cc29f 100644 (file)
@@ -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)) {
index bf4e179..7b7f359 100644 (file)
@@ -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*/
index 2809392..33c1592 100644 (file)
@@ -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"
 #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");