Home | History | Annotate | Download | only in pm_service
      1 /*
      2  * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
      3  *
      4  * SPDX-License-Identifier: BSD-3-Clause
      5  */
      6 
      7 #include <arch_helpers.h>
      8 #include <bakery_lock.h>
      9 #include <mmio.h>
     10 #include <platform.h>
     11 #include "../zynqmp_private.h"
     12 #include "pm_ipi.h"
     13 
     14 /* IPI message buffers */
     15 #define IPI_BUFFER_BASEADDR	0xFF990000U
     16 
     17 #define IPI_BUFFER_RPU_0_BASE	(IPI_BUFFER_BASEADDR + 0x0U)
     18 #define IPI_BUFFER_RPU_1_BASE	(IPI_BUFFER_BASEADDR + 0x200U)
     19 #define IPI_BUFFER_APU_BASE	(IPI_BUFFER_BASEADDR + 0x400U)
     20 #define IPI_BUFFER_PL_0_BASE	(IPI_BUFFER_BASEADDR + 0x600U)
     21 #define IPI_BUFFER_PL_1_BASE	(IPI_BUFFER_BASEADDR + 0x800U)
     22 #define IPI_BUFFER_PL_2_BASE	(IPI_BUFFER_BASEADDR + 0xA00U)
     23 #define IPI_BUFFER_PL_3_BASE	(IPI_BUFFER_BASEADDR + 0xC00U)
     24 #define IPI_BUFFER_PMU_BASE	(IPI_BUFFER_BASEADDR + 0xE00U)
     25 
     26 #define IPI_BUFFER_TARGET_RPU_0_OFFSET	0x0U
     27 #define IPI_BUFFER_TARGET_RPU_1_OFFSET	0x40U
     28 #define IPI_BUFFER_TARGET_APU_OFFSET	0x80U
     29 #define IPI_BUFFER_TARGET_PL_0_OFFSET	0xC0U
     30 #define IPI_BUFFER_TARGET_PL_1_OFFSET	0x100U
     31 #define IPI_BUFFER_TARGET_PL_2_OFFSET	0x140U
     32 #define IPI_BUFFER_TARGET_PL_3_OFFSET	0x180U
     33 #define IPI_BUFFER_TARGET_PMU_OFFSET	0x1C0U
     34 
     35 #define IPI_BUFFER_MAX_WORDS	8
     36 
     37 #define IPI_BUFFER_REQ_OFFSET	0x0U
     38 #define IPI_BUFFER_RESP_OFFSET	0x20U
     39 
     40 /* IPI Base Address */
     41 #define IPI_BASEADDR		0XFF300000
     42 
     43 /* APU's IPI registers */
     44 #define IPI_APU_ISR		(IPI_BASEADDR + 0X00000010)
     45 #define IPI_APU_IER		(IPI_BASEADDR + 0X00000018)
     46 #define IPI_APU_IDR		(IPI_BASEADDR + 0X0000001C)
     47 #define IPI_APU_IXR_PMU_0_MASK		(1 << 16)
     48 
     49 #define IPI_TRIG_OFFSET		0
     50 #define IPI_OBS_OFFSET		4
     51 
     52 /* Power Management IPI interrupt number */
     53 #define PM_INT_NUM		0
     54 #define IPI_PMU_PM_INT_BASE	(IPI_PMU_0_TRIG + (PM_INT_NUM * 0x1000))
     55 #define IPI_PMU_PM_INT_MASK	(IPI_APU_IXR_PMU_0_MASK << PM_INT_NUM)
     56 #if (PM_INT_NUM < 0 || PM_INT_NUM > 3)
     57 	#error PM_INT_NUM value out of range
     58 #endif
     59 
     60 #define IPI_APU_MASK		1U
     61 
     62 DEFINE_BAKERY_LOCK(pm_secure_lock);
     63 
     64 const struct pm_ipi apu_ipi = {
     65 	.mask = IPI_APU_MASK,
     66 	.base = IPI_BASEADDR,
     67 	.buffer_base = IPI_BUFFER_APU_BASE,
     68 };
     69 
     70 /**
     71  * pm_ipi_init() - Initialize IPI peripheral for communication with PMU
     72  *
     73  * @return	On success, the initialization function must return 0.
     74  *		Any other return value will cause the framework to ignore
     75  *		the service
     76  *
     77  * Called from pm_setup initialization function
     78  */
     79 int pm_ipi_init(void)
     80 {
     81 	bakery_lock_init(&pm_secure_lock);
     82 
     83 	/* IPI Interrupts Clear & Disable */
     84 	mmio_write_32(IPI_APU_ISR, 0xffffffff);
     85 	mmio_write_32(IPI_APU_IDR, 0xffffffff);
     86 
     87 	return 0;
     88 }
     89 
     90 /**
     91  * pm_ipi_wait() - wait for pmu to handle request
     92  * @proc	proc which is waiting for PMU to handle request
     93  */
     94 static enum pm_ret_status pm_ipi_wait(const struct pm_proc *proc)
     95 {
     96 	int status;
     97 
     98 	/* Wait until previous interrupt is handled by PMU */
     99 	do {
    100 		status = mmio_read_32(proc->ipi->base + IPI_OBS_OFFSET) &
    101 					IPI_PMU_PM_INT_MASK;
    102 		/* TODO: 1) Use timer to add delay between read attempts */
    103 		/* TODO: 2) Return PM_RET_ERR_TIMEOUT if this times out */
    104 	} while (status);
    105 
    106 	return PM_RET_SUCCESS;
    107 }
    108 
    109 /**
    110  * pm_ipi_send_common() - Sends IPI request to the PMU
    111  * @proc	Pointer to the processor who is initiating request
    112  * @payload	API id and call arguments to be written in IPI buffer
    113  *
    114  * Send an IPI request to the power controller. Caller needs to hold
    115  * the 'pm_secure_lock' lock.
    116  *
    117  * @return	Returns status, either success or error+reason
    118  */
    119 static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc,
    120 					     uint32_t payload[PAYLOAD_ARG_CNT])
    121 {
    122 	unsigned int offset = 0;
    123 	uintptr_t buffer_base = proc->ipi->buffer_base +
    124 					IPI_BUFFER_TARGET_PMU_OFFSET +
    125 					IPI_BUFFER_REQ_OFFSET;
    126 
    127 	/* Wait until previous interrupt is handled by PMU */
    128 	pm_ipi_wait(proc);
    129 
    130 	/* Write payload into IPI buffer */
    131 	for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) {
    132 		mmio_write_32(buffer_base + offset, payload[i]);
    133 		offset += PAYLOAD_ARG_SIZE;
    134 	}
    135 	/* Generate IPI to PMU */
    136 	mmio_write_32(proc->ipi->base + IPI_TRIG_OFFSET, IPI_PMU_PM_INT_MASK);
    137 
    138 	return PM_RET_SUCCESS;
    139 }
    140 
    141 /**
    142  * pm_ipi_send() - Sends IPI request to the PMU
    143  * @proc	Pointer to the processor who is initiating request
    144  * @payload	API id and call arguments to be written in IPI buffer
    145  *
    146  * Send an IPI request to the power controller.
    147  *
    148  * @return	Returns status, either success or error+reason
    149  */
    150 enum pm_ret_status pm_ipi_send(const struct pm_proc *proc,
    151 			       uint32_t payload[PAYLOAD_ARG_CNT])
    152 {
    153 	enum pm_ret_status ret;
    154 
    155 	bakery_lock_get(&pm_secure_lock);
    156 
    157 	ret = pm_ipi_send_common(proc, payload);
    158 
    159 	bakery_lock_release(&pm_secure_lock);
    160 
    161 	return ret;
    162 }
    163 
    164 
    165 /**
    166  * pm_ipi_buff_read() - Reads IPI response after PMU has handled interrupt
    167  * @proc	Pointer to the processor who is waiting and reading response
    168  * @value	Used to return value from IPI buffer element (optional)
    169  * @count	Number of values to return in @value
    170  *
    171  * @return	Returns status, either success or error+reason
    172  */
    173 static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc,
    174 					   unsigned int *value, size_t count)
    175 {
    176 	size_t i;
    177 	uintptr_t buffer_base = proc->ipi->buffer_base +
    178 				IPI_BUFFER_TARGET_PMU_OFFSET +
    179 				IPI_BUFFER_RESP_OFFSET;
    180 
    181 	pm_ipi_wait(proc);
    182 
    183 	/*
    184 	 * Read response from IPI buffer
    185 	 * buf-0: success or error+reason
    186 	 * buf-1: value
    187 	 * buf-2: unused
    188 	 * buf-3: unused
    189 	 */
    190 	for (i = 1; i <= count; i++) {
    191 		*value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
    192 		value++;
    193 	}
    194 
    195 	return mmio_read_32(buffer_base);
    196 }
    197 
    198 /**
    199  * pm_ipi_buff_read_callb() - Reads IPI response after PMU has handled interrupt
    200  * @value	Used to return value from IPI buffer element (optional)
    201  * @count	Number of values to return in @value
    202  *
    203  * @return	Returns status, either success or error+reason
    204  */
    205 void pm_ipi_buff_read_callb(unsigned int *value, size_t count)
    206 {
    207 	size_t i;
    208 	uintptr_t buffer_base = IPI_BUFFER_PMU_BASE +
    209 				IPI_BUFFER_TARGET_APU_OFFSET +
    210 				IPI_BUFFER_REQ_OFFSET;
    211 
    212 	if (count > IPI_BUFFER_MAX_WORDS)
    213 		count = IPI_BUFFER_MAX_WORDS;
    214 
    215 	for (i = 0; i <= count; i++) {
    216 		*value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE));
    217 		value++;
    218 	}
    219 }
    220 
    221 /**
    222  * pm_ipi_send_sync() - Sends IPI request to the PMU
    223  * @proc	Pointer to the processor who is initiating request
    224  * @payload	API id and call arguments to be written in IPI buffer
    225  * @value	Used to return value from IPI buffer element (optional)
    226  * @count	Number of values to return in @value
    227  *
    228  * Send an IPI request to the power controller and wait for it to be handled.
    229  *
    230  * @return	Returns status, either success or error+reason and, optionally,
    231  *		@value
    232  */
    233 enum pm_ret_status pm_ipi_send_sync(const struct pm_proc *proc,
    234 				    uint32_t payload[PAYLOAD_ARG_CNT],
    235 				    unsigned int *value, size_t count)
    236 {
    237 	enum pm_ret_status ret;
    238 
    239 	bakery_lock_get(&pm_secure_lock);
    240 
    241 	ret = pm_ipi_send_common(proc, payload);
    242 	if (ret != PM_RET_SUCCESS)
    243 		goto unlock;
    244 
    245 	ret = pm_ipi_buff_read(proc, value, count);
    246 
    247 unlock:
    248 	bakery_lock_release(&pm_secure_lock);
    249 
    250 	return ret;
    251 }
    252 
    253 void pm_ipi_irq_enable(void)
    254 {
    255 	mmio_write_32(IPI_APU_IER, IPI_APU_IXR_PMU_0_MASK);
    256 }
    257 
    258 void pm_ipi_irq_disable(void)
    259 {
    260 	mmio_write_32(IPI_APU_IDR, IPI_APU_IXR_PMU_0_MASK);
    261 }
    262 
    263 void pm_ipi_irq_clear(void)
    264 {
    265 	mmio_write_32(IPI_APU_ISR, IPI_APU_IXR_PMU_0_MASK);
    266 }
    267