X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fnet%2Fvsc9953.c;h=5d196cfb3f382893f4691c3f9782128a15fe9157;hb=5f78786499593592bbd435445bd2d667de90fc5b;hp=160f4782e412f92c28706bb17937132030e32f94;hpb=ba389e65e5bb64506ad35b3d7007ec1b3b9cf4de;p=u-boot diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 160f4782e4..5d196cfb3f 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2014 - 2015 Freescale Semiconductor, Inc. * - * SPDX-License-Identifier: GPL-2.0+ - * * Driver for the Vitesse VSC9953 L2 Switch */ @@ -335,7 +334,7 @@ static int vsc9953_port_vlan_pvid_get(int port_nr, int *pvid) struct vsc9953_analyzer *l2ana_reg; /* Administrative down */ - if (vsc9953_l2sw.port[port_nr].enabled) { + if (!vsc9953_l2sw.port[port_nr].enabled) { printf("Port %d is administrative down\n", port_nr); return -1; } @@ -469,6 +468,47 @@ static void vsc9953_vlan_ingr_fltr_learn_drop(int enable) clrbits_le32(&l2ana_reg->ana.adv_learn, VSC9953_VLAN_CHK); } +enum aggr_code_mode { + AGGR_CODE_RAND = 0, + AGGR_CODE_ALL, /* S/D MAC, IPv4 S/D IP, IPv6 Flow Label, S/D PORT */ +}; + +/* Set aggregation code generation mode */ +static int vsc9953_aggr_code_set(enum aggr_code_mode ac) +{ + int rc; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (ac) { + case AGGR_CODE_RAND: + clrsetbits_le32(&l2ana_reg->common.aggr_cfg, + VSC9953_AC_DMAC_ENA | VSC9953_AC_SMAC_ENA | + VSC9953_AC_IP6_LBL_ENA | + VSC9953_AC_IP6_TCPUDP_ENA | + VSC9953_AC_IP4_SIPDIP_ENA | + VSC9953_AC_IP4_TCPUDP_ENA, VSC9953_AC_RND_ENA); + rc = 0; + break; + case AGGR_CODE_ALL: + clrsetbits_le32(&l2ana_reg->common.aggr_cfg, VSC9953_AC_RND_ENA, + VSC9953_AC_DMAC_ENA | VSC9953_AC_SMAC_ENA | + VSC9953_AC_IP6_LBL_ENA | + VSC9953_AC_IP6_TCPUDP_ENA | + VSC9953_AC_IP4_SIPDIP_ENA | + VSC9953_AC_IP4_TCPUDP_ENA); + rc = 0; + break; + default: + /* unknown mode for aggregation code */ + rc = -EINVAL; + } + + return rc; +} + /* Egress untag modes of a VSC9953 port */ enum egress_untag_mode { EGRESS_UNTAG_ALL = 0, @@ -1493,6 +1533,224 @@ static int vsc9953_port_ingress_filtering_get(int port_no) return !!(val & (1 << port_no)); } +/* Get the aggregation group of a port */ +static int vsc9953_port_aggr_grp_get(int port_no, int *aggr_grp) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + if (!VSC9953_PORT_CHECK(port_no)) + return -EINVAL; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->port[port_no].port_cfg); + *aggr_grp = bitfield_extract_by_mask(val, + VSC9953_PORT_CFG_PORTID_MASK); + + return 0; +} + +static void vsc9953_aggr_grp_members_get(int aggr_grp, + u8 aggr_membr[VSC9953_MAX_PORTS]) +{ + int port_no; + int aggr_membr_grp; + + for (port_no = 0; port_no < VSC9953_MAX_PORTS; port_no++) { + aggr_membr[port_no] = 0; + + if (vsc9953_port_aggr_grp_get(port_no, &aggr_membr_grp)) + continue; + + if (aggr_grp == aggr_membr_grp) + aggr_membr[port_no] = 1; + } +} + +static void vsc9953_update_dest_members_masks(int port_no, u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + u32 pgid; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* + * NOTE: Only the unicast destination masks are updated, since + * we do not support for now Layer-2 multicast entries + */ + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (i == port_no) { + clrsetbits_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], + VSC9953_PGID_PORT_MASK, + membr_bitfld_new); + continue; + } + + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[i]); + if ((u32)(1 << i) & membr_bitfld_old & VSC9953_PGID_PORT_MASK) + pgid &= ~((u32)(1 << port_no)); + if ((u32)(1 << i) & membr_bitfld_new & VSC9953_PGID_PORT_MASK) + pgid |= ((u32)(1 << port_no)); + + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], pgid); + } +} + +static void vsc9953_update_source_members_masks(int port_no, + u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + int index; + u32 pgid; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + for (i = 0; i < VSC9953_MAX_PORTS + 1; i++) { + index = PGID_SRC_START + i; + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[index]); + if (i == port_no) { + pgid = (pgid | VSC9953_PGID_PORT_MASK) & + ~membr_bitfld_new; + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[index], + pgid); + continue; + } + + if ((u32)(1 << i) & membr_bitfld_old & VSC9953_PGID_PORT_MASK) + pgid |= (u32)(1 << port_no); + + if ((u32)(1 << i) & membr_bitfld_new & VSC9953_PGID_PORT_MASK) + pgid &= ~(u32)(1 << port_no); + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[index], pgid); + } +} + +static u32 vsc9953_aggr_mask_get_next(u32 aggr_mask, u32 member_bitfield) +{ + if (!member_bitfield) + return 0; + + if (!(aggr_mask & VSC9953_PGID_PORT_MASK)) + aggr_mask = 1; + else + aggr_mask <<= 1; + + while (!(aggr_mask & member_bitfield)) { + aggr_mask <<= 1; + if (!(aggr_mask & VSC9953_PGID_PORT_MASK)) + aggr_mask = 1; + } + + return aggr_mask; +} + +static void vsc9953_update_aggr_members_masks(int port_no, u32 membr_bitfld_old, + u32 membr_bitfld_new) +{ + int i; + u32 pgid; + u32 aggr_mask_old = 0; + u32 aggr_mask_new = 0; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* Update all the PGID aggregation masks */ + for (i = PGID_AGGR_START; i < PGID_SRC_START; i++) { + pgid = in_le32(&l2ana_reg->port_id_tbl.port_grp_id[i]); + + aggr_mask_old = vsc9953_aggr_mask_get_next(aggr_mask_old, + membr_bitfld_old); + pgid = (pgid & ~membr_bitfld_old) | aggr_mask_old; + + aggr_mask_new = vsc9953_aggr_mask_get_next(aggr_mask_new, + membr_bitfld_new); + pgid = (pgid & ~membr_bitfld_new) | aggr_mask_new; + + out_le32(&l2ana_reg->port_id_tbl.port_grp_id[i], pgid); + } +} + +static u32 vsc9953_aggr_membr_bitfield_get(u8 member[VSC9953_MAX_PORTS]) +{ + int i; + u32 member_bitfield = 0; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (member[i]) + member_bitfield |= 1 << i; + } + member_bitfield &= VSC9953_PGID_PORT_MASK; + + return member_bitfield; +} + +static void vsc9953_update_members_masks(int port_no, + u8 member_old[VSC9953_MAX_PORTS], + u8 member_new[VSC9953_MAX_PORTS]) +{ + u32 membr_bitfld_old = vsc9953_aggr_membr_bitfield_get(member_old); + u32 membr_bitfld_new = vsc9953_aggr_membr_bitfield_get(member_new); + + vsc9953_update_dest_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); + vsc9953_update_source_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); + vsc9953_update_aggr_members_masks(port_no, membr_bitfld_old, + membr_bitfld_new); +} + +/* Set the aggregation group of a port */ +static int vsc9953_port_aggr_grp_set(int port_no, int aggr_grp) +{ + u8 aggr_membr_old[VSC9953_MAX_PORTS]; + u8 aggr_membr_new[VSC9953_MAX_PORTS]; + int rc; + int aggr_grp_old; + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + if (!VSC9953_PORT_CHECK(port_no) || !VSC9953_PORT_CHECK(aggr_grp)) + return -EINVAL; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + rc = vsc9953_port_aggr_grp_get(port_no, &aggr_grp_old); + if (rc) + return rc; + + /* get all the members of the old aggregation group */ + vsc9953_aggr_grp_members_get(aggr_grp_old, aggr_membr_old); + + /* get all the members of the same aggregation group */ + vsc9953_aggr_grp_members_get(aggr_grp, aggr_membr_new); + + /* add current port as member to the new aggregation group */ + aggr_membr_old[port_no] = 0; + aggr_membr_new[port_no] = 1; + + /* update masks */ + vsc9953_update_members_masks(port_no, aggr_membr_old, aggr_membr_new); + + /* Change logical port number */ + val = in_le32(&l2ana_reg->port[port_no].port_cfg); + val = bitfield_replace_by_mask(val, + VSC9953_PORT_CFG_PORTID_MASK, aggr_grp); + out_le32(&l2ana_reg->port[port_no].port_cfg, val); + + return 0; +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -2083,6 +2341,72 @@ static int vsc9953_ingr_fltr_set_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +static int vsc9953_port_aggr_show_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + int aggr_grp; + + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL) { + if (!VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + + if (vsc9953_port_aggr_grp_get(parsed_cmd->port, &aggr_grp)) + return CMD_RET_FAILURE; + printf("%7s %10s\n", "Port", "Aggr grp"); + printf("%7d %10d\n", parsed_cmd->port, aggr_grp); + } else { + printf("%7s %10s\n", "Port", "Aggr grp"); + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_aggr_grp_get(i, &aggr_grp)) + continue; + printf("%7d %10d\n", i, aggr_grp); + } + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_port_aggr_set_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + + /* Aggregation group number should be set in parsed_cmd->aggr_grp */ + if (parsed_cmd->aggr_grp == ETHSW_CMD_AGGR_GRP_NONE) { + printf("Please set an aggregation group value\n"); + return CMD_RET_FAILURE; + } + + if (!VSC9953_PORT_CHECK(parsed_cmd->aggr_grp)) { + printf("Invalid aggregation group number: %d\n", + parsed_cmd->aggr_grp); + return CMD_RET_FAILURE; + } + + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL) { + if (!VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + if (vsc9953_port_aggr_grp_set(parsed_cmd->port, + parsed_cmd->aggr_grp)) { + printf("Port %d: failed to set aggr group %d\n", + parsed_cmd->port, parsed_cmd->aggr_grp); + } + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (vsc9953_port_aggr_grp_set(i, + parsed_cmd->aggr_grp)) { + printf("Port %d: failed to set aggr group %d\n", + i, parsed_cmd->aggr_grp); + } + } + } + + return CMD_RET_SUCCESS; +} + static struct ethsw_command_func vsc9953_cmd_func = { .ethsw_name = "L2 Switch VSC9953", .port_enable = &vsc9953_port_status_key_func, @@ -2107,7 +2431,9 @@ static struct ethsw_command_func vsc9953_cmd_func = { .vlan_learn_show = &vsc9953_vlan_learn_show_key_func, .vlan_learn_set = &vsc9953_vlan_learn_set_key_func, .port_ingr_filt_show = &vsc9953_ingr_fltr_show_key_func, - .port_ingr_filt_set = &vsc9953_ingr_fltr_set_key_func + .port_ingr_filt_set = &vsc9953_ingr_fltr_set_key_func, + .port_aggr_show = &vsc9953_port_aggr_show_key_func, + .port_aggr_set = &vsc9953_port_aggr_set_key_func, }; #endif /* CONFIG_CMD_ETHSW */ @@ -2138,6 +2464,8 @@ void vsc9953_default_configuration(void) vsc9953_vlan_table_membership_all_set(1, 1); vsc9953_vlan_ingr_fltr_learn_drop(1); vsc9953_port_all_vlan_egress_untagged_set(EGRESS_UNTAG_PVID_AND_ZERO); + if (vsc9953_aggr_code_set(AGGR_CODE_ALL)) + debug("VSC9953: failed to set default aggregation code mode\n"); } void vsc9953_init(bd_t *bis) @@ -2196,6 +2524,9 @@ void vsc9953_init(bd_t *bis) if (vsc9953_port_init(i)) printf("Failed to initialize l2switch port %d\n", i); + if (!vsc9953_l2sw.port[i].enabled) + continue; + /* Enable VSC9953 GMII Ports Port ID 0 - 7 */ if (VSC9953_INTERNAL_PORT_CHECK(i)) { out_le32(&l2ana_reg->pfc[i].pfc_cfg, @@ -2208,6 +2539,11 @@ void vsc9953_init(bd_t *bis) out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], VSC9953_MAC_FC_CFG); } + + l2dev_gmii_reg = (struct vsc9953_dev_gmii *) + (VSC9953_OFFSET + VSC9953_DEV_GMII_OFFSET + + T1040_SWITCH_GMII_DEV_OFFSET * i); + out_le32(&l2dev_gmii_reg->port_mode.clock_cfg, VSC9953_CLOCK_CFG); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ena_cfg, @@ -2230,10 +2566,6 @@ void vsc9953_init(bd_t *bis) /* WAIT FOR 2 us*/ udelay(2); - l2dev_gmii_reg = (struct vsc9953_dev_gmii *)( - (char *)l2dev_gmii_reg - + T1040_SWITCH_GMII_DEV_OFFSET); - /* Initialize Lynx PHY Wrappers */ phy_addr = 0; if (vsc9953_l2sw.port[i].enet_if ==