net: ethernet: ti: ale: switch to use tables for vlan entry description
[linux-2.6-microblaze.git] / drivers / net / ethernet / ti / cpsw_ale.c
index 7b54e99..0dd0c33 100644 (file)
 /* 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 */
@@ -64,6 +76,7 @@ enum {
  * @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;
@@ -71,6 +84,7 @@ struct cpsw_ale_dev_id {
        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)
@@ -132,6 +146,51 @@ 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),
+};
+
 DEFINE_ALE_FIELD(entry_type,           60,     2)
 DEFINE_ALE_FIELD(vlan_id,              48,     12)
 DEFINE_ALE_FIELD(mcast_state,          62,     2)
@@ -141,17 +200,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)
 {
@@ -446,19 +564,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
@@ -480,17 +601,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);
@@ -509,20 +632,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;
@@ -530,16 +653,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)
@@ -577,15 +700,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;
@@ -618,14 +741,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);
        }
 }
@@ -635,15 +759,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
@@ -653,7 +777,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));
 
@@ -677,9 +802,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;
@@ -1056,18 +1181,21 @@ static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = {
                .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",
@@ -1075,6 +1203,7 @@ static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = {
                .tbl_entries = 64,
                .major_ver_mask = 0x7,
                .nu_switch_ale = true,
+               .vlan_entry_tbl = vlan_entry_nu,
        },
        {
                .dev_id = "am65x-cpsw2g",
@@ -1082,6 +1211,7 @@ static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = {
                .tbl_entries = 64,
                .major_ver_mask = 0x7,
                .nu_switch_ale = true,
+               .vlan_entry_tbl = vlan_entry_nu,
        },
        { },
 };
@@ -1129,6 +1259,7 @@ 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);
        ale->version =