Merge patch series "Add Turris 1.x board"

Marek Mojík <marek.mojik@nic.cz> says:

Hello all,

this is a continuation of previous work by Pali to add support for the
Turris 1.x board. As the patches were based on u-boot v2022.04, a
nontrivial rebasing was needed.

Some notes:
- Some options that are in SD defconfig are disabled in NOR defconfig
  because over the years u-boot grew and the old NOR defconfig will not
  fit into NOR memory.
- SD boot with RAM larger than 2GB will only allocate 2GB of RAM (We
  were not able to fix this yet)
diff --git a/board/CZ.NIC/turris_1x/Kconfig b/board/CZ.NIC/turris_1x/Kconfig
new file mode 100644
index 0000000..baea4d8
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/Kconfig
@@ -0,0 +1,155 @@
+# SPDX-License-Identifier: GPL-2.0+
+# (C) 2022 Pali Rohár <pali@kernel.org>
+
+if TARGET_TURRIS_1X
+
+# Board identification
+config SYS_BOARD
+	default "turris_1x"
+config SYS_VENDOR
+	default "CZ.NIC"
+config SYS_CONFIG_NAME
+	default "turris_1x"
+config DEFAULT_DEVICE_TREE
+	default "turris1x"
+
+# Board functions
+config ATSHA204A
+	default y
+config BOARD_EARLY_INIT_F
+	default y
+config BOARD_EARLY_INIT_R
+	default y
+config LAST_STAGE_INIT
+	default y
+config MISC
+	default y
+config OF_BOARD_FIXUP
+	default y
+config OF_BOARD_SETUP
+	default y
+
+# ENV
+config ENV_SIZE
+	default 0x2000
+config ENV_SECT_SIZE
+	default 0x20000
+config ENV_OVERWRITE
+	default y
+config ENV_IS_IN_FLASH
+	default y
+config ENV_ADDR
+	default 0xeff20000 # in NOR
+config SYS_RELOC_GD_ENV_ADDR
+	default y
+
+# DDR
+config DDR_CLK_FREQ
+	default 66666666
+config NR_DRAM_BANKS
+	default 5
+
+# UART
+config DEBUG_UART_BASE
+	default 0xffe04500 if DEBUG_UART
+config DEBUG_UART_CLOCK
+	default 37500000 if DEBUG_UART
+config SYS_NS16550
+	default y
+
+# I2C
+config I2C_SET_DEFAULT_BUS_NUM
+	default y
+config SYS_FSL_I2C_OFFSET
+	default 0x3000
+config SYS_FSL_HAS_I2C2_OFFSET
+	default y
+config SYS_FSL_I2C2_OFFSET
+	default 0x3100
+config SYS_I2C_FSL
+	default y
+
+# GPIO
+config MPC8XXX_GPIO
+	default y
+
+# WDT
+config WDT_MAX6370
+	default y
+
+# PCIe
+config PCI_INIT_R
+	default y
+config PCIE_FSL
+	default y
+
+# Ethernet
+config MII
+	default y
+config PHY_FIXED
+	default y
+config TSEC_ENET
+	default y
+
+# USB
+config USB_EHCI_FSL
+	default y
+config USB_XHCI_HCD
+	default y
+config USB_XHCI_PCI
+	default y
+
+# SDHC
+config FSL_ESDHC
+	default y
+config SYS_FSL_ESDHC_DEFAULT_BUS_WIDTH
+	default 4
+
+# NOR
+config MTD_NOR_FLASH
+	default y
+config CFI_FLASH
+	default y
+config FLASH_CFI_MTD
+	default y
+config SYS_FLASH_USE_BUFFER_WRITE
+	default y
+
+# NAND
+config MTD_RAW_NAND
+	default y
+config NAND_FSL_ELBC
+	default y
+config NAND_FSL_ELBC_DT
+	default y
+config BCH
+	default y
+config SYS_FLASH_CFI
+	default y
+config NAND_ECC_BCH
+	default y
+config SYS_LOAD_ADDR
+	default 0x1000000
+
+if SPL
+
+config SPL_ENV_SUPPORT
+	default y
+config SPL_FRAMEWORK
+	default n
+config SPL_I2C
+	default y
+config SPL_LIBCOMMON_SUPPORT
+	default y
+config SPL_LIBGENERIC_SUPPORT
+	default y
+config SPL_MPC8XXX_INIT_DDR
+	default y
+config SPL_SERIAL
+	default y
+config SPL_SYS_I2C_LEGACY
+	default y
+
+endif
+
+endif
diff --git a/board/CZ.NIC/turris_1x/MAINTAINERS b/board/CZ.NIC/turris_1x/MAINTAINERS
new file mode 100644
index 0000000..e858c97
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/MAINTAINERS
@@ -0,0 +1,13 @@
+TURRIS 1.X BOARD
+M:	Marek Mojík <marek.mojik@nic.cz>
+S:	Maintained
+F:	arch/powerpc/dts/turris1x-u-boot.dtsi
+F:	arch/powerpc/dts/turris1x.dts
+F:	board/CZ.NIC/turris_1x/
+F:	board/CZ.NIC/turris_atsha_otp.c
+F:	board/CZ.NIC/turris_atsha_otp.h
+F:	board/freescale/p1_p2_rdb_pc/p1_p2_rdb_pc.c
+F:	board/freescale/p1_p2_rdb_pc/spl.c
+F:	configs/turris_1x_nor_defconfig
+F:	configs/turris_1x_sdcard_defconfig
+F:	include/configs/turris_1x.h
diff --git a/board/CZ.NIC/turris_1x/Makefile b/board/CZ.NIC/turris_1x/Makefile
new file mode 100644
index 0000000..a24aee9
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0+
+# (C) 2022 Pali Rohár <pali@kernel.org>
+
+ifdef CONFIG_SPL_BUILD
+obj-y	+= ../../freescale/p1_p2_rdb_pc/spl.o
+endif
+
+obj-y	+= ../../freescale/p1_p2_rdb_pc/p1_p2_rdb_pc.o
+obj-y	+= ../turris_atsha_otp.o
+obj-y	+= ../turris_common.o
+
+obj-y	+= turris_1x.o
+obj-y	+= ddr.o
+obj-y	+= law.o
+obj-y	+= tlb.o
diff --git a/board/CZ.NIC/turris_1x/ddr.c b/board/CZ.NIC/turris_1x/ddr.c
new file mode 100644
index 0000000..27400f9
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/ddr.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <config.h>
+#include <linux/types.h>
+#include <fsl_ddr_sdram.h>
+#include <fsl_ddr_dimm_params.h>
+
+void fsl_ddr_board_options(memctl_options_t *popts, dimm_params_t *pdimm, unsigned int ctrl_num)
+{
+	int i;
+
+	popts->clk_adjust = 6;
+	popts->cpo_override = 0x1f;
+	popts->write_data_delay = 2;
+	popts->half_strength_driver_enable = 1;
+	popts->wrlvl_en = 1;
+	popts->wrlvl_override = 1;
+	popts->wrlvl_sample = 0xf;
+	popts->wrlvl_start = 0x8;
+	popts->trwt_override = 1;
+	popts->trwt = 0;
+
+	for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) {
+		popts->cs_local_opts[i].odt_rd_cfg = FSL_DDR_ODT_NEVER;
+		popts->cs_local_opts[i].odt_wr_cfg = FSL_DDR_ODT_CS;
+	}
+}
diff --git a/board/CZ.NIC/turris_1x/law.c b/board/CZ.NIC/turris_1x/law.c
new file mode 100644
index 0000000..51217cc
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/law.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <config.h>
+#include <asm/fsl_law.h>
+
+struct law_entry law_table[] = {
+	SET_LAW(CFG_SYS_FLASH_BASE_PHYS, LAW_SIZE_16M, LAW_TRGT_IF_LBC),
+	SET_LAW(CFG_SYS_NAND_BASE_PHYS, LAW_SIZE_256K, LAW_TRGT_IF_LBC),
+	SET_LAW(CFG_SYS_CPLD_BASE_PHYS, LAW_SIZE_128K, LAW_TRGT_IF_LBC),
+};
+
+int num_law_entries = ARRAY_SIZE(law_table);
diff --git a/board/CZ.NIC/turris_1x/tlb.c b/board/CZ.NIC/turris_1x/tlb.c
new file mode 100644
index 0000000..f35a555
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/tlb.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <config.h>
+#include <mpc85xx.h>
+#include <asm/mmu.h>
+#include <linux/sizes.h>
+#include <linux/build_bug.h>
+
+/*
+ * NOTE: e500v2 supports only following Book-E page sizes:
+ *
+ * TLB0:
+ * BOOKE_PAGESZ_4K
+ *
+ * TLB1:
+ * BOOKE_PAGESZ_4K
+ * BOOKE_PAGESZ_16K
+ * BOOKE_PAGESZ_64K
+ * BOOKE_PAGESZ_256K
+ * BOOKE_PAGESZ_1M
+ * BOOKE_PAGESZ_4M
+ * BOOKE_PAGESZ_16M
+ * BOOKE_PAGESZ_64M
+ * BOOKE_PAGESZ_256M
+ * BOOKE_PAGESZ_1G
+ * BOOKE_PAGESZ_4G
+ */
+
+struct fsl_e_tlb_entry tlb_table[] = {
+	/* TLB 0 */
+
+	/* ***** - Initial stack in L1 cache 16K */
+	SET_TLB_ENTRY(0, CFG_SYS_INIT_RAM_ADDR + 0 * SZ_4K,
+		      CFG_SYS_INIT_RAM_ADDR_PHYS + 0 * SZ_4K,
+		      MAS3_SX | MAS3_SW | MAS3_SR, 0,
+		      0, 0, BOOKE_PAGESZ_4K, 0),
+	SET_TLB_ENTRY(0, CFG_SYS_INIT_RAM_ADDR + 1 * SZ_4K,
+		      CFG_SYS_INIT_RAM_ADDR_PHYS + 1 * SZ_4K,
+		      MAS3_SX | MAS3_SW | MAS3_SR, 0,
+		      0, 0, BOOKE_PAGESZ_4K, 0),
+	SET_TLB_ENTRY(0, CFG_SYS_INIT_RAM_ADDR + 2 * SZ_4K,
+		      CFG_SYS_INIT_RAM_ADDR_PHYS + 2 * SZ_4K,
+		      MAS3_SX | MAS3_SW | MAS3_SR, 0,
+		      0, 0, BOOKE_PAGESZ_4K, 0),
+	SET_TLB_ENTRY(0, CFG_SYS_INIT_RAM_ADDR + 3 * SZ_4K,
+		      CFG_SYS_INIT_RAM_ADDR_PHYS + 3 * SZ_4K,
+		      MAS3_SX | MAS3_SW | MAS3_SR, 0,
+		      0, 0, BOOKE_PAGESZ_4K, 0),
+
+	/* TLB 1 */
+
+	/* *I*** - Boot page 4K */
+	SET_TLB_ENTRY(1, BPTR_VIRT_ADDR,
+		      0xfffff000,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I,
+		      0, 0, BOOKE_PAGESZ_4K, 1),
+
+	/* *I*G* - CCSR 1M */
+	SET_TLB_ENTRY(1, CFG_SYS_CCSRBAR,
+		      CFG_SYS_CCSRBAR_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 1, BOOKE_PAGESZ_1M, 1),
+
+	/* W**G* - NOR 16M */
+	/* This will be changed to *I*G* after relocation to RAM in board_early_init_r() */
+	SET_TLB_ENTRY(1, CFG_SYS_FLASH_BASE,
+		      CFG_SYS_FLASH_BASE_PHYS,
+		      MAS3_SX | MAS3_SR, MAS2_W | MAS2_G,
+		      0, 2, BOOKE_PAGESZ_16M, 1),
+
+	/* *I*G* - CPLD 256K (effective 128K) */
+	SET_TLB_ENTRY(1, CFG_SYS_CPLD_BASE,
+		      CFG_SYS_CPLD_BASE_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 3, BOOKE_PAGESZ_256K, 1),
+
+	/* *I*G* - NAND 256K */
+	SET_TLB_ENTRY(1, CFG_SYS_NAND_BASE,
+		      CFG_SYS_NAND_BASE_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 4, BOOKE_PAGESZ_256K, 1),
+
+	/* *I*G* - PCIe MEM (bus 1 and 2) 1G */
+	SET_TLB_ENTRY(1, CFG_SYS_PCIE1_MEM_VIRT,
+		      CFG_SYS_PCIE1_MEM_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 5, BOOKE_PAGESZ_1G, 1),
+
+	/* *I*G* - PCIe MEM (bus 3) 4M (effective 2M) */
+	SET_TLB_ENTRY(1, CFG_SYS_PCIE3_MEM_VIRT,
+		      CFG_SYS_PCIE3_MEM_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 6, BOOKE_PAGESZ_4M, 1),
+
+	/* *I*G* - PCIe I/O (all 3 buses) 256K (effective 192K) */
+	SET_TLB_ENTRY(1, CFG_SYS_PCIE1_IO_VIRT,
+		      CFG_SYS_PCIE1_IO_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_I | MAS2_G,
+		      0, 7, BOOKE_PAGESZ_256K, 1),
+
+#ifdef CFG_SYS_INIT_L2_ADDR
+	/* ***G* - Initial SRAM in L2 cache 512K */
+	SET_TLB_ENTRY(1, CFG_SYS_INIT_L2_ADDR,
+		      CFG_SYS_INIT_L2_ADDR_PHYS,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_G,
+		      0, 8, BOOKE_PAGESZ_256K, 1),
+	SET_TLB_ENTRY(1, CFG_SYS_INIT_L2_ADDR + SZ_256K,
+		      CFG_SYS_INIT_L2_ADDR_PHYS + SZ_256K,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_G,
+		      0, 9, BOOKE_PAGESZ_256K, 1),
+#endif
+
+#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)
+	/* **M** - SDRAM 2G */
+	SET_TLB_ENTRY(1, CFG_SYS_DDR_SDRAM_BASE,
+		      CFG_SYS_DDR_SDRAM_BASE,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M,
+		      0, 10, BOOKE_PAGESZ_1G, 1),
+	SET_TLB_ENTRY(1, CFG_SYS_DDR_SDRAM_BASE + SZ_1G,
+		      CFG_SYS_DDR_SDRAM_BASE + SZ_1G,
+		      MAS3_SX | MAS3_SW | MAS3_SR, MAS2_M,
+		      0, 11, BOOKE_PAGESZ_1G, 1),
+#endif
+};
+
+int num_tlb_entries = ARRAY_SIZE(tlb_table);
+
+/*
+ * PCIe MEM TLB entry expects that second PCIe MEM window is mapped after the
+ * first PCIe MEM window. Check for this requirement.
+ */
+static_assert(CFG_SYS_PCIE1_MEM_VIRT + SZ_512M == CFG_SYS_PCIE2_MEM_VIRT);
+static_assert(CFG_SYS_PCIE1_MEM_PHYS + SZ_512M == CFG_SYS_PCIE2_MEM_PHYS);
+
+/*
+ * PCIe I/O TLB entry expects that all 3 PCIe I/O windows are mapped one after
+ * another. Check for this requirement.
+ */
+static_assert(CFG_SYS_PCIE1_IO_VIRT + SZ_64K == CFG_SYS_PCIE2_IO_VIRT);
+static_assert(CFG_SYS_PCIE1_IO_PHYS + SZ_64K == CFG_SYS_PCIE2_IO_PHYS);
+static_assert(CFG_SYS_PCIE2_IO_VIRT + SZ_64K == CFG_SYS_PCIE3_IO_VIRT);
+static_assert(CFG_SYS_PCIE2_IO_PHYS + SZ_64K == CFG_SYS_PCIE3_IO_PHYS);
diff --git a/board/CZ.NIC/turris_1x/turris_1x.c b/board/CZ.NIC/turris_1x/turris_1x.c
new file mode 100644
index 0000000..7a0b68c
--- /dev/null
+++ b/board/CZ.NIC/turris_1x/turris_1x.c
@@ -0,0 +1,571 @@
+// SPDX-License-Identifier: GPL-2.0+
+// (C) 2022 Pali Rohár <pali@kernel.org>
+
+#include <init.h>
+#include <env.h>
+#include <fdt_support.h>
+#include <clock_legacy.h>
+#include <image.h>
+#include <asm/fsl_law.h>
+#include <asm/global_data.h>
+#include <asm/mmu.h>
+#include <dm/device.h>
+#include <dm/ofnode.h>
+#include <linux/build_bug.h>
+#include <display_options.h>
+
+#include "../turris_atsha_otp.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Reset time cycle register provided by Turris CPLD firmware.
+ * Turris CPLD firmware is open source and available at:
+ * https://gitlab.nic.cz/turris/hw/turris_cpld/-/blob/master/CZ_NIC_Router_CPLD.v
+ */
+#define TURRIS_CPLD_RESET_TIME_CYCLE_REG	((void *)CFG_SYS_CPLD_BASE + 0x1f)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_300MS	BIT(0)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_1S	BIT(1)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_2S	BIT(2)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_3S	BIT(3)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_4S	BIT(4)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_5S	BIT(5)
+#define  TURRIS_CPLD_RESET_TIME_CYCLE_6S	BIT(6)
+
+#define TURRIS_CPLD_LED_BRIGHTNESS_REG_FIRST	((void *)CFG_SYS_CPLD_BASE + 0x13)
+#define TURRIS_CPLD_LED_BRIGHTNESS_REG_LAST	((void *)CFG_SYS_CPLD_BASE + 0x1e)
+#define TURRIS_CPLD_LED_SW_OVERRIDE_REG		((void *)CFG_SYS_CPLD_BASE + 0x22)
+
+int dram_init_banksize(void)
+{
+	phys_size_t size = gd->ram_size;
+
+	static_assert(CONFIG_NR_DRAM_BANKS >= 3);
+
+	gd->bd->bi_dram[0].start = gd->ram_base;
+	gd->bd->bi_dram[0].size = get_effective_memsize();
+	size -= gd->bd->bi_dram[0].size;
+
+	/* Note: This address space is not mapped via TLB entries in U-Boot */
+
+#ifndef CONFIG_SDCARD
+	if (size > 0) {
+		/*
+		 * Setup additional overlapping 1 GB DDR LAW at the end of
+		 * 32-bit physical address space. It overlaps with all other
+		 * peripherals on P2020 mapped to physical address space.
+		 * But this is not issue because documentation says:
+		 * P2020 QorIQ Integrated Processor Reference Manual,
+		 * section 2.3.1 Precedence of local access windows:
+		 * If two local access windows overlap, the lower
+		 * numbered window takes precedence.
+		 */
+		if (set_ddr_laws(0xc0000000, SZ_1G, LAW_TRGT_IF_DDR_1) < 0) {
+			printf("Error: Cannot setup DDR LAW for more than 2 GB\n");
+			return 0;
+		}
+	}
+
+	if (size > 0) {
+		/* Free space between PCIe bus 3 MEM and NOR */
+		gd->bd->bi_dram[1].start = 0xc0200000;
+		gd->bd->bi_dram[1].size = min(size, 0xef000000 - gd->bd->bi_dram[1].start);
+		size -= gd->bd->bi_dram[1].size;
+	}
+
+	if (size > 0) {
+		/* Free space between NOR and NAND */
+		gd->bd->bi_dram[2].start = 0xf0000000;
+		gd->bd->bi_dram[2].size = min(size, 0xff800000 - gd->bd->bi_dram[2].start);
+		size -= gd->bd->bi_dram[2].size;
+	}
+#else
+	puts("\n\n!!! TODO: fix sdcard >2GB RAM\n\n\n");
+#endif
+	return 0;
+}
+
+static inline int fdt_setprop_inplace_u32_partial(void *blob, int node,
+						  const char *name,
+						  u32 idx, u32 val)
+{
+	val = cpu_to_fdt32(val);
+
+	return fdt_setprop_inplace_namelen_partial(blob, node, name,
+						   strlen(name),
+						   idx * sizeof(u32),
+						   &val, sizeof(u32));
+}
+
+/* Setup correct size of PCIe controller MEM in DT "ranges" property recursively */
+static void fdt_fixup_pcie_mem_size(void *blob, int node, phys_size_t pcie1_mem,
+				    phys_size_t pcie2_mem, phys_size_t pcie3_mem)
+{
+	int pci_cells, cpu_cells, size_cells;
+	const u32 *ranges;
+	int pnode;
+	int i, len;
+	u32 pci_flags;
+	u64 cpu_addr;
+	u64 size;
+	u64 new_size;
+	int pcie_id;
+	int idx;
+	int subnode;
+	int ret;
+
+	if (!fdtdec_get_is_enabled(blob, node))
+		return;
+
+	ranges = fdt_getprop(blob, node, "ranges", &len);
+	if (!ranges || !len || len % sizeof(u32))
+		return;
+
+	/*
+	 * The "ranges" property is an array of
+	 *   { <PCI address> <CPU address> <size in PCI address space> }
+	 * where number of PCI address cells and size cells is stored in the
+	 * "#address-cells" and "#size-cells" properties of the same node
+	 * containing the "ranges" property and number of CPU address cells
+	 * is stored in the parent's "#address-cells" property.
+	 *
+	 * All 3 elements can span a different number of cells. Fetch them.
+	 */
+	pnode = fdt_parent_offset(blob, node);
+	pci_cells = fdt_address_cells(blob, node);
+	cpu_cells = fdt_address_cells(blob, pnode);
+	size_cells = fdt_size_cells(blob, node);
+
+	/* PCI addresses always use 3 cells */
+	if (pci_cells != 3)
+		return;
+
+	/* CPU addresses and sizes on P2020 may be 32-bit (1 cell) or 64-bit (2 cells) */
+	if (cpu_cells != 1 && cpu_cells != 2)
+		return;
+	if (size_cells != 1 && size_cells != 2)
+		return;
+
+	for (i = 0; i < len / sizeof(u32); i += pci_cells + cpu_cells + size_cells) {
+		/* PCI address consists of 3 cells: flags, addr.hi, addr.lo */
+		pci_flags = fdt32_to_cpu(ranges[i]);
+
+		cpu_addr = fdt32_to_cpu(ranges[i + pci_cells]);
+		if (cpu_cells == 2) {
+			cpu_addr <<= 32;
+			cpu_addr |= fdt32_to_cpu(ranges[i + pci_cells + 1]);
+		}
+
+		size = fdt32_to_cpu(ranges[i + pci_cells + cpu_cells]);
+		if (size_cells == 2) {
+			size <<= 32;
+			size |= fdt32_to_cpu(ranges[i + pci_cells + cpu_cells + 1]);
+		}
+
+		/*
+		 * Bits [25:24] of PCI flags defines space code
+		 * 0b10 is 32-bit MEM and 0b11 is 64-bit MEM.
+		 * Check for any type of PCIe MEM mapping.
+		 */
+		if (!(pci_flags & 0x02000000))
+			continue;
+
+		if (cpu_addr == CFG_SYS_PCIE1_MEM_PHYS && size > pcie1_mem) {
+			pcie_id = 1;
+			new_size = pcie1_mem;
+		} else if (cpu_addr == CFG_SYS_PCIE2_MEM_PHYS && size > pcie2_mem) {
+			pcie_id = 2;
+			new_size = pcie2_mem;
+		} else if (cpu_addr == CFG_SYS_PCIE3_MEM_PHYS && size > pcie3_mem) {
+			pcie_id = 3;
+			new_size = pcie3_mem;
+		} else {
+			continue;
+		}
+
+		printf("Decreasing PCIe MEM %d size from ", pcie_id);
+		print_size(size, " to ");
+		print_size(new_size, "\n");
+		idx = i + pci_cells + cpu_cells;
+		if (size_cells == 2) {
+			ret = fdt_setprop_inplace_u32_partial(blob, node,
+							      "ranges", idx, 0);
+			if (ret)
+				goto err;
+			idx++;
+		}
+		ret = fdt_setprop_inplace_u32_partial(blob, node,
+						      "ranges", idx, SZ_2M);
+		if (ret)
+			goto err;
+	}
+
+	/* Recursively fix also all subnodes */
+	fdt_for_each_subnode(subnode, blob, node)
+		fdt_fixup_pcie_mem_size(blob, subnode, pcie1_mem, pcie2_mem, pcie3_mem);
+
+	return;
+
+err:
+	printf("Error: Cannot update \"ranges\" property\n");
+}
+
+static inline phys_size_t get_law_size(phys_addr_t addr, enum law_trgt_if id)
+{
+	struct law_entry e;
+
+	e = find_law_by_addr_id(addr, id);
+	if (e.index < 0)
+		return 0;
+
+	return 2ULL << e.size;
+}
+
+void ft_memory_setup(void *blob, struct bd_info *bd)
+{
+	phys_size_t pcie1_mem, pcie2_mem, pcie3_mem;
+	u64 start[CONFIG_NR_DRAM_BANKS];
+	u64 size[CONFIG_NR_DRAM_BANKS];
+	int count;
+	int node;
+
+	if (!env_get("bootm_low") && !env_get("bootm_size")) {
+		for (count = 0; count < CONFIG_NR_DRAM_BANKS; count++) {
+			start[count] = gd->bd->bi_dram[count].start;
+			size[count] = gd->bd->bi_dram[count].size;
+			if (!size[count])
+				break;
+		}
+		fdt_fixup_memory_banks(blob, start, size, count);
+	} else {
+		fdt_fixup_memory(blob, env_get_bootm_low(), env_get_bootm_size());
+	}
+
+	pcie1_mem = get_law_size(CFG_SYS_PCIE1_MEM_PHYS, LAW_TRGT_IF_PCIE_1);
+	pcie2_mem = get_law_size(CFG_SYS_PCIE2_MEM_PHYS, LAW_TRGT_IF_PCIE_2);
+	pcie3_mem = get_law_size(CFG_SYS_PCIE3_MEM_PHYS, LAW_TRGT_IF_PCIE_3);
+
+	fdt_for_each_node_by_compatible(node, blob, -1, "fsl,mpc8548-pcie")
+		fdt_fixup_pcie_mem_size(blob, node, pcie1_mem, pcie2_mem, pcie3_mem);
+}
+
+static int detect_model_serial(const char **model, char serial[17])
+{
+	u32 version_num;
+	int err;
+
+	err = turris_atsha_otp_get_serial_number(serial);
+	if (err) {
+		*model = "Turris 1.x";
+		strcpy(serial, "unknown");
+		return -1;
+	}
+
+	version_num = simple_strtoull(serial, NULL, 16) >> 32;
+
+	/*
+	 * Turris 1.0 boards (RTRS01) have version_num 0x5.
+	 * Turris 1.1 boards (RTRS02) have version_num 0x6, 0x7, 0x8 and 0x9.
+	 */
+	if (be32_to_cpu(version_num) >= 0x6) {
+		*model = "Turris 1.1 (RTRS02)";
+		return 1;
+	}
+
+	*model = "Turris 1.0 (RTRS01)";
+	return 0;
+}
+
+void p1_p2_rdb_pc_fix_fdt_model(void *blob)
+{
+	const char *model;
+	char serial[17];
+	int len;
+	int off;
+	int rev;
+	char c;
+
+	rev = detect_model_serial(&model, serial);
+	if (rev < 0)
+		return;
+
+	/* Turris 1.0 boards (RTRS01) do not have third PCIe controller */
+	if (rev == 0) {
+		off = fdt_path_offset(blob, "pci2");
+		if (off >= 0)
+			fdt_del_node(blob, off);
+	}
+
+	/* Fix model string only in case it is generic "Turris 1.x" */
+	model = fdt_getprop(blob, 0, "model", &len);
+	if (len < sizeof("Turris 1.x") - 1)
+		return;
+	if (memcmp(model, "Turris 1.x", sizeof("Turris 1.x") - 1) != 0)
+		return;
+
+	c = '0' + rev;
+	fdt_setprop_inplace_namelen_partial(blob, 0, "model", sizeof("model") - 1,
+					    sizeof("Turris 1.") - 1, &c, 1);
+}
+
+int misc_init_r(void)
+{
+	turris_atsha_otp_init_mac_addresses(0);
+	turris_atsha_otp_init_serial_number();
+	return 0;
+}
+
+/* This comes from ../../freescale/p1_p2_rdb_pc/p1_p2_rdb_pc.c */
+extern int checkboard_p1_p2(void);
+
+int checkboard(void)
+{
+	const char *model;
+	char serial[17];
+	void *reg;
+
+	/* Disable software control of all Turris LEDs */
+	out_8(TURRIS_CPLD_LED_SW_OVERRIDE_REG, 0x00);
+
+	/* Reset colors of all Turris LEDs to their default values */
+	for (reg = TURRIS_CPLD_LED_BRIGHTNESS_REG_FIRST;
+	     reg <= TURRIS_CPLD_LED_BRIGHTNESS_REG_LAST;
+	     reg++)
+		out_8(reg, 0xff);
+
+	detect_model_serial(&model, serial);
+	printf("Revision: %s\n", model);
+	printf("Serial Number: %s\n", serial);
+
+	return checkboard_p1_p2();
+}
+
+static void handle_reset_button(void)
+{
+	const char * const vars[1] = { "bootcmd_rescue", };
+	u8 reset_time_raw, reset_time;
+
+	/*
+	 * Ensure that bootcmd_rescue has always stock value, so that running
+	 *   run bootcmd_rescue
+	 * always works correctly.
+	 */
+	env_set_default_vars(1, (char * const *)vars, 0);
+
+	reset_time_raw = in_8(TURRIS_CPLD_RESET_TIME_CYCLE_REG);
+	if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_6S)
+		reset_time = 6;
+	else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_5S)
+		reset_time = 5;
+	else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_4S)
+		reset_time = 4;
+	else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_3S)
+		reset_time = 3;
+	else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_2S)
+		reset_time = 2;
+	else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_1S)
+		reset_time = 1;
+	else
+		reset_time = 0;
+
+	env_set_ulong("turris_reset", reset_time);
+
+	/* Check if red reset button was hold for at least six seconds. */
+	if (reset_time >= 6) {
+		const char * const vars[3] = {
+			"bootcmd",
+			"bootdelay",
+			"distro_bootcmd",
+		};
+
+		/*
+		 * Set the above envs to their default values, in case the user
+		 * managed to break them.
+		 */
+		env_set_default_vars(3, (char * const *)vars, 0);
+
+		/* Ensure bootcmd_rescue is used by distroboot */
+		env_set("boot_targets", "rescue");
+
+		printf("RESET button was hold for >= 6s, overwriting boot_targets for system rescue!\n");
+	} else {
+		/*
+		 * In case the user somehow managed to save environment with
+		 * boot_targets=rescue, reset boot_targets to default value.
+		 * This could happen in subsequent commands if bootcmd_rescue
+		 * failed.
+		 */
+		if (!strcmp(env_get("boot_targets"), "rescue")) {
+			const char * const vars[1] = {
+				"boot_targets",
+			};
+
+			env_set_default_vars(1, (char * const *)vars, 0);
+		}
+
+		if (reset_time > 0)
+			printf("RESET button was hold for %us.\n", reset_time);
+	}
+}
+
+static int recalculate_pcie_mem_law(phys_addr_t addr,
+				    pci_size_t pcie_size,
+				    enum law_trgt_if id,
+				    phys_addr_t *free_start,
+				    phys_size_t *free_size)
+{
+	phys_size_t cur_size, new_size;
+	struct law_entry e;
+
+	e = find_law_by_addr_id(addr, id);
+	if (e.index < 0) {
+		*free_start = *free_size = 0;
+		return 0;
+	}
+
+	cur_size = 2ULL << e.size;
+	new_size = roundup_pow_of_two(pcie_size);
+
+	if (new_size >= cur_size) {
+		*free_start = *free_size = 0;
+		return 0;
+	}
+
+	set_law(e.index, addr, law_size_bits(new_size), id);
+
+	*free_start = addr + new_size;
+	*free_size = cur_size - new_size;
+	return 1;
+}
+
+static void recalculate_used_pcie_mem(void)
+{
+	phys_addr_t free_start1, free_start2;
+	phys_size_t free_size1, free_size2;
+	pci_size_t pcie1_used_mem_size;
+	pci_size_t pcie2_used_mem_size;
+	struct law_entry e;
+	phys_size_t size;
+	ofnode node;
+	int i;
+
+	size = gd->ram_size;
+
+	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
+		size -= gd->bd->bi_dram[i].size;
+
+	if (size == 0)
+		return;
+
+	e = find_law_by_addr_id(CFG_SYS_PCIE3_MEM_PHYS, LAW_TRGT_IF_PCIE_3);
+	if (e.index < 0 && gd->bd->bi_dram[1].size > 0) {
+		/*
+		 * If there is no LAW for PCIe 3 MEM then 3rd PCIe controller
+		 * is inactive, which is the case for Turris 1.0 boards. So
+		 * use its reserved 2 MB physical space for DDR RAM.
+		 */
+		unsigned int bank_size = SZ_2M;
+
+		if (bank_size > size)
+			bank_size = size;
+		printf("Reserving unused ");
+		print_size(bank_size, "");
+		printf(" of PCIe 3 MEM for DDR RAM\n");
+		gd->bd->bi_dram[1].start -= bank_size;
+		gd->bd->bi_dram[1].size += bank_size;
+		size -= bank_size;
+		if (size == 0)
+			return;
+	}
+
+#ifdef CONFIG_PCI_PNP
+	/*
+	 * Detect how much space of PCIe MEM is needed for both PCIe 1 and
+	 * PCIe 2 controllers with all connected cards on whole hierarchy.
+	 * This works only when U-Boot has enabled PCI PNP code which scans
+	 * all PCI devices and calculate required memory for every PCI BAR of
+	 * every PCI device.
+	 */
+	ofnode_for_each_compatible_node(node, "fsl,mpc8548-pcie") {
+		struct udevice *dev;
+
+		if (device_find_global_by_ofnode(node, &dev))
+			continue;
+
+		struct pci_controller *hose = dev_get_uclass_priv(pci_get_controller(dev));
+
+		if (!hose)
+			continue;
+		if (!hose->pci_mem)
+			continue;
+		if (!hose->pci_mem->size)
+			continue;
+
+		pci_size_t used_mem_size = hose->pci_mem->bus_lower - hose->pci_mem->bus_start;
+
+		if (hose->pci_mem->phys_start == CFG_SYS_PCIE1_MEM_PHYS)
+			pcie1_used_mem_size = used_mem_size;
+		else if (hose->pci_mem->phys_start == CFG_SYS_PCIE2_MEM_PHYS)
+			pcie2_used_mem_size = used_mem_size;
+	}
+
+	if (pcie1_used_mem_size == 0 && pcie2_used_mem_size == 0)
+		return;
+
+	e = find_law_by_addr_id(0xc0000000, LAW_TRGT_IF_DDR_1);
+	if (e.index < 0) {
+		printf("Error: Cannot setup DDR LAW for more than 3 GB of RAM\n");
+		return;
+	}
+
+	/*
+	 * Increase additional overlapping 1 GB DDR LAW from 1GB to 2GB by
+	 * moving its left side from 0xc0000000 to 0x80000000. After this
+	 * change it would overlap with PCIe MEM 1 and 2 LAWs.
+	 */
+	set_law(e.index, 0x80000000, LAW_SIZE_2G, LAW_TRGT_IF_DDR_1);
+
+	i = 3;
+	static_assert(CONFIG_NR_DRAM_BANKS >= 3 + 2);
+
+	if (recalculate_pcie_mem_law(CFG_SYS_PCIE2_MEM_PHYS,
+				     pcie2_used_mem_size, LAW_TRGT_IF_PCIE_2,
+				     &free_start2, &free_size2)) {
+		printf("Reserving unused ");
+		print_size(free_size2, "");
+		printf(" of PCIe 2 MEM for DDR RAM\n");
+		gd->bd->bi_dram[i].start = free_start2;
+		gd->bd->bi_dram[i].size = min(size, free_size2);
+		size -= gd->bd->bi_dram[i].start;
+		i++;
+		if (size == 0)
+			return;
+	}
+
+	if (recalculate_pcie_mem_law(CFG_SYS_PCIE1_MEM_PHYS,
+				     pcie1_used_mem_size, LAW_TRGT_IF_PCIE_1,
+				     &free_start1, &free_size1)) {
+		printf("Reserving unused ");
+		print_size(free_size1, "");
+		printf(" of PCIe 1 MEM for DDR RAM\n");
+		gd->bd->bi_dram[i].start = free_start1;
+		gd->bd->bi_dram[i].size = min(size, free_size1);
+		size -= gd->bd->bi_dram[i].size;
+		i++;
+		if (size == 0)
+			return;
+	}
+#endif
+}
+
+int last_stage_init(void)
+{
+	handle_reset_button();
+	recalculate_used_pcie_mem();
+	return 0;
+}
+
+int get_serial_clock(void)
+{
+	return get_bus_freq(0);
+}