xref: /rk3399_ARM-atf/plat/xilinx/zynqmp/pm_service/pm_client.c (revision 7a1b2794307bf18cdea975b8897f8cd7e0579fc9)
1 /*
2  * Copyright (c) 2013-2016, 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 <assert.h>
37 #include <bakery_lock.h>
38 #include <gicv2.h>
39 #include <gic_common.h>
40 #include <bl_common.h>
41 #include <mmio.h>
42 #include <string.h>
43 #include <utils.h>
44 #include "pm_api_sys.h"
45 #include "pm_client.h"
46 #include "pm_ipi.h"
47 #include "../zynqmp_def.h"
48 
49 #define IRQ_MAX		84
50 #define NUM_GICD_ISENABLER	((IRQ_MAX >> 5) + 1)
51 #define UNDEFINED_CPUID		(~0)
52 
53 DEFINE_BAKERY_LOCK(pm_client_secure_lock);
54 
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 /* Interrupt to PM node ID map */
82 static enum pm_node_id irq_node_map[IRQ_MAX + 1] = {
83 	NODE_UNKNOWN,
84 	NODE_UNKNOWN,
85 	NODE_UNKNOWN,
86 	NODE_UNKNOWN,	/* 3 */
87 	NODE_UNKNOWN,
88 	NODE_UNKNOWN,
89 	NODE_UNKNOWN,
90 	NODE_UNKNOWN,	/* 7 */
91 	NODE_UNKNOWN,
92 	NODE_UNKNOWN,
93 	NODE_UNKNOWN,
94 	NODE_UNKNOWN,	/* 11 */
95 	NODE_UNKNOWN,
96 	NODE_UNKNOWN,
97 	NODE_NAND,
98 	NODE_QSPI,	/* 15 */
99 	NODE_GPIO,
100 	NODE_I2C_0,
101 	NODE_I2C_1,
102 	NODE_SPI_0,	/* 19 */
103 	NODE_SPI_1,
104 	NODE_UART_0,
105 	NODE_UART_1,
106 	NODE_CAN_0,	/* 23 */
107 	NODE_CAN_1,
108 	NODE_UNKNOWN,
109 	NODE_RTC,
110 	NODE_RTC,	/* 27 */
111 	NODE_UNKNOWN,
112 	NODE_UNKNOWN,
113 	NODE_UNKNOWN,
114 	NODE_UNKNOWN,	/* 31 */
115 	NODE_UNKNOWN,
116 	NODE_UNKNOWN,
117 	NODE_UNKNOWN,
118 	NODE_UNKNOWN,	/* 35, NODE_IPI_APU */
119 	NODE_TTC_0,
120 	NODE_TTC_0,
121 	NODE_TTC_0,
122 	NODE_TTC_1,	/* 39 */
123 	NODE_TTC_1,
124 	NODE_TTC_1,
125 	NODE_TTC_2,
126 	NODE_TTC_2,	/* 43 */
127 	NODE_TTC_2,
128 	NODE_TTC_3,
129 	NODE_TTC_3,
130 	NODE_TTC_3,	/* 47 */
131 	NODE_SD_0,
132 	NODE_SD_1,
133 	NODE_SD_0,
134 	NODE_SD_1,	/* 51 */
135 	NODE_UNKNOWN,
136 	NODE_UNKNOWN,
137 	NODE_UNKNOWN,
138 	NODE_UNKNOWN,	/* 55 */
139 	NODE_UNKNOWN,
140 	NODE_ETH_0,
141 	NODE_ETH_0,
142 	NODE_ETH_1,	/* 59 */
143 	NODE_ETH_1,
144 	NODE_ETH_2,
145 	NODE_ETH_2,
146 	NODE_ETH_3,	/* 63 */
147 	NODE_ETH_3,
148 	NODE_USB_0,
149 	NODE_USB_0,
150 	NODE_USB_0,	/* 67 */
151 	NODE_USB_0,
152 	NODE_USB_0,
153 	NODE_USB_1,
154 	NODE_USB_1,	/* 71 */
155 	NODE_USB_1,
156 	NODE_USB_1,
157 	NODE_USB_1,
158 	NODE_USB_0,	/* 75 */
159 	NODE_USB_0,
160 	NODE_ADMA,
161 	NODE_ADMA,
162 	NODE_ADMA,	/* 79 */
163 	NODE_ADMA,
164 	NODE_ADMA,
165 	NODE_ADMA,
166 	NODE_ADMA,	/* 83 */
167 	NODE_ADMA,
168 };
169 
170 /**
171  * irq_to_pm_node - Get PM node ID corresponding to the interrupt number
172  * @irq:	Interrupt number
173  *
174  * Return:	PM node ID corresponding to the specified interrupt
175  */
176 static enum pm_node_id irq_to_pm_node(unsigned int irq)
177 {
178 	assert(irq <= IRQ_MAX);
179 	return irq_node_map[irq];
180 }
181 
182 /**
183  * pm_client_set_wakeup_sources - Set all slaves with enabled interrupts as wake
184  *				sources in the PMU firmware
185  */
186 static void pm_client_set_wakeup_sources(void)
187 {
188 	uint32_t reg_num;
189 	uint8_t pm_wakeup_nodes_set[NODE_MAX];
190 	uintptr_t isenabler1 = BASE_GICD_BASE + GICD_ISENABLER + 4;
191 
192 	memset(&pm_wakeup_nodes_set, 0, sizeof(pm_wakeup_nodes_set));
193 
194 	for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
195 		uint32_t base_irq = reg_num << ISENABLER_SHIFT;
196 		uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
197 
198 		if (!reg)
199 			continue;
200 
201 		while (reg) {
202 			enum pm_node_id node;
203 			uint32_t idx, ret, irq, lowest_set = reg & (-reg);
204 
205 			idx = __builtin_ctz(lowest_set);
206 			irq = base_irq + idx;
207 
208 			if (irq > IRQ_MAX)
209 				break;
210 
211 			node = irq_to_pm_node(irq);
212 			reg &= ~lowest_set;
213 
214 			if ((node != NODE_UNKNOWN) &&
215 			    (!pm_wakeup_nodes_set[node])) {
216 				ret = pm_set_wakeup_source(NODE_APU, node, 1);
217 				pm_wakeup_nodes_set[node] = !ret;
218 			}
219 		}
220 	}
221 }
222 
223 /**
224  * pm_get_proc() - returns pointer to the proc structure
225  * @cpuid:	id of the cpu whose proc struct pointer should be returned
226  *
227  * Return: pointer to a proc structure if proc is found, otherwise NULL
228  */
229 const struct pm_proc *pm_get_proc(unsigned int cpuid)
230 {
231 	if (cpuid < ARRAY_SIZE(pm_procs_all))
232 		return &pm_procs_all[cpuid];
233 
234 	return NULL;
235 }
236 
237 /**
238  * pm_get_proc_by_node() - returns pointer to the proc structure
239  * @nid:	node id of the processor
240  *
241  * Return: pointer to a proc structure if proc is found, otherwise NULL
242  */
243 const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid)
244 {
245 	for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
246 		if (nid == pm_procs_all[i].node_id)
247 			return &pm_procs_all[i];
248 	}
249 	return NULL;
250 }
251 
252 /**
253  * pm_get_cpuid() - get the local cpu ID for a global node ID
254  * @nid:	node id of the processor
255  *
256  * Return: the cpu ID (starting from 0) for the subsystem
257  */
258 static unsigned int pm_get_cpuid(enum pm_node_id nid)
259 {
260 	for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
261 		if (pm_procs_all[i].node_id == nid)
262 			return i;
263 	}
264 	return UNDEFINED_CPUID;
265 }
266 
267 const struct pm_proc *primary_proc = &pm_procs_all[0];
268 
269 /**
270  * pm_client_suspend() - Client-specific suspend actions
271  *
272  * This function should contain any PU-specific actions
273  * required prior to sending suspend request to PMU
274  * Actions taken depend on the state system is suspending to.
275  */
276 void pm_client_suspend(const struct pm_proc *proc, unsigned int state)
277 {
278 	bakery_lock_get(&pm_client_secure_lock);
279 
280 	if (state == PM_STATE_SUSPEND_TO_RAM)
281 		pm_client_set_wakeup_sources();
282 
283 	/* Set powerdown request */
284 	mmio_write_32(APU_PWRCTL, mmio_read_32(APU_PWRCTL) | proc->pwrdn_mask);
285 
286 	bakery_lock_release(&pm_client_secure_lock);
287 }
288 
289 
290 /**
291  * pm_client_abort_suspend() - Client-specific abort-suspend actions
292  *
293  * This function should contain any PU-specific actions
294  * required for aborting a prior suspend request
295  */
296 void pm_client_abort_suspend(void)
297 {
298 	/* Enable interrupts at processor level (for current cpu) */
299 	gicv2_cpuif_enable();
300 
301 	bakery_lock_get(&pm_client_secure_lock);
302 
303 	/* Clear powerdown request */
304 	mmio_write_32(APU_PWRCTL,
305 		 mmio_read_32(APU_PWRCTL) & ~primary_proc->pwrdn_mask);
306 
307 	bakery_lock_release(&pm_client_secure_lock);
308 }
309 
310 /**
311  * pm_client_wakeup() - Client-specific wakeup actions
312  *
313  * This function should contain any PU-specific actions
314  * required for waking up another APU core
315  */
316 void pm_client_wakeup(const struct pm_proc *proc)
317 {
318 	unsigned int cpuid = pm_get_cpuid(proc->node_id);
319 
320 	if (cpuid == UNDEFINED_CPUID)
321 		return;
322 
323 	bakery_lock_get(&pm_client_secure_lock);
324 
325 	/* clear powerdown bit for affected cpu */
326 	uint32_t val = mmio_read_32(APU_PWRCTL);
327 	val &= ~(proc->pwrdn_mask);
328 	mmio_write_32(APU_PWRCTL, val);
329 
330 	bakery_lock_release(&pm_client_secure_lock);
331 }
332