aboutsummaryrefslogtreecommitdiff
path: root/board
diff options
context:
space:
mode:
authorPali Rohár2022-07-29 13:29:07 +0200
committerStefan Roese2022-08-09 08:57:23 +0200
commit1da53ae26afc4f084cf35fea706090fa03dc8475 (patch)
tree4d2faa9dfb1d7d6bf655929138015eb3c9497e84 /board
parent336a27ab1d3659c7e1dd6a48e017f5074be70eca (diff)
arm: mvebu: turris_omnia: Add support for design with SW reset signals
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@kernel.org> Reviewed-by: Stefan Roese <sr@denx.de> Reviewed-by: Marek Behún <kabel@kernel.org>
Diffstat (limited to 'board')
-rw-r--r--board/CZ.NIC/turris_omnia/turris_omnia.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/board/CZ.NIC/turris_omnia/turris_omnia.c b/board/CZ.NIC/turris_omnia/turris_omnia.c
index dab5711cf0c..a7c1c1fc797 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);