1 /* 2 * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * APU specific definition of processors in the subsystem as well as functions 33 * for getting information about and changing state of the APU. 34 */ 35 36 #include <bakery_lock.h> 37 #include <gicv2.h> 38 #include <bl_common.h> 39 #include <mmio.h> 40 #include "pm_api_sys.h" 41 #include "pm_client.h" 42 #include "pm_ipi.h" 43 #include "../zynqmp_def.h" 44 45 #define OCM_BANK_0 0xFFFC0000 46 #define OCM_BANK_1 (OCM_BANK_0 + 0x10000) 47 #define OCM_BANK_2 (OCM_BANK_1 + 0x10000) 48 #define OCM_BANK_3 (OCM_BANK_2 + 0x10000) 49 50 #define UNDEFINED_CPUID (~0) 51 DEFINE_BAKERY_LOCK(pm_client_secure_lock); 52 53 /* Declaration of linker defined symbol */ 54 extern unsigned long __BL31_END__; 55 extern const struct pm_ipi apu_ipi; 56 57 /* Order in pm_procs_all array must match cpu ids */ 58 static const struct pm_proc const pm_procs_all[] = { 59 { 60 .node_id = NODE_APU_0, 61 .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK, 62 .ipi = &apu_ipi, 63 }, 64 { 65 .node_id = NODE_APU_1, 66 .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK, 67 .ipi = &apu_ipi, 68 }, 69 { 70 .node_id = NODE_APU_2, 71 .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK, 72 .ipi = &apu_ipi, 73 }, 74 { 75 .node_id = NODE_APU_3, 76 .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK, 77 .ipi = &apu_ipi, 78 }, 79 }; 80 81 /** 82 * set_ocm_retention() - Configure OCM memory banks for retention 83 * 84 * APU specific requirements for suspend action: 85 * OCM has to enter retention state in order to preserve saved 86 * context after suspend request. OCM banks are determined by 87 * __BL31_END__ linker symbol. 88 * 89 * Return: Returns status, either success or error+reason 90 */ 91 enum pm_ret_status set_ocm_retention(void) 92 { 93 enum pm_ret_status ret; 94 95 /* OCM_BANK_0 will always be occupied */ 96 ret = pm_set_requirement(NODE_OCM_BANK_0, PM_CAP_CONTEXT, 0, 97 REQ_ACK_NO); 98 99 /* Check for other OCM banks */ 100 if ((unsigned long)&__BL31_END__ >= OCM_BANK_1) 101 ret = pm_set_requirement(NODE_OCM_BANK_1, PM_CAP_CONTEXT, 0, 102 REQ_ACK_NO); 103 if ((unsigned long)&__BL31_END__ >= OCM_BANK_2) 104 ret = pm_set_requirement(NODE_OCM_BANK_2, PM_CAP_CONTEXT, 0, 105 REQ_ACK_NO); 106 if ((unsigned long)&__BL31_END__ >= OCM_BANK_3) 107 ret = pm_set_requirement(NODE_OCM_BANK_3, PM_CAP_CONTEXT, 0, 108 REQ_ACK_NO); 109 110 return ret; 111 } 112 113 /** 114 * pm_get_proc() - returns pointer to the proc structure 115 * @cpuid: id of the cpu whose proc struct pointer should be returned 116 * 117 * Return: pointer to a proc structure if proc is found, otherwise NULL 118 */ 119 const struct pm_proc *pm_get_proc(unsigned int cpuid) 120 { 121 if (cpuid < ARRAY_SIZE(pm_procs_all)) 122 return &pm_procs_all[cpuid]; 123 124 return NULL; 125 } 126 127 /** 128 * pm_get_proc_by_node() - returns pointer to the proc structure 129 * @nid: node id of the processor 130 * 131 * Return: pointer to a proc structure if proc is found, otherwise NULL 132 */ 133 const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid) 134 { 135 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { 136 if (nid == pm_procs_all[i].node_id) 137 return &pm_procs_all[i]; 138 } 139 return NULL; 140 } 141 142 /** 143 * pm_get_cpuid() - get the local cpu ID for a global node ID 144 * @nid: node id of the processor 145 * 146 * Return: the cpu ID (starting from 0) for the subsystem 147 */ 148 static unsigned int pm_get_cpuid(enum pm_node_id nid) 149 { 150 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { 151 if (pm_procs_all[i].node_id == nid) 152 return i; 153 } 154 return UNDEFINED_CPUID; 155 } 156 157 const struct pm_proc *primary_proc = &pm_procs_all[0]; 158 159 /** 160 * pm_client_suspend() - Client-specific suspend actions 161 * 162 * This function should contain any PU-specific actions 163 * required prior to sending suspend request to PMU 164 */ 165 void pm_client_suspend(const struct pm_proc *proc) 166 { 167 bakery_lock_get(&pm_client_secure_lock); 168 169 /* Set powerdown request */ 170 mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask); 171 172 bakery_lock_release(&pm_client_secure_lock); 173 } 174 175 176 /** 177 * pm_client_abort_suspend() - Client-specific abort-suspend actions 178 * 179 * This function should contain any PU-specific actions 180 * required for aborting a prior suspend request 181 */ 182 void pm_client_abort_suspend(void) 183 { 184 /* Enable interrupts at processor level (for current cpu) */ 185 gicv2_cpuif_enable(); 186 187 bakery_lock_get(&pm_client_secure_lock); 188 189 /* Clear powerdown request */ 190 mmio_write_32(APU_PWRCTL, 191 mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask); 192 193 bakery_lock_release(&pm_client_secure_lock); 194 } 195 196 /** 197 * pm_client_wakeup() - Client-specific wakeup actions 198 * 199 * This function should contain any PU-specific actions 200 * required for waking up another APU core 201 */ 202 void pm_client_wakeup(const struct pm_proc *proc) 203 { 204 unsigned int cpuid = pm_get_cpuid(proc->node_id); 205 206 if (cpuid == UNDEFINED_CPUID) 207 return; 208 209 bakery_lock_get(&pm_client_secure_lock); 210 211 /* clear powerdown bit for affected cpu */ 212 uint32_t val = mmio_read_32(APU_PWRCTL); 213 val &= ~(proc->pwrdn_mask); 214 mmio_write_32(APU_PWRCTL, val); 215 216 bakery_lock_release(&pm_client_secure_lock); 217 } 218