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 <gicv2.h> 37 #include <bl_common.h> 38 #include <mmio.h> 39 #include "pm_api_sys.h" 40 #include "pm_client.h" 41 #include "pm_ipi.h" 42 #include "../zynqmp_def.h" 43 44 #define OCM_BANK_0 0xFFFC0000 45 #define OCM_BANK_1 (OCM_BANK_0 + 0x10000) 46 #define OCM_BANK_2 (OCM_BANK_1 + 0x10000) 47 #define OCM_BANK_3 (OCM_BANK_2 + 0x10000) 48 49 #define UNDEFINED_CPUID (~0) 50 51 /* Declaration of linker defined symbol */ 52 extern unsigned long __BL31_END__; 53 extern const struct pm_ipi apu_ipi; 54 55 /* Order in pm_procs_all array must match cpu ids */ 56 static const struct pm_proc const pm_procs_all[] = { 57 { 58 .node_id = NODE_APU_0, 59 .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK, 60 .ipi = &apu_ipi, 61 }, 62 { 63 .node_id = NODE_APU_1, 64 .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK, 65 .ipi = &apu_ipi, 66 }, 67 { 68 .node_id = NODE_APU_2, 69 .pwrdn_mask = APU_2_PWRCTL_CPUPWRDWNREQ_MASK, 70 .ipi = &apu_ipi, 71 }, 72 { 73 .node_id = NODE_APU_3, 74 .pwrdn_mask = APU_3_PWRCTL_CPUPWRDWNREQ_MASK, 75 .ipi = &apu_ipi, 76 }, 77 }; 78 79 /** 80 * set_ocm_retention() - Configure OCM memory banks for retention 81 * 82 * APU specific requirements for suspend action: 83 * OCM has to enter retention state in order to preserve saved 84 * context after suspend request. OCM banks are determined by 85 * __BL31_END__ linker symbol. 86 * 87 * Return: Returns status, either success or error+reason 88 */ 89 enum pm_ret_status set_ocm_retention(void) 90 { 91 enum pm_ret_status ret; 92 93 /* OCM_BANK_0 will always be occupied */ 94 ret = pm_set_requirement(NODE_OCM_BANK_0, PM_CAP_CONTEXT, 0, 95 REQ_ACK_NO); 96 97 /* Check for other OCM banks */ 98 if ((unsigned long)&__BL31_END__ >= OCM_BANK_1) 99 ret = pm_set_requirement(NODE_OCM_BANK_1, PM_CAP_CONTEXT, 0, 100 REQ_ACK_NO); 101 if ((unsigned long)&__BL31_END__ >= OCM_BANK_2) 102 ret = pm_set_requirement(NODE_OCM_BANK_2, PM_CAP_CONTEXT, 0, 103 REQ_ACK_NO); 104 if ((unsigned long)&__BL31_END__ >= OCM_BANK_3) 105 ret = pm_set_requirement(NODE_OCM_BANK_3, PM_CAP_CONTEXT, 0, 106 REQ_ACK_NO); 107 108 return ret; 109 } 110 111 /** 112 * pm_get_proc() - returns pointer to the proc structure 113 * @cpuid: id of the cpu whose proc struct pointer should be returned 114 * 115 * Return: pointer to a proc structure if proc is found, otherwise NULL 116 */ 117 const struct pm_proc *pm_get_proc(unsigned int cpuid) 118 { 119 if (cpuid < ARRAY_SIZE(pm_procs_all)) 120 return &pm_procs_all[cpuid]; 121 122 return NULL; 123 } 124 125 /** 126 * pm_get_proc_by_node() - returns pointer to the proc structure 127 * @nid: node id of the processor 128 * 129 * Return: pointer to a proc structure if proc is found, otherwise NULL 130 */ 131 const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid) 132 { 133 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { 134 if (nid == pm_procs_all[i].node_id) 135 return &pm_procs_all[i]; 136 } 137 return NULL; 138 } 139 140 /** 141 * pm_get_cpuid() - get the local cpu ID for a global node ID 142 * @nid: node id of the processor 143 * 144 * Return: the cpu ID (starting from 0) for the subsystem 145 */ 146 static unsigned int pm_get_cpuid(enum pm_node_id nid) 147 { 148 for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { 149 if (pm_procs_all[i].node_id == nid) 150 return i; 151 } 152 return UNDEFINED_CPUID; 153 } 154 155 const struct pm_proc *primary_proc = &pm_procs_all[0]; 156 157 /** 158 * pm_client_suspend() - Client-specific suspend actions 159 * 160 * This function should contain any PU-specific actions 161 * required prior to sending suspend request to PMU 162 */ 163 void pm_client_suspend(const struct pm_proc *proc) 164 { 165 /* Set powerdown request */ 166 mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask); 167 } 168 169 170 /** 171 * pm_client_abort_suspend() - Client-specific abort-suspend actions 172 * 173 * This function should contain any PU-specific actions 174 * required for aborting a prior suspend request 175 */ 176 void pm_client_abort_suspend(void) 177 { 178 /* Enable interrupts at processor level (for current cpu) */ 179 gicv2_cpuif_enable(); 180 /* Clear powerdown request */ 181 mmio_write_32(APU_PWRCTL, 182 mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask); 183 } 184 185 /** 186 * pm_client_wakeup() - Client-specific wakeup actions 187 * 188 * This function should contain any PU-specific actions 189 * required for waking up another APU core 190 */ 191 void pm_client_wakeup(const struct pm_proc *proc) 192 { 193 unsigned int cpuid = pm_get_cpuid(proc->node_id); 194 195 if (cpuid == UNDEFINED_CPUID) 196 return; 197 198 /* clear powerdown bit for affected cpu */ 199 uint32_t val = mmio_read_32(APU_PWRCTL); 200 val &= ~(proc->pwrdn_mask); 201 mmio_write_32(APU_PWRCTL, val); 202 } 203