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