[PATCH 2/2] arm: mvebu: turris_omnia: Add support for design with SW reset signals

Stefan Roese sr at denx.de
Mon Aug 1 13:58:24 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>

Wow, this looks pretty complex. But I assume that you already de-stilled
the "best solution", so:

Reviewed-by: Stefan Roese <sr at denx.de>

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