xref: /rk3399_ARM-atf/plat/xilinx/versal/pm_service/pm_client.c (revision 6e82cd8c9b37ad6226523e88829ecba776b15bc8)
1c73a90e5STejas Patel /*
2c73a90e5STejas Patel  * Copyright (c) 2019, Xilinx, Inc. All rights reserved.
3c73a90e5STejas Patel  *
4c73a90e5STejas Patel  * SPDX-License-Identifier: BSD-3-Clause
5c73a90e5STejas Patel  */
6c73a90e5STejas Patel 
7c73a90e5STejas Patel /*
8c73a90e5STejas Patel  * APU specific definition of processors in the subsystem as well as functions
9c73a90e5STejas Patel  * for getting information about and changing state of the APU.
10c73a90e5STejas Patel  */
11c73a90e5STejas Patel 
12*6e82cd8cSTejas Patel #include <assert.h>
13c73a90e5STejas Patel #include <plat_ipi.h>
14c73a90e5STejas Patel #include <platform_def.h>
15c73a90e5STejas Patel #include <versal_def.h>
16c73a90e5STejas Patel #include <lib/bakery_lock.h>
17fbb32695STejas Patel #include <lib/mmio.h>
18*6e82cd8cSTejas Patel #include <lib/utils.h>
19fbb32695STejas Patel #include <drivers/arm/gicv3.h>
20*6e82cd8cSTejas Patel #include <drivers/arm/gic_common.h>
21fbb32695STejas Patel #include <plat/common/platform.h>
22*6e82cd8cSTejas Patel #include "pm_api_sys.h"
23c73a90e5STejas Patel #include "pm_client.h"
24c73a90e5STejas Patel 
2525b1a910STejas Patel #define UNDEFINED_CPUID		(~0)
26*6e82cd8cSTejas Patel #define IRQ_MAX		142
27*6e82cd8cSTejas Patel #define NUM_GICD_ISENABLER	((IRQ_MAX >> 5) + 1)
2825b1a910STejas Patel 
29c73a90e5STejas Patel DEFINE_BAKERY_LOCK(pm_client_secure_lock);
30c73a90e5STejas Patel 
31c73a90e5STejas Patel static const struct pm_ipi apu_ipi = {
32c73a90e5STejas Patel 	.local_ipi_id = IPI_ID_APU,
33c73a90e5STejas Patel 	.remote_ipi_id = IPI_ID_PMC,
34c73a90e5STejas Patel 	.buffer_base = IPI_BUFFER_APU_BASE,
35c73a90e5STejas Patel };
36c73a90e5STejas Patel 
37c73a90e5STejas Patel /* Order in pm_procs_all array must match cpu ids */
38c73a90e5STejas Patel static const struct pm_proc pm_procs_all[] = {
39c73a90e5STejas Patel 	{
40c73a90e5STejas Patel 		.node_id = XPM_DEVID_ACPU_0,
41c73a90e5STejas Patel 		.ipi = &apu_ipi,
42fbb32695STejas Patel 		.pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK,
43c73a90e5STejas Patel 	},
44c73a90e5STejas Patel 	{
45c73a90e5STejas Patel 		.node_id = XPM_DEVID_ACPU_1,
46c73a90e5STejas Patel 		.ipi = &apu_ipi,
47fbb32695STejas Patel 		.pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK,
48c73a90e5STejas Patel 	}
49c73a90e5STejas Patel };
50c73a90e5STejas Patel 
51c73a90e5STejas Patel const struct pm_proc *primary_proc = &pm_procs_all[0];
52fbb32695STejas Patel 
53*6e82cd8cSTejas Patel /* Interrupt to PM node index map */
54*6e82cd8cSTejas Patel static enum pm_device_node_idx irq_node_map[IRQ_MAX + 1] = {
55*6e82cd8cSTejas Patel 	[13] = XPM_NODEIDX_DEV_GPIO,
56*6e82cd8cSTejas Patel 	[14] = XPM_NODEIDX_DEV_I2C_0,
57*6e82cd8cSTejas Patel 	[15] = XPM_NODEIDX_DEV_I2C_1,
58*6e82cd8cSTejas Patel 	[16] = XPM_NODEIDX_DEV_SPI_0,
59*6e82cd8cSTejas Patel 	[17] = XPM_NODEIDX_DEV_SPI_1,
60*6e82cd8cSTejas Patel 	[18] = XPM_NODEIDX_DEV_UART_0,
61*6e82cd8cSTejas Patel 	[19] = XPM_NODEIDX_DEV_UART_1,
62*6e82cd8cSTejas Patel 	[20] = XPM_NODEIDX_DEV_CAN_FD_0,
63*6e82cd8cSTejas Patel 	[21] = XPM_NODEIDX_DEV_CAN_FD_1,
64*6e82cd8cSTejas Patel 	[22] = XPM_NODEIDX_DEV_USB_0,
65*6e82cd8cSTejas Patel 	[23] = XPM_NODEIDX_DEV_USB_0,
66*6e82cd8cSTejas Patel 	[24] = XPM_NODEIDX_DEV_USB_0,
67*6e82cd8cSTejas Patel 	[25] = XPM_NODEIDX_DEV_USB_0,
68*6e82cd8cSTejas Patel 	[26] = XPM_NODEIDX_DEV_USB_0,
69*6e82cd8cSTejas Patel 	[37] = XPM_NODEIDX_DEV_TTC_0,
70*6e82cd8cSTejas Patel 	[38] = XPM_NODEIDX_DEV_TTC_0,
71*6e82cd8cSTejas Patel 	[39] = XPM_NODEIDX_DEV_TTC_0,
72*6e82cd8cSTejas Patel 	[40] = XPM_NODEIDX_DEV_TTC_1,
73*6e82cd8cSTejas Patel 	[41] = XPM_NODEIDX_DEV_TTC_1,
74*6e82cd8cSTejas Patel 	[42] = XPM_NODEIDX_DEV_TTC_1,
75*6e82cd8cSTejas Patel 	[43] = XPM_NODEIDX_DEV_TTC_2,
76*6e82cd8cSTejas Patel 	[44] = XPM_NODEIDX_DEV_TTC_2,
77*6e82cd8cSTejas Patel 	[45] = XPM_NODEIDX_DEV_TTC_2,
78*6e82cd8cSTejas Patel 	[46] = XPM_NODEIDX_DEV_TTC_3,
79*6e82cd8cSTejas Patel 	[47] = XPM_NODEIDX_DEV_TTC_3,
80*6e82cd8cSTejas Patel 	[48] = XPM_NODEIDX_DEV_TTC_3,
81*6e82cd8cSTejas Patel 	[56] = XPM_NODEIDX_DEV_GEM_0,
82*6e82cd8cSTejas Patel 	[57] = XPM_NODEIDX_DEV_GEM_0,
83*6e82cd8cSTejas Patel 	[58] = XPM_NODEIDX_DEV_GEM_1,
84*6e82cd8cSTejas Patel 	[59] = XPM_NODEIDX_DEV_GEM_1,
85*6e82cd8cSTejas Patel 	[60] = XPM_NODEIDX_DEV_ADMA_0,
86*6e82cd8cSTejas Patel 	[61] = XPM_NODEIDX_DEV_ADMA_1,
87*6e82cd8cSTejas Patel 	[62] = XPM_NODEIDX_DEV_ADMA_2,
88*6e82cd8cSTejas Patel 	[63] = XPM_NODEIDX_DEV_ADMA_3,
89*6e82cd8cSTejas Patel 	[64] = XPM_NODEIDX_DEV_ADMA_4,
90*6e82cd8cSTejas Patel 	[65] = XPM_NODEIDX_DEV_ADMA_5,
91*6e82cd8cSTejas Patel 	[66] = XPM_NODEIDX_DEV_ADMA_6,
92*6e82cd8cSTejas Patel 	[67] = XPM_NODEIDX_DEV_ADMA_7,
93*6e82cd8cSTejas Patel 	[74] = XPM_NODEIDX_DEV_USB_0,
94*6e82cd8cSTejas Patel 	[126] = XPM_NODEIDX_DEV_SDIO_0,
95*6e82cd8cSTejas Patel 	[127] = XPM_NODEIDX_DEV_SDIO_0,
96*6e82cd8cSTejas Patel 	[128] = XPM_NODEIDX_DEV_SDIO_1,
97*6e82cd8cSTejas Patel 	[129] = XPM_NODEIDX_DEV_SDIO_1,
98*6e82cd8cSTejas Patel 	[142] = XPM_NODEIDX_DEV_RTC,
99*6e82cd8cSTejas Patel };
100*6e82cd8cSTejas Patel 
101*6e82cd8cSTejas Patel /**
102*6e82cd8cSTejas Patel  * irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number
103*6e82cd8cSTejas Patel  * @irq:	Interrupt number
104*6e82cd8cSTejas Patel  *
105*6e82cd8cSTejas Patel  * Return:	PM node index corresponding to the specified interrupt
106*6e82cd8cSTejas Patel  */
107*6e82cd8cSTejas Patel static enum pm_device_node_idx irq_to_pm_node_idx(unsigned int irq)
108*6e82cd8cSTejas Patel {
109*6e82cd8cSTejas Patel 	assert(irq <= IRQ_MAX);
110*6e82cd8cSTejas Patel 	return irq_node_map[irq];
111*6e82cd8cSTejas Patel }
112*6e82cd8cSTejas Patel 
113*6e82cd8cSTejas Patel /**
114*6e82cd8cSTejas Patel  * pm_client_set_wakeup_sources - Set all devices with enabled interrupts as
115*6e82cd8cSTejas Patel  *				  wake sources in the LibPM.
116*6e82cd8cSTejas Patel  */
117*6e82cd8cSTejas Patel static void pm_client_set_wakeup_sources(void)
118*6e82cd8cSTejas Patel {
119*6e82cd8cSTejas Patel 	uint32_t reg_num;
120*6e82cd8cSTejas Patel 	uint32_t device_id;
121*6e82cd8cSTejas Patel 	uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX];
122*6e82cd8cSTejas Patel 	uintptr_t isenabler1 = PLAT_VERSAL_GICD_BASE + GICD_ISENABLER + 4;
123*6e82cd8cSTejas Patel 
124*6e82cd8cSTejas Patel 	zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set));
125*6e82cd8cSTejas Patel 
126*6e82cd8cSTejas Patel 	for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) {
127*6e82cd8cSTejas Patel 		uint32_t base_irq = reg_num << ISENABLER_SHIFT;
128*6e82cd8cSTejas Patel 		uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2));
129*6e82cd8cSTejas Patel 
130*6e82cd8cSTejas Patel 		if (!reg)
131*6e82cd8cSTejas Patel 			continue;
132*6e82cd8cSTejas Patel 
133*6e82cd8cSTejas Patel 		while (reg) {
134*6e82cd8cSTejas Patel 			enum pm_device_node_idx node_idx;
135*6e82cd8cSTejas Patel 			uint32_t idx, ret, irq, lowest_set = reg & (-reg);
136*6e82cd8cSTejas Patel 
137*6e82cd8cSTejas Patel 			idx = __builtin_ctz(lowest_set);
138*6e82cd8cSTejas Patel 			irq = base_irq + idx;
139*6e82cd8cSTejas Patel 
140*6e82cd8cSTejas Patel 			if (irq > IRQ_MAX)
141*6e82cd8cSTejas Patel 				break;
142*6e82cd8cSTejas Patel 
143*6e82cd8cSTejas Patel 			node_idx = irq_to_pm_node_idx(irq);
144*6e82cd8cSTejas Patel 			reg &= ~lowest_set;
145*6e82cd8cSTejas Patel 
146*6e82cd8cSTejas Patel 			if ((node_idx != XPM_NODEIDX_DEV_MIN) &&
147*6e82cd8cSTejas Patel 			    (!pm_wakeup_nodes_set[node_idx])) {
148*6e82cd8cSTejas Patel 				/* Get device ID from node index */
149*6e82cd8cSTejas Patel 				device_id = PERIPH_DEVID(node_idx);
150*6e82cd8cSTejas Patel 				ret = pm_set_wakeup_source(XPM_DEVID_ACPU_0,
151*6e82cd8cSTejas Patel 							   device_id, 1);
152*6e82cd8cSTejas Patel 				pm_wakeup_nodes_set[node_idx] = !ret;
153*6e82cd8cSTejas Patel 			}
154*6e82cd8cSTejas Patel 		}
155*6e82cd8cSTejas Patel 	}
156*6e82cd8cSTejas Patel }
157*6e82cd8cSTejas Patel 
158fbb32695STejas Patel /**
159fbb32695STejas Patel  * pm_client_suspend() - Client-specific suspend actions
160fbb32695STejas Patel  *
161fbb32695STejas Patel  * This function should contain any PU-specific actions
162fbb32695STejas Patel  * required prior to sending suspend request to PMU
163fbb32695STejas Patel  * Actions taken depend on the state system is suspending to.
164fbb32695STejas Patel  */
165fbb32695STejas Patel void pm_client_suspend(const struct pm_proc *proc, unsigned int state)
166fbb32695STejas Patel {
167fbb32695STejas Patel 	bakery_lock_get(&pm_client_secure_lock);
168fbb32695STejas Patel 
169*6e82cd8cSTejas Patel 	if (state == PM_STATE_SUSPEND_TO_RAM)
170*6e82cd8cSTejas Patel 		pm_client_set_wakeup_sources();
171*6e82cd8cSTejas Patel 
172fbb32695STejas Patel 	/* Set powerdown request */
173fbb32695STejas Patel 	mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) |
174fbb32695STejas Patel 		      proc->pwrdn_mask);
175fbb32695STejas Patel 
176fbb32695STejas Patel 	bakery_lock_release(&pm_client_secure_lock);
177fbb32695STejas Patel }
178fbb32695STejas Patel 
179fbb32695STejas Patel /**
180fbb32695STejas Patel  * pm_client_abort_suspend() - Client-specific abort-suspend actions
181fbb32695STejas Patel  *
182fbb32695STejas Patel  * This function should contain any PU-specific actions
183fbb32695STejas Patel  * required for aborting a prior suspend request
184fbb32695STejas Patel  */
185fbb32695STejas Patel void pm_client_abort_suspend(void)
186fbb32695STejas Patel {
187fbb32695STejas Patel 	/* Enable interrupts at processor level (for current cpu) */
188fbb32695STejas Patel 	gicv3_cpuif_enable(plat_my_core_pos());
189fbb32695STejas Patel 
190fbb32695STejas Patel 	bakery_lock_get(&pm_client_secure_lock);
191fbb32695STejas Patel 
192fbb32695STejas Patel 	/* Clear powerdown request */
193fbb32695STejas Patel 	mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) &
194fbb32695STejas Patel 		      ~primary_proc->pwrdn_mask);
195fbb32695STejas Patel 
196fbb32695STejas Patel 	bakery_lock_release(&pm_client_secure_lock);
197fbb32695STejas Patel }
198fbb32695STejas Patel 
199fbb32695STejas Patel /**
20025b1a910STejas Patel  * pm_get_cpuid() - get the local cpu ID for a global node ID
20125b1a910STejas Patel  * @nid:	node id of the processor
20225b1a910STejas Patel  *
20325b1a910STejas Patel  * Return: the cpu ID (starting from 0) for the subsystem
20425b1a910STejas Patel  */
20525b1a910STejas Patel static unsigned int pm_get_cpuid(uint32_t nid)
20625b1a910STejas Patel {
20725b1a910STejas Patel 	for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) {
20825b1a910STejas Patel 		if (pm_procs_all[i].node_id == nid)
20925b1a910STejas Patel 			return i;
21025b1a910STejas Patel 	}
21125b1a910STejas Patel 	return UNDEFINED_CPUID;
21225b1a910STejas Patel }
21325b1a910STejas Patel 
21425b1a910STejas Patel /**
21525b1a910STejas Patel  * pm_client_wakeup() - Client-specific wakeup actions
21625b1a910STejas Patel  *
21725b1a910STejas Patel  * This function should contain any PU-specific actions
21825b1a910STejas Patel  * required for waking up another APU core
21925b1a910STejas Patel  */
22025b1a910STejas Patel void pm_client_wakeup(const struct pm_proc *proc)
22125b1a910STejas Patel {
22225b1a910STejas Patel 	unsigned int cpuid = pm_get_cpuid(proc->node_id);
22325b1a910STejas Patel 
22425b1a910STejas Patel 	if (cpuid == UNDEFINED_CPUID)
22525b1a910STejas Patel 		return;
22625b1a910STejas Patel 
22725b1a910STejas Patel 	bakery_lock_get(&pm_client_secure_lock);
22825b1a910STejas Patel 
22925b1a910STejas Patel 	/* clear powerdown bit for affected cpu */
23025b1a910STejas Patel 	uint32_t val = mmio_read_32(FPD_APU_PWRCTL);
23125b1a910STejas Patel 	val &= ~(proc->pwrdn_mask);
23225b1a910STejas Patel 	mmio_write_32(FPD_APU_PWRCTL, val);
23325b1a910STejas Patel 
23425b1a910STejas Patel 	bakery_lock_release(&pm_client_secure_lock);
23525b1a910STejas Patel }
23625b1a910STejas Patel 
23725b1a910STejas Patel /**
238fbb32695STejas Patel  * pm_get_proc() - returns pointer to the proc structure
239fbb32695STejas Patel  * @cpuid:	id of the cpu whose proc struct pointer should be returned
240fbb32695STejas Patel  *
241fbb32695STejas Patel  * Return: pointer to a proc structure if proc is found, otherwise NULL
242fbb32695STejas Patel  */
243fbb32695STejas Patel const struct pm_proc *pm_get_proc(unsigned int cpuid)
244fbb32695STejas Patel {
245fbb32695STejas Patel 	if (cpuid < ARRAY_SIZE(pm_procs_all))
246fbb32695STejas Patel 		return &pm_procs_all[cpuid];
247fbb32695STejas Patel 
248fbb32695STejas Patel 	return NULL;
249fbb32695STejas Patel }
250