xref: /optee_os/core/drivers/zynqmp_pm.c (revision 6e96536ef5c53ffa3b81d0b9e5b54ac136370af1)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2021 Foundries.io Ltd
4  */
5 
6 #include <arm.h>
7 #include <drivers/zynqmp_pm.h>
8 #include <kernel/cache_helpers.h>
9 #include <kernel/tee_misc.h>
10 #include <kernel/thread.h>
11 #include <mm/core_memprot.h>
12 #include <string.h>
13 #include <tee/cache.h>
14 #include <tee_api_types.h>
15 #include <types_ext.h>
16 #include <utee_defines.h>
17 
18 /*
19  * For additional details about ZynqMP specific SMC ID's and PM request
20  * handling in TF-A check
21  * https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842107/Arm+Trusted+Firmware
22  */
23 #define EFUSE_ACCESS_SMC	0xC2000035
24 #define VERSION_ACCESS_SMC	0xC2000018
25 
26 #define EFUSE_NOT_ENABLED	29
27 #define VERSION_MASK		GENMASK_32(3, 0)
28 
29 static uint32_t zynqmp_sip_call(uint32_t pm_api_id, uint32_t arg0,
30 				uint32_t arg1, uint32_t arg2, uint32_t arg3,
31 				uint32_t *payload)
32 {
33 	struct thread_smc_args args = {
34 		.a0 = pm_api_id,
35 		.a1 = reg_pair_to_64(arg1, arg0),
36 		.a2 = reg_pair_to_64(arg3, arg2),
37 	};
38 
39 	thread_smccc(&args);
40 
41 	if (payload) {
42 		switch (pm_api_id) {
43 		case EFUSE_ACCESS_SMC:
44 			*payload = args.a0 >> 32;
45 			break;
46 		case VERSION_ACCESS_SMC:
47 			*payload = args.a1 & VERSION_MASK;
48 			break;
49 		default:
50 			break;
51 		}
52 	}
53 
54 	return args.a0;
55 }
56 
57 /*
58  * Stores all required details to read/write eFuse memory.
59  * @src:        Physical address of the buffer to store the data to be
60  *              written/read (in LE)
61  * @size:       number of 32-bit words to be read/written
62  * @offset:     offset in bytes to be read from/written to
63  * @flag:       EFUSE_READ  - represents eFuse read operation
64  *              EFUSE_WRITE - represents eFuse write operation
65  * @pufuserfuse:0 - represents non-PUF eFuses, offset is used for read/write
66  *              1 - represents PUF user eFuse row number.
67  */
68 struct xilinx_efuse {
69 	uint64_t src;
70 	uint32_t size;
71 	uint32_t offset;
72 	uint32_t flag;
73 	uint32_t pufuserfuse;
74 };
75 
76 enum efuse_op { EFUSE_READ = 0, EFUSE_WRITE = 1 };
77 
78 #define EFUSE_ELT(__x) \
79 	[__x] = { \
80 		.offset = ZYNQMP_EFUSE_##__x##_OFFSET, \
81 		.bytes = ZYNQMP_EFUSE_##__x##_LENGTH, \
82 	}
83 
84 static const struct {
85 	uint32_t offset;
86 	uint32_t bytes;
87 } efuse_tbl[] = {
88 	EFUSE_ELT(DNA),
89 	EFUSE_ELT(IP_DISABLE),
90 	EFUSE_ELT(MISC_USER_CTRL),
91 	EFUSE_ELT(SEC_CTRL),
92 };
93 
94 static TEE_Result efuse_op(enum efuse_op op, uint8_t *buf, size_t buf_sz,
95 			   enum zynqmp_efuse_id id, bool puf)
96 {
97 	struct xilinx_efuse *efuse_op = NULL;
98 	uint8_t *tmpbuf = NULL;
99 	paddr_t addr = 0;
100 	uint32_t efuse_ret = 0;
101 	TEE_Result res = TEE_ERROR_GENERIC;
102 
103 	if (!buf)
104 		return TEE_ERROR_BAD_PARAMETERS;
105 
106 	if (id >= ARRAY_SIZE(efuse_tbl)) {
107 		EMSG("Invalid efuse");
108 		return TEE_ERROR_BAD_PARAMETERS;
109 	}
110 
111 	efuse_op = alloc_cache_aligned(sizeof(*efuse_op));
112 	if (!efuse_op) {
113 		EMSG("Failed to allocate cache aligned buffer for operation");
114 		return TEE_ERROR_OUT_OF_MEMORY;
115 	}
116 
117 	tmpbuf = alloc_cache_aligned(buf_sz);
118 	if (!tmpbuf) {
119 		EMSG("Failed to allocate cache aligned buffer for data");
120 		res = TEE_ERROR_OUT_OF_MEMORY;
121 		goto out;
122 	}
123 
124 	if (op == EFUSE_WRITE)
125 		memcpy(tmpbuf, buf, buf_sz);
126 
127 	efuse_op->size = efuse_tbl[id].bytes / sizeof(uint32_t);
128 	efuse_op->offset = efuse_tbl[id].offset;
129 	efuse_op->src = virt_to_phys(tmpbuf);
130 	efuse_op->pufuserfuse = puf;
131 	efuse_op->flag = op;
132 
133 	cache_operation(TEE_CACHECLEAN, tmpbuf, buf_sz);
134 	cache_operation(TEE_CACHECLEAN, efuse_op, sizeof(*efuse_op));
135 
136 	addr = virt_to_phys(efuse_op);
137 
138 	efuse_ret = zynqmp_sip_call(EFUSE_ACCESS_SMC, addr >> 32, addr, 0, 0,
139 				    NULL);
140 	if (efuse_ret) {
141 		if (efuse_ret == EFUSE_NOT_ENABLED)
142 			EMSG("eFuse access is not enabled");
143 		else
144 			EMSG("Error in eFuse access %#"PRIx32, efuse_ret);
145 		res = TEE_ERROR_GENERIC;
146 		goto out;
147 	}
148 
149 	if (op == EFUSE_READ) {
150 		res = cache_operation(TEE_CACHEINVALIDATE, tmpbuf, buf_sz);
151 		if (res)
152 			goto out;
153 		memcpy(buf, tmpbuf, buf_sz);
154 	}
155 
156 	res = TEE_SUCCESS;
157 
158 out:
159 	free(tmpbuf);
160 	free(efuse_op);
161 	return res;
162 }
163 
164 TEE_Result zynqmp_efuse_read(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id,
165 			     bool puf)
166 {
167 	return efuse_op(EFUSE_READ, buf, sz, id, puf);
168 }
169 
170 TEE_Result zynqmp_efuse_write(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id,
171 			      bool puf)
172 {
173 	return efuse_op(EFUSE_WRITE, buf, sz, id, puf);
174 }
175 
176 TEE_Result zynqmp_soc_version(uint32_t *version)
177 {
178 	uint32_t res = 0;
179 
180 	if (!version)
181 		return TEE_ERROR_BAD_PARAMETERS;
182 
183 	res = zynqmp_sip_call(VERSION_ACCESS_SMC, 0, 0, 0, 0, version);
184 	if (res) {
185 		EMSG("Failed to retrieve version");
186 		return TEE_ERROR_GENERIC;
187 	}
188 
189 	return TEE_SUCCESS;
190 }
191