From 9999e48639b3cecb08ffb37358bcba3b48161b29 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 08:50:17 +0000 Subject: [PATCH] add ax88772_rst --- kernel/drivers/net/ethernet/ti/cpsw_ale.c | 663 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 568 insertions(+), 95 deletions(-) diff --git a/kernel/drivers/net/ethernet/ti/cpsw_ale.c b/kernel/drivers/net/ethernet/ti/cpsw_ale.c index c245629..73efc8b 100644 --- a/kernel/drivers/net/ethernet/ti/cpsw_ale.c +++ b/kernel/drivers/net/ethernet/ti/cpsw_ale.c @@ -1,17 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments N-Port Ethernet Switch Address Lookup Engine * * Copyright (C) 2012 Texas Instruments * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ +#include <linux/bitmap.h> +#include <linux/if_vlan.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -37,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 @@ -48,6 +44,48 @@ #define ALE_UNKNOWNVLAN_REG_MCAST_FLOOD 0x98 #define ALE_UNKNOWNVLAN_FORCE_UNTAG_EGRESS 0x9C #define ALE_VLAN_MASK_MUX(reg) (0xc0 + (0x4 * (reg))) + +#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) @@ -63,27 +101,40 @@ #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) { - int idx; + int idx, idx2; + u32 hi_val = 0; idx = start / 32; + idx2 = (start + bits - 1) / 32; + /* Check if bits to be fetched exceed a word */ + if (idx != idx2) { + idx2 = 2 - idx2; /* flip */ + hi_val = ale_entry[idx2] << ((idx2 * 32) - start); + } start -= idx * 32; idx = 2 - idx; /* flip */ - return (ale_entry[idx] >> start) & BITMASK(bits); + return (hi_val + (ale_entry[idx] >> start)) & BITMASK(bits); } static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32 bits, u32 value) { - int idx; + int idx, idx2; value &= BITMASK(bits); - idx = start / 32; + idx = start / 32; + idx2 = (start + bits - 1) / 32; + /* Check if bits to be set exceed a word */ + if (idx != idx2) { + idx2 = 2 - idx2; /* flip */ + ale_entry[idx2] &= ~(BITMASK(bits + start - (idx2 * 32))); + ale_entry[idx2] |= (value >> ((idx2 * 32) - start)); + } start -= idx * 32; - idx = 2 - idx; /* flip */ + idx = 2 - idx; /* flip */ ale_entry[idx] &= ~(BITMASK(bits) << start); ale_entry[idx] |= (value << start); } @@ -109,6 +160,59 @@ 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) @@ -118,14 +222,75 @@ 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) @@ -136,7 +301,7 @@ addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8); } -static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr) +static inline void cpsw_ale_set_addr(u32 *ale_entry, const u8 *addr) { int i; @@ -175,7 +340,7 @@ return idx; } -static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) +static int cpsw_ale_match_addr(struct cpsw_ale *ale, const u8 *addr, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; @@ -287,6 +452,9 @@ if (cpsw_ale_get_mcast(ale_entry)) { u8 addr[6]; + if (cpsw_ale_get_super(ale_entry)) + continue; + cpsw_ale_get_addr(ale_entry, addr); if (!is_broadcast_ether_addr(addr)) cpsw_ale_flush_mcast(ale, ale_entry, port_mask); @@ -296,7 +464,6 @@ } return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_flush_multicast); static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry, int flags, u16 vid) @@ -309,7 +476,7 @@ } } -int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_add_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -334,9 +501,8 @@ cpsw_ale_write(ale, idx, ale_entry); return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_add_ucast); -int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, +int cpsw_ale_del_ucast(struct cpsw_ale *ale, const u8 *addr, int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -350,9 +516,8 @@ cpsw_ale_write(ale, idx, ale_entry); return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast); -int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_add_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -365,7 +530,7 @@ cpsw_ale_set_vlan_entry_type(ale_entry, flags, vid); cpsw_ale_set_addr(ale_entry, addr); - cpsw_ale_set_super(ale_entry, (flags & ALE_BLOCKED) ? 1 : 0); + cpsw_ale_set_super(ale_entry, (flags & ALE_SUPER) ? 1 : 0); cpsw_ale_set_mcast_state(ale_entry, mcast_state); mask = cpsw_ale_get_port_mask(ale_entry, @@ -384,12 +549,12 @@ cpsw_ale_write(ale, idx, ale_entry); return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_add_mcast); -int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, +int cpsw_ale_del_mcast(struct cpsw_ale *ale, const u8 *addr, int port_mask, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int mcast_members = 0; int idx; idx = cpsw_ale_match_addr(ale, addr, (flags & ALE_VLAN) ? vid : 0); @@ -398,8 +563,14 @@ cpsw_ale_read(ale, idx, ale_entry); - if (port_mask) - cpsw_ale_set_port_mask(ale_entry, port_mask, + if (port_mask) { + mcast_members = cpsw_ale_get_port_mask(ale_entry, + ale->port_mask_bits); + mcast_members &= ~port_mask; + } + + if (mcast_members) + cpsw_ale_set_port_mask(ale_entry, mcast_members, ale->port_mask_bits); else cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); @@ -407,7 +578,6 @@ cpsw_ale_write(ale, idx, ale_entry); return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_del_mcast); /* ALE NetCP NU switch specific vlan functions */ static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry, @@ -416,15 +586,29 @@ 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)); } -int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, +static void cpsw_ale_set_vlan_untag(struct cpsw_ale *ale, u32 *ale_entry, + u16 vid, int untag_mask) +{ + 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 + bitmap_clear(ale->p0_untag_vid_mask, vid, 1); +} + +int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port_mask, int untag, int reg_mcast, int unreg_mcast) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; @@ -436,17 +620,22 @@ cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); cpsw_ale_set_vlan_id(ale_entry, vid); + cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag); - cpsw_ale_set_vlan_untag_force(ale_entry, untag, ale->vlan_field_bits); 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_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, 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); @@ -458,7 +647,45 @@ cpsw_ale_write(ale, idx, ale_entry); return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_add_vlan); + +static void cpsw_ale_del_vlan_modify(struct cpsw_ale *ale, u32 *ale_entry, + u16 vid, int port_mask) +{ + int reg_mcast, unreg_mcast; + int members, untag; + + 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_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; + + cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag); + + if (!ale->params.nu_switch_ale) { + 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_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) { @@ -471,29 +698,63 @@ cpsw_ale_read(ale, idx, ale_entry); - if (port_mask) - cpsw_ale_set_vlan_member_list(ale_entry, port_mask, - ale->vlan_field_bits); - else + if (port_mask) { + cpsw_ale_del_vlan_modify(ale, ale_entry, vid, port_mask); + } else { + cpsw_ale_set_vlan_untag(ale, ale_entry, vid, 0); cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); + } cpsw_ale_write(ale, idx, ale_entry); + return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_del_vlan); -void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti) +int cpsw_ale_vlan_add_modify(struct cpsw_ale *ale, u16 vid, int port_mask, + int untag_mask, int reg_mask, int unreg_mask) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int reg_mcast_members, unreg_mcast_members; + int vlan_members, untag_members; + int idx, ret = 0; + + idx = cpsw_ale_match_vlan(ale, vid); + if (idx >= 0) + cpsw_ale_read(ale, idx, ale_entry); + + 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_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; + reg_mcast_members = (reg_mcast_members & ~port_mask) | reg_mask; + unreg_mcast_members = (unreg_mcast_members & ~port_mask) | unreg_mask; + + ret = cpsw_ale_add_vlan(ale, vid, vlan_members, untag_members, + reg_mcast_members, unreg_mcast_members); + if (ret) { + dev_err(ale->params.dev, "Unable to add vlan\n"); + return ret; + } + dev_dbg(ale->params.dev, "port mask 0x%x untag 0x%x\n", vlan_members, + untag_mask); + + return ret; +} + +void cpsw_ale_set_unreg_mcast(struct cpsw_ale *ale, int unreg_mcast_mask, + bool add) { u32 ale_entry[ALE_ENTRY_WORDS]; + int unreg_members = 0; int type, idx; - int unreg_mcast = 0; - - /* Only bother doing the work if the setting is actually changing */ - if (ale->allmulti == allmulti) - return; - - /* Remember the new setting to check against next time */ - ale->allmulti = allmulti; for (idx = 0; idx < ale->params.ale_entries; idx++) { cpsw_ale_read(ale, idx, ale_entry); @@ -501,19 +762,84 @@ if (type != ALE_TYPE_VLAN) continue; - unreg_mcast = - cpsw_ale_get_vlan_unreg_mcast(ale_entry, - ale->vlan_field_bits); - if (allmulti) - unreg_mcast |= 1; + unreg_members = + cpsw_ale_vlan_get_fld(ale, ale_entry, + ALE_ENT_VID_UNREG_MCAST_MSK); + if (add) + unreg_members |= unreg_mcast_mask; else - unreg_mcast &= ~1; - cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast, - ale->vlan_field_bits); + unreg_members &= ~unreg_mcast_mask; + cpsw_ale_vlan_set_fld(ale, ale_entry, + ALE_ENT_VID_UNREG_MCAST_MSK, + unreg_members); cpsw_ale_write(ale, idx, ale_entry); } } -EXPORT_SYMBOL_GPL(cpsw_ale_set_allmulti); + +static void cpsw_ale_vlan_set_unreg_mcast(struct cpsw_ale *ale, u32 *ale_entry, + int allmulti) +{ + int unreg_mcast; + + 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_vlan_set_fld(ale, ale_entry, + ALE_ENT_VID_UNREG_MCAST_MSK, unreg_mcast); +} + +static void +cpsw_ale_vlan_set_unreg_mcast_idx(struct cpsw_ale *ale, u32 *ale_entry, + int allmulti) +{ + int unreg_mcast; + int idx; + + 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)); + + if (allmulti) + unreg_mcast |= ALE_PORT_HOST; + else + unreg_mcast &= ~ALE_PORT_HOST; + + writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx)); +} + +void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti, int port) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < ale->params.ale_entries; idx++) { + int vlan_members; + + cpsw_ale_read(ale, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_VLAN) + continue; + + vlan_members = cpsw_ale_vlan_get_fld(ale, ale_entry, + ALE_ENT_VID_MEMBER_LIST); + + if (port != -1 && !(vlan_members & BIT(port))) + continue; + + if (!ale->params.nu_switch_ale) + cpsw_ale_vlan_set_unreg_mcast(ale, ale_entry, allmulti); + else + cpsw_ale_vlan_set_unreg_mcast_idx(ale, ale_entry, + allmulti); + + cpsw_ale_write(ale, idx, ale_entry); + } +} struct ale_control_info { const char *name; @@ -659,6 +985,22 @@ .port_shift = 0, .bits = 1, }, + [ALE_PORT_MACONLY] = { + .name = "mac_only_port_mode", + .offset = ALE_PORTCTL, + .port_offset = 4, + .shift = 11, + .port_shift = 0, + .bits = 1, + }, + [ALE_PORT_MACONLY_CAF] = { + .name = "mac_only_port_caf", + .offset = ALE_PORTCTL, + .port_offset = 4, + .shift = 13, + .port_shift = 0, + .bits = 1, + }, [ALE_PORT_MCAST_LIMIT] = { .name = "mcast_limit", .offset = ALE_PORTCTL, @@ -707,6 +1049,22 @@ .port_shift = 0, .bits = 6, }, + [ALE_DEFAULT_THREAD_ID] = { + .name = "default_thread_id", + .offset = AM65_CPSW_ALE_THREAD_DEF_REG, + .port_offset = 0, + .shift = 0, + .port_shift = 0, + .bits = 6, + }, + [ALE_DEFAULT_THREAD_ENABLE] = { + .name = "default_thread_id_enable", + .offset = AM65_CPSW_ALE_THREAD_DEF_REG, + .port_offset = 0, + .shift = 15, + .port_shift = 0, + .bits = 1, + }, }; int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, @@ -739,7 +1097,6 @@ return 0; } -EXPORT_SYMBOL_GPL(cpsw_ale_control_set); int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) { @@ -763,7 +1120,6 @@ tmp = readl_relaxed(ale->params.ale_regs + offset) >> shift; return tmp & BITMASK(info->bits); } -EXPORT_SYMBOL_GPL(cpsw_ale_control_get); static void cpsw_ale_timer(struct timer_list *t) { @@ -777,42 +1133,163 @@ } } +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); } -EXPORT_SYMBOL_GPL(cpsw_ale_start); 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); } -EXPORT_SYMBOL_GPL(cpsw_ale_stop); + +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 NULL; + return ERR_PTR(-ENOMEM); + + ale->p0_untag_vid_mask = + devm_kmalloc_array(params->dev, BITS_TO_LONGS(VLAN_N_VID), + sizeof(unsigned long), + GFP_KERNEL); + if (!ale->p0_untag_vid_mask) + return ERR_PTR(-ENOMEM); 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); @@ -820,7 +1297,8 @@ 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; @@ -829,16 +1307,12 @@ * 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, @@ -881,7 +1355,6 @@ cpsw_ale_control_set(ale, 0, ALE_CLEAR, 1); return ale; } -EXPORT_SYMBOL_GPL(cpsw_ale_create); void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) { @@ -892,8 +1365,8 @@ data += ALE_ENTRY_WORDS; } } -EXPORT_SYMBOL_GPL(cpsw_ale_dump); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TI CPSW ALE driver"); -MODULE_AUTHOR("Texas Instruments"); +u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale) +{ + return ale ? ale->params.ale_entries : 0; +} -- Gitblit v1.6.2