Add support for loading U-Boot on the Broadcom 7445D0 SoC.  This port
assumes Broadcom's BOLT bootloader is acting as the second stage
bootloader, and U-Boot is acting as the third stage bootloader, loaded
as an ELF program by BOLT.

Signed-off-by: Thomas Fitzsimmons <fitz...@fitzsim.org>
Cc: Stefan Roese <s...@denx.de>
---
 arch/arm/Kconfig                                |  12 +
 arch/arm/cpu/armv7/Makefile                     |   1 +
 arch/arm/cpu/armv7/bcm7445d0/Makefile           |  11 +
 arch/arm/cpu/armv7/bcm7445d0/lowlevel_init.S    |  24 ++
 arch/arm/lib/crt0.S                             |   2 +
 arch/arm/mach-bcm7445d0/include/mach/gpio.h     |  12 +
 arch/arm/mach-bcm7445d0/include/mach/hardware.h |  12 +
 arch/arm/mach-bcm7445d0/include/mach/sdhci.h    |  15 +
 board/broadcom/bcm7445d0/Kconfig                | 132 ++++++++
 board/broadcom/bcm7445d0/Makefile               |  11 +
 board/broadcom/bcm7445d0/bcm7445d0.c            | 147 ++++++++
 common/fdt_support.c                            |   9 +-
 common/image-fit.c                              |   2 +
 configs/bcm7445d0_defconfig                     |  21 ++
 drivers/mmc/Makefile                            |   1 +
 drivers/mmc/bcmstb_sdhci.c                      |  59 ++++
 drivers/spi/Kconfig                             |   7 +
 drivers/spi/Makefile                            |   1 +
 drivers/spi/bcmstb_spi.c                        | 428 ++++++++++++++++++++++++
 dts/Kconfig                                     |   6 +
 include/configs/bcm7445d0.h                     | 227 +++++++++++++
 include/configs/bcmstb.h                        |  57 ++++
 lib/fdtdec.c                                    |   8 +
 23 files changed, 1204 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/cpu/armv7/bcm7445d0/Makefile
 create mode 100644 arch/arm/cpu/armv7/bcm7445d0/lowlevel_init.S
 create mode 100644 arch/arm/mach-bcm7445d0/include/mach/gpio.h
 create mode 100644 arch/arm/mach-bcm7445d0/include/mach/hardware.h
 create mode 100644 arch/arm/mach-bcm7445d0/include/mach/sdhci.h
 create mode 100644 board/broadcom/bcm7445d0/Kconfig
 create mode 100644 board/broadcom/bcm7445d0/Makefile
 create mode 100644 board/broadcom/bcm7445d0/bcm7445d0.c
 create mode 100644 configs/bcm7445d0_defconfig
 create mode 100644 drivers/mmc/bcmstb_sdhci.c
 create mode 100644 drivers/spi/bcmstb_spi.c
 create mode 100644 include/configs/bcm7445d0.h
 create mode 100644 include/configs/bcmstb.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9bd70f4..b2df30a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -498,6 +498,17 @@ config TARGET_VEXPRESS_CA15_TC2
        select CPU_V7_HAS_VIRT
        select PL011_SERIAL
 
+config TARGET_BCM7445D0
+       bool "Broadcom 7445D0 TSBL"
+       select CPU_V7
+       select SUPPORT_SPL
+       help
+         Support for the Broadcom 7445D0 SoC.  This port assumes Bolt
+         is acting as the second stage bootloader, and U-Boot is
+         acting as the third stage bootloader (TSBL), loaded by Bolt.
+         This port may work on other BCM7xxx boards with
+         configuration changes.
+
 config TARGET_VEXPRESS_CA5X2
        bool "Support vexpress_ca5x2"
        select CPU_V7
@@ -1320,6 +1331,7 @@ source "board/armltd/vexpress/Kconfig"
 source "board/armltd/vexpress64/Kconfig"
 source "board/broadcom/bcm23550_w1d/Kconfig"
 source "board/broadcom/bcm28155_ap/Kconfig"
+source "board/broadcom/bcm7445d0/Kconfig"
 source "board/broadcom/bcmcygnus/Kconfig"
 source "board/broadcom/bcmnsp/Kconfig"
 source "board/broadcom/bcmns2/Kconfig"
diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
index b14ee54..7183d4d 100644
--- a/arch/arm/cpu/armv7/Makefile
+++ b/arch/arm/cpu/armv7/Makefile
@@ -30,6 +30,7 @@ endif
 
 obj-$(if $(filter bcm235xx,$(SOC)),y) += bcm235xx/
 obj-$(if $(filter bcm281xx,$(SOC)),y) += bcm281xx/
+obj-$(if $(filter bcm7445d0,$(SOC)),y) += bcm7445d0/
 obj-$(if $(filter bcmcygnus,$(SOC)),y) += bcmcygnus/
 obj-$(if $(filter bcmnsp,$(SOC)),y) += bcmnsp/
 obj-$(if $(filter ls102xa,$(SOC)),y) += ls102xa/
diff --git a/arch/arm/cpu/armv7/bcm7445d0/Makefile 
b/arch/arm/cpu/armv7/bcm7445d0/Makefile
new file mode 100644
index 0000000..796f482
--- /dev/null
+++ b/arch/arm/cpu/armv7/bcm7445d0/Makefile
@@ -0,0 +1,11 @@
+#
+# (C) Copyright 2018
+# Cisco Systems, Inc. <www.cisco.com>
+#
+# Author :
+#      Thomas Fitzsimmons <fitz...@fitzsim.org>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y  := lowlevel_init.o
diff --git a/arch/arm/cpu/armv7/bcm7445d0/lowlevel_init.S 
b/arch/arm/cpu/armv7/bcm7445d0/lowlevel_init.S
new file mode 100644
index 0000000..1eb67a0
--- /dev/null
+++ b/arch/arm/cpu/armv7/bcm7445d0/lowlevel_init.S
@@ -0,0 +1,24 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <linux/linkage.h>
+
+ENTRY(save_boot_params)
+       ldr     r6, =bcm7445d0_boot_parameters
+       str     r0, [r6, #0]
+       str     r1, [r6, #4]
+       str     r2, [r6, #8]
+       str     r3, [r6, #12]
+       str     sp, [r6, #16]
+       str     lr, [r6, #20]
+       ldr     r6, =prior_stage_fdt_address
+       str     r2, [r6]
+       b       save_boot_params_ret
+ENDPROC(save_boot_params)
diff --git a/arch/arm/lib/crt0.S b/arch/arm/lib/crt0.S
index fa81317..f1a6f35 100644
--- a/arch/arm/lib/crt0.S
+++ b/arch/arm/lib/crt0.S
@@ -94,6 +94,7 @@ ENTRY(_main)
  * 'here' but relocated.
  */
 
+#if !defined(CONFIG_OF_PRIOR_STAGE)
        ldr     r0, [r9, #GD_START_ADDR_SP]     /* sp = gd->start_addr_sp */
        bic     r0, r0, #7      /* 8-byte alignment for ABI compliance */
        mov     sp, r0
@@ -108,6 +109,7 @@ ENTRY(_main)
 #endif
        ldr     r0, [r9, #GD_RELOCADDR]         /* r0 = gd->relocaddr */
        b       relocate_code
+#endif
 here:
 /*
  * now relocate vectors
diff --git a/arch/arm/mach-bcm7445d0/include/mach/gpio.h 
b/arch/arm/mach-bcm7445d0/include/mach/gpio.h
new file mode 100644
index 0000000..f7163e4
--- /dev/null
+++ b/arch/arm/mach-bcm7445d0/include/mach/gpio.h
@@ -0,0 +1,12 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#ifndef _BCM7445D0_GPIO_H
+#define _BCM7445D0_GPIO_H
+#endif /* _BCM7445D0_GPIO_H */
diff --git a/arch/arm/mach-bcm7445d0/include/mach/hardware.h 
b/arch/arm/mach-bcm7445d0/include/mach/hardware.h
new file mode 100644
index 0000000..28418bf
--- /dev/null
+++ b/arch/arm/mach-bcm7445d0/include/mach/hardware.h
@@ -0,0 +1,12 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#ifndef _BCM7445D0_HARDWARE_H
+#define _BCM7445D0_HARDWARE_H
+#endif /* _BCM7445D0_HARDWARE_H */
diff --git a/arch/arm/mach-bcm7445d0/include/mach/sdhci.h 
b/arch/arm/mach-bcm7445d0/include/mach/sdhci.h
new file mode 100644
index 0000000..2c7fb09
--- /dev/null
+++ b/arch/arm/mach-bcm7445d0/include/mach/sdhci.h
@@ -0,0 +1,15 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+#ifndef _BCM7445D0_SDHCI_H
+#define _BCM7445D0_SDHCI_H
+
+int bcmstb_sdhci_init(phys_addr_t regbase);
+
+#endif /* _BCM7445D0_SDHCI_H */
diff --git a/board/broadcom/bcm7445d0/Kconfig b/board/broadcom/bcm7445d0/Kconfig
new file mode 100644
index 0000000..d710503
--- /dev/null
+++ b/board/broadcom/bcm7445d0/Kconfig
@@ -0,0 +1,132 @@
+if TARGET_BCM7445D0
+
+config SYS_BOARD
+       default "bcm7445d0"
+
+config SYS_VENDOR
+       default "broadcom"
+
+config SYS_CONFIG_NAME
+       default "bcm7445d0"
+
+config SYS_SOC
+       default "bcm7445d0"
+
+config BCHP_BSPI_MAST_N_BOOT_CTRL
+       hex ""
+       default 0x003e3208
+
+config BCHP_EBI_CS_SPI_SELECT
+       hex ""
+       default 0x003e0920
+
+config BCHP_HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK
+       hex ""
+       default 0x00000080
+
+config BCHP_HIF_MSPI_SPCR2_SPE_MASK
+       hex ""
+       default 0x00000040
+
+config BCHP_HIF_MSPI_SPCR2_SPIFIE_MASK
+       hex ""
+       default 0x00000020
+
+config BCHP_HIF_MSPI_WRITE_LOCK
+       hex ""
+       default 0x003e3580
+
+config BCHP_HIF_MSPI_WRITE_LOCK_WRITE_LOCK_DEFAULT
+       hex ""
+       default 0x00000000
+
+config BCHP_HIF_MSPI_WRITE_LOCK_WRITE_LOCK_MASK
+       hex ""
+       default 0x00000001
+
+config BCHP_HIF_MSPI_WRITE_LOCK_WRITE_LOCK_SHIFT
+       hex ""
+       default 0
+
+config BCHP_HIF_SPI_INTR2_CPU_CLEAR
+       hex ""
+       default 0x003e1a08
+
+config BCHP_HIF_SPI_INTR2_CPU_MASK_CLEAR
+       hex ""
+       default 0x003e1a14
+
+config BCHP_HIF_SPI_INTR2_CPU_MASK_SET
+       hex ""
+       default 0x003e1a10
+
+config BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK
+       hex ""
+       default 0x00000020
+
+config BCMSTB_ACCOMMODATE_STBLINUX
+       bool ""
+       default y
+       help
+         This prevents U-Boot from adding memory reservations for the
+          lengths of initramfs and DTB.  Without skipping these,
+          stblinux's "contiguous memory allocator" (CMA) Linux driver
+          (cma_driver) will allocate memory ranges smaller than what
+          are actually available, because it only checks reservation
+          sizes.  It doesn't check if the reserved range overlaps the
+          range it allocates.  stblinux also tries to move the DTB to
+          a lower memory location early in the Linux boot.  If the FIT
+          image specifies a load address for the initramfs then
+          sometimes the DTB is moved into the range where the
+          initramfs image is loaded.  Defining this will mean that
+          FIT-provided initramfs load addresses are ignored.
+
+config BCMSTB_SDHCI
+       bool ""
+       default y
+
+config BCMSTB_SDHCI_BASE
+       hex ""
+       default 0xf03e0200
+
+config BCMSTB_SPI_BASE
+       hex ""
+       default 0xf03e3400
+
+config CMD_FDT_MAX_DUMP
+       int ""
+       default 256
+
+config GENERIC_MMC
+       bool ""
+       default y
+
+config MMC_SDMA
+       bool ""
+       default y
+
+config SDHCI
+       bool ""
+       default y
+
+config SYS_BCMSTB_SPI_WAIT
+       int ""
+       default 10
+
+config SYS_FDT_SAVE_ADDRESS
+       hex ""
+       default 0x1f00000
+
+config SYS_NO_FLASH
+       bool ""
+       default y
+
+config TIMER_FREQUENCY_REGISTER_ADDRESS
+       hex ""
+       default 0xf0412020
+
+config TIMER_LOW_REGISTER_ADDRESS
+       hex ""
+       default 0xf0412008
+
+endif
diff --git a/board/broadcom/bcm7445d0/Makefile 
b/board/broadcom/bcm7445d0/Makefile
new file mode 100644
index 0000000..265bc74
--- /dev/null
+++ b/board/broadcom/bcm7445d0/Makefile
@@ -0,0 +1,11 @@
+#
+# (C) Copyright 2018
+# Cisco Systems, Inc. <www.cisco.com>
+#
+# Author :
+#      Thomas Fitzsimmons <fitz...@fitzsim.org>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+
+obj-y  := bcm7445d0.o
diff --git a/board/broadcom/bcm7445d0/bcm7445d0.c 
b/board/broadcom/bcm7445d0/bcm7445d0.c
new file mode 100644
index 0000000..7f8e1f6
--- /dev/null
+++ b/board/broadcom/bcm7445d0/bcm7445d0.c
@@ -0,0 +1,147 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <linux/types.h>
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/sdhci.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct bcm7445d0_boot_parameters {
+       u32 r0;
+       u32 r1;
+       u32 r2;
+       u32 r3;
+       u32 sp;
+       u32 lr;
+};
+
+struct bcm7445d0_boot_parameters bcm7445d0_boot_parameters \
+__attribute__((section(".data")));
+
+phys_addr_t prior_stage_fdt_address __attribute__((section(".data")));
+
+union reg_value_union {
+       const char *data;
+       const phys_addr_t *address;
+};
+
+int board_init(void)
+{
+       return 0;
+}
+
+u32 get_board_rev(void)
+{
+       return 0;
+}
+
+void reset_cpu(ulong ignored)
+{
+}
+
+int print_cpuinfo(void)
+{
+       return 0;
+}
+
+int dram_init(void)
+{
+       gd->ram_size = 0xc0000000;
+
+       return 0;
+}
+
+int dram_init_banksize(void)
+{
+       bd_t *bd = gd->bd;
+
+       bd->bi_dram[0].start = 0x00000000;
+       bd->bi_dram[0].size  = 0x40000000;
+       bd->bi_dram[1].start = 0x40000000;
+       bd->bi_dram[1].size  = 0x40000000;
+       bd->bi_dram[2].start = 0x80000000;
+       bd->bi_dram[2].size  = 0x40000000;
+
+       return 0;
+}
+
+void enable_caches(void)
+{
+       /*
+        * Nothing required here, since the prior stage bootloader has
+        * enabled I-cache and D-cache already.  Implementing this
+        * function silences the warning in the default function.
+        */
+}
+
+int board_mmc_init(bd_t *bis)
+{
+       /* Just use the hard-coded SDHCI base address, though it could
+        * be determined from the DTB provided by the prior stage
+        * bootloader.
+        */
+       bcmstb_sdhci_init(CONFIG_BCMSTB_SDHCI_BASE);
+
+       return 0;
+}
+
+int timer_init(void)
+{
+       gd->arch.timer_rate_hz = readl(CONFIG_TIMER_FREQUENCY_REGISTER_ADDRESS);
+
+       return 0;
+}
+
+ulong get_tbclk(void)
+{
+       return gd->arch.timer_rate_hz;
+}
+
+unsigned long timer_read_counter(void)
+{
+       return readl(CONFIG_TIMER_LOW_REGISTER_ADDRESS);
+}
+
+int board_late_init(void)
+{
+       printf("Arguments from prior stage bootloader:\n");
+       printf("General Purpose Register 0: 0x%x\n",
+              bcm7445d0_boot_parameters.r0);
+       printf("General Purpose Register 1: 0x%x\n",
+              bcm7445d0_boot_parameters.r1);
+       printf("General Purpose Register 2: 0x%x\n",
+              bcm7445d0_boot_parameters.r2);
+       printf("General Purpose Register 3: 0x%x\n",
+              bcm7445d0_boot_parameters.r3);
+       printf("Stack Pointer Register:     0x%x\n",
+              bcm7445d0_boot_parameters.sp);
+       printf("Link Register:              0x%x\n",
+              bcm7445d0_boot_parameters.lr);
+       printf("Assuming timer frequency register at: 0x%x\n",
+              CONFIG_TIMER_FREQUENCY_REGISTER_ADDRESS);
+       printf("Read timer frequency (in Hz): %ld\n", gd->arch.timer_rate_hz);
+       printf("Prior stage provided DTB at: 0x%p\n",
+              (void *)prior_stage_fdt_address);
+       /*
+        * Set fdtcontroladdr in the environment so that scripts can
+        * refer to it, for example, to reuse it for fdtaddr.
+        */
+       env_set_hex("fdtcontroladdr", prior_stage_fdt_address);
+       /*
+        * Do not set machid to the machine identifier value provided
+        * by the prior stage bootloader
+        * (bcm7445d0_boot_parameters.r1) because we're using a device
+        * tree to boot Linux.
+        */
+
+       return 0;
+}
diff --git a/common/fdt_support.c b/common/fdt_support.c
index 66a313e..f07dfe3 100644
--- a/common/fdt_support.c
+++ b/common/fdt_support.c
@@ -242,11 +242,13 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong 
initrd_end)
                }
        }
 
+#if !defined(CONFIG_BCMSTB_ACCOMMODATE_STBLINUX)
        err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start);
        if (err < 0) {
                printf("fdt_initrd: %s\n", fdt_strerror(err));
                return err;
        }
+#endif
 
        is_u64 = (fdt_address_cells(fdt, 0) == 2);
 
@@ -602,7 +604,10 @@ int fdt_shrink_to_minimum(void *blob, uint extrasize)
 {
        int i;
        uint64_t addr, size;
-       int total, ret;
+       int total;
+#if !defined(CONFIG_BCMSTB_ACCOMMODATE_STBLINUX)
+       int ret;
+#endif
        uint actualsize;
 
        if (!blob)
@@ -635,9 +640,11 @@ int fdt_shrink_to_minimum(void *blob, uint extrasize)
        fdt_set_totalsize(blob, actualsize);
 
        /* Add the new reservation */
+#if !defined(CONFIG_BCMSTB_ACCOMMODATE_STBLINUX)
        ret = fdt_add_mem_rsv(blob, (uintptr_t)blob, actualsize);
        if (ret < 0)
                return ret;
+#endif
 
        return actualsize;
 }
diff --git a/common/image-fit.c b/common/image-fit.c
index 030a3e5..a346f8c 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -1905,6 +1905,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
                        return -EBADF;
                }
        } else if (load_op != FIT_LOAD_OPTIONAL_NON_ZERO || load) {
+#if !defined(CONFIG_BCMSTB_ACCOMMODATE_STBLINUX)
                ulong image_start, image_end;
                ulong load_end;
                void *dst;
@@ -1929,6 +1930,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
                dst = map_sysmem(load, len);
                memmove(dst, buf, len);
                data = load;
+#endif
        }
        bootstage_mark(bootstage_id + BOOTSTAGE_SUB_LOAD);
 
diff --git a/configs/bcm7445d0_defconfig b/configs/bcm7445d0_defconfig
new file mode 100644
index 0000000..1e1162e
--- /dev/null
+++ b/configs/bcm7445d0_defconfig
@@ -0,0 +1,21 @@
+CONFIG_ARM=y
+CONFIG_SYS_TEXT_BASE=0x80100000
+CONFIG_TARGET_BCM7445D0=y
+CONFIG_USE_PRIVATE_LIBGCC=y
+CONFIG_OF_CONTROL=y
+CONFIG_OF_PRIOR_STAGE=y
+CONFIG_DM=y
+CONFIG_DM_SPI=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI=y
+CONFIG_SPI_FLASH=y
+CONFIG_ENV_IS_IN_SPI_FLASH=y
+CONFIG_FIT=y
+CONFIG_FIT_SIGNATURE=y
+CONFIG_RSA=y
+CONFIG_BLK=n
+CONFIG_MMC_SDHCI=y
+CONFIG_CONS_INDEX=3
+CONFIG_BOOTDELAY=1
+CONFIG_SYS_PROMPT="U-Boot>"
+CONFIG_HUSH_PARSER=y
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index cf46c33..959f410 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_MMC_DW_EXYNOS)           += exynos_dw_mmc.o
 obj-$(CONFIG_MMC_DW_K3)                        += hi6220_dw_mmc.o
 obj-$(CONFIG_MMC_DW_ROCKCHIP)          += rockchip_dw_mmc.o
 obj-$(CONFIG_MMC_DW_SOCFPGA)           += socfpga_dw_mmc.o
+obj-$(CONFIG_BCMSTB_SDHCI) += bcmstb_sdhci.o
 obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 obj-$(CONFIG_FTSDC010) += ftsdc010_mci.o
 obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
diff --git a/drivers/mmc/bcmstb_sdhci.c b/drivers/mmc/bcmstb_sdhci.c
new file mode 100644
index 0000000..933c546
--- /dev/null
+++ b/drivers/mmc/bcmstb_sdhci.c
@@ -0,0 +1,59 @@
+#include <common.h>
+#include <malloc.h>
+#include <sdhci.h>
+
+/*
+ * The BCMSTB SDHCI has a quirk in that its actual maximum frequency
+ * capability is 100 MHz.  The divisor that is eventually written to
+ * SDHCI_CLOCK_CONTROL is calculated based on what the MMC device
+ * reports, and relative to this maximum frequency.
+ *
+ * This define used to be set to 52000000 (52 MHz), the desired
+ * maximum frequency, but that would result in the communication
+ * actually running at 100 MHz (seemingly without issue), which is
+ * out-of-spec.
+ *
+ * Now, by setting this to 0 (auto-detect), 100 MHz will be read from
+ * the capabilities register, and the resulting divisor will be
+ * doubled, meaning that the clock control register will be set to the
+ * in-spec 52 MHz value.
+ */
+#define BCMSTB_SDHCI_MAXIMUM_CLOCK_FREQUENCY   0
+/*
+ * When the minimum clock frequency is set to 0 (auto-detect), U-Boot
+ * sets it to 100 MHz divided by SDHCI_MAX_DIV_SPEC_300, or 48,875 Hz,
+ * which results in the controller timing out when trying to
+ * communicate with the MMC device.  Hard-code this value to 400000
+ * (400 kHz) to prevent this.
+ */
+#define BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY   400000
+
+static char *BCMSTB_SDHCI_NAME = "bcmstb-sdhci";
+
+/*
+ * This driver has only been tested with eMMC devices; SD devices may
+ * not work.
+ */
+int bcmstb_sdhci_init(phys_addr_t regbase)
+{
+       struct sdhci_host *host = NULL;
+
+       host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
+       if (!host) {
+               printf("bcmstb-sdhci malloc fail!\n");
+               return 1;
+       }
+       memset(host, 0, sizeof(*host));
+
+       host->name = BCMSTB_SDHCI_NAME;
+       host->ioaddr = (void *)regbase;
+       host->quirks = 0;
+
+       host->cfg.part_type = PART_TYPE_DOS;
+
+       host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
+
+       return add_sdhci(host,
+                        BCMSTB_SDHCI_MAXIMUM_CLOCK_FREQUENCY,
+                        BCMSTB_SDHCI_MINIMUM_CLOCK_FREQUENCY);
+}
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6667f73..c4a6c8d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -66,6 +66,13 @@ config BCM63XX_SPI
          access the SPI NOR flash on platforms embedding these Broadcom
          SPI cores.
 
+config BCMSTB_SPI
+       bool "Broadcom Set Top Box SPI driver"
+       help
+         Enable the Broadcom Set Top Box SPI driver. This driver can
+         be used to access the SPI flash on platforms embedding this
+         Broadcom SPI core.
+
 config CADENCE_QSPI
        bool "Cadence QSPI driver"
        help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 176bfa0..0f864be 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
 obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
 obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o
 obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o
+obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o
 obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
 obj-$(CONFIG_CF_SPI) += cf_spi.o
 obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
diff --git a/drivers/spi/bcmstb_spi.c b/drivers/spi/bcmstb_spi.c
new file mode 100644
index 0000000..183a547
--- /dev/null
+++ b/drivers/spi/bcmstb_spi.c
@@ -0,0 +1,428 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <config.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <spi.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define SPBR_MIN               8
+#define BITS_PER_WORD          8
+
+#define NUM_TXRAM              32
+#define NUM_RXRAM              32
+#define NUM_CDRAM              16
+
+#ifdef DEBUG
+static int debug_tx_rx;
+#define D(fmt, args...) debug_cond(debug_tx_rx, fmt, ##args)
+#else
+#define D(fmt, args...)
+#endif
+
+/* MSPI registers.  BSPI is disabled. */
+struct bcmstb_spi_regs {
+       u32 spcr0_lsb;          /* 0x000 */
+       u32 spcr0_msb;          /* 0x004 */
+       u32 spcr1_lsb;          /* 0x008 */
+       u32 spcr1_msb;          /* 0x00c */
+       u32 newqp;              /* 0x010 */
+       u32 endqp;              /* 0x014 */
+       u32 spcr2;              /* 0x018 */
+       u32 reserved0;          /* 0x01c */
+       u32 mspi_status;        /* 0x020 */
+       u32 cptqp;              /* 0x024 */
+       u32 spcr3;              /* 0x028 */
+       u32 revision;           /* 0x02c */
+       u32 reserved1[4];       /* 0x030 */
+       u32 txram[NUM_TXRAM];   /* 0x040 */
+       u32 rxram[NUM_RXRAM];   /* 0x0c0 */
+       u32 cdram[NUM_CDRAM];   /* 0x140 */
+};
+
+struct bcmstb_spi_platdata {
+       struct bcmstb_spi_regs *regs;
+};
+
+struct bcmstb_spi_priv {
+       struct bcmstb_spi_regs *regs;
+       int default_cs;
+       int curr_cs;
+       uint tx_slot;
+       uint rx_slot;
+       u8 saved_cmd[NUM_CDRAM];
+       uint saved_cmd_len;
+       void *saved_din_addr;
+};
+
+/*
+ * If we could rely on the prior stage bootloader to insert a "spi0"
+ * alias, we could enable CONFIG_DM_SEQ_ALIAS and omit
+ * bcmstb_spi_bind.  We cannot rely on this behaviour, so we have to
+ * handle binding explicitly.
+ */
+static int bcmstb_spi_bind(struct udevice *bus)
+{
+       char spi0_name[30];
+       int node = dev_of_offset(bus);
+
+       memset(spi0_name, 0, sizeof(spi0_name));
+
+       debug("bcmstb_spi_bind: %s, ", bus->name);
+
+       snprintf(spi0_name, sizeof(spi0_name), "spi@%08x",
+                CONFIG_BCMSTB_SPI_BASE);
+       if (strcmp(fdt_get_name(gd->fdt_blob, node, NULL),
+                  spi0_name) == 0) {
+               bus->req_seq = 0;
+       }
+
+       debug("%d\n", bus->req_seq);
+
+       return 0;
+}
+
+static int bcmstb_spi_ofdata_to_platdata(struct udevice *bus)
+{
+       struct bcmstb_spi_platdata *plat = dev_get_platdata(bus);
+       const void *blob = gd->fdt_blob;
+       int node = dev_of_offset(bus);
+       u32 address = 0;
+       int code = 0;
+
+       code = fdtdec_get_int_array(blob, node, "reg", &address, 1);
+       if (code != 0) {
+               printf("bcmstb_spi_ofdata_to_platdata:"
+                      " Failed to read reg property\n");
+               return code;
+       }
+
+       plat->regs = (struct bcmstb_spi_regs *)address;
+       D("spi_xfer: tx regs: %p\n", &plat->regs->txram[0]);
+       D("spi_xfer: rx regs: %p\n", &plat->regs->rxram[0]);
+
+       return 0;
+}
+
+static void bcmstb_spi_hw_set_parms(struct bcmstb_spi_priv *priv)
+{
+       writel(SPBR_MIN, &priv->regs->spcr0_lsb);
+       writel(BITS_PER_WORD << 2 | SPI_MODE_3, &priv->regs->spcr0_msb);
+}
+
+static void bcmstb_spi_enable_interrupt(u32 mask)
+{
+       BDEV_SET_RB(CONFIG_BCHP_HIF_SPI_INTR2_CPU_MASK_CLEAR, mask);
+}
+
+static void bcmstb_spi_disable_interrupt(u32 mask)
+{
+       BDEV_SET_RB(CONFIG_BCHP_HIF_SPI_INTR2_CPU_MASK_SET, mask);
+}
+
+static void bcmstb_spi_clear_interrupt(u32 mask)
+{
+       BDEV_SET_RB(CONFIG_BCHP_HIF_SPI_INTR2_CPU_CLEAR, mask);
+}
+
+static int bcmstb_spi_probe(struct udevice *bus)
+{
+       struct bcmstb_spi_platdata *plat = dev_get_platdata(bus);
+       struct bcmstb_spi_priv *priv = dev_get_priv(bus);
+
+       priv->regs = plat->regs;
+       priv->default_cs = 0;
+       priv->curr_cs = -1;
+       priv->tx_slot = 0;
+       priv->rx_slot = 0;
+       memset(priv->saved_cmd, 0, NUM_CDRAM);
+       priv->saved_cmd_len = 0;
+       priv->saved_din_addr = NULL;
+
+       /* Disable BSPI. */
+       BDEV_WR_RB(CONFIG_BCHP_BSPI_MAST_N_BOOT_CTRL, 1);
+
+       /* Set up interrupts. */
+       bcmstb_spi_disable_interrupt(0xffffffff);
+       bcmstb_spi_clear_interrupt(0xffffffff);
+       bcmstb_spi_enable_interrupt(
+               CONFIG_BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK);
+
+       /* Set up control registers. */
+       writel(0, &priv->regs->spcr1_lsb);
+       writel(0, &priv->regs->spcr1_msb);
+       writel(0, &priv->regs->newqp);
+       writel(0, &priv->regs->endqp);
+       writel(CONFIG_BCHP_HIF_MSPI_SPCR2_SPIFIE_MASK, &priv->regs->spcr2);
+       writel(0, &priv->regs->spcr3);
+
+       bcmstb_spi_hw_set_parms(priv);
+
+       return 0;
+}
+
+static int bcmstb_spi_remove(struct udevice *dev)
+{
+       return 0;
+}
+
+static int bcmstb_spi_claim_bus(struct udevice *dev)
+{
+       return 0;
+}
+
+static int bcmstb_spi_release_bus(struct udevice *dev)
+{
+       return 0;
+}
+
+static void bcmstb_spi_submit(struct bcmstb_spi_priv *priv, bool done)
+{
+       D("WR NEWQP: %d\n", 0);
+       writel(0, &priv->regs->newqp);
+
+       D("WR ENDQP: %d\n", priv->tx_slot - 1);
+       writel(priv->tx_slot - 1, &priv->regs->endqp);
+
+       if (done) {
+               D("WR CDRAM[%d]: %02x\n", priv->tx_slot - 1,
+                 readl(&priv->regs->cdram[priv->tx_slot - 1]) & ~0x80);
+               writel(readl(&priv->regs->cdram[priv->tx_slot - 1]) & ~0x80,
+                      &priv->regs->cdram[priv->tx_slot - 1]);
+       }
+
+       /* Force chip select first time. */
+       if (priv->curr_cs != priv->default_cs) {
+               D("spi_xfer: switching chip select to %d\n",
+                 priv->default_cs);
+               BDEV_WR_RB(CONFIG_BCHP_EBI_CS_SPI_SELECT,
+                          (BDEV_RD(CONFIG_BCHP_EBI_CS_SPI_SELECT) &
+                           ~0xff) | (1 << priv->default_cs));
+               udelay(10);
+               priv->curr_cs = priv->default_cs;
+       }
+
+       D("WR WRITE_LOCK: %02x\n", 1);
+       BDEV_WR_F_RB(HIF_MSPI_WRITE_LOCK, WRITE_LOCK, 1);
+
+       D("WR SPCR2: %02x\n",
+         CONFIG_BCHP_HIF_MSPI_SPCR2_SPIFIE_MASK |
+         CONFIG_BCHP_HIF_MSPI_SPCR2_SPE_MASK |
+         CONFIG_BCHP_HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK);
+       writel(CONFIG_BCHP_HIF_MSPI_SPCR2_SPIFIE_MASK |
+              CONFIG_BCHP_HIF_MSPI_SPCR2_SPE_MASK |
+              CONFIG_BCHP_HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK,
+              &priv->regs->spcr2);
+}
+
+static int bcmstb_spi_wait(struct bcmstb_spi_regs *regs)
+{
+       u32 start_time = get_timer(0);
+       u32 status = readl(&regs->mspi_status);
+
+       while (!(status & 1)) {
+               if (get_timer(start_time) >
+                   CONFIG_SYS_BCMSTB_SPI_WAIT) {
+                       return -ETIMEDOUT;
+               }
+               status = readl(&regs->mspi_status);
+       }
+
+       writel(readl(&regs->mspi_status) & ~1, &regs->mspi_status);
+       bcmstb_spi_clear_interrupt(
+               CONFIG_BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK);
+
+       return 0;
+}
+
+static int bcmstb_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                          const void *dout, void *din, unsigned long flags)
+{
+       uint len = bitlen / 8;
+       uint tx_len = len;
+       uint rx_len = len;
+       const u8 *out_bytes = (u8 *)dout;
+       u8 *in_bytes = (u8 *)din;
+       struct udevice *bus = dev_get_parent(dev);
+       struct bcmstb_spi_priv *priv = dev_get_priv(bus);
+       struct bcmstb_spi_regs *regs = priv->regs;
+
+       D("spi_xfer: %d, t: %p, r: %p, f: %lx\n", len, dout, din, flags);
+       D("spi_xfer: chip select: %x\n",
+         readl(CONFIG_BCHP_EBI_CS_SPI_SELECT) & 0xff);
+       D("spi_xfer: tx addr: %p\n", &regs->txram[0]);
+       D("spi_xfer: rx addr: %p\n", &regs->rxram[0]);
+       D("spi_xfer: cd addr: %p\n", &regs->cdram[0]);
+
+       if (flags & SPI_XFER_END) {
+               D("spi_xfer: clearing saved din address: %p\n",
+                 priv->saved_din_addr);
+               priv->saved_din_addr = NULL;
+               priv->saved_cmd_len = 0;
+               memset(priv->saved_cmd, 0, NUM_CDRAM);
+       }
+
+       if (bitlen == 0)
+               return 0;
+
+       if (bitlen % 8) {
+               printf("bcmstb_spi_xfer: Non-byte-aligned transfer\n");
+               return -EOPNOTSUPP;
+       }
+
+       if (flags & ~(SPI_XFER_BEGIN | SPI_XFER_END)) {
+               printf("bcmstb_spi_xfer: Unsupported flags: %lx\n", flags);
+               return -EOPNOTSUPP;
+       }
+
+       if (flags & SPI_XFER_BEGIN) {
+               priv->tx_slot = 0;
+               priv->rx_slot = 0;
+
+               if (out_bytes && len > NUM_CDRAM) {
+                       printf("bcmstb_spi_xfer: Unable to save transfer\n");
+                       return -EOPNOTSUPP;
+               }
+
+               if (out_bytes && !(flags & SPI_XFER_END)) {
+                       /*
+                        * This is the start of a transmit operation
+                        * that will need repeating if the calling
+                        * code polls for the result.  Save it for
+                        * subsequent transmission.
+                        */
+                       D("spi_xfer: saving command: %x, %d\n",
+                         out_bytes[0], len);
+                       priv->saved_cmd_len = len;
+                       memcpy(priv->saved_cmd, out_bytes, priv->saved_cmd_len);
+               }
+       }
+
+       if (!(flags & (SPI_XFER_BEGIN | SPI_XFER_END))) {
+               if (priv->saved_din_addr == din) {
+                       /*
+                        * The caller is polling for status.  Repeat
+                        * the last transmission.
+                        */
+                       int code = 0;
+
+                       D("spi_xfer: Making recursive call\n");
+                       code = bcmstb_spi_xfer(dev, priv->saved_cmd_len * 8,
+                                              priv->saved_cmd, NULL,
+                                              SPI_XFER_BEGIN);
+                       if (code) {
+                               printf("bcmstb_spi_xfer:"
+                                      " Recursive call failed\n");
+                               return code;
+                       }
+               } else {
+                       D("spi_xfer: saving din address: %p\n", din);
+                       priv->saved_din_addr = din;
+               }
+       }
+
+       while (rx_len > 0) {
+               priv->rx_slot = priv->tx_slot;
+
+               while (priv->tx_slot < NUM_CDRAM && tx_len > 0) {
+                       bcmstb_spi_hw_set_parms(priv);
+                       D("WR TXRAM[%d]: %02x\n", priv->tx_slot,
+                         out_bytes ? out_bytes[len - tx_len] : 0xff);
+                       writel(out_bytes ? out_bytes[len - tx_len] : 0xff,
+                              &regs->txram[priv->tx_slot << 1]);
+                       D("WR CDRAM[%d]: %02x\n", priv->tx_slot, 0x8e);
+                       writel(0x8e, &regs->cdram[priv->tx_slot]);
+                       priv->tx_slot++;
+                       tx_len--;
+                       if (!in_bytes)
+                               rx_len--;
+               }
+
+               D("spi_xfer: early return clauses: %d, %d, %d\n",
+                 len <= NUM_CDRAM,
+                 !in_bytes,
+                 (flags & (SPI_XFER_BEGIN | SPI_XFER_END)) == SPI_XFER_BEGIN);
+               if (len <= NUM_CDRAM &&
+                   !in_bytes &&
+                   (flags & (SPI_XFER_BEGIN | SPI_XFER_END)) == SPI_XFER_BEGIN)
+                       return 0;
+
+               bcmstb_spi_submit(priv, tx_len == 0);
+
+               if (bcmstb_spi_wait(regs) == -ETIMEDOUT) {
+                       printf("bcmstb_spi_xfer: Timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               priv->tx_slot %= NUM_CDRAM;
+
+               if (in_bytes) {
+                       while (priv->rx_slot < NUM_CDRAM && rx_len > 0) {
+                               in_bytes[len - rx_len] =
+                                       readl(&regs->rxram[(priv->rx_slot << 1)
+                                                          + 1])
+                                       & 0xff;
+                               D("RD RXRAM[%d]: %02x\n",
+                                 priv->rx_slot, in_bytes[len - rx_len]);
+                               priv->rx_slot++;
+                               rx_len--;
+                       }
+               }
+       }
+
+       if (flags & SPI_XFER_END) {
+               D("WR WRITE_LOCK: %02x\n", 0);
+               BDEV_WR_F_RB(HIF_MSPI_WRITE_LOCK, WRITE_LOCK, 0);
+       }
+
+       return 0;
+}
+
+static int bcmstb_spi_set_speed(struct udevice *dev, uint speed)
+{
+       return 0;
+}
+
+static int bcmstb_spi_set_mode(struct udevice *dev, uint mode)
+{
+       return 0;
+}
+
+static const struct dm_spi_ops bcmstb_spi_ops = {
+       .claim_bus      = bcmstb_spi_claim_bus,
+       .release_bus    = bcmstb_spi_release_bus,
+       .xfer           = bcmstb_spi_xfer,
+       .set_speed      = bcmstb_spi_set_speed,
+       .set_mode       = bcmstb_spi_set_mode,
+};
+
+static const struct udevice_id bcmstb_spi_ids[] = {
+       { .compatible = "brcm,spi-brcmstb" },
+       { }
+};
+
+U_BOOT_DRIVER(bcmstb_spi) = {
+       .name                           = "bcmstb_spi",
+       .id                             = UCLASS_SPI,
+       .of_match                       = bcmstb_spi_ids,
+       .ops                            = &bcmstb_spi_ops,
+       .ofdata_to_platdata             = bcmstb_spi_ofdata_to_platdata,
+       .probe                          = bcmstb_spi_probe,
+       .remove                         = bcmstb_spi_remove,
+       .platdata_auto_alloc_size       = sizeof(struct bcmstb_spi_platdata),
+       .priv_auto_alloc_size           = sizeof(struct bcmstb_spi_priv),
+       .bind                           = bcmstb_spi_bind,
+};
diff --git a/dts/Kconfig b/dts/Kconfig
index 0cef225..074784b 100644
--- a/dts/Kconfig
+++ b/dts/Kconfig
@@ -101,6 +101,12 @@ config OF_HOSTFILE
          This is only useful for Sandbox.  Use the -d flag to U-Boot to
          specify the file to read.
 
+config OF_PRIOR_STAGE
+       bool "Prior stage bootloader DTB for DT control"
+       help
+         If this option is enabled, DTB will be read from a memory
+         location passed to U-Boot by the prior stage bootloader.
+
 endchoice
 
 config DEFAULT_DEVICE_TREE
diff --git a/include/configs/bcm7445d0.h b/include/configs/bcm7445d0.h
new file mode 100644
index 0000000..36155c7
--- /dev/null
+++ b/include/configs/bcm7445d0.h
@@ -0,0 +1,227 @@
+/*
+ * (C) Copyright 2018
+ * Cisco Systems, Inc. <www.cisco.com>
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * Configuration settings for the Broadcom BCM7445D0 SoC.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include "bcmstb.h"
+#include "version.h"
+
+/*
+ * Generic board configuration.
+ */
+#define CONFIG_SYS_GENERIC_BOARD
+
+/*
+ * CPU configuration.
+ */
+#define CONFIG_SKIP_LOWLEVEL_INIT
+
+/*
+ * Memory configuration.
+ *
+ * The prior stage BOLT bootloader sets up memory for us.  An example
+ * invocation is:
+ *
+ * BOLT> boot -bsu -elf flash0.u-boot1
+ *
+ * An example boot memory layout after loading everything is:
+ *
+ *      0x0000 8000    vmlinux.bin.gz
+ *            :        [31 MiB uncompressed max]
+ *      0x01ef f000    FIT containing signed public key
+ *            :        [~2 KiB in size]
+ *      0x01f0 0000    DTB copied from prior-stage-provided region
+ *            :        [~1 MiB max]
+ *     ~0x01ff 4000    DTB modified by U-Boot (fdt_high - DTB size)
+ *            :        [~40 KiB in size]
+ *      0x0200 0000    FIT containing initramfs.cpio.gz
+ *            :        [208 MiB uncompressed max, to CMA low address]
+ *            :        [80 MiB compressed max, to PSB low address]
+ *      0x0700 0000    Prior stage bootloader (PSB)
+ *            :
+ *      0x0761 7000    Prior-stage-provided device tree blob (DTB)
+ *            :        [~40 KiB in size]
+ *      0x0f00 0000    Contiguous memory allocator (CMA) low address
+ *            :
+ *      0x8010 0000    U-Boot code at ELF load address
+ *            :        [~500 KiB in size, stripped]
+ *      0xc000 0000    Top of RAM
+ *
+ * CONFIG_OF_PRIOR_STAGE=y prevents U-Boot from relocating itself when
+ * it is run as an ELF program by the prior stage bootloader.
+ *
+ * The maximum value for fdt_high is the lowest physical address from
+ * which stblinux's CMA driver starts allocating, which is 0x0f000000.
+ * But a good setting for fdt_high is the default load address.
+ *
+ * Relocating the prior stage DTB prevents it from being overwritten
+ * when large initramfs images are loaded.  Then the upper limit for
+ * the initramfs load region becomes the CMA low address.
+ *
+ * Overwriting the prior stage bootloader causes memory instability,
+ * so the compressed initramfs needs to fit between the load address
+ * and the PSB low address.  In BOLT's default configuration this
+ * limits the compressed size of the initramfs to approximately 80
+ * MiB.  However, BOLT can be configured to allow loading larger
+ * initramfs images, in which case this limitation is eliminated.
+ *
+ * Newer versions of stblinux use the bmem mechanism for reserving
+ * physical regions of memory.  In practice CMA/bmem values require
+ * experimentation and tuning to achieve a stable U-Boot memory layout
+ * and the desired Linux memory layout (see also
+ * BCMSTB_ACCOMMODATE_STBLINUX).
+ */
+#define CONFIG_NR_DRAM_BANKS           3
+
+#define CONFIG_SYS_SDRAM_BASE          0x00000000
+#define CONFIG_SYS_TEXT_BASE           0x80100000
+#define CONFIG_SYS_INIT_RAM_ADDR       0x80200000
+#define CONFIG_SYS_INIT_RAM_SIZE       0x100000
+#define CONFIG_SYS_INIT_SP_ADDR                (CONFIG_SYS_INIT_RAM_ADDR + \
+                                        CONFIG_SYS_INIT_RAM_SIZE - \
+                                        GENERATED_GBL_DATA_SIZE)
+#define CONFIG_SYS_MALLOC_LEN          ((10 * 1024) << 10) /* 10 MiB */
+#define CONFIG_SYS_LOAD_ADDR           0x2000000
+/*
+ * CONFIG_SYS_LOAD_ADDR - 1 MiB.
+ */
+#define CONFIG_SYS_FDT_SAVE_ADDRESS    0x1f00000
+#define CONFIG_SYS_CBSIZE              512
+#define CONFIG_SYS_MAXARGS             32
+
+/*
+ * Timer configuration.
+ */
+#define CONFIG_TIMER_FREQUENCY_REGISTER_ADDRESS        0xf0412020
+#define CONFIG_TIMER_LOW_REGISTER_ADDRESS      0xf0412008
+
+/*
+ * NS16550 configuration.
+ */
+#define V_NS16550_CLK                  81000000
+
+#define CONFIG_SYS_NS16550
+#define CONFIG_SYS_NS16550_SERIAL
+#define CONFIG_SYS_NS16550_REG_SIZE    (-4)
+#define CONFIG_SYS_NS16550_CLK         V_NS16550_CLK
+
+/*
+ * Serial console configuration.
+ */
+#define CONFIG_SERIAL3                 3
+/*
+ * For now, this must be a pre-defined macro, not looked up from the
+ * prior-stage-provided DTB.
+ */
+#define CONFIG_SYS_NS16550_COM3                0xf040ab00
+#define CONFIG_BAUDRATE                        115200
+#define CONFIG_SYS_BAUDRATE_TABLE      {4800, 9600, 19200, 38400, 57600,\
+                                       115200}
+
+/*
+ * Informational display configuration.
+ */
+#define CONFIG_REVISION_TAG
+
+/*
+ * Command configuration.
+ */
+#define CONFIG_CMD_ASKENV
+#define CONFIG_CMD_CACHE
+#define CONFIG_CMD_EXT2
+#define CONFIG_CMD_SF
+#define CONFIG_CMD_SPI
+#define CONFIG_CMD_SF_TEST
+#define CONFIG_CMD_MMC
+
+/*
+ * Flattened device tree configuration.
+ */
+#define CONFIG_CMD_FDT_MAX_DUMP                256
+
+/*
+ * Flash configuration.
+ */
+#define CONFIG_ST_SMI
+#define CONFIG_BCMSTB_SPI
+#define CONFIG_SPI_FLASH_STMICRO
+#define CONFIG_SPI_FLASH_MACRONIX
+
+#define CONFIG_BCMSTB_SPI_BASE         0xf03e3400
+
+/*
+ * Copied from stblinux, include/linux/brcmstb/7445d0/bchp_hif_mspi.h.
+ */
+#define CONFIG_BCHP_HIF_MSPI_SPCR2_SPIFIE_MASK                 0x00000020
+#define CONFIG_BCHP_HIF_MSPI_SPCR2_SPE_MASK                    0x00000040
+#define CONFIG_BCHP_HIF_MSPI_SPCR2_CONT_AFTER_CMD_MASK         0x00000080
+#define CONFIG_BCHP_HIF_MSPI_WRITE_LOCK                                
0x003e3580
+#define CONFIG_BCHP_HIF_MSPI_WRITE_LOCK_WRITE_LOCK_MASK                
0x00000001
+#define CONFIG_BCHP_HIF_MSPI_WRITE_LOCK_WRITE_LOCK_DEFAULT     0x00000000
+
+/*
+ * Copied from stblinux, include/linux/brcmstb/7445d0/bchp_hif_spi_intr2.h.
+ */
+#define CONFIG_BCHP_HIF_SPI_INTR2_CPU_CLEAR                    0x003e1a08
+#define CONFIG_BCHP_HIF_SPI_INTR2_CPU_MASK_SET                 0x003e1a10
+#define CONFIG_BCHP_HIF_SPI_INTR2_CPU_MASK_CLEAR               0x003e1a14
+#define CONFIG_BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK       0x00000020
+
+/*
+ * Copied from stblinux, include/linux/brcmstb/7445d0/bchp_ebi.h.
+ */
+#define CONFIG_BCHP_EBI_CS_SPI_SELECT                          0x003e0920
+
+/*
+ * Copied from stblinux, include/linux/brcmstb/7445d0/bchp_bspi.h.
+ */
+#define CONFIG_BCHP_BSPI_MAST_N_BOOT_CTRL                      0x003e3208
+
+/*
+ * Filesystem configuration.
+ */
+#define CONFIG_DOS_PARTITION
+#define CONFIG_CMD_EXT4
+#define CONFIG_FS_EXT4
+#define CONFIG_CMD_FS_GENERIC
+
+/*
+ * Environment configuration.
+ */
+#define CONFIG_SYS_REDUNDAND_ENVIRONMENT
+
+#define CONFIG_ENV_IS_IN_SPI_FLASH      1
+#define CONFIG_ENV_OFFSET              0x1e0000
+#define CONFIG_ENV_SIZE                        (64 << 10) /* 64 KiB */
+#define CONFIG_ENV_SECT_SIZE           CONFIG_ENV_SIZE
+#define CONFIG_ENV_OFFSET_REDUND       (CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE)
+#define CONFIG_ENV_OVERWRITE
+
+#define CONFIG_PREBOOT                                 \
+       "fdt addr ${fdtcontroladdr};"                   \
+       "fdt move ${fdtcontroladdr} ${fdtsaveaddr};"    \
+       "fdt addr ${fdtsaveaddr};"
+#define CONFIG_EXTRA_ENV_SETTINGS                                      \
+       "fdtsaveaddr=" __stringify(CONFIG_SYS_FDT_SAVE_ADDRESS) "\0"
+
+/*
+ * Set fdtaddr to prior stage-provided DTB in board_late_init, when
+ * writeable environment is available.
+ */
+#define CONFIG_BOARD_LATE_INIT
+
+#define CONFIG_SYS_MAX_FLASH_BANKS 1
+
+#define CONFIG_DM_SPI 1
+
+#endif /* __CONFIG_H */
diff --git a/include/configs/bcmstb.h b/include/configs/bcmstb.h
new file mode 100644
index 0000000..33d1efc
--- /dev/null
+++ b/include/configs/bcmstb.h
@@ -0,0 +1,57 @@
+/*
+ * (C) Copyright 2009
+ * Broadcom Corporation
+ *
+ * Author :
+ *     Thomas Fitzsimmons <fitz...@fitzsim.org>
+ *
+ * Macros imported from Broadcom stblinux.
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#ifndef __BCMSTB_H
+#define __BCMSTB_H
+
+/* Copied from stblinux, include/linux/brcmstb/brcmstb.h. */
+#define DEV_RD(x) (readl((x)))
+#define DEV_WR(x, y) do { writel((y), (x)); } while (0)
+#define DEV_UNSET(x, y) do { DEV_WR((x), DEV_RD(x) & ~(y)); } while (0)
+#define DEV_SET(x, y) do { DEV_WR((x), DEV_RD(x) | (y)); } while (0)
+
+#define DEV_WR_RB(x, y) do { DEV_WR((x), (y)); DEV_RD(x); } while (0)
+#define DEV_SET_RB(x, y) do { DEV_SET((x), (y)); DEV_RD(x); } while (0)
+#define DEV_UNSET_RB(x, y) do { DEV_UNSET((x), (y)); DEV_RD(x); } while (0)
+
+/* Adjusted for U-Boot. */
+#define BRCMSTB_PERIPH_VIRT    0xf0000000
+
+#define BVIRTADDR(x)           (BRCMSTB_PERIPH_VIRT + ((x) & 0x0fffffff))
+
+#define BDEV_RD(x) (DEV_RD(BVIRTADDR(x)))
+#define BDEV_WR(x, y) do { DEV_WR(BVIRTADDR(x), (y)); } while (0)
+#define BDEV_UNSET(x, y) do { BDEV_WR((x), BDEV_RD(x) & ~(y)); } while (0)
+#define BDEV_SET(x, y) do { BDEV_WR((x), BDEV_RD(x) | (y)); } while (0)
+
+#define BDEV_SET_RB(x, y) do { BDEV_SET((x), (y)); BDEV_RD(x); } while (0)
+#define BDEV_UNSET_RB(x, y) do { BDEV_UNSET((x), (y)); BDEV_RD(x); } while (0)
+#define BDEV_WR_RB(x, y) do { BDEV_WR((x), (y)); BDEV_RD(x); } while (0)
+
+#define BDEV_RD_F(reg, field) \
+       ((BDEV_RD(BCHP_##reg) & BCHP_##reg##_##field##_MASK) >> \
+        BCHP_##reg##_##field##_SHIFT)
+#define BDEV_WR_F(reg, field, val) do { \
+       BDEV_WR(BCHP_##reg, \
+       (BDEV_RD(BCHP_##reg) & ~BCHP_##reg##_##field##_MASK) | \
+       (((val) << BCHP_##reg##_##field##_SHIFT) & \
+        BCHP_##reg##_##field##_MASK)); \
+       } while (0)
+#define BDEV_WR_F_RB(reg, field, val) do { \
+       BDEV_WR(CONFIG_BCHP_##reg, \
+       (BDEV_RD(CONFIG_BCHP_##reg) & ~CONFIG_BCHP_##reg##_##field##_MASK) | \
+       (((val) << CONFIG_BCHP_##reg##_##field##_SHIFT) & \
+        CONFIG_BCHP_##reg##_##field##_MASK)); \
+       BDEV_RD(CONFIG_BCHP_##reg); \
+       } while (0)
+
+#endif /* __BCMSTB_H */
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 320ee1d..aec12f2 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -1299,6 +1299,10 @@ __weak void *board_fdt_blob_setup(void)
 }
 #endif
 
+#if defined(CONFIG_OF_PRIOR_STAGE)
+extern phys_addr_t prior_stage_fdt_address;
+#endif
+
 int fdtdec_setup(void)
 {
 #if CONFIG_IS_ENABLED(OF_CONTROL)
@@ -1323,8 +1327,12 @@ int fdtdec_setup(void)
 # endif
 # ifndef CONFIG_SPL_BUILD
        /* Allow the early environment to override the fdt address */
+#  if defined(CONFIG_OF_PRIOR_STAGE)
+       gd->fdt_blob = (void *)prior_stage_fdt_address;
+#  else
        gd->fdt_blob = (void *)env_get_ulong("fdtcontroladdr", 16,
                                                (uintptr_t)gd->fdt_blob);
+#  endif
 # endif
 
 # if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
-- 
1.8.3.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to