mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-26 22:52:18 +00:00
cd93d625fd
Move this uncommon header out of the common header. Signed-off-by: Simon Glass <sjg@chromium.org>
341 lines
7.6 KiB
C
341 lines
7.6 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
|
* Copyright (C) 2020 Marek Behun <marek.behun@nic.cz>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <cpu_func.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.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>
|
|
#include <sort.h>
|
|
|
|
/* Armada 3700 */
|
|
#define MVEBU_GPIO_NB_REG_BASE (MVEBU_REGISTER(0x13800))
|
|
|
|
#define MVEBU_TEST_PIN_LATCH_N (MVEBU_GPIO_NB_REG_BASE + 0x8)
|
|
#define MVEBU_XTAL_MODE_MASK BIT(9)
|
|
#define MVEBU_XTAL_MODE_OFFS 9
|
|
#define MVEBU_XTAL_CLOCK_25MHZ 0x0
|
|
#define MVEBU_XTAL_CLOCK_40MHZ 0x1
|
|
|
|
#define MVEBU_NB_WARM_RST_REG (MVEBU_GPIO_NB_REG_BASE + 0x40)
|
|
#define MVEBU_NB_WARM_RST_MAGIC_NUM 0x1d1e
|
|
|
|
/* 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
|
|
* 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 = A3700_PTE_BLOCK_DEVICE
|
|
},
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
static void build_mem_map(void)
|
|
{
|
|
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 enable_caches(void)
|
|
{
|
|
build_mem_map();
|
|
|
|
icache_enable();
|
|
dcache_enable();
|
|
}
|
|
|
|
int a3700_dram_init(void)
|
|
{
|
|
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);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct a3700_dram_window {
|
|
size_t base, size;
|
|
};
|
|
|
|
static int dram_win_cmp(const void *a, const void *b)
|
|
{
|
|
size_t ab, bb;
|
|
|
|
ab = ((const struct a3700_dram_window *)a)->base;
|
|
bb = ((const struct a3700_dram_window *)b)->base;
|
|
|
|
if (ab < bb)
|
|
return -1;
|
|
else if (ab > bb)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int a3700_dram_init_banksize(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
static u32 find_pcie_window_base(void)
|
|
{
|
|
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;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int a3700_fdt_fix_pcie_regions(void *blob)
|
|
{
|
|
u32 new_ranges[14], base;
|
|
const u32 *ranges;
|
|
int node, len;
|
|
|
|
node = fdt_path_offset(blob, PCIE_PATH);
|
|
if (node < 0)
|
|
return node;
|
|
|
|
ranges = fdt_getprop(blob, node, "ranges", &len);
|
|
if (!ranges)
|
|
return -ENOENT;
|
|
|
|
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);
|
|
}
|
|
|
|
void reset_cpu(ulong ignored)
|
|
{
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* get_ref_clk
|
|
*
|
|
* return: reference clock in MHz (25 or 40)
|
|
*/
|
|
u32 get_ref_clk(void)
|
|
{
|
|
u32 regval;
|
|
|
|
regval = (readl(MVEBU_TEST_PIN_LATCH_N) & MVEBU_XTAL_MODE_MASK) >>
|
|
MVEBU_XTAL_MODE_OFFS;
|
|
|
|
if (regval == MVEBU_XTAL_CLOCK_25MHZ)
|
|
return 25;
|
|
else
|
|
return 40;
|
|
}
|