diff --git a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c index fd054be..b144c84 100644 --- a/plat/xilinx/zynqmp/aarch64/zynqmp_common.c +++ b/plat/xilinx/zynqmp/aarch64/zynqmp_common.c @@ -8,8 +8,11 @@ #include #include #include +#include +#include #include #include "../zynqmp_private.h" +#include "pm_api_sys.h" /* * Table of regions to map using the MMU. @@ -59,40 +62,103 @@ #if LOG_LEVEL >= LOG_LEVEL_NOTICE static const struct { unsigned int id; + unsigned int ver; char *name; + bool evexists; } zynqmp_devices[] = { { .id = 0x10, .name = "3EG", }, { + .id = 0x10, + .ver = 0x2c, + .name = "3CG", + }, + { .id = 0x11, .name = "2EG", }, { + .id = 0x11, + .ver = 0x2c, + .name = "2CG", + }, + { .id = 0x20, .name = "5EV", + .evexists = true, + }, + { + .id = 0x20, + .ver = 0x100, + .name = "5EG", + .evexists = true, + }, + { + .id = 0x20, + .ver = 0x12c, + .name = "5CG", }, { .id = 0x21, .name = "4EV", + .evexists = true, + }, + { + .id = 0x21, + .ver = 0x100, + .name = "4EG", + .evexists = true, + }, + { + .id = 0x21, + .ver = 0x12c, + .name = "4CG", }, { .id = 0x30, .name = "7EV", + .evexists = true, + }, + { + .id = 0x30, + .ver = 0x100, + .name = "7EG", + .evexists = true, + }, + { + .id = 0x30, + .ver = 0x12c, + .name = "7CG", }, { .id = 0x38, .name = "9EG", }, { + .id = 0x38, + .ver = 0x2c, + .name = "9CG", + }, + { .id = 0x39, .name = "6EG", }, { + .id = 0x39, + .ver = 0x2c, + .name = "6CG", + }, + { .id = 0x40, .name = "11EG", }, + { /* For testing purpose only */ + .id = 0x50, + .ver = 0x2c, + .name = "15CG", + }, { .id = 0x50, .name = "15EG", @@ -105,30 +171,75 @@ .id = 0x59, .name = "17EG", }, + { + .id = 0x60, + .name = "28DR", + }, + { + .id = 0x61, + .name = "21DR", + }, + { + .id = 0x62, + .name = "29DR", + }, + { + .id = 0x63, + .name = "23DR", + }, + { + .id = 0x64, + .name = "27DR", + }, + { + .id = 0x65, + .name = "25DR", + }, }; -static unsigned int zynqmp_get_silicon_id(void) -{ - uint32_t id; - - id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET); - - id &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | ZYNQMP_CSU_IDCODE_SVD_MASK; - id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; - - return id; -} +#define ZYNQMP_PL_STATUS_BIT 9 +#define ZYNQMP_PL_STATUS_MASK BIT(ZYNQMP_PL_STATUS_BIT) +#define ZYNQMP_CSU_VERSION_MASK ~(ZYNQMP_PL_STATUS_MASK) static char *zynqmp_get_silicon_idcode_name(void) { - unsigned int id; + uint32_t id, ver, chipid[2]; + size_t i, j, len; + enum pm_ret_status ret; + const char *name = "EG/EV"; - id = zynqmp_get_silicon_id(); - for (size_t i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) { - if (zynqmp_devices[i].id == id) - return zynqmp_devices[i].name; + ret = pm_get_chipid(chipid); + if (ret) + return "UNKN"; + + id = chipid[0] & (ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | + ZYNQMP_CSU_IDCODE_SVD_MASK); + id >>= ZYNQMP_CSU_IDCODE_SVD_SHIFT; + ver = chipid[1] >> ZYNQMP_EFUSE_IPDISABLE_SHIFT; + + for (i = 0; i < ARRAY_SIZE(zynqmp_devices); i++) { + if (zynqmp_devices[i].id == id && + zynqmp_devices[i].ver == (ver & ZYNQMP_CSU_VERSION_MASK)) + break; } - return "UNKN"; + + if (i >= ARRAY_SIZE(zynqmp_devices)) + return "UNKN"; + + if (!zynqmp_devices[i].evexists) + return zynqmp_devices[i].name; + + if (ver & ZYNQMP_PL_STATUS_MASK) + return zynqmp_devices[i].name; + + len = strlen(zynqmp_devices[i].name) - 2; + for (j = 0; j < strlen(name); j++) { + zynqmp_devices[i].name[len] = name[j]; + len++; + } + zynqmp_devices[i].name[len] = '\0'; + + return zynqmp_devices[i].name; } static unsigned int zynqmp_get_rtl_ver(void) @@ -195,60 +306,29 @@ break; } - NOTICE("ATF running on XCZU%s/%s v%d/RTL%d.%d at 0x%x%s\n", + NOTICE("ATF running on XCZU%s/%s v%d/RTL%d.%d at 0x%x\n", zynqmp_print_silicon_idcode(), label, zynqmp_get_ps_ver(), - (rtl & 0xf0) >> 4, rtl & 0xf, BL31_BASE, - zynqmp_is_pmu_up() ? ", with PMU firmware" : ""); + (rtl & 0xf0) >> 4, rtl & 0xf, BL31_BASE); } #else static inline void zynqmp_print_platform_name(void) { } #endif -/* - * Indicator for PMUFW discovery: - * 0 = No FW found - * non-zero = FW is present - */ -static int zynqmp_pmufw_present; - -/* - * zynqmp_discover_pmufw - Discover presence of PMUFW - * - * Discover the presence of PMUFW and store it for later run-time queries - * through zynqmp_is_pmu_up. - * NOTE: This discovery method is fragile and will break if: - * - setting FW_PRESENT is done by PMUFW itself and could be left out in PMUFW - * (be it by error or intentionally) - * - XPPU/XMPU may restrict ATF's access to the PMU address space - */ -static int zynqmp_discover_pmufw(void) -{ - zynqmp_pmufw_present = mmio_read_32(PMU_GLOBAL_CNTRL); - zynqmp_pmufw_present &= PMU_GLOBAL_CNTRL_FW_IS_PRESENT; - - return !!zynqmp_pmufw_present; -} - -/* - * zynqmp_is_pmu_up - Find if PMU firmware is up and running - * - * Return 0 if firmware is not available, non 0 otherwise - */ -int zynqmp_is_pmu_up(void) -{ - return zynqmp_pmufw_present; -} - unsigned int zynqmp_get_bootmode(void) { - uint32_t r = mmio_read_32(CRL_APB_BOOT_MODE_USER); + uint32_t r; + unsigned int ret; + + ret = pm_mmio_read(CRL_APB_BOOT_MODE_USER, &r); + + if (ret != PM_RET_SUCCESS) + r = mmio_read_32(CRL_APB_BOOT_MODE_USER); return r & CRL_APB_BOOT_MODE_MASK; } void zynqmp_config_setup(void) { - zynqmp_discover_pmufw(); zynqmp_print_platform_name(); generic_delay_timer_init(); } diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c index 1edbd0f..0b3106f 100644 --- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c +++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c @@ -36,6 +36,19 @@ } /* + * Set the build time defaults. We want to do this when doing a JTAG boot + * or if we can't find any other config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* * Perform any BL31 specific platform actions. Here is an opportunity to copy * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they * are lost (potentially). This needs to be done before the MMU is initialized @@ -69,15 +82,15 @@ SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); if (zynqmp_get_bootmode() == ZYNQMP_BOOTMODE_JTAG) { - /* use build time defaults in JTAG boot mode */ - bl32_image_ep_info.pc = BL32_BASE; - bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); - bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); - bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, - DISABLE_ALL_EXCEPTIONS); + bl31_set_default_config(); } else { /* use parameters from FSBL */ - fsbl_atf_handover(&bl32_image_ep_info, &bl33_image_ep_info); + enum fsbl_handoff ret = fsbl_atf_handover(&bl32_image_ep_info, + &bl33_image_ep_info); + if (ret == FSBL_HANDOFF_NO_STRUCT) + bl31_set_default_config(); + else if (ret != FSBL_HANDOFF_SUCCESS) + panic(); } NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); @@ -103,6 +116,39 @@ } #endif +#if ZYNQMP_WDT_RESTART +static interrupt_type_handler_t type_el3_interrupt_table[MAX_INTR_EL3]; + +int request_intr_type_el3(uint32_t id, interrupt_type_handler_t handler) +{ + /* Validate 'handler' and 'id' parameters */ + if (!handler || id >= MAX_INTR_EL3) + return -EINVAL; + + /* Check if a handler has already been registered */ + if (type_el3_interrupt_table[id]) + return -EALREADY; + + type_el3_interrupt_table[id] = handler; + + return 0; +} + +static uint64_t rdo_el3_interrupt_handler(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + uint32_t intr_id; + interrupt_type_handler_t handler; + + intr_id = plat_ic_get_pending_interrupt_id(); + handler = type_el3_interrupt_table[intr_id]; + if (handler != NULL) + handler(intr_id, flags, handle, cookie); + + return 0; +} +#endif + void bl31_platform_setup(void) { /* Initialize the gic cpu and distributor interfaces */ @@ -113,6 +159,16 @@ void bl31_plat_runtime_setup(void) { +#if ZYNQMP_WDT_RESTART + uint64_t flags = 0; + uint64_t rc; + + set_interrupt_rm_flag(flags, NON_SECURE); + rc = register_interrupt_type_handler(INTR_TYPE_EL3, + rdo_el3_interrupt_handler, flags); + if (rc) + panic(); +#endif } /* diff --git a/plat/xilinx/zynqmp/include/platform_def.h b/plat/xilinx/zynqmp/include/platform_def.h index e74b9bb..ebbc8c2 100644 --- a/plat/xilinx/zynqmp/include/platform_def.h +++ b/plat/xilinx/zynqmp/include/platform_def.h @@ -96,6 +96,7 @@ * terminology. On a GICv2 system or mode, the lists will be merged and treated * as Group 0 interrupts. */ +#if !ZYNQMP_WDT_RESTART #define PLAT_ARM_G1S_IRQ_PROPS(grp) \ INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_LEVEL), \ @@ -115,6 +116,29 @@ GIC_INTR_CFG_EDGE), \ INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, grp, \ GIC_INTR_CFG_EDGE) +#else +#define PLAT_ARM_G1S_IRQ_PROPS(grp) \ + INTR_PROP_DESC(ARM_IRQ_SEC_PHY_TIMER, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_LEVEL), \ + INTR_PROP_DESC(IRQ_TTC3_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_0, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_1, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_2, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_3, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_4, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_5, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_6, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE), \ + INTR_PROP_DESC(ARM_IRQ_SEC_SGI_7, GIC_HIGHEST_SEC_PRIORITY, grp, \ + GIC_INTR_CFG_EDGE) +#endif #define PLAT_ARM_G0_IRQ_PROPS(grp) diff --git a/plat/xilinx/zynqmp/plat_psci.c b/plat/xilinx/zynqmp/plat_psci.c index c9fd361..a82f696 100644 --- a/plat/xilinx/zynqmp/plat_psci.c +++ b/plat/xilinx/zynqmp/plat_psci.c @@ -27,46 +27,6 @@ wfi(); } -static int zynqmp_nopmu_pwr_domain_on(u_register_t mpidr) -{ - uint32_t r; - unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); - - VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); - - if (cpu_id == -1) - return PSCI_E_INTERN_FAIL; - - /* program RVBAR */ - mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry); - mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32); - - /* clear VINITHI */ - r = mmio_read_32(APU_CONFIG_0); - r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id); - mmio_write_32(APU_CONFIG_0, r); - - /* clear power down request */ - r = mmio_read_32(APU_PWRCTL); - r &= ~(1 << cpu_id); - mmio_write_32(APU_PWRCTL, r); - - /* power up island */ - mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id); - mmio_write_32(PMU_GLOBAL_REQ_PWRUP_TRIG, 1 << cpu_id); - /* FIXME: we should have a way to break out */ - while (mmio_read_32(PMU_GLOBAL_REQ_PWRUP_STATUS) & (1 << cpu_id)) - ; - - /* release core reset */ - r = mmio_read_32(CRF_APB_RST_FPD_APU); - r &= ~((CRF_APB_RST_FPD_APU_ACPU_PWRON_RESET | - CRF_APB_RST_FPD_APU_ACPU_RESET) << cpu_id); - mmio_write_32(CRF_APB_RST_FPD_APU, r); - - return PSCI_E_SUCCESS; -} - static int zynqmp_pwr_domain_on(u_register_t mpidr) { unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); @@ -78,6 +38,8 @@ return PSCI_E_INTERN_FAIL; proc = pm_get_proc(cpu_id); + /* Clear power down request */ + pm_client_wakeup(proc); /* Send request to PMU to wake up selected APU CPU core */ pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING); @@ -85,24 +47,6 @@ return PSCI_E_SUCCESS; } -static void zynqmp_nopmu_pwr_domain_off(const psci_power_state_t *target_state) -{ - uint32_t r; - unsigned int cpu_id = plat_my_core_pos(); - - for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) - VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", - __func__, i, target_state->pwr_domain_state[i]); - - /* Prevent interrupts from spuriously waking up this cpu */ - gicv2_cpuif_disable(); - - /* set power down request */ - r = mmio_read_32(APU_PWRCTL); - r |= (1 << cpu_id); - mmio_write_32(APU_PWRCTL, r); -} - static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state) { unsigned int cpu_id = plat_my_core_pos(); @@ -126,33 +70,6 @@ pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); } -static void zynqmp_nopmu_pwr_domain_suspend(const psci_power_state_t *target_state) -{ - uint32_t r; - unsigned int cpu_id = plat_my_core_pos(); - - for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) - VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", - __func__, i, target_state->pwr_domain_state[i]); - - /* set power down request */ - r = mmio_read_32(APU_PWRCTL); - r |= (1 << cpu_id); - mmio_write_32(APU_PWRCTL, r); - - /* program RVBAR */ - mmio_write_32(APU_RVBAR_L_0 + (cpu_id << 3), zynqmp_sec_entry); - mmio_write_32(APU_RVBAR_H_0 + (cpu_id << 3), zynqmp_sec_entry >> 32); - - /* clear VINITHI */ - r = mmio_read_32(APU_CONFIG_0); - r &= ~(1 << APU_CONFIG_0_VINITHI_SHIFT << cpu_id); - mmio_write_32(APU_CONFIG_0, r); - - /* enable power up on IRQ */ - mmio_write_32(PMU_GLOBAL_REQ_PWRUP_EN, 1 << cpu_id); -} - static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state) { unsigned int state; @@ -186,24 +103,6 @@ gicv2_pcpu_distif_init(); } -static void zynqmp_nopmu_pwr_domain_suspend_finish(const psci_power_state_t *target_state) -{ - uint32_t r; - unsigned int cpu_id = plat_my_core_pos(); - - for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) - VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", - __func__, i, target_state->pwr_domain_state[i]); - - /* disable power up on IRQ */ - mmio_write_32(PMU_GLOBAL_REQ_PWRUP_DIS, 1 << cpu_id); - - /* clear powerdown bit */ - r = mmio_read_32(APU_PWRCTL); - r &= ~(1 << cpu_id); - mmio_write_32(APU_PWRCTL, r); -} - static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) { unsigned int cpu_id = plat_my_core_pos(); @@ -230,15 +129,6 @@ /******************************************************************************* * ZynqMP handlers to shutdown/reboot the system ******************************************************************************/ -static void __dead2 zynqmp_nopmu_system_off(void) -{ - ERROR("ZynqMP System Off: operation not handled.\n"); - - /* disable coherency */ - plat_arm_interconnect_exit_coherency(); - - panic(); -} static void __dead2 zynqmp_system_off(void) { @@ -247,29 +137,7 @@ /* Send the power down request to the PMU */ pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN, - PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM); - - while (1) - wfi(); -} - -static void __dead2 zynqmp_nopmu_system_reset(void) -{ - /* - * This currently triggers a system reset. I.e. the whole - * system will be reset! Including RPUs, PMU, PL, etc. - */ - - /* disable coherency */ - plat_arm_interconnect_exit_coherency(); - - /* bypass RPLL (needed on 1.0 silicon) */ - uint32_t reg = mmio_read_32(CRL_APB_RPLL_CTRL); - reg |= CRL_APB_RPLL_CTRL_BYPASS; - mmio_write_32(CRL_APB_RPLL_CTRL, reg); - - /* trigger system reset */ - mmio_write_32(CRL_APB_RESET_CTRL, CRL_APB_RESET_CTRL_SOFT_RESET); + pm_get_shutdown_scope()); while (1) wfi(); @@ -282,7 +150,7 @@ /* Send the system reset request to the PMU */ pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, - PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM); + pm_get_shutdown_scope()); while (1) wfi(); @@ -341,20 +209,6 @@ .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, }; -static const struct plat_psci_ops zynqmp_nopmu_psci_ops = { - .cpu_standby = zynqmp_cpu_standby, - .pwr_domain_on = zynqmp_nopmu_pwr_domain_on, - .pwr_domain_off = zynqmp_nopmu_pwr_domain_off, - .pwr_domain_suspend = zynqmp_nopmu_pwr_domain_suspend, - .pwr_domain_on_finish = zynqmp_pwr_domain_on_finish, - .pwr_domain_suspend_finish = zynqmp_nopmu_pwr_domain_suspend_finish, - .system_off = zynqmp_nopmu_system_off, - .system_reset = zynqmp_nopmu_system_reset, - .validate_power_state = zynqmp_validate_power_state, - .validate_ns_entrypoint = zynqmp_validate_ns_entrypoint, - .get_sys_suspend_power_state = zynqmp_get_sys_suspend_power_state, -}; - /******************************************************************************* * Export the platform specific power ops. ******************************************************************************/ @@ -363,10 +217,7 @@ { zynqmp_sec_entry = sec_entrypoint; - if (zynqmp_is_pmu_up()) - *psci_ops = &zynqmp_psci_ops; - else - *psci_ops = &zynqmp_nopmu_psci_ops; + *psci_ops = &zynqmp_psci_ops; return 0; } diff --git a/plat/xilinx/zynqmp/plat_startup.c b/plat/xilinx/zynqmp/plat_startup.c index 18d150c..d3e182c 100644 --- a/plat/xilinx/zynqmp/plat_startup.c +++ b/plat/xilinx/zynqmp/plat_startup.c @@ -9,6 +9,7 @@ #include #include #include "zynqmp_def.h" +#include "zynqmp_private.h" /* * ATFHandoffParams @@ -147,8 +148,11 @@ * * Process the handoff paramters from the FSBL and populate the BL32 and BL33 * image info structures accordingly. + * + * Return: Return the status of the handoff. The value will be from the + * fsbl_handoff enum. */ -void fsbl_atf_handover(entry_point_info_t *bl32, entry_point_info_t *bl33) +enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32, entry_point_info_t *bl33) { uint64_t atf_handoff_addr; const struct xfsbl_atf_handoff_params *ATFHandoffParams; @@ -157,8 +161,8 @@ assert((atf_handoff_addr < BL31_BASE) || (atf_handoff_addr > (uint64_t)&__BL31_END__)); if (!atf_handoff_addr) { - ERROR("BL31: No ATF handoff structure passed\n"); - panic(); + WARN("BL31: No ATF handoff structure passed\n"); + return FSBL_HANDOFF_NO_STRUCT; } ATFHandoffParams = (struct xfsbl_atf_handoff_params *)atf_handoff_addr; @@ -168,7 +172,7 @@ (ATFHandoffParams->magic[3] != 'X')) { ERROR("BL31: invalid ATF handoff structure at %llx\n", atf_handoff_addr); - panic(); + return FSBL_HANDOFF_INVAL_STRUCT; } VERBOSE("BL31: ATF handoff params at:0x%llx, entries:%u\n", @@ -176,7 +180,7 @@ if (ATFHandoffParams->num_entries > FSBL_MAX_PARTITIONS) { ERROR("BL31: ATF handoff params: too many partitions (%u/%u)\n", ATFHandoffParams->num_entries, FSBL_MAX_PARTITIONS); - panic(); + return FSBL_HANDOFF_TOO_MANY_PARTS; } /* @@ -261,4 +265,6 @@ else EP_SET_EE(image->h.attr, EP_EE_LITTLE); } + + return FSBL_HANDOFF_SUCCESS; } diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk index e49a9cd..3ac9db9 100644 --- a/plat/xilinx/zynqmp/platform.mk +++ b/plat/xilinx/zynqmp/platform.mk @@ -9,6 +9,7 @@ PSCI_EXTENDED_STATE_ID := 1 A53_DISABLE_NON_TEMPORAL_HINT := 0 SEPARATE_CODE_AND_RODATA := 1 +ZYNQMP_WDT_RESTART := 0 override RESET_TO_BL31 := 1 # Do not enable SVE @@ -41,6 +42,10 @@ ZYNQMP_CONSOLE ?= cadence $(eval $(call add_define_val,ZYNQMP_CONSOLE,ZYNQMP_CONSOLE_ID_${ZYNQMP_CONSOLE})) +ifdef ZYNQMP_WDT_RESTART +$(eval $(call add_define,ZYNQMP_WDT_RESTART)) +endif + PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ -Iinclude/plat/arm/common/aarch64/ \ -Iplat/xilinx/zynqmp/include/ \ diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index d75f7c0..133043d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -19,6 +19,19 @@ #include "pm_common.h" #include "pm_ipi.h" +/* default shutdown/reboot scope is system(2) */ +static unsigned int pm_shutdown_scope = PMF_SHUTDOWN_SUBTYPE_SYSTEM; + +/** + * pm_get_shutdown_scope() - Get the currently set shutdown scope + * + * @return Shutdown scope value + */ +unsigned int pm_get_shutdown_scope(void) +{ + return pm_shutdown_scope; +} + /** * Assigning of argument values into array elements. */ @@ -130,10 +143,7 @@ { uint32_t payload[PAYLOAD_ARG_CNT]; uint64_t encoded_address; - const struct pm_proc *proc = pm_get_proc_by_node(target); - /* invoke APU-specific code for waking up another APU core */ - pm_client_wakeup(proc); /* encode set Address into 1st bit of address */ encoded_address = address; @@ -218,7 +228,8 @@ /** * pm_system_shutdown() - PM call to request a system shutdown or restart - * @restart Shutdown or restart? 0 for shutdown, 1 for restart + * @type Shutdown or restart? 0=shutdown, 1=restart, 2=setscope + * @subtype Scope: 0=APU-subsystem, 1=PS, 2=system * * @return Returns status, either success or error+reason */ @@ -226,8 +237,14 @@ { uint32_t payload[PAYLOAD_ARG_CNT]; + if (type == PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY) { + /* Setting scope for subsequent PSCI reboot or shutdown */ + pm_shutdown_scope = subtype; + return PM_RET_SUCCESS; + } + PM_PACK_PAYLOAD3(payload, PM_SYSTEM_SHUTDOWN, type, subtype); - return pm_ipi_send(primary_proc, payload); + return pm_ipi_send_non_blocking(primary_proc, payload); } /* APIs for managing PM slaves: */ @@ -342,18 +359,38 @@ } /** - * pm_get_node_status() - PM call to request a node's current power state - * @nid Node id of the slave + * pm_init_finalize() - Call to notify PMU firmware that master has power + * management enabled and that it has finished its + * initialization + * + * @return Status returned by the PMU firmware + */ +enum pm_ret_status pm_init_finalize(void) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD1(payload, PM_INIT_FINALIZE); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_get_node_status() - PM call to request a node's current status + * @nid Node id + * @ret_buff Buffer for the return values: + * [0] - Current power state of the node + * [1] - Current requirements for the node (slave nodes only) + * [2] - Current usage status for the node (slave nodes only) * * @return Returns status, either success or error+reason */ -enum pm_ret_status pm_get_node_status(enum pm_node_id nid) +enum pm_ret_status pm_get_node_status(enum pm_node_id nid, + uint32_t *ret_buff) { - /* TODO: Add power state argument!! */ uint32_t payload[PAYLOAD_ARG_CNT]; PM_PACK_PAYLOAD2(payload, PM_GET_NODE_STATUS, nid); - return pm_ipi_send(primary_proc, payload); + return pm_ipi_send_sync(primary_proc, payload, ret_buff, 3); } /** @@ -501,7 +538,7 @@ /* Send request to the PMU */ PM_PACK_PAYLOAD5(payload, PM_FPGA_LOAD, address_high, address_low, size, flags); - return pm_ipi_send(primary_proc, payload); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } /** @@ -538,15 +575,30 @@ } /** - * pm_get_callbackdata() - Read from IPI response buffer - * @data - array of PAYLOAD_ARG_CNT elements + * pm_secure_rsaaes() - Load the secure images. * - * Read value from ipi buffer response buffer. + * This function provides access to the xilsecure library to load + * the authenticated, encrypted, and authenicated/encrypted images. + * + * address_low: lower 32-bit Linear memory space address + * + * address_high: higher 32-bit Linear memory space address + * + * size: Number of 32bit words + * + * @return Returns status, either success or error+reason */ -void pm_get_callbackdata(uint32_t *data, size_t count) +enum pm_ret_status pm_secure_rsaaes(uint32_t address_low, + uint32_t address_high, + uint32_t size, + uint32_t flags) { - pm_ipi_buff_read_callb(data, count); - pm_ipi_irq_clear(primary_proc); + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_RSA_AES, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); } /** @@ -1074,3 +1126,43 @@ return ret; } + +enum pm_ret_status pm_sha_hash(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_SHA, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +enum pm_ret_status pm_rsa_core(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_RSA, address_high, address_low, + size, flags); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +enum pm_ret_status pm_secure_image(uint32_t address_low, + uint32_t address_high, + uint32_t key_lo, + uint32_t key_hi, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD5(payload, PM_SECURE_IMAGE, address_high, address_low, + key_hi, key_lo); + return pm_ipi_send_sync(primary_proc, payload, value, 2); +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index c6de560..55a8a6e 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -76,7 +76,9 @@ /* Miscellaneous API functions */ enum pm_ret_status pm_get_api_version(unsigned int *version); enum pm_ret_status pm_set_configuration(unsigned int phys_addr); -enum pm_ret_status pm_get_node_status(enum pm_node_id node); +enum pm_ret_status pm_init_finalize(void); +enum pm_ret_status pm_get_node_status(enum pm_node_id node, + uint32_t *ret_buff); enum pm_ret_status pm_register_notifier(enum pm_node_id nid, unsigned int event, unsigned int wake, @@ -107,7 +109,11 @@ enum pm_ret_status pm_fpga_get_status(unsigned int *value); enum pm_ret_status pm_get_chipid(uint32_t *value); -void pm_get_callbackdata(uint32_t *data, size_t count); +enum pm_ret_status pm_secure_rsaaes(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags); +unsigned int pm_get_shutdown_scope(void); enum pm_ret_status pm_pinctrl_request(unsigned int pin); enum pm_ret_status pm_pinctrl_release(unsigned int pin); enum pm_ret_status pm_pinctrl_get_function(unsigned int pin, @@ -146,4 +152,17 @@ unsigned int arg2, unsigned int arg3, unsigned int *data); +enum pm_ret_status pm_sha_hash(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags); +enum pm_ret_status pm_rsa_core(uint32_t address_high, + uint32_t address_low, + uint32_t size, + uint32_t flags); +enum pm_ret_status pm_secure_image(uint32_t address_low, + uint32_t address_high, + uint32_t key_lo, + uint32_t key_hi, + uint32_t *value); #endif /* _PM_API_SYS_H_ */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.c b/plat/xilinx/zynqmp/pm_service/pm_client.c index 9016fd6..874b8a9 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.c +++ b/plat/xilinx/zynqmp/pm_service/pm_client.c @@ -26,10 +26,15 @@ #define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1) #define UNDEFINED_CPUID (~0) +#define PM_SUSPEND_MODE_STD 0 +#define PM_SUSPEND_MODE_POWER_OFF 1 + DEFINE_BAKERY_LOCK(pm_client_secure_lock); extern const struct pm_ipi apu_ipi; +static uint32_t suspend_mode = PM_SUSPEND_MODE_STD; + /* Order in pm_procs_all array must match cpu ids */ static const struct pm_proc pm_procs_all[] = { { @@ -165,6 +170,19 @@ uint8_t pm_wakeup_nodes_set[NODE_MAX]; uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4; + /* In case of power-off suspend, only NODE_EXTERN must be set */ + if (suspend_mode == PM_SUSPEND_MODE_POWER_OFF) { + enum pm_ret_status ret; + + ret = pm_set_wakeup_source(NODE_APU, NODE_EXTERN, 1); + /** + * If NODE_EXTERN could not be set as wake source, proceed with + * standard suspend (no one will wake the system otherwise) + */ + if (ret == PM_RET_SUCCESS) + return; + } + zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set)); for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) { @@ -305,3 +323,13 @@ bakery_lock_release(&pm_client_secure_lock); } + +enum pm_ret_status pm_set_suspend_mode(uint32_t mode) +{ + if ((mode != PM_SUSPEND_MODE_STD) && + (mode != PM_SUSPEND_MODE_POWER_OFF)) + return PM_RET_ERROR_ARGS; + + suspend_mode = mode; + return PM_RET_SUCCESS; +} diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.h b/plat/xilinx/zynqmp/pm_service/pm_client.h index 16e37d5..070db89 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.h +++ b/plat/xilinx/zynqmp/pm_service/pm_client.h @@ -20,6 +20,7 @@ void pm_client_abort_suspend(void); void pm_client_wakeup(const struct pm_proc *proc); enum pm_ret_status set_ocm_retention(void); +enum pm_ret_status pm_set_suspend_mode(uint32_t mode); /* Global variables to be set in pm_client.c */ extern const struct pm_proc *primary_proc; diff --git a/plat/xilinx/zynqmp/pm_service/pm_defs.h b/plat/xilinx/zynqmp/pm_service/pm_defs.h index 0c46e73..9a8026f 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_defs.h +++ b/plat/xilinx/zynqmp/pm_service/pm_defs.h @@ -62,7 +62,7 @@ PM_RESET_GET_STATUS, PM_MMIO_WRITE, PM_MMIO_READ, - PM_INIT, + PM_INIT_FINALIZE, PM_FPGA_LOAD, PM_FPGA_GET_STATUS, PM_GET_CHIPID, @@ -88,6 +88,7 @@ PM_CLOCK_GETRATE, PM_CLOCK_SETPARENT, PM_CLOCK_GETPARENT, + PM_SECURE_IMAGE, PM_API_MAX }; @@ -141,7 +142,7 @@ NODE_GPIO, NODE_CAN_0, NODE_CAN_1, - NODE_AFI, + NODE_EXTERN, NODE_APLL, NODE_VPLL, NODE_DPLL, @@ -239,11 +240,22 @@ PM_BOOT_ERROR, }; +/** + * @PMF_SHUTDOWN_TYPE_SHUTDOWN: shutdown + * @PMF_SHUTDOWN_TYPE_RESET: reset/reboot + * @PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY: set the shutdown/reboot scope + */ enum pm_shutdown_type { PMF_SHUTDOWN_TYPE_SHUTDOWN, PMF_SHUTDOWN_TYPE_RESET, + PMF_SHUTDOWN_TYPE_SETSCOPE_ONLY, }; +/** + * @PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM: shutdown/reboot APU subsystem only + * @PMF_SHUTDOWN_SUBTYPE_PS_ONLY: shutdown/reboot entire PS (but not PL) + * @PMF_SHUTDOWN_SUBTYPE_SYSTEM: shutdown/reboot entire system + */ enum pm_shutdown_subtype { PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM, PMF_SHUTDOWN_SUBTYPE_PS_ONLY, diff --git a/plat/xilinx/zynqmp/pm_service/pm_ipi.c b/plat/xilinx/zynqmp/pm_service/pm_ipi.c index 58faf0e..dc1ea4d 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_ipi.c +++ b/plat/xilinx/zynqmp/pm_service/pm_ipi.c @@ -26,6 +26,9 @@ #define IPI_BUFFER_REQ_OFFSET 0x0U #define IPI_BUFFER_RESP_OFFSET 0x20U +#define IPI_BLOCKING 1 +#define IPI_NON_BLOCKING 0 + DEFINE_BAKERY_LOCK(pm_secure_lock); const struct pm_ipi apu_ipi = { @@ -63,7 +66,8 @@ * @return Returns status, either success or error+reason */ static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc, - uint32_t payload[PAYLOAD_ARG_CNT]) + uint32_t payload[PAYLOAD_ARG_CNT], + uint32_t is_blocking) { unsigned int offset = 0; uintptr_t buffer_base = proc->ipi->buffer_base + @@ -75,13 +79,39 @@ mmio_write_32(buffer_base + offset, payload[i]); offset += PAYLOAD_ARG_SIZE; } + /* Generate IPI to PMU */ - ipi_mb_notify(proc->ipi->apu_ipi_id, proc->ipi->pmu_ipi_id, 1); + ipi_mb_notify(proc->ipi->apu_ipi_id, proc->ipi->pmu_ipi_id, + is_blocking); return PM_RET_SUCCESS; } /** + * pm_ipi_send_non_blocking() - Sends IPI request to the PMU without blocking + * notification + * @proc Pointer to the processor who is initiating request + * @payload API id and call arguments to be written in IPI buffer + * + * Send an IPI request to the power controller. + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_ipi_send_non_blocking(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]) +{ + enum pm_ret_status ret; + + bakery_lock_get(&pm_secure_lock); + + ret = pm_ipi_send_common(proc, payload, IPI_NON_BLOCKING); + + bakery_lock_release(&pm_secure_lock); + + return ret; +} + +/** * pm_ipi_send() - Sends IPI request to the PMU * @proc Pointer to the processor who is initiating request * @payload API id and call arguments to be written in IPI buffer @@ -97,7 +127,7 @@ bakery_lock_get(&pm_secure_lock); - ret = pm_ipi_send_common(proc, payload); + ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); bakery_lock_release(&pm_secure_lock); @@ -179,7 +209,7 @@ bakery_lock_get(&pm_secure_lock); - ret = pm_ipi_send_common(proc, payload); + ret = pm_ipi_send_common(proc, payload, IPI_BLOCKING); if (ret != PM_RET_SUCCESS) goto unlock; diff --git a/plat/xilinx/zynqmp/pm_service/pm_ipi.h b/plat/xilinx/zynqmp/pm_service/pm_ipi.h index e6b36f5..439dcf4 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_ipi.h +++ b/plat/xilinx/zynqmp/pm_service/pm_ipi.h @@ -13,6 +13,8 @@ enum pm_ret_status pm_ipi_send(const struct pm_proc *proc, uint32_t payload[PAYLOAD_ARG_CNT]); +enum pm_ret_status pm_ipi_send_non_blocking(const struct pm_proc *proc, + uint32_t payload[PAYLOAD_ARG_CNT]); enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc, uint32_t payload[PAYLOAD_ARG_CNT], unsigned int *value, size_t count); diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index 34b3ad4..dd9bbc8 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -10,19 +10,30 @@ */ #include -#include #include -#include #include "../zynqmp_private.h" #include "pm_api_sys.h" #include "pm_client.h" #include "pm_ipi.h" +#if ZYNQMP_WDT_RESTART +#include +#include +#include +#include +#include +#endif -#define PM_GET_CALLBACK_DATA 0xa01 +#define PM_SET_SUSPEND_MODE 0xa02 #define PM_GET_TRUSTZONE_VERSION 0xa03 -/* 0 - UP, !0 - DOWN */ -static int32_t pm_down = !0; +/* !0 - UP, 0 - DOWN */ +static int32_t pm_up = 0; + +#if ZYNQMP_WDT_RESTART +static spinlock_t inc_lock; +static int active_cores = 0; +#endif + /** * pm_context - Structure which contains data for power management @@ -35,6 +46,142 @@ uint32_t payload[PAYLOAD_ARG_CNT]; } pm_ctx; +#if ZYNQMP_WDT_RESTART +/** + * trigger_wdt_restart() - Trigger warm restart event to APU cores + * + * This function triggers SGI for all active APU CPUs. SGI handler then + * power down CPU and call system reset. + */ +static void trigger_wdt_restart(void) +{ + uint32_t core_count = 0; + uint32_t core_status[3]; + uint32_t target_cpu_list = 0; + int i; + + for (i = 0; i < 4; i++) { + pm_get_node_status(NODE_APU_0 + i, core_status); + if (core_status[0] == 1) { + core_count++; + target_cpu_list |= (1 << i); + } + } + + spin_lock(&inc_lock); + active_cores = core_count; + spin_unlock(&inc_lock); + + INFO("Active Cores: %d\n", active_cores); + + /* trigger SGI to active cores */ + gicv2_raise_sgi(ARM_IRQ_SEC_SGI_7, target_cpu_list); +} + +/** + * ttc_fiq_handler() - TTC Handler for timer event + * @id number of the highest priority pending interrupt of the type + * that this handler was registered for + * @flags security state, bit[0] + * @handler pointer to 'cpu_context' structure of the current CPU for the + * security state specified in the 'flags' parameter + * @cookie unused + * + * Function registered as INTR_TYPE_EL3 interrupt handler + * + * When WDT event is received in PMU, PMU needs to notify master to do cleanup + * if required. PMU sets up timer and starts timer to overflow in zero time upon + * WDT event. ATF handles this timer event and takes necessary action required + * for warm restart. + * + * In presence of non-secure software layers (EL1/2) sets the interrupt + * at registered entrance in GIC and informs that PMU responsed or demands + * action. + */ +static uint64_t ttc_fiq_handler(uint32_t id, uint32_t flags, void *handle, + void *cookie) +{ + INFO("BL31: Got TTC FIQ\n"); + + /* Clear TTC interrupt by reading interrupt register */ + mmio_read_32(TTC3_INTR_REGISTER_1); + + /* Disable the timer interrupts */ + mmio_write_32(TTC3_INTR_ENABLE_1, 0); + + trigger_wdt_restart(); + + return 0; +} + +/** + * zynqmp_sgi7_irq() - Handler for SGI7 IRQ + * @id number of the highest priority pending interrupt of the type + * that this handler was registered for + * @flags security state, bit[0] + * @handler pointer to 'cpu_context' structure of the current CPU for the + * security state specified in the 'flags' parameter + * @cookie unused + * + * Function registered as INTR_TYPE_EL3 interrupt handler + * + * On receiving WDT event from PMU, ATF generates SGI7 to all running CPUs. + * In response to SGI7 interrupt, each CPUs do clean up if required and last + * running CPU calls system restart. + */ +static uint64_t __unused __dead2 zynqmp_sgi7_irq(uint32_t id, uint32_t flags, + void *handle, void *cookie) +{ + int i; + /* enter wfi and stay there */ + INFO("Entering wfi\n"); + + spin_lock(&inc_lock); + active_cores--; + + for (i = 0; i < 4; i++) { + mmio_write_32(BASE_GICD_BASE + GICD_CPENDSGIR + 4 * i, + 0xffffffff); + } + + spin_unlock(&inc_lock); + + if (active_cores == 0) { + pm_system_shutdown(PMF_SHUTDOWN_TYPE_RESET, + PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM); + } + + /* enter wfi and stay there */ + while (1) + wfi(); +} + +/** + * pm_wdt_restart_setup() - Setup warm restart interrupts + * + * This function sets up handler for SGI7 and TTC interrupts + * used for warm restart. + */ +static int pm_wdt_restart_setup(void) +{ + int ret; + + /* register IRQ handler for SGI7 */ + ret = request_intr_type_el3(ARM_IRQ_SEC_SGI_7, zynqmp_sgi7_irq); + if (ret) { + WARN("BL31: registering SGI7 interrupt failed\n"); + goto err; + } + + ret = request_intr_type_el3(IRQ_TTC3_1, ttc_fiq_handler); + if (ret) + WARN("BL31: registering TTC3 interrupt failed\n"); + +err: + return ret; +} +#endif + /** * pm_setup() - PM service setup * @@ -52,11 +199,14 @@ { int status, ret; - if (!zynqmp_is_pmu_up()) - return -ENODEV; - status = pm_ipi_init(primary_proc); +#if ZYNQMP_WDT_RESTART + status = pm_wdt_restart_setup(); + if (status) + WARN("BL31: warm-restart setup failed\n"); +#endif + if (status >= 0) { INFO("BL31: PM Service Init Complete: API v%d.%d\n", PM_VERSION_MAJOR, PM_VERSION_MINOR); @@ -66,7 +216,7 @@ ret = status; } - pm_down = status; + pm_up = !status; return ret; } @@ -95,7 +245,7 @@ uint32_t pm_arg[4]; /* Handle case where PM wasn't initialized properly */ - if (pm_down) + if (!pm_up) SMC_RET1(handle, SMC_UNK); pm_arg[0] = (uint32_t)x1; @@ -116,9 +266,16 @@ SMC_RET1(handle, (uint64_t)ret); case PM_REQ_WAKEUP: - ret = pm_req_wakeup(pm_arg[0], pm_arg[1], pm_arg[2], + { + /* Use address flag is encoded in the 1st bit of the low-word */ + unsigned int set_addr = pm_arg[1] & 0x1; + uint64_t address = (uint64_t)pm_arg[2] << 32; + + address |= pm_arg[1] & (~0x1); + ret = pm_req_wakeup(pm_arg[0], set_addr, address, pm_arg[3]); SMC_RET1(handle, (uint64_t)ret); + } case PM_FORCE_POWERDOWN: ret = pm_force_powerdown(pm_arg[0], pm_arg[1]); @@ -175,10 +332,19 @@ ret = pm_set_configuration(pm_arg[0]); SMC_RET1(handle, (uint64_t)ret); - case PM_GET_NODE_STATUS: - ret = pm_get_node_status(pm_arg[0]); + case PM_INIT_FINALIZE: + ret = pm_init_finalize(); SMC_RET1(handle, (uint64_t)ret); + case PM_GET_NODE_STATUS: + { + uint32_t buff[3]; + + ret = pm_get_node_status(pm_arg[0], buff); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)buff[0] << 32), + (uint64_t)buff[1] | ((uint64_t)buff[2] << 32)); + } + case PM_GET_OP_CHARACTERISTIC: { uint32_t result; @@ -239,15 +405,10 @@ result[1]); } - case PM_GET_CALLBACK_DATA: - { - uint32_t result[4]; - - pm_get_callbackdata(result, sizeof(result)); - SMC_RET2(handle, - (uint64_t)result[0] | ((uint64_t)result[1] << 32), - (uint64_t)result[2] | ((uint64_t)result[3] << 32)); - } + case PM_SECURE_RSA_AES: + ret = pm_secure_rsaaes(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); case PM_PINCTRL_REQUEST: ret = pm_pinctrl_request(pm_arg[0]); @@ -361,6 +522,30 @@ SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | ((uint64_t)ZYNQMP_TZ_VERSION << 32)); + case PM_SET_SUSPEND_MODE: + ret = pm_set_suspend_mode(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_SHA: + ret = pm_sha_hash(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_RSA: + ret = pm_rsa_core(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SECURE_IMAGE: + { + uint32_t result[2]; + + ret = pm_secure_image(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], &result[0]); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32), + result[1]); + } + default: WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); SMC_RET1(handle, SMC_UNK); diff --git a/plat/xilinx/zynqmp/zynqmp_def.h b/plat/xilinx/zynqmp/zynqmp_def.h index 60df187..22256eb 100644 --- a/plat/xilinx/zynqmp/zynqmp_def.h +++ b/plat/xilinx/zynqmp/zynqmp_def.h @@ -101,6 +101,14 @@ #define BASE_GICH_BASE 0xF9040000 #define BASE_GICV_BASE 0xF9060000 +#if ZYNQMP_WDT_RESTART +#define IRQ_SEC_IPI_APU 67 +#define IRQ_TTC3_1 77 +#define TTC3_BASE_ADDR 0xFF140000 +#define TTC3_INTR_REGISTER_1 (TTC3_BASE_ADDR + 0x54) +#define TTC3_INTR_ENABLE_1 (TTC3_BASE_ADDR + 0x60) +#endif + #define ARM_IRQ_SEC_PHY_TIMER 29 #define ARM_IRQ_SEC_SGI_0 8 @@ -158,7 +166,8 @@ #define ZYNQMP_CSU_IDCODE_XILINX_ID 0x093 #define ZYNQMP_CSU_IDCODE_SVD_SHIFT 12 -#define ZYNQMP_CSU_IDCODE_SVD_MASK (0xE << ZYNQMP_CSU_IDCODE_SVD_SHIFT) +#define ZYNQMP_CSU_IDCODE_SVD_MASK (0x7 << \ + ZYNQMP_CSU_IDCODE_SVD_SHIFT) #define ZYNQMP_CSU_IDCODE_DEVICE_CODE_SHIFT 15 #define ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK (0xF << ZYNQMP_CSU_IDCODE_DEVICE_CODE_SHIFT) #define ZYNQMP_CSU_IDCODE_SUB_FAMILY_SHIFT 19 @@ -173,6 +182,12 @@ #define ZYNQMP_CSU_VERSION_OFFSET 0x44 +/* Efuse */ +#define EFUSE_BASEADDR 0xFFCC0000 +#define EFUSE_IPDISABLE_OFFSET 0x1018 +#define EFUSE_IPDISABLE_VERSION 0x1FFU +#define ZYNQMP_EFUSE_IPDISABLE_SHIFT 20 + /* Access control register defines */ #define ACTLR_EL3_L2ACTLR_BIT (1 << 6) #define ACTLR_EL3_CPUACTLR_BIT (1 << 0) diff --git a/plat/xilinx/zynqmp/zynqmp_ipi.c b/plat/xilinx/zynqmp/zynqmp_ipi.c index 755a3b7..5038f84 100644 --- a/plat/xilinx/zynqmp/zynqmp_ipi.c +++ b/plat/xilinx/zynqmp/zynqmp_ipi.c @@ -84,7 +84,7 @@ { .ipi_bit_mask = 0x20000, .ipi_reg_base = 0xFF331000, - .secure_only = IPI_SECURE_MASK, + .secure_only = 0, }, /* PMU2 IPI */ { diff --git a/plat/xilinx/zynqmp/zynqmp_private.h b/plat/xilinx/zynqmp/zynqmp_private.h index 94a99f4..08a5410 100644 --- a/plat/xilinx/zynqmp/zynqmp_private.h +++ b/plat/xilinx/zynqmp/zynqmp_private.h @@ -7,17 +7,32 @@ #ifndef __ZYNQMP_PRIVATE_H__ #define __ZYNQMP_PRIVATE_H__ +#include #include void zynqmp_config_setup(void); /* ZynqMP specific functions */ unsigned int zynqmp_get_uart_clk(void); -int zynqmp_is_pmu_up(void); unsigned int zynqmp_get_bootmode(void); /* For FSBL handover */ -void fsbl_atf_handover(entry_point_info_t *bl32_image_ep_info, +enum fsbl_handoff { + FSBL_HANDOFF_SUCCESS = 0, + FSBL_HANDOFF_NO_STRUCT, + FSBL_HANDOFF_INVAL_STRUCT, + FSBL_HANDOFF_TOO_MANY_PARTS, +}; + +#if ZYNQMP_WDT_RESTART +/* + * Register handler to specific GIC entrance + * for INTR_TYPE_EL3 type of interrupt + */ +int request_intr_type_el3(uint32_t, interrupt_type_handler_t); +#endif + +enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32_image_ep_info, entry_point_info_t *bl33_image_ep_info); #endif /* __ZYNQMP_PRIVATE_H__ */