12e9c9e82SBenjamin Fair /* 22e9c9e82SBenjamin Fair * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. 32e9c9e82SBenjamin Fair * 42e9c9e82SBenjamin Fair * SPDX-License-Identifier: BSD-3-Clause 52e9c9e82SBenjamin Fair */ 62e9c9e82SBenjamin Fair 72e9c9e82SBenjamin Fair #include <assert.h> 82e9c9e82SBenjamin Fair #include <stdbool.h> 92e9c9e82SBenjamin Fair 1009d40e0eSAntonio Nino Diaz #include <arch_helpers.h> 1109d40e0eSAntonio Nino Diaz #include <common/debug.h> 1209d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/cpu_data.h> 1309d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h> 1409d40e0eSAntonio Nino Diaz #include <plat/common/platform.h> 1509d40e0eSAntonio Nino Diaz 16a9ae424eSAndrew F. Davis #include <ti_sci_protocol.h> 1709d40e0eSAntonio Nino Diaz #include <k3_gicv3.h> 18df83b034SAndrew F. Davis #include <ti_sci.h> 19df83b034SAndrew F. Davis 20*586621f1SAndrew F. Davis #define CORE_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL0]) 21*586621f1SAndrew F. Davis #define CLUSTER_PWR_STATE(state) ((state)->pwr_domain_state[MPIDR_AFFLVL1]) 22*586621f1SAndrew F. Davis #define SYSTEM_PWR_STATE(state) ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL]) 23*586621f1SAndrew F. Davis 242e9c9e82SBenjamin Fair uintptr_t k3_sec_entrypoint; 252e9c9e82SBenjamin Fair 262e9c9e82SBenjamin Fair static void k3_cpu_standby(plat_local_state_t cpu_state) 272e9c9e82SBenjamin Fair { 28deed2b83SAndrew F. Davis unsigned int scr; 29deed2b83SAndrew F. Davis 30deed2b83SAndrew F. Davis scr = read_scr_el3(); 31deed2b83SAndrew F. Davis /* Enable the Non secure interrupt to wake the CPU */ 32deed2b83SAndrew F. Davis write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); 33deed2b83SAndrew F. Davis isb(); 34deed2b83SAndrew F. Davis /* dsb is good practice before using wfi to enter low power states */ 352e9c9e82SBenjamin Fair dsb(); 36deed2b83SAndrew F. Davis /* Enter standby state */ 372e9c9e82SBenjamin Fair wfi(); 38deed2b83SAndrew F. Davis /* Restore SCR */ 39deed2b83SAndrew F. Davis write_scr_el3(scr); 402e9c9e82SBenjamin Fair } 412e9c9e82SBenjamin Fair 422e9c9e82SBenjamin Fair static int k3_pwr_domain_on(u_register_t mpidr) 432e9c9e82SBenjamin Fair { 449f49a177SAndrew F. Davis int core, proc_id, device_id, ret; 452e9c9e82SBenjamin Fair 469f49a177SAndrew F. Davis core = plat_core_pos_by_mpidr(mpidr); 479f49a177SAndrew F. Davis if (core < 0) { 489f49a177SAndrew F. Davis ERROR("Could not get target core id: %d\n", core); 49df83b034SAndrew F. Davis return PSCI_E_INTERN_FAIL; 50df83b034SAndrew F. Davis } 51df83b034SAndrew F. Davis 529f49a177SAndrew F. Davis proc_id = PLAT_PROC_START_ID + core; 539f49a177SAndrew F. Davis device_id = PLAT_PROC_DEVICE_START_ID + core; 54df83b034SAndrew F. Davis 559f49a177SAndrew F. Davis ret = ti_sci_proc_request(proc_id); 56df83b034SAndrew F. Davis if (ret) { 57df83b034SAndrew F. Davis ERROR("Request for processor failed: %d\n", ret); 58df83b034SAndrew F. Davis return PSCI_E_INTERN_FAIL; 59df83b034SAndrew F. Davis } 60df83b034SAndrew F. Davis 619f49a177SAndrew F. Davis ret = ti_sci_proc_set_boot_cfg(proc_id, k3_sec_entrypoint, 0, 0); 62df83b034SAndrew F. Davis if (ret) { 63df83b034SAndrew F. Davis ERROR("Request to set core boot address failed: %d\n", ret); 64df83b034SAndrew F. Davis return PSCI_E_INTERN_FAIL; 65df83b034SAndrew F. Davis } 66df83b034SAndrew F. Davis 67*586621f1SAndrew F. Davis /* sanity check these are off before starting a core */ 68*586621f1SAndrew F. Davis ret = ti_sci_proc_set_boot_ctrl(proc_id, 69*586621f1SAndrew F. Davis 0, PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ | 70*586621f1SAndrew F. Davis PROC_BOOT_CTRL_FLAG_ARMV8_AINACTS | 71*586621f1SAndrew F. Davis PROC_BOOT_CTRL_FLAG_ARMV8_ACINACTM); 72*586621f1SAndrew F. Davis if (ret) { 73*586621f1SAndrew F. Davis ERROR("Request to clear boot configuration failed: %d\n", ret); 74*586621f1SAndrew F. Davis return PSCI_E_INTERN_FAIL; 75*586621f1SAndrew F. Davis } 76*586621f1SAndrew F. Davis 779f49a177SAndrew F. Davis ret = ti_sci_device_get(device_id); 78df83b034SAndrew F. Davis if (ret) { 79df83b034SAndrew F. Davis ERROR("Request to start core failed: %d\n", ret); 80df83b034SAndrew F. Davis return PSCI_E_INTERN_FAIL; 81df83b034SAndrew F. Davis } 82df83b034SAndrew F. Davis 832e9c9e82SBenjamin Fair return PSCI_E_SUCCESS; 842e9c9e82SBenjamin Fair } 852e9c9e82SBenjamin Fair 862e9c9e82SBenjamin Fair void k3_pwr_domain_off(const psci_power_state_t *target_state) 872e9c9e82SBenjamin Fair { 88*586621f1SAndrew F. Davis int core, cluster, proc_id, device_id, cluster_id, ret; 89*586621f1SAndrew F. Davis 90*586621f1SAndrew F. Davis /* At very least the local core should be powering down */ 91*586621f1SAndrew F. Davis assert(CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE); 9234cae37fSAndrew F. Davis 932e9c9e82SBenjamin Fair /* Prevent interrupts from spuriously waking up this cpu */ 942e9c9e82SBenjamin Fair k3_gic_cpuif_disable(); 952e9c9e82SBenjamin Fair 969f49a177SAndrew F. Davis core = plat_my_core_pos(); 97*586621f1SAndrew F. Davis cluster = MPIDR_AFFLVL1_VAL(read_mpidr_el1()); 989f49a177SAndrew F. Davis proc_id = PLAT_PROC_START_ID + core; 999f49a177SAndrew F. Davis device_id = PLAT_PROC_DEVICE_START_ID + core; 100*586621f1SAndrew F. Davis cluster_id = PLAT_CLUSTER_DEVICE_START_ID + (cluster * 2); 101*586621f1SAndrew F. Davis 102*586621f1SAndrew F. Davis /* 103*586621f1SAndrew F. Davis * If we are the last core in the cluster then we take a reference to 104*586621f1SAndrew F. Davis * the cluster device so that it does not get shutdown before we 105*586621f1SAndrew F. Davis * execute the entire cluster L2 cleaning sequence below. 106*586621f1SAndrew F. Davis */ 107*586621f1SAndrew F. Davis if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) { 108*586621f1SAndrew F. Davis ret = ti_sci_device_get(cluster_id); 109*586621f1SAndrew F. Davis if (ret) { 110*586621f1SAndrew F. Davis ERROR("Request to get cluster failed: %d\n", ret); 111*586621f1SAndrew F. Davis return; 112*586621f1SAndrew F. Davis } 113*586621f1SAndrew F. Davis } 11434cae37fSAndrew F. Davis 115a9ae424eSAndrew F. Davis /* Start by sending wait for WFI command */ 1169f49a177SAndrew F. Davis ret = ti_sci_proc_wait_boot_status_no_wait(proc_id, 117a9ae424eSAndrew F. Davis /* 118a9ae424eSAndrew F. Davis * Wait maximum time to give us the best chance to get 119a9ae424eSAndrew F. Davis * to WFI before this command timeouts 120a9ae424eSAndrew F. Davis */ 121a9ae424eSAndrew F. Davis UINT8_MAX, 100, UINT8_MAX, UINT8_MAX, 122a9ae424eSAndrew F. Davis /* Wait for WFI */ 123a9ae424eSAndrew F. Davis PROC_BOOT_STATUS_FLAG_ARMV8_WFI, 0, 0, 0); 12434cae37fSAndrew F. Davis if (ret) { 125a9ae424eSAndrew F. Davis ERROR("Sending wait for WFI failed (%d)\n", ret); 126a9ae424eSAndrew F. Davis return; 127a9ae424eSAndrew F. Davis } 128a9ae424eSAndrew F. Davis 129a9ae424eSAndrew F. Davis /* Now queue up the core shutdown request */ 1309f49a177SAndrew F. Davis ret = ti_sci_device_put_no_wait(device_id); 131a9ae424eSAndrew F. Davis if (ret) { 132a9ae424eSAndrew F. Davis ERROR("Sending core shutdown message failed (%d)\n", ret); 13334cae37fSAndrew F. Davis return; 13434cae37fSAndrew F. Davis } 135*586621f1SAndrew F. Davis 136*586621f1SAndrew F. Davis /* If our cluster is not going down we stop here */ 137*586621f1SAndrew F. Davis if (CLUSTER_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE) 138*586621f1SAndrew F. Davis return; 139*586621f1SAndrew F. Davis 140*586621f1SAndrew F. Davis /* set AINACTS */ 141*586621f1SAndrew F. Davis ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id, 142*586621f1SAndrew F. Davis PROC_BOOT_CTRL_FLAG_ARMV8_AINACTS, 0); 143*586621f1SAndrew F. Davis if (ret) { 144*586621f1SAndrew F. Davis ERROR("Sending set control message failed (%d)\n", ret); 145*586621f1SAndrew F. Davis return; 146*586621f1SAndrew F. Davis } 147*586621f1SAndrew F. Davis 148*586621f1SAndrew F. Davis /* set L2FLUSHREQ */ 149*586621f1SAndrew F. Davis ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id, 150*586621f1SAndrew F. Davis PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ, 0); 151*586621f1SAndrew F. Davis if (ret) { 152*586621f1SAndrew F. Davis ERROR("Sending set control message failed (%d)\n", ret); 153*586621f1SAndrew F. Davis return; 154*586621f1SAndrew F. Davis } 155*586621f1SAndrew F. Davis 156*586621f1SAndrew F. Davis /* wait for L2FLUSHDONE*/ 157*586621f1SAndrew F. Davis ret = ti_sci_proc_wait_boot_status_no_wait(proc_id, 158*586621f1SAndrew F. Davis UINT8_MAX, 2, UINT8_MAX, UINT8_MAX, 159*586621f1SAndrew F. Davis PROC_BOOT_STATUS_FLAG_ARMV8_L2F_DONE, 0, 0, 0); 160*586621f1SAndrew F. Davis if (ret) { 161*586621f1SAndrew F. Davis ERROR("Sending wait message failed (%d)\n", ret); 162*586621f1SAndrew F. Davis return; 163*586621f1SAndrew F. Davis } 164*586621f1SAndrew F. Davis 165*586621f1SAndrew F. Davis /* clear L2FLUSHREQ */ 166*586621f1SAndrew F. Davis ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id, 167*586621f1SAndrew F. Davis 0, PROC_BOOT_CTRL_FLAG_ARMV8_L2FLUSHREQ); 168*586621f1SAndrew F. Davis if (ret) { 169*586621f1SAndrew F. Davis ERROR("Sending set control message failed (%d)\n", ret); 170*586621f1SAndrew F. Davis return; 171*586621f1SAndrew F. Davis } 172*586621f1SAndrew F. Davis 173*586621f1SAndrew F. Davis /* set ACINACTM */ 174*586621f1SAndrew F. Davis ret = ti_sci_proc_set_boot_ctrl_no_wait(proc_id, 175*586621f1SAndrew F. Davis PROC_BOOT_CTRL_FLAG_ARMV8_ACINACTM, 0); 176*586621f1SAndrew F. Davis if (ret) { 177*586621f1SAndrew F. Davis ERROR("Sending set control message failed (%d)\n", ret); 178*586621f1SAndrew F. Davis return; 179*586621f1SAndrew F. Davis } 180*586621f1SAndrew F. Davis 181*586621f1SAndrew F. Davis /* wait for STANDBYWFIL2 */ 182*586621f1SAndrew F. Davis ret = ti_sci_proc_wait_boot_status_no_wait(proc_id, 183*586621f1SAndrew F. Davis UINT8_MAX, 2, UINT8_MAX, UINT8_MAX, 184*586621f1SAndrew F. Davis PROC_BOOT_STATUS_FLAG_ARMV8_STANDBYWFIL2, 0, 0, 0); 185*586621f1SAndrew F. Davis if (ret) { 186*586621f1SAndrew F. Davis ERROR("Sending wait message failed (%d)\n", ret); 187*586621f1SAndrew F. Davis return; 188*586621f1SAndrew F. Davis } 189*586621f1SAndrew F. Davis 190*586621f1SAndrew F. Davis /* Now queue up the cluster shutdown request */ 191*586621f1SAndrew F. Davis ret = ti_sci_device_put_no_wait(cluster_id); 192*586621f1SAndrew F. Davis if (ret) { 193*586621f1SAndrew F. Davis ERROR("Sending cluster shutdown message failed (%d)\n", ret); 194*586621f1SAndrew F. Davis return; 195*586621f1SAndrew F. Davis } 1962e9c9e82SBenjamin Fair } 1972e9c9e82SBenjamin Fair 1982e9c9e82SBenjamin Fair void k3_pwr_domain_on_finish(const psci_power_state_t *target_state) 1992e9c9e82SBenjamin Fair { 2002e9c9e82SBenjamin Fair /* TODO: Indicate to System firmware about completion */ 2012e9c9e82SBenjamin Fair 2022e9c9e82SBenjamin Fair k3_gic_pcpu_init(); 2032e9c9e82SBenjamin Fair k3_gic_cpuif_enable(); 2042e9c9e82SBenjamin Fair } 2052e9c9e82SBenjamin Fair 2062e9c9e82SBenjamin Fair static void __dead2 k3_system_reset(void) 2072e9c9e82SBenjamin Fair { 208c8761b4dSAndrew F. Davis /* Send the system reset request to system firmware */ 209c8761b4dSAndrew F. Davis ti_sci_core_reboot(); 2102e9c9e82SBenjamin Fair 2112e9c9e82SBenjamin Fair while (true) 2122e9c9e82SBenjamin Fair wfi(); 2132e9c9e82SBenjamin Fair } 2142e9c9e82SBenjamin Fair 2152e9c9e82SBenjamin Fair static int k3_validate_power_state(unsigned int power_state, 2162e9c9e82SBenjamin Fair psci_power_state_t *req_state) 2172e9c9e82SBenjamin Fair { 2182e9c9e82SBenjamin Fair /* TODO: perform the proper validation */ 2192e9c9e82SBenjamin Fair 2202e9c9e82SBenjamin Fair return PSCI_E_SUCCESS; 2212e9c9e82SBenjamin Fair } 2222e9c9e82SBenjamin Fair 2232e9c9e82SBenjamin Fair static int k3_validate_ns_entrypoint(uintptr_t entrypoint) 2242e9c9e82SBenjamin Fair { 2252e9c9e82SBenjamin Fair /* TODO: perform the proper validation */ 2262e9c9e82SBenjamin Fair 2272e9c9e82SBenjamin Fair return PSCI_E_SUCCESS; 2282e9c9e82SBenjamin Fair } 2292e9c9e82SBenjamin Fair 2302e9c9e82SBenjamin Fair static const plat_psci_ops_t k3_plat_psci_ops = { 2312e9c9e82SBenjamin Fair .cpu_standby = k3_cpu_standby, 2322e9c9e82SBenjamin Fair .pwr_domain_on = k3_pwr_domain_on, 2332e9c9e82SBenjamin Fair .pwr_domain_off = k3_pwr_domain_off, 2342e9c9e82SBenjamin Fair .pwr_domain_on_finish = k3_pwr_domain_on_finish, 2352e9c9e82SBenjamin Fair .system_reset = k3_system_reset, 2362e9c9e82SBenjamin Fair .validate_power_state = k3_validate_power_state, 2372e9c9e82SBenjamin Fair .validate_ns_entrypoint = k3_validate_ns_entrypoint 2382e9c9e82SBenjamin Fair }; 2392e9c9e82SBenjamin Fair 2402e9c9e82SBenjamin Fair int plat_setup_psci_ops(uintptr_t sec_entrypoint, 2412e9c9e82SBenjamin Fair const plat_psci_ops_t **psci_ops) 2422e9c9e82SBenjamin Fair { 2432e9c9e82SBenjamin Fair k3_sec_entrypoint = sec_entrypoint; 2442e9c9e82SBenjamin Fair 2452e9c9e82SBenjamin Fair *psci_ops = &k3_plat_psci_ops; 2462e9c9e82SBenjamin Fair 2472e9c9e82SBenjamin Fair return 0; 2482e9c9e82SBenjamin Fair } 249