xref: /optee_os/core/drivers/zynqmp_pm.c (revision 214ee971badabdd9da0a45ebb5870999c0f0b291)
19b61a2bcSJorge Ramirez-Ortiz // SPDX-License-Identifier: BSD-2-Clause
29b61a2bcSJorge Ramirez-Ortiz /*
39b61a2bcSJorge Ramirez-Ortiz  * Copyright (C) 2021 Foundries.io Ltd
49b61a2bcSJorge Ramirez-Ortiz  */
59b61a2bcSJorge Ramirez-Ortiz 
69b61a2bcSJorge Ramirez-Ortiz #include <arm.h>
79b61a2bcSJorge Ramirez-Ortiz #include <drivers/zynqmp_pm.h>
89b61a2bcSJorge Ramirez-Ortiz #include <kernel/cache_helpers.h>
997558570SVesa Jääskeläinen #include <kernel/tee_misc.h>
109b61a2bcSJorge Ramirez-Ortiz #include <kernel/thread.h>
119b61a2bcSJorge Ramirez-Ortiz #include <mm/core_memprot.h>
129b61a2bcSJorge Ramirez-Ortiz #include <string.h>
139b61a2bcSJorge Ramirez-Ortiz #include <tee/cache.h>
149b61a2bcSJorge Ramirez-Ortiz #include <tee_api_types.h>
159b61a2bcSJorge Ramirez-Ortiz #include <types_ext.h>
169b61a2bcSJorge Ramirez-Ortiz #include <utee_defines.h>
179b61a2bcSJorge Ramirez-Ortiz 
189b61a2bcSJorge Ramirez-Ortiz /*
199b61a2bcSJorge Ramirez-Ortiz  * For additional details about ZynqMP specific SMC ID's and PM request
209b61a2bcSJorge Ramirez-Ortiz  * handling in TF-A check
219b61a2bcSJorge Ramirez-Ortiz  * https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842107/Arm+Trusted+Firmware
229b61a2bcSJorge Ramirez-Ortiz  */
239b61a2bcSJorge Ramirez-Ortiz #define EFUSE_ACCESS_SMC	0xC2000035
249b61a2bcSJorge Ramirez-Ortiz #define VERSION_ACCESS_SMC	0xC2000018
259b61a2bcSJorge Ramirez-Ortiz 
269b61a2bcSJorge Ramirez-Ortiz #define EFUSE_NOT_ENABLED	29
279b61a2bcSJorge Ramirez-Ortiz #define VERSION_MASK		GENMASK_32(3, 0)
289b61a2bcSJorge Ramirez-Ortiz 
zynqmp_sip_call(uint32_t pm_api_id,uint32_t arg0,uint32_t arg1,uint32_t arg2,uint32_t arg3,uint32_t * payload)299b61a2bcSJorge Ramirez-Ortiz static uint32_t zynqmp_sip_call(uint32_t pm_api_id, uint32_t arg0,
309b61a2bcSJorge Ramirez-Ortiz 				uint32_t arg1, uint32_t arg2, uint32_t arg3,
319b61a2bcSJorge Ramirez-Ortiz 				uint32_t *payload)
329b61a2bcSJorge Ramirez-Ortiz {
339b61a2bcSJorge Ramirez-Ortiz 	struct thread_smc_args args = {
349b61a2bcSJorge Ramirez-Ortiz 		.a0 = pm_api_id,
359b61a2bcSJorge Ramirez-Ortiz 		.a1 = reg_pair_to_64(arg1, arg0),
369b61a2bcSJorge Ramirez-Ortiz 		.a2 = reg_pair_to_64(arg3, arg2),
379b61a2bcSJorge Ramirez-Ortiz 	};
389b61a2bcSJorge Ramirez-Ortiz 
399b61a2bcSJorge Ramirez-Ortiz 	thread_smccc(&args);
409b61a2bcSJorge Ramirez-Ortiz 
419b61a2bcSJorge Ramirez-Ortiz 	if (payload) {
429b61a2bcSJorge Ramirez-Ortiz 		switch (pm_api_id) {
439b61a2bcSJorge Ramirez-Ortiz 		case EFUSE_ACCESS_SMC:
449b61a2bcSJorge Ramirez-Ortiz 			*payload = args.a0 >> 32;
459b61a2bcSJorge Ramirez-Ortiz 			break;
469b61a2bcSJorge Ramirez-Ortiz 		case VERSION_ACCESS_SMC:
479b61a2bcSJorge Ramirez-Ortiz 			*payload = args.a1 & VERSION_MASK;
489b61a2bcSJorge Ramirez-Ortiz 			break;
499b61a2bcSJorge Ramirez-Ortiz 		default:
509b61a2bcSJorge Ramirez-Ortiz 			break;
519b61a2bcSJorge Ramirez-Ortiz 		}
529b61a2bcSJorge Ramirez-Ortiz 	}
539b61a2bcSJorge Ramirez-Ortiz 
549b61a2bcSJorge Ramirez-Ortiz 	return args.a0;
559b61a2bcSJorge Ramirez-Ortiz }
569b61a2bcSJorge Ramirez-Ortiz 
579b61a2bcSJorge Ramirez-Ortiz /*
589b61a2bcSJorge Ramirez-Ortiz  * Stores all required details to read/write eFuse memory.
599b61a2bcSJorge Ramirez-Ortiz  * @src:        Physical address of the buffer to store the data to be
609b61a2bcSJorge Ramirez-Ortiz  *              written/read (in LE)
619b61a2bcSJorge Ramirez-Ortiz  * @size:       number of 32-bit words to be read/written
629b61a2bcSJorge Ramirez-Ortiz  * @offset:     offset in bytes to be read from/written to
639b61a2bcSJorge Ramirez-Ortiz  * @flag:       EFUSE_READ  - represents eFuse read operation
649b61a2bcSJorge Ramirez-Ortiz  *              EFUSE_WRITE - represents eFuse write operation
659b61a2bcSJorge Ramirez-Ortiz  * @pufuserfuse:0 - represents non-PUF eFuses, offset is used for read/write
669b61a2bcSJorge Ramirez-Ortiz  *              1 - represents PUF user eFuse row number.
679b61a2bcSJorge Ramirez-Ortiz  */
689b61a2bcSJorge Ramirez-Ortiz struct xilinx_efuse {
699b61a2bcSJorge Ramirez-Ortiz 	uint64_t src;
709b61a2bcSJorge Ramirez-Ortiz 	uint32_t size;
719b61a2bcSJorge Ramirez-Ortiz 	uint32_t offset;
729b61a2bcSJorge Ramirez-Ortiz 	uint32_t flag;
739b61a2bcSJorge Ramirez-Ortiz 	uint32_t pufuserfuse;
749b61a2bcSJorge Ramirez-Ortiz };
759b61a2bcSJorge Ramirez-Ortiz 
769b61a2bcSJorge Ramirez-Ortiz enum efuse_op { EFUSE_READ = 0, EFUSE_WRITE = 1 };
779b61a2bcSJorge Ramirez-Ortiz 
789b61a2bcSJorge Ramirez-Ortiz #define EFUSE_ELT(__x) \
799b61a2bcSJorge Ramirez-Ortiz 	[__x] = { \
809b61a2bcSJorge Ramirez-Ortiz 		.offset = ZYNQMP_EFUSE_##__x##_OFFSET, \
819b61a2bcSJorge Ramirez-Ortiz 		.bytes = ZYNQMP_EFUSE_##__x##_LENGTH, \
829b61a2bcSJorge Ramirez-Ortiz 	}
839b61a2bcSJorge Ramirez-Ortiz 
849b61a2bcSJorge Ramirez-Ortiz static const struct {
859b61a2bcSJorge Ramirez-Ortiz 	uint32_t offset;
869b61a2bcSJorge Ramirez-Ortiz 	uint32_t bytes;
879b61a2bcSJorge Ramirez-Ortiz } efuse_tbl[] = {
889b61a2bcSJorge Ramirez-Ortiz 	EFUSE_ELT(DNA),
899b61a2bcSJorge Ramirez-Ortiz 	EFUSE_ELT(IP_DISABLE),
90*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER0),
91*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER1),
92*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER2),
93*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER3),
94*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER4),
95*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER5),
96*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER6),
97*214ee971SVesa Jääskeläinen 	EFUSE_ELT(USER7),
989b61a2bcSJorge Ramirez-Ortiz 	EFUSE_ELT(MISC_USER_CTRL),
999b61a2bcSJorge Ramirez-Ortiz 	EFUSE_ELT(SEC_CTRL),
1009b61a2bcSJorge Ramirez-Ortiz };
1019b61a2bcSJorge Ramirez-Ortiz 
efuse_op(enum efuse_op op,uint8_t * buf,size_t buf_sz,enum zynqmp_efuse_id id,bool puf)1029b61a2bcSJorge Ramirez-Ortiz static TEE_Result efuse_op(enum efuse_op op, uint8_t *buf, size_t buf_sz,
1039b61a2bcSJorge Ramirez-Ortiz 			   enum zynqmp_efuse_id id, bool puf)
1049b61a2bcSJorge Ramirez-Ortiz {
10597558570SVesa Jääskeläinen 	struct xilinx_efuse *efuse_op = NULL;
10697558570SVesa Jääskeläinen 	uint8_t *tmpbuf = NULL;
1079b61a2bcSJorge Ramirez-Ortiz 	paddr_t addr = 0;
10897558570SVesa Jääskeläinen 	uint32_t efuse_ret = 0;
10997558570SVesa Jääskeläinen 	TEE_Result res = TEE_ERROR_GENERIC;
1109b61a2bcSJorge Ramirez-Ortiz 
1119b61a2bcSJorge Ramirez-Ortiz 	if (!buf)
1129b61a2bcSJorge Ramirez-Ortiz 		return TEE_ERROR_BAD_PARAMETERS;
1139b61a2bcSJorge Ramirez-Ortiz 
1149b61a2bcSJorge Ramirez-Ortiz 	if (id >= ARRAY_SIZE(efuse_tbl)) {
1159b61a2bcSJorge Ramirez-Ortiz 		EMSG("Invalid efuse");
1169b61a2bcSJorge Ramirez-Ortiz 		return TEE_ERROR_BAD_PARAMETERS;
1179b61a2bcSJorge Ramirez-Ortiz 	}
1189b61a2bcSJorge Ramirez-Ortiz 
11997558570SVesa Jääskeläinen 	efuse_op = alloc_cache_aligned(sizeof(*efuse_op));
12097558570SVesa Jääskeläinen 	if (!efuse_op) {
12197558570SVesa Jääskeläinen 		EMSG("Failed to allocate cache aligned buffer for operation");
12297558570SVesa Jääskeläinen 		return TEE_ERROR_OUT_OF_MEMORY;
1239b61a2bcSJorge Ramirez-Ortiz 	}
1249b61a2bcSJorge Ramirez-Ortiz 
12597558570SVesa Jääskeläinen 	tmpbuf = alloc_cache_aligned(buf_sz);
12697558570SVesa Jääskeläinen 	if (!tmpbuf) {
12797558570SVesa Jääskeläinen 		EMSG("Failed to allocate cache aligned buffer for data");
12897558570SVesa Jääskeläinen 		res = TEE_ERROR_OUT_OF_MEMORY;
12997558570SVesa Jääskeläinen 		goto out;
13097558570SVesa Jääskeläinen 	}
1319b61a2bcSJorge Ramirez-Ortiz 
1326e96536eSVesa Jääskeläinen 	if (op == EFUSE_WRITE)
1336e96536eSVesa Jääskeläinen 		memcpy(tmpbuf, buf, buf_sz);
1346e96536eSVesa Jääskeläinen 
13597558570SVesa Jääskeläinen 	efuse_op->size = efuse_tbl[id].bytes / sizeof(uint32_t);
13697558570SVesa Jääskeläinen 	efuse_op->offset = efuse_tbl[id].offset;
13797558570SVesa Jääskeläinen 	efuse_op->src = virt_to_phys(tmpbuf);
13897558570SVesa Jääskeläinen 	efuse_op->pufuserfuse = puf;
13997558570SVesa Jääskeläinen 	efuse_op->flag = op;
1409b61a2bcSJorge Ramirez-Ortiz 
14197558570SVesa Jääskeläinen 	cache_operation(TEE_CACHECLEAN, tmpbuf, buf_sz);
14297558570SVesa Jääskeläinen 	cache_operation(TEE_CACHECLEAN, efuse_op, sizeof(*efuse_op));
1439b61a2bcSJorge Ramirez-Ortiz 
14497558570SVesa Jääskeläinen 	addr = virt_to_phys(efuse_op);
1459b61a2bcSJorge Ramirez-Ortiz 
14697558570SVesa Jääskeläinen 	efuse_ret = zynqmp_sip_call(EFUSE_ACCESS_SMC, addr >> 32, addr, 0, 0,
14797558570SVesa Jääskeläinen 				    NULL);
14897558570SVesa Jääskeläinen 	if (efuse_ret) {
14997558570SVesa Jääskeläinen 		if (efuse_ret == EFUSE_NOT_ENABLED)
1509b61a2bcSJorge Ramirez-Ortiz 			EMSG("eFuse access is not enabled");
1519b61a2bcSJorge Ramirez-Ortiz 		else
15297558570SVesa Jääskeläinen 			EMSG("Error in eFuse access %#"PRIx32, efuse_ret);
15397558570SVesa Jääskeläinen 		res = TEE_ERROR_GENERIC;
15497558570SVesa Jääskeläinen 		goto out;
1559b61a2bcSJorge Ramirez-Ortiz 	}
1569b61a2bcSJorge Ramirez-Ortiz 
15797558570SVesa Jääskeläinen 	if (op == EFUSE_READ) {
15897558570SVesa Jääskeläinen 		res = cache_operation(TEE_CACHEINVALIDATE, tmpbuf, buf_sz);
15997558570SVesa Jääskeläinen 		if (res)
16097558570SVesa Jääskeläinen 			goto out;
16197558570SVesa Jääskeläinen 		memcpy(buf, tmpbuf, buf_sz);
16297558570SVesa Jääskeläinen 	}
1639b61a2bcSJorge Ramirez-Ortiz 
1646e96536eSVesa Jääskeläinen 	res = TEE_SUCCESS;
1656e96536eSVesa Jääskeläinen 
16697558570SVesa Jääskeläinen out:
16797558570SVesa Jääskeläinen 	free(tmpbuf);
16897558570SVesa Jääskeläinen 	free(efuse_op);
16997558570SVesa Jääskeläinen 	return res;
1709b61a2bcSJorge Ramirez-Ortiz }
1719b61a2bcSJorge Ramirez-Ortiz 
zynqmp_efuse_read(uint8_t * buf,size_t sz,enum zynqmp_efuse_id id,bool puf)1729b61a2bcSJorge Ramirez-Ortiz TEE_Result zynqmp_efuse_read(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id,
1739b61a2bcSJorge Ramirez-Ortiz 			     bool puf)
1749b61a2bcSJorge Ramirez-Ortiz {
1759b61a2bcSJorge Ramirez-Ortiz 	return efuse_op(EFUSE_READ, buf, sz, id, puf);
1769b61a2bcSJorge Ramirez-Ortiz }
1779b61a2bcSJorge Ramirez-Ortiz 
zynqmp_efuse_write(uint8_t * buf,size_t sz,enum zynqmp_efuse_id id,bool puf)1786e96536eSVesa Jääskeläinen TEE_Result zynqmp_efuse_write(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id,
1796e96536eSVesa Jääskeläinen 			      bool puf)
1806e96536eSVesa Jääskeläinen {
1816e96536eSVesa Jääskeläinen 	return efuse_op(EFUSE_WRITE, buf, sz, id, puf);
1826e96536eSVesa Jääskeläinen }
1836e96536eSVesa Jääskeläinen 
zynqmp_soc_version(uint32_t * version)1849b61a2bcSJorge Ramirez-Ortiz TEE_Result zynqmp_soc_version(uint32_t *version)
1859b61a2bcSJorge Ramirez-Ortiz {
1869b61a2bcSJorge Ramirez-Ortiz 	uint32_t res = 0;
1879b61a2bcSJorge Ramirez-Ortiz 
1889b61a2bcSJorge Ramirez-Ortiz 	if (!version)
1899b61a2bcSJorge Ramirez-Ortiz 		return TEE_ERROR_BAD_PARAMETERS;
1909b61a2bcSJorge Ramirez-Ortiz 
1919b61a2bcSJorge Ramirez-Ortiz 	res = zynqmp_sip_call(VERSION_ACCESS_SMC, 0, 0, 0, 0, version);
1929b61a2bcSJorge Ramirez-Ortiz 	if (res) {
1939b61a2bcSJorge Ramirez-Ortiz 		EMSG("Failed to retrieve version");
1949b61a2bcSJorge Ramirez-Ortiz 		return TEE_ERROR_GENERIC;
1959b61a2bcSJorge Ramirez-Ortiz 	}
1969b61a2bcSJorge Ramirez-Ortiz 
1979b61a2bcSJorge Ramirez-Ortiz 	return TEE_SUCCESS;
1989b61a2bcSJorge Ramirez-Ortiz }
199