MIPS: SEAD3: Probe UARTs using DT

Probe the UARTs on SEAD3 boards using device tree rather than platform
code, in order to reduce the amount of the latter. This requires that
CONFIG_SERIAL_OF_PLATFORM be enabled, so enable it in sead3_defconfig.
The SEAD3 DT shim code is extended to read bootloader environment
variables to determine the appropriate UART & mode for kernel console
output & set the stdout-path property of the chosen node accordingly.

In contrast to the old platform code, which appears to have only ever
set "console=ttyS0,38400n8r" with the code in console_config never
having an effect, this will honor the "yamontty" environment variable to
select between the 2 UARTs on the board and then check the "modetty0" or
"modetty1" variable as appropriate to determine the UART configuration.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/14048/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/mti-sead3/sead3-dtshim.c b/arch/mips/mti-sead3/sead3-dtshim.c
index 3283a7e..b462f65 100644
--- a/arch/mips/mti-sead3/sead3-dtshim.c
+++ b/arch/mips/mti-sead3/sead3-dtshim.c
@@ -14,6 +14,7 @@
 #include <linux/libfdt.h>
 #include <linux/printk.h>
 
+#include <asm/fw/fw.h>
 #include <asm/io.h>
 
 #define SEAD_CONFIG			CKSEG1ADDR(0x1b100110)
@@ -23,7 +24,8 @@
 
 static int remove_gic(void *fdt)
 {
-	int gic_off, cpu_off, err;
+	const unsigned int cpu_uart_int = 4;
+	int gic_off, cpu_off, uart_off, err;
 	uint32_t cfg, cpu_phandle;
 
 	/* leave the GIC node intact if a GIC is present */
@@ -62,6 +64,103 @@
 		return err;
 	}
 
+	uart_off = fdt_node_offset_by_compatible(fdt, -1, "ns16550a");
+	while (uart_off >= 0) {
+		err = fdt_setprop_u32(fdt, uart_off, "interrupts",
+				      cpu_uart_int);
+		if (err) {
+			pr_err("unable to set UART interrupts property: %d\n",
+			       err);
+			return err;
+		}
+
+		uart_off = fdt_node_offset_by_compatible(fdt, uart_off,
+							 "ns16550a");
+	}
+	if (uart_off != -FDT_ERR_NOTFOUND) {
+		pr_err("error searching for UART DT node: %d\n", uart_off);
+		return uart_off;
+	}
+
+	return 0;
+}
+
+static int serial_config(void *fdt)
+{
+	const char *yamontty, *mode_var;
+	char mode_var_name[9], path[18], parity;
+	unsigned int uart, baud, stop_bits;
+	bool hw_flow;
+	int chosen_off, err;
+
+	yamontty = fw_getenv("yamontty");
+	if (!yamontty || !strcmp(yamontty, "tty0")) {
+		uart = 0;
+	} else if (!strcmp(yamontty, "tty1")) {
+		uart = 1;
+	} else {
+		pr_warn("yamontty environment variable '%s' invalid\n",
+			yamontty);
+		uart = 0;
+	}
+
+	baud = stop_bits = 0;
+	parity = 0;
+	hw_flow = false;
+
+	snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart);
+	mode_var = fw_getenv(mode_var_name);
+	if (mode_var) {
+		while (mode_var[0] >= '0' && mode_var[0] <= '9') {
+			baud *= 10;
+			baud += mode_var[0] - '0';
+			mode_var++;
+		}
+		if (mode_var[0] == ',')
+			mode_var++;
+		if (mode_var[0])
+			parity = mode_var[0];
+		if (mode_var[0] == ',')
+			mode_var++;
+		if (mode_var[0])
+			stop_bits = mode_var[0] - '0';
+		if (mode_var[0] == ',')
+			mode_var++;
+		if (!strcmp(mode_var, "hw"))
+			hw_flow = true;
+	}
+
+	if (!baud)
+		baud = 38400;
+
+	if (parity != 'e' && parity != 'n' && parity != 'o')
+		parity = 'n';
+
+	if (stop_bits != 7 && stop_bits != 8)
+		stop_bits = 8;
+
+	WARN_ON(snprintf(path, sizeof(path), "uart%u:%u%c%u%s",
+			 uart, baud, parity, stop_bits,
+			 hw_flow ? "r" : "") >= sizeof(path));
+
+	/* find or add chosen node */
+	chosen_off = fdt_path_offset(fdt, "/chosen");
+	if (chosen_off == -FDT_ERR_NOTFOUND)
+		chosen_off = fdt_path_offset(fdt, "/chosen@0");
+	if (chosen_off == -FDT_ERR_NOTFOUND)
+		chosen_off = fdt_add_subnode(fdt, 0, "chosen");
+	if (chosen_off < 0) {
+		pr_err("Unable to find or add DT chosen node: %d\n",
+		       chosen_off);
+		return chosen_off;
+	}
+
+	err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path);
+	if (err) {
+		pr_err("Unable to set stdout-path property: %d\n", err);
+		return err;
+	}
+
 	return 0;
 }
 
@@ -84,6 +183,10 @@
 	if (err)
 		panic("Unable to patch FDT: %d", err);
 
+	err = serial_config(fdt_buf);
+	if (err)
+		panic("Unable to patch FDT: %d", err);
+
 	err = fdt_pack(fdt_buf);
 	if (err)
 		panic("Unable to pack FDT: %d\n", err);