uboot/arch/arm/mach-mvebu/armada3700/cpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Stefan Roese <sr@denx.de>
   4 * Copyright (C) 2020 Marek Behun <marek.behun@nic.cz>
   5 */
   6
   7#include <common.h>
   8#include <cpu_func.h>
   9#include <dm.h>
  10#include <fdtdec.h>
  11#include <init.h>
  12#include <asm/global_data.h>
  13#include <linux/bitops.h>
  14#include <linux/libfdt.h>
  15#include <asm/io.h>
  16#include <asm/system.h>
  17#include <asm/arch/cpu.h>
  18#include <asm/arch/soc.h>
  19#include <asm/armv8/mmu.h>
  20#include <sort.h>
  21
  22/* Armada 3700 */
  23#define MVEBU_GPIO_NB_REG_BASE          (MVEBU_REGISTER(0x13800))
  24
  25#define MVEBU_TEST_PIN_LATCH_N          (MVEBU_GPIO_NB_REG_BASE + 0x8)
  26#define MVEBU_XTAL_MODE_MASK            BIT(9)
  27#define MVEBU_XTAL_MODE_OFFS            9
  28#define MVEBU_XTAL_CLOCK_25MHZ          0x0
  29#define MVEBU_XTAL_CLOCK_40MHZ          0x1
  30
  31#define MVEBU_NB_WARM_RST_REG           (MVEBU_GPIO_NB_REG_BASE + 0x40)
  32#define MVEBU_NB_WARM_RST_MAGIC_NUM     0x1d1e
  33
  34/* Armada 3700 CPU Address Decoder registers */
  35#define MVEBU_CPU_DEC_WIN_REG_BASE      (size_t)(MVEBU_REGISTER(0xcf00))
  36#define MVEBU_CPU_DEC_WIN_CTRL(w) \
  37        (MVEBU_CPU_DEC_WIN_REG_BASE + ((w) << 4))
  38#define MVEBU_CPU_DEC_WIN_CTRL_EN       BIT(0)
  39#define MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK 0xf
  40#define MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS 4
  41#define MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM 0
  42#define MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE 2
  43#define MVEBU_CPU_DEC_WIN_SIZE(w)       (MVEBU_CPU_DEC_WIN_CTRL(w) + 0x4)
  44#define MVEBU_CPU_DEC_WIN_BASE(w)       (MVEBU_CPU_DEC_WIN_CTRL(w) + 0x8)
  45#define MVEBU_CPU_DEC_WIN_REMAP(w)      (MVEBU_CPU_DEC_WIN_CTRL(w) + 0xc)
  46#define MVEBU_CPU_DEC_WIN_GRANULARITY   16
  47#define MVEBU_CPU_DEC_WINS              5
  48
  49#define MAX_MEM_MAP_REGIONS             (MVEBU_CPU_DEC_WINS + 2)
  50
  51#define A3700_PTE_BLOCK_NORMAL \
  52        (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE)
  53#define A3700_PTE_BLOCK_DEVICE \
  54        (PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE)
  55
  56#define PCIE_PATH                       "/soc/pcie@d0070000"
  57
  58DECLARE_GLOBAL_DATA_PTR;
  59
  60static struct mm_region mvebu_mem_map[MAX_MEM_MAP_REGIONS] = {
  61        {
  62                /*
  63                 * SRAM, MMIO regions
  64                 * Don't remove this, a3700_build_mem_map needs it.
  65                 */
  66                .phys = SOC_REGS_PHY_BASE,
  67                .virt = SOC_REGS_PHY_BASE,
  68                .size = 0x02000000UL,   /* 32MiB internal registers */
  69                .attrs = A3700_PTE_BLOCK_DEVICE
  70        },
  71};
  72
  73struct mm_region *mem_map = mvebu_mem_map;
  74
  75static int get_cpu_dec_win(int win, u32 *tgt, u32 *base, u32 *size)
  76{
  77        u32 reg;
  78
  79        reg = readl(MVEBU_CPU_DEC_WIN_CTRL(win));
  80        if (!(reg & MVEBU_CPU_DEC_WIN_CTRL_EN))
  81                return -1;
  82
  83        if (tgt) {
  84                reg >>= MVEBU_CPU_DEC_WIN_CTRL_TGT_OFFS;
  85                reg &= MVEBU_CPU_DEC_WIN_CTRL_TGT_MASK;
  86                *tgt = reg;
  87        }
  88
  89        if (base) {
  90                reg = readl(MVEBU_CPU_DEC_WIN_BASE(win));
  91                *base = reg << MVEBU_CPU_DEC_WIN_GRANULARITY;
  92        }
  93
  94        if (size) {
  95                /*
  96                 * Window size is encoded as the number of 1s from LSB to MSB,
  97                 * followed by 0s. The number of 1s specifies the size in 64 KiB
  98                 * granularity.
  99                 */
 100                reg = readl(MVEBU_CPU_DEC_WIN_SIZE(win));
 101                *size = ((reg + 1) << MVEBU_CPU_DEC_WIN_GRANULARITY);
 102        }
 103
 104        return 0;
 105}
 106
 107/*
 108 * Builds mem_map according to CPU Address Decoder settings, which were set by
 109 * the TIMH image on the Cortex-M3 secure processor, or by ARM Trusted Firmware
 110 */
 111static void build_mem_map(void)
 112{
 113        int win, region;
 114
 115        region = 1;
 116        for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
 117                u32 base, tgt, size;
 118                u64 attrs;
 119
 120                /* skip disabled windows */
 121                if (get_cpu_dec_win(win, &tgt, &base, &size))
 122                        continue;
 123
 124                if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
 125                        attrs = A3700_PTE_BLOCK_NORMAL;
 126                else if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
 127                        attrs = A3700_PTE_BLOCK_DEVICE;
 128                else
 129                        /* skip windows with other targets */
 130                        continue;
 131
 132                mvebu_mem_map[region].phys = base;
 133                mvebu_mem_map[region].virt = base;
 134                mvebu_mem_map[region].size = size;
 135                mvebu_mem_map[region].attrs = attrs;
 136                ++region;
 137        }
 138
 139        /* add list terminator */
 140        mvebu_mem_map[region].size = 0;
 141        mvebu_mem_map[region].attrs = 0;
 142}
 143
 144void enable_caches(void)
 145{
 146        build_mem_map();
 147
 148        icache_enable();
 149        dcache_enable();
 150}
 151
 152int a3700_dram_init(void)
 153{
 154        int win;
 155
 156        gd->ram_size = 0;
 157        for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
 158                u32 base, tgt, size;
 159
 160                /* skip disabled windows */
 161                if (get_cpu_dec_win(win, &tgt, &base, &size))
 162                        continue;
 163
 164                /* skip non-DRAM windows */
 165                if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
 166                        continue;
 167
 168                /*
 169                 * It is possible that one image was built for boards with
 170                 * different RAM sizes, for example 512 MiB and 1 GiB.
 171                 * We therefore try to determine the actual RAM size in the
 172                 * window with get_ram_size.
 173                 */
 174                gd->ram_size += get_ram_size((void *)(size_t)base, size);
 175        }
 176
 177        return 0;
 178}
 179
 180struct a3700_dram_window {
 181        size_t base, size;
 182};
 183
 184static int dram_win_cmp(const void *a, const void *b)
 185{
 186        size_t ab, bb;
 187
 188        ab = ((const struct a3700_dram_window *)a)->base;
 189        bb = ((const struct a3700_dram_window *)b)->base;
 190
 191        if (ab < bb)
 192                return -1;
 193        else if (ab > bb)
 194                return 1;
 195        else
 196                return 0;
 197}
 198
 199int a3700_dram_init_banksize(void)
 200{
 201        struct a3700_dram_window dram_wins[MVEBU_CPU_DEC_WINS];
 202        int bank, win, ndram_wins;
 203        u32 last_end;
 204        size_t size;
 205
 206        ndram_wins = 0;
 207        for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
 208                u32 base, tgt, size;
 209
 210                /* skip disabled windows */
 211                if (get_cpu_dec_win(win, &tgt, &base, &size))
 212                        continue;
 213
 214                /* skip non-DRAM windows */
 215                if (tgt != MVEBU_CPU_DEC_WIN_CTRL_TGT_DRAM)
 216                        continue;
 217
 218                dram_wins[win].base = base;
 219                dram_wins[win].size = size;
 220                ++ndram_wins;
 221        }
 222
 223        qsort(dram_wins, ndram_wins, sizeof(dram_wins[0]), dram_win_cmp);
 224
 225        bank = 0;
 226        last_end = -1;
 227
 228        for (win = 0; win < ndram_wins; ++win) {
 229                /* again determining actual RAM size as in a3700_dram_init */
 230                size = get_ram_size((void *)dram_wins[win].base,
 231                                    dram_wins[win].size);
 232
 233                /*
 234                 * Check if previous window ends as the current starts. If yes,
 235                 * merge these windows into one "bank". This is possible by this
 236                 * simple check thanks to mem_map regions being qsorted in
 237                 * build_mem_map.
 238                 */
 239                if (last_end == dram_wins[win].base) {
 240                        gd->bd->bi_dram[bank - 1].size += size;
 241                        last_end += size;
 242                } else {
 243                        if (bank == CONFIG_NR_DRAM_BANKS) {
 244                                printf("Need more CONFIG_NR_DRAM_BANKS\n");
 245                                return -ENOBUFS;
 246                        }
 247
 248                        gd->bd->bi_dram[bank].start = dram_wins[win].base;
 249                        gd->bd->bi_dram[bank].size = size;
 250                        last_end = dram_wins[win].base + size;
 251                        ++bank;
 252                }
 253        }
 254
 255        /*
 256         * If there is more place for DRAM BANKS definitions than needed, fill
 257         * the rest with zeros.
 258         */
 259        for (; bank < CONFIG_NR_DRAM_BANKS; ++bank) {
 260                gd->bd->bi_dram[bank].start = 0;
 261                gd->bd->bi_dram[bank].size = 0;
 262        }
 263
 264        return 0;
 265}
 266
 267static u32 find_pcie_window_base(void)
 268{
 269        int win;
 270
 271        for (win = 0; win < MVEBU_CPU_DEC_WINS; ++win) {
 272                u32 base, tgt;
 273
 274                /* skip disabled windows */
 275                if (get_cpu_dec_win(win, &tgt, &base, NULL))
 276                        continue;
 277
 278                if (tgt == MVEBU_CPU_DEC_WIN_CTRL_TGT_PCIE)
 279                        return base;
 280        }
 281
 282        return -1;
 283}
 284
 285int a3700_fdt_fix_pcie_regions(void *blob)
 286{
 287        u32 new_ranges[14], base;
 288        const u32 *ranges;
 289        int node, len;
 290
 291        node = fdt_path_offset(blob, PCIE_PATH);
 292        if (node < 0)
 293                return node;
 294
 295        ranges = fdt_getprop(blob, node, "ranges", &len);
 296        if (!ranges)
 297                return -ENOENT;
 298
 299        if (len != sizeof(new_ranges))
 300                return -EINVAL;
 301
 302        memcpy(new_ranges, ranges, len);
 303
 304        base = find_pcie_window_base();
 305        if (base == -1)
 306                return -ENOENT;
 307
 308        new_ranges[2] = cpu_to_fdt32(base);
 309        new_ranges[4] = new_ranges[2];
 310
 311        new_ranges[9] = cpu_to_fdt32(base + 0x1000000);
 312        new_ranges[11] = new_ranges[9];
 313
 314        return fdt_setprop_inplace(blob, node, "ranges", new_ranges, len);
 315}
 316
 317void reset_cpu(ulong ignored)
 318{
 319        /*
 320         * Write magic number of 0x1d1e to North Bridge Warm Reset register
 321         * to trigger warm reset
 322         */
 323        writel(MVEBU_NB_WARM_RST_MAGIC_NUM, MVEBU_NB_WARM_RST_REG);
 324}
 325
 326/*
 327 * get_ref_clk
 328 *
 329 * return: reference clock in MHz (25 or 40)
 330 */
 331u32 get_ref_clk(void)
 332{
 333        u32 regval;
 334
 335        regval = (readl(MVEBU_TEST_PIN_LATCH_N) & MVEBU_XTAL_MODE_MASK) >>
 336                MVEBU_XTAL_MODE_OFFS;
 337
 338        if (regval == MVEBU_XTAL_CLOCK_25MHZ)
 339                return 25;
 340        else
 341                return 40;
 342}
 343