Merge pull request #1369 from sivadur/xilinxdiff
Xilinx platform mangement related changes
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 <generic_delay_timer.h>
#include <mmio.h>
#include <platform.h>
+#include <stdbool.h>
+#include <string.h>
#include <xlat_tables.h>
#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",
+ },
};
+#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 unsigned int zynqmp_get_silicon_id(void)
+static char *zynqmp_get_silicon_idcode_name(void)
{
- uint32_t id;
+ uint32_t id, ver, chipid[2];
+ size_t i, j, len;
+ enum pm_ret_status ret;
+ const char *name = "EG/EV";
- id = mmio_read_32(ZYNQMP_CSU_BASEADDR + ZYNQMP_CSU_IDCODE_OFFSET);
+ ret = pm_get_chipid(chipid);
+ if (ret)
+ return "UNKN";
- id &= ZYNQMP_CSU_IDCODE_DEVICE_CODE_MASK | ZYNQMP_CSU_IDCODE_SVD_MASK;
+ 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;
- return id;
-}
+ 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;
+ }
-static char *zynqmp_get_silicon_idcode_name(void)
-{
- unsigned int id;
+ if (i >= ARRAY_SIZE(zynqmp_devices))
+ return "UNKN";
- 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;
+ 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++;
}
- return "UNKN";
+ 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)
+unsigned int zynqmp_get_bootmode(void)
{
- zynqmp_pmufw_present = mmio_read_32(PMU_GLOBAL_CNTRL);
- zynqmp_pmufw_present &= PMU_GLOBAL_CNTRL_FW_IS_PRESENT;
+ uint32_t r;
+ unsigned int ret;
- 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;
-}
+ ret = pm_mmio_read(CRL_APB_BOOT_MODE_USER, &r);
-unsigned int zynqmp_get_bootmode(void)
-{
- uint32_t r = mmio_read_32(CRL_APB_BOOT_MODE_USER);
+ 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,34 +137,12 @@
/* Send the power down request to the PMU */
pm_system_shutdown(PMF_SHUTDOWN_TYPE_SHUTDOWN,
- PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM);
+ pm_get_shutdown_scope());
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);
-
- while (1)
- wfi();
-}
-
static void __dead2 zynqmp_system_reset(void)
{
/* disable coherency */
@@ -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 <debug.h>
#include <mmio.h>
#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.
+ *
+ * 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
*
- * Read value from ipi buffer response buffer.
+ * @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 <errno.h>
-#include <gic_common.h>
#include <runtime_svc.h>
-#include <string.h>
#include "../zynqmp_private.h"
#include "pm_api_sys.h"
#include "pm_client.h"
#include "pm_ipi.h"
+#if ZYNQMP_WDT_RESTART
+#include <arch_helpers.h>
+#include <gicv2.h>
+#include <mmio.h>
+#include <platform.h>
+#include <spinlock.h>
+#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,7 +46,143 @@
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
*
* @return On success, the initialization function must return 0.
@@ -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 <bl_common.h>
#include <interrupt_mgmt.h>
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__ */