1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2022 Foundries.io Ltd
4  * Jorge Ramirez-Ortiz <jorge@foundries.io>
5  */
6 
7 #include <arm.h>
8 #include <drivers/versal_mbox.h>
9 #include <drivers/versal_pm.h>
10 #include <initcall.h>
11 #include <kernel/cache_helpers.h>
12 #include <kernel/delay.h>
13 #include <kernel/panic.h>
14 #include <kernel/thread.h>
15 #include <mm/core_memprot.h>
16 #include <string.h>
17 #include <tee/cache.h>
18 #include <tee_api_types.h>
19 #include <utee_defines.h>
20 
21 /* VERSAL_SIP_UID: 2ab9e4ec-93b9-11e7-a019dfe0dbad0ae0 */
22 #define VERSAL_SIP_UID_0 U(0xece4b92a)
23 #define VERSAL_SIP_UID_1 U(0xe711b993)
24 #define VERSAL_SIP_UID_2 U(0xe0df19a0)
25 #define VERSAL_SIP_UID_3 U(0xe00aaddb)
26 #define VERSAL_SIP_MAJOR  0
27 #define VERSAL_SIP_MINOR  1
28 
29 #define VERSAL_SIP_SVC_VERSION		0x8200ff03
30 #define VERSAL_SIP_SVC_UID		0x8200ff01
31 #define VERSAL_SIP_SVC			0xc2000000
32 
33 #define PAYLOAD_ARG_CNT		8
34 
35 /* MBOX IPI */
36 #define PM_MODULE_SHIFT		8
37 #define PM_MODULE		2
38 #define PM_API_ID(x)		((PM_MODULE << PM_MODULE_SHIFT) | (x))
39 #define VERSAL_PM_MAJOR		0
40 #define VERSAL_PM_MINOR		1
41 
42 /* PM API ids */
43 #define PM_GET_API_VERSION		1
44 #define PM_GET_DEVICE_STATUS		3
45 #define PM_GET_OP_CHARACTERISTIC	4
46 #define PM_REGISTER_NOTIFIER		5
47 #define PM_REQ_SUSPEND			6
48 #define PM_SELF_SUSPEND			7
49 #define PM_FORCE_POWERDOWN		8
50 #define PM_ABORT_SUSPEND		9
51 #define PM_REQ_WAKEUP			10
52 #define PM_SET_WAKEUP_SOURCE		11
53 #define PM_SYSTEM_SHUTDOWN		12
54 #define PM_REQUEST_DEVICE		13
55 #define PM_RELEASE_DEVICE		14
56 #define PM_SET_REQUIREMENT		15
57 #define PM_SET_MAX_LATENCY		16
58 #define PM_RESET_ASSERT			17
59 #define PM_RESET_GET_STATUS		18
60 #define PM_INIT_FINALIZE		21
61 #define PM_GET_CHIPID			24
62 #define	PM_PINCTRL_REQUEST		28
63 #define	PM_PINCTRL_RELEASE		29
64 #define	PM_PINCTRL_GET_FUNCTION		30
65 #define	PM_PINCTRL_SET_FUNCTION		31
66 #define	PM_PINCTRL_CONFIG_PARAM_GET	32
67 #define	PM_PINCTRL_CONFIG_PARAM_SET	33
68 #define PM_IOCTL			34
69 #define PM_QUERY_DATA			35
70 #define PM_CLOCK_ENABLE			36
71 #define PM_CLOCK_DISABLE		37
72 #define PM_CLOCK_GETSTATE		38
73 #define PM_CLOCK_SETDIVIDER		39
74 #define PM_CLOCK_GETDIVIDER		40
75 #define PM_CLOCK_SETRATE		41
76 #define PM_CLOCK_GETRATE		42
77 #define PM_CLOCK_SETPARENT		43
78 #define PM_CLOCK_GETPARENT		44
79 #define PM_PLL_SET_PARAMETER		48
80 #define PM_PLL_GET_PARAMETER		49
81 #define PM_PLL_SET_MODE			50
82 #define PM_PLL_GET_MODE			51
83 #define PM_FEATURE_CHECK		63
84 
85 /* Loader API id */
86 #define PM_LOAD_PDI			0x701
87 
88 /* PDI sources */
89 #define PDI_SRC_JTAG		0x0
90 #define PDI_SRC_QSPI24		0x1
91 #define PDI_SRC_QSPI32		0x2
92 #define PDI_SRC_SD0		0x3
93 #define PDI_SRC_EMMC0		0x4
94 #define PDI_SRC_SD1		0x5
95 #define PDI_SRC_EMMC1		0x6
96 #define PDI_SRC_USB		0x7
97 #define PDI_SRC_OSPI		0x8
98 #define PDI_SRC_SBI		0x9
99 #define PDI_SRC_SMAP		0xA
100 #define PDI_SRC_PCIE		0xB
101 #define PDI_SRC_SD1_LS		0xE
102 #define PDI_SRC_DDR		0xF
103 
104 struct versal_sip_payload {
105 	uint32_t data[PAYLOAD_ARG_CNT];
106 };
107 
versal_sip_call(uint32_t smc_fid,uint32_t arg0,uint32_t arg1,uint32_t arg2,uint32_t arg3,struct versal_sip_payload * payload)108 static uint32_t versal_sip_call(uint32_t smc_fid, uint32_t arg0, uint32_t arg1,
109 				uint32_t arg2, uint32_t arg3,
110 				struct versal_sip_payload *payload)
111 {
112 	struct thread_smc_args args = {
113 		.a0 = smc_fid,
114 		.a1 = reg_pair_to_64(arg1, arg0),
115 		.a2 = reg_pair_to_64(arg3, arg2),
116 	};
117 
118 	thread_smccc(&args);
119 
120 	if (payload) {
121 		reg_pair_from_64(args.a0, &payload->data[1], &payload->data[0]);
122 		reg_pair_from_64(args.a1, &payload->data[3], &payload->data[2]);
123 		reg_pair_from_64(args.a2, &payload->data[5], &payload->data[4]);
124 		reg_pair_from_64(args.a3, &payload->data[7], &payload->data[6]);
125 	}
126 
127 	/* allow the PLM to output its debug information */
128 	if (IS_ENABLED(CFG_VERSAL_TRACE_PLM))
129 		mdelay(500);
130 
131 	return args.a0;
132 }
133 
134 /* SIP call to program the FPGA has been obsoleted, use the PLM */
versal_write_fpga(paddr_t pa)135 TEE_Result versal_write_fpga(paddr_t pa)
136 {
137 	struct versal_ipi_cmd cmd = { };
138 
139 	cmd.data[0] = PM_LOAD_PDI;
140 	cmd.data[1] = PDI_SRC_DDR;
141 	reg_pair_from_64(pa, &cmd.data[2], &cmd.data[3]);
142 
143 	if (versal_mbox_notify(&cmd, NULL, NULL))
144 		return TEE_ERROR_GENERIC;
145 
146 	return TEE_SUCCESS;
147 }
148 
versal_soc_version(uint8_t * version)149 TEE_Result versal_soc_version(uint8_t *version)
150 {
151 	struct versal_sip_payload p = { };
152 	const uint32_t version_shift = 12;
153 
154 	if (!version)
155 		return TEE_ERROR_BAD_PARAMETERS;
156 
157 	if (versal_sip_call(VERSAL_SIP_SVC | PM_GET_CHIPID, 0, 0, 0, 0, &p))
158 		return TEE_ERROR_GENERIC;
159 
160 	*version = p.data[2] >> version_shift;
161 
162 	return TEE_SUCCESS;
163 }
164 
uuid_is_versal_pm(void)165 static bool uuid_is_versal_pm(void)
166 {
167 	struct versal_sip_payload p = { };
168 
169 	versal_sip_call(VERSAL_SIP_SVC_UID, 0, 0, 0, 0, &p);
170 
171 	if (p.data[0] == VERSAL_SIP_UID_0 && p.data[2] == VERSAL_SIP_UID_1 &&
172 	    p.data[4] == VERSAL_SIP_UID_2 && p.data[6] == VERSAL_SIP_UID_3)
173 		return true;
174 
175 	return false;
176 }
177 
versal_check_pm_abi(void)178 static TEE_Result versal_check_pm_abi(void)
179 {
180 	struct versal_sip_payload p = { };
181 	struct versal_ipi_cmd cmd = { };
182 	struct versal_ipi_cmd rsp = { };
183 	unsigned int major = 0;
184 	unsigned int minor = 0;
185 
186 	if (!uuid_is_versal_pm()) {
187 		EMSG("Invalid SiP Service");
188 		return TEE_ERROR_GENERIC;
189 	}
190 
191 	if (versal_sip_call(VERSAL_SIP_SVC_VERSION, 0, 0, 0, 0, &p))
192 		return TEE_ERROR_GENERIC;
193 
194 	major = p.data[0];
195 	minor = p.data[2];
196 	if (major != VERSAL_SIP_MAJOR || minor < VERSAL_SIP_MINOR) {
197 		EMSG("Invalid SiP version: Major %d, Minor %d", major, minor);
198 		return TEE_ERROR_GENERIC;
199 	}
200 
201 	cmd.data[0] = PM_API_ID(PM_GET_API_VERSION);
202 	if (versal_mbox_notify(&cmd, &rsp, NULL))
203 		return TEE_ERROR_GENERIC;
204 
205 	major = rsp.data[1] & 0xFFFF;
206 	minor = rsp.data[1] >> 16;
207 	if (major != VERSAL_PM_MAJOR || minor < VERSAL_PM_MINOR) {
208 		EMSG("Invalid PM version: Major %d, Minor %d", major, minor);
209 		return TEE_ERROR_GENERIC;
210 	}
211 
212 	return TEE_SUCCESS;
213 }
214 
215 early_init_late(versal_check_pm_abi);
216