mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-12 07:57:21 +00:00
0678941ae5
This patch fills the MMU map for DDR at run time based on information read from Device Tree or automatically detected from static configuration. The patch is needed because for systems which has for example 1GB of memory but MMU map is 2GB there could be spurious accesses which was seen in past when mapping is not fitting with actual memory installed. Signed-off-by: Nitin Jain <nitin.jain@xilinx.com> Signed-off-by: Siva Durga Prasad Paladugu <sivadur@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
264 lines
5.9 KiB
C
264 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2014 - 2015 Xilinx, Inc.
|
|
* Michal Simek <michal.simek@xilinx.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/arch/sys_proto.h>
|
|
#include <asm/armv8/mmu.h>
|
|
#include <asm/io.h>
|
|
|
|
#define ZYNQ_SILICON_VER_MASK 0xF000
|
|
#define ZYNQ_SILICON_VER_SHIFT 12
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/*
|
|
* Number of filled static entries and also the first empty
|
|
* slot in zynqmp_mem_map.
|
|
*/
|
|
#define ZYNQMP_MEM_MAP_USED 4
|
|
|
|
#if !defined(CONFIG_ZYNQMP_NO_DDR)
|
|
#define DRAM_BANKS CONFIG_NR_DRAM_BANKS
|
|
#else
|
|
#define DRAM_BANKS 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_DEFINE_TCM_OCM_MMAP)
|
|
#define TCM_MAP 1
|
|
#else
|
|
#define TCM_MAP 0
|
|
#endif
|
|
|
|
/* +1 is end of list which needs to be empty */
|
|
#define ZYNQMP_MEM_MAP_MAX (ZYNQMP_MEM_MAP_USED + DRAM_BANKS + TCM_MAP + 1)
|
|
|
|
static struct mm_region zynqmp_mem_map[ZYNQMP_MEM_MAP_MAX] = {
|
|
{
|
|
.virt = 0x80000000UL,
|
|
.phys = 0x80000000UL,
|
|
.size = 0x70000000UL,
|
|
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
|
|
PTE_BLOCK_NON_SHARE |
|
|
PTE_BLOCK_PXN | PTE_BLOCK_UXN
|
|
}, {
|
|
.virt = 0xf8000000UL,
|
|
.phys = 0xf8000000UL,
|
|
.size = 0x07e00000UL,
|
|
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
|
|
PTE_BLOCK_NON_SHARE |
|
|
PTE_BLOCK_PXN | PTE_BLOCK_UXN
|
|
}, {
|
|
.virt = 0x400000000UL,
|
|
.phys = 0x400000000UL,
|
|
.size = 0x400000000UL,
|
|
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
|
|
PTE_BLOCK_NON_SHARE |
|
|
PTE_BLOCK_PXN | PTE_BLOCK_UXN
|
|
}, {
|
|
.virt = 0x1000000000UL,
|
|
.phys = 0x1000000000UL,
|
|
.size = 0xf000000000UL,
|
|
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
|
|
PTE_BLOCK_NON_SHARE |
|
|
PTE_BLOCK_PXN | PTE_BLOCK_UXN
|
|
}
|
|
};
|
|
|
|
void mem_map_fill(void)
|
|
{
|
|
int banks = ZYNQMP_MEM_MAP_USED;
|
|
|
|
#if defined(CONFIG_DEFINE_TCM_OCM_MMAP)
|
|
zynqmp_mem_map[banks].virt = 0xffe00000UL;
|
|
zynqmp_mem_map[banks].phys = 0xffe00000UL;
|
|
zynqmp_mem_map[banks].size = 0x00200000UL;
|
|
zynqmp_mem_map[banks].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
|
PTE_BLOCK_INNER_SHARE;
|
|
banks = banks + 1;
|
|
#endif
|
|
|
|
#if !defined(CONFIG_ZYNQMP_NO_DDR)
|
|
for (int i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
|
|
/* Zero size means no more DDR that's this is end */
|
|
if (!gd->bd->bi_dram[i].size)
|
|
break;
|
|
|
|
zynqmp_mem_map[banks].virt = gd->bd->bi_dram[i].start;
|
|
zynqmp_mem_map[banks].phys = gd->bd->bi_dram[i].start;
|
|
zynqmp_mem_map[banks].size = gd->bd->bi_dram[i].size;
|
|
zynqmp_mem_map[banks].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
|
|
PTE_BLOCK_INNER_SHARE;
|
|
banks = banks + 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct mm_region *mem_map = zynqmp_mem_map;
|
|
|
|
u64 get_page_table_size(void)
|
|
{
|
|
return 0x14000;
|
|
}
|
|
|
|
#ifdef CONFIG_SYS_MEM_RSVD_FOR_MMU
|
|
int reserve_mmu(void)
|
|
{
|
|
initialize_tcm(TCM_LOCK);
|
|
memset((void *)ZYNQMP_TCM_BASE_ADDR, 0, ZYNQMP_TCM_SIZE);
|
|
gd->arch.tlb_size = PGTABLE_SIZE;
|
|
gd->arch.tlb_addr = ZYNQMP_TCM_BASE_ADDR;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static unsigned int zynqmp_get_silicon_version_secure(void)
|
|
{
|
|
u32 ver;
|
|
|
|
ver = readl(&csu_base->version);
|
|
ver &= ZYNQMP_SILICON_VER_MASK;
|
|
ver >>= ZYNQMP_SILICON_VER_SHIFT;
|
|
|
|
return ver;
|
|
}
|
|
|
|
unsigned int zynqmp_get_silicon_version(void)
|
|
{
|
|
if (current_el() == 3)
|
|
return zynqmp_get_silicon_version_secure();
|
|
|
|
gd->cpu_clk = get_tbclk();
|
|
|
|
switch (gd->cpu_clk) {
|
|
case 0 ... 1000000:
|
|
return ZYNQMP_CSU_VERSION_VELOCE;
|
|
case 50000000:
|
|
return ZYNQMP_CSU_VERSION_QEMU;
|
|
case 4000000:
|
|
return ZYNQMP_CSU_VERSION_EP108;
|
|
}
|
|
|
|
return ZYNQMP_CSU_VERSION_SILICON;
|
|
}
|
|
|
|
#define ZYNQMP_MMIO_READ 0xC2000014
|
|
#define ZYNQMP_MMIO_WRITE 0xC2000013
|
|
|
|
int __maybe_unused invoke_smc(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2,
|
|
u32 arg3, u32 *ret_payload)
|
|
{
|
|
/*
|
|
* Added SIP service call Function Identifier
|
|
* Make sure to stay in x0 register
|
|
*/
|
|
struct pt_regs regs;
|
|
|
|
regs.regs[0] = pm_api_id;
|
|
regs.regs[1] = ((u64)arg1 << 32) | arg0;
|
|
regs.regs[2] = ((u64)arg3 << 32) | arg2;
|
|
|
|
smc_call(®s);
|
|
|
|
if (ret_payload != NULL) {
|
|
ret_payload[0] = (u32)regs.regs[0];
|
|
ret_payload[1] = upper_32_bits(regs.regs[0]);
|
|
ret_payload[2] = (u32)regs.regs[1];
|
|
ret_payload[3] = upper_32_bits(regs.regs[1]);
|
|
ret_payload[4] = (u32)regs.regs[2];
|
|
}
|
|
|
|
return regs.regs[0];
|
|
}
|
|
|
|
#define ZYNQMP_SIP_SVC_GET_API_VERSION 0xC2000001
|
|
|
|
#define ZYNQMP_PM_VERSION_MAJOR 0
|
|
#define ZYNQMP_PM_VERSION_MINOR 3
|
|
#define ZYNQMP_PM_VERSION_MAJOR_SHIFT 16
|
|
#define ZYNQMP_PM_VERSION_MINOR_MASK 0xFFFF
|
|
|
|
#define ZYNQMP_PM_VERSION \
|
|
((ZYNQMP_PM_VERSION_MAJOR << ZYNQMP_PM_VERSION_MAJOR_SHIFT) | \
|
|
ZYNQMP_PM_VERSION_MINOR)
|
|
|
|
#if defined(CONFIG_CLK_ZYNQMP)
|
|
void zynqmp_pmufw_version(void)
|
|
{
|
|
int ret;
|
|
u32 ret_payload[PAYLOAD_ARG_CNT];
|
|
u32 pm_api_version;
|
|
|
|
ret = invoke_smc(ZYNQMP_SIP_SVC_GET_API_VERSION, 0, 0, 0, 0,
|
|
ret_payload);
|
|
pm_api_version = ret_payload[1];
|
|
|
|
if (ret)
|
|
panic("PMUFW is not found - Please load it!\n");
|
|
|
|
printf("PMUFW:\tv%d.%d\n",
|
|
pm_api_version >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
|
|
pm_api_version & ZYNQMP_PM_VERSION_MINOR_MASK);
|
|
|
|
if (pm_api_version < ZYNQMP_PM_VERSION)
|
|
panic("PMUFW version error. Expected: v%d.%d\n",
|
|
ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
|
|
}
|
|
#endif
|
|
|
|
static int zynqmp_mmio_rawwrite(const u32 address,
|
|
const u32 mask,
|
|
const u32 value)
|
|
{
|
|
u32 data;
|
|
u32 value_local = value;
|
|
|
|
zynqmp_mmio_read(address, &data);
|
|
data &= ~mask;
|
|
value_local &= mask;
|
|
value_local |= data;
|
|
writel(value_local, (ulong)address);
|
|
return 0;
|
|
}
|
|
|
|
static int zynqmp_mmio_rawread(const u32 address, u32 *value)
|
|
{
|
|
*value = readl((ulong)address);
|
|
return 0;
|
|
}
|
|
|
|
int zynqmp_mmio_write(const u32 address,
|
|
const u32 mask,
|
|
const u32 value)
|
|
{
|
|
if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3)
|
|
return zynqmp_mmio_rawwrite(address, mask, value);
|
|
else
|
|
return invoke_smc(ZYNQMP_MMIO_WRITE, address, mask,
|
|
value, 0, NULL);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
int zynqmp_mmio_read(const u32 address, u32 *value)
|
|
{
|
|
u32 ret_payload[PAYLOAD_ARG_CNT];
|
|
u32 ret;
|
|
|
|
if (!value)
|
|
return -EINVAL;
|
|
|
|
if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
|
|
ret = zynqmp_mmio_rawread(address, value);
|
|
} else {
|
|
ret = invoke_smc(ZYNQMP_MMIO_READ, address, 0, 0,
|
|
0, ret_payload);
|
|
*value = ret_payload[1];
|
|
}
|
|
|
|
return ret;
|
|
}
|