diff options
Diffstat (limited to 'arch/arm/mach-zynqmp')
-rw-r--r-- | arch/arm/mach-zynqmp/Kconfig | 206 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/Makefile | 14 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/aes.c | 58 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/clk.c | 47 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/cpu.c | 226 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/ecc_spl_init.c | 162 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/handoff.c | 106 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/clk.h | 12 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/ecc_spl_init.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/hardware.h | 186 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h | 26 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/sys_proto.h | 57 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/include/mach/zynqmp_aes.h | 34 | ||||
-rwxr-xr-x | arch/arm/mach-zynqmp/mkimage_fit_atf.sh | 240 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/mp.c | 368 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/psu_spl_init.c | 88 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/spl.c | 141 | ||||
-rw-r--r-- | arch/arm/mach-zynqmp/zynqmp.c | 431 |
18 files changed, 2415 insertions, 0 deletions
diff --git a/arch/arm/mach-zynqmp/Kconfig b/arch/arm/mach-zynqmp/Kconfig new file mode 100644 index 00000000000..aea13622b68 --- /dev/null +++ b/arch/arm/mach-zynqmp/Kconfig @@ -0,0 +1,206 @@ +if ARCH_ZYNQMP + +config SYS_BOARD + string "Board name" + default "zynqmp" + +config SYS_VENDOR + string "Vendor name" + default "xilinx" + +config SYS_SOC + default "zynqmp" + +config SYS_MEM_RSVD_FOR_MMU + bool "Reserve memory for MMU Table" + help + If defined this option is used to setup different space for + MMU table than the one which will be allocated during + relocation. + +config BOOT_INIT_FILE + string "boot.bin init register filename" + depends on SPL + default "" + help + Add register writes to boot.bin format (max 256 pairs). + Expect a table of register-value pairs, e.g. "0x12345678 0x4321" + +config PMUFW_INIT_FILE + string "PMU firmware" + depends on SPL + default "" + help + Include external PMUFW (Platform Management Unit FirmWare) to + a Xilinx bootable image (boot.bin). + +config ZYNQMP_SPL_PM_CFG_OBJ_FILE + string "PMU firmware configuration object to load at runtime by SPL" + depends on SPL + help + Path to a binary PMU firmware configuration object to be linked + into U-Boot SPL and loaded at runtime into the PMU firmware. + + The ZynqMP Power Management Unit (PMU) needs a configuration + object for most SoC peripherals to work. To have it loaded by + U-Boot SPL set here the file name (absolute path or relative to + the top source tree) of your configuration, which must be a + binary blob. It will be linked in the SPL binary and loaded + into the PMU firmware by U-Boot SPL during board + initialization. + + Leave this option empty if your PMU firmware has a hard-coded + configuration object or you are loading it by any other means. + +config ZYNQMP_NO_DDR + bool "Disable DDR MMU mapping" + help + This option configures MMU with no DDR to avoid speculative + access to DDR memory where DDR is not present. + +config SPL_ZYNQMP_DRAM_ECC_INIT + bool "Initialize DRAM ECC" + depends on SPL + help + This option initializes all memory to 0xdeadbeef. Must be set if your + memory is of ECC type. + +config SPL_ZYNQMP_DRAM_BANK1_BASE + depends on SPL_ZYNQMP_DRAM_ECC_INIT + hex "DRAM Bank1 address" + default 0x00000000 + help + Start address of DRAM ECC bank1 + +config SPL_ZYNQMP_DRAM_BANK1_LEN + depends on SPL_ZYNQMP_DRAM_ECC_INIT + hex "DRAM Bank1 size" + default 0x80000000 + help + Size in bytes of the DRAM ECC bank1 + +config SPL_ZYNQMP_DRAM_BANK2_BASE + depends on SPL_ZYNQMP_DRAM_ECC_INIT + hex "DRAM Bank2 address" + default 0x800000000 + help + Start address of DRAM ECC bank2 + +config SPL_ZYNQMP_DRAM_BANK2_LEN + depends on SPL_ZYNQMP_DRAM_ECC_INIT + hex "DRAM Bank2 size" + default 0x0 + help + Size in bytes of the DRAM ECC bank2. A null size takes no action. + +config SYS_MALLOC_F_LEN + default 0x600 + +config DEFINE_TCM_OCM_MMAP + bool "Define TCM and OCM memory in MMU Table" + default y if MP + help + This option if enabled defines the TCM and OCM memory and its + memory attributes in MMU table entry. + +config ZYNQMP_PSU_INIT_ENABLED + bool "Include psu_init" + select BOARD_EARLY_INIT_F + help + Include psu_init to full u-boot. + +config SPL_ZYNQMP_PSU_INIT_ENABLED + bool "Include psu_init in SPL" + depends on SPL + default y + select BOARD_EARLY_INIT_F + help + Include psu_init by default in SPL. + +config SPL_ZYNQMP_ALT_BOOTMODE_ENABLED + bool "Overwrite SPL bootmode" + depends on SPL + help + Overwrite bootmode selected via boot mode pins to tell SPL what should + be the next boot device. + +config SPL_ZYNQMP_RESTORE_JTAG + bool "Restore JTAG" + depends on SPL + help + Booting SPL in secure mode causes the CSU to disable the JTAG interface + even if no eFuses were burnt. This option restores the interface if + possible. + +config ZYNQ_SDHCI_MAX_FREQ + default 200000000 + +config SPL_ZYNQMP_ALT_BOOTMODE + hex + default 0x0 if JTAG_MODE + default 0x1 if QSPI_MODE_24BIT + default 0x2 if QSPI_MODE_32BIT + default 0x3 if SD_MODE + default 0x4 if NAND_MODE + default 0x5 if SD_MODE1 + default 0x6 if EMMC_MODE + default 0x7 if USB_MODE + default 0xa if SW_USBHOST_MODE + default 0xb if SW_SATA_MODE + default 0xe if SD1_LSHFT_MODE + +choice + prompt "Boot mode" + depends on SPL_ZYNQMP_ALT_BOOTMODE_ENABLED + default JTAG_MODE + +config JTAG_MODE + bool "JTAG_MODE" + +config QSPI_MODE_24BIT + bool "QSPI_MODE_24BIT" + +config QSPI_MODE_32BIT + bool "QSPI_MODE_32BIT" + +config SD_MODE + bool "SD_MODE" + +config SD_MODE1 + bool "SD_MODE1" + +config NAND_MODE + bool "NAND_MODE" + +config EMMC_MODE + bool "EMMC_MODE" + +config USB_MODE + bool "USB" + +config SW_USBHOST_MODE + bool "SW USBHOST_MODE" + +config SW_SATA_MODE + bool "SW SATA_MODE" + +config SD1_LSHFT_MODE + bool "SD1_LSHFT_MODE" + +endchoice + +config CMD_ZYNQMP + bool "Enable ZynqMP specific commands" + depends on ZYNQMP_FIRMWARE + default y + help + Enable ZynqMP specific commands like "zynqmp secure" + which is used for zynqmp secure image verification. + The secure image is a xilinx specific BOOT.BIN with + either authentication or encryption or both encryption + and authentication feature enabled while generating + BOOT.BIN using Xilinx bootgen tool. + +source "board/xilinx/Kconfig" + +endif diff --git a/arch/arm/mach-zynqmp/Makefile b/arch/arm/mach-zynqmp/Makefile new file mode 100644 index 00000000000..38be1627eeb --- /dev/null +++ b/arch/arm/mach-zynqmp/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2014 - 2015 Xilinx, Inc. +# Michal Simek <michal.simek@amd.com> + +obj-y += aes.o clk.o cpu.o +obj-$(CONFIG_MP) += mp.o +obj-$(CONFIG_SPL_BUILD) += spl.o handoff.o psu_spl_init.o +obj-$(CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT) += ecc_spl_init.o +obj-$(CONFIG_$(SPL_)ZYNQMP_PSU_INIT_ENABLED) += psu_spl_init.o + +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_CMD_ZYNQMP) += zynqmp.o +endif # !CONFIG_SPL_BUILD diff --git a/arch/arm/mach-zynqmp/aes.c b/arch/arm/mach-zynqmp/aes.c new file mode 100644 index 00000000000..9a05fbf9c11 --- /dev/null +++ b/arch/arm/mach-zynqmp/aes.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Xilinx, Inc. + * Siva Durga Prasad Paladugu <siva.durga.paladugu@xilinx.com> + * + * Copyright (C) 2023 Weidmueller Interface GmbH & Co. KG <oss@weidmueller.com> + * Christian Taedcke <christian.taedcke@weidmueller.com> + */ + +#include <mach/zynqmp_aes.h> +#include <linux/errno.h> +#include <asm/arch/sys_proto.h> +#include <cpu_func.h> +#include <memalign.h> +#include <zynqmp_firmware.h> + +int zynqmp_aes_operation(struct zynqmp_aes *aes) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (zynqmp_firmware_version() <= PMUFW_V1_0) + return -ENOENT; + + if (aes->srcaddr && aes->ivaddr && aes->dstaddr) { + flush_dcache_range(aes->srcaddr, + aes->srcaddr + + roundup(aes->len, ARCH_DMA_MINALIGN)); + flush_dcache_range(aes->ivaddr, + aes->ivaddr + + roundup(IV_SIZE, ARCH_DMA_MINALIGN)); + flush_dcache_range(aes->dstaddr, + aes->dstaddr + + roundup(aes->len, ARCH_DMA_MINALIGN)); + } + + if (aes->keysrc == 0) { + if (aes->keyaddr == 0) + return -EINVAL; + + flush_dcache_range(aes->keyaddr, + aes->keyaddr + + roundup(KEY_PTR_LEN, ARCH_DMA_MINALIGN)); + } + + flush_dcache_range((ulong)aes, (ulong)(aes) + + roundup(sizeof(struct zynqmp_aes), ARCH_DMA_MINALIGN)); + + ret = xilinx_pm_request(PM_SECURE_AES, upper_32_bits((ulong)aes), + lower_32_bits((ulong)aes), 0, 0, ret_payload); + if (ret || ret_payload[1]) { + printf("Failed: AES op status:0x%x, errcode:0x%x\n", + ret, ret_payload[1]); + return -EIO; + } + + return 0; +} diff --git a/arch/arm/mach-zynqmp/clk.c b/arch/arm/mach-zynqmp/clk.c new file mode 100644 index 00000000000..9b573b1746a --- /dev/null +++ b/arch/arm/mach-zynqmp/clk.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#include <init.h> +#include <time.h> +#include <asm/arch/clk.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +unsigned long zynqmp_get_system_timer_freq(void) +{ + u32 ver = zynqmp_get_silicon_version(); + + switch (ver) { + case ZYNQMP_CSU_VERSION_QEMU: + return 50000000; + } + + return 100000000; +} + +#ifdef CONFIG_CLOCKS +/** + * set_cpu_clk_info() - Initialize clock framework + * Always returns zero. + * + * This function is called from common code after relocation and sets up the + * clock framework. The framework must not be used before this function had been + * called. + */ +int set_cpu_clk_info(void) +{ + gd->cpu_clk = get_tbclk(); + + gd->bd->bi_arm_freq = gd->cpu_clk / 1000000; + + gd->bd->bi_dsp_freq = 0; + + return 0; +} +#endif diff --git a/arch/arm/mach-zynqmp/cpu.c b/arch/arm/mach-zynqmp/cpu.c new file mode 100644 index 00000000000..07668c94689 --- /dev/null +++ b/arch/arm/mach-zynqmp/cpu.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#include <init.h> +#include <time.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> +#include <asm/armv8/mmu.h> +#include <asm/cache.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <zynqmp_firmware.h> +#include <asm/cache.h> +#include <dm/platdata.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; +} + +#if defined(CONFIG_SYS_MEM_RSVD_FOR_MMU) || defined(CONFIG_DEFINE_TCM_OCM_MMAP) +void tcm_init(u8 mode) +{ + puts("WARNING: Initializing TCM overwrites TCM content\n"); + initialize_tcm(mode); + memset((void *)ZYNQMP_TCM_BASE_ADDR, 0, ZYNQMP_TCM_SIZE); +} +#endif + +#ifdef CONFIG_SYS_MEM_RSVD_FOR_MMU +int arm_reserve_mmu(void) +{ + tcm_init(TCM_LOCK); + 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 50000000: + return ZYNQMP_CSU_VERSION_QEMU; + } + + return ZYNQMP_CSU_VERSION_SILICON; +} + +static int zynqmp_mmio_rawwrite(const u32 address, + const u32 mask, + const u32 value) +{ + u32 data; + u32 value_local = value; + int ret; + + ret = zynqmp_mmio_read(address, &data); + if (ret) + return ret; + + 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); +#if defined(CONFIG_ZYNQMP_FIRMWARE) + else + return xilinx_pm_request(PM_MMIO_WRITE, address, mask, + value, 0, NULL); +#endif + + return -EINVAL; +} + +int zynqmp_mmio_read(const u32 address, u32 *value) +{ + u32 ret = -EINVAL; + + if (!value) + return ret; + + if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) { + ret = zynqmp_mmio_rawread(address, value); + } +#if defined(CONFIG_ZYNQMP_FIRMWARE) + else { + u32 ret_payload[PAYLOAD_ARG_CNT]; + + ret = xilinx_pm_request(PM_MMIO_READ, address, 0, 0, + 0, ret_payload); + *value = ret_payload[1]; + } +#endif + + return ret; +} + +U_BOOT_DRVINFO(soc_xilinx_zynqmp) = { + .name = "soc_xilinx_zynqmp", +}; diff --git a/arch/arm/mach-zynqmp/ecc_spl_init.c b/arch/arm/mach-zynqmp/ecc_spl_init.c new file mode 100644 index 00000000000..1eef1078951 --- /dev/null +++ b/arch/arm/mach-zynqmp/ecc_spl_init.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright(c) 2015 - 2020 Xilinx, Inc. + * + * Jorge Ramirez-Ortiz <jorge@foundries.io> + */ + +#include <cpu_func.h> +#include <asm/arch/hardware.h> +#include <asm/arch/ecc_spl_init.h> +#include <asm/io.h> +#include <linux/delay.h> + +#define ZDMA_TRANSFER_MAX_LEN (0x3FFFFFFFU - 7U) +#define ZDMA_CH_STATUS ((ADMA_CH0_BASEADDR) + 0x0000011CU) +#define ZDMA_CH_STATUS_STATE_MASK 0x00000003U +#define ZDMA_CH_STATUS_STATE_DONE 0x00000000U +#define ZDMA_CH_STATUS_STATE_ERR 0x00000003U +#define ZDMA_CH_CTRL0 ((ADMA_CH0_BASEADDR) + 0x00000110U) +#define ZDMA_CH_CTRL0_POINT_TYPE_MASK (u32)0x00000040U +#define ZDMA_CH_CTRL0_POINT_TYPE_NORMAL (u32)0x00000000U +#define ZDMA_CH_CTRL0_MODE_MASK (u32)0x00000030U +#define ZDMA_CH_CTRL0_MODE_WR_ONLY (u32)0x00000010U +#define ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT ((ADMA_CH0_BASEADDR) + 0x00000188U) +#define ZDMA_CH_WR_ONLY_WORD0 ((ADMA_CH0_BASEADDR) + 0x00000148U) +#define ZDMA_CH_WR_ONLY_WORD1 ((ADMA_CH0_BASEADDR) + 0x0000014CU) +#define ZDMA_CH_WR_ONLY_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000150U) +#define ZDMA_CH_WR_ONLY_WORD3 ((ADMA_CH0_BASEADDR) + 0x00000154U) +#define ZDMA_CH_DST_DSCR_WORD0 ((ADMA_CH0_BASEADDR) + 0x00000138U) +#define ZDMA_CH_DST_DSCR_WORD0_LSB_MASK 0xFFFFFFFFU +#define ZDMA_CH_DST_DSCR_WORD1 ((ADMA_CH0_BASEADDR) + 0x0000013CU) +#define ZDMA_CH_DST_DSCR_WORD1_MSB_MASK 0x0001FFFFU +#define ZDMA_CH_SRC_DSCR_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000130U) +#define ZDMA_CH_DST_DSCR_WORD2 ((ADMA_CH0_BASEADDR) + 0x00000140U) +#define ZDMA_CH_CTRL2 ((ADMA_CH0_BASEADDR) + 0x00000200U) +#define ZDMA_CH_CTRL2_EN_MASK 0x00000001U +#define ZDMA_CH_ISR ((ADMA_CH0_BASEADDR) + 0x00000100U) +#define ZDMA_CH_ISR_DMA_DONE_MASK 0x00000400U +#define ECC_INIT_VAL_WORD 0xDEADBEEFU + +#define ZDMA_IDLE_TIMEOUT_USEC 1000000 +#define ZDMA_DONE_TIMEOUT_USEC 5000000 + +static void ecc_zdma_restore(void) +{ + /* Restore reset values for the DMA registers used */ + writel(ZDMA_CH_CTRL0, 0x00000080U); + writel(ZDMA_CH_WR_ONLY_WORD0, 0x00000000U); + writel(ZDMA_CH_WR_ONLY_WORD1, 0x00000000U); + writel(ZDMA_CH_WR_ONLY_WORD2, 0x00000000U); + writel(ZDMA_CH_WR_ONLY_WORD3, 0x00000000U); + writel(ZDMA_CH_DST_DSCR_WORD0, 0x00000000U); + writel(ZDMA_CH_DST_DSCR_WORD1, 0x00000000U); + writel(ZDMA_CH_SRC_DSCR_WORD2, 0x00000000U); + writel(ZDMA_CH_DST_DSCR_WORD2, 0x00000000U); + writel(ZDMA_CH_CTRL0_TOTAL_BYTE_COUNT, 0x00000000U); +} + +static void ecc_dram_bank_init(u64 addr, u64 len) +{ + bool retry = true; + u32 timeout; + u64 bytes; + u32 size; + u64 src; + u32 reg; + + if (!len) + return; +retry: + bytes = len; + src = addr; + ecc_zdma_restore(); + while (bytes > 0) { + size = bytes > ZDMA_TRANSFER_MAX_LEN ? + ZDMA_TRANSFER_MAX_LEN : (u32)bytes; + + /* Wait until the DMA is in idle state */ + timeout = ZDMA_IDLE_TIMEOUT_USEC; + do { + udelay(1); + reg = readl(ZDMA_CH_STATUS); + reg &= ZDMA_CH_STATUS_STATE_MASK; + if (!timeout--) { + puts("error, ECC DMA failed to idle\n"); + goto done; + } + + } while ((reg != ZDMA_CH_STATUS_STATE_DONE) && + (reg != ZDMA_CH_STATUS_STATE_ERR)); + + /* Enable Simple (Write Only) Mode */ + reg = readl(ZDMA_CH_CTRL0); + reg &= (ZDMA_CH_CTRL0_POINT_TYPE_MASK | + ZDMA_CH_CTRL0_MODE_MASK); + reg |= (ZDMA_CH_CTRL0_POINT_TYPE_NORMAL | + ZDMA_CH_CTRL0_MODE_WR_ONLY); + writel(reg, ZDMA_CH_CTRL0); + + /* Fill in the data to be written */ + writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD0); + writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD1); + writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD2); + writel(ECC_INIT_VAL_WORD, ZDMA_CH_WR_ONLY_WORD3); + + /* Write Destination Address */ + writel((u32)(src & ZDMA_CH_DST_DSCR_WORD0_LSB_MASK), + ZDMA_CH_DST_DSCR_WORD0); + writel((u32)((src >> 32) & ZDMA_CH_DST_DSCR_WORD1_MSB_MASK), + ZDMA_CH_DST_DSCR_WORD1); + + /* Size to be Transferred. Recommended to set both src and dest sizes */ + writel(size, ZDMA_CH_SRC_DSCR_WORD2); + writel(size, ZDMA_CH_DST_DSCR_WORD2); + + /* DMA Enable */ + reg = readl(ZDMA_CH_CTRL2); + reg |= ZDMA_CH_CTRL2_EN_MASK; + writel(reg, ZDMA_CH_CTRL2); + + /* Check the status of the transfer by polling on DMA Done */ + timeout = ZDMA_DONE_TIMEOUT_USEC; + do { + udelay(1); + reg = readl(ZDMA_CH_ISR); + reg &= ZDMA_CH_ISR_DMA_DONE_MASK; + if (!timeout--) { + puts("error, ECC DMA timeout\n"); + goto done; + } + } while (reg != ZDMA_CH_ISR_DMA_DONE_MASK); + + /* Clear DMA status */ + reg = readl(ZDMA_CH_ISR); + reg |= ZDMA_CH_ISR_DMA_DONE_MASK; + writel(ZDMA_CH_ISR_DMA_DONE_MASK, ZDMA_CH_ISR); + + /* Read the channel status for errors */ + reg = readl(ZDMA_CH_STATUS); + if (reg == ZDMA_CH_STATUS_STATE_ERR) { + if (retry) { + retry = false; + goto retry; + } + puts("error, ECC DMA error\n"); + break; + } + + bytes -= size; + src += size; + } +done: + ecc_zdma_restore(); +} + +void zynqmp_ecc_init(void) +{ + ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK1_BASE, + CONFIG_SPL_ZYNQMP_DRAM_BANK1_LEN); + ecc_dram_bank_init(CONFIG_SPL_ZYNQMP_DRAM_BANK2_BASE, + CONFIG_SPL_ZYNQMP_DRAM_BANK2_LEN); +} diff --git a/arch/arm/mach-zynqmp/handoff.c b/arch/arm/mach-zynqmp/handoff.c new file mode 100644 index 00000000000..b007307e1f3 --- /dev/null +++ b/arch/arm/mach-zynqmp/handoff.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2016 - 2017 Xilinx, Inc. + * + * Michal Simek <michal.simek@amd.com> + */ + +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> +#include <spl.h> + +/* + * atfhandoffparams + * Parameter bitfield encoding + * ----------------------------------------------------------------------------- + * Exec State 0 0 -> Aarch64, 1-> Aarch32 + * endianness 1 0 -> LE, 1 -> BE + * secure (TZ) 2 0 -> Non secure, 1 -> secure + * EL 3:4 00 -> EL0, 01 -> EL1, 10 -> EL2, 11 -> EL3 + * CPU# 5:6 00 -> A53_0, 01 -> A53_1, 10 -> A53_2, 11 -> A53_3 + */ + +#define FSBL_FLAGS_ESTATE_SHIFT 0 +#define FSBL_FLAGS_ESTATE_MASK (1 << FSBL_FLAGS_ESTATE_SHIFT) +#define FSBL_FLAGS_ESTATE_A64 0 +#define FSBL_FLAGS_ESTATE_A32 1 + +#define FSBL_FLAGS_ENDIAN_SHIFT 1 +#define FSBL_FLAGS_ENDIAN_MASK (1 << FSBL_FLAGS_ENDIAN_SHIFT) +#define FSBL_FLAGS_ENDIAN_LE 0 +#define FSBL_FLAGS_ENDIAN_BE 1 + +#define FSBL_FLAGS_TZ_SHIFT 2 +#define FSBL_FLAGS_TZ_MASK (1 << FSBL_FLAGS_TZ_SHIFT) +#define FSBL_FLAGS_NON_SECURE 0 +#define FSBL_FLAGS_SECURE 1 + +#define FSBL_FLAGS_EL_SHIFT 3 +#define FSBL_FLAGS_EL_MASK (3 << FSBL_FLAGS_EL_SHIFT) +#define FSBL_FLAGS_EL0 0 +#define FSBL_FLAGS_EL1 1 +#define FSBL_FLAGS_EL2 2 +#define FSBL_FLAGS_EL3 3 + +#define FSBL_FLAGS_CPU_SHIFT 5 +#define FSBL_FLAGS_CPU_MASK (3 << FSBL_FLAGS_CPU_SHIFT) +#define FSBL_FLAGS_A53_0 0 +#define FSBL_FLAGS_A53_1 1 +#define FSBL_FLAGS_A53_2 2 +#define FSBL_FLAGS_A53_3 3 + +#define FSBL_MAX_PARTITIONS 8 + +/* Structure corresponding to each partition entry */ +struct xfsbl_partition { + uint64_t entry_point; + uint64_t flags; +}; + +/* Structure for handoff parameters to ARM Trusted Firmware (ATF) */ +struct xfsbl_atf_handoff_params { + uint8_t magic[4]; + uint32_t num_entries; + struct xfsbl_partition partition[FSBL_MAX_PARTITIONS]; +}; + +#ifdef CONFIG_SPL_ATF +struct bl31_params *bl2_plat_get_bl31_params(uintptr_t bl32_entry, + uintptr_t bl33_entry, + uintptr_t fdt_addr) +{ + struct xfsbl_atf_handoff_params *atfhandoffparams; + u32 index = 0; + + atfhandoffparams = (void *)CONFIG_SPL_TEXT_BASE; + atfhandoffparams->magic[0] = 'X'; + atfhandoffparams->magic[1] = 'L'; + atfhandoffparams->magic[2] = 'N'; + atfhandoffparams->magic[3] = 'X'; + + debug("Creating handoff:\n"); + + if (bl32_entry) { + debug(" to BL32 at 0x%x EL-1, Secure\n", (u32)bl32_entry); + atfhandoffparams->partition[index].entry_point = bl32_entry; + atfhandoffparams->partition[index].flags = FSBL_FLAGS_EL1 << FSBL_FLAGS_EL_SHIFT | + FSBL_FLAGS_SECURE << FSBL_FLAGS_TZ_SHIFT; + index++; + } + + if (bl33_entry) { + debug(" to BL33 at 0x%x EL-2\n", (u32)bl33_entry); + atfhandoffparams->partition[index].entry_point = bl33_entry; + atfhandoffparams->partition[index].flags = FSBL_FLAGS_EL2 << + FSBL_FLAGS_EL_SHIFT; + index++; + } + + atfhandoffparams->num_entries = index; + + writel(CONFIG_SPL_TEXT_BASE, &pmu_base->gen_storage6); + + return NULL; +} +#endif diff --git a/arch/arm/mach-zynqmp/include/mach/clk.h b/arch/arm/mach-zynqmp/include/mach/clk.h new file mode 100644 index 00000000000..9918d469122 --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/clk.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#ifndef _ASM_ARCH_CLK_H_ +#define _ASM_ARCH_CLK_H_ + +unsigned long zynqmp_get_system_timer_freq(void); + +#endif /* _ASM_ARCH_CLK_H_ */ diff --git a/arch/arm/mach-zynqmp/include/mach/ecc_spl_init.h b/arch/arm/mach-zynqmp/include/mach/ecc_spl_init.h new file mode 100644 index 00000000000..b4b6fcf53bc --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/ecc_spl_init.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright(c) 2015 - 2020 Xilinx, Inc. + * + * Jorge Ramirez-Ortiz <jorge@foundries.io> + */ + +#ifndef __ARCH_ZYNQMP_ECC_INIT_H +#define __ARCH_ZYNQMP_ECC_INIT_H + +void zynqmp_ecc_init(void); + +#endif diff --git a/arch/arm/mach-zynqmp/include/mach/hardware.h b/arch/arm/mach-zynqmp/include/mach/hardware.h new file mode 100644 index 00000000000..8cb6494e52c --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/hardware.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#ifndef _ASM_ARCH_HARDWARE_H +#define _ASM_ARCH_HARDWARE_H + +#ifndef __ASSEMBLY__ +#include <linux/bitops.h> +#endif + +#define ZYNQMP_TCM_BASE_ADDR 0xFFE00000 +#define ZYNQMP_TCM_SIZE 0x40000 + +#define ZYNQMP_CRL_APB_BASEADDR 0xFF5E0000 +#define ZYNQMP_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT 0x1000000 +#define ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_EN_SHIFT 0 +#define ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_VAL_SHIFT 8 + +#define ZYNQMP_AMS_PS_SYSMON_BASEADDR 0XFFA50800 +#define ZYNQMP_AMS_PS_SYSMON_ANALOG_BUS ((ZYNQMP_AMS_PS_SYSMON_BASEADDR) \ + + 0x00000114) +#define ZYNQMP_PS_SYSMON_ANALOG_BUS_VAL 0x00003210 + +#define ADMA_CH0_BASEADDR 0xFFA80000 + +#define PS_MODE0 BIT(0) +#define PS_MODE1 BIT(1) +#define PS_MODE2 BIT(2) +#define PS_MODE3 BIT(3) + +#define RESET_REASON_DEBUG_SYS BIT(6) +#define RESET_REASON_SOFT BIT(5) +#define RESET_REASON_SRST BIT(4) +#define RESET_REASON_PSONLY BIT(3) +#define RESET_REASON_PMU BIT(2) +#define RESET_REASON_INTERNAL BIT(1) +#define RESET_REASON_EXTERNAL BIT(0) + +#define CRLAPB_DBG_LPD_CTRL_SETUP_CLK 0x01002002 +#define CRLAPB_RST_LPD_DBG_RESET 0 + +struct crlapb_regs { + u32 reserved0[36]; + u32 cpu_r5_ctrl; /* 0x90 */ + u32 reserved1[7]; + u32 dbg_lpd_ctrl; /* 0xB0 */ + u32 reserved2[29]; + u32 timestamp_ref_ctrl; /* 0x128 */ + u32 reserved3[53]; + u32 boot_mode; /* 0x200 */ + u32 reserved4_0[7]; + u32 reset_reason; /* 0x220 */ + u32 reserved4_1[6]; + u32 rst_lpd_top; /* 0x23C */ + u32 rst_lpd_dbg; /* 0x240 */ + u32 reserved5[3]; + u32 boot_pin_ctrl; /* 0x250 */ + u32 reserved6[21]; +}; + +#define crlapb_base ((struct crlapb_regs *)ZYNQMP_CRL_APB_BASEADDR) + +#define ZYNQMP_IOU_SCNTR_SECURE 0xFF260000 +#define ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_EN 0x1 +#define ZYNQMP_IOU_SCNTR_COUNTER_CONTROL_REGISTER_HDBG 0x2 + +struct iou_scntr_secure { + u32 counter_control_register; + u32 reserved0[7]; + u32 base_frequency_id_register; +}; + +#define iou_scntr_secure ((struct iou_scntr_secure *)ZYNQMP_IOU_SCNTR_SECURE) + +#define ZYNQMP_PS_VERSION 0xFFCA0044 +#define ZYNQMP_PS_VER_MASK GENMASK(1, 0) + +/* Bootmode setting values */ +#define BOOT_MODES_MASK 0x0000000F +#define QSPI_MODE_24BIT 0x00000001 +#define QSPI_MODE_32BIT 0x00000002 +#define SD_MODE 0x00000003 /* sd 0 */ +#define SD_MODE1 0x00000005 /* sd 1 */ +#define NAND_MODE 0x00000004 +#define EMMC_MODE 0x00000006 +#define USB_MODE 0x00000007 +#define SD1_LSHFT_MODE 0x0000000E /* SD1 Level shifter */ +#define JTAG_MODE 0x00000000 +#define BOOT_MODE_USE_ALT 0x100 +#define BOOT_MODE_ALT_SHIFT 12 +/* SW secondary boot modes 0xa - 0xd */ +#define SW_USBHOST_MODE 0x0000000A +#define SW_SATA_MODE 0x0000000B + +#define ZYNQMP_IOU_SLCR_BASEADDR 0xFF180000 + +struct iou_slcr_regs { + u32 mio_pin[78]; + u32 reserved[442]; +}; + +#define slcr_base ((struct iou_slcr_regs *)ZYNQMP_IOU_SLCR_BASEADDR) + +#define ZYNQMP_RPU_BASEADDR 0xFF9A0000 + +struct rpu_regs { + u32 rpu_glbl_ctrl; + u32 reserved0[63]; + u32 rpu0_cfg; /* 0x100 */ + u32 reserved1[63]; + u32 rpu1_cfg; /* 0x200 */ +}; + +#define rpu_base ((struct rpu_regs *)ZYNQMP_RPU_BASEADDR) + +#define ZYNQMP_CRF_APB_BASEADDR 0xFD1A0000 + +struct crfapb_regs { + u32 reserved0[65]; + u32 rst_fpd_apu; /* 0x104 */ + u32 reserved1; +}; + +#define crfapb_base ((struct crfapb_regs *)ZYNQMP_CRF_APB_BASEADDR) + +#define ZYNQMP_APU_BASEADDR 0xFD5C0000 + +struct apu_regs { + u32 reserved0[16]; + u32 rvbar_addr0_l; /* 0x40 */ + u32 rvbar_addr0_h; /* 0x44 */ + u32 reserved1[20]; +}; + +#define apu_base ((struct apu_regs *)ZYNQMP_APU_BASEADDR) + +/* Board version value */ +#define ZYNQMP_CSU_BASEADDR 0xFFCA0000 +#define ZYNQMP_CSU_VERSION_SILICON 0x0 +#define ZYNQMP_CSU_VERSION_QEMU 0x3 + +#define ZYNQMP_CSU_VERSION_EMPTY_SHIFT 20 + +#define ZYNQMP_SILICON_VER_MASK 0xF +#define ZYNQMP_SILICON_VER_SHIFT 0 + +#define CSU_JTAG_SEC_GATE_DISABLE GENMASK(7, 0) +#define CSU_JTAG_DAP_ENABLE_DEBUG GENMASK(7, 0) +#define CSU_JTAG_CHAIN_WR_SETUP GENMASK(1, 0) +#define CSU_PCAP_PROG_RELEASE_PL BIT(0) + +#define ZYNQMP_CSU_STATUS_AUTHENTICATED BIT(0) +#define ZYNQMP_CSU_STATUS_ENCRYPTED BIT(1) + +struct csu_regs { + u32 status; + u32 reserved0[3]; + u32 multi_boot; + u32 reserved1[7]; + u32 jtag_chain_status_wr; + u32 jtag_chain_status; + u32 jtag_sec; + u32 jtag_dap_cfg; + u32 idcode; + u32 version; + u32 reserved2[3054]; + u32 pcap_prog; +}; + +#define csu_base ((struct csu_regs *)ZYNQMP_CSU_BASEADDR) + +#define ZYNQMP_PMU_BASEADDR 0xFFD80000 + +struct pmu_regs { + u32 reserved0[16]; + u32 gen_storage4; /* 0x40 */ + u32 reserved1[1]; + u32 gen_storage6; /* 0x48 */ +}; + +#define pmu_base ((struct pmu_regs *)ZYNQMP_PMU_BASEADDR) + +#endif /* _ASM_ARCH_HARDWARE_H */ diff --git a/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h b/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h new file mode 100644 index 00000000000..783d7c45c7b --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/psu_init_gpl.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _PSU_INIT_GPL_H_ /* prevent circular inclusions */ +#define _PSU_INIT_GPL_H_ + +#include <asm/io.h> + +int mask_pollonvalue(unsigned long add, u32 mask, u32 value); + +int mask_poll(u32 add, u32 mask); + +u32 mask_read(u32 add, u32 mask); + +void mask_delay(u32 delay); + +void psu_mask_write(unsigned long offset, unsigned long mask, + unsigned long val); + +void prog_reg(unsigned long addr, unsigned long mask, + unsigned long shift, unsigned long value); + +int psu_init(void); +unsigned long psu_post_config_data(void); +int psu_uboot_init(void); + +#endif /* _PSU_INIT_GPL_H_ */ diff --git a/arch/arm/mach-zynqmp/include/mach/sys_proto.h b/arch/arm/mach-zynqmp/include/mach/sys_proto.h new file mode 100644 index 00000000000..15b69e77712 --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/sys_proto.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#ifndef _ASM_ARCH_SYS_PROTO_H +#define _ASM_ARCH_SYS_PROTO_H + +#define ZYNQMP_CSU_SILICON_VER_MASK 0xF +#define KEY_PTR_LEN 32 +#define IV_SIZE 12 +#define RSA_KEY_SIZE 512 +#define MODULUS_LEN 512 +#define PRIV_EXPO_LEN 512 +#define PUB_EXPO_LEN 4 + +#define ZYNQMP_SHA3_INIT 1 +#define ZYNQMP_SHA3_UPDATE 2 +#define ZYNQMP_SHA3_FINAL 4 +#define ZYNQMP_SHA3_SIZE 48 + +#define ZYNQMP_FPGA_BIT_AUTH_DDR 1 +#define ZYNQMP_FPGA_BIT_AUTH_OCM 2 +#define ZYNQMP_FPGA_BIT_ENC_USR_KEY 3 +#define ZYNQMP_FPGA_BIT_ENC_DEV_KEY 4 +#define ZYNQMP_FPGA_BIT_NS 5 + +#define ZYNQMP_FPGA_AUTH_DDR 1 + +enum { + IDCODE, + VERSION, + IDCODE2, +}; + +enum { + ZYNQMP_SILICON_V1, + ZYNQMP_SILICON_V2, + ZYNQMP_SILICON_V3, + ZYNQMP_SILICON_V4, +}; + +enum { + TCM_LOCK, + TCM_SPLIT, +}; + +unsigned int zynqmp_get_silicon_version(void); + +void initialize_tcm(bool mode); +void mem_map_fill(void); +#if defined(CONFIG_SYS_MEM_RSVD_FOR_MMU) || defined(CONFIG_DEFINE_TCM_OCM_MMAP) +void tcm_init(u8 mode); +#endif + +#endif /* _ASM_ARCH_SYS_PROTO_H */ diff --git a/arch/arm/mach-zynqmp/include/mach/zynqmp_aes.h b/arch/arm/mach-zynqmp/include/mach/zynqmp_aes.h new file mode 100644 index 00000000000..01a13d4c7c0 --- /dev/null +++ b/arch/arm/mach-zynqmp/include/mach/zynqmp_aes.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Weidmueller Interface GmbH & Co. KG <oss@weidmueller.com> + * Christian Taedcke <christian.taedcke@weidmueller.com> + * + * Declaration of AES operation functionality for ZynqMP. + */ + +#ifndef ZYNQMP_AES_H +#define ZYNQMP_AES_H + +#include <linux/types.h> + +struct zynqmp_aes { + u64 srcaddr; + u64 ivaddr; + u64 keyaddr; + u64 dstaddr; + u64 len; + u64 op; + u64 keysrc; +}; + +/** + * zynqmp_aes_operation() - Performs an aes operation using the pmu firmware + * + * @aes: The aes operation buffer that must have been allocated using + * ALLOC_CACHE_ALIGN_BUFFER(struct zynqmp_aes, aes, 1) + * + * Return: 0 in case of success, in case of an error any other value + */ +int zynqmp_aes_operation(struct zynqmp_aes *aes); + +#endif /* ZYNQMP_AES_H */ diff --git a/arch/arm/mach-zynqmp/mkimage_fit_atf.sh b/arch/arm/mach-zynqmp/mkimage_fit_atf.sh new file mode 100755 index 00000000000..cdecb1c1d35 --- /dev/null +++ b/arch/arm/mach-zynqmp/mkimage_fit_atf.sh @@ -0,0 +1,240 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0+ +# +# script to generate FIT image source for Xilinx ZynqMP boards with +# ARM Trusted Firmware and multiple device trees (given on the command line) +# +# usage: $0 <dt_name> [<dt_name> [<dt_name] ...] + +BL33="u-boot-nodtb.bin" +[ -z "$BL31" ] && BL31="bl31.bin" +BL31_ELF="${BL31%.*}.elf" +[ -f ${BL31_ELF} ] && ATF_LOAD_ADDR=`${CROSS_COMPILE}readelf -l "${BL31_ELF}" | \ +awk '/Entry point/ { print $3 }'` + +[ -z "$ATF_LOAD_ADDR" ] && ATF_LOAD_ADDR="0xfffea000" +ATF_LOAD_ADDR_LOW=`printf 0x%x $((ATF_LOAD_ADDR & 0xffffffff))` +ATF_LOAD_ADDR_HIGH=`printf 0x%x $((ATF_LOAD_ADDR >> 32))` + +[ -z "$BL32" ] && BL32="tee.bin" +BL32_ELF="${BL32%.*}.elf" +[ -f ${BL32_ELF} ] && TEE_LOAD_ADDR=`${CROSS_COMPILE}readelf -l "${BL32_ELF}" | \ +awk '/Entry point/ { print $3 }'` + +[ -z "$TEE_LOAD_ADDR" ] && TEE_LOAD_ADDR="0x60000000" +TEE_LOAD_ADDR_LOW=`printf 0x%x $((TEE_LOAD_ADDR & 0xffffffff))` +TEE_LOAD_ADDR_HIGH=`printf 0x%x $((TEE_LOAD_ADDR >> 32))` + +if [ -z "$BL33_LOAD_ADDR" ];then + BL33_LOAD_ADDR=`awk '/CONFIG_TEXT_BASE/ { print $3 }' include/generated/autoconf.h` +fi +BL33_LOAD_ADDR_LOW=`printf 0x%x $((BL33_LOAD_ADDR & 0xffffffff))` +BL33_LOAD_ADDR_HIGH=`printf 0x%x $((BL33_LOAD_ADDR >> 32))` + +DTB_LOAD_ADDR=`awk '/CONFIG_XILINX_OF_BOARD_DTB_ADDR/ { print $3 }' include/generated/autoconf.h` +if [ ! -z "$DTB_LOAD_ADDR" ]; then + DTB_LOAD_ADDR_LOW=`printf 0x%x $((DTB_LOAD_ADDR & 0xffffffff))` + DTB_LOAD_ADDR_HIGH=`printf 0x%x $((DTB_LOAD_ADDR >> 32))` + DTB_LOAD="load = <$DTB_LOAD_ADDR_HIGH $DTB_LOAD_ADDR_LOW>;" +else + DTB_LOAD="" +fi + +if [ -z "$*" ]; then + DT=arch/arm/dts/${DEVICE_TREE}.dtb +else + DT=$* +fi + +if [ ! -f $BL31 ]; then + echo "WARNING: BL31 file $BL31 NOT found, U-Boot will run in EL3" >&2 + BL31=/dev/null +fi + +cat << __HEADER_EOF +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + +/dts-v1/; + +/ { + description = "Configuration for Xilinx ZynqMP SoC"; + + images { + uboot { + description = "U-Boot (64-bit)"; + data = /incbin/("$BL33"); + type = "firmware"; + os = "u-boot"; + arch = "arm64"; + compression = "none"; + load = <$BL33_LOAD_ADDR_HIGH $BL33_LOAD_ADDR_LOW>; + entry = <$BL33_LOAD_ADDR_HIGH $BL33_LOAD_ADDR_LOW>; + hash { + algo = "md5"; + }; + }; +__HEADER_EOF + +if [ -f $BL31 ]; then +cat << __ATF + atf { + description = "Trusted Firmware-A"; + data = /incbin/("$BL31"); + type = "firmware"; + os = "arm-trusted-firmware"; + arch = "arm64"; + compression = "none"; + load = <$ATF_LOAD_ADDR_HIGH $ATF_LOAD_ADDR_LOW>; + entry = <$ATF_LOAD_ADDR_HIGH $ATF_LOAD_ADDR_LOW>; + hash { + algo = "md5"; + }; + }; +__ATF +fi + +if [ -f $BL32 ]; then +cat << __TEE + tee { + description = "TEE firmware"; + data = /incbin/("$BL32"); + type = "firmware"; + os = "tee"; + arch = "arm64"; + compression = "none"; + load = <$TEE_LOAD_ADDR_HIGH $TEE_LOAD_ADDR_LOW>; + entry = <$TEE_LOAD_ADDR_HIGH $TEE_LOAD_ADDR_LOW>; + hash { + algo = "md5"; + }; + }; +__TEE +fi + +MULTI_DTB=`awk '/CONFIG_MULTI_DTB_FIT / { print $3 }' include/generated/autoconf.h` + +if [ 1"$MULTI_DTB" -eq 11 ]; then + cat << __FDT_IMAGE_EOF + fdt_1 { + description = "Multi DTB fit image"; + data = /incbin/("fit-dtb.blob"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + $DTB_LOAD + hash { + algo = "md5"; + }; + }; + }; + configurations { + default = "config_1"; +__FDT_IMAGE_EOF + +if [ ! -f $BL31 ]; then +cat << __CONF_SECTION1_EOF + config_1 { + description = "Multi DTB without TF-A"; + firmware = "uboot"; + loadables = "fdt_1"; + }; +__CONF_SECTION1_EOF +else +if [ -f $BL32 ]; then +cat << __CONF_SECTION1_EOF + config_1 { + description = "Multi DTB with TF-A and TEE"; + firmware = "atf"; + loadables = "uboot", "tee", "fdt_1"; + }; +__CONF_SECTION1_EOF +else +cat << __CONF_SECTION1_EOF + config_1 { + description = "Multi DTB with TF-A"; + firmware = "atf"; + loadables = "uboot", "fdt_1"; + }; +__CONF_SECTION1_EOF +fi +fi + +cat << __ITS_EOF + }; +}; +__ITS_EOF + +else + +DEFAULT=1 +cnt=1 +for dtname in $DT +do + cat << __FDT_IMAGE_EOF + fdt_$cnt { + description = "$(basename $dtname .dtb)"; + data = /incbin/("$dtname"); + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + $DTB_LOAD + hash { + algo = "md5"; + }; + }; +__FDT_IMAGE_EOF + +[ "x$(basename $dtname .dtb)" = "x${DEVICE_TREE}" ] && DEFAULT=$cnt + +cnt=$((cnt+1)) +done + +cat << __CONF_HEADER_EOF + }; + configurations { + default = "config_$DEFAULT"; + +__CONF_HEADER_EOF + +cnt=1 +for dtname in $DT +do +if [ ! -f $BL31 ]; then +cat << __CONF_SECTION1_EOF + config_$cnt { + description = "$(basename $dtname .dtb)"; + firmware = "uboot"; + fdt = "fdt_$cnt"; + }; +__CONF_SECTION1_EOF +else +if [ -f $BL32 ]; then +cat << __CONF_SECTION1_EOF + config_$cnt { + description = "$(basename $dtname .dtb)"; + firmware = "atf"; + loadables = "uboot", "tee"; + fdt = "fdt_$cnt"; + }; +__CONF_SECTION1_EOF +else +cat << __CONF_SECTION1_EOF + config_$cnt { + description = "$(basename $dtname .dtb)"; + firmware = "atf"; + loadables = "uboot"; + fdt = "fdt_$cnt"; + }; +__CONF_SECTION1_EOF +fi +fi + +cnt=$((cnt+1)) +done + +cat << __ITS_EOF + }; +}; +__ITS_EOF + +fi diff --git a/arch/arm/mach-zynqmp/mp.c b/arch/arm/mach-zynqmp/mp.c new file mode 100644 index 00000000000..9b46a25a1cb --- /dev/null +++ b/arch/arm/mach-zynqmp/mp.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2014 - 2015 Xilinx, Inc. + * Michal Simek <michal.simek@amd.com> + */ + +#include <config.h> +#include <cpu_func.h> +#include <log.h> +#include <vsprintf.h> +#include <zynqmp_firmware.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/string.h> + +#define LOCK 0 +#define SPLIT 1 + +#define HALT 0 +#define RELEASE 1 + +#define ZYNQMP_BOOTADDR_HIGH_MASK 0xFFFFFFFF +#define ZYNQMP_R5_HIVEC_ADDR 0xFFFF0000 +#define ZYNQMP_R5_LOVEC_ADDR 0x0 +#define ZYNQMP_RPU_CFG_CPU_HALT_MASK 0x01 +#define ZYNQMP_RPU_CFG_HIVEC_MASK 0x04 +#define ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK 0x08 +#define ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK 0x40 +#define ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK 0x10 + +#define ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK 0x04 +#define ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK 0x01 +#define ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK 0x02 +#define ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK 0x1000000 + +#define ZYNQMP_R5_0_TCM_START_ADDR 0xFFE00000 +#define ZYNQMP_R5_1_TCM_START_ADDR 0xFFE90000 +#define ZYNQMP_TCM_BOTH_SIZE 0x40000 + +#define ZYNQMP_CORE_APU0 0 +#define ZYNQMP_CORE_APU3 3 +#define ZYNQMP_CORE_RPU0 4 +#define ZYNQMP_CORE_RPU1 5 + +#define ZYNQMP_MAX_CORES 6 + +#define ZYNQMP_RPU0_USE_MASK BIT(1) +#define ZYNQMP_RPU1_USE_MASK BIT(2) + +int is_core_valid(unsigned int core) +{ + if (core < ZYNQMP_MAX_CORES) + return 1; + + return 0; +} + +int cpu_reset(u32 nr) +{ + puts("Feature is not implemented.\n"); + return 0; +} + +static void set_r5_halt_mode(u32 nr, u8 halt, u8 mode) +{ + u32 tmp; + + if (mode == LOCK || nr == ZYNQMP_CORE_RPU0) { + tmp = readl(&rpu_base->rpu0_cfg); + if (halt == HALT) + tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; + else + tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; + writel(tmp, &rpu_base->rpu0_cfg); + } + + if (mode == LOCK || nr == ZYNQMP_CORE_RPU1) { + tmp = readl(&rpu_base->rpu1_cfg); + if (halt == HALT) + tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; + else + tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; + writel(tmp, &rpu_base->rpu1_cfg); + } +} + +static void set_r5_tcm_mode(u8 mode) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu_glbl_ctrl); + if (mode == LOCK) { + tmp &= ~ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; + tmp |= ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | + ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK; + } else { + tmp |= ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; + tmp &= ~(ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | + ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK); + } + + writel(tmp, &rpu_base->rpu_glbl_ctrl); +} + +static void set_r5_reset(u32 nr, u8 mode) +{ + u32 tmp; + + tmp = readl(&crlapb_base->rst_lpd_top); + if (mode == LOCK) { + tmp |= (ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK); + } else { + if (nr == ZYNQMP_CORE_RPU0) { + tmp |= ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK; + if (tmp & ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK) + tmp |= ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK; + } else { + tmp |= ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK; + if (tmp & ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK) + tmp |= ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK; + } + } + + writel(tmp, &crlapb_base->rst_lpd_top); +} + +static void release_r5_reset(u32 nr, u8 mode) +{ + u32 tmp; + + tmp = readl(&crlapb_base->rst_lpd_top); + if (mode == LOCK || nr == ZYNQMP_CORE_RPU0) + tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK); + + if (mode == LOCK || nr == ZYNQMP_CORE_RPU1) + tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | + ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK); + + writel(tmp, &crlapb_base->rst_lpd_top); +} + +static void enable_clock_r5(void) +{ + u32 tmp; + + tmp = readl(&crlapb_base->cpu_r5_ctrl); + tmp |= ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK; + writel(tmp, &crlapb_base->cpu_r5_ctrl); + + /* Give some delay for clock + * to propagate */ + udelay(0x500); +} + +static int check_r5_mode(void) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu_glbl_ctrl); + if (tmp & ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK) + return SPLIT; + + return LOCK; +} + +int cpu_disable(u32 nr) +{ + if (nr <= ZYNQMP_CORE_APU3) { + u32 val = readl(&crfapb_base->rst_fpd_apu); + val |= 1 << nr; + writel(val, &crfapb_base->rst_fpd_apu); + } else { + set_r5_reset(nr, check_r5_mode()); + } + + return 0; +} + +int cpu_status(u32 nr) +{ + if (nr <= ZYNQMP_CORE_APU3) { + u32 addr_low = readl(((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); + u32 addr_high = readl(((u8 *)&apu_base->rvbar_addr0_h) + + nr * 8); + u32 val = readl(&crfapb_base->rst_fpd_apu); + val &= 1 << nr; + printf("APU CPU%d %s - starting address HI: %x, LOW: %x\n", + nr, val ? "OFF" : "ON" , addr_high, addr_low); + } else { + u32 val = readl(&crlapb_base->rst_lpd_top); + val &= 1 << (nr - 4); + printf("RPU CPU%d %s\n", nr - 4, val ? "OFF" : "ON"); + } + + return 0; +} + +static void set_r5_start(u8 high) +{ + u32 tmp; + + tmp = readl(&rpu_base->rpu0_cfg); + if (high) + tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; + else + tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; + writel(tmp, &rpu_base->rpu0_cfg); + + tmp = readl(&rpu_base->rpu1_cfg); + if (high) + tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; + else + tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; + writel(tmp, &rpu_base->rpu1_cfg); +} + +static void write_tcm_boot_trampoline(u32 nr, u32 boot_addr) +{ + if (boot_addr) { + u64 tcm_start_addr = ZYNQMP_R5_0_TCM_START_ADDR; + + if (nr == ZYNQMP_CORE_RPU1) + tcm_start_addr = ZYNQMP_R5_1_TCM_START_ADDR; + + /* + * Boot trampoline is simple ASM code below. + * + * b over; + * label: + * .word 0 + * over: ldr r0, =label + * ldr r1, [r0] + * bx r1 + */ + debug("Write boot trampoline for %x\n", boot_addr); + writel(0xea000000, tcm_start_addr); + writel(boot_addr, tcm_start_addr + 0x4); + writel(0xe59f0004, tcm_start_addr + 0x8); + writel(0xe5901000, tcm_start_addr + 0xc); + writel(0xe12fff11, tcm_start_addr + 0x10); + writel(0x00000004, tcm_start_addr + 0x14); + } +} + +void initialize_tcm(bool mode) +{ + if (!mode) { + set_r5_tcm_mode(LOCK); + set_r5_halt_mode(ZYNQMP_CORE_RPU0, HALT, LOCK); + enable_clock_r5(); + release_r5_reset(ZYNQMP_CORE_RPU0, LOCK); + } else { + set_r5_tcm_mode(SPLIT); + set_r5_halt_mode(ZYNQMP_CORE_RPU0, HALT, SPLIT); + set_r5_halt_mode(ZYNQMP_CORE_RPU1, HALT, SPLIT); + enable_clock_r5(); + release_r5_reset(ZYNQMP_CORE_RPU0, SPLIT); + release_r5_reset(ZYNQMP_CORE_RPU1, SPLIT); + } +} + +static void mark_r5_used(u32 nr, u8 mode) +{ + u32 mask = 0; + + if (mode == LOCK) { + mask = ZYNQMP_RPU0_USE_MASK | ZYNQMP_RPU1_USE_MASK; + } else { + switch (nr) { + case ZYNQMP_CORE_RPU0: + mask = ZYNQMP_RPU0_USE_MASK; + break; + case ZYNQMP_CORE_RPU1: + mask = ZYNQMP_RPU1_USE_MASK; + break; + default: + return; + } + } + zynqmp_mmio_write((ulong)&pmu_base->gen_storage4, mask, mask); +} + +int cpu_release(u32 nr, int argc, char *const argv[]) +{ + if (nr <= ZYNQMP_CORE_APU3) { + u64 boot_addr = simple_strtoull(argv[0], NULL, 16); + /* HIGH */ + writel((u32)(boot_addr >> 32), + ((u8 *)&apu_base->rvbar_addr0_h) + nr * 8); + /* LOW */ + writel((u32)(boot_addr & ZYNQMP_BOOTADDR_HIGH_MASK), + ((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); + + u32 val = readl(&crfapb_base->rst_fpd_apu); + val &= ~(1 << nr); + writel(val, &crfapb_base->rst_fpd_apu); + } else { + if (argc != 2) { + printf("Invalid number of arguments to release.\n"); + printf("<addr> <mode>-Start addr lockstep or split\n"); + return 1; + } + + u32 boot_addr = hextoul(argv[0], NULL); + u32 boot_addr_uniq = 0; + if (!(boot_addr == ZYNQMP_R5_LOVEC_ADDR || + boot_addr == ZYNQMP_R5_HIVEC_ADDR)) { + printf("Using TCM jump trampoline for address 0x%x\n", + boot_addr); + /* Save boot address for later usage */ + boot_addr_uniq = boot_addr; + /* + * R5 needs to start from LOVEC at TCM + * OCM will be probably occupied by ATF + */ + boot_addr = ZYNQMP_R5_LOVEC_ADDR; + } + + /* + * Since we don't know where the user may have loaded the image + * for an R5 we have to flush all the data cache to ensure + * the R5 sees it. + */ + flush_dcache_all(); + + if (!strncmp(argv[1], "lockstep", 8)) { + if (nr != ZYNQMP_CORE_RPU0) { + printf("Lockstep mode should run on ZYNQMP_CORE_RPU0\n"); + return 1; + } + printf("R5 lockstep mode\n"); + set_r5_reset(nr, LOCK); + set_r5_tcm_mode(LOCK); + set_r5_halt_mode(nr, HALT, LOCK); + set_r5_start(boot_addr); + enable_clock_r5(); + release_r5_reset(nr, LOCK); + dcache_disable(); + write_tcm_boot_trampoline(nr, boot_addr_uniq); + dcache_enable(); + set_r5_halt_mode(nr, RELEASE, LOCK); + mark_r5_used(nr, LOCK); + } else if (!strncmp(argv[1], "split", 5)) { + printf("R5 split mode\n"); + set_r5_reset(nr, SPLIT); + set_r5_tcm_mode(SPLIT); + set_r5_halt_mode(nr, HALT, SPLIT); + set_r5_start(boot_addr); + enable_clock_r5(); + release_r5_reset(nr, SPLIT); + dcache_disable(); + write_tcm_boot_trampoline(nr, boot_addr_uniq); + dcache_enable(); + set_r5_halt_mode(nr, RELEASE, SPLIT); + mark_r5_used(nr, SPLIT); + } else { + printf("Unsupported mode\n"); + return 1; + } + } + + return 0; +} diff --git a/arch/arm/mach-zynqmp/psu_spl_init.c b/arch/arm/mach-zynqmp/psu_spl_init.c new file mode 100644 index 00000000000..5b4d66359bf --- /dev/null +++ b/arch/arm/mach-zynqmp/psu_spl_init.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Xilinx, Inc. + * + * Michal Simek <michal.simek@amd.com> + */ +#include <asm/io.h> +#include <asm/arch/psu_init_gpl.h> +#include <linux/delay.h> + +#define PSU_MASK_POLL_TIME 1100000 + +int __maybe_unused mask_pollonvalue(unsigned long add, u32 mask, u32 value) +{ + int i = 0; + + while ((__raw_readl(add) & mask) != value) { + if (i == PSU_MASK_POLL_TIME) + return 0; + i++; + } + return 1; +} + +__weak int mask_poll(u32 add, u32 mask) +{ + int i = 0; + unsigned long addr = add; + + while (!(__raw_readl(addr) & mask)) { + if (i == PSU_MASK_POLL_TIME) + return 0; + i++; + } + return 1; +} + +__weak u32 mask_read(u32 add, u32 mask) +{ + unsigned long addr = add; + + return __raw_readl(addr) & mask; +} + +__weak void mask_delay(u32 delay) +{ + udelay(delay); +} + +__weak void psu_mask_write(unsigned long offset, unsigned long mask, + unsigned long val) +{ + unsigned long regval = 0; + + regval = readl(offset); + regval &= ~(mask); + regval |= (val & mask); + writel(regval, offset); +} + +__weak void prog_reg(unsigned long addr, unsigned long mask, + unsigned long shift, unsigned long value) +{ + int rdata = 0; + + rdata = readl(addr); + rdata = rdata & (~mask); + rdata = rdata | (value << shift); + writel(rdata, addr); +} + +__weak int psu_init(void) +{ + /* + * This function is overridden by the one in + * board/xilinx/zynqmp/(platform)/psu_init_gpl.c, if it exists. + */ + return -1; +} + +__weak unsigned long psu_post_config_data(void) +{ + /* + * This function is overridden by the one in + * board/xilinx/zynqmp/(platform)/psu_init_gpl.c, if it exists. + */ + return 0; +} diff --git a/arch/arm/mach-zynqmp/spl.c b/arch/arm/mach-zynqmp/spl.c new file mode 100644 index 00000000000..4a2d240abbb --- /dev/null +++ b/arch/arm/mach-zynqmp/spl.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015 - 2016 Xilinx, Inc. + * + * Michal Simek <michal.simek@amd.com> + */ + +#include <image.h> +#include <init.h> +#include <log.h> +#include <semihosting.h> +#include <spl.h> +#include <linux/delay.h> + +#include <asm/io.h> +#include <asm/spl.h> +#include <asm/arch/hardware.h> +#include <asm/arch/ecc_spl_init.h> +#include <asm/arch/psu_init_gpl.h> +#include <asm/arch/sys_proto.h> + +#if defined(CONFIG_DEBUG_UART_BOARD_INIT) +void board_debug_uart_init(void) +{ + psu_uboot_init(); +} +#endif + +void board_init_f(ulong dummy) +{ +#if !defined(CONFIG_DEBUG_UART_BOARD_INIT) + psu_uboot_init(); +#endif + + board_early_init_r(); +#ifdef CONFIG_SPL_ZYNQMP_DRAM_ECC_INIT + zynqmp_ecc_init(); +#endif +} + +static void ps_mode_reset(ulong mode) +{ + writel(mode << ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_EN_SHIFT, + &crlapb_base->boot_pin_ctrl); + udelay(5); + writel(mode << ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_VAL_SHIFT | + mode << ZYNQMP_CRL_APB_BOOT_PIN_CTRL_OUT_EN_SHIFT, + &crlapb_base->boot_pin_ctrl); +} + +/* + * Set default PS_MODE1 which is used for USB ULPI phy reset + * Also other resets can be connected to this certain pin + */ +#ifndef MODE_RESET +# define MODE_RESET PS_MODE1 +#endif + +#ifdef CONFIG_SPL_SOC_INIT +void spl_soc_init(void) +{ + preloader_console_init(); + ps_mode_reset(MODE_RESET); + board_init(); + psu_post_config_data(); +} +#endif + +static u32 jtag_boot_device(void) +{ + return semihosting_enabled() ? BOOT_DEVICE_SMH : BOOT_DEVICE_RAM; +} + +void board_boot_order(u32 *spl_boot_list) +{ + spl_boot_list[0] = spl_boot_device(); + + if (spl_boot_list[0] == BOOT_DEVICE_MMC1) + spl_boot_list[1] = BOOT_DEVICE_MMC2; + if (spl_boot_list[0] == BOOT_DEVICE_MMC2) + spl_boot_list[1] = BOOT_DEVICE_MMC1; + + spl_boot_list[2] = jtag_boot_device(); +} + +u32 spl_boot_device(void) +{ + u32 reg = 0; + u8 bootmode; + +#if defined(CONFIG_SPL_ZYNQMP_ALT_BOOTMODE_ENABLED) + /* Change default boot mode at run-time */ + reg = CONFIG_SPL_ZYNQMP_ALT_BOOTMODE; + writel(CONFIG_SPL_ZYNQMP_ALT_BOOTMODE << BOOT_MODE_ALT_SHIFT, + &crlapb_base->boot_mode); +#else + reg = readl(&crlapb_base->boot_mode); + if (reg >> BOOT_MODE_ALT_SHIFT) + reg >>= BOOT_MODE_ALT_SHIFT; +#endif + + bootmode = reg & BOOT_MODES_MASK; + + switch (bootmode) { + case JTAG_MODE: + return jtag_boot_device(); +#ifdef CONFIG_SPL_MMC + case SD_MODE1: + case SD1_LSHFT_MODE: /* not working on silicon v1 */ + return BOOT_DEVICE_MMC2; + case SD_MODE: + case EMMC_MODE: + return BOOT_DEVICE_MMC1; +#endif +#ifdef CONFIG_SPL_DFU + case USB_MODE: + return BOOT_DEVICE_DFU; +#endif +#ifdef CONFIG_SPL_SATA + case SW_SATA_MODE: + return BOOT_DEVICE_SATA; +#endif +#ifdef CONFIG_SPL_SPI + case QSPI_MODE_24BIT: + case QSPI_MODE_32BIT: + return BOOT_DEVICE_SPI; +#endif + default: + printf("Invalid Boot Mode:0x%x\n", bootmode); + break; + } + + return 0; +} + +#ifdef CONFIG_SPL_OS_BOOT +int spl_start_uboot(void) +{ + return 0; +} +#endif diff --git a/arch/arm/mach-zynqmp/zynqmp.c b/arch/arm/mach-zynqmp/zynqmp.c new file mode 100644 index 00000000000..bf39c5472ea --- /dev/null +++ b/arch/arm/mach-zynqmp/zynqmp.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2018 Xilinx, Inc. + * Siva Durga Prasad Paladugu <siva.durga.prasad.paladugu@amd.com>> + */ + +#include <command.h> +#include <cpu_func.h> +#include <env.h> +#include <malloc.h> +#include <memalign.h> +#include <vsprintf.h> +#include <zynqmp_firmware.h> +#include <linux/errno.h> +#include <asm/arch/hardware.h> +#include <asm/arch/sys_proto.h> +#include <asm/io.h> +#include <mach/zynqmp_aes.h> + +static int do_zynqmp_verify_secure(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u64 src_addr, addr; + u32 len, src_lo, src_hi; + u8 *key_ptr = NULL; + int ret; + u32 key_lo = 0; + u32 key_hi = 0; + u32 ret_payload[PAYLOAD_ARG_CNT]; + + if (argc < 4) + return CMD_RET_USAGE; + + src_addr = simple_strtoull(argv[2], NULL, 16); + len = hextoul(argv[3], NULL); + + if (argc == 5) + key_ptr = (uint8_t *)(uintptr_t)simple_strtoull(argv[4], + NULL, 16); + + if ((ulong)src_addr != ALIGN((ulong)src_addr, + CONFIG_SYS_CACHELINE_SIZE)) { + printf("Failed: source address not aligned:%lx\n", + (ulong)src_addr); + return -EINVAL; + } + + src_lo = lower_32_bits((ulong)src_addr); + src_hi = upper_32_bits((ulong)src_addr); + flush_dcache_range((ulong)src_addr, (ulong)(src_addr + len)); + + if (key_ptr) { + key_lo = lower_32_bits((ulong)key_ptr); + key_hi = upper_32_bits((ulong)key_ptr); + flush_dcache_range((ulong)key_ptr, + (ulong)(key_ptr + KEY_PTR_LEN)); + } + + ret = xilinx_pm_request(PM_SECURE_IMAGE, src_lo, src_hi, + key_lo, key_hi, ret_payload); + if (ret) { + printf("Failed: secure op status:0x%x\n", ret); + } else { + addr = (u64)ret_payload[1] << 32 | ret_payload[2]; + printf("Verified image at 0x%llx\n", addr); + env_set_hex("zynqmp_verified_img_addr", addr); + } + + return ret; +} + +static int do_zynqmp_mmio_read(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 read_val, addr; + int ret; + + if (argc != cmdtp->maxargs) + return CMD_RET_USAGE; + + addr = hextoul(argv[2], NULL); + + ret = zynqmp_mmio_read(addr, &read_val); + if (!ret) + printf("mmio read value at 0x%x = 0x%x\n", + addr, read_val); + else + printf("Failed: mmio read\n"); + + return ret; +} + +static int do_zynqmp_mmio_write(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 addr, mask, val; + int ret; + + if (argc != cmdtp->maxargs) + return CMD_RET_USAGE; + + addr = hextoul(argv[2], NULL); + mask = hextoul(argv[3], NULL); + val = hextoul(argv[4], NULL); + + ret = zynqmp_mmio_write(addr, mask, val); + if (ret != 0) + printf("Failed: mmio write\n"); + + return ret; +} + +static int do_zynqmp_aes(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + ALLOC_CACHE_ALIGN_BUFFER(struct zynqmp_aes, aes, 1); + + if (zynqmp_firmware_version() <= PMUFW_V1_0) { + puts("ERR: PMUFW v1.0 or less is detected\n"); + puts("ERR: Encrypt/Decrypt feature is not supported\n"); + puts("ERR: Please upgrade PMUFW\n"); + return CMD_RET_FAILURE; + } + + if (argc < cmdtp->maxargs - 1) + return CMD_RET_USAGE; + + aes->srcaddr = hextoul(argv[2], NULL); + aes->ivaddr = hextoul(argv[3], NULL); + aes->len = hextoul(argv[4], NULL); + aes->op = hextoul(argv[5], NULL); + aes->keysrc = hextoul(argv[6], NULL); + aes->dstaddr = hextoul(argv[7], NULL); + + if (aes->keysrc == 0) { + if (argc < cmdtp->maxargs) + return CMD_RET_USAGE; + + aes->keyaddr = hextoul(argv[8], NULL); + } + + return zynqmp_aes_operation(aes); +} + +#ifdef CONFIG_DEFINE_TCM_OCM_MMAP +static int do_zynqmp_tcm_init(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u8 mode; + + if (argc != cmdtp->maxargs) + return CMD_RET_USAGE; + + if (strcmp(argv[2], "lockstep") && strcmp(argv[2], "split")) { + printf("mode param should be lockstep or split\n"); + return CMD_RET_FAILURE; + } + + mode = hextoul(argv[2], NULL); + if (mode != TCM_LOCK && mode != TCM_SPLIT) { + printf("Mode should be either 0(lock)/1(split)\n"); + return CMD_RET_FAILURE; + } + + dcache_disable(); + tcm_init(mode); + dcache_enable(); + + return CMD_RET_SUCCESS; +} +#endif + +static int do_zynqmp_pmufw(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + u32 addr, size; + + if (argc != cmdtp->maxargs) + return CMD_RET_USAGE; + + if (!strncmp(argv[2], "node", 4)) { + u32 id; + int ret; + + if (!strncmp(argv[3], "close", 5)) + return zynqmp_pmufw_config_close(); + + id = dectoul(argv[3], NULL); + if (!id) { + printf("Incorrect ID passed\n"); + return CMD_RET_USAGE; + } + + printf("Enable permission for node ID %d\n", id); + + ret = zynqmp_pmufw_node(id); + if (ret == -ENODEV) + ret = 0; + + return ret; + } + + addr = hextoul(argv[2], NULL); + size = hextoul(argv[3], NULL); + + zynqmp_pmufw_load_config_object((const void *)(uintptr_t)addr, + (size_t)size); + + return 0; +} + +static int do_zynqmp_rsa(struct cmd_tbl *cmdtp, int flag, int argc, + char * const argv[]) +{ + u64 srcaddr, mod, exp; + u32 srclen, rsaop, size, ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (argc != cmdtp->maxargs) + return CMD_RET_USAGE; + + if (zynqmp_firmware_version() <= PMUFW_V1_0) { + puts("ERR: PMUFW v1.0 or less is detected\n"); + puts("ERR: Encrypt/Decrypt feature is not supported\n"); + puts("ERR: Please upgrade PMUFW\n"); + return CMD_RET_FAILURE; + } + + srcaddr = hextoul(argv[2], NULL); + srclen = hextoul(argv[3], NULL); + if (srclen != RSA_KEY_SIZE) { + puts("ERR: srclen should be equal to 0x200(512 bytes)\n"); + return CMD_RET_USAGE; + } + + mod = hextoul(argv[4], NULL); + exp = hextoul(argv[5], NULL); + rsaop = hextoul(argv[6], NULL); + if (!(rsaop == 0 || rsaop == 1)) { + puts("ERR: rsaop should be either 0 or 1\n"); + return CMD_RET_USAGE; + } + + memcpy((void *)srcaddr + srclen, (void *)mod, MODULUS_LEN); + + /* + * For encryption we load public exponent (key size 4096-bits), + * for decryption we load private exponent (32-bits) + */ + if (rsaop) { + memcpy((void *)srcaddr + srclen + MODULUS_LEN, + (void *)exp, PUB_EXPO_LEN); + size = srclen + MODULUS_LEN + PUB_EXPO_LEN; + } else { + memcpy((void *)srcaddr + srclen + MODULUS_LEN, + (void *)exp, PRIV_EXPO_LEN); + size = srclen + MODULUS_LEN + PRIV_EXPO_LEN; + } + + flush_dcache_range((ulong)srcaddr, + (ulong)(srcaddr) + roundup(size, ARCH_DMA_MINALIGN)); + + ret = xilinx_pm_request(PM_SECURE_RSA, upper_32_bits((ulong)srcaddr), + lower_32_bits((ulong)srcaddr), srclen, rsaop, + ret_payload); + if (ret || ret_payload[1]) { + printf("Failed: RSA status:0x%x, errcode:0x%x\n", + ret, ret_payload[1]); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int do_zynqmp_sha3(struct cmd_tbl *cmdtp, int flag, + int argc, char * const argv[]) +{ + u64 srcaddr, hashaddr; + u32 srclen, ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (argc > cmdtp->maxargs || argc < (cmdtp->maxargs - 1)) + return CMD_RET_USAGE; + + if (zynqmp_firmware_version() <= PMUFW_V1_0) { + puts("ERR: PMUFW v1.0 or less is detected\n"); + puts("ERR: Encrypt/Decrypt feature is not supported\n"); + puts("ERR: Please upgrade PMUFW\n"); + return CMD_RET_FAILURE; + } + + srcaddr = hextoul(argv[2], NULL); + srclen = hextoul(argv[3], NULL); + + if (argc == 5) { + hashaddr = hextoul(argv[4], NULL); + flush_dcache_range(hashaddr, + hashaddr + roundup(ZYNQMP_SHA3_SIZE, + ARCH_DMA_MINALIGN)); + } else { + hashaddr = srcaddr; + } + + /* Check srcaddr or srclen != 0 */ + if (!srcaddr || !srclen) { + puts("ERR: srcaddr & srclen should not be 0\n"); + return CMD_RET_USAGE; + } + + flush_dcache_range(srcaddr, + srcaddr + roundup(srclen, ARCH_DMA_MINALIGN)); + + ret = xilinx_pm_request(PM_SECURE_SHA, 0, 0, 0, + ZYNQMP_SHA3_INIT, ret_payload); + if (ret || ret_payload[1]) { + printf("Failed: SHA INIT status:0x%x, errcode:0x%x\n", + ret, ret_payload[1]); + return CMD_RET_FAILURE; + } + + ret = xilinx_pm_request(PM_SECURE_SHA, upper_32_bits((ulong)srcaddr), + lower_32_bits((ulong)srcaddr), + srclen, ZYNQMP_SHA3_UPDATE, ret_payload); + if (ret || ret_payload[1]) { + printf("Failed: SHA UPDATE status:0x%x, errcode:0x%x\n", + ret, ret_payload[1]); + return CMD_RET_FAILURE; + } + + ret = xilinx_pm_request(PM_SECURE_SHA, upper_32_bits((ulong)hashaddr), + lower_32_bits((ulong)hashaddr), + ZYNQMP_SHA3_SIZE, ZYNQMP_SHA3_FINAL, + ret_payload); + if (ret || ret_payload[1]) { + printf("Failed: SHA FINAL status:0x%x, errcode:0x%x\n", + ret, ret_payload[1]); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static struct cmd_tbl cmd_zynqmp_sub[] = { + U_BOOT_CMD_MKENT(secure, 5, 0, do_zynqmp_verify_secure, "", ""), + U_BOOT_CMD_MKENT(pmufw, 4, 0, do_zynqmp_pmufw, "", ""), + U_BOOT_CMD_MKENT(mmio_read, 3, 0, do_zynqmp_mmio_read, "", ""), + U_BOOT_CMD_MKENT(mmio_write, 5, 0, do_zynqmp_mmio_write, "", ""), + U_BOOT_CMD_MKENT(aes, 9, 0, do_zynqmp_aes, "", ""), + U_BOOT_CMD_MKENT(rsa, 7, 0, do_zynqmp_rsa, "", ""), + U_BOOT_CMD_MKENT(sha3, 5, 0, do_zynqmp_sha3, "", ""), +#ifdef CONFIG_DEFINE_TCM_OCM_MMAP + U_BOOT_CMD_MKENT(tcminit, 3, 0, do_zynqmp_tcm_init, "", ""), +#endif +}; + +/** + * do_zynqmp - Handle the "zynqmp" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Processes the zynqmp specific commands + * + * Return: return 0 on success and CMD_RET_USAGE incase of misuse and error + */ +static int do_zynqmp(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct cmd_tbl *c; + int ret = CMD_RET_USAGE; + + if (argc < 2) + return CMD_RET_USAGE; + + c = find_cmd_tbl(argv[1], &cmd_zynqmp_sub[0], + ARRAY_SIZE(cmd_zynqmp_sub)); + if (c) + ret = c->cmd(c, flag, argc, argv); + + return cmd_process_error(c, ret); +} + +/***************************************************/ +U_BOOT_LONGHELP(zynqmp, + "secure src len [key_addr] - verifies secure images of $len bytes\n" + " long at address $src. Optional key_addr\n" + " can be specified if user key needs to\n" + " be used for decryption\n" + "zynqmp mmio_read address - read from address\n" + "zynqmp mmio_write address mask value - write value after masking to\n" + " address\n" + "zynqmp aes srcaddr ivaddr len aesop keysrc dstaddr [keyaddr] -\n" + " Encrypts or decrypts blob of data at src address and puts it\n" + " back to dstaddr using key and iv at keyaddr and ivaddr\n" + " respectively. keysrc value specifies from which source key\n" + " has to be used, it can be User/Device/PUF key. A value of 0\n" + " for KUP(user key),1 for DeviceKey and 2 for PUF key. The\n" + " aesop value specifies the operation which can be 0 for\n" + " decrypt and 1 for encrypt operation\n" +#ifdef CONFIG_DEFINE_TCM_OCM_MMAP + "zynqmp tcminit mode - Initialize the TCM with zeros. TCM needs to be\n" + " initialized before accessing to avoid ECC\n" + " errors. mode specifies in which mode TCM has\n" + " to be initialized. Supported modes will be\n" + " lock(0)/split(1)\n" +#endif + "zynqmp pmufw address size - load PMU FW configuration object\n" + "zynqmp pmufw node <id> - load PMU FW configuration object, <id> in dec\n" + "zynqmp pmufw node close - disable config object loading\n" + " node: keyword, id: NODE_ID in decimal format\n" + "zynqmp rsa srcaddr srclen mod exp rsaop -\n" + " Performs RSA encryption and RSA decryption on blob of data\n" + " at srcaddr and puts it back in srcaddr using modulus and\n" + " public or private exponent\n" + " srclen : must be key size(4096 bits)\n" + " exp : private key exponent for RSA decryption(4096 bits)\n" + " public key exponent for RSA encryption(32 bits)\n" + " rsaop : 0 for RSA Decryption, 1 for RSA Encryption\n" + "zynqmp sha3 srcaddr srclen [key_addr] -\n" + " Generates sha3 hash value for data blob at srcaddr and puts\n" + " 48 bytes hash value into srcaddr\n" + " Optional key_addr can be specified for saving sha3 hash value\n" + " Note: srcaddr/srclen should not be 0\n" + ); + +U_BOOT_CMD( + zynqmp, 9, 1, do_zynqmp, + "ZynqMP sub-system", + zynqmp_help_text +); |