Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arm / mach-mvebu / armada3700 / cpu.c
index 7d8f7b8686b28df1c347cf9b22e281fd5aa4abfa..e438b4922e148259aeba0f2f2e8e252ded37de58 100644 (file)
@@ -1,20 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2016 Stefan Roese <sr@denx.de>
- *
- * SPDX-License-Identifier:    GPL-2.0+
+ * Copyright (C) 2020 Marek Behun <marek.behun@nic.cz>
  */
 
 #include <common.h>
+#include <cpu_func.h>
 #include <dm.h>
 #include <fdtdec.h>
-#include <libfdt.h>
+#include <init.h>
+#include <linux/bitops.h>
+#include <linux/libfdt.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/soc.h>
 #include <asm/armv8/mmu.h>
-
-DECLARE_GLOBAL_DATA_PTR;
+#include <sort.h>
 
 /* Armada 3700 */
 #define MVEBU_GPIO_NB_REG_BASE         (MVEBU_REGISTER(0x13800))
@@ -28,161 +30,312 @@ DECLARE_GLOBAL_DATA_PTR;
 #define MVEBU_NB_WARM_RST_REG          (MVEBU_GPIO_NB_REG_BASE + 0x40)
 #define MVEBU_NB_WARM_RST_MAGIC_NUM    0x1d1e
 
-static struct mm_region mvebu_mem_map[] = {
-       {
-               /* RAM */
-               .phys = 0x0UL,
-               .virt = 0x0UL,
-               .size = 0x80000000UL,
-               .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
-                        PTE_BLOCK_INNER_SHARE
-       },
+/* Armada 3700 CPU Address Decoder registers */
+#define MVEBU_CPU_DEC_WIN_REG_BASE     (size_t)(MVEBU_REGISTER(0xcf00))
+#define MVEBU_CPU_DEC_WIN_CTRL(w) \
+       (MVEBU_CPU_DEC_WIN_REG_BASE + ((w) << 4))
+#define MVEBU_CPU_DEC_WIN_CTRL_EN      BIT(0)
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK        0xf
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS        4
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM        0
+#define MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE        2
+#define MVEBU_CPU_DEC_WIN_SIZE(w)      (MVEBU_CPU_DEC_WIN_CTRL(w) + 0x4)
+#define MVEBU_CPU_DEC_WIN_BASE(w)      (MVEBU_CPU_DEC_WIN_CTRL(w) + 0x8)
+#define MVEBU_CPU_DEC_WIN_REMAP(w)     (MVEBU_CPU_DEC_WIN_CTRL(w) + 0xc)
+#define MVEBU_CPU_DEC_WIN_GRANULARITY  16
+#define MVEBU_CPU_DEC_WINS             5
+
+#define MAX_MEM_MAP_REGIONS            (MVEBU_CPU_DEC_WINS + 2)
+
+#define A3700_PTE_BLOCK_NORMAL \
+       (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE)
+#define A3700_PTE_BLOCK_DEVICE \
+       (PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE)
+
+#define PCIE_PATH                      "/soc/pcie@d0070000"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static struct mm_region mvebu_mem_map[MAX_MEM_MAP_REGIONS] = {
        {
-               /* SRAM, MMIO regions */
-               .phys = 0xd0000000UL,
-               .virt = 0xd0000000UL,
+               /*
+                * SRAM, MMIO regions
+                * Don't remove this, a3700_build_mem_map needs it.
+                */
+               .phys = SOC_REGS_PHY_BASE,
+               .virt = SOC_REGS_PHY_BASE,
                .size = 0x02000000UL,   /* 32MiB internal registers */
-               .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
-                        PTE_BLOCK_NON_SHARE
+               .attrs = A3700_PTE_BLOCK_DEVICE
        },
-       {
-               /* List terminator */
-               0,
-       }
 };
 
 struct mm_region *mem_map = mvebu_mem_map;
 
+static int get_cpu_dec_win(int win, u32 *tgt, u32 *base, u32 *size)
+{
+       u32 reg;
+
+       reg = readl(MVEBU_CPU_DEC_WIN_CTRL(win));
+       if (!(reg & MVEBU_CPU_DEC_WIN_CTRL_EN))
+               return -1;
+
+       if (tgt) {
+               reg >>= MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS;
+               reg &= MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK;
+               *tgt = reg;
+       }
+
+       if (base) {
+               reg = readl(MVEBU_CPU_DEC_WIN_BASE(win));
+               *base = reg << MVEBU_CPU_DEC_WIN_GRANULARITY;
+       }
+
+       if (size) {
+               /*
+                * Window size is encoded as the number of 1s from LSB to MSB,
+                * followed by 0s. The number of 1s specifies the size in 64 KiB
+                * granularity.
+                */
+               reg = readl(MVEBU_CPU_DEC_WIN_SIZE(win));
+               *size = ((reg + 1) << MVEBU_CPU_DEC_WIN_GRANULARITY);
+       }
+
+       return 0;
+}
+
 /*
- * On ARMv8, MBus is not configured in U-Boot. To enable compilation
- * of the already implemented drivers, lets add a dummy version of
- * this function so that linking does not fail.
+ * Builds mem_map according to CPU Address Decoder settings, which were set by
+ * the TIMH image on the Cortex-M3 secure processor, or by ARM Trusted Firmware
  */
-const struct mbus_dram_target_info *mvebu_mbus_dram_info(void)
+static void build_mem_map(void)
 {
-       return NULL;
+       int win, region;
+
+       region = 1;
+       for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+               u32 base, tgt, size;
+               u64 attrs;
+
+               /* skip disabled windows */
+               if (get_cpu_dec_win(win, &tgt, &base, &size))
+                       continue;
+
+               if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+                       attrs = A3700_PTE_BLOCK_NORMAL;
+               else if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
+                       attrs = A3700_PTE_BLOCK_DEVICE;
+               else
+                       /* skip windows with other targets */
+                       continue;
+
+               mvebu_mem_map[region].phys = base;
+               mvebu_mem_map[region].virt = base;
+               mvebu_mem_map[region].size = size;
+               mvebu_mem_map[region].attrs = attrs;
+               ++region;
+       }
+
+       /* add list terminator */
+       mvebu_mem_map[region].size = 0;
+       mvebu_mem_map[region].attrs = 0;
 }
 
-void reset_cpu(ulong ignored)
+void enable_caches(void)
 {
-       /*
-        * Write magic number of 0x1d1e to North Bridge Warm Reset register
-        * to trigger warm reset
-        */
-       writel(MVEBU_NB_WARM_RST_MAGIC_NUM, MVEBU_NB_WARM_RST_REG);
+       build_mem_map();
+
+       icache_enable();
+       dcache_enable();
 }
 
-/*
- * get_ref_clk
- *
- * return: reference clock in MHz (25 or 40)
- */
-u32 get_ref_clk(void)
+int a3700_dram_init(void)
 {
-       u32 regval;
-
-       regval = (readl(MVEBU_TEST_PIN_LATCH_N) & MVEBU_XTAL_MODE_MASK) >>
-               MVEBU_XTAL_MODE_OFFS;
+       int win;
+
+       gd->ram_size = 0;
+       for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+               u32 base, tgt, size;
+
+               /* skip disabled windows */
+               if (get_cpu_dec_win(win, &tgt, &base, &size))
+                       continue;
+
+               /* skip non-DRAM windows */
+               if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+                       continue;
+
+               /*
+                * It is possible that one image was built for boards with
+                * different RAM sizes, for example 512 MiB and 1 GiB.
+                * We therefore try to determine the actual RAM size in the
+                * window with get_ram_size.
+                */
+               gd->ram_size += get_ram_size((void *)(size_t)base, size);
+       }
 
-       if (regval == MVEBU_XTAL_CLOCK_25MHZ)
-               return 25;
-       else
-               return 40;
+       return 0;
 }
 
-/* DRAM init code ... */
+struct a3700_dram_window {
+       size_t base, size;
+};
 
-static const void *get_memory_reg_prop(const void *fdt, int *lenp)
+static int dram_win_cmp(const void *a, const void *b)
 {
-       int offset;
+       size_t ab, bb;
 
-       offset = fdt_path_offset(fdt, "/memory");
-       if (offset < 0)
-               return NULL;
+       ab = ((const struct a3700_dram_window *)a)->base;
+       bb = ((const struct a3700_dram_window *)b)->base;
 
-       return fdt_getprop(fdt, offset, "reg", lenp);
+       if (ab < bb)
+               return -1;
+       else if (ab > bb)
+               return 1;
+       else
+               return 0;
 }
 
-int dram_init(void)
+int a3700_dram_init_banksize(void)
 {
-       const void *fdt = gd->fdt_blob;
-       const fdt32_t *val;
-       int ac, sc, len;
-
-       ac = fdt_address_cells(fdt, 0);
-       sc = fdt_size_cells(fdt, 0);
-       if (ac < 0 || sc < 1 || sc > 2) {
-               printf("invalid address/size cells\n");
-               return -EINVAL;
+       struct a3700_dram_window dram_wins[MVEBU_CPU_DEC_WINS];
+       int bank, win, ndram_wins;
+       u32 last_end;
+       size_t size;
+
+       ndram_wins = 0;
+       for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+               u32 base, tgt, size;
+
+               /* skip disabled windows */
+               if (get_cpu_dec_win(win, &tgt, &base, &size))
+                       continue;
+
+               /* skip non-DRAM windows */
+               if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
+                       continue;
+
+               dram_wins[win].base = base;
+               dram_wins[win].size = size;
+               ++ndram_wins;
        }
 
-       val = get_memory_reg_prop(fdt, &len);
-       if (len / sizeof(*val) < ac + sc)
-               return -EINVAL;
-
-       val += ac;
-
-       gd->ram_size = fdtdec_get_number(val, sc);
+       qsort(dram_wins, ndram_wins, sizeof(dram_wins[0]), dram_win_cmp);
+
+       bank = 0;
+       last_end = -1;
+
+       for (win = 0; win < ndram_wins; ++win) {
+               /* again determining actual RAM size as in a3700_dram_init */
+               size = get_ram_size((void *)dram_wins[win].base,
+                                   dram_wins[win].size);
+
+               /*
+                * Check if previous window ends as the current starts. If yes,
+                * merge these windows into one "bank". This is possible by this
+                * simple check thanks to mem_map regions being qsorted in
+                * build_mem_map.
+                */
+               if (last_end == dram_wins[win].base) {
+                       gd->bd->bi_dram[bank - 1].size += size;
+                       last_end += size;
+               } else {
+                       if (bank == CONFIG_NR_DRAM_BANKS) {
+                               printf("Need more CONFIG_NR_DRAM_BANKS\n");
+                               return -ENOBUFS;
+                       }
+
+                       gd->bd->bi_dram[bank].start = dram_wins[win].base;
+                       gd->bd->bi_dram[bank].size = size;
+                       last_end = dram_wins[win].base + size;
+                       ++bank;
+               }
+       }
 
-       debug("DRAM size = %08lx\n", (unsigned long)gd->ram_size);
+       /*
+        * If there is more place for DRAM BANKS definitions than needed, fill
+        * the rest with zeros.
+        */
+       for (; bank < CONFIG_NR_DRAM_BANKS; ++bank) {
+               gd->bd->bi_dram[bank].start = 0;
+               gd->bd->bi_dram[bank].size = 0;
+       }
 
        return 0;
 }
 
-void dram_init_banksize(void)
+static u32 find_pcie_window_base(void)
 {
-       const void *fdt = gd->fdt_blob;
-       const fdt32_t *val;
-       int ac, sc, cells, len, i;
-
-       val = get_memory_reg_prop(fdt, &len);
-       if (len < 0)
-               return;
-
-       ac = fdt_address_cells(fdt, 0);
-       sc = fdt_size_cells(fdt, 0);
-       if (ac < 1 || sc > 2 || sc < 1 || sc > 2) {
-               printf("invalid address/size cells\n");
-               return;
+       int win;
+
+       for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
+               u32 base, tgt;
+
+               /* skip disabled windows */
+               if (get_cpu_dec_win(win, &tgt, &base, NULL))
+                       continue;
+
+               if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
+                       return base;
        }
 
-       cells = ac + sc;
+       return -1;
+}
+
+int a3700_fdt_fix_pcie_regions(void *blob)
+{
+       u32 new_ranges[14], base;
+       const u32 *ranges;
+       int node, len;
 
-       len /= sizeof(*val);
+       node = fdt_path_offset(blob, PCIE_PATH);
+       if (node < 0)
+               return node;
 
-       for (i = 0; i < CONFIG_NR_DRAM_BANKS && len >= cells;
-            i++, len -= cells) {
-               gd->bd->bi_dram[i].start = fdtdec_get_number(val, ac);
-               val += ac;
-               gd->bd->bi_dram[i].size = fdtdec_get_number(val, sc);
-               val += sc;
+       ranges = fdt_getprop(blob, node, "ranges", &len);
+       if (!ranges)
+               return -ENOENT;
 
-               debug("DRAM bank %d: start = %08lx, size = %08lx\n",
-                     i, (unsigned long)gd->bd->bi_dram[i].start,
-                     (unsigned long)gd->bd->bi_dram[i].size);
-       }
+       if (len != sizeof(new_ranges))
+               return -EINVAL;
+
+       memcpy(new_ranges, ranges, len);
+
+       base = find_pcie_window_base();
+       if (base == -1)
+               return -ENOENT;
+
+       new_ranges[2] = cpu_to_fdt32(base);
+       new_ranges[4] = new_ranges[2];
+
+       new_ranges[9] = cpu_to_fdt32(base + 0x1000000);
+       new_ranges[11] = new_ranges[9];
+
+       return fdt_setprop_inplace(blob, node, "ranges", new_ranges, len);
 }
 
-int arch_cpu_init(void)
+void reset_cpu(ulong ignored)
 {
-       /* Nothing to do (yet) */
-       return 0;
+       /*
+        * Write magic number of 0x1d1e to North Bridge Warm Reset register
+        * to trigger warm reset
+        */
+       writel(MVEBU_NB_WARM_RST_MAGIC_NUM, MVEBU_NB_WARM_RST_REG);
 }
 
-int arch_early_init_r(void)
+/*
+ * get_ref_clk
+ *
+ * return: reference clock in MHz (25 or 40)
+ */
+u32 get_ref_clk(void)
 {
-       struct udevice *dev;
-       int ret;
-
-       /* Call the comphy code via the MISC uclass driver */
-       ret = uclass_get_device(UCLASS_MISC, 0, &dev);
-       if (ret) {
-               debug("COMPHY init failed: %d\n", ret);
-               return -ENODEV;
-       }
+       u32 regval;
 
-       /* Cause the SATA device to do its early init */
-       uclass_first_device(UCLASS_AHCI, &dev);
+       regval = (readl(MVEBU_TEST_PIN_LATCH_N) & MVEBU_XTAL_MODE_MASK) >>
+               MVEBU_XTAL_MODE_OFFS;
 
-       return 0;
+       if (regval == MVEBU_XTAL_CLOCK_25MHZ)
+               return 25;
+       else
+               return 40;
 }