diff --git a/docs/plat/imx8m.rst b/docs/plat/imx8m.rst new file mode 100644 index 0000000..ab33a8a --- /dev/null +++ b/docs/plat/imx8m.rst @@ -0,0 +1,42 @@ +Description +=========== + +The i.MX 8M family of applications processors based on Arm Corte-A53 and Cortex-M4 +cores provide high-performance computing, power efficiency, enhanced system +reliability and embedded security needed to drive the growth of fast-growing +edge node computing, streaming multimedia, and machine learning applications. + +Boot Sequence +============= + +Bootrom --> SPL --> BL31 --> BL33(u-boot) --> Linux kernel + +How to build +============ + +Build Procedure +--------------- + +- Prepare AARCH64 toolchain. + +- Build spl and u-boot firstly, and get binary images: u-boot-spl.bin, + u-boot-nodtb.bin and dtb for the target board. + +- Build TF-A + + Build bl31: + + .. code:: shell + + CROSS_COMPILE=aarch64-linux-gnu- make PLAT= bl31 + + Target_SoC should be "imx8mq" for i.MX8MQ SoC. + +Deploy TF-A Images +----------------- + +TF-A binary(bl31.bin), u-boot-spl.bin u-boot-nodtb.bin and dtb are combined +together to generate a binary file called flash.bin, the imx-mkimage tool is +used to generate flash.bin, and flash.bin needs to be flashed into SD card +with certain offset for BOOT ROM. the u-boot and imx-mkimage will be upstreamed +soon, this doc will be updated once they are ready, and the link will be posted. diff --git a/plat/imx/common/imx_uart_console.S b/plat/imx/common/imx_uart_console.S new file mode 100644 index 0000000..7dbde79 --- /dev/null +++ b/plat/imx/common/imx_uart_console.S @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#define USE_FINISH_CONSOLE_REG_2 +#include +#include +#include "imx_uart.h" + +#define URXD 0x0 /* Receiver Register */ +#define UTXD 0x40 /* Transmitter Register */ +#define UTS 0xb4 /* UART Test Register (mx31) */ +#define URXD_RX_DATA (0xFF) + + .globl console_uart_register + .globl console_uart_init + .globl console_uart_putc + .globl console_uart_getc + +func console_imx_uart_register + mov x7, x30 + mov x6, x3 + cbz x6, register_fail + str x0, [x6, #CONSOLE_T_DRVDATA] + + bl console_imx_uart_init + cbz x0, register_fail + + mov x0, x6 + mov x30, x7 + finish_console_register imx_uart putc=1, getc=1 + +register_fail: + ret x7 +endfunc console_imx_uart_register + +func console_imx_uart_init + mov w0, #1 + ret +endfunc console_imx_uart_init + +func console_imx_uart_putc + ldr x1, [x1, #CONSOLE_T_DRVDATA] + cbz x1, putc_error + + /* Prepare '\r' to '\n' */ + cmp w0, #0xA + b.ne 2f +1: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UTS] + tbz w2, #6, 1b + mov w2, #0xD + str w2, [x1, #UTXD] +2: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UTS] + tbz w2, #6, 2b + str w0, [x1, #UTXD] + ret +putc_error: + mov w0, #-1 + ret +endfunc console_imx_uart_putc + +func console_imx_uart_getc + ldr x0, [x0, #CONSOLE_T_DRVDATA] + cbz x0, getc_error +1: + ldr w1, [x0, #UTS] + tbnz w1, #5, 1b + + ldr w1, [x0, #URXD] + and w0, w1, #URXD_RX_DATA + + ret +getc_error: + mov w0, #-1 + ret +endfunc console_imx_uart_getc diff --git a/plat/imx/common/include/imx_uart.h b/plat/imx/common/include/imx_uart.h new file mode 100644 index 0000000..d2c3968 --- /dev/null +++ b/plat/imx/common/include/imx_uart.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX_UART_H +#define IMX_UART_H + +#include + +#ifndef __ASSEMBLY__ + +typedef struct { + console_t console; + uintptr_t base; +} console_uart_t; + +int console_uart_register(uintptr_t baseaddr, uint32_t clock, uint32_t baud, + console_uart_t *console); +#endif /*__ASSEMBLY__*/ + +#endif /* IMX_UART_H */ diff --git a/plat/imx/imx8m/imx8mq/gpc.c b/plat/imx/imx8m/imx8mq/gpc.c new file mode 100644 index 0000000..187a4ad --- /dev/null +++ b/plat/imx/imx8m/imx8mq/gpc.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void imx_set_cpu_secure_entry(unsigned int core_id, uintptr_t sec_entrypoint) +{ + uint64_t temp_base; + + temp_base = (uint64_t) sec_entrypoint; + temp_base >>= 2; + + mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET + (core_id << 3), + ((uint32_t)(temp_base >> 22) & 0xffff)); + mmio_write_32(IMX_SRC_BASE + SRC_GPR1_OFFSET + (core_id << 3) + 4, + ((uint32_t)temp_base & 0x003fffff)); +} + +/* use wfi power down the core */ +void imx_set_cpu_pwr_off(unsigned int core_id) +{ + /* enable the wfi power down of the core */ + mmio_setbits_32(IMX_GPC_BASE + LPCR_A53_AD, COREx_WFI_PDN(core_id) | + (1 << (core_id + 20))); + /* assert the pcg pcr bit of the core */ + mmio_setbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); +}; + +/* use the sw method to power up the core */ +void imx_set_cpu_pwr_on(unsigned int core_id) +{ + /* clear the wfi power down bit of the core */ + mmio_clrbits_32(IMX_GPC_BASE + LPCR_A53_AD, COREx_WFI_PDN(core_id)); + /* assert the ncpuporeset */ + mmio_clrbits_32(IMX_SRC_BASE + SRC_A53RCR1, (1 << core_id)); + /* assert the pcg pcr bit of the core */ + mmio_setbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); + /* sw power up the core */ + mmio_setbits_32(IMX_GPC_BASE + CPU_PGC_UP_TRG, (1 << core_id)); + + /* wait for the power up finished */ + while ((mmio_read_32(IMX_GPC_BASE + CPU_PGC_UP_TRG) & (1 << core_id)) != 0) + ; + + /* deassert the pcg pcr bit of the core */ + mmio_clrbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); + /* deassert the ncpuporeset */ + mmio_setbits_32(IMX_SRC_BASE + SRC_A53RCR1, (1 << core_id)); +} + +/* if out of lpm, we need to do reverse steps */ +void imx_set_cpu_lpm(unsigned int core_id, bool pdn) +{ + if (pdn) { + /* enable the core WFI PDN & IRQ PUP */ + mmio_setbits_32(IMX_GPC_BASE + LPCR_A53_AD, COREx_WFI_PDN(core_id) | + (1 << (core_id + 20)) | COREx_IRQ_WUP(core_id)); + /* assert the pcg pcr bit of the core */ + mmio_setbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); + } else { + /* disable CORE WFI PDN & IRQ PUP */ + mmio_clrbits_32(IMX_GPC_BASE + LPCR_A53_AD, COREx_WFI_PDN(core_id) | + COREx_IRQ_WUP(core_id)); + /* deassert the pcg pcr bit of the core */ + mmio_setbits_32(IMX_GPC_BASE + COREx_PGC_PCR(core_id), 0x1); + } +} + +void imx_set_sys_wakeup(unsigned int last_core, bool pdn) +{ + /* TODO */ +} + +void imx_pup_pdn_slot_config(int last_core, bool pdn) +{ + if (pdn) { + /* SLOT0 for A53 PLAT power down */ + mmio_setbits_32(IMX_GPC_BASE + SLTx_CFG(0), SLT_PLAT_PDN); + /* SLOT1 for A53 PLAT power up */ + mmio_setbits_32(IMX_GPC_BASE + SLTx_CFG(1), SLT_PLAT_PUP); + /* SLOT2 for A53 primary core power up */ + mmio_setbits_32(IMX_GPC_BASE + SLTx_CFG(2), SLT_COREx_PUP(last_core)); + /* ACK setting: PLAT ACK for PDN, CORE ACK for PUP */ + mmio_clrsetbits_32(IMX_GPC_BASE + PGC_ACK_SEL_A53, 0xFFFFFFFF, + A53_PLAT_PDN_ACK | A53_PLAT_PUP_ACK); + } else { + mmio_clrbits_32(IMX_GPC_BASE + SLTx_CFG(0), 0xFFFFFFFF); + mmio_clrbits_32(IMX_GPC_BASE + SLTx_CFG(1), 0xFFFFFFFF); + mmio_clrbits_32(IMX_GPC_BASE + SLTx_CFG(2), 0xFFFFFFFF); + mmio_clrsetbits_32(IMX_GPC_BASE + PGC_ACK_SEL_A53, 0xFFFFFFFF, + A53_DUMMY_PDN_ACK | A53_DUMMY_PUP_ACK); + } +} + +void imx_set_cluster_standby(bool retention) +{ + /* + * Enable BIT 6 of A53 AD register to make sure system + * don't enter LPM mode. + */ + if (retention) + mmio_setbits_32(IMX_GPC_BASE + LPCR_A53_AD, (1 << 6)); + else + mmio_clrbits_32(IMX_GPC_BASE + LPCR_A53_AD, (1 << 6)); +} + +void imx_set_cluster_powerdown(unsigned int last_core, uint8_t power_state) +{ + uint32_t val; + + if (is_local_state_off(power_state)) { + val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC); + val |= A53_LPM_STOP; /* enable C0-C1's STOP mode */ + val &= ~CPU_CLOCK_ON_LPM; /* disable CPU clock in LPM mode */ + mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val); + + /* enable C2-3's STOP mode */ + mmio_setbits_32(IMX_GPC_BASE + LPCR_A53_BSC2, A53_LPM_STOP); + + /* enable PLAT/SCU power down */ + val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_AD); + val &= ~EN_L2_WFI_PDN; + val |= L2PGE | EN_PLAT_PDN; + val &= ~COREx_IRQ_WUP(last_core); /* disable IRQ PUP for last core */ + val |= COREx_LPM_PUP(last_core); /* enable LPM PUP for last core */ + mmio_write_32(IMX_GPC_BASE + LPCR_A53_AD, val); + + imx_pup_pdn_slot_config(last_core, true); + + /* enable PLAT PGC */ + mmio_setbits_32(IMX_GPC_BASE + A53_PLAT_PGC, 0x1); + } else { + /* clear PLAT PGC */ + mmio_clrbits_32(IMX_GPC_BASE + A53_PLAT_PGC, 0x1); + + /* clear the slot and ack for cluster power down */ + imx_pup_pdn_slot_config(last_core, false); + + val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC); + val &= ~A53_LPM_MASK; /* clear the C0~1 LPM */ + val |= CPU_CLOCK_ON_LPM; /* disable cpu clock in LPM */ + mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val); + + /* set A53 LPM to RUN mode */ + mmio_clrbits_32(IMX_GPC_BASE + LPCR_A53_BSC2, A53_LPM_MASK); + + /* clear PLAT/SCU power down */ + val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_AD); + val |= EN_L2_WFI_PDN; + val &= ~(L2PGE | EN_PLAT_PDN); + val &= ~COREx_LPM_PUP(last_core); /* disable C0's LPM PUP */ + mmio_write_32(IMX_GPC_BASE + LPCR_A53_AD, val); + } +} + +/* config the system level power mode */ +void imx_set_sys_lpm(bool retention) +{ + uint32_t val; + + /* set system DSM mode SLPCR(0x14) */ + val = mmio_read_32(IMX_GPC_BASE + SLPCR); + val &= ~(SLPCR_EN_DSM | SLPCR_VSTBY | SLPCR_SBYOS | + SLPCR_BYPASS_PMIC_READY | SLPCR_RBC_EN); + + if (retention) + val |= (SLPCR_EN_DSM | SLPCR_VSTBY | SLPCR_SBYOS | + SLPCR_BYPASS_PMIC_READY | SLPCR_RBC_EN | + SLPCR_A53_FASTWUP_STOP_MODE); + + mmio_write_32(IMX_GPC_BASE + SLPCR, val); +} + +void imx_set_rbc_count(void) +{ + mmio_setbits_32(IMX_GPC_BASE + SLPCR, 0x3f << SLPCR_RBC_COUNT_SHIFT); +} + +void imx_clear_rbc_count(void) +{ + mmio_clrbits_32(IMX_GPC_BASE + SLPCR, 0x3f << SLPCR_RBC_COUNT_SHIFT); +} + +void imx_gpc_init(void) +{ + uint32_t val; + int i; + /* mask all the interrupt by default */ + /* Due to the hardware design requirement, need to make + * sure GPR interrupt(#32) is unmasked during RUN mode to + * avoid entering DSM mode by mistake. + */ + for (i = 0; i < 4; i++) { + mmio_write_32(IMX_GPC_BASE + IMR1_CORE0_A53 + i * 4, 0xFFFFFFFE); + mmio_write_32(IMX_GPC_BASE + IMR1_CORE1_A53 + i * 4, 0xFFFFFFFE); + mmio_write_32(IMX_GPC_BASE + IMR1_CORE2_A53 + i * 4, 0xFFFFFFFE); + mmio_write_32(IMX_GPC_BASE + IMR1_CORE3_A53 + i * 4, 0xFFFFFFFE); + mmio_write_32(IMX_GPC_BASE + IMR1_CORE0_M4 + i * 4, ~0x0); + } + + /* use external IRQs to wakeup C0~C3 from LPM */ + val = mmio_read_32(IMX_GPC_BASE + LPCR_A53_BSC); + val |= IRQ_SRC_A53_WUP; + /* clear the MASTER0 LPM handshake */ + val &= ~MASTER0_LPM_HSK; + mmio_write_32(IMX_GPC_BASE + LPCR_A53_BSC, val); + + /* mask M4 DSM trigger if M4 is NOT enabled */ + mmio_setbits_32(IMX_GPC_BASE + LPCR_M4, DSM_MODE_MASK); + + /* set all mix/PU in A53 domain */ + mmio_write_32(IMX_GPC_BASE + PGC_CPU_0_1_MAPPING, 0xfffd); + + /* set SCU timming */ + mmio_write_32(IMX_GPC_BASE + PGC_SCU_TIMING, + (0x59 << 10) | 0x5B | (0x2 << 20)); + + /* set DUMMY PDN/PUP ACK by default for A53 domain */ + mmio_write_32(IMX_GPC_BASE + PGC_ACK_SEL_A53, A53_DUMMY_PUP_ACK | + A53_DUMMY_PDN_ACK); + + /* disable DSM mode by default */ + mmio_clrbits_32(IMX_GPC_BASE + SLPCR, DSM_MODE_MASK); + + /* + * USB PHY power up needs to make sure RESET bit in SRC is clear, + * otherwise, the PU power up bit in GPC will NOT self-cleared. + * only need to do it once. + */ + mmio_clrbits_32(IMX_SRC_BASE + SRC_OTG1PHY_SCR, 0x1); + mmio_clrbits_32(IMX_SRC_BASE + SRC_OTG2PHY_SCR, 0x1); +} diff --git a/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c new file mode 100644 index 0000000..45d2a40 --- /dev/null +++ b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +IMPORT_SYM(uintptr_t, __COHERENT_RAM_START__, BL31_COHERENT_RAM_START); +IMPORT_SYM(uintptr_t, __COHERENT_RAM_END__, BL31_COHERENT_RAM_END); +IMPORT_SYM(uintptr_t, __RO_START__, BL31_RO_START); +IMPORT_SYM(uintptr_t, __RO_END__, BL31_RO_END); +IMPORT_SYM(uintptr_t, __RW_START__, BL31_RW_START); +IMPORT_SYM(uintptr_t, __RW_END__, BL31_RW_END); + +static const mmap_region_t imx_mmap[] = { + MAP_REGION_FLAT(GPV_BASE, GPV_SIZE, MT_DEVICE | MT_RW), /* GPV map */ + MAP_REGION_FLAT(IMX_AIPS_BASE, IMX_AIPS_SIZE, MT_DEVICE | MT_RW), /* AIPS map */ + MAP_REGION_FLAT(IMX_GIC_BASE, IMX_GIC_SIZE, MT_DEVICE | MT_RW), /* GIC map */ + {0}, +}; + +static entry_point_info_t bl32_image_ep_info; +static entry_point_info_t bl33_image_ep_info; + +/* get SPSR for BL33 entry */ +static uint32_t get_spsr_for_bl33_entry(void) +{ + unsigned long el_status; + unsigned long mode; + uint32_t spsr; + + /* figure out what mode we enter the non-secure world */ + el_status = read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL2_SHIFT; + el_status &= ID_AA64PFR0_ELX_MASK; + + mode = (el_status) ? MODE_EL2 : MODE_EL1; + + spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); + return spsr; +} + +static void bl31_tz380_setup(void) +{ + unsigned int val; + + val = mmio_read_32(IMX_IOMUX_GPR_BASE + IOMUXC_GPR10); + if ((val & GPR_TZASC_EN) != GPR_TZASC_EN) + return; + + tzc380_init(IMX_TZASC_BASE); + /* + * Need to substact offset 0x40000000 from CPU address when + * programming tzasc region for i.mx8mq. Enable 1G-5G S/NS RW + */ + tzc380_configure_region(0, 0x00000000, TZC_ATTR_REGION_SIZE(TZC_REGION_SIZE_4G) | + TZC_ATTR_REGION_EN_MASK | TZC_ATTR_SP_ALL); +} + +void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, + u_register_t arg2, u_register_t arg3) +{ + int i; + /* enable CSU NS access permission */ + for (i = 0; i < 64; i++) { + mmio_write_32(IMX_CSU_BASE + i * 4, 0xffffffff); + } + +#if DEBUG_CONSOLE + static console_uart_t console; + + console_uart_register(IMX_BOOT_UART_BASE, IMX_BOOT_UART_CLK_IN_HZ, + IMX_CONSOLE_BAUDRATE, &console); +#endif + /* + * tell BL3-1 where the non-secure software image is located + * and the entry state information. + */ + bl33_image_ep_info.pc = PLAT_NS_IMAGE_OFFSET; + bl33_image_ep_info.spsr = get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + + bl31_tz380_setup(); +} + +void bl31_plat_arch_setup(void) +{ + mmap_add_region(BL31_RO_START, BL31_RO_START, (BL31_RO_END - BL31_RO_START), + MT_MEMORY | MT_RO | MT_SECURE); + mmap_add_region(BL31_RW_START, BL31_RW_START, (BL31_RW_END - BL31_RW_START), + MT_MEMORY | MT_RW | MT_SECURE); + + mmap_add(imx_mmap); + +#if USE_COHERENT_MEM + mmap_add_region(BL31_COHERENT_RAM_START, BL31_COHERENT_RAM_START, + BL31_COHERENT_RAM_END - BL31_COHERENT_RAM_START, + MT_DEVICE | MT_RW | MT_SECURE); +#endif + /* setup xlat table */ + init_xlat_tables(); + /* enable the MMU */ + enable_mmu_el3(0); +} + +void bl31_platform_setup(void) +{ + /* init the GICv3 cpu and distributor interface */ + plat_gic_driver_init(); + plat_gic_init(); + + /* gpc init */ + imx_gpc_init(); +} + +entry_point_info_t *bl31_plat_get_next_image_ep_info(unsigned int type) +{ + if (type == NON_SECURE) + return &bl33_image_ep_info; + if (type == SECURE) + return &bl32_image_ep_info; + + return NULL; +} + +unsigned int plat_get_syscnt_freq2(void) +{ + return COUNTER_FREQUENCY; +} + +void bl31_plat_runtime_setup(void) +{ + return; +} diff --git a/plat/imx/imx8m/imx8mq/imx8mq_psci.c b/plat/imx/imx8m/imx8mq/imx8mq_psci.c new file mode 100644 index 0000000..7afe52d --- /dev/null +++ b/plat/imx/imx8m/imx8mq/imx8mq_psci.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0]) +#define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1]) +#define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) + +int imx_pwr_domain_on(u_register_t mpidr) +{ + unsigned int core_id; + uint64_t base_addr = BL31_BASE; + + core_id = MPIDR_AFFLVL0_VAL(mpidr); + + /* set the secure entrypoint */ + imx_set_cpu_secure_entry(core_id, base_addr); + /* power up the core */ + imx_set_cpu_pwr_on(core_id); + + return PSCI_E_SUCCESS; +} + +void imx_pwr_domain_on_finish(const psci_power_state_t *target_state) +{ + /* program the GIC per cpu dist and rdist interface */ + plat_gic_pcpu_init(); + /* enable the GICv3 cpu interface */ + plat_gic_cpuif_enable(); +} + +void imx_pwr_domain_off(const psci_power_state_t *target_state) +{ + uint64_t mpidr = read_mpidr_el1(); + unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); + + /* disable the GIC cpu interface first */ + plat_gic_cpuif_disable(); + /* config the core for power down */ + imx_set_cpu_pwr_off(core_id); +} + +int imx_validate_ns_entrypoint(uintptr_t ns_entrypoint) +{ + /* The non-secure entrypoint should be in RAM space */ + if (ns_entrypoint < PLAT_NS_IMAGE_OFFSET) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +int imx_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + int pwr_lvl = psci_get_pstate_pwrlvl(power_state); + int pwr_type = psci_get_pstate_type(power_state); + int state_id = psci_get_pstate_id(power_state); + + if (pwr_lvl > PLAT_MAX_PWR_LVL) + return PSCI_E_INVALID_PARAMS; + + if (pwr_type == PSTATE_TYPE_STANDBY) { + CORE_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; + CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; + } + + if (pwr_type == PSTATE_TYPE_POWERDOWN && state_id == 0x33) { + CORE_PWR_STATE(req_state) = PLAT_MAX_OFF_STATE; + CLUSTER_PWR_STATE(req_state) = PLAT_MAX_RET_STATE; + } + + return PSCI_E_SUCCESS; +} + +void imx_cpu_standby(plat_local_state_t cpu_state) +{ + dsb(); + write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); + isb(); + + wfi(); + + write_scr_el3(read_scr_el3() & (~SCR_FIQ_BIT)); + isb(); +} + +void imx_domain_suspend(const psci_power_state_t *target_state) +{ + uint64_t base_addr = BL31_BASE; + uint64_t mpidr = read_mpidr_el1(); + unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); + + if (is_local_state_off(CORE_PWR_STATE(target_state))) { + /* disable the cpu interface */ + plat_gic_cpuif_disable(); + imx_set_cpu_secure_entry(core_id, base_addr); + imx_set_cpu_lpm(core_id, true); + } else { + dsb(); + write_scr_el3(read_scr_el3() | SCR_FIQ_BIT); + isb(); + } + + if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) + imx_set_cluster_powerdown(core_id, true); + else + imx_set_cluster_standby(true); + + if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { + imx_set_sys_lpm(true); + } +} + +void imx_domain_suspend_finish(const psci_power_state_t *target_state) +{ + uint64_t mpidr = read_mpidr_el1(); + unsigned int core_id = MPIDR_AFFLVL0_VAL(mpidr); + + /* check the system level status */ + if (is_local_state_retn(SYSTEM_PWR_STATE(target_state))) { + imx_set_sys_lpm(false); + imx_clear_rbc_count(); + } + + /* check the cluster level power status */ + if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) + imx_set_cluster_powerdown(core_id, false); + else + imx_set_cluster_standby(false); + + /* check the core level power status */ + if (is_local_state_off(CORE_PWR_STATE(target_state))) { + /* clear the core lpm setting */ + imx_set_cpu_lpm(core_id, false); + /* enable the gic cpu interface */ + plat_gic_cpuif_enable(); + } else { + write_scr_el3(read_scr_el3() & (~0x4)); + isb(); + } +} + +void imx_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + unsigned int i; + + for (i = IMX_PWR_LVL0; i < PLAT_MAX_PWR_LVL; i++) + req_state->pwr_domain_state[i] = PLAT_STOP_OFF_STATE; + + req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] = PLAT_MAX_RET_STATE; +} + +void __dead2 imx_system_reset(void) +{ + uintptr_t wdog_base = IMX_WDOG_BASE; + unsigned int val; + + /* WDOG_B reset */ + val = mmio_read_16(wdog_base); +#ifdef IMX_WDOG_B_RESET + val = (val & 0x00FF) | WDOG_WCR_WDZST | WDOG_WCR_WDE | + WDOG_WCR_WDT | WDOG_WCR_SRS; +#else + val = (val & 0x00FF) | WDOG_WCR_WDZST | WDOG_WCR_SRS; +#endif + mmio_write_16(wdog_base, val); + + mmio_write_16(wdog_base + WDOG_WSR, 0x5555); + mmio_write_16(wdog_base + WDOG_WSR, 0xaaaa); + while (1) + ; +} + + + +void __dead2 imx_system_off(void) +{ + mmio_write_32(IMX_SNVS_BASE + SNVS_LPCR, SNVS_LPCR_SRTC_ENV | + SNVS_LPCR_DP_EN | SNVS_LPCR_TOP); + + while (1) + ; +} + +void __dead2 imx_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ + if (is_local_state_off(CLUSTER_PWR_STATE(target_state))) + imx_set_rbc_count(); + + while (1) + wfi(); +} + +static const plat_psci_ops_t imx_plat_psci_ops = { + .pwr_domain_on = imx_pwr_domain_on, + .pwr_domain_on_finish = imx_pwr_domain_on_finish, + .pwr_domain_off = imx_pwr_domain_off, + .validate_ns_entrypoint = imx_validate_ns_entrypoint, + .validate_power_state = imx_validate_power_state, + .cpu_standby = imx_cpu_standby, + .pwr_domain_suspend = imx_domain_suspend, + .pwr_domain_suspend_finish = imx_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = imx_pwr_domain_pwr_down_wfi, + .get_sys_suspend_power_state = imx_get_sys_suspend_power_state, + .system_reset = imx_system_reset, + .system_off = imx_system_off, +}; + +/* export the platform specific psci ops */ +int plat_setup_psci_ops(uintptr_t sec_entrypoint, + const plat_psci_ops_t **psci_ops) +{ + imx_mailbox_init(sec_entrypoint); + /* sec_entrypoint is used for warm reset */ + *psci_ops = &imx_plat_psci_ops; + + return 0; +} diff --git a/plat/imx/imx8m/imx8mq/include/platform_def.h b/plat/imx/imx8m/imx8mq/include/platform_def.h new file mode 100644 index 0000000..4957582 --- /dev/null +++ b/plat/imx/imx8m/imx8mq/include/platform_def.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#define PLATFORM_LINKER_FORMAT "elf64-littleaarch64" +#define PLATFORM_LINKER_ARCH aarch64 + +#define PLATFORM_STACK_SIZE 0x800 +#define CACHE_WRITEBACK_GRANULE 64 + +#define PLAT_PRIMARY_CPU 0x0 +#define PLATFORM_MAX_CPU_PER_CLUSTER 4 +#define PLATFORM_CLUSTER_COUNT 1 +#define PLATFORM_CLUSTER0_CORE_COUNT 4 +#define PLATFORM_CLUSTER1_CORE_COUNT 0 +#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER0_CORE_COUNT) + +#define IMX_PWR_LVL0 MPIDR_AFFLVL0 +#define IMX_PWR_LVL1 MPIDR_AFFLVL1 +#define IMX_PWR_LVL2 MPIDR_AFFLVL2 + +#define PWR_DOMAIN_AT_MAX_LVL U(1) +#define PLAT_MAX_PWR_LVL U(2) +#define PLAT_MAX_OFF_STATE U(4) +#define PLAT_MAX_RET_STATE U(1) + +#define PLAT_WAIT_OFF_STATE U(2) +#define PLAT_STOP_OFF_STATE U(3) + +#define BL31_BASE U(0x910000) +#define BL31_LIMIT U(0x920000) +#define BL32_BASE U(0xfe000000) + +/* non-secure uboot base */ +#define PLAT_NS_IMAGE_OFFSET U(0x40200000) + +/* GICv3 base address */ +#define PLAT_GICD_BASE U(0x38800000) +#define PLAT_GICR_BASE U(0x38880000) + +#define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) +#define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) + +#define MAX_XLAT_TABLES 4 +#define MAX_MMAP_REGIONS 14 + +#define HAB_RVT_BASE U(0x00000880) /* HAB_RVT for i.MX8MQ */ + +#define IMX_BOOT_UART_BASE U(0x30860000) +#define IMX_BOOT_UART_CLK_IN_HZ 25000000 /* Select 25Mhz oscillator */ +#define PLAT_CRASH_UART_BASE IMX_BOOT_UART_BASE +#define PLAT_CRASH_UART_CLK_IN_HZ 25000000 +#define IMX_CONSOLE_BAUDRATE 115200 + +#define IMX_AIPS_BASE U(0x30200000) +#define IMX_AIPS_SIZE U(0xC00000) +#define IMX_AIPS1_BASE U(0x30200000) +#define IMX_AIPS3_ARB_BASE U(0x30800000) +#define IMX_ANAMIX_BASE U(0x30360000) +#define IMX_CCM_BASE U(0x30380000) +#define IMX_SRC_BASE U(0x30390000) +#define IMX_GPC_BASE U(0x303a0000) +#define IMX_RDC_BASE U(0x303d0000) +#define IMX_CSU_BASE U(0x303e0000) +#define IMX_WDOG_BASE U(0x30280000) +#define IMX_SNVS_BASE U(0x30370000) +#define IMX_NOC_BASE U(0x32700000) +#define IMX_TZASC_BASE U(0x32F80000) +#define IMX_IOMUX_GPR_BASE U(0x30340000) +#define IMX_DDRC_BASE U(0x3d400000) +#define IMX_DDRPHY_BASE U(0x3c000000) +#define IMX_DDR_IPS_BASE U(0x3d000000) +#define IMX_ROM_BASE U(0x00000000) + +#define AIPSTZ1_BASE U(0x301f0000) +#define AIPSTZ2_BASE U(0x305f0000) +#define AIPSTZ3_BASE U(0x309f0000) +#define AIPSTZ4_BASE U(0x32df0000) + +#define GPV_BASE U(0x32000000) +#define GPV_SIZE U(0x800000) +#define IMX_GIC_BASE PLAT_GICD_BASE +#define IMX_GIC_SIZE U(0x200000) + +#define WDOG_WSR U(0x2) +#define WDOG_WCR_WDZST BIT(0) +#define WDOG_WCR_WDBG BIT(1) +#define WDOG_WCR_WDE BIT(2) +#define WDOG_WCR_WDT BIT(3) +#define WDOG_WCR_SRS BIT(4) +#define WDOG_WCR_WDA BIT(5) +#define WDOG_WCR_SRE BIT(6) +#define WDOG_WCR_WDW BIT(7) + +#define SRC_A53RCR0 U(0x4) +#define SRC_A53RCR1 U(0x8) +#define SRC_OTG1PHY_SCR U(0x20) +#define SRC_OTG2PHY_SCR U(0x24) +#define SRC_GPR1_OFFSET U(0x74) + +#define SNVS_LPCR U(0x38) +#define SNVS_LPCR_SRTC_ENV BIT(0) +#define SNVS_LPCR_DP_EN BIT(5) +#define SNVS_LPCR_TOP BIT(6) + + +#define IOMUXC_GPR10 U(0x28) +#define GPR_TZASC_EN BIT(0) +#define GPR_TZASC_EN_LOCK BIT(16) + +#define OCRAM_S_BASE U(0x00180000) +#define OCRAM_S_SIZE U(0x8000) +#define OCRAM_S_LIMIT (OCRAM_S_BASE + OCRAM_S_SIZE) + +#define COUNTER_FREQUENCY 8000000 /* 8MHz */ + +#define DEBUG_CONSOLE 0 +#define IMX_WDOG_B_RESET +#define PLAT_IMX8M 1 diff --git a/plat/imx/imx8m/imx8mq/platform.mk b/plat/imx/imx8m/imx8mq/platform.mk new file mode 100644 index 0000000..0255268 --- /dev/null +++ b/plat/imx/imx8m/imx8mq/platform.mk @@ -0,0 +1,36 @@ +# +# Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +PLAT_INCLUDES := -Iplat/imx/common/include \ + -Iplat/imx/imx8m/include \ + -Iplat/imx/imx8m/imx8mq/include + +IMX_GIC_SOURCES := drivers/arm/gic/v3/gicv3_helpers.c \ + drivers/arm/gic/v3/arm_gicv3_common.c \ + drivers/arm/gic/v3/gic500.c \ + drivers/arm/gic/v3/gicv3_main.c \ + drivers/arm/gic/common/gic_common.c \ + plat/common/plat_gicv3.c \ + plat/common/plat_psci_common.c \ + plat/imx/common/plat_imx8_gic.c + +BL31_SOURCES += plat/imx/common/imx8_helpers.S \ + plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c \ + plat/imx/imx8m/imx8mq/imx8mq_psci.c \ + plat/imx/imx8m/imx8mq/gpc.c \ + plat/imx/common/imx8_topology.c \ + plat/imx/common/imx_uart_console.S \ + lib/xlat_tables/aarch64/xlat_tables.c \ + lib/xlat_tables/xlat_tables_common.c \ + lib/cpus/aarch64/cortex_a53.S \ + drivers/console/aarch64/console.S \ + drivers/arm/tzc/tzc380.c \ + ${IMX_GIC_SOURCES} + +USE_COHERENT_MEM := 1 +RESET_TO_BL31 := 1 +A53_DISABLE_NON_TEMPORAL_HINT := 0 +MULTI_CONSOLE_API := 1 diff --git a/plat/imx/imx8m/include/gpc.h b/plat/imx/imx8m/include/gpc.h new file mode 100644 index 0000000..6fdf6ad --- /dev/null +++ b/plat/imx/imx8m/include/gpc.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IMX8M_GPC_H +#define IMX8M_GPC_H + +#define LPCR_A53_BSC 0x0 +#define LPCR_A53_BSC2 0x108 +#define LPCR_A53_AD 0x4 +#define LPCR_M4 0x8 +#define SLPCR 0x14 +#define MST_CPU_MAPPING 0x18 +#define MLPCR 0x20 +#define PGC_ACK_SEL_A53 0x24 +#define IMR1_CORE0_A53 0x30 +#define IMR1_CORE1_A53 0x40 +#define IMR1_CORE2_A53 0x1C0 +#define IMR1_CORE3_A53 0x1D0 +#define IMR1_CORE0_M4 0x50 +#define SLT0_CFG 0xB0 +#define GPC_PU_PWRHSK 0x1FC +#define PGC_CPU_0_1_MAPPING 0xEC +#define CPU_PGC_UP_TRG 0xF0 +#define PU_PGC_UP_TRG 0xF8 +#define CPU_PGC_DN_TRG 0xFC +#define PU_PGC_DN_TRG 0x104 +#define A53_CORE0_PGC 0x800 +#define A53_PLAT_PGC 0x900 +#define PGC_SCU_TIMING 0x910 + +#define MASK_DSM_TRIGGER_A53 BIT(31) +#define IRQ_SRC_A53_WUP BIT(30) +#define IRQ_SRC_C1 BIT(29) +#define IRQ_SRC_C0 BIT(28) +#define IRQ_SRC_C3 BIT(23) +#define IRQ_SRC_C2 BIT(22) +#define CPU_CLOCK_ON_LPM BIT(14) +#define MASTER0_LPM_HSK BIT(6) + +#define L2PGE BIT(31) +#define EN_L2_WFI_PDN BIT(5) +#define EN_PLAT_PDN BIT(4) + +#define SLPCR_EN_DSM BIT(31) +#define SLPCR_RBC_EN BIT(30) +#define SLPCR_A53_FASTWUP_STOP_MODE BIT(17) +#define SLPCR_A53_FASTWUP_WAIT_MODE BIT(16) +#define SLPCR_VSTBY BIT(2) +#define SLPCR_SBYOS BIT(1) +#define SLPCR_BYPASS_PMIC_READY BIT(0) +#define SLPCR_RBC_COUNT_SHIFT 24 + +#define A53_DUMMY_PDN_ACK BIT(15) +#define A53_DUMMY_PUP_ACK BIT(31) +#define A53_PLAT_PDN_ACK BIT(2) +#define A53_PLAT_PUP_ACK BIT(18) + +#define SLT_PLAT_PDN BIT(8) +#define SLT_PLAT_PUP BIT(9) + +/* helper macro */ +#define A53_LPM_MASK U(0xF) +#define A53_LPM_WAIT U(0x5) +#define A53_LPM_STOP U(0xA) + +#define DSM_MODE_MASK BIT(31) + +#define A53_CORE_WUP_SRC(core_id) (1 << ((core_id) < 2 ? 28 + (core_id) : 22 + (core_id) - 2)) +#define COREx_PGC_PCR(core_id) (0x800 + (core_id) * 0x40) +#define COREx_WFI_PDN(core_id) (1 << ((core_id) < 2 ? (core_id) * 2 : ((core_id) - 2) * 2 + 16)) +#define COREx_IRQ_WUP(core_id) ((core_id) < 2 ? (1 << ((core_id) * 2 + 8)) : (1 << ((core_id) * 2 + 20))) +#define COREx_LPM_PUP(core_id) ((core_id) < 2 ? (1 << ((core_id) * 2 + 9)) : (1 << ((core_id) * 2 + 21))) +#define SLTx_CFG(n) ((SLT0_CFG + ((n) * 4))) +#define SLT_COREx_PUP(core_id) (0x2 << ((core_id) * 2)) + +/* function declare */ +void imx_gpc_init(void); +void imx_set_cpu_secure_entry(unsigned int core_index, uintptr_t sec_entrypoint); +void imx_set_cpu_pwr_off(unsigned int core_index); +void imx_set_cpu_pwr_on(unsigned int core_index); +void imx_set_cpu_lpm(unsigned int core_index, bool pdn); +void imx_set_cluster_standby(bool retention); +void imx_set_cluster_powerdown(unsigned int last_core, uint8_t power_state); +void imx_set_sys_lpm(bool retention); +void imx_set_rbc_count(void); +void imx_clear_rbc_count(void); + +#endif /*IMX8M_GPC_H */