diff mbox series

[v2,u-boot-marvell,3/4] arm: mvebu: turris_mox: Setup Linux's device tree before boot

Message ID 20200403152501.7552-3-marek.behun@nic.cz
State Superseded
Delegated to: Stefan Roese
Headers show
Series [v2,u-boot-marvell,1/4] arm: mvebu: turris_mox: Fix early SPI communication | expand

Commit Message

Marek Behún April 3, 2020, 3:25 p.m. UTC
Patch Linux's device tree according to which Mox modules are connected.
Linux's device tree has all possible Mox module nodes preprogrammed, but
in disabled state.

If MOX B, MOX F or MOX G module is present, this code enables the PCI
node.

For the network modules (MOX C, MOX D and MOX E) are present, the code
enables corresponding ethernet and swtich nodes and DSA connections.
For the SFP cage the SFP GPIO controller node and SFP node are also
enabled.

Signed-off-by: Marek Behún <marek.behun@nic.cz>
---
 board/CZ.NIC/turris_mox/turris_mox.c | 263 +++++++++++++++++++++++++++
 configs/turris_mox_defconfig         |   1 +
 2 files changed, 264 insertions(+)

Comments

Stefan Roese April 6, 2020, 9:12 a.m. UTC | #1
On 03.04.20 17:25, Marek Behún wrote:
> Patch Linux's device tree according to which Mox modules are connected.
> Linux's device tree has all possible Mox module nodes preprogrammed, but
> in disabled state.
> 
> If MOX B, MOX F or MOX G module is present, this code enables the PCI
> node.
> 
> For the network modules (MOX C, MOX D and MOX E) are present, the code
> enables corresponding ethernet and swtich nodes and DSA connections.
> For the SFP cage the SFP GPIO controller node and SFP node are also
> enabled.
> 
> Signed-off-by: Marek Behún <marek.behun@nic.cz>
> ---

Again, patch revision history  is missing. No need to re-send now, but
please include it next time.

>   board/CZ.NIC/turris_mox/turris_mox.c | 263 +++++++++++++++++++++++++++
>   configs/turris_mox_defconfig         |   1 +
>   2 files changed, 264 insertions(+)
> 
> diff --git a/board/CZ.NIC/turris_mox/turris_mox.c b/board/CZ.NIC/turris_mox/turris_mox.c
> index 0b13d1d190..bc0d067889 100644
> --- a/board/CZ.NIC/turris_mox/turris_mox.c
> +++ b/board/CZ.NIC/turris_mox/turris_mox.c
> @@ -3,6 +3,7 @@
>    * Copyright (C) 2018 Marek Behun <marek.behun@nic.cz>
>    */
>   
> +#include <stdarg.h>

Nitpickung: Please sort include files alpabetically.

>   #include <common.h>
>   #include <init.h>
>   #include <asm/gpio.h>
> @@ -34,7 +35,11 @@
>   #define ARMADA_37XX_SPI_DOUT	0xd0010608
>   #define ARMADA_37XX_SPI_DIN	0xd001060c
>   
> +#define ETH1_PATH	"/soc/internal-regs@d0000000/ethernet@40000"
> +#define MDIO_PATH	"/soc/internal-regs@d0000000/mdio@32004"
> +#define SFP_GPIO_PATH	"/soc/internal-regs@d0000000/spi@10600/moxtet@1/gpio@0"
>   #define PCIE_PATH	"/soc/pcie@d0070000"
> +#define SFP_PATH	"/sfp"
>   
>   DECLARE_GLOBAL_DATA_PTR;
>   
> @@ -551,3 +556,261 @@ int last_stage_init(void)
>   
>   	return 0;
>   }
> +
> +#if defined(CONFIG_OF_BOARD_SETUP)
> +
> +static int vnode_by_path(void *blob, const char *fmt, va_list ap)
> +{
> +	char path[128];
> +
> +	vsprintf(path, fmt, ap);

vsnprintf ?

> +	return fdt_path_offset(blob, path);
> +}
> +
> +static int node_by_path(void *blob, const char *fmt, ...)
> +{
> +	va_list ap;
> +	int res;
> +
> +	va_start(ap, fmt);
> +	res = vnode_by_path(blob, fmt, ap);
> +	va_end(ap);
> +
> +	return res;
> +}
> +
> +static int phandle_by_path(void *blob, const char *fmt, ...)
> +{
> +	va_list ap;
> +	int node, phandle, res;
> +
> +	va_start(ap, fmt);
> +	node = vnode_by_path(blob, fmt, ap);
> +	va_end(ap);
> +
> +	if (node < 0)
> +		return node;
> +
> +	phandle = fdt_get_phandle(blob, node);
> +	if (phandle > 0)
> +		return phandle;
> +
> +	phandle = fdt_get_max_phandle(blob);
> +	if (phandle < 0)
> +		return phandle;
> +
> +	phandle += 1;
> +
> +	res = fdt_setprop_u32(blob, node, "linux,phandle", phandle);
> +	if (res < 0)
> +		return res;
> +
> +	res = fdt_setprop_u32(blob, node, "phandle", phandle);
> +	if (res < 0)
> +		return res;
> +
> +	return phandle;
> +}
> +
> +static int enable_by_path(void *blob, const char *fmt, ...)
> +{
> +	va_list ap;
> +	int node;
> +
> +	va_start(ap, fmt);
> +	node = vnode_by_path(blob, fmt, ap);
> +	va_end(ap);
> +
> +	if (node < 0)
> +		return node;
> +
> +	return fdt_setprop_string(blob, node, "status", "okay");
> +}
> +
> +static bool is_topaz(int id)
> +{
> +	return topaz && id == peridot + topaz - 1;
> +}
> +
> +static int switch_addr(int id)
> +{
> +	return is_topaz(id) ? 0x2 : 0x10 + id;
> +}
> +
> +static int setup_switch(void *blob, int id)
> +{
> +	int res, addr, i, node, phandle;
> +
> +	addr = switch_addr(id);
> +
> +	/* first enable the switch by setting status = "okay" */
> +	res = enable_by_path(blob, MDIO_PATH "/switch%i@%x", id, addr);
> +	if (res < 0)
> +		return res;
> +
> +	/*
> +	 * now if there are more switches or a SFP module coming after,
> +	 * enable corresponding ports
> +	 */
> +	if (id < peridot + topaz - 1)
> +		res = enable_by_path(blob,
> +				     MDIO_PATH "/switch%i@%x/ports/port@a",
> +				     id, addr);
> +	else if (id == peridot - 1 && !topaz && sfp)
> +		res = enable_by_path(blob,
> +				     MDIO_PATH "/switch%i@%x/ports/port-sfp@a",
> +				     id, addr);
> +	else
> +		res = 0;

Please use paranthesis on multi-line statements.

> +	if (res < 0)
> +		return res;
> +
> +	if (id >= peridot + topaz - 1)
> +		return 0;
> +
> +	/* finally change link property if needed */
> +	node = node_by_path(blob, MDIO_PATH "/switch%i@%x/ports/port@a", id,
> +			    addr);
> +	if (node < 0)
> +		return node;
> +
> +	for (i = id + 1; i < peridot + topaz; ++i) {
> +		phandle = phandle_by_path(blob,
> +					  MDIO_PATH "/switch%i@%x/ports/port@%x",
> +					  i, switch_addr(i),
> +					  is_topaz(i) ? 5 : 9);
> +		if (phandle < 0)
> +			return phandle;
> +
> +		if (i == id + 1)
> +			res = fdt_setprop_u32(blob, node, "link", phandle);
> +		else
> +			res = fdt_appendprop_u32(blob, node, "link", phandle);
> +		if (res < 0)
> +			return res;
> +	}
> +
> +	return 0;
> +}
> +
> +static int remove_disabled_nodes(void *blob)
> +{
> +	while (1) {
> +		int res, offset;
> +
> +		offset = fdt_node_offset_by_prop_value(blob, -1, "status",
> +						       "disabled", 9);
> +		if (offset < 0)
> +			break;
> +
> +		res = fdt_del_node(blob, offset);
> +		if (res < 0)
> +			return res;
> +	}
> +
> +	return 0;
> +}
> +
> +int ft_board_setup(void *blob, bd_t *bd)
> +{
> +	int node, phandle, res;
> +
> +	/*
> +	 * If MOX B (PCI), MOX F (USB) or MOX G (Passthrough PCI) modules are
> +	 * connected, enable the PCIe node.
> +	 */
> +	if (pci || usb || passpci) {
> +		node = fdt_path_offset(blob, PCIE_PATH);
> +		if (node < 0)
> +			return node;
> +
> +		res = fdt_setprop_string(blob, node, "status", "okay");
> +		if (res < 0)
> +			return res;
> +	}
> +
> +	/*
> +	 * If MOX C (Topaz switch) and/or MOX E (Peridot switch) are connected,
> +	 * enable the eth1 node and setup the switches.
> +	 */
> +	if (peridot || topaz) {
> +		int i;
> +
> +		res = enable_by_path(blob, ETH1_PATH);
> +		if (res < 0)
> +			return res;
> +
> +		for (i = 0; i < peridot + topaz; ++i) {
> +			res = setup_switch(blob, i);
> +			if (res < 0)
> +				return res;
> +		}
> +	}
> +
> +	/*
> +	 * If MOX D (SFP cage module) is connected, enable the SFP node and eth1
> +	 * node. If there is no Peridot switch between MOX A and MOX D, add link
> +	 * to the SFP node to eth1 node.
> +	 * Also enable and configure SFP GPIO controller node.
> +	 */
> +	if (sfp) {
> +		res = enable_by_path(blob, SFP_PATH);
> +		if (res < 0)
> +			return res;
> +
> +		res = enable_by_path(blob, ETH1_PATH);
> +		if (res < 0)
> +			return res;
> +
> +		if (!peridot) {
> +			phandle = phandle_by_path(blob, SFP_PATH);
> +			if (phandle < 0)
> +				return res;
> +
> +			node = node_by_path(blob, ETH1_PATH);
> +			if (node < 0)
> +				return node;
> +
> +			res = fdt_setprop_u32(blob, node, "sfp", phandle);
> +			if (res < 0)
> +				return res;
> +
> +			res = fdt_setprop_string(blob, node, "phy-mode",
> +						 "sgmii");
> +			if (res < 0)
> +				return res;
> +		}
> +
> +		res = enable_by_path(blob, SFP_GPIO_PATH);
> +		if (res < 0)
> +			return res;
> +
> +		if (sfp_pos) {
> +			char newname[16];
> +
> +			/* moxtet-sfp is on non-zero position, change default */
> +			node = node_by_path(blob, SFP_GPIO_PATH);
> +			if (node < 0)
> +				return node;
> +
> +			res = fdt_setprop_u32(blob, node, "reg", sfp_pos);
> +			if (res < 0)
> +				return res;
> +
> +			sprintf(newname, "gpio@%x", sfp_pos);
> +
> +			res = fdt_set_name(blob, node, newname);
> +			if (res < 0)
> +				return res;
> +		}
> +	}
> +
> +	fdt_fixup_ethernet(blob);
> +
> +	/* Finally remove disabled nodes, as per Rob Herring's request. */
> +	remove_disabled_nodes(blob);
> +
> +	return 0;
> +}
> +
> +#endif
> diff --git a/configs/turris_mox_defconfig b/configs/turris_mox_defconfig
> index 2e637044c1..3bc69cda4d 100644
> --- a/configs/turris_mox_defconfig
> +++ b/configs/turris_mox_defconfig
> @@ -37,6 +37,7 @@ CONFIG_CMD_BTRFS=y
>   CONFIG_CMD_EXT4_WRITE=y
>   CONFIG_MAC_PARTITION=y
>   CONFIG_OF_BOARD_FIXUP=y
> +CONFIG_OF_BOARD_SETUP=y
>   CONFIG_DEFAULT_DEVICE_TREE="armada-3720-turris-mox"
>   CONFIG_ENV_IS_IN_SPI_FLASH=y
>   CONFIG_SYS_RELOC_GD_ENV_ADDR=y
> 

Other than the above comments:

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

Thanks,
Stefan
diff mbox series

Patch

diff --git a/board/CZ.NIC/turris_mox/turris_mox.c b/board/CZ.NIC/turris_mox/turris_mox.c
index 0b13d1d190..bc0d067889 100644
--- a/board/CZ.NIC/turris_mox/turris_mox.c
+++ b/board/CZ.NIC/turris_mox/turris_mox.c
@@ -3,6 +3,7 @@ 
  * Copyright (C) 2018 Marek Behun <marek.behun@nic.cz>
  */
 
+#include <stdarg.h>
 #include <common.h>
 #include <init.h>
 #include <asm/gpio.h>
@@ -34,7 +35,11 @@ 
 #define ARMADA_37XX_SPI_DOUT	0xd0010608
 #define ARMADA_37XX_SPI_DIN	0xd001060c
 
+#define ETH1_PATH	"/soc/internal-regs@d0000000/ethernet@40000"
+#define MDIO_PATH	"/soc/internal-regs@d0000000/mdio@32004"
+#define SFP_GPIO_PATH	"/soc/internal-regs@d0000000/spi@10600/moxtet@1/gpio@0"
 #define PCIE_PATH	"/soc/pcie@d0070000"
+#define SFP_PATH	"/sfp"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -551,3 +556,261 @@  int last_stage_init(void)
 
 	return 0;
 }
+
+#if defined(CONFIG_OF_BOARD_SETUP)
+
+static int vnode_by_path(void *blob, const char *fmt, va_list ap)
+{
+	char path[128];
+
+	vsprintf(path, fmt, ap);
+	return fdt_path_offset(blob, path);
+}
+
+static int node_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	va_start(ap, fmt);
+	res = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	return res;
+}
+
+static int phandle_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node, phandle, res;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	phandle = fdt_get_phandle(blob, node);
+	if (phandle > 0)
+		return phandle;
+
+	phandle = fdt_get_max_phandle(blob);
+	if (phandle < 0)
+		return phandle;
+
+	phandle += 1;
+
+	res = fdt_setprop_u32(blob, node, "linux,phandle", phandle);
+	if (res < 0)
+		return res;
+
+	res = fdt_setprop_u32(blob, node, "phandle", phandle);
+	if (res < 0)
+		return res;
+
+	return phandle;
+}
+
+static int enable_by_path(void *blob, const char *fmt, ...)
+{
+	va_list ap;
+	int node;
+
+	va_start(ap, fmt);
+	node = vnode_by_path(blob, fmt, ap);
+	va_end(ap);
+
+	if (node < 0)
+		return node;
+
+	return fdt_setprop_string(blob, node, "status", "okay");
+}
+
+static bool is_topaz(int id)
+{
+	return topaz && id == peridot + topaz - 1;
+}
+
+static int switch_addr(int id)
+{
+	return is_topaz(id) ? 0x2 : 0x10 + id;
+}
+
+static int setup_switch(void *blob, int id)
+{
+	int res, addr, i, node, phandle;
+
+	addr = switch_addr(id);
+
+	/* first enable the switch by setting status = "okay" */
+	res = enable_by_path(blob, MDIO_PATH "/switch%i@%x", id, addr);
+	if (res < 0)
+		return res;
+
+	/*
+	 * now if there are more switches or a SFP module coming after,
+	 * enable corresponding ports
+	 */
+	if (id < peridot + topaz - 1)
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port@a",
+				     id, addr);
+	else if (id == peridot - 1 && !topaz && sfp)
+		res = enable_by_path(blob,
+				     MDIO_PATH "/switch%i@%x/ports/port-sfp@a",
+				     id, addr);
+	else
+		res = 0;
+	if (res < 0)
+		return res;
+
+	if (id >= peridot + topaz - 1)
+		return 0;
+
+	/* finally change link property if needed */
+	node = node_by_path(blob, MDIO_PATH "/switch%i@%x/ports/port@a", id,
+			    addr);
+	if (node < 0)
+		return node;
+
+	for (i = id + 1; i < peridot + topaz; ++i) {
+		phandle = phandle_by_path(blob,
+					  MDIO_PATH "/switch%i@%x/ports/port@%x",
+					  i, switch_addr(i),
+					  is_topaz(i) ? 5 : 9);
+		if (phandle < 0)
+			return phandle;
+
+		if (i == id + 1)
+			res = fdt_setprop_u32(blob, node, "link", phandle);
+		else
+			res = fdt_appendprop_u32(blob, node, "link", phandle);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+static int remove_disabled_nodes(void *blob)
+{
+	while (1) {
+		int res, offset;
+
+		offset = fdt_node_offset_by_prop_value(blob, -1, "status",
+						       "disabled", 9);
+		if (offset < 0)
+			break;
+
+		res = fdt_del_node(blob, offset);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+int ft_board_setup(void *blob, bd_t *bd)
+{
+	int node, phandle, res;
+
+	/*
+	 * If MOX B (PCI), MOX F (USB) or MOX G (Passthrough PCI) modules are
+	 * connected, enable the PCIe node.
+	 */
+	if (pci || usb || passpci) {
+		node = fdt_path_offset(blob, PCIE_PATH);
+		if (node < 0)
+			return node;
+
+		res = fdt_setprop_string(blob, node, "status", "okay");
+		if (res < 0)
+			return res;
+	}
+
+	/*
+	 * If MOX C (Topaz switch) and/or MOX E (Peridot switch) are connected,
+	 * enable the eth1 node and setup the switches.
+	 */
+	if (peridot || topaz) {
+		int i;
+
+		res = enable_by_path(blob, ETH1_PATH);
+		if (res < 0)
+			return res;
+
+		for (i = 0; i < peridot + topaz; ++i) {
+			res = setup_switch(blob, i);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	/*
+	 * If MOX D (SFP cage module) is connected, enable the SFP node and eth1
+	 * node. If there is no Peridot switch between MOX A and MOX D, add link
+	 * to the SFP node to eth1 node.
+	 * Also enable and configure SFP GPIO controller node.
+	 */
+	if (sfp) {
+		res = enable_by_path(blob, SFP_PATH);
+		if (res < 0)
+			return res;
+
+		res = enable_by_path(blob, ETH1_PATH);
+		if (res < 0)
+			return res;
+
+		if (!peridot) {
+			phandle = phandle_by_path(blob, SFP_PATH);
+			if (phandle < 0)
+				return res;
+
+			node = node_by_path(blob, ETH1_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "sfp", phandle);
+			if (res < 0)
+				return res;
+
+			res = fdt_setprop_string(blob, node, "phy-mode",
+						 "sgmii");
+			if (res < 0)
+				return res;
+		}
+
+		res = enable_by_path(blob, SFP_GPIO_PATH);
+		if (res < 0)
+			return res;
+
+		if (sfp_pos) {
+			char newname[16];
+
+			/* moxtet-sfp is on non-zero position, change default */
+			node = node_by_path(blob, SFP_GPIO_PATH);
+			if (node < 0)
+				return node;
+
+			res = fdt_setprop_u32(blob, node, "reg", sfp_pos);
+			if (res < 0)
+				return res;
+
+			sprintf(newname, "gpio@%x", sfp_pos);
+
+			res = fdt_set_name(blob, node, newname);
+			if (res < 0)
+				return res;
+		}
+	}
+
+	fdt_fixup_ethernet(blob);
+
+	/* Finally remove disabled nodes, as per Rob Herring's request. */
+	remove_disabled_nodes(blob);
+
+	return 0;
+}
+
+#endif
diff --git a/configs/turris_mox_defconfig b/configs/turris_mox_defconfig
index 2e637044c1..3bc69cda4d 100644
--- a/configs/turris_mox_defconfig
+++ b/configs/turris_mox_defconfig
@@ -37,6 +37,7 @@  CONFIG_CMD_BTRFS=y
 CONFIG_CMD_EXT4_WRITE=y
 CONFIG_MAC_PARTITION=y
 CONFIG_OF_BOARD_FIXUP=y
+CONFIG_OF_BOARD_SETUP=y
 CONFIG_DEFAULT_DEVICE_TREE="armada-3720-turris-mox"
 CONFIG_ENV_IS_IN_SPI_FLASH=y
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y