[PATCH 2/2] arm: mvebu: turris_omnia: Add support for design with SW reset signals
Stefan Roese
sr at denx.de
Tue Aug 9 13:32:15 CEST 2022
On 29.07.22 13:29, Pali Rohár wrote:
> New Turris Omnia HW board revision requires that software controls
> peripheral reset signals, namely PERST# signals on mPCIe slots, ethernet
> phy reset and lan switch reset. Those pins are connected to MCU controlled
> by MCU i2c API as GPIOs. On new HW board revision those pins stay in reset
> after board reset and software has to release these peripherals from reset
> manually. MCU announce this requirement by FEAT_PERIPH_MCU bit in
> CMD_GET_FEATURES command.
>
> On older HW board revisions when FEAT_PERIPH_MCU is not announced, all
> those reset signals are automatically released after board finish reset.
>
> Detect FEAT_PERIPH_MCU bit in board_fix_fdt() and ft_board_setup()
> functions and insert into device tree blob pcie "reset-gpios" and eth phy
> "phy-reset-gpios" properties with corresponding MCU gpio definitions.
> PCIe and eth PHY drivers then automatically release resets during device
> initialization. Both U-Boot and Linux kernel drivers support those device
> tree reset properties.
>
> Initialization of lan switch on new HW board revision is more complicated.
> Switch strapping pins are shared with switch RGMII pins. And strapping pins
> must be in specific configuration after releasing switch reset. Due to pin
> sharing, it is first required to switch A385 side of switch pins into GPIO
> mode, set strapping configuration, release switch from reset and after that
> switch A385 pins back to RGMII mode.
>
> Because this complicated setup is not supported by switch DSA drivers and
> cannot be expressed easily in device tree, implement it manually in SPL
> function spl_board_init(). So in proper U-Boot and OS/kernel would be lan
> switch initialized and be in same configuration like it was on old HW board
> revisions (where reset sequence did those steps at hardware level).
>
> Signed-off-by: Pali Rohár <pali at kernel.org>
Applied to u-boot-marvell/master
Thanks,
Stefan
> ---
> board/CZ.NIC/turris_omnia/turris_omnia.c | 226 +++++++++++++++++++++++
> 1 file changed, 226 insertions(+)
>
> diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c
> index 6fc2018c1cfb..e262ea1aba51 100644
> --- a/board/CZ.NIC/turris_omnia/turris_omnia.c
> +++ b/board/CZ.NIC/turris_omnia/turris_omnia.c
> @@ -19,9 +19,11 @@
> #include <asm/arch/cpu.h>
> #include <asm/arch/soc.h>
> #include <dm/uclass.h>
> +#include <dt-bindings/gpio/gpio.h>
> #include <fdt_support.h>
> #include <time.h>
> #include <linux/bitops.h>
> +#include <linux/delay.h>
> #include <u-boot/crc.h>
>
> #include "../drivers/ddr/marvell/a38x/ddr3_init.h"
> @@ -65,6 +67,9 @@ enum mcu_commands {
>
> /* available if STS_FEATURES_SUPPORTED bit set in status word */
> CMD_GET_FEATURES = 0x10,
> +
> + /* available if EXT_CMD bit set in features */
> + CMD_EXT_CONTROL = 0x12,
> };
>
> enum status_word_bits {
> @@ -81,6 +86,16 @@ enum status_word_bits {
> /* CMD_GET_FEATURES */
> enum features_e {
> FEAT_PERIPH_MCU = BIT(0),
> + FEAT_EXT_CMDS = BIT(1),
> +};
> +
> +/* CMD_EXT_CONTROL */
> +enum ext_ctl_e {
> + EXT_CTL_nRES_LAN = BIT(1),
> + EXT_CTL_nRES_PHY = BIT(2),
> + EXT_CTL_nPERST0 = BIT(3),
> + EXT_CTL_nPERST1 = BIT(4),
> + EXT_CTL_nPERST2 = BIT(5),
> };
>
> /*
> @@ -543,6 +558,90 @@ static void handle_reset_button(void)
> }
> }
>
> +static void initialize_switch(void)
> +{
> + u32 val, val04, val08, val10, val14;
> + u16 ctrl[2];
> + int err;
> +
> + printf("Initializing LAN eth switch... ");
> +
> + /* Change RGMII pins to GPIO mode */
> +
> + val = val04 = readl(MVEBU_MPP_BASE + 0x04);
> + val &= ~GENMASK(19, 16); /* MPP[12] := GPIO */
> + val &= ~GENMASK(23, 20); /* MPP[13] := GPIO */
> + val &= ~GENMASK(27, 24); /* MPP[14] := GPIO */
> + val &= ~GENMASK(31, 28); /* MPP[15] := GPIO */
> + writel(val, MVEBU_MPP_BASE + 0x04);
> +
> + val = val08 = readl(MVEBU_MPP_BASE + 0x08);
> + val &= ~GENMASK(3, 0); /* MPP[16] := GPIO */
> + val &= ~GENMASK(23, 20); /* MPP[21] := GPIO */
> + writel(val, MVEBU_MPP_BASE + 0x08);
> +
> + val = val10 = readl(MVEBU_MPP_BASE + 0x10);
> + val &= ~GENMASK(27, 24); /* MPP[38] := GPIO */
> + val &= ~GENMASK(31, 28); /* MPP[39] := GPIO */
> + writel(val, MVEBU_MPP_BASE + 0x10);
> +
> + val = val14 = readl(MVEBU_MPP_BASE + 0x14);
> + val &= ~GENMASK(3, 0); /* MPP[40] := GPIO */
> + val &= ~GENMASK(7, 4); /* MPP[41] := GPIO */
> + writel(val, MVEBU_MPP_BASE + 0x14);
> +
> + /* Set initial values for switch reset strapping pins */
> +
> + val = readl(MVEBU_GPIO0_BASE + 0x00);
> + val |= BIT(12); /* GPIO[12] := 1 */
> + val |= BIT(13); /* GPIO[13] := 1 */
> + val |= BIT(14); /* GPIO[14] := 1 */
> + val |= BIT(15); /* GPIO[15] := 1 */
> + val &= ~BIT(16); /* GPIO[16] := 0 */
> + val |= BIT(21); /* GPIO[21] := 1 */
> + writel(val, MVEBU_GPIO0_BASE + 0x00);
> +
> + val = readl(MVEBU_GPIO1_BASE + 0x00);
> + val |= BIT(6); /* GPIO[38] := 1 */
> + val |= BIT(7); /* GPIO[39] := 1 */
> + val |= BIT(8); /* GPIO[40] := 1 */
> + val &= ~BIT(9); /* GPIO[41] := 0 */
> + writel(val, MVEBU_GPIO1_BASE + 0x00);
> +
> + val = readl(MVEBU_GPIO0_BASE + 0x04);
> + val &= ~BIT(12); /* GPIO[12] := Out Enable */
> + val &= ~BIT(13); /* GPIO[13] := Out Enable */
> + val &= ~BIT(14); /* GPIO[14] := Out Enable */
> + val &= ~BIT(15); /* GPIO[15] := Out Enable */
> + val &= ~BIT(16); /* GPIO[16] := Out Enable */
> + val &= ~BIT(21); /* GPIO[21] := Out Enable */
> + writel(val, MVEBU_GPIO0_BASE + 0x04);
> +
> + val = readl(MVEBU_GPIO1_BASE + 0x04);
> + val &= ~BIT(6); /* GPIO[38] := Out Enable */
> + val &= ~BIT(7); /* GPIO[39] := Out Enable */
> + val &= ~BIT(8); /* GPIO[40] := Out Enable */
> + val &= ~BIT(9); /* GPIO[41] := Out Enable */
> + writel(val, MVEBU_GPIO1_BASE + 0x04);
> +
> + /* Release switch reset */
> +
> + ctrl[0] = EXT_CTL_nRES_LAN;
> + ctrl[1] = EXT_CTL_nRES_LAN;
> + err = omnia_mcu_write(CMD_EXT_CONTROL, ctrl, sizeof(ctrl));
> +
> + mdelay(10);
> +
> + /* Change RGMII pins back to RGMII mode */
> +
> + writel(val04, MVEBU_MPP_BASE + 0x04);
> + writel(val08, MVEBU_MPP_BASE + 0x08);
> + writel(val10, MVEBU_MPP_BASE + 0x10);
> + writel(val14, MVEBU_MPP_BASE + 0x14);
> +
> + puts(err ? "failed\n" : "done\n");
> +}
> +
> int board_early_init_f(void)
> {
> /* Configure MPP */
> @@ -572,6 +671,9 @@ int board_early_init_f(void)
>
> void spl_board_init(void)
> {
> + u16 val;
> + int ret;
> +
> /*
> * If booting from UART, disable MCU watchdog in SPL, since uploading
> * U-Boot proper can take too much time and trigger it. Instead enable
> @@ -581,6 +683,19 @@ void spl_board_init(void)
> enable_a385_watchdog(10);
> disable_mcu_watchdog();
> }
> +
> + /*
> + * When MCU controls peripheral resets then release LAN eth switch from
> + * the reset and initialize it. When MCU does not control peripheral
> + * resets then LAN eth switch is initialized automatically by bootstrap
> + * pins when A385 is released from the reset.
> + */
> + ret = omnia_mcu_read(CMD_GET_STATUS_WORD, &val, sizeof(val));
> + if (ret == 0 && (val & STS_FEATURES_SUPPORTED)) {
> + ret = omnia_mcu_read(CMD_GET_FEATURES, &val, sizeof(val));
> + if (ret == 0 && (val & FEAT_PERIPH_MCU))
> + initialize_switch();
> + }
> }
>
> #if IS_ENABLED(CONFIG_OF_BOARD_FIXUP) || IS_ENABLED(CONFIG_OF_BOARD_SETUP)
> @@ -689,11 +804,109 @@ static void fixup_wwan_port_nodes(void *blob)
> disable_pcie_node(blob, 2);
> }
>
> +static int insert_mcu_gpio_prop(void *blob, int node, const char *prop,
> + unsigned int phandle, u32 bank, u32 gpio,
> + u32 flags)
> +{
> + fdt32_t val[4] = { cpu_to_fdt32(phandle), cpu_to_fdt32(bank),
> + cpu_to_fdt32(gpio), cpu_to_fdt32(flags) };
> + return fdt_setprop(blob, node, prop, &val, sizeof(val));
> +}
> +
> +static int fixup_mcu_gpio_in_pcie_nodes(void *blob)
> +{
> + unsigned int mcu_phandle;
> + int port, gpio;
> + int pcie_node;
> + int port_node;
> + int ret;
> +
> + ret = fdt_increase_size(blob, 128);
> + if (ret < 0) {
> + printf("Cannot increase FDT size!\n");
> + return ret;
> + }
> +
> + mcu_phandle = fdt_create_phandle_by_compatible(blob, "cznic,turris-omnia-mcu");
> + if (!mcu_phandle)
> + return -FDT_ERR_NOPHANDLES;
> +
> + fdt_for_each_node_by_compatible(pcie_node, blob, -1, "marvell,armada-370-pcie") {
> + if (!fdtdec_get_is_enabled(blob, pcie_node))
> + continue;
> +
> + fdt_for_each_subnode(port_node, blob, pcie_node) {
> + if (!fdtdec_get_is_enabled(blob, port_node))
> + continue;
> +
> + port = fdtdec_get_int(blob, port_node, "marvell,pcie-port", -1);
> +
> + if (port == 0)
> + gpio = ilog2(EXT_CTL_nPERST0);
> + else if (port == 1)
> + gpio = ilog2(EXT_CTL_nPERST1);
> + else if (port == 2)
> + gpio = ilog2(EXT_CTL_nPERST2);
> + else
> + continue;
> +
> + /* insert: reset-gpios = <&mcu 2 gpio GPIO_ACTIVE_LOW>; */
> + ret = insert_mcu_gpio_prop(blob, port_node, "reset-gpios",
> + mcu_phandle, 2, gpio, GPIO_ACTIVE_LOW);
> + if (ret < 0)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int fixup_mcu_gpio_in_eth_wan_node(void *blob)
> +{
> + unsigned int mcu_phandle;
> + int eth_wan_node;
> + int ret;
> +
> + ret = fdt_increase_size(blob, 64);
> + if (ret < 0) {
> + printf("Cannot increase FDT size!\n");
> + return ret;
> + }
> +
> + eth_wan_node = fdt_path_offset(blob, "ethernet2");
> + if (eth_wan_node < 0)
> + return eth_wan_node;
> +
> + mcu_phandle = fdt_create_phandle_by_compatible(blob, "cznic,turris-omnia-mcu");
> + if (!mcu_phandle)
> + return -FDT_ERR_NOPHANDLES;
> +
> + /* insert: phy-reset-gpios = <&mcu 2 gpio GPIO_ACTIVE_LOW>; */
> + ret = insert_mcu_gpio_prop(blob, eth_wan_node, "phy-reset-gpios",
> + mcu_phandle, 2, ilog2(EXT_CTL_nRES_PHY), GPIO_ACTIVE_LOW);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> #endif
>
> #if IS_ENABLED(CONFIG_OF_BOARD_FIXUP)
> int board_fix_fdt(void *blob)
> {
> + u16 val;
> + int ret;
> +
> + ret = omnia_mcu_read(CMD_GET_STATUS_WORD, &val, sizeof(val));
> + if (ret == 0 && (val & STS_FEATURES_SUPPORTED)) {
> + ret = omnia_mcu_read(CMD_GET_FEATURES, &val, sizeof(val));
> + if (ret == 0 && (val & FEAT_PERIPH_MCU)) {
> + fixup_mcu_gpio_in_pcie_nodes(blob);
> + fixup_mcu_gpio_in_eth_wan_node(blob);
> + }
> + }
> +
> fixup_msata_port_nodes(blob);
> fixup_wwan_port_nodes(blob);
>
> @@ -840,6 +1053,19 @@ fail:
>
> int ft_board_setup(void *blob, struct bd_info *bd)
> {
> + int node;
> +
> + /*
> + * U-Boot's FDT blob contains phy-reset-gpios in ethernet2
> + * node when MCU controls all peripherals resets.
> + * Fixup MCU GPIO nodes in PCIe and eth wan nodes in this case.
> + */
> + node = fdt_path_offset(gd->fdt_blob, "ethernet2");
> + if (node >= 0 && fdt_getprop(gd->fdt_blob, node, "phy-reset-gpios", NULL)) {
> + fixup_mcu_gpio_in_pcie_nodes(blob);
> + fixup_mcu_gpio_in_eth_wan_node(blob);
> + }
> +
> fixup_spi_nor_partitions(blob);
> fixup_msata_port_nodes(blob);
> fixup_wwan_port_nodes(blob);
Viele Grüße,
Stefan Roese
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
More information about the U-Boot
mailing list