diff options
author | Tom Rini <trini@konsulko.com> | 2016-01-28 18:42:10 -0500 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-01-28 18:42:10 -0500 |
commit | 82d72a1b9967cff4908f22c57536c3660f794401 (patch) | |
tree | ed7f02df10ffa0f7c4ef87298c1c5e5f4be5dd45 | |
parent | 3faf2216d9649e2a22e6728194e9797cb46db933 (diff) | |
parent | b2b7fbc33ff1b990804e481153dd45de579cff75 (diff) | |
download | u-boot-imx-82d72a1b9967cff4908f22c57536c3660f794401.zip u-boot-imx-82d72a1b9967cff4908f22c57536c3660f794401.tar.gz u-boot-imx-82d72a1b9967cff4908f22c57536c3660f794401.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-net
32 files changed, 1933 insertions, 879 deletions
diff --git a/arch/arm/cpu/armv7/ls102xa/fdt.c b/arch/arm/cpu/armv7/ls102xa/fdt.c index 856abed..ae5e794 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; } diff --git a/board/freescale/bsc9132qds/bsc9132qds.c b/board/freescale/bsc9132qds/bsc9132qds.c index 586dacc..71a7bb5 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 diff --git a/board/freescale/c29xpcie/c29xpcie.c b/board/freescale/c29xpcie/c29xpcie.c index f42d373..e325b4d 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) diff --git a/board/freescale/ls1021atwr/ls1021atwr.c b/board/freescale/ls1021atwr/ls1021atwr.c index b85774c..616e0bf 100644 --- a/board/freescale/ls1021atwr/ls1021atwr.c +++ b/board/freescale/ls1021atwr/ls1021atwr.c @@ -244,9 +244,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; @@ -281,10 +281,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) diff --git a/board/freescale/mpc8548cds/mpc8548cds.c b/board/freescale/mpc8548cds/mpc8548cds.c index ca9b43c..de76d36 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) diff --git a/board/freescale/mpc8572ds/mpc8572ds.c b/board/freescale/mpc8572ds/mpc8572ds.c index 3f68cf4..ed6836a 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) diff --git a/board/freescale/p1010rdb/p1010rdb.c b/board/freescale/p1010rdb/p1010rdb.c index ebffe9a..1ae1540 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) diff --git a/cmd/ethsw.c b/cmd/ethsw.c index 8e452e9..491cb8e 100644 --- a/cmd/ethsw.c +++ b/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 <port_no>] 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) { @@ -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 <port_no>] aggr" \ +" { [help] | show | <lag_group_no> } " \ +"- 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 */ @@ -1010,7 +1083,7 @@ static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #define ETHSW_PORT_CONF_HELP "[port <port_no>] { 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", @@ -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 14dbf31..6f03de2 100644 --- a/doc/README.t1040-l2switch +++ b/doc/README.t1040-l2switch @@ -24,16 +24,31 @@ 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 + - Port LAG Commands syntax - ethsw port <port_nr> enable|disable - enable/disable an l2 switch port - ethsw port <port_nr> show - show an l2 switch port's configuration +ethsw [port <port_no>] { enable | disable | show } - enable/disable a port; show a port's configuration +ethsw [port <port_no>] statistics { [help] | [clear] } - show an l2 switch port's statistics +ethsw [port <port_no>] learning { [help] | show | auto | disable } - enable/disable/show learning configuration on a port +ethsw [port <port_no>] [vlan <vid>] fdb { [help] | show | flush | { add | del } <mac> } - add/delete a mac entry in FDB; use show to see FDB entries; + if [vlan <vid>] is missing, VID 1 will be used +ethsw [port <port_no>] pvid { [help] | show | <pvid> } - set/show PVID (ingress and egress VLAN tagging) for a port +ethsw [port <port_no>] vlan { [help] | show | add <vid> | del <vid> } - add a VLAN to a port (VLAN members) +ethsw [port <port_no>] untagged { [help] | show | all | none | pvid } - set egress tagging mode for a port +ethsw [port <port_no>] 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 <port_no>] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port +ethsw [port <port_no>] aggr { [help] | show | <lag_group_no> } - get/set LAG group for a 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 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 0000000..c5bf48c --- /dev/null +++ b/doc/device-tree-bindings/net/fsl-tsec-phy.txt @@ -0,0 +1,64 @@ +* 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>; + }; + }; + +* 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/doc/device-tree-bindings/net/micrel-ksz90x1.txt b/doc/device-tree-bindings/net/micrel-ksz90x1.txt new file mode 100644 index 0000000..307f53f --- /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. diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 68b6548..77b98c9 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; @@ -404,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; @@ -417,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; @@ -599,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); @@ -633,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; @@ -644,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 4b9ec39..ed6344c 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; diff --git a/drivers/net/fsl_mdio.c b/drivers/net/fsl_mdio.c index f48bbc3..77b9739 100644 --- a/drivers/net/fsl_mdio.c +++ b/drivers/net/fsl_mdio.c @@ -5,6 +5,7 @@ * * SPDX-License-Identifier: GPL-2.0+ */ + #include <common.h> #include <miiphy.h> #include <phy.h> @@ -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/phy/cortina.c b/drivers/net/phy/cortina.c index 447ecfb..f975fd8 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -257,6 +257,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; @@ -276,6 +282,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, }; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 19b6bc7..c3da160 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, }; @@ -211,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; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 51b5746..17866a2 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,29 +79,40 @@ 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 */ - if (phydev->supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) { - oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + oldadv = adv; + + if (adv < 0) + return adv; - if (adv < 0) - return adv; + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); - adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { 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 (adv != oldadv) + changed = 1; - if (err < 0) - return err; - changed = 1; - } - } + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv); + if (err < 0) + return err; return changed; } @@ -117,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; @@ -224,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", @@ -280,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; @@ -371,8 +383,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); @@ -411,8 +421,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); @@ -436,7 +446,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, @@ -517,6 +529,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; @@ -707,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)) { @@ -717,15 +756,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 +769,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); diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 9b09caf..be0f382 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -1,18 +1,16 @@ /* * 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 <config.h> #include <common.h> +#include <dm.h> #include <malloc.h> #include <net.h> #include <command.h> @@ -24,21 +22,7 @@ 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); - -#else -#error "rtx must be 64-bit aligned" -#endif - -static int tsec_send(struct eth_device *dev, void *packet, int length); - +#ifndef CONFIG_DM_ETH /* Default initializations for TSEC controllers. */ static struct tsec_info_struct tsec_info[] = { @@ -64,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 \ @@ -84,8 +69,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 +87,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 +99,13 @@ 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. + */ +#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; @@ -135,7 +127,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 +174,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 +206,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) @@ -231,15 +226,174 @@ 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 + */ +#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; + 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; +} + +#ifndef CONFIG_DM_ETH +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; +} +#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; + + 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 * 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; @@ -274,25 +428,27 @@ 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(&rxbd[rx_idx].status) & RXBD_EMPTY; t++) { + 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; } } - 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(&priv->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(&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); @@ -315,49 +471,49 @@ 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) +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; /* 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 /* 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(); 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); @@ -369,113 +525,22 @@ 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(&txbd[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 | - (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++) { - 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; - - 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(&rxbd[rx_idx].status) & RXBD_EMPTY)) { - int length = in_be16(&rxbd[rx_idx].length); - uint16_t status = in_be16(&rxbd[rx_idx].status); - - /* Send the packet up if there were no errors */ - if (!(status & RXBD_STATS)) - net_process_received_packet(net_rx_packets[rx_idx], - length - 4); - else - printf("Got error %x\n", (status & RXBD_STATS)); - - out_be16(&rxbd[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) - status |= RXBD_WRAP; - out_be16(&rxbd[rx_idx].status, status); - - rx_idx = (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 +/* + * 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. */ +#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,17 +554,27 @@ 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. */ +#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); @@ -507,7 +582,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); @@ -551,8 +626,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,14 +640,13 @@ 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 */ -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 | @@ -584,14 +658,15 @@ static int init_phy(struct eth_device *dev) 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); 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; @@ -605,7 +680,9 @@ static int init_phy(struct eth_device *dev) return 1; } -/* Initialize device structure. Returns success if PHY +#ifndef CONFIG_DM_ETH +/* + * 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) @@ -630,11 +707,13 @@ 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; strcpy(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; @@ -645,7 +724,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; @@ -657,7 +736,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); } /* @@ -690,3 +769,118 @@ 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; + } + + 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) + 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/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 7595db1..44afe14 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, @@ -593,6 +634,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 */ @@ -1474,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; @@ -2064,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, @@ -2088,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 */ @@ -2107,6 +2453,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); @@ -2115,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/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c index 5a7fefe..5dadf6f 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) { diff --git a/include/ethsw.h b/include/ethsw.h index 2d3c12a..25f358d 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/fsl_mdio.h b/include/fsl_mdio.h index 2137282..25678a9 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/net.h b/include/net.h index ac44d61..a739f45 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 { diff --git a/include/phy.h b/include/phy.h index 66cf61b..09bbe48 100644 --- a/include/phy.h +++ b/include/phy.h @@ -17,18 +17,28 @@ #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_FLAG_BROKEN_RESET (1 << 0) /* soft reset not supported */ + +#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) @@ -226,6 +236,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); diff --git a/include/tsec.h b/include/tsec.h index 1119d2c..fb27edf 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 @@ -21,6 +18,8 @@ #include <config.h> #include <phy.h> +#ifndef CONFIG_DM_ETH + #ifdef CONFIG_LS102XA #define TSEC_SIZE 0x40000 #define TSEC_MDIO_OFFSET 0x40000 @@ -67,11 +66,13 @@ x.mii_devname = DEFAULT_MII_NAME;\ } -#define MAC_ADDR_LEN 6 +#endif /* CONFIG_DM_ETH */ + +#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 +84,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 +94,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 +122,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 +137,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 +163,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 +178,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 +201,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 +333,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,21 +385,33 @@ 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 */ #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; 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 */ + 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 +424,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 */ diff --git a/include/vsc9953.h b/include/vsc9953.h index cd5cfc7..a2d4554 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 @@ -136,6 +137,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 @@ -153,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) @@ -164,6 +181,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" @@ -235,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]; }; @@ -269,7 +291,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]; diff --git a/net/Makefile b/net/Makefile index e9cc8ad..f03d608 100644 --- a/net/Makefile +++ b/net/Makefile @@ -12,7 +12,12 @@ obj-$(CONFIG_CMD_NET) += arp.o obj-$(CONFIG_CMD_NET) += bootp.o obj-$(CONFIG_CMD_CDP) += cdp.o obj-$(CONFIG_CMD_DNS) += dns.o -obj-$(CONFIG_CMD_NET) += eth.o +ifdef CONFIG_DM_ETH +obj-$(CONFIG_CMD_NET) += eth-uclass.o +else +obj-$(CONFIG_CMD_NET) += eth_legacy.o +endif +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/bootp.c b/net/bootp.c index 8aeddb0..f2978a2 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 @@ -995,6 +996,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: /* diff --git a/net/eth.c b/net/eth-uclass.c index 45fe6e3..a356a08 100644 --- a/net/eth.c +++ b/net/eth-uclass.c @@ -7,113 +7,13 @@ */ #include <common.h> -#include <command.h> #include <dm.h> #include <environment.h> #include <net.h> -#include <miiphy.h> -#include <phy.h> -#include <asm/errno.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> +#include "eth_internal.h" -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]; - 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 - */ -static int __def_eth_init(bd_t *bis) -{ - return -1; -} -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 - - /* - * 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 { -#ifndef CONFIG_DM_ETH - printf("Net Initialization Skipped\n"); -#endif - } -} - -#ifdef CONFIG_DM_ETH /** * struct eth_device_priv - private structure for each Ethernet device * @@ -144,7 +44,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; @@ -177,7 +77,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); @@ -647,493 +547,3 @@ UCLASS_DRIVER(eth) = { .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]; - int length; -} eth_rcv_bufs[PKTBUFSRX]; - -static unsigned int eth_rcv_current, eth_rcv_last; -#endif - -static struct eth_device *eth_devices; -struct eth_device *eth_current; - -static void eth_set_current_to_next(void) -{ - eth_current = eth_current->next; -} - -static void eth_set_dev(struct eth_device *dev) -{ - eth_current = dev; -} - -struct eth_device *eth_get_dev_by_name(const char *devname) -{ - struct eth_device *dev, *target_dev; - - BUG_ON(devname == NULL); - - if (!eth_devices) - return NULL; - - dev = eth_devices; - target_dev = NULL; - do { - if (strcmp(devname, dev->name) == 0) { - target_dev = dev; - break; - } - dev = dev->next; - } while (dev != eth_devices); - - return target_dev; -} - -struct eth_device *eth_get_dev_by_index(int index) -{ - struct eth_device *dev, *target_dev; - - if (!eth_devices) - return NULL; - - dev = eth_devices; - target_dev = NULL; - do { - if (dev->index == index) { - target_dev = dev; - break; - } - dev = dev->next; - } while (dev != eth_devices); - - return target_dev; -} - -int eth_get_dev_index(void) -{ - if (!eth_current) - return -1; - - return eth_current->index; -} - -static int on_ethaddr(const char *name, const char *value, enum env_op op, - int flags) -{ - int index; - struct eth_device *dev; - - if (!eth_devices) - return 0; - - /* look for an index after "eth" */ - index = simple_strtoul(name + 3, NULL, 10); - - dev = eth_devices; - do { - if (dev->index == index) { - switch (op) { - case env_op_create: - case env_op_overwrite: - eth_parse_enetaddr(value, dev->enetaddr); - break; - case env_op_delete: - memset(dev->enetaddr, 0, 6); - } - } - dev = dev->next; - } while (dev != eth_devices); - - return 0; -} -U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr); - -int eth_write_hwaddr(struct eth_device *dev, const char *base_name, - int eth_number) -{ - unsigned char env_enetaddr[6]; - int ret = 0; - - eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr); - - if (!is_zero_ethaddr(env_enetaddr)) { - if (!is_zero_ethaddr(dev->enetaddr) && - memcmp(dev->enetaddr, env_enetaddr, 6)) { - printf("\nWarning: %s MAC addresses don't match:\n", - dev->name); - printf("Address in SROM is %pM\n", - dev->enetaddr); - printf("Address in environment is %pM\n", - env_enetaddr); - } - - memcpy(dev->enetaddr, env_enetaddr, 6); - } else if (is_valid_ethaddr(dev->enetaddr)) { - eth_setenv_enetaddr_by_index(base_name, eth_number, - dev->enetaddr); - } else if (is_zero_ethaddr(dev->enetaddr)) { -#ifdef CONFIG_NET_RANDOM_ETHADDR - net_random_ethaddr(dev->enetaddr); - printf("\nWarning: %s (eth%d) using random MAC address - %pM\n", - dev->name, eth_number, dev->enetaddr); -#else - printf("\nError: %s address not set.\n", - dev->name); - return -EINVAL; -#endif - } - - if (dev->write_hwaddr && !eth_mac_skip(eth_number)) { - if (!is_valid_ethaddr(dev->enetaddr)) { - printf("\nError: %s address %pM illegal value\n", - dev->name, dev->enetaddr); - return -EINVAL; - } - - ret = dev->write_hwaddr(dev); - if (ret) - printf("\nWarning: %s failed to set MAC address\n", - dev->name); - } - - return ret; -} - -int eth_register(struct eth_device *dev) -{ - struct eth_device *d; - static int index; - - assert(strlen(dev->name) < sizeof(dev->name)); - - if (!eth_devices) { - eth_devices = dev; - eth_current = dev; - eth_current_changed(); - } else { - for (d = eth_devices; d->next != eth_devices; d = d->next) - ; - d->next = dev; - } - - dev->state = ETH_STATE_INIT; - dev->next = eth_devices; - dev->index = index++; - - return 0; -} - -int eth_unregister(struct eth_device *dev) -{ - struct eth_device *cur; - - /* No device */ - if (!eth_devices) - return -ENODEV; - - for (cur = eth_devices; cur->next != eth_devices && cur->next != dev; - cur = cur->next) - ; - - /* Device not found */ - if (cur->next != dev) - return -ENODEV; - - cur->next = dev->next; - - if (eth_devices == dev) - eth_devices = dev->next == eth_devices ? NULL : dev->next; - - if (eth_current == dev) { - eth_current = eth_devices; - eth_current_changed(); - } - - return 0; -} - -int eth_initialize(void) -{ - int num_devices = 0; - - eth_devices = NULL; - eth_current = NULL; - eth_common_init(); - - if (!eth_devices) { - puts("No ethernet found.\n"); - bootstage_error(BOOTSTAGE_ID_NET_ETH_START); - } else { - struct eth_device *dev = eth_devices; - char *ethprime = getenv("ethprime"); - - bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); - do { - if (dev->index) - puts(", "); - - printf("%s", dev->name); - - if (ethprime && strcmp(dev->name, ethprime) == 0) { - eth_current = dev; - puts(" [PRIME]"); - } - - if (strchr(dev->name, ' ')) - puts("\nWarning: eth device name has a space!" - "\n"); - - eth_write_hwaddr(dev, "eth", dev->index); - - dev = dev->next; - num_devices++; - } while (dev != eth_devices); - - eth_current_changed(); - putc('\n'); - } - - return num_devices; -} - -#ifdef CONFIG_MCAST_TFTP -/* Multicast. - * mcast_addr: multicast ipaddr from which multicast Mac is made - * join: 1=join, 0=leave. - */ -int eth_mcast_join(struct in_addr mcast_ip, int join) -{ - u8 mcast_mac[6]; - if (!eth_current || !eth_current->mcast) - return -1; - mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff; - mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff; - mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f; - mcast_mac[2] = 0x5e; - mcast_mac[1] = 0x0; - mcast_mac[0] = 0x1; - return eth_current->mcast(eth_current, mcast_mac, join); -} - -/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c - * and this is the ethernet-crc method needed for TSEC -- and perhaps - * some other adapter -- hash tables - */ -#define CRCPOLY_LE 0xedb88320 -u32 ether_crc(size_t len, unsigned char const *p) -{ - int i; - u32 crc; - crc = ~0; - while (len--) { - crc ^= *p++; - for (i = 0; i < 8; i++) - crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); - } - /* an reverse the bits, cuz of way they arrive -- last-first */ - crc = (crc >> 16) | (crc << 16); - crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); - crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); - crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); - crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); - return crc; -} - -#endif - - -int eth_init(void) -{ - struct eth_device *old_current; - - if (!eth_current) { - puts("No ethernet found.\n"); - return -ENODEV; - } - - old_current = eth_current; - do { - debug("Trying %s\n", eth_current->name); - - if (eth_current->init(eth_current, gd->bd) >= 0) { - eth_current->state = ETH_STATE_ACTIVE; - - return 0; - } - debug("FAIL\n"); - - eth_try_another(0); - } while (old_current != eth_current); - - return -ETIMEDOUT; -} - -void eth_halt(void) -{ - if (!eth_current) - return; - - eth_current->halt(eth_current); - - eth_current->state = ETH_STATE_PASSIVE; -} - -int eth_is_active(struct eth_device *dev) -{ - return dev && dev->state == ETH_STATE_ACTIVE; -} - -int eth_send(void *packet, int length) -{ - if (!eth_current) - return -ENODEV; - - return eth_current->send(eth_current, packet, length); -} - -int eth_rx(void) -{ - if (!eth_current) - return -ENODEV; - - return eth_current->recv(eth_current); -} -#endif /* ifndef CONFIG_DM_ETH */ - -#ifdef CONFIG_API -static void eth_save_packet(void *packet, int length) -{ - char *p = packet; - int i; - - if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current) - return; - - if (PKTSIZE < length) - return; - - for (i = 0; i < length; i++) - eth_rcv_bufs[eth_rcv_last].data[i] = p[i]; - - eth_rcv_bufs[eth_rcv_last].length = length; - eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX; -} - -int eth_receive(void *packet, int length) -{ - char *p = packet; - void *pp = push_packet; - int i; - - if (eth_rcv_current == eth_rcv_last) { - push_packet = eth_save_packet; - eth_rx(); - push_packet = pp; - - if (eth_rcv_current == eth_rcv_last) - return -1; - } - - length = min(eth_rcv_bufs[eth_rcv_current].length, length); - - for (i = 0; i < length; i++) - p[i] = eth_rcv_bufs[eth_rcv_current].data[i]; - - eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX; - 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 new file mode 100644 index 0000000..2880901 --- /dev/null +++ b/net/eth_common.c @@ -0,0 +1,166 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <net.h> +#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); +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_init(); +#endif + +#ifdef CONFIG_PHYLIB + 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 new file mode 100644 index 0000000..6e4753c --- /dev/null +++ b/net/eth_internal.h @@ -0,0 +1,40 @@ +/* + * (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); + +/** + * 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); + +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 diff --git a/net/eth_legacy.c b/net/eth_legacy.c new file mode 100644 index 0000000..bdcd6ea --- /dev/null +++ b/net/eth_legacy.c @@ -0,0 +1,439 @@ +/* + * (C) Copyright 2001-2015 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Joe Hershberger, National Instruments + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> +#include <environment.h> +#include <net.h> +#include <phy.h> +#include <asm/errno.h> +#include "eth_internal.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* + * CPU and board-specific Ethernet initializations. Aliased function + * signals caller to move on + */ +static int __def_eth_init(bd_t *bis) +{ + return -1; +} +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_API +static struct { + uchar data[PKTSIZE]; + int length; +} eth_rcv_bufs[PKTBUFSRX]; + +static unsigned int eth_rcv_current, eth_rcv_last; +#endif + +static struct eth_device *eth_devices; +struct eth_device *eth_current; + +void eth_set_current_to_next(void) +{ + eth_current = eth_current->next; +} + +void eth_set_dev(struct eth_device *dev) +{ + eth_current = dev; +} + +struct eth_device *eth_get_dev_by_name(const char *devname) +{ + struct eth_device *dev, *target_dev; + + BUG_ON(devname == NULL); + + if (!eth_devices) + return NULL; + + dev = eth_devices; + target_dev = NULL; + do { + if (strcmp(devname, dev->name) == 0) { + target_dev = dev; + break; + } + dev = dev->next; + } while (dev != eth_devices); + + return target_dev; +} + +struct eth_device *eth_get_dev_by_index(int index) +{ + struct eth_device *dev, *target_dev; + + if (!eth_devices) + return NULL; + + dev = eth_devices; + target_dev = NULL; + do { + if (dev->index == index) { + target_dev = dev; + break; + } + dev = dev->next; + } while (dev != eth_devices); + + return target_dev; +} + +int eth_get_dev_index(void) +{ + if (!eth_current) + return -1; + + return eth_current->index; +} + +static int on_ethaddr(const char *name, const char *value, enum env_op op, + int flags) +{ + int index; + struct eth_device *dev; + + if (!eth_devices) + return 0; + + /* look for an index after "eth" */ + index = simple_strtoul(name + 3, NULL, 10); + + dev = eth_devices; + do { + if (dev->index == index) { + switch (op) { + case env_op_create: + case env_op_overwrite: + eth_parse_enetaddr(value, dev->enetaddr); + break; + case env_op_delete: + memset(dev->enetaddr, 0, 6); + } + } + dev = dev->next; + } while (dev != eth_devices); + + return 0; +} +U_BOOT_ENV_CALLBACK(ethaddr, on_ethaddr); + +int eth_write_hwaddr(struct eth_device *dev, const char *base_name, + int eth_number) +{ + unsigned char env_enetaddr[6]; + int ret = 0; + + eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr); + + if (!is_zero_ethaddr(env_enetaddr)) { + if (!is_zero_ethaddr(dev->enetaddr) && + memcmp(dev->enetaddr, env_enetaddr, 6)) { + printf("\nWarning: %s MAC addresses don't match:\n", + dev->name); + printf("Address in SROM is %pM\n", + dev->enetaddr); + printf("Address in environment is %pM\n", + env_enetaddr); + } + + memcpy(dev->enetaddr, env_enetaddr, 6); + } else if (is_valid_ethaddr(dev->enetaddr)) { + eth_setenv_enetaddr_by_index(base_name, eth_number, + dev->enetaddr); + } else if (is_zero_ethaddr(dev->enetaddr)) { +#ifdef CONFIG_NET_RANDOM_ETHADDR + net_random_ethaddr(dev->enetaddr); + printf("\nWarning: %s (eth%d) using random MAC address - %pM\n", + dev->name, eth_number, dev->enetaddr); +#else + printf("\nError: %s address not set.\n", + dev->name); + return -EINVAL; +#endif + } + + if (dev->write_hwaddr && !eth_mac_skip(eth_number)) { + if (!is_valid_ethaddr(dev->enetaddr)) { + printf("\nError: %s address %pM illegal value\n", + dev->name, dev->enetaddr); + return -EINVAL; + } + + ret = dev->write_hwaddr(dev); + if (ret) + printf("\nWarning: %s failed to set MAC address\n", + dev->name); + } + + return ret; +} + +int eth_register(struct eth_device *dev) +{ + struct eth_device *d; + static int index; + + assert(strlen(dev->name) < sizeof(dev->name)); + + if (!eth_devices) { + eth_devices = dev; + eth_current = dev; + eth_current_changed(); + } else { + for (d = eth_devices; d->next != eth_devices; d = d->next) + ; + d->next = dev; + } + + dev->state = ETH_STATE_INIT; + dev->next = eth_devices; + dev->index = index++; + + return 0; +} + +int eth_unregister(struct eth_device *dev) +{ + struct eth_device *cur; + + /* No device */ + if (!eth_devices) + return -ENODEV; + + for (cur = eth_devices; cur->next != eth_devices && cur->next != dev; + cur = cur->next) + ; + + /* Device not found */ + if (cur->next != dev) + return -ENODEV; + + cur->next = dev->next; + + if (eth_devices == dev) + eth_devices = dev->next == eth_devices ? NULL : dev->next; + + if (eth_current == dev) { + eth_current = eth_devices; + eth_current_changed(); + } + + return 0; +} + +int eth_initialize(void) +{ + int num_devices = 0; + + 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"); + bootstage_error(BOOTSTAGE_ID_NET_ETH_START); + } else { + struct eth_device *dev = eth_devices; + char *ethprime = getenv("ethprime"); + + bootstage_mark(BOOTSTAGE_ID_NET_ETH_INIT); + do { + if (dev->index) + puts(", "); + + printf("%s", dev->name); + + if (ethprime && strcmp(dev->name, ethprime) == 0) { + eth_current = dev; + puts(" [PRIME]"); + } + + if (strchr(dev->name, ' ')) + puts("\nWarning: eth device name has a space!" + "\n"); + + eth_write_hwaddr(dev, "eth", dev->index); + + dev = dev->next; + num_devices++; + } while (dev != eth_devices); + + eth_current_changed(); + putc('\n'); + } + + return num_devices; +} + +#ifdef CONFIG_MCAST_TFTP +/* Multicast. + * mcast_addr: multicast ipaddr from which multicast Mac is made + * join: 1=join, 0=leave. + */ +int eth_mcast_join(struct in_addr mcast_ip, int join) +{ + u8 mcast_mac[6]; + if (!eth_current || !eth_current->mcast) + return -1; + mcast_mac[5] = htonl(mcast_ip.s_addr) & 0xff; + mcast_mac[4] = (htonl(mcast_ip.s_addr)>>8) & 0xff; + mcast_mac[3] = (htonl(mcast_ip.s_addr)>>16) & 0x7f; + mcast_mac[2] = 0x5e; + mcast_mac[1] = 0x0; + mcast_mac[0] = 0x1; + return eth_current->mcast(eth_current, mcast_mac, join); +} + +/* the 'way' for ethernet-CRC-32. Spliced in from Linux lib/crc32.c + * and this is the ethernet-crc method needed for TSEC -- and perhaps + * some other adapter -- hash tables + */ +#define CRCPOLY_LE 0xedb88320 +u32 ether_crc(size_t len, unsigned char const *p) +{ + int i; + u32 crc; + crc = ~0; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + /* an reverse the bits, cuz of way they arrive -- last-first */ + crc = (crc >> 16) | (crc << 16); + crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00); + crc = (crc >> 4 & 0x0f0f0f0f) | (crc << 4 & 0xf0f0f0f0); + crc = (crc >> 2 & 0x33333333) | (crc << 2 & 0xcccccccc); + crc = (crc >> 1 & 0x55555555) | (crc << 1 & 0xaaaaaaaa); + return crc; +} + +#endif + + +int eth_init(void) +{ + struct eth_device *old_current; + + if (!eth_current) { + puts("No ethernet found.\n"); + return -ENODEV; + } + + old_current = eth_current; + do { + debug("Trying %s\n", eth_current->name); + + if (eth_current->init(eth_current, gd->bd) >= 0) { + eth_current->state = ETH_STATE_ACTIVE; + + return 0; + } + debug("FAIL\n"); + + eth_try_another(0); + } while (old_current != eth_current); + + return -ETIMEDOUT; +} + +void eth_halt(void) +{ + if (!eth_current) + return; + + eth_current->halt(eth_current); + + eth_current->state = ETH_STATE_PASSIVE; +} + +int eth_is_active(struct eth_device *dev) +{ + return dev && dev->state == ETH_STATE_ACTIVE; +} + +int eth_send(void *packet, int length) +{ + if (!eth_current) + return -ENODEV; + + return eth_current->send(eth_current, packet, length); +} + +int eth_rx(void) +{ + if (!eth_current) + return -ENODEV; + + return eth_current->recv(eth_current); +} + +#ifdef CONFIG_API +static void eth_save_packet(void *packet, int length) +{ + char *p = packet; + int i; + + if ((eth_rcv_last+1) % PKTBUFSRX == eth_rcv_current) + return; + + if (PKTSIZE < length) + return; + + for (i = 0; i < length; i++) + eth_rcv_bufs[eth_rcv_last].data[i] = p[i]; + + eth_rcv_bufs[eth_rcv_last].length = length; + eth_rcv_last = (eth_rcv_last + 1) % PKTBUFSRX; +} + +int eth_receive(void *packet, int length) +{ + char *p = packet; + void *pp = push_packet; + int i; + + if (eth_rcv_current == eth_rcv_last) { + push_packet = eth_save_packet; + eth_rx(); + push_packet = pp; + + if (eth_rcv_current == eth_rcv_last) + return -1; + } + + length = min(eth_rcv_bufs[eth_rcv_current].length, length); + + for (i = 0; i < length; i++) + p[i] = eth_rcv_bufs[eth_rcv_current].data[i]; + + eth_rcv_current = (eth_rcv_current + 1) % PKTBUFSRX; + return length; +} +#endif /* CONFIG_API */ |