From a058052c358c3ecf5f394ff37def6a45eb26768c Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 9 Dec 2015 11:21:25 -0800 Subject: [PATCH 01/44] net: phy: do not read configuration register on reset When doing a software reset, the reset flag should be written without other bits set. Writing the current state will lead to restoring the state of the PHY (e.g. Powerdown), which is not what is expected from a software reset. Signed-off-by: Stefan Agner Acked-by: Michael Welling Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 51b5746a5a..ef9f13bd46 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -717,15 +717,7 @@ int phy_reset(struct phy_device *phydev) } #endif - reg = phy_read(phydev, devad, MII_BMCR); - if (reg < 0) { - debug("PHY status read failed\n"); - return -1; - } - - reg |= BMCR_RESET; - - if (phy_write(phydev, devad, MII_BMCR, reg) < 0) { + if (phy_write(phydev, devad, MII_BMCR, BMCR_RESET) < 0) { debug("PHY reset failed\n"); return -1; } @@ -738,6 +730,7 @@ int phy_reset(struct phy_device *phydev) * auto-clearing). This should happen within 0.5 seconds per the * IEEE spec. */ + reg = phy_read(phydev, devad, MII_BMCR); while ((reg & BMCR_RESET) && timeout--) { reg = phy_read(phydev, devad, MII_BMCR); From 96979dc94c1743e63cc8cc62c955075e47b7187b Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Tue, 15 Dec 2015 15:21:02 +0200 Subject: [PATCH 02/44] drivers: net: vsc9953: Fix number of reserved registers There are only 21 registers reserved between ana_ana and ana_pgid register groups. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger --- include/vsc9953.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/vsc9953.h b/include/vsc9953.h index cd5cfc76b0..00aa2227f4 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -269,7 +269,7 @@ struct vsc9953_analyzer { struct vsc9953_ana_ana_tables ana_tables; u32 reserved2[14]; struct vsc9953_ana_ana ana; - u32 reserved3[22]; + u32 reserved3[21]; struct vsc9953_ana_pgid port_id_tbl; u32 reserved4[549]; struct vsc9953_ana_pfc pfc[10]; From ba389e65e5bb64506ad35b3d7007ec1b3b9cf4de Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Tue, 15 Dec 2015 15:21:03 +0200 Subject: [PATCH 03/44] drivers: net: vsc9953: Fix FDB aging time By default, the aging period is set to 0, so the dynamic FDB entries are never removed. This patch sets the aging time to 300 seconds. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger --- drivers/net/vsc9953.c | 23 +++++++++++++++++++++++ include/vsc9953.h | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 7595db1acb..160f4782e4 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -593,6 +593,25 @@ static void vsc9953_port_all_vlan_egress_untagged_set( vsc9953_port_vlan_egr_untag_set(i, mode); } +static int vsc9953_autoage_time_set(int age_period) +{ + u32 autoage; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + if (age_period < 0 || age_period > VSC9953_AUTOAGE_PERIOD_MASK) + return -EINVAL; + + autoage = bitfield_replace_by_mask(in_le32(&l2ana_reg->ana.auto_age), + VSC9953_AUTOAGE_PERIOD_MASK, + age_period); + out_le32(&l2ana_reg->ana.auto_age, autoage); + + return 0; +} + #ifdef CONFIG_CMD_ETHSW /* Enable/disable status of a VSC9953 port */ @@ -2107,6 +2126,10 @@ void vsc9953_default_configuration(void) { int i; + if (vsc9953_autoage_time_set(VSC9953_DEFAULT_AGE_TIME)) + debug("VSC9953: failed to set AGE time to %d\n", + VSC9953_DEFAULT_AGE_TIME); + for (i = 0; i < VSC9953_MAX_VLAN; i++) vsc9953_vlan_table_membership_all_set(i, 0); vsc9953_port_all_vlan_aware_set(1); diff --git a/include/vsc9953.h b/include/vsc9953.h index 00aa2227f4..35fcbb51cc 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -136,6 +136,9 @@ /* Macros for vsc9953_ana_ana.adv_learn register */ #define VSC9953_VLAN_CHK 0x00000400 +/* Macros for vsc9953_ana_ana.auto_age register */ +#define VSC9953_AUTOAGE_PERIOD_MASK 0x001ffffe + /* Macros for vsc9953_rew_port.port_tag_cfg register */ #define VSC9953_TAG_CFG_MASK 0x00000180 #define VSC9953_TAG_CFG_NONE 0x00000000 @@ -164,6 +167,7 @@ #define VSC9953_MAX_VLAN 4096 #define VSC9953_VLAN_CHECK(vid) \ (((vid) < 0 || (vid) >= VSC9953_MAX_VLAN) ? 0 : 1) +#define VSC9953_DEFAULT_AGE_TIME 300 #define DEFAULT_VSC9953_MDIO_NAME "VSC9953_MDIO0" From 3507cf7f4a7ad09b017a3890a4a71322238d693f Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Tue, 15 Dec 2015 15:21:04 +0200 Subject: [PATCH 04/44] doc: t1040-l2switch: Update README The driver for VSC9953 L2 switch IP supports many features and the documentation needs to be updated. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger --- doc/README.t1040-l2switch | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/doc/README.t1040-l2switch b/doc/README.t1040-l2switch index 14dbf31bf2..4e0f418c7c 100644 --- a/doc/README.t1040-l2switch +++ b/doc/README.t1040-l2switch @@ -24,16 +24,29 @@ Switch interfaces: Commands Overview: ============= Commands supported - - enable/disable a port - - check a port's link speed, duplexity and status. + - enable/disable a port or show its configuration (speed, duplexity, status, etc.) + - port statistics + - MAC learning + - add/remove FDB entries + - Port-based VLAN + - Private/Shared VLAN learning + - VLAN ingress filtering Commands syntax - ethsw port enable|disable - enable/disable an l2 switch port - ethsw port show - show an l2 switch port's configuration +ethsw [port ] { enable | disable | show } - enable/disable a port; show a port's configuration +ethsw [port ] statistics { [help] | [clear] } - show an l2 switch port's statistics +ethsw [port ] learning { [help] | show | auto | disable } - enable/disable/show learning configuration on a port +ethsw [port ] [vlan ] fdb { [help] | show | flush | { add | del } } - add/delete a mac entry in FDB; use show to see FDB entries; + if [vlan ] is missing, VID 1 will be used +ethsw [port ] pvid { [help] | show | } - set/show PVID (ingress and egress VLAN tagging) for a port +ethsw [port ] vlan { [help] | show | add | del } - add a VLAN to a port (VLAN members) +ethsw [port ] untagged { [help] | show | all | none | pvid } - set egress tagging mode for a port +ethsw [port ] egress tag { [help] | show | pvid | classified } - configure VID source for egress tag. + Tag's VID could be the frame's classified VID or the PVID of the port +ethsw vlan fdb { [help] | show | shared | private } - make VLAN learning shared or private +ethsw [port ] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port - port_nr=0..9; use "all" for all ports - -=> ethsw port all show +=> ethsw show Port Status Link Speed Duplex 0 enabled down 10 half 1 enabled down 10 half From bf9f2ed83b16c6d037609278f66dbf54f40b1903 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Tue, 15 Dec 2015 15:21:05 +0200 Subject: [PATCH 05/44] common: cmd_ethsw: Spelling fixes Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger --- common/cmd_ethsw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c index 8e452e95be..a5ed5557f5 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -71,7 +71,7 @@ static int ethsw_vlan_help_key_func(struct ethsw_command_def *parsed_cmd) #define ETHSW_PORT_UNTAG_HELP "ethsw [port ] untagged " \ "{ [help] | show | all | none | pvid } " \ -" - set egress tagging mod for a port" +" - set egress tagging mode for a port" static int ethsw_port_untag_help_key_func(struct ethsw_command_def *parsed_cmd) { @@ -1010,7 +1010,7 @@ static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #define ETHSW_PORT_CONF_HELP "[port ] { enable | disable | show } " \ -"- enable/disable a port; show shows a port's configuration" +"- enable/disable a port; show a port's configuration" U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, "Ethernet l2 switch commands", From aae0e68909bf2ef1b60d18c54105ab8b9c67c5a2 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Tue, 15 Dec 2015 15:21:06 +0200 Subject: [PATCH 06/44] drivers: net: vsc9953: Add LAG support You can now configure LAG on VSC9953's ports using the command: ethsw [port ] aggr {[help] | show | } A port must belong to a single LAG. By default, a port belongs to a LAG equal to the port's number. For each frame, a hash will be calculated based on Source/Destination MAC addresses, Source/Destination IP(v4/v6) addresses, Source/Destination ports. This hash will be used to select a single egress port from LAG. This also assures that frames from the same flow will always have the same egress port. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger --- common/cmd_ethsw.c | 74 +++++++++ doc/README.t1040-l2switch | 2 + drivers/net/vsc9953.c | 331 +++++++++++++++++++++++++++++++++++++- include/ethsw.h | 6 + include/vsc9953.h | 18 +++ 5 files changed, 430 insertions(+), 1 deletion(-) diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c index a5ed5557f5..491cb8eac3 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -114,6 +114,17 @@ static int ethsw_ingr_fltr_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_PORT_AGGR_HELP "ethsw [port ] aggr" \ +" { [help] | show | } " \ +"- get/set LAG group for a port" + +static int ethsw_port_aggr_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_AGGR_HELP"\n"); + + return CMD_RET_SUCCESS; +} + static struct keywords_to_function { enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; int cmd_func_offset; @@ -532,6 +543,39 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_ingr_filt_set), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_aggr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_aggr, + ethsw_id_aggr_no, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_aggr_set), + .keyword_function = NULL, }, }; @@ -576,6 +620,9 @@ static int keyword_match_pvid(enum ethsw_keyword_id key_id, int argc, static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, char *const argv[], int *argc_nr, struct ethsw_command_def *parsed_cmd); +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); /* * Define properties for each keyword; @@ -661,6 +708,9 @@ struct keyword_def { }, { .keyword_name = "filtering", .match = &keyword_match_gen, + }, { + .keyword_name = "aggr", + .match = &keyword_match_aggr, }, }; @@ -826,6 +876,28 @@ static int keyword_match_mac_addr(enum ethsw_keyword_id key_id, int argc, return 1; } +/* Function used to match the command's aggregation number */ +static int keyword_match_aggr(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + unsigned long val; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 1; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->aggr_grp = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_aggr_no; + } + + return 1; +} + /* Finds optional keywords and modifies *argc_va to skip them */ static void cmd_keywords_opt_check(const struct ethsw_command_def *parsed_cmd, int *argc_val) @@ -984,6 +1056,7 @@ static void command_def_init(struct ethsw_command_def *parsed_cmd) parsed_cmd->port = ETHSW_CMD_PORT_ALL; parsed_cmd->vid = ETHSW_CMD_VLAN_ALL; + parsed_cmd->aggr_grp = ETHSW_CMD_AGGR_GRP_NONE; parsed_cmd->cmd_function = NULL; /* We initialize the MAC address with the Broadcast address */ @@ -1024,4 +1097,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, ETHSW_EGR_VLAN_TAG_HELP"\n" ETHSW_VLAN_FDB_HELP"\n" ETHSW_PORT_INGR_FLTR_HELP"\n" + ETHSW_PORT_AGGR_HELP"\n" ); diff --git a/doc/README.t1040-l2switch b/doc/README.t1040-l2switch index 4e0f418c7c..6f03de239e 100644 --- a/doc/README.t1040-l2switch +++ b/doc/README.t1040-l2switch @@ -31,6 +31,7 @@ Commands supported - Port-based VLAN - Private/Shared VLAN learning - VLAN ingress filtering + - Port LAG Commands syntax ethsw [port ] { enable | disable | show } - enable/disable a port; show a port's configuration @@ -45,6 +46,7 @@ ethsw [port ] egress tag { [help] | show | pvid | classified } - config Tag's VID could be the frame's classified VID or the PVID of the port ethsw vlan fdb { [help] | show | shared | private } - make VLAN learning shared or private ethsw [port ] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port +ethsw [port ] aggr { [help] | show | } - get/set LAG group for a port => ethsw show Port Status Link Speed Duplex diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 160f4782e4..44afe14051 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -469,6 +469,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 +1534,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 +2342,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 +2432,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 +2465,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) diff --git a/include/ethsw.h b/include/ethsw.h index 2d3c12a39e..25f358d128 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -12,6 +12,7 @@ #define ETHSW_MAX_CMD_PARAMS 20 #define ETHSW_CMD_PORT_ALL -1 #define ETHSW_CMD_VLAN_ALL -1 +#define ETHSW_CMD_AGGR_GRP_NONE -1 /* IDs used to track keywords in a command */ enum ethsw_keyword_id { @@ -41,6 +42,7 @@ enum ethsw_keyword_id { ethsw_id_private, ethsw_id_ingress, ethsw_id_filtering, + ethsw_id_aggr, ethsw_id_count, /* keep last */ }; @@ -50,6 +52,7 @@ enum ethsw_keyword_opt_id { ethsw_id_pvid_no, ethsw_id_add_del_no, ethsw_id_add_del_mac, + ethsw_id_aggr_no, ethsw_id_count_all, /* keep last */ }; @@ -58,6 +61,7 @@ struct ethsw_command_def { int cmd_keywords_nr; int port; int vid; + int aggr_grp; uchar ethaddr[6]; int (*cmd_function)(struct ethsw_command_def *parsed_cmd); }; @@ -88,6 +92,8 @@ struct ethsw_command_func { int (*vlan_learn_set)(struct ethsw_command_def *parsed_cmd); int (*port_ingr_filt_show)(struct ethsw_command_def *parsed_cmd); int (*port_ingr_filt_set)(struct ethsw_command_def *parsed_cmd); + int (*port_aggr_show)(struct ethsw_command_def *parsed_cmd); + int (*port_aggr_set)(struct ethsw_command_def *parsed_cmd); }; int ethsw_define_functions(const struct ethsw_command_func *cmd_func); diff --git a/include/vsc9953.h b/include/vsc9953.h index 35fcbb51cc..a2d4554c3b 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -126,6 +126,7 @@ #define VSC9953_PORT_CFG_LEARN_AUTO 0x00000100 #define VSC9953_PORT_CFG_LEARN_CPU 0x00000200 #define VSC9953_PORT_CFG_LEARN_DROP 0x00000400 +#define VSC9953_PORT_CFG_PORTID_MASK 0x0000003c /* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define VSC9953_PORT_ENA 0x00002000 @@ -156,6 +157,19 @@ /* Macros for vsc9953_ana_ana_tables.mach_data register */ #define VSC9953_MACHDATA_VID_MASK 0x1fff0000 +/* Macros for vsc9953_ana_common.aggr_cfg register */ +#define VSC9953_AC_RND_ENA 0x00000080 +#define VSC9953_AC_DMAC_ENA 0x00000040 +#define VSC9953_AC_SMAC_ENA 0x00000020 +#define VSC9953_AC_IP6_LBL_ENA 0x00000010 +#define VSC9953_AC_IP6_TCPUDP_ENA 0x00000008 +#define VSC9953_AC_IP4_SIPDIP_ENA 0x00000004 +#define VSC9953_AC_IP4_TCPUDP_ENA 0x00000002 +#define VSC9953_AC_MASK 0x000000fe + +/* Macros for vsc9953_ana_pgid.port_grp_id[] registers */ +#define VSC9953_PGID_PORT_MASK 0x000003ff + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) @@ -239,6 +253,10 @@ struct vsc9953_ana_ana { u32 port_mode[12]; }; +#define PGID_DST_START 0 +#define PGID_AGGR_START 64 +#define PGID_SRC_START 80 + struct vsc9953_ana_pgid { u32 port_grp_id[91]; }; From 44c42dd40eb58679419abb8ab5f20fb5bff26eac Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Thu, 7 Jan 2016 15:28:23 +0800 Subject: [PATCH 07/44] net: bootp: Ignore packets whose yiaddr is 0 When doing `dhcp`, there is a bad dhcp server in my network which always reply dhcp request with yiaddr 0, which cause uboot can not successfully get ipaddr from the good dhcp server. But the Linux PC can get the ip address even if there is a bad dhcp server. This patch is to fix that even if there is a bad dhcp server, uboot can still get ipaddr and tftp work ok. The way is to ignore the packets from the bad dhcp server by filtering out the yiaddr whose value is 0. Signed-off-by: Peng Fan Cc: Joe Hershberger Reviewed-by: Wolfgang Denk Acked-by: Joe Hershberger --- net/bootp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bootp.c b/net/bootp.c index 8aeddb08ea..8da2e9b8b4 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -995,6 +995,9 @@ static void dhcp_handler(uchar *pkt, unsigned dest, struct in_addr sip, debug("DHCPHandler: got DHCP packet: (src=%d, dst=%d, len=%d) state: " "%d\n", src, dest, len, dhcp_state); + if (net_read_ip(&bp->bp_yiaddr).s_addr == 0) + return; + switch (dhcp_state) { case SELECTING: /* From 89c97842dbbc053535f0d9b2cc2e7987c09d2946 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:12 -0800 Subject: [PATCH 08/44] powerpc: bsc9132qds: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/bsc9132qds/bsc9132qds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/bsc9132qds/bsc9132qds.c b/board/freescale/bsc9132qds/bsc9132qds.c index 586daccb4a..71a7bb55c4 100644 --- a/board/freescale/bsc9132qds/bsc9132qds.c +++ b/board/freescale/bsc9132qds/bsc9132qds.c @@ -227,9 +227,9 @@ int checkboard(void) return 0; } -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[4]; int num = 0; @@ -250,6 +250,7 @@ int board_eth_init(bd_t *bis) fsl_pq_mdio_init(bis, &mdio_info); tsec_eth_init(bis, tsec_info, num); +#endif #ifdef CONFIG_PCI pci_eth_init(bis); @@ -257,7 +258,6 @@ int board_eth_init(bd_t *bis) return 0; } -#endif #define USBMUX_SEL_MASK 0xc0 #define USBMUX_SEL_UART2 0xc0 From 98ae83b5e103ff3ce8804593421f3554e1372af1 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:13 -0800 Subject: [PATCH 09/44] powerpc: c29xpcie: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/c29xpcie/c29xpcie.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/c29xpcie/c29xpcie.c b/board/freescale/c29xpcie/c29xpcie.c index f42d373daf..e325b4db4a 100644 --- a/board/freescale/c29xpcie/c29xpcie.c +++ b/board/freescale/c29xpcie/c29xpcie.c @@ -83,9 +83,9 @@ void pci_init_board(void) } #endif /* ifdef CONFIG_PCI */ -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[2]; int num = 0; @@ -110,10 +110,10 @@ int board_eth_init(bd_t *bis) fsl_pq_mdio_init(bis, &mdio_info); tsec_eth_init(bis, tsec_info, num); +#endif return pci_eth_init(bis); } -#endif #if defined(CONFIG_OF_BOARD_SETUP) void fdt_del_sec(void *blob, int offset) From 4521ae9dca9e0a5aaed95e86fe1be3ccf078abe8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:14 -0800 Subject: [PATCH 10/44] powerpc: mpc8572ds: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/mpc8572ds/mpc8572ds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/mpc8572ds/mpc8572ds.c b/board/freescale/mpc8572ds/mpc8572ds.c index 3f68cf496a..ed6836a930 100644 --- a/board/freescale/mpc8572ds/mpc8572ds.c +++ b/board/freescale/mpc8572ds/mpc8572ds.c @@ -171,9 +171,9 @@ int board_early_init_r(void) return 0; } -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[4]; int num = 0; @@ -226,10 +226,10 @@ int board_eth_init(bd_t *bis) fsl_pq_mdio_init(bis, &mdio_info); tsec_eth_init(bis, tsec_info, num); +#endif return pci_eth_init(bis); } -#endif #if defined(CONFIG_OF_BOARD_SETUP) int ft_board_setup(void *blob, bd_t *bd) From 1adc09544fc4479b1d275e71efa8c55f1446cdee Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:15 -0800 Subject: [PATCH 11/44] powerpc: mpc8548cds: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/mpc8548cds/mpc8548cds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/mpc8548cds/mpc8548cds.c b/board/freescale/mpc8548cds/mpc8548cds.c index ca9b43c6b6..de76d36174 100644 --- a/board/freescale/mpc8548cds/mpc8548cds.c +++ b/board/freescale/mpc8548cds/mpc8548cds.c @@ -301,9 +301,9 @@ void configure_rgmii(void) return; } -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[4]; int num = 0; @@ -345,10 +345,10 @@ int board_eth_init(bd_t *bis) tsec_eth_init(bis, tsec_info, num); configure_rgmii(); +#endif return pci_eth_init(bis); } -#endif #if defined(CONFIG_OF_BOARD_SETUP) void ft_pci_setup(void *blob, bd_t *bd) From c712df1d1d385fbbb1c02803b6ca117162e174c8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:16 -0800 Subject: [PATCH 12/44] powerpc: p1010rdb: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/p1010rdb/p1010rdb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/p1010rdb/p1010rdb.c b/board/freescale/p1010rdb/p1010rdb.c index ebffe9a58a..1ae15407db 100644 --- a/board/freescale/p1010rdb/p1010rdb.c +++ b/board/freescale/p1010rdb/p1010rdb.c @@ -326,9 +326,9 @@ int checkboard(void) return 0; } -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[4]; struct cpu_type *cpu; @@ -362,10 +362,10 @@ int board_eth_init(bd_t *bis) fsl_pq_mdio_init(bis, &mdio_info); tsec_eth_init(bis, tsec_info, num); +#endif return pci_eth_init(bis); } -#endif #if defined(CONFIG_OF_BOARD_SETUP) void fdt_del_flexcan(void *blob) From 9ccb309651912ce71f295b3b2442c36c9cc1e094 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:17 -0800 Subject: [PATCH 13/44] arm: ls1021atwr: Do not wrap pci_eth_init() with CONFIG_TSEC_ENET The call to pci_eth_init() should not be wrapped with CONFIG_TSEC_ENET. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- board/freescale/ls1021atwr/ls1021atwr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/board/freescale/ls1021atwr/ls1021atwr.c b/board/freescale/ls1021atwr/ls1021atwr.c index 8eaff5f0ce..f82e567c84 100644 --- a/board/freescale/ls1021atwr/ls1021atwr.c +++ b/board/freescale/ls1021atwr/ls1021atwr.c @@ -243,9 +243,9 @@ int board_mmc_init(bd_t *bis) } #endif -#ifdef CONFIG_TSEC_ENET int board_eth_init(bd_t *bis) { +#ifdef CONFIG_TSEC_ENET struct fsl_pq_mdio_info mdio_info; struct tsec_info_struct tsec_info[4]; int num = 0; @@ -280,10 +280,10 @@ int board_eth_init(bd_t *bis) fsl_pq_mdio_init(bis, &mdio_info); tsec_eth_init(bis, tsec_info, num); +#endif return pci_eth_init(bis); } -#endif #if !defined(CONFIG_QSPI_BOOT) && !defined(CONFIG_SD_BOOT_QSPI) int config_serdes_mux(void) From 9872b736f957585c6494e727634fe6ed837bcd86 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:18 -0800 Subject: [PATCH 14/44] net: tsec: fsl_mdio: Fix several cosmetic issues Clean up the tsec and fsl_mdio driver codes a little bit, by: - Fix misuse of tab and space here and there - Use correct multi-line comment format - Replace license identifier to GPL-2.0+ Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/fsl_mdio.c | 4 +-- drivers/net/tsec.c | 61 +++++++++++++++++++++++------------------- include/fsl_mdio.h | 7 ++--- include/tsec.h | 51 +++++++++++++++-------------------- 4 files changed, 62 insertions(+), 61 deletions(-) diff --git a/drivers/net/fsl_mdio.c b/drivers/net/fsl_mdio.c index d6b181b386..ae3d035848 100644 --- a/drivers/net/fsl_mdio.c +++ b/drivers/net/fsl_mdio.c @@ -5,6 +5,7 @@ * * SPDX-License-Identifier: GPL-2.0+ */ + #include #include #include @@ -32,8 +33,7 @@ int tsec_local_mdio_read(struct tsec_mii_mng __iomem *phyregs, int port_addr, int value; int timeout = 1000000; - /* Put the address of the phy, and the register - * number into MIIMADD */ + /* Put the address of the phy, and the register number into MIIMADD */ out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f)); /* Clear the command register, and wait */ diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 4bdc188c8f..4aeb387acb 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -1,14 +1,11 @@ /* * Freescale Three Speed Ethernet Controller driver * - * This software may be used and distributed according to the - * terms of the GNU Public License, Version 2, incorporated - * herein by reference. - * * Copyright 2004-2011, 2013 Freescale Semiconductor, Inc. * (C) Copyright 2003, Motorola, Inc. * author Andy Fleming * + * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -84,8 +81,10 @@ static struct tsec_info_struct tsec_info[] = { /* Configure the TBI for SGMII operation */ static void tsec_configure_serdes(struct tsec_private *priv) { - /* Access TBI PHY registers at given TSEC register offset as opposed - * to the register offset used for external PHY accesses */ + /* + * Access TBI PHY registers at given TSEC register offset as opposed + * to the register offset used for external PHY accesses + */ tsec_local_mdio_write(priv->phyregs_sgmii, in_be32(&priv->regs->tbipa), 0, TBI_ANA, TBIANA_SETTINGS); tsec_local_mdio_write(priv->phyregs_sgmii, in_be32(&priv->regs->tbipa), @@ -100,7 +99,8 @@ static void tsec_configure_serdes(struct tsec_private *priv) /* Set the appropriate hash bit for the given addr */ -/* The algorithm works like so: +/* + * The algorithm works like so: * 1) Take the Destination Address (ie the multicast address), and * do a CRC on it (little endian), and reverse the bits of the * result. @@ -111,9 +111,9 @@ static void tsec_configure_serdes(struct tsec_private *priv) * hash index which gaddr register to use, and the 5 other bits * indicate which bit (assuming an IBM numbering scheme, which * for PowerPC (tm) is usually the case) in the register holds - * the entry. */ -static int -tsec_mcast_addr(struct eth_device *dev, const u8 *mcast_mac, u8 set) + * the entry. + */ +static int tsec_mcast_addr(struct eth_device *dev, const u8 *mcast_mac, u8 set) { struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; @@ -135,7 +135,8 @@ tsec_mcast_addr(struct eth_device *dev, const u8 *mcast_mac, u8 set) } #endif /* Multicast TFTP ? */ -/* Initialized required registers to appropriate values, zeroing +/* + * Initialized required registers to appropriate values, zeroing * those we don't care about (unless zero is bad, in which case, * choose a more appropriate value) */ @@ -181,7 +182,8 @@ static void init_registers(struct tsec __iomem *regs) } -/* Configure maccfg2 based on negotiated speed and duplex +/* + * Configure maccfg2 based on negotiated speed and duplex * reported by PHY handling code */ static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) @@ -212,7 +214,8 @@ static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) case 10: maccfg2 |= MACCFG2_MII; - /* Set R100 bit in all modes although + /* + * Set R100 bit in all modes although * it is only used in RGMII mode */ if (phydev->speed == 100) @@ -315,7 +318,8 @@ void redundant_init(struct eth_device *dev) } #endif -/* Set up the buffers and their descriptors, and bring up the +/* + * Set up the buffers and their descriptors, and bring up the * interface */ static void startup_tsec(struct eth_device *dev) @@ -369,9 +373,10 @@ static void startup_tsec(struct eth_device *dev) clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); } -/* This returns the status bits of the device. The return value +/* + * This returns the status bits of the device. The return value * is never checked, and this is what the 8260 driver did, so we - * do the same. Presumably, this would be zero if there were no + * do the same. Presumably, this would be zero if there were no * errors */ static int tsec_send(struct eth_device *dev, void *packet, int length) @@ -446,7 +451,6 @@ static int tsec_recv(struct eth_device *dev) } return -1; - } /* Stop the interface */ @@ -468,10 +472,11 @@ static void tsec_halt(struct eth_device *dev) phy_shutdown(priv->phydev); } -/* Initializes data structures and registers for the controller, - * and brings the interface up. Returns the link status, meaning +/* + * Initializes data structures and registers for the controller, + * and brings the interface up. Returns the link status, meaning * that it returns success if the link is up, failure otherwise. - * This allows u-boot to find the first active controller. + * This allows U-Boot to find the first active controller. */ static int tsec_init(struct eth_device *dev, bd_t * bd) { @@ -489,7 +494,8 @@ static int tsec_init(struct eth_device *dev, bd_t * bd) /* Init ECNTRL */ out_be32(®s->ecntrl, ECNTRL_INIT_SETTINGS); - /* Copy the station address into the address registers. + /* + * Copy the station address into the address registers. * For a station address of 0x12345678ABCD in transmission * order (BE), MACnADDR1 is set to 0xCDAB7856 and * MACnADDR2 is set to 0x34120000. @@ -551,8 +557,8 @@ static phy_interface_t tsec_get_interface(struct tsec_private *priv) * be set by the platform code. */ if ((interface == PHY_INTERFACE_MODE_RGMII_ID) || - (interface == PHY_INTERFACE_MODE_RGMII_TXID) || - (interface == PHY_INTERFACE_MODE_RGMII_RXID)) + (interface == PHY_INTERFACE_MODE_RGMII_TXID) || + (interface == PHY_INTERFACE_MODE_RGMII_RXID)) return interface; return PHY_INTERFACE_MODE_RGMII; @@ -565,8 +571,8 @@ static phy_interface_t tsec_get_interface(struct tsec_private *priv) return PHY_INTERFACE_MODE_MII; } - -/* Discover which PHY is attached to the device, and configure it +/* + * Discover which PHY is attached to the device, and configure it * properly. If the PHY is not recognized, then return 0 * (failure). Otherwise, return 1 */ @@ -605,7 +611,8 @@ static int init_phy(struct eth_device *dev) return 1; } -/* Initialize device structure. Returns success if PHY +/* + * Initialize device structure. Returns success if PHY * initialization succeeded (i.e. if it recognizes the PHY) */ static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) @@ -645,7 +652,7 @@ static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) dev->mcast = tsec_mcast_addr; #endif - /* Tell u-boot to get the addr from the env */ + /* Tell U-Boot to get the addr from the env */ for (i = 0; i < 6; i++) dev->enetaddr[i] = 0; diff --git a/include/fsl_mdio.h b/include/fsl_mdio.h index 2137282df3..25678a9988 100644 --- a/include/fsl_mdio.h +++ b/include/fsl_mdio.h @@ -5,6 +5,7 @@ * * SPDX-License-Identifier: GPL-2.0+ */ + #ifndef __FSL_PHY_H__ #define __FSL_PHY_H__ @@ -27,9 +28,9 @@ int fdt_fixup_phy_connection(void *blob, int offset, phy_interface_t phyc); #define PHY_EXT_PAGE_ACCESS 0x1f /* MII Management Configuration Register */ -#define MIIMCFG_RESET_MGMT 0x80000000 -#define MIIMCFG_MGMT_CLOCK_SELECT 0x00000007 -#define MIIMCFG_INIT_VALUE 0x00000003 +#define MIIMCFG_RESET_MGMT 0x80000000 +#define MIIMCFG_MGMT_CLOCK_SELECT 0x00000007 +#define MIIMCFG_INIT_VALUE 0x00000003 /* MII Management Command Register */ #define MIIMCOM_READ_CYCLE 0x00000001 diff --git a/include/tsec.h b/include/tsec.h index 1119d2cb60..f2aa11abe6 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -3,15 +3,12 @@ * * Driver for the Motorola Triple Speed Ethernet Controller * - * This software may be used and distributed according to the - * terms of the GNU Public License, Version 2, incorporated - * herein by reference. - * * Copyright 2004, 2007, 2009, 2011, 2013 Freescale Semiconductor, Inc. * (C) Copyright 2003, Motorola, Inc. * maintained by Xianghua Xiao (x.xiao@motorola.com) * author Andy Fleming * + * SPDX-License-Identifier: GPL-2.0+ */ #ifndef __TSEC_H @@ -67,11 +64,11 @@ x.mii_devname = DEFAULT_MII_NAME;\ } -#define MAC_ADDR_LEN 6 +#define MAC_ADDR_LEN 6 /* #define TSEC_TIMEOUT 1000000 */ -#define TSEC_TIMEOUT 1000 -#define TOUT_LOOP 1000000 +#define TSEC_TIMEOUT 1000 +#define TOUT_LOOP 1000000 /* TBI register addresses */ #define TBI_CR 0x00 @@ -83,8 +80,8 @@ /* TBI MDIO register bit fields*/ #define TBICON_CLK_SELECT 0x0020 -#define TBIANA_ASYMMETRIC_PAUSE 0x0100 -#define TBIANA_SYMMETRIC_PAUSE 0x0080 +#define TBIANA_ASYMMETRIC_PAUSE 0x0100 +#define TBIANA_SYMMETRIC_PAUSE 0x0080 #define TBIANA_HALF_DUPLEX 0x0040 #define TBIANA_FULL_DUPLEX 0x0020 #define TBICR_PHY_RESET 0x8000 @@ -93,13 +90,12 @@ #define TBICR_FULL_DUPLEX 0x0100 #define TBICR_SPEED1_SET 0x0040 - /* MAC register bits */ #define MACCFG1_SOFT_RESET 0x80000000 #define MACCFG1_RESET_RX_MC 0x00080000 #define MACCFG1_RESET_TX_MC 0x00040000 #define MACCFG1_RESET_RX_FUN 0x00020000 -#define MACCFG1_RESET_TX_FUN 0x00010000 +#define MACCFG1_RESET_TX_FUN 0x00010000 #define MACCFG1_LOOPBACK 0x00000100 #define MACCFG1_RX_FLOW 0x00000020 #define MACCFG1_TX_FLOW 0x00000010 @@ -122,7 +118,7 @@ #define ECNTRL_SGMII_MODE 0x00000002 #ifndef CONFIG_SYS_TBIPA_VALUE - #define CONFIG_SYS_TBIPA_VALUE 0x1f +# define CONFIG_SYS_TBIPA_VALUE 0x1f #endif #define MRBLR_INIT_SETTINGS PKTSIZE_ALIGN @@ -137,7 +133,6 @@ #define TSTAT_CLEAR_THALT 0x80000000 #define RSTAT_CLEAR_RHALT 0x00800000 - #define IEVENT_INIT_CLEAR 0xffffffff #define IEVENT_BABR 0x80000000 #define IEVENT_RXC 0x40000000 @@ -164,11 +159,9 @@ #define IMASK_TXFEN 0x00100000 #define IMASK_RXFEN0 0x00000080 - /* Default Attribute fields */ -#define ATTR_INIT_SETTINGS 0x000000c0 -#define ATTRELI_INIT_SETTINGS 0x00000000 - +#define ATTR_INIT_SETTINGS 0x000000c0 +#define ATTRELI_INIT_SETTINGS 0x00000000 /* TxBD status field bits */ #define TXBD_READY 0x8000 @@ -181,7 +174,7 @@ #define TXBD_HUGEFRAME 0x0080 #define TXBD_LATECOLLISION 0x0080 #define TXBD_RETRYLIMIT 0x0040 -#define TXBD_RETRYCOUNTMASK 0x003c +#define TXBD_RETRYCOUNTMASK 0x003c #define TXBD_UNDERRUN 0x0002 #define TXBD_STATS 0x03ff @@ -204,15 +197,15 @@ #define RXBD_STATS 0x003f struct txbd8 { - uint16_t status; /* Status Fields */ - uint16_t length; /* Buffer length */ - uint32_t bufptr; /* Buffer Pointer */ + uint16_t status; /* Status Fields */ + uint16_t length; /* Buffer length */ + uint32_t bufptr; /* Buffer Pointer */ }; struct rxbd8 { - uint16_t status; /* Status Fields */ - uint16_t length; /* Buffer Length */ - uint32_t bufptr; /* Buffer Pointer */ + uint16_t status; /* Status Fields */ + uint16_t length; /* Buffer Length */ + uint32_t bufptr; /* Buffer Pointer */ }; struct tsec_rmon_mib { @@ -336,15 +329,15 @@ struct tsec { u32 rbdlen; /* RxBD Data Length */ u32 res310[4]; u32 res320; - u32 crbptr; /* Current Receive Buffer Pointer */ + u32 crbptr; /* Current Receive Buffer Pointer */ u32 res328[6]; - u32 mrblr; /* Maximum Receive Buffer Length */ + u32 mrblr; /* Maximum Receive Buffer Length */ u32 res344[16]; - u32 rbptr; /* RxBD Pointer */ + u32 rbptr; /* RxBD Pointer */ u32 res388[30]; /* (0x2_n400) */ u32 res400; - u32 rbase; /* RxBD Base Address */ + u32 rbase; /* RxBD Base Address */ u32 res408[62]; /* MAC Registers (0x2_n500) */ @@ -388,7 +381,7 @@ struct tsec { u32 resc00[256]; }; -#define TSEC_GIGABIT (1 << 0) +#define TSEC_GIGABIT (1 << 0) /* These flags currently only have meaning if we're using the eTSEC */ #define TSEC_REDUCED (1 << 1) /* MAC-PHY interface uses RGMII */ From 362b123f473cbc72d43720f9245c68d174439310 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:19 -0800 Subject: [PATCH 15/44] net: tsec: Move rx_idx and tx_idx to struct tsec_private At present rx_idx and tx_idx are declared as static variables in the driver codes. To support multiple interfaces, move it to struct tsec_private. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/tsec.c | 54 ++++++++++++++++++++++------------------------ include/tsec.h | 2 ++ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 4aeb387acb..943c4b3711 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -23,9 +23,6 @@ DECLARE_GLOBAL_DATA_PTR; #define TX_BUF_CNT 2 -static uint rx_idx; /* index of the current RX buffer */ -static uint tx_idx; /* index of the current TX buffer */ - #ifdef __GNUC__ static struct txbd8 __iomem txbd[TX_BUF_CNT] __aligned(8); static struct rxbd8 __iomem rxbd[PKTBUFSRX] __aligned(8); @@ -280,22 +277,23 @@ void redundant_init(struct eth_device *dev) tsec_send(dev, (void *)pkt, sizeof(pkt)); /* Wait for buffer to be received */ - for (t = 0; in_be16(&rxbd[rx_idx].status) & RXBD_EMPTY; t++) { + for (t = 0; in_be16(&rxbd[priv->rx_idx].status) & RXBD_EMPTY; + t++) { if (t >= 10 * TOUT_LOOP) { printf("%s: tsec: rx error\n", dev->name); break; } } - if (!memcmp(pkt, (void *)net_rx_packets[rx_idx], sizeof(pkt))) + if (!memcmp(pkt, net_rx_packets[priv->rx_idx], sizeof(pkt))) fail = 0; - out_be16(&rxbd[rx_idx].length, 0); + out_be16(&rxbd[priv->rx_idx].length, 0); status = RXBD_EMPTY; - if ((rx_idx + 1) == PKTBUFSRX) + if ((priv->rx_idx + 1) == PKTBUFSRX) status |= RXBD_WRAP; - out_be16(&rxbd[rx_idx].status, status); - rx_idx = (rx_idx + 1) % PKTBUFSRX; + out_be16(&rxbd[priv->rx_idx].status, status); + priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; if (in_be32(®s->ievent) & IEVENT_BSY) { out_be32(®s->ievent, IEVENT_BSY); @@ -330,8 +328,8 @@ static void startup_tsec(struct eth_device *dev) int i; /* reset the indices to zero */ - rx_idx = 0; - tx_idx = 0; + priv->rx_idx = 0; + priv->tx_idx = 0; #ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 uint svr; #endif @@ -388,32 +386,32 @@ static int tsec_send(struct eth_device *dev, void *packet, int length) int i; /* Find an empty buffer descriptor */ - for (i = 0; in_be16(&txbd[tx_idx].status) & TXBD_READY; i++) { + for (i = 0; in_be16(&txbd[priv->tx_idx].status) & TXBD_READY; i++) { if (i >= TOUT_LOOP) { debug("%s: tsec: tx buffers full\n", dev->name); return result; } } - out_be32(&txbd[tx_idx].bufptr, (u32)packet); - out_be16(&txbd[tx_idx].length, length); - status = in_be16(&txbd[tx_idx].status); - out_be16(&txbd[tx_idx].status, status | + out_be32(&txbd[priv->tx_idx].bufptr, (u32)packet); + out_be16(&txbd[priv->tx_idx].length, length); + status = in_be16(&txbd[priv->tx_idx].status); + out_be16(&txbd[priv->tx_idx].status, status | (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT)); /* Tell the DMA to go */ out_be32(®s->tstat, TSTAT_CLEAR_THALT); /* Wait for buffer to be transmitted */ - for (i = 0; in_be16(&txbd[tx_idx].status) & TXBD_READY; i++) { + for (i = 0; in_be16(&txbd[priv->tx_idx].status) & TXBD_READY; i++) { if (i >= TOUT_LOOP) { debug("%s: tsec: tx error\n", dev->name); return result; } } - tx_idx = (tx_idx + 1) % TX_BUF_CNT; - result = in_be16(&txbd[tx_idx].status) & TXBD_STATS; + priv->tx_idx = (priv->tx_idx + 1) % TX_BUF_CNT; + result = in_be16(&txbd[priv->tx_idx].status) & TXBD_STATS; return result; } @@ -423,26 +421,26 @@ static int tsec_recv(struct eth_device *dev) struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; - while (!(in_be16(&rxbd[rx_idx].status) & RXBD_EMPTY)) { - int length = in_be16(&rxbd[rx_idx].length); - uint16_t status = in_be16(&rxbd[rx_idx].status); + while (!(in_be16(&rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { + int length = in_be16(&rxbd[priv->rx_idx].length); + uint16_t status = in_be16(&rxbd[priv->rx_idx].status); + uchar *packet = net_rx_packets[priv->rx_idx]; /* Send the packet up if there were no errors */ if (!(status & RXBD_STATS)) - net_process_received_packet(net_rx_packets[rx_idx], - length - 4); + net_process_received_packet(packet, length - 4); else printf("Got error %x\n", (status & RXBD_STATS)); - out_be16(&rxbd[rx_idx].length, 0); + out_be16(&rxbd[priv->rx_idx].length, 0); status = RXBD_EMPTY; /* Set the wrap bit if this is the last element in the list */ - if ((rx_idx + 1) == PKTBUFSRX) + if ((priv->rx_idx + 1) == PKTBUFSRX) status |= RXBD_WRAP; - out_be16(&rxbd[rx_idx].status, status); + out_be16(&rxbd[priv->rx_idx].status, status); - rx_idx = (rx_idx + 1) % PKTBUFSRX; + priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; } if (in_be32(®s->ievent) & IEVENT_BSY) { diff --git a/include/tsec.h b/include/tsec.h index f2aa11abe6..023f095427 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -396,6 +396,8 @@ struct tsec_private { uint phyaddr; char mii_devname[16]; u32 flags; + uint rx_idx; /* index of the current RX buffer */ + uint tx_idx; /* index of the current TX buffer */ }; struct tsec_info_struct { From e677da9723ede30b3072f8b8e290e9d8daea8642 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:20 -0800 Subject: [PATCH 16/44] net: tsec: Move rxbd and txbd to struct tsec_private rxbd and txbd are declared static with 8 byte alignment requirement, but they can be put into struct tsec_private as well and are natually aligned to 8 byte. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/tsec.c | 69 +++++++++++++++++++++------------------------- include/tsec.h | 4 +++ 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 943c4b3711..9a3b1a9874 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -21,16 +21,6 @@ DECLARE_GLOBAL_DATA_PTR; -#define TX_BUF_CNT 2 - -#ifdef __GNUC__ -static struct txbd8 __iomem txbd[TX_BUF_CNT] __aligned(8); -static struct rxbd8 __iomem rxbd[PKTBUFSRX] __aligned(8); - -#else -#error "rtx must be 64-bit aligned" -#endif - static int tsec_send(struct eth_device *dev, void *packet, int length); /* Default initializations for TSEC controllers. */ @@ -277,7 +267,8 @@ void redundant_init(struct eth_device *dev) tsec_send(dev, (void *)pkt, sizeof(pkt)); /* Wait for buffer to be received */ - for (t = 0; in_be16(&rxbd[priv->rx_idx].status) & RXBD_EMPTY; + for (t = 0; + in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY; t++) { if (t >= 10 * TOUT_LOOP) { printf("%s: tsec: rx error\n", dev->name); @@ -288,11 +279,11 @@ void redundant_init(struct eth_device *dev) if (!memcmp(pkt, net_rx_packets[priv->rx_idx], sizeof(pkt))) fail = 0; - out_be16(&rxbd[priv->rx_idx].length, 0); + out_be16(&priv->rxbd[priv->rx_idx].length, 0); status = RXBD_EMPTY; if ((priv->rx_idx + 1) == PKTBUFSRX) status |= RXBD_WRAP; - out_be16(&rxbd[priv->rx_idx].status, status); + out_be16(&priv->rxbd[priv->rx_idx].status, status); priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; if (in_be32(®s->ievent) & IEVENT_BSY) { @@ -335,26 +326,26 @@ static void startup_tsec(struct eth_device *dev) #endif /* Point to the buffer descriptors */ - out_be32(®s->tbase, (u32)&txbd[0]); - out_be32(®s->rbase, (u32)&rxbd[0]); + out_be32(®s->tbase, (u32)&priv->txbd[0]); + out_be32(®s->rbase, (u32)&priv->rxbd[0]); /* Initialize the Rx Buffer descriptors */ for (i = 0; i < PKTBUFSRX; i++) { - out_be16(&rxbd[i].status, RXBD_EMPTY); - out_be16(&rxbd[i].length, 0); - out_be32(&rxbd[i].bufptr, (u32)net_rx_packets[i]); + out_be16(&priv->rxbd[i].status, RXBD_EMPTY); + out_be16(&priv->rxbd[i].length, 0); + out_be32(&priv->rxbd[i].bufptr, (u32)net_rx_packets[i]); } - status = in_be16(&rxbd[PKTBUFSRX - 1].status); - out_be16(&rxbd[PKTBUFSRX - 1].status, status | RXBD_WRAP); + status = in_be16(&priv->rxbd[PKTBUFSRX - 1].status); + out_be16(&priv->rxbd[PKTBUFSRX - 1].status, status | RXBD_WRAP); /* Initialize the TX Buffer Descriptors */ for (i = 0; i < TX_BUF_CNT; i++) { - out_be16(&txbd[i].status, 0); - out_be16(&txbd[i].length, 0); - out_be32(&txbd[i].bufptr, 0); + out_be16(&priv->txbd[i].status, 0); + out_be16(&priv->txbd[i].length, 0); + out_be32(&priv->txbd[i].bufptr, 0); } - status = in_be16(&txbd[TX_BUF_CNT - 1].status); - out_be16(&txbd[TX_BUF_CNT - 1].status, status | TXBD_WRAP); + status = in_be16(&priv->txbd[TX_BUF_CNT - 1].status); + out_be16(&priv->txbd[TX_BUF_CNT - 1].status, status | TXBD_WRAP); #ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 svr = get_svr(); @@ -386,24 +377,28 @@ static int tsec_send(struct eth_device *dev, void *packet, int length) int i; /* Find an empty buffer descriptor */ - for (i = 0; in_be16(&txbd[priv->tx_idx].status) & TXBD_READY; i++) { + for (i = 0; + in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; + i++) { if (i >= TOUT_LOOP) { debug("%s: tsec: tx buffers full\n", dev->name); return result; } } - out_be32(&txbd[priv->tx_idx].bufptr, (u32)packet); - out_be16(&txbd[priv->tx_idx].length, length); - status = in_be16(&txbd[priv->tx_idx].status); - out_be16(&txbd[priv->tx_idx].status, status | + out_be32(&priv->txbd[priv->tx_idx].bufptr, (u32)packet); + out_be16(&priv->txbd[priv->tx_idx].length, length); + status = in_be16(&priv->txbd[priv->tx_idx].status); + out_be16(&priv->txbd[priv->tx_idx].status, status | (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT)); /* Tell the DMA to go */ out_be32(®s->tstat, TSTAT_CLEAR_THALT); /* Wait for buffer to be transmitted */ - for (i = 0; in_be16(&txbd[priv->tx_idx].status) & TXBD_READY; i++) { + for (i = 0; + in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; + i++) { if (i >= TOUT_LOOP) { debug("%s: tsec: tx error\n", dev->name); return result; @@ -411,7 +406,7 @@ static int tsec_send(struct eth_device *dev, void *packet, int length) } priv->tx_idx = (priv->tx_idx + 1) % TX_BUF_CNT; - result = in_be16(&txbd[priv->tx_idx].status) & TXBD_STATS; + result = in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_STATS; return result; } @@ -421,9 +416,9 @@ static int tsec_recv(struct eth_device *dev) struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; - while (!(in_be16(&rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { - int length = in_be16(&rxbd[priv->rx_idx].length); - uint16_t status = in_be16(&rxbd[priv->rx_idx].status); + while (!(in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { + int length = in_be16(&priv->rxbd[priv->rx_idx].length); + uint16_t status = in_be16(&priv->rxbd[priv->rx_idx].status); uchar *packet = net_rx_packets[priv->rx_idx]; /* Send the packet up if there were no errors */ @@ -432,13 +427,13 @@ static int tsec_recv(struct eth_device *dev) else printf("Got error %x\n", (status & RXBD_STATS)); - out_be16(&rxbd[priv->rx_idx].length, 0); + out_be16(&priv->rxbd[priv->rx_idx].length, 0); status = RXBD_EMPTY; /* Set the wrap bit if this is the last element in the list */ if ((priv->rx_idx + 1) == PKTBUFSRX) status |= RXBD_WRAP; - out_be16(&rxbd[priv->rx_idx].status, status); + out_be16(&priv->rxbd[priv->rx_idx].status, status); priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; } diff --git a/include/tsec.h b/include/tsec.h index 023f095427..72002296e6 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -387,7 +387,11 @@ struct tsec { #define TSEC_REDUCED (1 << 1) /* MAC-PHY interface uses RGMII */ #define TSEC_SGMII (1 << 2) /* MAC-PHY interface uses SGMII */ +#define TX_BUF_CNT 2 + struct tsec_private { + struct txbd8 __iomem txbd[TX_BUF_CNT]; + struct rxbd8 __iomem rxbd[PKTBUFSRX]; struct tsec __iomem *regs; struct tsec_mii_mng __iomem *phyregs_sgmii; struct phy_device *phydev; From 8ba50176fc1bfa3d1e96f298bd86f3e04d095305 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:21 -0800 Subject: [PATCH 17/44] net: tsec: Adjust orders to avoid forward declaration of tsec_send() Adjust static functions in a proper order so that forward declaration of tsec_send() can be avoided. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/tsec.c | 208 ++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 105 deletions(-) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 9a3b1a9874..ea2363e0e7 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -21,8 +21,6 @@ DECLARE_GLOBAL_DATA_PTR; -static int tsec_send(struct eth_device *dev, void *packet, int length); - /* Default initializations for TSEC controllers. */ static struct tsec_info_struct tsec_info[] = { @@ -221,6 +219,109 @@ static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); } +/* + * This returns the status bits of the device. The return value + * is never checked, and this is what the 8260 driver did, so we + * do the same. Presumably, this would be zero if there were no + * errors + */ +static int tsec_send(struct eth_device *dev, void *packet, int length) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + uint16_t status; + int result = 0; + int i; + + /* Find an empty buffer descriptor */ + for (i = 0; + in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; + i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx buffers full\n", dev->name); + return result; + } + } + + out_be32(&priv->txbd[priv->tx_idx].bufptr, (u32)packet); + out_be16(&priv->txbd[priv->tx_idx].length, length); + status = in_be16(&priv->txbd[priv->tx_idx].status); + out_be16(&priv->txbd[priv->tx_idx].status, status | + (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT)); + + /* Tell the DMA to go */ + out_be32(®s->tstat, TSTAT_CLEAR_THALT); + + /* Wait for buffer to be transmitted */ + for (i = 0; + in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; + i++) { + if (i >= TOUT_LOOP) { + debug("%s: tsec: tx error\n", dev->name); + return result; + } + } + + priv->tx_idx = (priv->tx_idx + 1) % TX_BUF_CNT; + result = in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_STATS; + + return result; +} + +static int tsec_recv(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + + while (!(in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { + int length = in_be16(&priv->rxbd[priv->rx_idx].length); + uint16_t status = in_be16(&priv->rxbd[priv->rx_idx].status); + uchar *packet = net_rx_packets[priv->rx_idx]; + + /* Send the packet up if there were no errors */ + if (!(status & RXBD_STATS)) + net_process_received_packet(packet, length - 4); + else + printf("Got error %x\n", (status & RXBD_STATS)); + + out_be16(&priv->rxbd[priv->rx_idx].length, 0); + + status = RXBD_EMPTY; + /* Set the wrap bit if this is the last element in the list */ + if ((priv->rx_idx + 1) == PKTBUFSRX) + status |= RXBD_WRAP; + out_be16(&priv->rxbd[priv->rx_idx].status, status); + + priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; + } + + if (in_be32(®s->ievent) & IEVENT_BSY) { + out_be32(®s->ievent, IEVENT_BSY); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + } + + return -1; +} + +/* Stop the interface */ +static void tsec_halt(struct eth_device *dev) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + + clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + setbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + + while ((in_be32(®s->ievent) & (IEVENT_GRSC | IEVENT_GTSC)) + != (IEVENT_GRSC | IEVENT_GTSC)) + ; + + clrbits_be32(®s->maccfg1, MACCFG1_TX_EN | MACCFG1_RX_EN); + + /* Shut down the PHY, as needed */ + phy_shutdown(priv->phydev); +} + #ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 /* * When MACCFG1[Rx_EN] is enabled during system boot as part @@ -362,109 +463,6 @@ static void startup_tsec(struct eth_device *dev) clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); } -/* - * This returns the status bits of the device. The return value - * is never checked, and this is what the 8260 driver did, so we - * do the same. Presumably, this would be zero if there were no - * errors - */ -static int tsec_send(struct eth_device *dev, void *packet, int length) -{ - struct tsec_private *priv = (struct tsec_private *)dev->priv; - struct tsec __iomem *regs = priv->regs; - uint16_t status; - int result = 0; - int i; - - /* Find an empty buffer descriptor */ - for (i = 0; - in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; - i++) { - if (i >= TOUT_LOOP) { - debug("%s: tsec: tx buffers full\n", dev->name); - return result; - } - } - - out_be32(&priv->txbd[priv->tx_idx].bufptr, (u32)packet); - out_be16(&priv->txbd[priv->tx_idx].length, length); - status = in_be16(&priv->txbd[priv->tx_idx].status); - out_be16(&priv->txbd[priv->tx_idx].status, status | - (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT)); - - /* Tell the DMA to go */ - out_be32(®s->tstat, TSTAT_CLEAR_THALT); - - /* Wait for buffer to be transmitted */ - for (i = 0; - in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_READY; - i++) { - if (i >= TOUT_LOOP) { - debug("%s: tsec: tx error\n", dev->name); - return result; - } - } - - priv->tx_idx = (priv->tx_idx + 1) % TX_BUF_CNT; - result = in_be16(&priv->txbd[priv->tx_idx].status) & TXBD_STATS; - - return result; -} - -static int tsec_recv(struct eth_device *dev) -{ - struct tsec_private *priv = (struct tsec_private *)dev->priv; - struct tsec __iomem *regs = priv->regs; - - while (!(in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { - int length = in_be16(&priv->rxbd[priv->rx_idx].length); - uint16_t status = in_be16(&priv->rxbd[priv->rx_idx].status); - uchar *packet = net_rx_packets[priv->rx_idx]; - - /* Send the packet up if there were no errors */ - if (!(status & RXBD_STATS)) - net_process_received_packet(packet, length - 4); - else - printf("Got error %x\n", (status & RXBD_STATS)); - - out_be16(&priv->rxbd[priv->rx_idx].length, 0); - - status = RXBD_EMPTY; - /* Set the wrap bit if this is the last element in the list */ - if ((priv->rx_idx + 1) == PKTBUFSRX) - status |= RXBD_WRAP; - out_be16(&priv->rxbd[priv->rx_idx].status, status); - - priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; - } - - if (in_be32(®s->ievent) & IEVENT_BSY) { - out_be32(®s->ievent, IEVENT_BSY); - out_be32(®s->rstat, RSTAT_CLEAR_RHALT); - } - - return -1; -} - -/* Stop the interface */ -static void tsec_halt(struct eth_device *dev) -{ - struct tsec_private *priv = (struct tsec_private *)dev->priv; - struct tsec __iomem *regs = priv->regs; - - clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); - setbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); - - while ((in_be32(®s->ievent) & (IEVENT_GRSC | IEVENT_GTSC)) - != (IEVENT_GRSC | IEVENT_GTSC)) - ; - - clrbits_be32(®s->maccfg1, MACCFG1_TX_EN | MACCFG1_RX_EN); - - /* Shut down the PHY, as needed */ - phy_shutdown(priv->phydev); -} - /* * Initializes data structures and registers for the controller, * and brings the interface up. Returns the link status, meaning From 56a27a1e6cbc414a8d5b9e59314119777c092635 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:22 -0800 Subject: [PATCH 18/44] net: tsec: Use tsec_private pointer as the parameter for internal routines For internal routines like redundant_init(), startup_tsec() and init_phy(), change to use tsec_private pointer as the parameter. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- drivers/net/tsec.c | 23 +++++++++++------------ include/tsec.h | 1 + 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index ea2363e0e7..7e8bc68e80 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -328,9 +328,8 @@ static void tsec_halt(struct eth_device *dev) * of the eTSEC port initialization sequence, * the eTSEC Rx logic may not be properly initialized. */ -void redundant_init(struct eth_device *dev) +void redundant_init(struct tsec_private *priv) { - struct tsec_private *priv = dev->priv; struct tsec __iomem *regs = priv->regs; uint t, count = 0; int fail = 1; @@ -365,14 +364,14 @@ void redundant_init(struct eth_device *dev) do { uint16_t status; - tsec_send(dev, (void *)pkt, sizeof(pkt)); + tsec_send(priv->dev, (void *)pkt, sizeof(pkt)); /* Wait for buffer to be received */ for (t = 0; in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY; t++) { if (t >= 10 * TOUT_LOOP) { - printf("%s: tsec: rx error\n", dev->name); + printf("%s: tsec: rx error\n", priv->dev->name); break; } } @@ -412,9 +411,8 @@ void redundant_init(struct eth_device *dev) * Set up the buffers and their descriptors, and bring up the * interface */ -static void startup_tsec(struct eth_device *dev) +static void startup_tsec(struct tsec_private *priv) { - struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; uint16_t status; int i; @@ -451,7 +449,7 @@ static void startup_tsec(struct eth_device *dev) #ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 svr = get_svr(); if ((SVR_MAJ(svr) == 1) || IS_SVR_REV(svr, 2, 0)) - redundant_init(dev); + redundant_init(priv); #endif /* Enable Transmit and Receive */ setbits_be32(®s->maccfg1, MACCFG1_RX_EN | MACCFG1_TX_EN); @@ -504,7 +502,7 @@ static int tsec_init(struct eth_device *dev, bd_t * bd) init_registers(regs); /* Ready the device for tx/rx */ - startup_tsec(dev); + startup_tsec(priv); /* Start up the PHY */ ret = phy_startup(priv->phydev); @@ -567,9 +565,8 @@ static phy_interface_t tsec_get_interface(struct tsec_private *priv) * properly. If the PHY is not recognized, then return 0 * (failure). Otherwise, return 1 */ -static int init_phy(struct eth_device *dev) +static int init_phy(struct tsec_private *priv) { - struct tsec_private *priv = (struct tsec_private *)dev->priv; struct phy_device *phydev; struct tsec __iomem *regs = priv->regs; u32 supported = (SUPPORTED_10baseT_Half | @@ -588,7 +585,8 @@ static int init_phy(struct eth_device *dev) if (priv->interface == PHY_INTERFACE_MODE_SGMII) tsec_configure_serdes(priv); - phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); + phydev = phy_connect(priv->bus, priv->phyaddr, priv->dev, + priv->interface); if (!phydev) return 0; @@ -633,6 +631,7 @@ static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) sprintf(dev->name, tsec_info->devname); priv->interface = tsec_info->interface; priv->bus = miiphy_get_dev_by_name(tsec_info->mii_devname); + priv->dev = dev; dev->iobase = 0; dev->priv = priv; dev->init = tsec_init; @@ -655,7 +654,7 @@ static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) clrbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); /* Try to initialize PHY here, and return */ - return init_phy(dev); + return init_phy(priv); } /* diff --git a/include/tsec.h b/include/tsec.h index 72002296e6..e8b03d64f5 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -402,6 +402,7 @@ struct tsec_private { u32 flags; uint rx_idx; /* index of the current RX buffer */ uint tx_idx; /* index of the current TX buffer */ + struct eth_device *dev; }; struct tsec_info_struct { From 69a00875e3db178cfcb19ea8ab97c8927a11e593 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:23 -0800 Subject: [PATCH 19/44] doc: dt-bindings: Describe Freescale TSEC ethernet controller Adapted from the same file name in the kernel device tree bindings documentation, to use with U-Boot. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- doc/device-tree-bindings/net/fsl-tsec-phy.txt | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 doc/device-tree-bindings/net/fsl-tsec-phy.txt diff --git a/doc/device-tree-bindings/net/fsl-tsec-phy.txt b/doc/device-tree-bindings/net/fsl-tsec-phy.txt new file mode 100644 index 0000000000..dbe91aa161 --- /dev/null +++ b/doc/device-tree-bindings/net/fsl-tsec-phy.txt @@ -0,0 +1,43 @@ +* TSEC-compatible ethernet nodes + +Properties: + + - compatible : Should be "fsl,tsec" + - reg : Offset and length of the register set for the device + - phy-handle : See ethernet.txt file in the same directory. + - phy-connection-type : See ethernet.txt file in the same directory. This + property is only really needed if the connection is of type "rgmii-id", + "rgmii-rxid" and "rgmii-txid" as all other connection types are detected + by hardware. + +Example: + ethernet@24000 { + compatible = "fsl,tsec"; + reg = <0x24000 0x1000>; + phy-handle = <&phy0>; + phy-connection-type = "sgmii"; + }; + +Child nodes of the TSEC controller are typically the individual PHY devices +connected via the MDIO bus (sometimes the MDIO bus controller is separate). + +* MDIO IO device + +The MDIO is a bus to which the PHY devices are connected. For each +device that exists on this bus, a PHY node should be created. + +Required properties: + - compatible : Should define the compatible device type for the + mdio. Currently supported string/device is "fsl,tsec-mdio". + - reg : Offset and length of the register set for the device + +Example: + + mdio@24520 { + compatible = "fsl,tsec-mdio"; + reg = <0x24520 0x20>; + + ethernet-phy@0 { + reg = <0>; + }; + }; From 9a1d6af55ecd73938d49076422e87da9f87fc68f Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:24 -0800 Subject: [PATCH 20/44] net: tsec: Add driver model ethernet support This adds driver model support to Freescale TSEC ethernet driver. Signed-off-by: Bin Meng Reviewed-by: Simon Glass Acked-by: Joe Hershberger --- drivers/net/tsec.c | 186 +++++++++++++++++++++++++++++++++++++++++++++ include/tsec.h | 10 +++ 2 files changed, 196 insertions(+) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 7e8bc68e80..18b44f6542 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,7 @@ DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_DM_ETH /* Default initializations for TSEC controllers. */ static struct tsec_info_struct tsec_info[] = { @@ -46,6 +48,7 @@ static struct tsec_info_struct tsec_info[] = { STD_TSEC_INFO(4), /* TSEC4 */ #endif }; +#endif /* CONFIG_DM_ETH */ #define TBIANA_SETTINGS ( \ TBIANA_ASYMMETRIC_PAUSE \ @@ -98,7 +101,11 @@ static void tsec_configure_serdes(struct tsec_private *priv) * for PowerPC (tm) is usually the case) in the register holds * the entry. */ +#ifndef CONFIG_DM_ETH static int tsec_mcast_addr(struct eth_device *dev, const u8 *mcast_mac, u8 set) +#else +static int tsec_mcast_addr(struct udevice *dev, const u8 *mcast_mac, int set) +#endif { struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; @@ -225,7 +232,11 @@ static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) * do the same. Presumably, this would be zero if there were no * errors */ +#ifndef CONFIG_DM_ETH static int tsec_send(struct eth_device *dev, void *packet, int length) +#else +static int tsec_send(struct udevice *dev, void *packet, int length) +#endif { struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; @@ -268,6 +279,7 @@ static int tsec_send(struct eth_device *dev, void *packet, int length) return result; } +#ifndef CONFIG_DM_ETH static int tsec_recv(struct eth_device *dev) { struct tsec_private *priv = (struct tsec_private *)dev->priv; @@ -302,9 +314,61 @@ static int tsec_recv(struct eth_device *dev) return -1; } +#else +static int tsec_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + struct tsec __iomem *regs = priv->regs; + int ret = -1; + + if (!(in_be16(&priv->rxbd[priv->rx_idx].status) & RXBD_EMPTY)) { + int length = in_be16(&priv->rxbd[priv->rx_idx].length); + uint16_t status = in_be16(&priv->rxbd[priv->rx_idx].status); + uint32_t buf; + + /* Send the packet up if there were no errors */ + if (!(status & RXBD_STATS)) { + buf = in_be32(&priv->rxbd[priv->rx_idx].bufptr); + *packetp = (uchar *)buf; + ret = length - 4; + } else { + printf("Got error %x\n", (status & RXBD_STATS)); + } + } + + if (in_be32(®s->ievent) & IEVENT_BSY) { + out_be32(®s->ievent, IEVENT_BSY); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + } + + return ret; +} + +static int tsec_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct tsec_private *priv = (struct tsec_private *)dev->priv; + uint16_t status; + + out_be16(&priv->rxbd[priv->rx_idx].length, 0); + + status = RXBD_EMPTY; + /* Set the wrap bit if this is the last element in the list */ + if ((priv->rx_idx + 1) == PKTBUFSRX) + status |= RXBD_WRAP; + out_be16(&priv->rxbd[priv->rx_idx].status, status); + + priv->rx_idx = (priv->rx_idx + 1) % PKTBUFSRX; + + return 0; +} +#endif /* Stop the interface */ +#ifndef CONFIG_DM_ETH static void tsec_halt(struct eth_device *dev) +#else +static void tsec_halt(struct udevice *dev) +#endif { struct tsec_private *priv = (struct tsec_private *)dev->priv; struct tsec __iomem *regs = priv->regs; @@ -467,9 +531,16 @@ static void startup_tsec(struct tsec_private *priv) * that it returns success if the link is up, failure otherwise. * This allows U-Boot to find the first active controller. */ +#ifndef CONFIG_DM_ETH static int tsec_init(struct eth_device *dev, bd_t * bd) +#else +static int tsec_init(struct udevice *dev) +#endif { struct tsec_private *priv = (struct tsec_private *)dev->priv; +#ifdef CONFIG_DM_ETH + struct eth_pdata *pdata = dev_get_platdata(dev); +#endif struct tsec __iomem *regs = priv->regs; u32 tempval; int ret; @@ -489,12 +560,21 @@ static int tsec_init(struct eth_device *dev, bd_t * bd) * order (BE), MACnADDR1 is set to 0xCDAB7856 and * MACnADDR2 is set to 0x34120000. */ +#ifndef CONFIG_DM_ETH tempval = (dev->enetaddr[5] << 24) | (dev->enetaddr[4] << 16) | (dev->enetaddr[3] << 8) | dev->enetaddr[2]; +#else + tempval = (pdata->enetaddr[5] << 24) | (pdata->enetaddr[4] << 16) | + (pdata->enetaddr[3] << 8) | pdata->enetaddr[2]; +#endif out_be32(®s->macstnaddr1, tempval); +#ifndef CONFIG_DM_ETH tempval = (dev->enetaddr[1] << 24) | (dev->enetaddr[0] << 16); +#else + tempval = (pdata->enetaddr[1] << 24) | (pdata->enetaddr[0] << 16); +#endif out_be32(®s->macstnaddr2, tempval); @@ -600,6 +680,7 @@ static int init_phy(struct tsec_private *priv) return 1; } +#ifndef CONFIG_DM_ETH /* * Initialize device structure. Returns success if PHY * initialization succeeded (i.e. if it recognizes the PHY) @@ -687,3 +768,108 @@ int tsec_standard_init(bd_t *bis) return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info)); } +#else /* CONFIG_DM_ETH */ +int tsec_probe(struct udevice *dev) +{ + struct tsec_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + struct fsl_pq_mdio_info mdio_info; + int offset = 0; + int reg; + const char *phy_mode; + int ret; + + pdata->iobase = (phys_addr_t)dev_get_addr(dev); + priv->regs = (struct tsec *)pdata->iobase; + + offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset, + "phy-handle"); + if (offset > 0) { + reg = fdtdec_get_int(gd->fdt_blob, offset, "reg", 0); + priv->phyaddr = reg; + } else { + debug("phy-handle does not exist under tsec %s\n", dev->name); + return -ENOENT; + } + + offset = fdt_parent_offset(gd->fdt_blob, offset); + if (offset > 0) { + reg = fdtdec_get_int(gd->fdt_blob, offset, "reg", 0); + priv->phyregs_sgmii = (struct tsec_mii_mng *)(reg + 0x520); + } else { + debug("No parent node for PHY?\n"); + return -ENOENT; + } + + phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, + "phy-connection-type", NULL); + if (phy_mode) + pdata->phy_interface = phy_get_interface_by_name(phy_mode); + if (pdata->phy_interface == -1) { + debug("Invalid PHY interface '%s'\n", phy_mode); + return -EINVAL; + } + priv->interface = pdata->phy_interface; + + /* Initialize flags */ + priv->flags = TSEC_GIGABIT; + if (priv->interface == PHY_INTERFACE_MODE_SGMII) + priv->flags |= TSEC_SGMII; + + mdio_info.regs = priv->phyregs_sgmii; + mdio_info.name = (char *)dev->name; + ret = fsl_pq_mdio_init(NULL, &mdio_info); + if (ret) + return ret; + + /* Reset the MAC */ + setbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); + udelay(2); /* Soft Reset must be asserted for 3 TX clocks */ + clrbits_be32(&priv->regs->maccfg1, MACCFG1_SOFT_RESET); + + priv->dev = dev; + priv->bus = miiphy_get_dev_by_name(dev->name); + + /* Try to initialize PHY here, and return */ + return !init_phy(priv); +} + +int tsec_remove(struct udevice *dev) +{ + struct tsec_private *priv = dev->priv; + + free(priv->phydev); + mdio_unregister(priv->bus); + mdio_free(priv->bus); + + return 0; +} + +static const struct eth_ops tsec_ops = { + .start = tsec_init, + .send = tsec_send, + .recv = tsec_recv, + .free_pkt = tsec_free_pkt, + .stop = tsec_halt, +#ifdef CONFIG_MCAST_TFTP + .mcast = tsec_mcast_addr, +#endif +}; + +static const struct udevice_id tsec_ids[] = { + { .compatible = "fsl,tsec" }, + { } +}; + +U_BOOT_DRIVER(eth_tsec) = { + .name = "tsec", + .id = UCLASS_ETH, + .of_match = tsec_ids, + .probe = tsec_probe, + .remove = tsec_remove, + .ops = &tsec_ops, + .priv_auto_alloc_size = sizeof(struct tsec_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; +#endif /* CONFIG_DM_ETH */ diff --git a/include/tsec.h b/include/tsec.h index e8b03d64f5..88ce964fd9 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -18,6 +18,8 @@ #include #include +#ifndef CONFIG_DM_ETH + #ifdef CONFIG_LS102XA #define TSEC_SIZE 0x40000 #define TSEC_MDIO_OFFSET 0x40000 @@ -64,6 +66,8 @@ x.mii_devname = DEFAULT_MII_NAME;\ } +#endif /* CONFIG_DM_ETH */ + #define MAC_ADDR_LEN 6 /* #define TSEC_TIMEOUT 1000000 */ @@ -402,7 +406,11 @@ struct tsec_private { u32 flags; uint rx_idx; /* index of the current RX buffer */ uint tx_idx; /* index of the current TX buffer */ +#ifndef CONFIG_DM_ETH struct eth_device *dev; +#else + struct udevice *dev; +#endif }; struct tsec_info_struct { @@ -415,7 +423,9 @@ struct tsec_info_struct { u32 flags; }; +#ifndef CONFIG_DM_ETH int tsec_standard_init(bd_t *bis); int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsec_info, int num); +#endif #endif /* __TSEC_H */ From a1c76c150831168c09810006ede95b164fa292df Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:25 -0800 Subject: [PATCH 21/44] net: tsec: Use priv->tbiaddr to initialize TBI PHY address Add a new member 'tbiaddr' to tsec_private struct. For non-DM driver, it is initialized as CONFIG_SYS_TBIPA_VALUE, but for DM driver, we can get this from device tree. Update the bindings doc as well. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- doc/device-tree-bindings/net/fsl-tsec-phy.txt | 21 +++++++++++++++++++ drivers/net/tsec.c | 13 +++++++++++- include/tsec.h | 1 + 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/doc/device-tree-bindings/net/fsl-tsec-phy.txt b/doc/device-tree-bindings/net/fsl-tsec-phy.txt index dbe91aa161..c5bf48c3cb 100644 --- a/doc/device-tree-bindings/net/fsl-tsec-phy.txt +++ b/doc/device-tree-bindings/net/fsl-tsec-phy.txt @@ -41,3 +41,24 @@ Example: reg = <0>; }; }; + +* TBI Internal MDIO bus + +As of this writing, every tsec is associated with an internal TBI PHY. +This PHY is accessed through the local MDIO bus. These buses are defined +similarly to the mdio buses. The TBI PHYs underneath them are similar to +normal PHYs, but the reg property is considered instructive, rather than +descriptive. The reg property should be chosen so it doesn't interfere +with other PHYs on the bus. The TBI PHYs are referred to by a "tbi-handle" +property under the tsec node, which has a similar meaning of "phy-handle". + +Example: + ethernet@24000 { + phy-handle = <&tbi1>; + }; + + mdio@24520 { + tbi1: tbi-phy@1f { + reg = <0x1f>; + }; + }; diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 18b44f6542..025e7a76f1 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -658,7 +658,7 @@ static int init_phy(struct tsec_private *priv) supported |= SUPPORTED_1000baseT_Full; /* Assign a Physical address to the TBI */ - out_be32(®s->tbipa, CONFIG_SYS_TBIPA_VALUE); + out_be32(®s->tbipa, priv->tbiaddr); priv->interface = tsec_get_interface(priv); @@ -707,6 +707,7 @@ static int tsec_initialize(bd_t *bis, struct tsec_info_struct *tsec_info) priv->phyregs_sgmii = tsec_info->miiregs_sgmii; priv->phyaddr = tsec_info->phyaddr; + priv->tbiaddr = CONFIG_SYS_TBIPA_VALUE; priv->flags = tsec_info->flags; sprintf(dev->name, tsec_info->devname); @@ -801,6 +802,16 @@ int tsec_probe(struct udevice *dev) return -ENOENT; } + offset = fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset, + "tbi-handle"); + if (offset > 0) { + reg = fdtdec_get_int(gd->fdt_blob, offset, "reg", + CONFIG_SYS_TBIPA_VALUE); + priv->tbiaddr = reg; + } else { + priv->tbiaddr = CONFIG_SYS_TBIPA_VALUE; + } + phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-connection-type", NULL); if (phy_mode) diff --git a/include/tsec.h b/include/tsec.h index 88ce964fd9..fb27edf225 100644 --- a/include/tsec.h +++ b/include/tsec.h @@ -402,6 +402,7 @@ struct tsec_private { phy_interface_t interface; struct mii_dev *bus; uint phyaddr; + uint tbiaddr; char mii_devname[16]; u32 flags; uint rx_idx; /* index of the current RX buffer */ From afe6462da9f68e872a9aeae1dc5389184819c1cf Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 11 Jan 2016 22:41:26 -0800 Subject: [PATCH 22/44] arm: ls102xa: Rewrite the logic of ft_fixup_enet_phy_connect_type() eth_get_dev_by_index() is an API which is not available in driver model. Use eth_get_dev_by_name() instead, which can also simplifly the code logic a little bit. Signed-off-by: Bin Meng Acked-by: Joe Hershberger --- arch/arm/cpu/armv7/ls102xa/fdt.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/arch/arm/cpu/armv7/ls102xa/fdt.c b/arch/arm/cpu/armv7/ls102xa/fdt.c index 856abed941..ae5e794230 100644 --- a/arch/arm/cpu/armv7/ls102xa/fdt.c +++ b/arch/arm/cpu/armv7/ls102xa/fdt.c @@ -30,17 +30,13 @@ void ft_fixup_enet_phy_connect_type(void *fdt) int phy_node; int i = 0; uint32_t ph; + char *name[3] = { "eTSEC1", "eTSEC2", "eTSEC3" }; - while ((dev = eth_get_dev_by_index(i++)) != NULL) { - if (strstr(dev->name, "eTSEC1")) { - strcpy(enet, "ethernet0"); - strcpy(phy, "enet0_rgmii_phy"); - } else if (strstr(dev->name, "eTSEC2")) { - strcpy(enet, "ethernet1"); - strcpy(phy, "enet1_rgmii_phy"); - } else if (strstr(dev->name, "eTSEC3")) { - strcpy(enet, "ethernet2"); - strcpy(phy, "enet2_rgmii_phy"); + for (; i < ARRAY_SIZE(name); i++) { + dev = eth_get_dev_by_name(name[i]); + if (dev) { + sprintf(enet, "ethernet%d", i); + sprintf(phy, "enet%d_rgmii_phy", i); } else { continue; } From bbdcaff12ad8c32100b5e1fdf9b9ae6931c3ba0d Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 13 Jan 2016 16:59:31 +0300 Subject: [PATCH 23/44] net: phy: ensure Gigabit features are masked off if requested When a Gigabit PHY device is connected to a 10/100Mbits capable Ethernet MAC, the driver will restrict the phydev->supported modes to mask off Gigabit. If the Gigabit PHY comes out of reset with the Gigabit features set by default in MII_CTRL1000, it will keep advertising these feature, so by the time we call genphy_config_advert(), the condition on phydev->supported having the Gigabit features on is false, and we do not update MII_CTRL1000 with updated values, and we keep advertising Gigabit features, eventually configuring the PHY for Gigabit whilst the Ethernet MAC does not support that. This patches fixes the problem by ensuring that the Gigabit feature bits are always cleared in MII_CTRL1000, if the PHY happens to be a Gigabit PHY, and then, if Gigabit features are supported, setting those and updating MII_CTRL1000 accordingly. This is a copy of patch from Linux kernel, see http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5273e3a5ca94fbeb8e07d31203069220d5e682aa Signed-off-by: Florian Fainelli Signed-off-by: Alexey Brodkin Cc: Joe Hershberger Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 49 ++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ef9f13bd46..aac9aa33f8 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -38,16 +38,16 @@ DECLARE_GLOBAL_DATA_PTR; static int genphy_config_advert(struct phy_device *phydev) { u32 advertise; - int oldadv, adv; + int oldadv, adv, bmsr; int err, changed = 0; - /* Only allow advertising what - * this PHY supports */ + /* Only allow advertising what this PHY supports */ phydev->advertising &= phydev->supported; advertise = phydev->advertising; /* Setup standard advertisement */ - oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + oldadv = adv; if (adv < 0) return adv; @@ -79,30 +79,41 @@ static int genphy_config_advert(struct phy_device *phydev) changed = 1; } + bmsr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all + * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a + * logical 1. + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + /* Configure gigabit if it's supported */ + adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + oldadv = adv; + + if (adv < 0) + return adv; + + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { - oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); - - if (adv < 0) - return adv; - - adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); if (advertise & SUPPORTED_1000baseT_Half) adv |= ADVERTISE_1000HALF; if (advertise & SUPPORTED_1000baseT_Full) adv |= ADVERTISE_1000FULL; - - if (adv != oldadv) { - err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, - adv); - - if (err < 0) - return err; - changed = 1; - } } + if (adv != oldadv) + changed = 1; + + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv); + if (err < 0) + return err; + return changed; } From 44bc317487386117a9a08d50be4cb105d512224c Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 13 Jan 2016 16:59:32 +0300 Subject: [PATCH 24/44] net: phy: genphy: Allow overwriting features of_set_phy_supported allows overwiting hardware capabilities of a phy with values from the devicetree. This does not work with the genphy driver though because the genphys config_init function will overwrite all values adjusted by of_set_phy_supported. Fix this by initialising the genphy features in the phy_driver struct and in config_init just limit the features to the ones the hardware can actually support. The resulting features are a subset of the devicetree specified features and the hardware features. This is a copy of the patch from Linux kernel, see http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=c242a47238fa2a6a54af8a16e62b54e6e031d4bc Signed-off-by: Sascha Hauer Signed-off-by: Alexey Brodkin Cc: Joe Hershberger Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index aac9aa33f8..d25bec27f0 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -382,8 +382,6 @@ int genphy_config(struct phy_device *phydev) int val; u32 features; - /* For now, I'll claim that the generic driver supports - * all possible port types */ features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_BNC); @@ -422,8 +420,8 @@ int genphy_config(struct phy_device *phydev) features |= SUPPORTED_1000baseX_Half; } - phydev->supported = features; - phydev->advertising = features; + phydev->supported &= features; + phydev->advertising &= features; genphy_config_aneg(phydev); @@ -447,7 +445,9 @@ static struct phy_driver genphy_driver = { .uid = 0xffffffff, .mask = 0xffffffff, .name = "Generic PHY", - .features = 0, + .features = PHY_GBIT_FEATURES | SUPPORTED_MII | + SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC, .config = genphy_config, .startup = genphy_startup, .shutdown = genphy_shutdown, From 4dae610b657fa0c7864cf68d4f44c786ff968c83 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 13 Jan 2016 16:59:33 +0300 Subject: [PATCH 25/44] net: phy: breakdown PHY_*_FEATURES defines Breakdown the PHY_*_FEATURES into per speed defines such that we can easily re-use them individually. Signed-off-by: Florian Fainelli Signed-off-by: Alexey Brodkin Cc: Joe Hershberger Acked-by: Joe Hershberger --- include/phy.h | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/include/phy.h b/include/phy.h index 66cf61bdfb..b793e90532 100644 --- a/include/phy.h +++ b/include/phy.h @@ -17,18 +17,26 @@ #define PHY_MAX_ADDR 32 -#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ - SUPPORTED_10baseT_Full | \ - SUPPORTED_100baseT_Half | \ - SUPPORTED_100baseT_Full | \ - SUPPORTED_Autoneg | \ +#define PHY_DEFAULT_FEATURES (SUPPORTED_Autoneg | \ SUPPORTED_TP | \ SUPPORTED_MII) -#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \ - SUPPORTED_1000baseT_Half | \ +#define PHY_10BT_FEATURES (SUPPORTED_10baseT_Half | \ + SUPPORTED_10baseT_Full) + +#define PHY_100BT_FEATURES (SUPPORTED_100baseT_Half | \ + SUPPORTED_100baseT_Full) + +#define PHY_1000BT_FEATURES (SUPPORTED_1000baseT_Half | \ SUPPORTED_1000baseT_Full) +#define PHY_BASIC_FEATURES (PHY_10BT_FEATURES | \ + PHY_100BT_FEATURES | \ + PHY_DEFAULT_FEATURES) + +#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \ + PHY_1000BT_FEATURES) + #define PHY_10G_FEATURES (PHY_GBIT_FEATURES | \ SUPPORTED_10000baseT_Full) From b18acb0a115dfbbd31ee53081b514cab6fe2f262 Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 13 Jan 2016 16:59:34 +0300 Subject: [PATCH 26/44] drivers/net/phy: introduce phy_set_supported() This new function will allow MAC drivers to override supported capabilities of the phy. It is required when MAC cannot handle all speeds supported by phy. For example phy supports up-to 1Gb connections while MAC may only work in modes up to 100 or even 10 Mbit/sec. Signed-off-by: Alexey Brodkin Cc: Joe Hershberger Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 24 ++++++++++++++++++++++++ include/phy.h | 1 + 2 files changed, 25 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d25bec27f0..e25d4e337d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -528,6 +528,30 @@ int phy_register(struct phy_driver *drv) return 0; } +int phy_set_supported(struct phy_device *phydev, u32 max_speed) +{ + /* The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults first + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return -ENOTSUPP; + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + + return 0; +} + static int phy_probe(struct phy_device *phydev) { int err = 0; diff --git a/include/phy.h b/include/phy.h index b793e90532..e030c9ff2e 100644 --- a/include/phy.h +++ b/include/phy.h @@ -234,6 +234,7 @@ int phy_startup(struct phy_device *phydev); int phy_config(struct phy_device *phydev); int phy_shutdown(struct phy_device *phydev); int phy_register(struct phy_driver *drv); +int phy_set_supported(struct phy_device *phydev, u32 max_speed); int genphy_config_aneg(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev); From f74264d6614a0c093a7c32e996f2b26058eead5d Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 13 Jan 2016 16:59:35 +0300 Subject: [PATCH 27/44] include/net.h: add max_speed member in struct eth_pdata This will be used for getting max speed mode of Ethernet interface that a particular MAC supports from Device Tree blob and later being used for phy configuration. Signed-off-by: Alexey Brodkin Cc: Joe Hershberger Acked-by: Joe Hershberger --- include/net.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/net.h b/include/net.h index ebed29ad57..7dbba09d49 100644 --- a/include/net.h +++ b/include/net.h @@ -86,11 +86,13 @@ enum eth_state_t { * @iobase: The base address of the hardware registers * @enetaddr: The Ethernet MAC address that is loaded from EEPROM or env * @phy_interface: PHY interface to use - see PHY_INTERFACE_MODE_... + * @max_speed: Maximum speed of Ethernet connection supported by MAC */ struct eth_pdata { phys_addr_t iobase; unsigned char enetaddr[6]; int phy_interface; + int max_speed; }; enum eth_recv_flags { From b884c3fe6392cd1f578db9350eb7c49eda94ff53 Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 13 Jan 2016 16:59:36 +0300 Subject: [PATCH 28/44] net/designware: do explicit port selection for 1Gb mode Current implementation only sets "port select" bit for non-1Gb mode. That works fine if GMAC has just exited reset state but we may as well change connection mode in runtime. Then we'll need to reprogram GMAC for that new mode of operation and if previous mode was 10 or 100 Mb and new one is 1 Gb we'll need to reset port mode bit. Signed-off-by: Alexey Brodkin Cc: Bin Meng Cc: Joe Hershberger Cc: Sonic Zhang cc: Simon Glass Acked-by: Joe Hershberger --- drivers/net/designware.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 0fccbc0040..f28e825bfc 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -196,6 +196,8 @@ static void dw_adjust_link(struct eth_mac_regs *mac_p, if (phydev->speed != 1000) conf |= MII_PORTSELECT; + else + conf &= ~MII_PORTSELECT; if (phydev->speed == 100) conf |= FES_100; From 6968ec921634b69f700f3e4ccdc35527160ac77a Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Wed, 13 Jan 2016 16:59:37 +0300 Subject: [PATCH 29/44] net/designware: add support of max-speed device tree property This property allows to specify fastest connection mode supported by the MAC (as opposed to features of the phy). There are situations when phy may handle faster modes than the MAC (or even it's particular implementation or even due to CPU being too slow). This property is a standard one in Linux kernel these days and some boards do already use it in their device tree descriptions. Signed-off-by: Alexey Brodkin Cc: Bin Meng Cc: Joe Hershberger Cc: Sonic Zhang cc: Simon Glass Acked-by: Joe Hershberger --- drivers/net/designware.c | 14 +++++++++++++- drivers/net/designware.h | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index f28e825bfc..f1bcc92a81 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -406,7 +406,7 @@ static int _dw_free_pkt(struct dw_eth_dev *priv) static int dw_phy_init(struct dw_eth_dev *priv, void *dev) { struct phy_device *phydev; - int mask = 0xffffffff; + int mask = 0xffffffff, ret; #ifdef CONFIG_PHY_ADDR mask = 1 << CONFIG_PHY_ADDR; @@ -419,6 +419,11 @@ static int dw_phy_init(struct dw_eth_dev *priv, void *dev) phy_connect_dev(phydev, dev); phydev->supported &= PHY_GBIT_FEATURES; + if (priv->max_speed) { + ret = phy_set_supported(phydev, priv->max_speed); + if (ret) + return ret; + } phydev->advertising = phydev->supported; priv->phydev = phydev; @@ -601,6 +606,7 @@ static int designware_eth_probe(struct udevice *dev) priv->mac_regs_p = (struct eth_mac_regs *)iobase; priv->dma_regs_p = (struct eth_dma_regs *)(iobase + DW_DMA_BASE_OFFSET); priv->interface = pdata->phy_interface; + priv->max_speed = pdata->max_speed; dw_mdio_init(dev->name, priv->mac_regs_p); priv->bus = miiphy_get_dev_by_name(dev->name); @@ -635,6 +641,7 @@ static int designware_eth_ofdata_to_platdata(struct udevice *dev) { struct eth_pdata *pdata = dev_get_platdata(dev); const char *phy_mode; + const fdt32_t *cell; pdata->iobase = dev_get_addr(dev); pdata->phy_interface = -1; @@ -646,6 +653,11 @@ static int designware_eth_ofdata_to_platdata(struct udevice *dev) return -EINVAL; } + pdata->max_speed = 0; + cell = fdt_getprop(gd->fdt_blob, dev->of_offset, "max-speed", NULL); + if (cell) + pdata->max_speed = fdt32_to_cpu(*cell); + return 0; } diff --git a/drivers/net/designware.h b/drivers/net/designware.h index 4b9ec39cc8..ed6344cc28 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -223,6 +223,7 @@ struct dw_eth_dev { char rxbuffs[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN); u32 interface; + u32 max_speed; u32 tx_currdescnum; u32 rx_currdescnum; From dfa71e9fcbb142d3cf253054f83e4a4b4ea63f94 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:51:55 -0700 Subject: [PATCH 30/44] tegra: Report errors from PCI init This function can fail, so be sure to report any errors that occur. Signed-off-by: Simon Glass Reviewed-by: Joe Hershberger --- drivers/pci/pci_tegra.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 5a7fefed5e..5dadf6fa62 100644 --- a/drivers/pci/pci_tegra.c +++ b/drivers/pci/pci_tegra.c @@ -465,7 +465,11 @@ static int tegra_pcie_parse_dt(const void *fdt, int node, enum tegra_pci_id id, return err; } - tegra_pcie_board_init(); + err = tegra_pcie_board_init(); + if (err < 0) { + error("tegra_pcie_board_init() failed: err=%d", err); + return err; + } pcie->phy = tegra_xusb_phy_get(TEGRA_XUSB_PADCTL_PCIE); if (pcie->phy) { From c32a6fd07b1839e4a45729587ebc8e1c55601a4d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:51:56 -0700 Subject: [PATCH 31/44] net: Don't call board/cpu_eth_init() with driver model We should avoid weak functions with driver model. Existing boards that use driver model don't need them, so let's kill them off. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- net/eth.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/eth.c b/net/eth.c index 45fe6e3c1c..d96d3a5f45 100644 --- a/net/eth.c +++ b/net/eth.c @@ -96,6 +96,7 @@ static void eth_common_init(void) phy_init(); #endif +#ifndef CONFIG_DM_ETH /* * If board-specific initialization exists, call it. * If not, call a CPU-specific one @@ -107,10 +108,9 @@ static void eth_common_init(void) if (cpu_eth_init(gd->bd) < 0) printf("CPU Net Initialization Failed\n"); } else { -#ifndef CONFIG_DM_ETH printf("Net Initialization Skipped\n"); -#endif } +#endif } #ifdef CONFIG_DM_ETH From 818f91eb5778781083e33ce3291bba76a3f1601c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:51:57 -0700 Subject: [PATCH 32/44] net: Move common init into a new eth_common.c file Only half of the init is actually common. Move that part into a new common file and call it from driver-model and legacy code. More common functions will be added in future patches. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- net/Makefile | 1 + net/eth.c | 42 ++++++++++++++---------------------------- net/eth_common.c | 23 +++++++++++++++++++++++ net/eth_internal.h | 15 +++++++++++++++ 4 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 net/eth_common.c create mode 100644 net/eth_internal.h diff --git a/net/Makefile b/net/Makefile index e9cc8ada96..b3a22c2e5b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_CMD_NET) += bootp.o obj-$(CONFIG_CMD_CDP) += cdp.o obj-$(CONFIG_CMD_DNS) += dns.o obj-$(CONFIG_CMD_NET) += eth.o +obj-$(CONFIG_CMD_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_CMD_NET) += net.o obj-$(CONFIG_CMD_NFS) += nfs.o diff --git a/net/eth.c b/net/eth.c index d96d3a5f45..602925d20f 100644 --- a/net/eth.c +++ b/net/eth.c @@ -16,6 +16,7 @@ #include #include #include +#include "eth_internal.h" DECLARE_GLOBAL_DATA_PTR; @@ -85,34 +86,6 @@ static int __def_eth_init(bd_t *bis) int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); -static void eth_common_init(void) -{ - bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); -#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) - miiphy_init(); -#endif - -#ifdef CONFIG_PHYLIB - phy_init(); -#endif - -#ifndef CONFIG_DM_ETH - /* - * If board-specific initialization exists, call it. - * If not, call a CPU-specific one - */ - if (board_eth_init != __def_eth_init) { - if (board_eth_init(gd->bd) < 0) - printf("Board Net Initialization Failed\n"); - } else if (cpu_eth_init != __def_eth_init) { - if (cpu_eth_init(gd->bd) < 0) - printf("CPU Net Initialization Failed\n"); - } else { - printf("Net Initialization Skipped\n"); - } -#endif -} - #ifdef CONFIG_DM_ETH /** * struct eth_device_priv - private structure for each Ethernet device @@ -865,6 +838,19 @@ int eth_initialize(void) eth_devices = NULL; eth_current = NULL; eth_common_init(); + /* + * If board-specific initialization exists, call it. + * If not, call a CPU-specific one + */ + if (board_eth_init != __def_eth_init) { + if (board_eth_init(gd->bd) < 0) + printf("Board Net Initialization Failed\n"); + } else if (cpu_eth_init != __def_eth_init) { + if (cpu_eth_init(gd->bd) < 0) + printf("CPU Net Initialization Failed\n"); + } else { + printf("Net Initialization Skipped\n"); + } if (!eth_devices) { puts("No ethernet found.\n"); diff --git a/net/eth_common.c b/net/eth_common.c new file mode 100644 index 0000000000..ee0b6df376 --- /dev/null +++ b/net/eth_common.c @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include "eth_internal.h" + +void eth_common_init(void) +{ + bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_init(); +#endif + +#ifdef CONFIG_PHYLIB + phy_init(); +#endif +} diff --git a/net/eth_internal.h b/net/eth_internal.h new file mode 100644 index 0000000000..e65d8984e8 --- /dev/null +++ b/net/eth_internal.h @@ -0,0 +1,15 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __ETH_INTERNAL_H +#define __ETH_INTERNAL_H + +/* Do init that is common to driver model and legacy networking */ +void eth_common_init(void); + +#endif From 9987ecdd36da79535c4229ecc5693533aaa8d17b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:51:58 -0700 Subject: [PATCH 33/44] net: Move environment functions to the common file Move the functions which set ethernet environment variables to the common file. Signed-off-by: Simon Glass Acked-by: Joe Hershberger --- net/eth.c | 43 ------------------------------------------- net/eth_common.c | 43 +++++++++++++++++++++++++++++++++++++++++++ net/eth_internal.h | 16 ++++++++++++++++ 3 files changed, 59 insertions(+), 43 deletions(-) diff --git a/net/eth.c b/net/eth.c index 602925d20f..af8fcaea90 100644 --- a/net/eth.c +++ b/net/eth.c @@ -20,49 +20,6 @@ DECLARE_GLOBAL_DATA_PTR; -void eth_parse_enetaddr(const char *addr, uchar *enetaddr) -{ - char *end; - int i; - - for (i = 0; i < 6; ++i) { - enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0; - if (addr) - addr = (*end) ? end + 1 : end; - } -} - -int eth_getenv_enetaddr(const char *name, uchar *enetaddr) -{ - eth_parse_enetaddr(getenv(name), enetaddr); - return is_valid_ethaddr(enetaddr); -} - -int eth_setenv_enetaddr(const char *name, const uchar *enetaddr) -{ - char buf[20]; - - sprintf(buf, "%pM", enetaddr); - - return setenv(name, buf); -} - -int eth_getenv_enetaddr_by_index(const char *base_name, int index, - uchar *enetaddr) -{ - char enetvar[32]; - sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); - return eth_getenv_enetaddr(enetvar, enetaddr); -} - -static inline int eth_setenv_enetaddr_by_index(const char *base_name, int index, - uchar *enetaddr) -{ - char enetvar[32]; - sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); - return eth_setenv_enetaddr(enetvar, enetaddr); -} - static int eth_mac_skip(int index) { char enetvar[15]; diff --git a/net/eth_common.c b/net/eth_common.c index ee0b6df376..3fa6d83c70 100644 --- a/net/eth_common.c +++ b/net/eth_common.c @@ -10,6 +10,49 @@ #include #include "eth_internal.h" +void eth_parse_enetaddr(const char *addr, uchar *enetaddr) +{ + char *end; + int i; + + for (i = 0; i < 6; ++i) { + enetaddr[i] = addr ? simple_strtoul(addr, &end, 16) : 0; + if (addr) + addr = (*end) ? end + 1 : end; + } +} + +int eth_getenv_enetaddr(const char *name, uchar *enetaddr) +{ + eth_parse_enetaddr(getenv(name), enetaddr); + return is_valid_ethaddr(enetaddr); +} + +int eth_setenv_enetaddr(const char *name, const uchar *enetaddr) +{ + char buf[20]; + + sprintf(buf, "%pM", enetaddr); + + return setenv(name, buf); +} + +int eth_getenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr) +{ + char enetvar[32]; + sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); + return eth_getenv_enetaddr(enetvar, enetaddr); +} + +int eth_setenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr) +{ + char enetvar[32]; + sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index); + return eth_setenv_enetaddr(enetvar, enetaddr); +} + void eth_common_init(void) { bootstage_mark(BOOTSTAGE_ID_NET_ETH_START); diff --git a/net/eth_internal.h b/net/eth_internal.h index e65d8984e8..38d84201e6 100644 --- a/net/eth_internal.h +++ b/net/eth_internal.h @@ -12,4 +12,20 @@ /* Do init that is common to driver model and legacy networking */ void eth_common_init(void); +/** + * eth_setenv_enetaddr_by_index() - set the MAC address envrionment variable + * + * This sets up an environment variable with the given MAC address (@enetaddr). + * The environment variable to be set is defined by <@base_name><@index>addr. + * If @index is 0 it is omitted. For common Ethernet this means ethaddr, + * eth1addr, etc. + * + * @base_name: Base name for variable, typically "eth" + * @index: Index of interface being updated (>=0) + * @enetaddr: Pointer to MAC address to put into the variable + * @return 0 if OK, other value on error + */ +int eth_setenv_enetaddr_by_index(const char *base_name, int index, + uchar *enetaddr); + #endif From 8607a6bf753df210f8873994294c13885f746337 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:51:59 -0700 Subject: [PATCH 34/44] net: Move remaining common functions to eth_common.c Move eth_current_changed(), eth_set_current(), eth_mac_skip() and eth_get_name() into the common file. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- net/eth.c | 108 ++------------------------------------------- net/eth_common.c | 100 +++++++++++++++++++++++++++++++++++++++++ net/eth_internal.h | 9 ++++ 3 files changed, 113 insertions(+), 104 deletions(-) diff --git a/net/eth.c b/net/eth.c index af8fcaea90..907eb118ff 100644 --- a/net/eth.c +++ b/net/eth.c @@ -20,18 +20,6 @@ DECLARE_GLOBAL_DATA_PTR; -static int eth_mac_skip(int index) -{ - char enetvar[15]; - char *skip_state; - - sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index); - skip_state = getenv(enetvar); - return skip_state != NULL; -} - -static void eth_current_changed(void); - /* * CPU and board-specific Ethernet initializations. Aliased function * signals caller to move on @@ -74,7 +62,7 @@ static struct eth_uclass_priv *eth_get_uclass_priv(void) return uc->priv; } -static void eth_set_current_to_next(void) +void eth_set_current_to_next(void) { struct eth_uclass_priv *uc_priv; @@ -107,7 +95,7 @@ struct udevice *eth_get_dev(void) * In case it was not probed, we will attempt to do so. * dev may be NULL to unset the active device. */ -static void eth_set_dev(struct udevice *dev) +void eth_set_dev(struct udevice *dev) { if (dev && !device_active(dev)) { eth_errno = device_probe(dev); @@ -593,12 +581,12 @@ static unsigned int eth_rcv_current, eth_rcv_last; static struct eth_device *eth_devices; struct eth_device *eth_current; -static void eth_set_current_to_next(void) +void eth_set_current_to_next(void) { eth_current = eth_current->next; } -static void eth_set_dev(struct eth_device *dev) +void eth_set_dev(struct eth_device *dev) { eth_current = dev; } @@ -992,91 +980,3 @@ int eth_receive(void *packet, int length) return length; } #endif /* CONFIG_API */ - -static void eth_current_changed(void) -{ - char *act = getenv("ethact"); - char *ethrotate; - - /* - * The call to eth_get_dev() below has a side effect of rotating - * ethernet device if uc_priv->current == NULL. This is not what - * we want when 'ethrotate' variable is 'no'. - */ - ethrotate = getenv("ethrotate"); - if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) - return; - - /* update current ethernet name */ - if (eth_get_dev()) { - if (act == NULL || strcmp(act, eth_get_name()) != 0) - setenv("ethact", eth_get_name()); - } - /* - * remove the variable completely if there is no active - * interface - */ - else if (act != NULL) - setenv("ethact", NULL); -} - -void eth_try_another(int first_restart) -{ - static void *first_failed; - char *ethrotate; - - /* - * Do not rotate between network interfaces when - * 'ethrotate' variable is set to 'no'. - */ - ethrotate = getenv("ethrotate"); - if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) - return; - - if (!eth_get_dev()) - return; - - if (first_restart) - first_failed = eth_get_dev(); - - eth_set_current_to_next(); - - eth_current_changed(); - - if (first_failed == eth_get_dev()) - net_restart_wrap = 1; -} - -void eth_set_current(void) -{ - static char *act; - static int env_changed_id; - int env_id; - - env_id = get_env_id(); - if ((act == NULL) || (env_changed_id != env_id)) { - act = getenv("ethact"); - env_changed_id = env_id; - } - - if (act == NULL) { - char *ethprime = getenv("ethprime"); - void *dev = NULL; - - if (ethprime) - dev = eth_get_dev_by_name(ethprime); - if (dev) - eth_set_dev(dev); - else - eth_set_dev(NULL); - } else { - eth_set_dev(eth_get_dev_by_name(act)); - } - - eth_current_changed(); -} - -const char *eth_get_name(void) -{ - return eth_get_dev() ? eth_get_dev()->name : "unknown"; -} diff --git a/net/eth_common.c b/net/eth_common.c index 3fa6d83c70..288090155e 100644 --- a/net/eth_common.c +++ b/net/eth_common.c @@ -7,7 +7,9 @@ */ #include +#include #include +#include #include "eth_internal.h" void eth_parse_enetaddr(const char *addr, uchar *enetaddr) @@ -64,3 +66,101 @@ void eth_common_init(void) phy_init(); #endif } + +int eth_mac_skip(int index) +{ + char enetvar[15]; + char *skip_state; + + sprintf(enetvar, index ? "eth%dmacskip" : "ethmacskip", index); + skip_state = getenv(enetvar); + return skip_state != NULL; +} + +void eth_current_changed(void) +{ + char *act = getenv("ethact"); + char *ethrotate; + + /* + * The call to eth_get_dev() below has a side effect of rotating + * ethernet device if uc_priv->current == NULL. This is not what + * we want when 'ethrotate' variable is 'no'. + */ + ethrotate = getenv("ethrotate"); + if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) + return; + + /* update current ethernet name */ + if (eth_get_dev()) { + if (act == NULL || strcmp(act, eth_get_name()) != 0) + setenv("ethact", eth_get_name()); + } + /* + * remove the variable completely if there is no active + * interface + */ + else if (act != NULL) + setenv("ethact", NULL); +} + +void eth_try_another(int first_restart) +{ + static void *first_failed; + char *ethrotate; + + /* + * Do not rotate between network interfaces when + * 'ethrotate' variable is set to 'no'. + */ + ethrotate = getenv("ethrotate"); + if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) + return; + + if (!eth_get_dev()) + return; + + if (first_restart) + first_failed = eth_get_dev(); + + eth_set_current_to_next(); + + eth_current_changed(); + + if (first_failed == eth_get_dev()) + net_restart_wrap = 1; +} + +void eth_set_current(void) +{ + static char *act; + static int env_changed_id; + int env_id; + + env_id = get_env_id(); + if ((act == NULL) || (env_changed_id != env_id)) { + act = getenv("ethact"); + env_changed_id = env_id; + } + + if (act == NULL) { + char *ethprime = getenv("ethprime"); + void *dev = NULL; + + if (ethprime) + dev = eth_get_dev_by_name(ethprime); + if (dev) + eth_set_dev(dev); + else + eth_set_dev(NULL); + } else { + eth_set_dev(eth_get_dev_by_name(act)); + } + + eth_current_changed(); +} + +const char *eth_get_name(void) +{ + return eth_get_dev() ? eth_get_dev()->name : "unknown"; +} diff --git a/net/eth_internal.h b/net/eth_internal.h index 38d84201e6..6e4753cd0d 100644 --- a/net/eth_internal.h +++ b/net/eth_internal.h @@ -28,4 +28,13 @@ void eth_common_init(void); int eth_setenv_enetaddr_by_index(const char *base_name, int index, uchar *enetaddr); +int eth_mac_skip(int index); +void eth_current_changed(void); +#ifdef CONFIG_DM_ETH +void eth_set_dev(struct udevice *dev); +#else +void eth_set_dev(struct eth_device *dev); +#endif +void eth_set_current_to_next(void); + #endif From db9391e165dd0bb94caa246d5ade756b491b09d4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:52:00 -0700 Subject: [PATCH 35/44] net: Move driver-model code into its own file Every other uclass is in its own file. Create a new eth-uclass.c file and move the driver-model code into it, so that networking is consistent. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- net/Makefile | 4 + net/eth-uclass.c | 549 +++++++++++++++++++++++++++++++++++++++++++++++ net/eth.c | 543 ---------------------------------------------- 3 files changed, 553 insertions(+), 543 deletions(-) create mode 100644 net/eth-uclass.c diff --git a/net/Makefile b/net/Makefile index b3a22c2e5b..943de145d2 100644 --- a/net/Makefile +++ b/net/Makefile @@ -12,7 +12,11 @@ obj-$(CONFIG_CMD_NET) += arp.o obj-$(CONFIG_CMD_NET) += bootp.o obj-$(CONFIG_CMD_CDP) += cdp.o obj-$(CONFIG_CMD_DNS) += dns.o +ifdef CONFIG_DM_ETH +obj-$(CONFIG_CMD_NET) += eth-uclass.o +else obj-$(CONFIG_CMD_NET) += eth.o +endif obj-$(CONFIG_CMD_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o obj-$(CONFIG_CMD_NET) += net.o diff --git a/net/eth-uclass.c b/net/eth-uclass.c new file mode 100644 index 0000000000..a356a08826 --- /dev/null +++ b/net/eth-uclass.c @@ -0,0 +1,549 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include "eth_internal.h" + +/** + * struct eth_device_priv - private structure for each Ethernet device + * + * @state: The state of the Ethernet MAC driver (defined by enum eth_state_t) + */ +struct eth_device_priv { + enum eth_state_t state; +}; + +/** + * struct eth_uclass_priv - The structure attached to the uclass itself + * + * @current: The Ethernet device that the network functions are using + */ +struct eth_uclass_priv { + struct udevice *current; +}; + +/* eth_errno - This stores the most recent failure code from DM functions */ +static int eth_errno; + +static struct eth_uclass_priv *eth_get_uclass_priv(void) +{ + struct uclass *uc; + + uclass_get(UCLASS_ETH, &uc); + assert(uc); + return uc->priv; +} + +void eth_set_current_to_next(void) +{ + struct eth_uclass_priv *uc_priv; + + uc_priv = eth_get_uclass_priv(); + if (uc_priv->current) + uclass_next_device(&uc_priv->current); + if (!uc_priv->current) + uclass_first_device(UCLASS_ETH, &uc_priv->current); +} + +/* + * Typically this will simply return the active device. + * In the case where the most recent active device was unset, this will attempt + * to return the first device. If that device doesn't exist or fails to probe, + * this function will return NULL. + */ +struct udevice *eth_get_dev(void) +{ + struct eth_uclass_priv *uc_priv; + + uc_priv = eth_get_uclass_priv(); + if (!uc_priv->current) + eth_errno = uclass_first_device(UCLASS_ETH, + &uc_priv->current); + return uc_priv->current; +} + +/* + * Typically this will just store a device pointer. + * In case it was not probed, we will attempt to do so. + * dev may be NULL to unset the active device. + */ +void eth_set_dev(struct udevice *dev) +{ + if (dev && !device_active(dev)) { + eth_errno = device_probe(dev); + if (eth_errno) + dev = NULL; + } + + eth_get_uclass_priv()->current = dev; +} + +/* + * Find the udevice that either has the name passed in as devname or has an + * alias named devname. + */ +struct udevice *eth_get_dev_by_name(const char *devname) +{ + int seq = -1; + char *endp = NULL; + const char *startp = NULL; + struct udevice *it; + struct uclass *uc; + int len = strlen("eth"); + + /* Must be longer than 3 to be an alias */ + if (!strncmp(devname, "eth", len) && strlen(devname) > len) { + startp = devname + len; + seq = simple_strtoul(startp, &endp, 10); + } + + uclass_get(UCLASS_ETH, &uc); + uclass_foreach_dev(it, uc) { + /* + * We need the seq to be valid, so try to probe it. + * If the probe fails, the seq will not match since it will be + * -1 instead of what we are looking for. + * We don't care about errors from probe here. Either they won't + * match an alias or it will match a literal name and we'll pick + * up the error when we try to probe again in eth_set_dev(). + */ + if (device_probe(it)) + continue; + /* Check for the name or the sequence number to match */ + if (strcmp(it->name, devname) == 0 || + (endp > startp && it->seq == seq)) + return it; + } + + return NULL; +} + +unsigned char *eth_get_ethaddr(void) +{ + struct eth_pdata *pdata; + + if (eth_get_dev()) { + pdata = eth_get_dev()->platdata; + return pdata->enetaddr; + } + + return NULL; +} + +/* Set active state without calling start on the driver */ +int eth_init_state_only(void) +{ + struct udevice *current; + struct eth_device_priv *priv; + + current = eth_get_dev(); + if (!current || !device_active(current)) + return -EINVAL; + + priv = current->uclass_priv; + priv->state = ETH_STATE_ACTIVE; + + return 0; +} + +/* Set passive state without calling stop on the driver */ +void eth_halt_state_only(void) +{ + struct udevice *current; + struct eth_device_priv *priv; + + current = eth_get_dev(); + if (!current || !device_active(current)) + return; + + priv = current->uclass_priv; + priv->state = ETH_STATE_PASSIVE; +} + +int eth_get_dev_index(void) +{ + if (eth_get_dev()) + return eth_get_dev()->seq; + return -1; +} + +static int eth_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *pdata = dev->platdata; + int ret = 0; + + if (!dev || !device_active(dev)) + return -EINVAL; + + /* seq is valid since the device is active */ + if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) { + if (!is_valid_ethaddr(pdata->enetaddr)) { + printf("\nError: %s address %pM illegal value\n", + dev->name, pdata->enetaddr); + return -EINVAL; + } + + /* + * Drivers are allowed to decide not to implement this at + * run-time. E.g. Some devices may use it and some may not. + */ + ret = eth_get_ops(dev)->write_hwaddr(dev); + if (ret == -ENOSYS) + ret = 0; + if (ret) + printf("\nWarning: %s failed to set MAC address\n", + dev->name); + } + + return ret; +} + +static int on_ethaddr(const char *name, const char *value, enum env_op op, + int flags) +{ + int index; + int retval; + struct udevice *dev; + + /* look for an index after "eth" */ + index = simple_strtoul(name + 3, NULL, 10); + + retval = uclass_find_device_by_seq(UCLASS_ETH, index, false, &dev); + if (!retval) { + struct eth_pdata *pdata = dev->platdata; + switch (op) { + case env_op_create: + case env_op_overwrite: + eth_parse_enetaddr(value, pdata->enetaddr); + break; + case env_op_delete: + memset(pdata->enetaddr, 0, 6); + } + } + + return 0; +} +U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr); + +int eth_init(void) +{ + char *ethact = getenv("ethact"); + char *ethrotate = getenv("ethrotate"); + struct udevice *current = NULL; + struct udevice *old_current; + int ret = -ENODEV; + + /* + * When 'ethrotate' variable is set to 'no' and 'ethact' variable + * is already set to an ethernet device, we should stick to 'ethact'. + */ + if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) { + if (ethact) { + current = eth_get_dev_by_name(ethact); + if (!current) + return -EINVAL; + } + } + + if (!current) { + current = eth_get_dev(); + if (!current) { + printf("No ethernet found.\n"); + return -ENODEV; + } + } + + old_current = current; + do { + if (current) { + debug("Trying %s\n", current->name); + + if (device_active(current)) { + ret = eth_get_ops(current)->start(current); + if (ret >= 0) { + struct eth_device_priv *priv = + current->uclass_priv; + + priv->state = ETH_STATE_ACTIVE; + return 0; + } + } else { + ret = eth_errno; + } + + debug("FAIL\n"); + } else { + debug("PROBE FAIL\n"); + } + + /* + * If ethrotate is enabled, this will change "current", + * otherwise we will drop out of this while loop immediately + */ + eth_try_another(0); + /* This will ensure the new "current" attempted to probe */ + current = eth_get_dev(); + } while (old_current != current); + + return ret; +} + +void eth_halt(void) +{ + struct udevice *current; + struct eth_device_priv *priv; + + current = eth_get_dev(); + if (!current || !device_active(current)) + return; + + eth_get_ops(current)->stop(current); + priv = current->uclass_priv; + priv->state = ETH_STATE_PASSIVE; +} + +int eth_is_active(struct udevice *dev) +{ + struct eth_device_priv *priv; + + if (!dev || !device_active(dev)) + return 0; + + priv = dev_get_uclass_priv(dev); + return priv->state == ETH_STATE_ACTIVE; +} + +int eth_send(void *packet, int length) +{ + struct udevice *current; + int ret; + + current = eth_get_dev(); + if (!current) + return -ENODEV; + + if (!device_active(current)) + return -EINVAL; + + ret = eth_get_ops(current)->send(current, packet, length); + if (ret < 0) { + /* We cannot completely return the error at present */ + debug("%s: send() returned error %d\n", __func__, ret); + } + return ret; +} + +int eth_rx(void) +{ + struct udevice *current; + uchar *packet; + int flags; + int ret; + int i; + + current = eth_get_dev(); + if (!current) + return -ENODEV; + + if (!device_active(current)) + return -EINVAL; + + /* Process up to 32 packets at one time */ + flags = ETH_RECV_CHECK_DEVICE; + for (i = 0; i < 32; i++) { + ret = eth_get_ops(current)->recv(current, flags, &packet); + flags = 0; + if (ret > 0) + net_process_received_packet(packet, ret); + if (ret >= 0 && eth_get_ops(current)->free_pkt) + eth_get_ops(current)->free_pkt(current, packet, ret); + if (ret <= 0) + break; + } + if (ret == -EAGAIN) + ret = 0; + if (ret < 0) { + /* We cannot completely return the error at present */ + debug("%s: recv() returned error %d\n", __func__, ret); + } + return ret; +} + +int eth_initialize(void) +{ + int num_devices = 0; + struct udevice *dev; + + eth_common_init(); + + /* + * Devices need to write the hwaddr even if not started so that Linux + * will have access to the hwaddr that u-boot stored for the device. + * This is accomplished by attempting to probe each device and calling + * their write_hwaddr() operation. + */ + uclass_first_device(UCLASS_ETH, &dev); + if (!dev) { + printf("No ethernet found.\n"); + bootstage_error(BOOTSTAGE_ID_NET_ETH_START); + } else { + char *ethprime = getenv("ethprime"); + struct udevice *prime_dev = NULL; + + if (ethprime) + prime_dev = eth_get_dev_by_name(ethprime); + if (prime_dev) { + eth_set_dev(prime_dev); + eth_current_changed(); + } else { + eth_set_dev(NULL); + } + + bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); + do { + if (num_devices) + printf(", "); + + printf("eth%d: %s", dev->seq, dev->name); + + if (ethprime && dev == prime_dev) + printf(" [PRIME]"); + + eth_write_hwaddr(dev); + + uclass_next_device(&dev); + num_devices++; + } while (dev); + + putc('\n'); + } + + return num_devices; +} + +static int eth_post_bind(struct udevice *dev) +{ + if (strchr(dev->name, ' ')) { + printf("\nError: eth device name \"%s\" has a space!\n", + dev->name); + return -EINVAL; + } + + return 0; +} + +static int eth_pre_unbind(struct udevice *dev) +{ + /* Don't hang onto a pointer that is going away */ + if (dev == eth_get_uclass_priv()->current) + eth_set_dev(NULL); + + return 0; +} + +static int eth_post_probe(struct udevice *dev) +{ + struct eth_device_priv *priv = dev->uclass_priv; + struct eth_pdata *pdata = dev->platdata; + unsigned char env_enetaddr[6]; + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + struct eth_ops *ops = eth_get_ops(dev); + static int reloc_done; + + if (!reloc_done) { + if (ops->start) + ops->start += gd->reloc_off; + if (ops->send) + ops->send += gd->reloc_off; + if (ops->recv) + ops->recv += gd->reloc_off; + if (ops->free_pkt) + ops->free_pkt += gd->reloc_off; + if (ops->stop) + ops->stop += gd->reloc_off; +#ifdef CONFIG_MCAST_TFTP + if (ops->mcast) + ops->mcast += gd->reloc_off; +#endif + if (ops->write_hwaddr) + ops->write_hwaddr += gd->reloc_off; + if (ops->read_rom_hwaddr) + ops->read_rom_hwaddr += gd->reloc_off; + + reloc_done++; + } +#endif + + priv->state = ETH_STATE_INIT; + + /* Check if the device has a MAC address in ROM */ + if (eth_get_ops(dev)->read_rom_hwaddr) + eth_get_ops(dev)->read_rom_hwaddr(dev); + + eth_getenv_enetaddr_by_index("eth", dev->seq, env_enetaddr); + if (!is_zero_ethaddr(env_enetaddr)) { + if (!is_zero_ethaddr(pdata->enetaddr) && + memcmp(pdata->enetaddr, env_enetaddr, 6)) { + printf("\nWarning: %s MAC addresses don't match:\n", + dev->name); + printf("Address in SROM is %pM\n", + pdata->enetaddr); + printf("Address in environment is %pM\n", + env_enetaddr); + } + + /* Override the ROM MAC address */ + memcpy(pdata->enetaddr, env_enetaddr, 6); + } else if (is_valid_ethaddr(pdata->enetaddr)) { + eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr); + printf("\nWarning: %s using MAC address from ROM\n", + dev->name); + } else if (is_zero_ethaddr(pdata->enetaddr)) { +#ifdef CONFIG_NET_RANDOM_ETHADDR + net_random_ethaddr(pdata->enetaddr); + printf("\nWarning: %s (eth%d) using random MAC address - %pM\n", + dev->name, dev->seq, pdata->enetaddr); +#else + printf("\nError: %s address not set.\n", + dev->name); + return -EINVAL; +#endif + } + + return 0; +} + +static int eth_pre_remove(struct udevice *dev) +{ + struct eth_pdata *pdata = dev->platdata; + + eth_get_ops(dev)->stop(dev); + + /* clear the MAC address */ + memset(pdata->enetaddr, 0, 6); + + return 0; +} + +UCLASS_DRIVER(eth) = { + .name = "eth", + .id = UCLASS_ETH, + .post_bind = eth_post_bind, + .pre_unbind = eth_pre_unbind, + .post_probe = eth_post_probe, + .pre_remove = eth_pre_remove, + .priv_auto_alloc_size = sizeof(struct eth_uclass_priv), + .per_device_auto_alloc_size = sizeof(struct eth_device_priv), + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; diff --git a/net/eth.c b/net/eth.c index 907eb118ff..bdcd6eaafc 100644 --- a/net/eth.c +++ b/net/eth.c @@ -8,14 +8,10 @@ #include #include -#include #include #include -#include #include #include -#include -#include #include "eth_internal.h" DECLARE_GLOBAL_DATA_PTR; @@ -31,544 +27,6 @@ static int __def_eth_init(bd_t *bis) int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init"))); -#ifdef CONFIG_DM_ETH -/** - * struct eth_device_priv - private structure for each Ethernet device - * - * @state: The state of the Ethernet MAC driver (defined by enum eth_state_t) - */ -struct eth_device_priv { - enum eth_state_t state; -}; - -/** - * struct eth_uclass_priv - The structure attached to the uclass itself - * - * @current: The Ethernet device that the network functions are using - */ -struct eth_uclass_priv { - struct udevice *current; -}; - -/* eth_errno - This stores the most recent failure code from DM functions */ -static int eth_errno; - -static struct eth_uclass_priv *eth_get_uclass_priv(void) -{ - struct uclass *uc; - - uclass_get(UCLASS_ETH, &uc); - assert(uc); - return uc->priv; -} - -void eth_set_current_to_next(void) -{ - struct eth_uclass_priv *uc_priv; - - uc_priv = eth_get_uclass_priv(); - if (uc_priv->current) - uclass_next_device(&uc_priv->current); - if (!uc_priv->current) - uclass_first_device(UCLASS_ETH, &uc_priv->current); -} - -/* - * Typically this will simply return the active device. - * In the case where the most recent active device was unset, this will attempt - * to return the first device. If that device doesn't exist or fails to probe, - * this function will return NULL. - */ -struct udevice *eth_get_dev(void) -{ - struct eth_uclass_priv *uc_priv; - - uc_priv = eth_get_uclass_priv(); - if (!uc_priv->current) - eth_errno = uclass_first_device(UCLASS_ETH, - &uc_priv->current); - return uc_priv->current; -} - -/* - * Typically this will just store a device pointer. - * In case it was not probed, we will attempt to do so. - * dev may be NULL to unset the active device. - */ -void eth_set_dev(struct udevice *dev) -{ - if (dev && !device_active(dev)) { - eth_errno = device_probe(dev); - if (eth_errno) - dev = NULL; - } - - eth_get_uclass_priv()->current = dev; -} - -/* - * Find the udevice that either has the name passed in as devname or has an - * alias named devname. - */ -struct udevice *eth_get_dev_by_name(const char *devname) -{ - int seq = -1; - char *endp = NULL; - const char *startp = NULL; - struct udevice *it; - struct uclass *uc; - int len = strlen("eth"); - - /* Must be longer than 3 to be an alias */ - if (!strncmp(devname, "eth", len) && strlen(devname) > len) { - startp = devname + len; - seq = simple_strtoul(startp, &endp, 10); - } - - uclass_get(UCLASS_ETH, &uc); - uclass_foreach_dev(it, uc) { - /* - * We need the seq to be valid, so try to probe it. - * If the probe fails, the seq will not match since it will be - * -1 instead of what we are looking for. - * We don't care about errors from probe here. Either they won't - * match an alias or it will match a literal name and we'll pick - * up the error when we try to probe again in eth_set_dev(). - */ - if (device_probe(it)) - continue; - /* Check for the name or the sequence number to match */ - if (strcmp(it->name, devname) == 0 || - (endp > startp && it->seq == seq)) - return it; - } - - return NULL; -} - -unsigned char *eth_get_ethaddr(void) -{ - struct eth_pdata *pdata; - - if (eth_get_dev()) { - pdata = eth_get_dev()->platdata; - return pdata->enetaddr; - } - - return NULL; -} - -/* Set active state without calling start on the driver */ -int eth_init_state_only(void) -{ - struct udevice *current; - struct eth_device_priv *priv; - - current = eth_get_dev(); - if (!current || !device_active(current)) - return -EINVAL; - - priv = current->uclass_priv; - priv->state = ETH_STATE_ACTIVE; - - return 0; -} - -/* Set passive state without calling stop on the driver */ -void eth_halt_state_only(void) -{ - struct udevice *current; - struct eth_device_priv *priv; - - current = eth_get_dev(); - if (!current || !device_active(current)) - return; - - priv = current->uclass_priv; - priv->state = ETH_STATE_PASSIVE; -} - -int eth_get_dev_index(void) -{ - if (eth_get_dev()) - return eth_get_dev()->seq; - return -1; -} - -static int eth_write_hwaddr(struct udevice *dev) -{ - struct eth_pdata *pdata = dev->platdata; - int ret = 0; - - if (!dev || !device_active(dev)) - return -EINVAL; - - /* seq is valid since the device is active */ - if (eth_get_ops(dev)->write_hwaddr && !eth_mac_skip(dev->seq)) { - if (!is_valid_ethaddr(pdata->enetaddr)) { - printf("\nError: %s address %pM illegal value\n", - dev->name, pdata->enetaddr); - return -EINVAL; - } - - /* - * Drivers are allowed to decide not to implement this at - * run-time. E.g. Some devices may use it and some may not. - */ - ret = eth_get_ops(dev)->write_hwaddr(dev); - if (ret == -ENOSYS) - ret = 0; - if (ret) - printf("\nWarning: %s failed to set MAC address\n", - dev->name); - } - - return ret; -} - -static int on_ethaddr(const char *name, const char *value, enum env_op op, - int flags) -{ - int index; - int retval; - struct udevice *dev; - - /* look for an index after "eth" */ - index = simple_strtoul(name + 3, NULL, 10); - - retval = uclass_find_device_by_seq(UCLASS_ETH, index, false, &dev); - if (!retval) { - struct eth_pdata *pdata = dev->platdata; - switch (op) { - case env_op_create: - case env_op_overwrite: - eth_parse_enetaddr(value, pdata->enetaddr); - break; - case env_op_delete: - memset(pdata->enetaddr, 0, 6); - } - } - - return 0; -} -U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr); - -int eth_init(void) -{ - char *ethact = getenv("ethact"); - char *ethrotate = getenv("ethrotate"); - struct udevice *current = NULL; - struct udevice *old_current; - int ret = -ENODEV; - - /* - * When 'ethrotate' variable is set to 'no' and 'ethact' variable - * is already set to an ethernet device, we should stick to 'ethact'. - */ - if ((ethrotate != NULL) && (strcmp(ethrotate, "no") == 0)) { - if (ethact) { - current = eth_get_dev_by_name(ethact); - if (!current) - return -EINVAL; - } - } - - if (!current) { - current = eth_get_dev(); - if (!current) { - printf("No ethernet found.\n"); - return -ENODEV; - } - } - - old_current = current; - do { - if (current) { - debug("Trying %s\n", current->name); - - if (device_active(current)) { - ret = eth_get_ops(current)->start(current); - if (ret >= 0) { - struct eth_device_priv *priv = - current->uclass_priv; - - priv->state = ETH_STATE_ACTIVE; - return 0; - } - } else { - ret = eth_errno; - } - - debug("FAIL\n"); - } else { - debug("PROBE FAIL\n"); - } - - /* - * If ethrotate is enabled, this will change "current", - * otherwise we will drop out of this while loop immediately - */ - eth_try_another(0); - /* This will ensure the new "current" attempted to probe */ - current = eth_get_dev(); - } while (old_current != current); - - return ret; -} - -void eth_halt(void) -{ - struct udevice *current; - struct eth_device_priv *priv; - - current = eth_get_dev(); - if (!current || !device_active(current)) - return; - - eth_get_ops(current)->stop(current); - priv = current->uclass_priv; - priv->state = ETH_STATE_PASSIVE; -} - -int eth_is_active(struct udevice *dev) -{ - struct eth_device_priv *priv; - - if (!dev || !device_active(dev)) - return 0; - - priv = dev_get_uclass_priv(dev); - return priv->state == ETH_STATE_ACTIVE; -} - -int eth_send(void *packet, int length) -{ - struct udevice *current; - int ret; - - current = eth_get_dev(); - if (!current) - return -ENODEV; - - if (!device_active(current)) - return -EINVAL; - - ret = eth_get_ops(current)->send(current, packet, length); - if (ret < 0) { - /* We cannot completely return the error at present */ - debug("%s: send() returned error %d\n", __func__, ret); - } - return ret; -} - -int eth_rx(void) -{ - struct udevice *current; - uchar *packet; - int flags; - int ret; - int i; - - current = eth_get_dev(); - if (!current) - return -ENODEV; - - if (!device_active(current)) - return -EINVAL; - - /* Process up to 32 packets at one time */ - flags = ETH_RECV_CHECK_DEVICE; - for (i = 0; i < 32; i++) { - ret = eth_get_ops(current)->recv(current, flags, &packet); - flags = 0; - if (ret > 0) - net_process_received_packet(packet, ret); - if (ret >= 0 && eth_get_ops(current)->free_pkt) - eth_get_ops(current)->free_pkt(current, packet, ret); - if (ret <= 0) - break; - } - if (ret == -EAGAIN) - ret = 0; - if (ret < 0) { - /* We cannot completely return the error at present */ - debug("%s: recv() returned error %d\n", __func__, ret); - } - return ret; -} - -int eth_initialize(void) -{ - int num_devices = 0; - struct udevice *dev; - - eth_common_init(); - - /* - * Devices need to write the hwaddr even if not started so that Linux - * will have access to the hwaddr that u-boot stored for the device. - * This is accomplished by attempting to probe each device and calling - * their write_hwaddr() operation. - */ - uclass_first_device(UCLASS_ETH, &dev); - if (!dev) { - printf("No ethernet found.\n"); - bootstage_error(BOOTSTAGE_ID_NET_ETH_START); - } else { - char *ethprime = getenv("ethprime"); - struct udevice *prime_dev = NULL; - - if (ethprime) - prime_dev = eth_get_dev_by_name(ethprime); - if (prime_dev) { - eth_set_dev(prime_dev); - eth_current_changed(); - } else { - eth_set_dev(NULL); - } - - bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); - do { - if (num_devices) - printf(", "); - - printf("eth%d: %s", dev->seq, dev->name); - - if (ethprime && dev == prime_dev) - printf(" [PRIME]"); - - eth_write_hwaddr(dev); - - uclass_next_device(&dev); - num_devices++; - } while (dev); - - putc('\n'); - } - - return num_devices; -} - -static int eth_post_bind(struct udevice *dev) -{ - if (strchr(dev->name, ' ')) { - printf("\nError: eth device name \"%s\" has a space!\n", - dev->name); - return -EINVAL; - } - - return 0; -} - -static int eth_pre_unbind(struct udevice *dev) -{ - /* Don't hang onto a pointer that is going away */ - if (dev == eth_get_uclass_priv()->current) - eth_set_dev(NULL); - - return 0; -} - -static int eth_post_probe(struct udevice *dev) -{ - struct eth_device_priv *priv = dev->uclass_priv; - struct eth_pdata *pdata = dev->platdata; - unsigned char env_enetaddr[6]; - -#if defined(CONFIG_NEEDS_MANUAL_RELOC) - struct eth_ops *ops = eth_get_ops(dev); - static int reloc_done; - - if (!reloc_done) { - if (ops->start) - ops->start += gd->reloc_off; - if (ops->send) - ops->send += gd->reloc_off; - if (ops->recv) - ops->recv += gd->reloc_off; - if (ops->free_pkt) - ops->free_pkt += gd->reloc_off; - if (ops->stop) - ops->stop += gd->reloc_off; -#ifdef CONFIG_MCAST_TFTP - if (ops->mcast) - ops->mcast += gd->reloc_off; -#endif - if (ops->write_hwaddr) - ops->write_hwaddr += gd->reloc_off; - if (ops->read_rom_hwaddr) - ops->read_rom_hwaddr += gd->reloc_off; - - reloc_done++; - } -#endif - - priv->state = ETH_STATE_INIT; - - /* Check if the device has a MAC address in ROM */ - if (eth_get_ops(dev)->read_rom_hwaddr) - eth_get_ops(dev)->read_rom_hwaddr(dev); - - eth_getenv_enetaddr_by_index("eth", dev->seq, env_enetaddr); - if (!is_zero_ethaddr(env_enetaddr)) { - if (!is_zero_ethaddr(pdata->enetaddr) && - memcmp(pdata->enetaddr, env_enetaddr, 6)) { - printf("\nWarning: %s MAC addresses don't match:\n", - dev->name); - printf("Address in SROM is %pM\n", - pdata->enetaddr); - printf("Address in environment is %pM\n", - env_enetaddr); - } - - /* Override the ROM MAC address */ - memcpy(pdata->enetaddr, env_enetaddr, 6); - } else if (is_valid_ethaddr(pdata->enetaddr)) { - eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr); - printf("\nWarning: %s using MAC address from ROM\n", - dev->name); - } else if (is_zero_ethaddr(pdata->enetaddr)) { -#ifdef CONFIG_NET_RANDOM_ETHADDR - net_random_ethaddr(pdata->enetaddr); - printf("\nWarning: %s (eth%d) using random MAC address - %pM\n", - dev->name, dev->seq, pdata->enetaddr); -#else - printf("\nError: %s address not set.\n", - dev->name); - return -EINVAL; -#endif - } - - return 0; -} - -static int eth_pre_remove(struct udevice *dev) -{ - struct eth_pdata *pdata = dev->platdata; - - eth_get_ops(dev)->stop(dev); - - /* clear the MAC address */ - memset(pdata->enetaddr, 0, 6); - - return 0; -} - -UCLASS_DRIVER(eth) = { - .name = "eth", - .id = UCLASS_ETH, - .post_bind = eth_post_bind, - .pre_unbind = eth_pre_unbind, - .post_probe = eth_post_probe, - .pre_remove = eth_pre_remove, - .priv_auto_alloc_size = sizeof(struct eth_uclass_priv), - .per_device_auto_alloc_size = sizeof(struct eth_device_priv), - .flags = DM_UC_FLAG_SEQ_ALIAS, -}; -#endif /* #ifdef CONFIG_DM_ETH */ - -#ifndef CONFIG_DM_ETH - #ifdef CONFIG_API static struct { uchar data[PKTSIZE]; @@ -935,7 +393,6 @@ int eth_rx(void) return eth_current->recv(eth_current); } -#endif /* ifndef CONFIG_DM_ETH */ #ifdef CONFIG_API static void eth_save_packet(void *packet, int length) From c4998f9634a023d498f647fa0f7fea7b08b22852 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 17 Jan 2016 14:52:01 -0700 Subject: [PATCH 36/44] net: Rename eth.c to eth_lecacy.c Rename this file to make it clear it is for the old networking drivers and not for use with driver model. Signed-off-by: Simon Glass Reviewed-by: Bin Meng Acked-by: Joe Hershberger --- net/Makefile | 2 +- net/{eth.c => eth_legacy.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename net/{eth.c => eth_legacy.c} (100%) diff --git a/net/Makefile b/net/Makefile index 943de145d2..f03d608326 100644 --- a/net/Makefile +++ b/net/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_CMD_DNS) += dns.o ifdef CONFIG_DM_ETH obj-$(CONFIG_CMD_NET) += eth-uclass.o else -obj-$(CONFIG_CMD_NET) += eth.o +obj-$(CONFIG_CMD_NET) += eth_legacy.o endif obj-$(CONFIG_CMD_NET) += eth_common.o obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o diff --git a/net/eth.c b/net/eth_legacy.c similarity index 100% rename from net/eth.c rename to net/eth_legacy.c From 79e3efd5e54acba20a5af94f713d7aae5e540d09 Mon Sep 17 00:00:00 2001 From: Alexandre Messier Date: Fri, 22 Jan 2016 14:06:33 -0500 Subject: [PATCH 37/44] net: phy: micrel: Disable B_CAST on config Micrel PHYs KSZ8021/31 and KSZ8081 have a feature where MDIO address 0 is considered as a broadcast address; the PHY will respond even if it is not its configured (pinstrapped) address. This feature is enabled by default. The Linux kernel disables that feature at initialisation, but not before it probes the MDIO bus. This causes an issue, because a PHY at address 3 will be discovered at addresses 0 and 3, but will then only respond at address 3. Because Linux attaches the first PHY it discovers on 'eth0', it will attach the PHY from address 0, which will never answer again. Fix the issue by disabling the broadcast feature in U-Boot, before Linux is started. Signed-off-by: Alexandre Messier Acked-by: Joe Hershberger --- drivers/net/phy/micrel.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 19b6bc7472..446d05ac5b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -27,12 +27,31 @@ static struct phy_driver KSZ804_driver = { .shutdown = &genphy_shutdown, }; +#define MII_KSZPHY_OMSO 0x16 +#define KSZPHY_OMSO_B_CAST_OFF (1 << 9) + +static int ksz_genconfig_bcastoff(struct phy_device *phydev) +{ + int ret; + + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO); + if (ret < 0) + return ret; + + ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_KSZPHY_OMSO, + ret | KSZPHY_OMSO_B_CAST_OFF); + if (ret < 0) + return ret; + + return genphy_config(phydev); +} + static struct phy_driver KSZ8031_driver = { .name = "Micrel KSZ8021/KSZ8031", .uid = 0x221550, .mask = 0xfffff0, .features = PHY_BASIC_FEATURES, - .config = &genphy_config, + .config = &ksz_genconfig_bcastoff, .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; @@ -70,7 +89,7 @@ static struct phy_driver KSZ8081_driver = { .uid = 0x221560, .mask = 0xfffff0, .features = PHY_BASIC_FEATURES, - .config = &genphy_config, + .config = &ksz_genconfig_bcastoff, .startup = &genphy_startup, .shutdown = &genphy_shutdown, }; From 53b0c38c7a691d13d53c08e4c4ebbfd2612f20a7 Mon Sep 17 00:00:00 2001 From: Alexandre Messier Date: Fri, 22 Jan 2016 14:16:15 -0500 Subject: [PATCH 38/44] net: phy: Set ANRESTART in setup_forced When configuring a PHY in fixed (forced) link mode, in order for the changes to be applied, either one of these conditions must be triggered: 1- PHY is reset 2- Autoneg is restarted 3- PHY transitions from power-down to power-up Neither of these is currently done, so effectively the fixed link configuration is not applied in the PHY. Fix this by setting the Autoneg restart bit. Signed-off-by: Alexandre Messier Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index e25d4e337d..a06115d9f8 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -128,7 +128,7 @@ static int genphy_config_advert(struct phy_device *phydev) static int genphy_setup_forced(struct phy_device *phydev) { int err; - int ctl = 0; + int ctl = BMCR_ANRESTART; phydev->pause = phydev->asym_pause = 0; From 1f9e672c792404744006658047c5866c74bc1ab1 Mon Sep 17 00:00:00 2001 From: Alexandre Messier Date: Fri, 22 Jan 2016 14:16:56 -0500 Subject: [PATCH 39/44] net: phy: Use 'autoneg' flag from phydev Use the 'autoneg' flag available in phydev when checking if autoneg is in use. The previous implementation was checking directly in the PHY if autoneg was supported. Some PHYs will report that autoneg is supported, even when it is disabled. Thus it is not possible to use that bit to determine if autoneg is currently in use or not. Signed-off-by: Alexandre Messier Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a06115d9f8..467efd8bc7 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -235,7 +235,8 @@ int genphy_update_link(struct phy_device *phydev) if (phydev->link && mii_reg & BMSR_LSTATUS) return 0; - if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) { + if ((phydev->autoneg == AUTONEG_ENABLE) && + !(mii_reg & BMSR_ANEGCOMPLETE)) { int i = 0; printf("%s Waiting for PHY auto negotiation to complete", @@ -291,7 +292,7 @@ int genphy_parse_link(struct phy_device *phydev) int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); /* We're using autonegotiation */ - if (phydev->supported & SUPPORTED_Autoneg) { + if (phydev->autoneg == AUTONEG_ENABLE) { u32 lpa = 0; int gblpa = 0; u32 estatus = 0; From c16e69f702b171473d46825db7e663fd27d141b4 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 27 Jan 2016 15:45:59 -0600 Subject: [PATCH 40/44] net: phy: micrel: add documentation for Micrel KSZ90x1 binding Add the DTS documentation for the Micrel KSZ90x1 binding. The original document was from: [commit 4b405efbe12de28b26289282b431323d73992381 from the Linux kernel] This takes the original document and adds a clarification on how the skew values are represented in the code. References: Micrel ksz9021rl/rn Data Sheet, Revision 1.2. Dated 2/13/2014. http://www.micrel.com/_PDF/Ethernet/datasheets/ksz9021rl-rn_ds.pdf Micrel ksz9031rnx Data Sheet, Revision 2.1. Dated 11/20/2014. http://www.micrel.com/_PDF/Ethernet/datasheets/KSZ9031RNX.pdf Signed-off-by: Dinh Nguyen Acked-by: Joe Hershberger --- .../net/micrel-ksz90x1.txt | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 doc/device-tree-bindings/net/micrel-ksz90x1.txt diff --git a/doc/device-tree-bindings/net/micrel-ksz90x1.txt b/doc/device-tree-bindings/net/micrel-ksz90x1.txt new file mode 100644 index 0000000000..307f53f726 --- /dev/null +++ b/doc/device-tree-bindings/net/micrel-ksz90x1.txt @@ -0,0 +1,165 @@ +Micrel KSZ9021/KSZ9031 Gigabit Ethernet PHY + +Some boards require special tuning values, particularly when it comes to +clock delays. You can specify clock delay values by adding +micrel-specific properties to an Ethernet OF device node. + +Note that these settings are applied after any phy-specific fixup from +phy_fixup_list (see phy_init_hw() from drivers/net/phy/phy_device.c), +and therefore may overwrite them. + +KSZ9021: + + All skew control options are specified in picoseconds. The minimum + value is 0, the maximum value is 1800, and it is incremented by 120ps + steps. + + Optional properties: + + - rxc-skew-ps : Skew control of RXC pad + - rxdv-skew-ps : Skew control of RX CTL pad + - txc-skew-ps : Skew control of TXC pad + - txen-skew-ps : Skew control of TX CTL pad + - rxd0-skew-ps : Skew control of RX data 0 pad + - rxd1-skew-ps : Skew control of RX data 1 pad + - rxd2-skew-ps : Skew control of RX data 2 pad + - rxd3-skew-ps : Skew control of RX data 3 pad + - txd0-skew-ps : Skew control of TX data 0 pad + - txd1-skew-ps : Skew control of TX data 1 pad + - txd2-skew-ps : Skew control of TX data 2 pad + - txd3-skew-ps : Skew control of TX data 3 pad + +KSZ9031: + + All skew control options are specified in picoseconds. The minimum + value is 0, and the maximum is property-dependent. The increment + step is 60ps. + + The KSZ9031 hardware supports a range of skew values from negative to + positive, where the specific range is property dependent. All values + specified in the devicetree are offset by the minimum value so they + can be represented as positive integers in the devicetree since it's + difficult to represent a negative number in the devictree. + + The following 5-bit values table apply to rxc-skew-ps and txc-skew-ps. + + Pad Skew Value Delay (ps) Devicetree Value + ------------------------------------------------------ + 0_0000 -900ps 0 + 0_0001 -840ps 60 + 0_0010 -780ps 120 + 0_0011 -720ps 180 + 0_0100 -660ps 240 + 0_0101 -600ps 300 + 0_0110 -540ps 360 + 0_0111 -480ps 420 + 0_1000 -420ps 480 + 0_1001 -360ps 540 + 0_1010 -300ps 600 + 0_1011 -240ps 660 + 0_1100 -180ps 720 + 0_1101 -120ps 780 + 0_1110 -60ps 840 + 0_1111 0ps 900 + 1_0000 60ps 960 + 1_0001 120ps 1020 + 1_0010 180ps 1080 + 1_0011 240ps 1140 + 1_0100 300ps 1200 + 1_0101 360ps 1260 + 1_0110 420ps 1320 + 1_0111 480ps 1380 + 1_1000 540ps 1440 + 1_1001 600ps 1500 + 1_1010 660ps 1560 + 1_1011 720ps 1620 + 1_1100 780ps 1680 + 1_1101 840ps 1740 + 1_1110 900ps 1800 + 1_1111 960ps 1860 + + The following 4-bit values table apply to the txdX-skew-ps, rxdX-skew-ps + data pads, and the rxdv-skew-ps, txen-skew-ps control pads. + + Pad Skew Value Delay (ps) Devicetree Value + ------------------------------------------------------ + 0000 -420ps 0 + 0001 -360ps 60 + 0010 -300ps 120 + 0011 -240ps 180 + 0100 -180ps 240 + 0101 -120ps 300 + 0110 -60ps 360 + 0111 0ps 420 + 1000 60ps 480 + 1001 120ps 540 + 1010 180ps 600 + 1011 240ps 660 + 1100 300ps 720 + 1101 360ps 780 + 1110 420ps 840 + 1111 480ps 900 + + Optional properties: + + Maximum value of 1860: + + - rxc-skew-ps : Skew control of RX clock pad + - txc-skew-ps : Skew control of TX clock pad + + Maximum value of 900: + + - rxdv-skew-ps : Skew control of RX CTL pad + - txen-skew-ps : Skew control of TX CTL pad + - rxd0-skew-ps : Skew control of RX data 0 pad + - rxd1-skew-ps : Skew control of RX data 1 pad + - rxd2-skew-ps : Skew control of RX data 2 pad + - rxd3-skew-ps : Skew control of RX data 3 pad + - txd0-skew-ps : Skew control of TX data 0 pad + - txd1-skew-ps : Skew control of TX data 1 pad + - txd2-skew-ps : Skew control of TX data 2 pad + - txd3-skew-ps : Skew control of TX data 3 pad + +Examples: + + /* Attach to an Ethernet device with autodetected PHY */ + &enet { + rxc-skew-ps = <1800>; + rxdv-skew-ps = <0>; + txc-skew-ps = <1800>; + txen-skew-ps = <0>; + status = "okay"; + }; + + /* Attach to an explicitly-specified PHY */ + mdio { + phy0: ethernet-phy@0 { + rxc-skew-ps = <1800>; + rxdv-skew-ps = <0>; + txc-skew-ps = <1800>; + txen-skew-ps = <0>; + reg = <0>; + }; + }; + ethernet@70000 { + status = "okay"; + phy = <&phy0>; + phy-mode = "rgmii-id"; + }; + +References + + Micrel ksz9021rl/rn Data Sheet, Revision 1.2. Dated 2/13/2014. + http://www.micrel.com/_PDF/Ethernet/datasheets/ksz9021rl-rn_ds.pdf + + Micrel ksz9031rnx Data Sheet, Revision 2.1. Dated 11/20/2014. + http://www.micrel.com/_PDF/Ethernet/datasheets/KSZ9031RNX.pdf + +Notes: + + Note that a previous version of the Micrel ksz9021rl/rn Data Sheet + was missing extended register 106 (transmit data pad skews), and + incorrectly specified the ps per step as 200ps/step instead of + 120ps/step. The latest update to this document reflects the latest + revision of the Micrel specification even though usage in the kernel + still reflects that incorrect document. From ff7bd212cb8a0a80a113e25af7616ef0a24abdfc Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 27 Jan 2016 15:46:00 -0600 Subject: [PATCH 41/44] net: phy: micrel: fix divisor value for KSZ9031 phy skew The picoseconds to register value divisor(ps_to_regval) should be 60 and not 200. Linux has KSZ9031_PS_TO_REG defined to be 60 as well. 60 is the correct divisor because the 4-bit skew values are defined from 0x0000(-420ps) to 0xffff(480ps), increments of 60. For example, a DTS skew value of 420, represents 0ps delay, which should be 0x7. With the previous divisor of 200, it would result in 0x2, which represents a -300ps delay. With this patch, ethernet on the SoCFPGA DE0 Atlas is now able to work with 1Gb ethernet. References: http://www.micrel.com/_PDF/Ethernet/datasheets/KSZ9031RNX.pdf -> page 26 Signed-off-by: Dinh Nguyen Acked-by: Marek Vasut Acked-by: Joe Hershberger --- drivers/net/phy/micrel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 446d05ac5b..c3da1606dc 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -230,7 +230,7 @@ static int ksz90x1_of_config_group(struct phy_device *phydev, { struct udevice *dev = phydev->dev; struct phy_driver *drv = phydev->drv; - const int ps_to_regval = 200; + const int ps_to_regval = 60; int val[4]; int i, changed = 0, offset, max; u16 regval = 0; From ddcd1f3084d88cc92403ed09f77f42fc6f2c4e0e Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Thu, 28 Jan 2016 15:55:46 +0800 Subject: [PATCH 42/44] net: phy: introduce a quirk PHY_FLAG_BROKEN_RESET Current driver always performs a phy soft reset when connecting the phy device, but soft reset is not always supported by a phy device, so introduce a quirk PHY_FLAG_BROKEN_RESET to let such a phy device to skip soft reset. This commit uses 'flags' of phy device structure to store the quirk. Signed-off-by: Shaohui Xie Acked-by: Joe Hershberger --- drivers/net/phy/phy.c | 3 +++ include/phy.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 467efd8bc7..17866a244b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -743,6 +743,9 @@ int phy_reset(struct phy_device *phydev) int timeout = 500; int devad = MDIO_DEVAD_NONE; + if (phydev->flags & PHY_FLAG_BROKEN_RESET) + return 0; + #ifdef CONFIG_PHYLIB_10G /* If it's 10G, we need to issue reset through one of the MMDs */ if (is_10g_interface(phydev->interface)) { diff --git a/include/phy.h b/include/phy.h index e030c9ff2e..09bbe483a4 100644 --- a/include/phy.h +++ b/include/phy.h @@ -17,6 +17,8 @@ #define PHY_MAX_ADDR 32 +#define PHY_FLAG_BROKEN_RESET (1 << 0) /* soft reset not supported */ + #define PHY_DEFAULT_FEATURES (SUPPORTED_Autoneg | \ SUPPORTED_TP | \ SUPPORTED_MII) From d8877e6f8ca4b6c08c0bc9c1fa8f855dc8776044 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Thu, 28 Jan 2016 15:56:36 +0800 Subject: [PATCH 43/44] net: phy: implements probe for Cortina phy Cortina phy cannot support soft reset, this commit implements probe for Cortina PHY to tell phylib to skip phy soft reset by setting PHY_FLAG_BROKEN_RESET in flags. Signed-off-by: Shaohui Xie Acked-by: Joe Hershberger --- drivers/net/phy/cortina.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 3a2b3bba99..ba1157fd10 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -256,6 +256,12 @@ int cs4340_config(struct phy_device *phydev) return 0; } +int cs4340_probe(struct phy_device *phydev) +{ + phydev->flags = PHY_FLAG_BROKEN_RESET; + return 0; +} + int cs4340_startup(struct phy_device *phydev) { phydev->link = 1; @@ -275,6 +281,7 @@ struct phy_driver cs4340_driver = { MDIO_DEVS_PHYXS | MDIO_DEVS_AN | MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2), .config = &cs4340_config, + .probe = &cs4340_probe, .startup = &cs4340_startup, .shutdown = &gen10g_shutdown, }; From b2b7fbc33ff1b990804e481153dd45de579cff75 Mon Sep 17 00:00:00 2001 From: Alexandre Messier Date: Thu, 28 Jan 2016 11:19:02 -0500 Subject: [PATCH 44/44] net: Add bootfile in DHCP Request Add the bootfile name in the DHCP Request packet, in addition to it already being sent in the DHCP Discover. This is needed by some DHCP servers so that the bootfile name is properly returned by the server to the client in the DHCP Ack, as expected by U-Boot. Signed-off-by: Alexandre Messier --- net/bootp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bootp.c b/net/bootp.c index 8da2e9b8b4..f2978a23ce 100644 --- a/net/bootp.c +++ b/net/bootp.c @@ -949,6 +949,7 @@ static void dhcp_send_request_packet(struct bootp_hdr *bp_offer) net_write_ip(&bp->bp_giaddr, zero_ip); memcpy(bp->bp_chaddr, net_ethaddr, 6); + copy_filename(bp->bp_file, net_boot_file_name, sizeof(bp->bp_file)); /* * ID is the id of the OFFER packet