From 7938b3be7cedcfe54e891c86e4297b0dccde0f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 18 Feb 2022 12:24:13 +0100 Subject: [PATCH 01/18] tools: kwboot: Fix quitting terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes kwboot after quitting terminal prints error message: terminal: Bad address This is caused by trying to call write() syscall with count of (size_t)-1 bytes. When quit sequence is split into more read() calls then number of input bytes (nin) at the end of cycle can underflow and be negative. Fix it. Fixes: de7514046ea5 ("tools: kwboot: Fix detection of quit esc sequence") Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese --- tools/kwboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 68c0ef1f1b0..2d2d545d825 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -1197,7 +1197,7 @@ kwboot_term_pipe(int in, int out, const char *quit, int *s) if (buf[i] == quit[*s]) { (*s)++; if (!quit[*s]) { - nin = i - *s; + nin = (i > *s) ? (i - *s) : 0; break; } } else { @@ -1208,7 +1208,7 @@ kwboot_term_pipe(int in, int out, const char *quit, int *s) } if (i == nin) - nin -= *s; + nin -= (nin > *s) ? *s : nin; } if (kwboot_write(out, buf, nin) < 0) From 68285176a9552ba264878cdc6c7daccb5806b569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 18 Feb 2022 12:25:22 +0100 Subject: [PATCH 02/18] pci: pci_mvebu: Remove unused SELECT and lane_mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Macro SELECT() is unused and struct mvebu_pcie field lane_mask is unused too. Remove them. Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Reviewed-by: Stefan Roese --- drivers/pci/pci_mvebu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 5a0a59a8b9e..16fe54fd366 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -30,8 +30,6 @@ #include /* PCIe unit register offsets */ -#define SELECT(x, n) ((x >> n) & 1UL) - #define PCIE_DEV_ID_OFF 0x0000 #define PCIE_CMD_OFF 0x0004 #define PCIE_DEV_REV_OFF 0x0008 @@ -77,7 +75,6 @@ struct mvebu_pcie { u32 lane; bool is_x4; int devfn; - u32 lane_mask; int sec_busno; char name[16]; unsigned int mem_target; From fc27e5df637dd4c954b67dd1415b9af144718a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 18 Feb 2022 12:25:23 +0100 Subject: [PATCH 03/18] pci: pci_mvebu: Cleanup macro names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use "MVPCIE_" prefix instead of generic "PCIE_" prefix for pci_mvebu.c specific macros. Define offset macros for Root Port registers and use standard register macros from pci.h when accessing Root Port registers. Signed-off-by: Pali Rohár Reviewed-by: Marek Behún Reviewed-by: Stefan Roese --- drivers/pci/pci_mvebu.c | 130 +++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 70 deletions(-) diff --git a/drivers/pci/pci_mvebu.c b/drivers/pci/pci_mvebu.c index 16fe54fd366..f07669374d7 100644 --- a/drivers/pci/pci_mvebu.c +++ b/drivers/pci/pci_mvebu.c @@ -30,35 +30,25 @@ #include /* PCIe unit register offsets */ -#define PCIE_DEV_ID_OFF 0x0000 -#define PCIE_CMD_OFF 0x0004 -#define PCIE_DEV_REV_OFF 0x0008 -#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) -#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) -#define PCIE_EXP_ROM_BAR_OFF 0x0030 -#define PCIE_CAPAB_OFF 0x0060 -#define PCIE_CTRL_STAT_OFF 0x0068 -#define PCIE_HEADER_LOG_4_OFF 0x0128 -#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) -#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) -#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) -#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) -#define PCIE_WIN5_CTRL_OFF 0x1880 -#define PCIE_WIN5_BASE_OFF 0x1884 -#define PCIE_WIN5_REMAP_OFF 0x188c -#define PCIE_CONF_ADDR_OFF 0x18f8 -#define PCIE_CONF_DATA_OFF 0x18fc -#define PCIE_MASK_OFF 0x1910 -#define PCIE_MASK_ENABLE_INTS (0xf << 24) -#define PCIE_CTRL_OFF 0x1a00 -#define PCIE_CTRL_X1_MODE BIT(0) -#define PCIE_CTRL_RC_MODE BIT(1) -#define PCIE_STAT_OFF 0x1a04 -#define PCIE_STAT_BUS (0xff << 8) -#define PCIE_STAT_DEV (0x1f << 16) -#define PCIE_STAT_LINK_DOWN BIT(0) -#define PCIE_DEBUG_CTRL 0x1a60 -#define PCIE_DEBUG_SOFT_RESET BIT(20) +#define MVPCIE_ROOT_PORT_PCI_CFG_OFF 0x0000 +#define MVPCIE_ROOT_PORT_PCI_EXP_OFF 0x0060 +#define MVPCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) +#define MVPCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) +#define MVPCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) +#define MVPCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) +#define MVPCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4)) +#define MVPCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4)) +#define MVPCIE_WIN5_CTRL_OFF 0x1880 +#define MVPCIE_WIN5_BASE_OFF 0x1884 +#define MVPCIE_WIN5_REMAP_OFF 0x188c +#define MVPCIE_CONF_ADDR_OFF 0x18f8 +#define MVPCIE_CONF_DATA_OFF 0x18fc +#define MVPCIE_CTRL_OFF 0x1a00 +#define MVPCIE_CTRL_RC_MODE BIT(1) +#define MVPCIE_STAT_OFF 0x1a04 +#define MVPCIE_STAT_BUS (0xff << 8) +#define MVPCIE_STAT_DEV (0x1f << 16) +#define MVPCIE_STAT_LINK_DOWN BIT(0) #define LINK_WAIT_RETRIES 100 #define LINK_WAIT_TIMEOUT 1000 @@ -87,8 +77,8 @@ struct mvebu_pcie { static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie) { u32 val; - val = readl(pcie->base + PCIE_STAT_OFF); - return !(val & PCIE_STAT_LINK_DOWN); + val = readl(pcie->base + MVPCIE_STAT_OFF); + return !(val & MVPCIE_STAT_LINK_DOWN); } static void mvebu_pcie_wait_for_link(struct mvebu_pcie *pcie) @@ -112,20 +102,20 @@ static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno) { u32 stat; - stat = readl(pcie->base + PCIE_STAT_OFF); - stat &= ~PCIE_STAT_BUS; + stat = readl(pcie->base + MVPCIE_STAT_OFF); + stat &= ~MVPCIE_STAT_BUS; stat |= busno << 8; - writel(stat, pcie->base + PCIE_STAT_OFF); + writel(stat, pcie->base + MVPCIE_STAT_OFF); } static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie *pcie, int devno) { u32 stat; - stat = readl(pcie->base + PCIE_STAT_OFF); - stat &= ~PCIE_STAT_DEV; + stat = readl(pcie->base + MVPCIE_STAT_OFF); + stat &= ~MVPCIE_STAT_DEV; stat |= devno << 16; - writel(stat, pcie->base + PCIE_STAT_OFF); + writel(stat, pcie->base + MVPCIE_STAT_OFF); } static inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose) @@ -195,18 +185,18 @@ static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); /* write address */ - writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF); /* read data */ switch (size) { case PCI_SIZE_8: - data = readb(pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + data = readb(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3)); break; case PCI_SIZE_16: - data = readw(pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + data = readw(pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2)); break; case PCI_SIZE_32: - data = readl(pcie->base + PCIE_CONF_DATA_OFF); + data = readl(pcie->base + MVPCIE_CONF_DATA_OFF); break; default: return -EINVAL; @@ -286,18 +276,18 @@ static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf, addr = PCI_CONF1_EXT_ADDRESS(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset); /* write address */ - writel(addr, pcie->base + PCIE_CONF_ADDR_OFF); + writel(addr, pcie->base + MVPCIE_CONF_ADDR_OFF); /* write data */ switch (size) { case PCI_SIZE_8: - writeb(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 3)); + writeb(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 3)); break; case PCI_SIZE_16: - writew(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 2)); + writew(value, pcie->base + MVPCIE_CONF_DATA_OFF + (offset & 2)); break; case PCI_SIZE_32: - writel(value, pcie->base + PCIE_CONF_DATA_OFF); + writel(value, pcie->base + MVPCIE_CONF_DATA_OFF); break; default: return -EINVAL; @@ -321,20 +311,20 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) /* First, disable and clear BARs and windows. */ for (i = 1; i < 3; i++) { - writel(0, pcie->base + PCIE_BAR_CTRL_OFF(i)); - writel(0, pcie->base + PCIE_BAR_LO_OFF(i)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_CTRL_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_LO_OFF(i)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(i)); } for (i = 0; i < 5; i++) { - writel(0, pcie->base + PCIE_WIN04_CTRL_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_CTRL_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_BASE_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i)); } - writel(0, pcie->base + PCIE_WIN5_CTRL_OFF); - writel(0, pcie->base + PCIE_WIN5_BASE_OFF); - writel(0, pcie->base + PCIE_WIN5_REMAP_OFF); + writel(0, pcie->base + MVPCIE_WIN5_CTRL_OFF); + writel(0, pcie->base + MVPCIE_WIN5_BASE_OFF); + writel(0, pcie->base + MVPCIE_WIN5_REMAP_OFF); /* Setup windows for DDR banks. Count total DDR size on the fly. */ size = 0; @@ -342,12 +332,12 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) const struct mbus_dram_window *cs = dram->cs + i; writel(cs->base & 0xffff0000, - pcie->base + PCIE_WIN04_BASE_OFF(i)); - writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i)); + pcie->base + MVPCIE_WIN04_BASE_OFF(i)); + writel(0, pcie->base + MVPCIE_WIN04_REMAP_OFF(i)); writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | (dram->mbus_dram_target_id << 4) | 1, - pcie->base + PCIE_WIN04_CTRL_OFF(i)); + pcie->base + MVPCIE_WIN04_CTRL_OFF(i)); size += cs->size; } @@ -357,14 +347,14 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie) size = 1 << fls(size); /* Setup BAR[1] to all DRAM banks. */ - writel(dram->cs[0].base | 0xc, pcie->base + PCIE_BAR_LO_OFF(1)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(1)); + writel(dram->cs[0].base | 0xc, pcie->base + MVPCIE_BAR_LO_OFF(1)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(1)); writel(((size - 1) & 0xffff0000) | 0x1, - pcie->base + PCIE_BAR_CTRL_OFF(1)); + pcie->base + MVPCIE_BAR_CTRL_OFF(1)); /* Setup BAR[0] to internal registers. */ - writel(pcie->intregs, pcie->base + PCIE_BAR_LO_OFF(0)); - writel(0, pcie->base + PCIE_BAR_HI_OFF(0)); + writel(pcie->intregs, pcie->base + MVPCIE_BAR_LO_OFF(0)); + writel(0, pcie->base + MVPCIE_BAR_HI_OFF(0)); } /* Only enable PCIe link, do not setup it */ @@ -403,9 +393,9 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) u32 reg; /* Setup PCIe controller to Root Complex mode */ - reg = readl(pcie->base + PCIE_CTRL_OFF); - reg |= PCIE_CTRL_RC_MODE; - writel(reg, pcie->base + PCIE_CTRL_OFF); + reg = readl(pcie->base + MVPCIE_CTRL_OFF); + reg |= MVPCIE_CTRL_RC_MODE; + writel(reg, pcie->base + MVPCIE_CTRL_OFF); /* * Set Maximum Link Width to X1 or X4 in Root Port's PCIe Link @@ -414,10 +404,10 @@ static void mvebu_pcie_setup_link(struct mvebu_pcie *pcie) * be set to number of SerDes PCIe lanes (1 or 4). If this register is * not set correctly then link with endpoint card is not established. */ - reg = readl(pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP); reg &= ~PCI_EXP_LNKCAP_MLW; reg |= (pcie->is_x4 ? 4 : 1) << 4; - writel(reg, pcie->base + PCIE_CAPAB_OFF + PCI_EXP_LNKCAP); + writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_EXP_OFF + PCI_EXP_LNKCAP); } static int mvebu_pcie_probe(struct udevice *dev) @@ -440,7 +430,7 @@ static int mvebu_pcie_probe(struct udevice *dev) * have the same format in Marvell's specification as in PCIe * specification, but their meaning is totally different and they do * different things: they are aliased into internal mvebu registers - * (e.g. PCIE_BAR_LO_OFF) and these should not be changed or + * (e.g. MVPCIE_BAR_LO_OFF) and these should not be changed or * reconfigured by pci device drivers. * * So our driver converts Type 0 config space to Type 1 and reports @@ -448,10 +438,10 @@ static int mvebu_pcie_probe(struct udevice *dev) * Type 1 registers is redirected to the virtual cfgcache[] buffer, * which avoids changing unrelated registers. */ - reg = readl(pcie->base + PCIE_DEV_REV_OFF); + reg = readl(pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION); reg &= ~0xffffff00; reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; - writel(reg, pcie->base + PCIE_DEV_REV_OFF); + writel(reg, pcie->base + MVPCIE_ROOT_PORT_PCI_CFG_OFF + PCI_CLASS_REVISION); /* * mvebu uses local bus number and local device number to determinate From 7f59ed68728e732062f4fb0c1e8940f5bf550ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Fri, 18 Feb 2022 17:46:25 +0100 Subject: [PATCH 04/18] arm: mvebu: turris_omnia: Enable ext4 write support in defconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable ext4 write support in Turris Omnia's defconfig. Some users find it useful. Signed-off-by: Marek Behún Reviewed-by: Stefan Roese --- configs/turris_omnia_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/configs/turris_omnia_defconfig b/configs/turris_omnia_defconfig index 280dd55f001..5b1fdbfb2d3 100644 --- a/configs/turris_omnia_defconfig +++ b/configs/turris_omnia_defconfig @@ -93,3 +93,4 @@ CONFIG_USB_XHCI_HCD=y CONFIG_USB_EHCI_HCD=y CONFIG_WDT=y CONFIG_WDT_ORION=y +CONFIG_EXT4_WRITE=y From 1fd54253bca7d43d046bba4853fe5fafd034bc17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 23 Feb 2022 13:52:32 +0100 Subject: [PATCH 05/18] arm: a37xx: pci: Fix a3700_fdt_fix_pcie_regions() function again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The a3700_fdt_fix_pcie_regions() function still computes nonsense. It computes the fixup offset from the PCI address taken from the first row of the "ranges" array, which means that: - PCI address must equal CPU address (otherwise the computed fix offset will be wrong), - the first row must contain the lowest address. This is the case for the default device-tree, which is why we didn't notice it. It also adds the fixup offset to all PCI and CPU addresses, which is wrong. Instead: 1) The fixup offset must be computed from the CPU address, not PCI address. 2) The fixup offset must be computed from the row containing the lowest CPU address, which is not necessarily contained in the first row. 3) The PCI address - the address to which the PCIe controller remaps the address space as seen from the point of view of the PCIe device - must be fixed by the fix offset in the same way as the CPU address only in the special case when the CPU adn PCI addresses are the same. Same addresses means that remapping is disabled, and thus if we change the CPU address, we need also to change the PCI address so that the remapping is still disabled afterwards. Consider an example: The ranges entries contain: PCI address CPU address 70000000 EA000000 E9000000 E9000000 EB000000 EB000000 By default CPU PCIe window is at: E8000000 - F0000000 Consider the case when TF-A moves it to: F2000000 - FA000000 Until now the function would take the PCI address of the first entry: 70000000, and the new base, F2000000, to compute the fix offset: F2000000 - 70000000 = 82000000, and then add 8200000 to all addresses, resulting in PCI address CPU address F2000000 6C000000 6B000000 6B000000 6D000000 6D000000 which is complete nonsense - none of the CPU addresses is in the requested window. Now it will take the lowest CPU address, which is in second row, E9000000, and compute the fix offset F2000000 - E9000000 = 09000000, and then add it to all CPU addresses and those PCI addresses which equal to their corresponding CPU addresses, resulting in PCI address CPU address 70000000 F3000000 F2000000 F2000000 F4000000 F4000000 where all of the CPU addresses are in the needed window. Fixes: 4a82fca8e330 ("arm: a37xx: pci: Fix a3700_fdt_fix_pcie_regions() function") Signed-off-by: Pali Rohár Signed-off-by: Marek Behún Reviewed-by: Stefan Roese --- arch/arm/mach-mvebu/armada3700/cpu.c | 81 +++++++++++++++++++--------- 1 file changed, 55 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-mvebu/armada3700/cpu.c b/arch/arm/mach-mvebu/armada3700/cpu.c index 23492f49dae..52b5109b735 100644 --- a/arch/arm/mach-mvebu/armada3700/cpu.c +++ b/arch/arm/mach-mvebu/armada3700/cpu.c @@ -316,8 +316,8 @@ static int fdt_setprop_inplace_u32_partial(void *blob, int node, int a3700_fdt_fix_pcie_regions(void *blob) { - int acells, pacells, scells; - u32 base, fix_offset; + u32 base, lowest_cpu_addr, fix_offset; + int pci_cells, cpu_cells, size_cells; const u32 *ranges; int node, pnode; int ret, i, len; @@ -331,51 +331,80 @@ int a3700_fdt_fix_pcie_regions(void *blob) return node; ranges = fdt_getprop(blob, node, "ranges", &len); - if (!ranges || len % sizeof(u32)) - return -ENOENT; + if (!ranges || !len || len % sizeof(u32)) + return -EINVAL; /* * The "ranges" property is an array of - * { } + * { } + * 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 diffent number of cells. Fetch their sizes. + * All 3 elements can span a diffent number of cells. Fetch them. */ pnode = fdt_parent_offset(blob, node); - acells = fdt_address_cells(blob, node); - pacells = fdt_address_cells(blob, pnode); - scells = fdt_size_cells(blob, node); + pci_cells = fdt_address_cells(blob, node); + cpu_cells = fdt_address_cells(blob, pnode); + size_cells = fdt_size_cells(blob, node); - /* Child PCI addresses always use 3 cells */ - if (acells != 3) - return -ENOENT; + /* PCI addresses always use 3 cells */ + if (pci_cells != 3) + return -EINVAL; - /* Calculate fixup offset from first child address (in last cell) */ - fix_offset = base - fdt32_to_cpu(ranges[2]); + /* CPU addresses on Armada 37xx always use 2 cells */ + if (cpu_cells != 2) + return -EINVAL; - /* If fixup offset is zero then there is nothing to fix */ + for (i = 0; i < len / sizeof(u32); + i += pci_cells + cpu_cells + size_cells) { + /* + * Parent CPU addresses on Armada 37xx are always 32-bit, so + * check that the high word is zero. + */ + if (fdt32_to_cpu(ranges[i + pci_cells])) + return -EINVAL; + + if (i == 0 || + fdt32_to_cpu(ranges[i + pci_cells + 1]) < lowest_cpu_addr) + lowest_cpu_addr = fdt32_to_cpu(ranges[i + pci_cells + 1]); + } + + /* Calculate fixup offset from the lowest (first) CPU address */ + fix_offset = base - lowest_cpu_addr; + + /* If fixup offset is zero there is nothing to fix */ if (!fix_offset) return 0; /* - * Fix address (last cell) of each child address and each parent - * address + * Fix each CPU address and corresponding PCI address if PCI address + * is not already remapped (has the same value) */ - for (i = 0; i < len / sizeof(u32); i += acells + pacells + scells) { + for (i = 0; i < len / sizeof(u32); + i += pci_cells + cpu_cells + size_cells) { + u32 cpu_addr; + u64 pci_addr; int idx; - /* fix child address */ - idx = i + acells - 1; + /* Fix CPU address */ + idx = i + pci_cells + cpu_cells - 1; + cpu_addr = fdt32_to_cpu(ranges[idx]); ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx, - fdt32_to_cpu(ranges[idx]) + - fix_offset); + cpu_addr + fix_offset); if (ret) return ret; - /* fix parent address */ - idx = i + acells + pacells - 1; + /* Fix PCI address only if it isn't remapped (is same as CPU) */ + idx = i + pci_cells - 1; + pci_addr = ((u64)fdt32_to_cpu(ranges[idx - 1]) << 32) | + fdt32_to_cpu(ranges[idx]); + if (cpu_addr != pci_addr) + continue; + ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx, - fdt32_to_cpu(ranges[idx]) + - fix_offset); + cpu_addr + fix_offset); if (ret) return ret; } From 2454de2c34531941756d259771fd0266ea2d5d0b Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Mon, 28 Feb 2022 10:31:45 +0100 Subject: [PATCH 06/18] drivers: rtc: fix null pointer access in armada38x_rtc_reset Replace null pointer by pointer to device registers when calling armada38x_rtc_write. Signed-off-by: Francois Berder Reviewed-by: Stefan Roese --- drivers/rtc/armada38x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rtc/armada38x.c b/drivers/rtc/armada38x.c index 2d264acf779..2af64e39122 100644 --- a/drivers/rtc/armada38x.c +++ b/drivers/rtc/armada38x.c @@ -121,7 +121,7 @@ static int armada38x_rtc_reset(struct udevice *dev) armada38x_rtc_write(0, rtc, RTC_CONF_TEST); mdelay(500); armada38x_rtc_write(0, rtc, RTC_TIME); - armada38x_rtc_write(BIT(0) | BIT(1), 0, RTC_STATUS); + armada38x_rtc_write(BIT(0) | BIT(1), rtc, RTC_STATUS); } return 0; From 87724d5c905be4fb5eeed0e31384664721db4098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= Date: Mon, 28 Feb 2022 15:59:37 +0100 Subject: [PATCH 07/18] arm64: a37xx: pinctrl: Fix PWM pins indexes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5534fb4f4833 ("arm64: a37xx: pinctrl: Correct PWM pins definitions") introduced bogus definitions os PWM pins: all 4 pins have index 11, instead of having indexes 11, 12, 13, 14. Fix this. Signed-off-by: Marek Behún Reviewed-by: Pali Rohár Reviewed-by: Stefan Roese --- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index 1cf1f06f101..e76ef153e60 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -162,11 +162,11 @@ static struct armada_37xx_pin_group armada_37xx_nb_groups[] = { PIN_GRP_GPIO("emmc_nb", 27, 9, BIT(2), "emmc"), PIN_GRP_GPIO_3("pwm0", 11, 1, BIT(3) | BIT(20), 0, BIT(20), BIT(3), "pwm", "led"), - PIN_GRP_GPIO_3("pwm1", 11, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4), + PIN_GRP_GPIO_3("pwm1", 12, 1, BIT(4) | BIT(21), 0, BIT(21), BIT(4), "pwm", "led"), - PIN_GRP_GPIO_3("pwm2", 11, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5), + PIN_GRP_GPIO_3("pwm2", 13, 1, BIT(5) | BIT(22), 0, BIT(22), BIT(5), "pwm", "led"), - PIN_GRP_GPIO_3("pwm3", 11, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6), + PIN_GRP_GPIO_3("pwm3", 14, 1, BIT(6) | BIT(23), 0, BIT(23), BIT(6), "pwm", "led"), PIN_GRP_GPIO("pmic1", 7, 1, BIT(7), "pmic"), PIN_GRP_GPIO("pmic0", 6, 1, BIT(8), "pmic"), From 0a6f0297c677946907b7ba34d34995fe03055aad Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 1 Mar 2022 13:53:23 +1300 Subject: [PATCH 08/18] ARM: mvebu: x530: clearfog: Add ODT configuration Commit 369e532691e0 ("ddr: marvell: a38x: allow board specific ODT configuration") added the odt_config member to struct mv_ddr_topology_map ahead of the clk_enable and ck_delay members. This means that any boards that configured either of clk_enable or ck_delay needed to have their board topology updated. This affects the x530 and clearfog boards. Other A38x boards don't touch any of the trailing members of mv_ddr_topology_map so don't need updating. Fixes: 369e532691e0 ("ddr: marvell: a38x: allow board specific ODT configuration") Signed-off-by: Chris Packham Acked-by: Baruch Siach Reviewed-by: Stefan Roese --- board/alliedtelesis/x530/x530.c | 1 + board/solidrun/clearfog/clearfog.c | 1 + 2 files changed, 2 insertions(+) diff --git a/board/alliedtelesis/x530/x530.c b/board/alliedtelesis/x530/x530.c index 8b31045a074..c0ec2afa301 100644 --- a/board/alliedtelesis/x530/x530.c +++ b/board/alliedtelesis/x530/x530.c @@ -73,6 +73,7 @@ static struct mv_ddr_topology_map board_topology_map = { {0}, /* timing parameters */ { {0} }, /* electrical configuration */ {0}, /* electrical parameters */ + 0, /* ODT configuration */ 0, /* Clock enable mask */ 160 /* Clock delay */ }; diff --git a/board/solidrun/clearfog/clearfog.c b/board/solidrun/clearfog/clearfog.c index c920cf8d6b5..03adb591d82 100644 --- a/board/solidrun/clearfog/clearfog.c +++ b/board/solidrun/clearfog/clearfog.c @@ -147,6 +147,7 @@ static struct mv_ddr_topology_map board_topology_map = { {0}, /* timing parameters */ { {0} }, /* electrical configuration */ {0,}, /* electrical parameters */ + 0, /* ODT configuration */ 0x3, /* clock enable mask */ }; From d8865f8677c6dd43109e22bfbb85a3cd6bf6aca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:18 +0100 Subject: [PATCH 09/18] tools: kwboot: Check for return value of kwboot_tty_send() and tcflush() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Failure of kwboot_tty_send() and tcflush() functions is fatal, it does not make sense to continue. So return error back to the caller like in other places where are called these functions. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 2d2d545d825..5a7c53ce892 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -740,10 +740,8 @@ kwboot_bootmsg(int tty, void *msg) for (count = 0; count < 128; count++) { rc = kwboot_tty_send(tty, msg, 8, 0); - if (rc) { - usleep(msg_req_delay * 1000); - continue; - } + if (rc) + break; } rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); @@ -772,11 +770,19 @@ kwboot_bootmsg(int tty, void *msg) */ /* flush output queue with remaining boot message patterns */ - tcflush(tty, TCOFLUSH); + rc = tcflush(tty, TCOFLUSH); + if (rc) { + perror("Failed to flush output queue"); + return rc; + } /* send one xmodem packet with 0xff bytes to force BootROM to re-sync */ memset(&block, 0xff, sizeof(block)); - kwboot_tty_send(tty, &block, sizeof(block), 0); + rc = kwboot_tty_send(tty, &block, sizeof(block), 0); + if (rc) { + perror("Failed to send sync sequence"); + return rc; + } /* * Sending 132 bytes via 115200B/8-N-1 takes 11.45 ms, reading 132 bytes @@ -785,7 +791,11 @@ kwboot_bootmsg(int tty, void *msg) usleep(30 * 1000); /* flush remaining NAK replies from input queue */ - tcflush(tty, TCIFLUSH); + rc = tcflush(tty, TCIFLUSH); + if (rc) { + perror("Failed to flush input queue"); + return rc; + } return 0; } @@ -805,10 +815,8 @@ kwboot_debugmsg(int tty, void *msg) break; rc = kwboot_tty_send(tty, msg, 8, 0); - if (rc) { - usleep(msg_req_delay * 1000); - continue; - } + if (rc) + break; rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); From 132016e2706affdd9e150c4e34fd2d1f5bd05749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:19 +0100 Subject: [PATCH 10/18] tools: kwboot: Remove msg_req_delay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Variable msg_req_delay is set but never used. So completely remove it. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 5a7c53ce892..4dfb1038b4f 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -48,11 +48,9 @@ static unsigned char kwboot_msg_debug[] = { }; /* Defines known to work on Kirkwood */ -#define KWBOOT_MSG_REQ_DELAY 10 /* ms */ #define KWBOOT_MSG_RSP_TIMEO 50 /* ms */ /* Defines known to work on Armada XP */ -#define KWBOOT_MSG_REQ_DELAY_AXP 1000 /* ms */ #define KWBOOT_MSG_RSP_TIMEO_AXP 1000 /* ms */ /* @@ -285,7 +283,6 @@ static const char kwb_baud_magic[16] = "$baudratechange"; static int kwboot_verbose; -static int msg_req_delay = KWBOOT_MSG_REQ_DELAY; static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO; static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO; @@ -1725,7 +1722,6 @@ kwboot_usage(FILE *stream, char *progname) " -D : boot without preamble (Dove)\n"); fprintf(stream, " -d: enter debug mode\n"); fprintf(stream, " -a: use timings for Armada XP\n"); - fprintf(stream, " -q : use specific request-delay\n"); fprintf(stream, " -s : use specific response-timeout\n"); fprintf(stream, " -o : use specific xmodem block timeout\n"); @@ -1804,12 +1800,11 @@ main(int argc, char **argv) break; case 'a': - msg_req_delay = KWBOOT_MSG_REQ_DELAY_AXP; msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP; break; case 'q': - msg_req_delay = atoi(optarg); + /* nop, for backward compatibility */ break; case 's': From c1d911f15f81a09126f52fac3e78e2eb6cfd224b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:20 +0100 Subject: [PATCH 11/18] tools: kwboot: Cleanup bootmsg and debugmsg variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function kwboot_debugmsg() is always called with kwboot_msg_debug as msg and function kwboot_bootmsg() with kwboot_msg_debug as msg. Function kwboot_bootmsg() is never called with NULL msg. Simplify, cleanup and remove dead code. No functional change. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 4dfb1038b4f..4e2acb52458 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -718,17 +718,14 @@ out: } static int -kwboot_bootmsg(int tty, void *msg) +kwboot_bootmsg(int tty) { struct kwboot_block block; int rc; char c; int count; - if (msg == NULL) - kwboot_printv("Please reboot the target into UART boot mode..."); - else - kwboot_printv("Sending boot message. Please reboot the target..."); + kwboot_printv("Sending boot message. Please reboot the target..."); do { rc = tcflush(tty, TCIOFLUSH); @@ -736,7 +733,7 @@ kwboot_bootmsg(int tty, void *msg) break; for (count = 0; count < 128; count++) { - rc = kwboot_tty_send(tty, msg, 8, 0); + rc = kwboot_tty_send(tty, kwboot_msg_boot, sizeof(kwboot_msg_boot), 0); if (rc) break; } @@ -798,7 +795,7 @@ kwboot_bootmsg(int tty, void *msg) } static int -kwboot_debugmsg(int tty, void *msg) +kwboot_debugmsg(int tty) { int rc; @@ -811,7 +808,7 @@ kwboot_debugmsg(int tty, void *msg) if (rc) break; - rc = kwboot_tty_send(tty, msg, 8, 0); + rc = kwboot_tty_send(tty, kwboot_msg_debug, sizeof(kwboot_msg_debug), 0); if (rc) break; @@ -1737,8 +1734,8 @@ main(int argc, char **argv) { const char *ttypath, *imgpath; int rv, rc, tty, term; - void *bootmsg; - void *debugmsg; + int bootmsg; + int debugmsg; void *img; size_t size; size_t after_img_rsv; @@ -1748,8 +1745,8 @@ main(int argc, char **argv) rv = 1; tty = -1; - bootmsg = NULL; - debugmsg = NULL; + bootmsg = 0; + debugmsg = 0; imgpath = NULL; img = NULL; term = 0; @@ -1771,7 +1768,7 @@ main(int argc, char **argv) case 'b': if (imgpath || bootmsg || debugmsg) goto usage; - bootmsg = kwboot_msg_boot; + bootmsg = 1; if (prev_optind == optind) goto usage; if (optind < argc - 1 && argv[optind] && argv[optind][0] != '-') @@ -1781,14 +1778,14 @@ main(int argc, char **argv) case 'D': if (imgpath || bootmsg || debugmsg) goto usage; - bootmsg = NULL; + bootmsg = 0; imgpath = optarg; break; case 'd': if (imgpath || bootmsg || debugmsg) goto usage; - debugmsg = kwboot_msg_debug; + debugmsg = 1; break; case 'p': @@ -1869,13 +1866,13 @@ main(int argc, char **argv) } if (debugmsg) { - rc = kwboot_debugmsg(tty, debugmsg); + rc = kwboot_debugmsg(tty); if (rc) { perror("debugmsg"); goto out; } } else if (bootmsg) { - rc = kwboot_bootmsg(tty, bootmsg); + rc = kwboot_bootmsg(tty); if (rc) { perror("bootmsg"); goto out; From 913866af6c97c37bbdb8165c32d8046b8ec2c5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:21 +0100 Subject: [PATCH 12/18] tools: kwboot: Use separate thread for sending boot message pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After BootROM successfully detects boot message pattern on UART it waits until host stop sending data on UART. For example Armada 385 BootROM requires that host does not send anything on UART at least 24 ms. If host is still sending something then BootROM waits (possibly infinitely). BootROM successfully detects boot message pattern if it receives it in small period of time after power on. So to ensure that host put BootROM into UART boot mode, host must send continuous stream of boot message pattern with a small gap (for A385 at least 24 ms) after series of pattern. But this gap cannot be too often or too long to ensure that it does not cover whole BootROM time window when it is detecting for boot message pattern. Therefore it is needed to do following steps in cycle without any delay: 1. send series of boot message pattern over UART 2. wait until kernel transmit all data 3. sleep small period of time At the same time, host needs to monitor input queue, data received on the UART and checking if it contains NAK byte by which BootROM informs that xmodem transfer is ready. But it is not possible to wait until kernel transmit all data on UART and at the same time in the one process to also wait for input data. This is limitation of POSIX tty API and also by linux kernel that it does not provide asynchronous function for waiting until all data are transmitted. There is only synchronous variant tcdrain(). So to correctly implement this handshake on systems with linux kernel, it is needed to use tcdrain() in separate thread. Implement sending of boot message pattern in one thread and reading of reply in the main thread. Use pthread library for threads. This change makes UART booting on Armada 385 more reliable. It is possible to start kwboot and power on board after minute and kwboot correctly put board into UART boot mode. Old implementation without separate thread has an issue that it read just one byte from UART input queue and then it send 128 message pattern to the output queue. If some noise was on UART then kwboot was not able to read BootROM response as its input queue was just overflowed and kwboot was sending more data than receiving. This change basically fixed above issue too. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/Makefile | 3 ++ tools/kwboot.c | 122 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 105 insertions(+), 20 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index 5409ff2879c..f8fb9fe09ee 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -196,6 +196,9 @@ hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl HOSTCFLAGS_mkexynosspl.o := -pedantic +HOSTCFLAGS_kwboot.o += -pthread +HOSTLDLIBS_kwboot += -pthread + ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o hostprogs-$(CONFIG_X86) += ifdtool diff --git a/tools/kwboot.c b/tools/kwboot.c index 4e2acb52458..9fd90b9bec7 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -28,6 +28,7 @@ #include #include #include +#include #ifdef __linux__ #include "termios_linux.h" @@ -717,37 +718,120 @@ out: return rc; } +static void * +kwboot_msg_write_handler(void *arg) +{ + int tty = *(int *)((void **)arg)[0]; + const void *msg = ((void **)arg)[1]; + int rsp_timeo = msg_rsp_timeo; + int i, dummy_oldtype; + + /* allow to cancel this thread at any time */ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dummy_oldtype); + + while (1) { + /* write 128 samples of message pattern into the output queue without waiting */ + for (i = 0; i < 128; i++) { + if (kwboot_tty_send(tty, msg, 8, 1) < 0) { + perror("\nFailed to send message pattern"); + exit(1); + } + } + /* wait until output queue is transmitted and then make pause */ + if (tcdrain(tty) < 0) { + perror("\nFailed to send message pattern"); + exit(1); + } + /* BootROM requires pause on UART after it detects message pattern */ + usleep(rsp_timeo * 1000); + } +} + +static int +kwboot_msg_start_thread(pthread_t *thread, int *tty, void *msg) +{ + void *arg[2]; + int rc; + + arg[0] = tty; + arg[1] = msg; + rc = pthread_create(thread, NULL, kwboot_msg_write_handler, arg); + if (rc) { + errno = rc; + return -1; + } + + return 0; +} + +static int +kwboot_msg_stop_thread(pthread_t thread) +{ + int rc; + + rc = pthread_cancel(thread); + if (rc) { + errno = rc; + return -1; + } + + rc = pthread_join(thread, NULL); + if (rc) { + errno = rc; + return -1; + } + + return 0; +} + static int kwboot_bootmsg(int tty) { struct kwboot_block block; - int rc; + pthread_t write_thread; + int rc, err; char c; - int count; + + /* flush input and output queue */ + tcflush(tty, TCIOFLUSH); + + rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_boot); + if (rc) { + perror("Failed to start write thread"); + return rc; + } kwboot_printv("Sending boot message. Please reboot the target..."); - do { - rc = tcflush(tty, TCIOFLUSH); - if (rc) - break; - - for (count = 0; count < 128; count++) { - rc = kwboot_tty_send(tty, kwboot_msg_boot, sizeof(kwboot_msg_boot), 0); - if (rc) - break; - } - - rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); - + err = 0; + while (1) { kwboot_spinner(); - } while (rc || c != NAK); + rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo); + if (rc && errno == ETIMEDOUT) { + continue; + } else if (rc) { + err = errno; + break; + } + + if (c == NAK) + break; + } kwboot_printv("\n"); - if (rc) + rc = kwboot_msg_stop_thread(write_thread); + if (rc) { + perror("Failed to stop write thread"); return rc; + } + + if (err) { + errno = err; + perror("Failed to read response for boot message pattern"); + return -1; + } /* * At this stage we have sent more boot message patterns and BootROM @@ -1873,10 +1957,8 @@ main(int argc, char **argv) } } else if (bootmsg) { rc = kwboot_bootmsg(tty); - if (rc) { - perror("bootmsg"); + if (rc) goto out; - } } if (img) { From 93976af58926626077eca8b7bbd1aa592649ec01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:22 +0100 Subject: [PATCH 13/18] tools: kwboot: Fix sending and processing debug message pattern (-d option) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -d option is currently broken. In most cases BootROM does not detect this message pattern. For sending debug message pattern it is needed to do same steps as for boot message pattern. Implement sending debug message pattern via same separate thread like it is for boot message pattern. Checking if BootROM entered into UART debug mode is different than detecting UART boot mode. When in boot mode, BootROM sends xmodem NAK bytes. When in debug mode, BootROM activates console echo and reply back every written byte (extept \r\n which is interpreted as executing command and \b which is interpreting as removing the last sent byte). So in kwboot, check that BootROM send back at least 4 debug message patterns as a echo reply for debug message patterns which kwboot is sending in the loop. Then there is another observation, if host writes too many bytes (as command) then BootROM command line buffer may overflow after trying to execute such long command. To workaround this overflow, it is enough to remove bytes from the input line buffer by sending 3 \b bytes for every sent character. So do it. With this change, it is possbile to enter into the UART debug mode with kwboot -d option. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 141 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 17 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 9fd90b9bec7..3ab49e74bb6 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -881,30 +881,139 @@ kwboot_bootmsg(int tty) static int kwboot_debugmsg(int tty) { - int rc; + unsigned char buf[8192]; + pthread_t write_thread; + int rc, err, i, pos; + size_t off; + + /* flush input and output queue */ + tcflush(tty, TCIOFLUSH); + + rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_debug); + if (rc) { + perror("Failed to start write thread"); + return rc; + } kwboot_printv("Sending debug message. Please reboot the target..."); + kwboot_spinner(); - do { - char buf[16]; - - rc = tcflush(tty, TCIOFLUSH); - if (rc) + err = 0; + off = 0; + while (1) { + /* Read immediately all bytes in queue without waiting */ + rc = read(tty, buf + off, sizeof(buf) - off); + if ((rc < 0 && errno == EINTR) || rc == 0) { + continue; + } else if (rc < 0) { + err = errno; break; - - rc = kwboot_tty_send(tty, kwboot_msg_debug, sizeof(kwboot_msg_debug), 0); - if (rc) - break; - - rc = kwboot_tty_recv(tty, buf, 16, msg_rsp_timeo); + } + off += rc - 1; kwboot_spinner(); - } while (rc); + /* + * Check if we received at least 4 debug message patterns + * (console echo from BootROM) in cyclic buffer + */ + + for (pos = 0; pos < sizeof(kwboot_msg_debug); pos++) + if (buf[off] == kwboot_msg_debug[(pos + off) % sizeof(kwboot_msg_debug)]) + break; + + for (i = off; i >= 0; i--) + if (buf[i] != kwboot_msg_debug[(pos + i) % sizeof(kwboot_msg_debug)]) + break; + + off -= i; + + if (off >= 4 * sizeof(kwboot_msg_debug)) + break; + + /* If not move valid suffix from end of the buffer to the beginning of buffer */ + memmove(buf, buf + i + 1, off); + } kwboot_printv("\n"); - return rc; + rc = kwboot_msg_stop_thread(write_thread); + if (rc) { + perror("Failed to stop write thread"); + return rc; + } + + if (err) { + errno = err; + perror("Failed to read response for debug message pattern"); + return -1; + } + + /* flush output queue with remaining debug message patterns */ + rc = tcflush(tty, TCOFLUSH); + if (rc) { + perror("Failed to flush output queue"); + return rc; + } + + kwboot_printv("Clearing input buffer...\n"); + + /* + * Wait until BootROM transmit all remaining echo characters. + * Experimentally it was measured that for Armada 385 BootROM + * it is required to wait at least 0.415s. So wait 0.5s. + */ + usleep(500 * 1000); + + /* + * In off variable is stored number of characters received after the + * successful detection of echo reply. So these characters are console + * echo for other following debug message patterns. BootROM may have in + * its output queue other echo characters which were being transmitting + * before above sleep call. So read remaining number of echo characters + * sent by the BootROM now. + */ + while ((rc = kwboot_tty_recv(tty, &buf[0], 1, 0)) == 0) + off++; + if (errno != ETIMEDOUT) { + perror("Failed to read response"); + return rc; + } + + /* + * Clear every echo character set by the BootROM by backspace byte. + * This is required prior writing any command to the BootROM debug + * because BootROM command line buffer has limited size. If length + * of the command is larger than buffer size then it looks like + * that Armada 385 BootROM crashes after sending ENTER. So erase it. + * Experimentally it was measured that for Armada 385 BootROM it is + * required to send at least 3 backspace bytes for one echo character. + * This is unknown why. But lets do it. + */ + off *= 3; + memset(buf, '\x08', sizeof(buf)); + while (off > sizeof(buf)) { + rc = kwboot_tty_send(tty, buf, sizeof(buf), 1); + if (rc) { + perror("Failed to send clear sequence"); + return rc; + } + off -= sizeof(buf); + } + rc = kwboot_tty_send(tty, buf, off, 0); + if (rc) { + perror("Failed to send clear sequence"); + return rc; + } + + usleep(msg_rsp_timeo * 1000); + rc = tcflush(tty, TCIFLUSH); + if (rc) { + perror("Failed to flush input queue"); + return rc; + } + + return 0; } static size_t @@ -1951,10 +2060,8 @@ main(int argc, char **argv) if (debugmsg) { rc = kwboot_debugmsg(tty); - if (rc) { - perror("debugmsg"); + if (rc) goto out; - } } else if (bootmsg) { rc = kwboot_bootmsg(tty); if (rc) From e8d26e8276358fcd1c2fe28293d3b4c82a735731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:23 +0100 Subject: [PATCH 14/18] tools: kwboot: Add support for backspace key in mini terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Marvell BootROM recognize only '\b' byte as backspace. Use terminfo for retrieving current backspace sequence and replace any occurrence of backspace sequence by the '\b' byte. Reading terminfo database is possible via tigetstr() function from system library libtinfo.so.*. So link kwboot with -ltinfo. Normally terminfo functions are in system header file. But this header file conflicts with U-Boot "termios_linux.h" header file. So declare terminfo functions manually. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/Makefile | 2 +- tools/kwboot.c | 109 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index f8fb9fe09ee..60231c728ce 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -197,7 +197,7 @@ hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl HOSTCFLAGS_mkexynosspl.o := -pedantic HOSTCFLAGS_kwboot.o += -pthread -HOSTLDLIBS_kwboot += -pthread +HOSTLDLIBS_kwboot += -pthread -ltinfo ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o hostprogs-$(CONFIG_X86) += ifdtool diff --git a/tools/kwboot.c b/tools/kwboot.c index 3ab49e74bb6..26cfe6dea6a 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -36,6 +36,13 @@ #include #endif +/* + * These functions are in header file, but this header file conflicts + * with "termios_linux.h" header file. So declare these functions manually. + */ +extern int setupterm(const char *, int, int *); +extern char *tigetstr(const char *); + /* * Marvell BootROM UART Sensing */ @@ -1376,37 +1383,84 @@ kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate) } static int -kwboot_term_pipe(int in, int out, const char *quit, int *s) +kwboot_term_pipe(int in, int out, const char *quit, int *s, const char *kbs, int *k) { char buf[128]; - ssize_t nin; + ssize_t nin, noff; nin = read(in, buf, sizeof(buf)); if (nin <= 0) return -1; - if (quit) { + noff = 0; + + if (quit || kbs) { int i; for (i = 0; i < nin; i++) { - if (buf[i] == quit[*s]) { + if ((quit || kbs) && + (!quit || buf[i] != quit[*s]) && + (!kbs || buf[i] != kbs[*k])) { + const char *prefix; + int plen; + + if (quit && kbs) { + prefix = (*s >= *k) ? quit : kbs; + plen = (*s >= *k) ? *s : *k; + } else if (quit) { + prefix = quit; + plen = *s; + } else { + prefix = kbs; + plen = *k; + } + + if (plen > i && kwboot_write(out, prefix, plen - i) < 0) + return -1; + } + + if (quit && buf[i] == quit[*s]) { (*s)++; if (!quit[*s]) { nin = (i > *s) ? (i - *s) : 0; break; } - } else { - if (*s > i && kwboot_write(out, quit, *s - i) < 0) - return -1; + } else if (quit) { *s = 0; } + + if (kbs && buf[i] == kbs[*k]) { + (*k)++; + if (!kbs[*k]) { + if (i > *k + noff && + kwboot_write(out, buf + noff, i - *k - noff) < 0) + return -1; + /* + * Replace backspace key by '\b' (0x08) + * byte which is the only recognized + * backspace byte by Marvell BootROM. + */ + if (write(out, "\x08", 1) < 0) + return -1; + noff = i + 1; + *k = 0; + } + } else if (kbs) { + *k = 0; + } } - if (i == nin) - nin -= (nin > *s) ? *s : nin; + if (i == nin) { + i = 0; + if (quit && i < *s) + i = *s; + if (kbs && i < *k) + i = *k; + nin -= (nin > i) ? i : nin; + } } - if (kwboot_write(out, buf, nin) < 0) + if (nin > noff && kwboot_write(out, buf + noff, nin - noff) < 0) return -1; return 0; @@ -1415,7 +1469,8 @@ kwboot_term_pipe(int in, int out, const char *quit, int *s) static int kwboot_terminal(int tty) { - int rc, in, s; + int rc, in, s, k; + const char *kbs = NULL; const char *quit = "\34c"; struct termios otio, tio; @@ -1434,6 +1489,33 @@ kwboot_terminal(int tty) goto out; } + /* + * Get sequence for backspace key used by the current + * terminal. Every occurrence of this sequence will be + * replaced by '\b' byte which is the only recognized + * backspace byte by Marvell BootROM. + * + * Note that we cannot read this sequence from termios + * c_cc[VERASE] as VERASE is valid only when ICANON is + * set in termios c_lflag, which is not case for us. + * + * Also most terminals do not set termios c_cc[VERASE] + * as c_cc[VERASE] can specify only one-byte sequence + * and instead let applications to read (possible + * multi-byte) sequence for backspace key from "kbs" + * terminfo database based on $TERM env variable. + * + * So read "kbs" from terminfo database via tigetstr() + * call after successful setupterm(). Most terminals + * use byte 0x7F for backspace key, so replacement with + * '\b' is required. + */ + if (setupterm(NULL, STDOUT_FILENO, &rc) == 0) { + kbs = tigetstr("kbs"); + if (kbs == (char *)-1) + kbs = NULL; + } + kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n", quit[0] | 0100, quit[1]); } else @@ -1441,6 +1523,7 @@ kwboot_terminal(int tty) rc = 0; s = 0; + k = 0; do { fd_set rfds; @@ -1460,13 +1543,13 @@ kwboot_terminal(int tty) break; if (FD_ISSET(tty, &rfds)) { - rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL); + rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL, NULL, NULL); if (rc) break; } if (in >= 0 && FD_ISSET(in, &rfds)) { - rc = kwboot_term_pipe(in, tty, quit, &s); + rc = kwboot_term_pipe(in, tty, quit, &s, kbs, &k); if (rc) break; } From bdc4dbaefe98890cc7ec7b6aa2d4e85b086527cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:24 +0100 Subject: [PATCH 15/18] tools: kwboot: Update usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add all supported Armada SoCs and document -b and -d options in usage. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index 26cfe6dea6a..11aca00bf1e 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -1986,14 +1986,15 @@ static void kwboot_usage(FILE *stream, char *progname) { fprintf(stream, - "Usage: %s [OPTIONS] [-b | -D ] [-B ] \n", + "Usage: %s [OPTIONS] [-b | -D | -b | -d ] [-B ] [-t] \n", progname); fprintf(stream, "\n"); fprintf(stream, - " -b : boot with preamble (Kirkwood, Armada 370/XP)\n"); + " -b : boot with preamble (Kirkwood, Armada 370/XP/375/38x/39x)\n"); fprintf(stream, " -D : boot without preamble (Dove)\n"); - fprintf(stream, " -d: enter debug mode\n"); + fprintf(stream, " -b: enter xmodem boot mode\n"); + fprintf(stream, " -d: enter console debug mode\n"); fprintf(stream, " -a: use timings for Armada XP\n"); fprintf(stream, " -s : use specific response-timeout\n"); fprintf(stream, From 787fcf5c004d3e4e9fd4b8b6e092bcfe73714cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:25 +0100 Subject: [PATCH 16/18] tools: kwboot: Update manpage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document -D, -b, -d, -q and -s options. Add common examples how to use kwboot. Add information about Armada 38x BootROM bug for debug console mode and how to workaround it. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- doc/kwboot.1 | 103 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 4 deletions(-) diff --git a/doc/kwboot.1 b/doc/kwboot.1 index acdea891d9f..bda049bde56 100644 --- a/doc/kwboot.1 +++ b/doc/kwboot.1 @@ -1,4 +1,4 @@ -.TH KWBOOT 1 "2021-08-25" +.TH KWBOOT 1 "2022-03-02" .SH NAME kwboot \- Boot Marvell Kirkwood (and others 32-bit) SoCs over a serial link. @@ -47,6 +47,48 @@ code in it's header which may also print some output via UART (for example U-Boot SPL does this). In such a case, this output is also written to stdout after the header is sent. +.TP +.B "\-b" +Do only handshake on \fITTY\fP without uploading any file. File upload +could be done later via option \fB\-D\fP or via any other Xmodem +application, like \fBsx\fP(1). + +.TP +.B "\-d" +Do special handshake on \fITTY\fP for console debug mode. + +This will instruct BootROM to enter builtin simple console debug mode. +Should be combined with option \fB\-t\fP. + +To get a BootROM help, type this command followed by ENTER key: + +.RS 1.2i +.TP +.B ? +.RE +.IP + +Armada 38x BootROM has a bug which cause that BootROM's standard output +is turned off on UART when SPI-NOR contains valid boot image. Nevertheless +BootROM's standard input and BootROM's terminal echo are active and working +fine. To workaround this BootROM bug with standard output, it is possible +to manually overwrite BootROM variables stored in SRAM which BootROM use +for checking if standard output is enabled or not. To enable BootROM +standard output on UART, type this command folled by ENTER key: + +.RS 1.2i +.TP +.B w 0x40034100 1 +.RE + +.TP +.BI "\-D" " image" +Upload file \fIimage\fP over \fITTY\fP without initial handshake. + +This method is used primary on Dove platforms, where BootROM does +not support initial handshake for entering UART upload mode and +strapping pins (exported via e.g. buttons) are used instead. + .TP .BI "\-p" Obsolete. Does nothing. @@ -55,13 +97,33 @@ In the past, when this option was used, the program patched the header in the image prior upload, to "UART boot" type. This is now done by default. +.TP +.B "\-q" +Obsolete. Does nothing. + +It is unknown whether it did something in the past. + +.TP +.BI "\-s" " response-timeout" +Specify custom response timeout when doing handshake. Default value is 50 ms. +It is the timeout between sending two consecutive handshake patterns, meaning +how long to wait for response from BootROM. Affects only option \fB\-b\fP with +image file and option \fB\-d\fP. + +Option \fB-a\fP specify response timeout suitable for Armada XP BootROM and +currently it is 1000 ms. + +Some testing showed that specifying 24 ms as response timeout make handshake +with Armada 385 BootROM more stable. + .TP .BI "\-t" Run a terminal program, connecting standard input and output to .RB \fITTY\fP. -If used in combination with \fB-b\fP, terminal mode is entered -immediately following a successful image upload. +If used in combination with \fB\-b\fP, \fB\-D\fP or \fB\-d\fP option, +terminal mode is entered immediately following a successful image upload +or successful handshake (if not doing image upload). If standard I/O streams connect to a console, this mode will terminate after receiving \fBctrl-\e\fP followed by \fBc\fP from console input. @@ -85,9 +147,42 @@ Tested values for \fIbaudrate\fP for Armada 38x include: 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 3125000, 4000000 and 5200000. +.SH "EXAMPLES" + +Instruct BootROM to enter boot Xmodem boot mode, send \fIu-boot-spl.kwb\fP +kwbimage file via Xmodem on \fI/dev/ttyUSB0\fP at 115200 Bd and run terminal +program: +.IP +.B kwboot -b u-boot-spl.kwb -t /dev/ttyUSB0 + +.PP +Instruct BootROM to enter boot Xmodem boot mode, send header of +\fIu-boot-spl.kwb\fP kwbimage file via Xmodem at 115200 Bd, then instruct +BootROM to change baudrate to 5200000 Bd, send data part of the kwbimage +file via Xmodem at high speed and finally run terminal program: +.IP +.B kwboot -b u-boot-spl.kwb -B 5200000 -t /dev/ttyUSB0 + +.PP +Only send \fIu-boot-spl.kwb\fP kwbimage file via Xmodem on \fI/dev/ttyUSB0\fP +at 115200 Bd: +.IP +.B kwboot -D u-boot-spl.kwb /dev/ttyUSB0 + +.PP +Instruct BootROM to enter console debug mode and run terminal program on +\fI/dev/ttyUSB0\fP at 115200 Bd: +.IP +.B kwboot -d -t /dev/ttyUSB0 + +.PP +Only run terminal program on \fI/dev/ttyUSB0\fP at 115200 Bd: +.IP +.B kwboot -t /dev/ttyUSB0 + .SH "SEE ALSO" .PP -\fBmkimage\fP(1) +\fBmkimage\fP(1), \fBsx\fP(1) .SH "AUTHORS" From 0b5909d3afaff4fe552cb01de3cb6e537e8bfece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:26 +0100 Subject: [PATCH 17/18] tools: kwboot: Update doc about Avanta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testes proved that current kwboot version supports also Avanta SoCs. It looks like that Avanta SoCs are using same kwbimage format as Armada. Signed-off-by: Pali Rohár Tested-by: Tony Dinh Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- doc/kwboot.1 | 2 +- tools/kwboot.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/kwboot.1 b/doc/kwboot.1 index bda049bde56..f555ff26a25 100644 --- a/doc/kwboot.1 +++ b/doc/kwboot.1 @@ -11,7 +11,7 @@ kwboot \- Boot Marvell Kirkwood (and others 32-bit) SoCs over a serial link. .SH "DESCRIPTION" The \fBkwboot\fP program boots boards based on Marvell's 32-bit -platforms including Kirkwood, Dove, A370, AXP, A375, A38x +platforms including Kirkwood, Dove, Avanta, A370, AXP, A375, A38x and A39x over their integrated UART. Boot image files will typically contain a second stage boot loader, such as U-Boot. The image file must conform to Marvell's BootROM firmware image format diff --git a/tools/kwboot.c b/tools/kwboot.c index 11aca00bf1e..cd1879246a8 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -1,7 +1,7 @@ /* * Boot a Marvell SoC, with Xmodem over UART0. - * supports Kirkwood, Dove, Armada 370, Armada XP, Armada 375, Armada 38x and - * Armada 39x + * supports Kirkwood, Dove, Avanta, Armada 370, Armada XP, Armada 375, + * Armada 38x and Armada 39x. * * (c) 2012 Daniel Stodden * (c) 2021 Pali Rohár @@ -1990,7 +1990,7 @@ kwboot_usage(FILE *stream, char *progname) progname); fprintf(stream, "\n"); fprintf(stream, - " -b : boot with preamble (Kirkwood, Armada 370/XP/375/38x/39x)\n"); + " -b : boot with preamble (Kirkwood, Avanta, Armada 370/XP/375/38x/39x)\n"); fprintf(stream, " -D : boot without preamble (Dove)\n"); fprintf(stream, " -b: enter xmodem boot mode\n"); From f4fa962fcdbd69589021a096f1af0690fe884279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Wed, 2 Mar 2022 11:49:27 +0100 Subject: [PATCH 18/18] tools: kwboot: Update references with public links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Public documents about BootROM of some Marvell SoCs are available in the public Web Archive. Put this information into source code. Signed-off-by: Pali Rohár Reviewed-by: Stefan Roese Tested-by: Stefan Roese --- tools/kwboot.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/tools/kwboot.c b/tools/kwboot.c index cd1879246a8..69d1be0f482 100644 --- a/tools/kwboot.c +++ b/tools/kwboot.c @@ -7,9 +7,34 @@ * (c) 2021 Pali Rohár * (c) 2021 Marek Behún * - * References: marvell.com, "88F6180, 88F6190, 88F6192, and 88F6281 - * Integrated Controller: Functional Specifications" December 2, - * 2008. Chapter 24.2 "BootROM Firmware". + * References: + * - "88F6180, 88F6190, 88F6192, and 88F6281: Integrated Controller: Functional + * Specifications" December 2, 2008. Chapter 24.2 "BootROM Firmware". + * https://web.archive.org/web/20130730091033/https://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf + * - "88AP510: High-Performance SoC with Integrated CPU, 2D/3D Graphics + * Processor, and High-Definition Video Decoder: Functional Specifications" + * August 3, 2011. Chapter 5 "BootROM Firmware" + * https://web.archive.org/web/20120130172443/https://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf + * - "88F6710, 88F6707, and 88F6W11: ARMADA(R) 370 SoC: Functional Specifications" + * May 26, 2014. Chapter 6 "BootROM Firmware". + * https://web.archive.org/web/20140617183701/https://www.marvell.com/embedded-processors/armada-300/assets/ARMADA370-FunctionalSpec-datasheet.pdf + * - "MV78230, MV78260, and MV78460: ARMADA(R) XP Family of Highly Integrated + * Multi-Core ARMv7 Based SoC Processors: Functional Specifications" + * May 29, 2014. Chapter 6 "BootROM Firmware". + * https://web.archive.org/web/20180829171131/https://www.marvell.com/embedded-processors/armada-xp/assets/ARMADA-XP-Functional-SpecDatasheet.pdf + * - "ARMADA(R) 375 Value-Performance Dual Core CPU System on Chip: Functional + * Specifications" Doc. No. MV-S109377-00, Rev. A. September 18, 2013. + * Chapter 7 "Boot Sequence" + * CONFIDENTIAL, no public documentation available + * - "88F6810, 88F6811, 88F6821, 88F6W21, 88F6820, and 88F6828: ARMADA(R) 38x + * Family High-Performance Single/Dual CPU System on Chip: Functional + * Specifications" Doc. No. MV-S109094-00, Rev. C. August 2, 2015. + * Chapter 7 "Boot Flow" + * CONFIDENTIAL, no public documentation available + * - "88F6920, 88F6925 and 88F6928: ARMADA(R) 39x High-Performance Dual Core CPU + * System on Chip Functional Specifications" Doc. No. MV-S109896-00, Rev. B. + * December 22, 2015. Chapter 7 "Boot Flow" + * CONFIDENTIAL, no public documentation available */ #include "kwbimage.h"