xref: /optee_os/core/drivers/stm32_bsec.c (revision 38df614f7f43a4aabaad8f3e9d4a6517c0ea7cb4)
1d64485e4SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2d64485e4SEtienne Carriere /*
38afb7c41SEtienne Carriere  * Copyright (c) 2017-2021, STMicroelectronics
4d64485e4SEtienne Carriere  */
5d64485e4SEtienne Carriere 
6d64485e4SEtienne Carriere #include <assert.h>
7890703c3SEtienne Carriere #include <config.h>
8d64485e4SEtienne Carriere #include <drivers/stm32_bsec.h>
9d64485e4SEtienne Carriere #include <io.h>
10d64485e4SEtienne Carriere #include <kernel/delay.h>
11890703c3SEtienne Carriere #include <kernel/dt.h>
1265401337SJens Wiklander #include <kernel/boot.h>
13d6df31b0SGatien Chevallier #include <kernel/pm.h>
14d64485e4SEtienne Carriere #include <kernel/spinlock.h>
15a2fc83d1SJerome Forissier #include <libfdt.h>
16d64485e4SEtienne Carriere #include <limits.h>
17d64485e4SEtienne Carriere #include <mm/core_memprot.h>
18d64485e4SEtienne Carriere #include <platform_config.h>
19d64485e4SEtienne Carriere #include <stm32_util.h>
20ef9888dcSEtienne Carriere #include <string.h>
21ef9888dcSEtienne Carriere #include <tee_api_defines.h>
22d64485e4SEtienne Carriere #include <types_ext.h>
23d64485e4SEtienne Carriere #include <util.h>
24d64485e4SEtienne Carriere 
25e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
26e090bb5aSGatien Chevallier #define DT_BSEC_COMPAT "st,stm32mp13-bsec"
27e090bb5aSGatien Chevallier #endif
28e090bb5aSGatien Chevallier #ifdef CFG_STM32MP15
29e090bb5aSGatien Chevallier #define DT_BSEC_COMPAT "st,stm32mp15-bsec"
30e090bb5aSGatien Chevallier #endif
31e090bb5aSGatien Chevallier 
32d64485e4SEtienne Carriere #define BSEC_OTP_MASK			GENMASK_32(4, 0)
334bbd20f1SGatien Chevallier #define BSEC_OTP_BANK_SHIFT		U(5)
34d64485e4SEtienne Carriere 
35d64485e4SEtienne Carriere /* Permanent lock bitmasks */
364bbd20f1SGatien Chevallier #define DATA_LOWER_OTP_PERLOCK_BIT	U(3)
374bbd20f1SGatien Chevallier #define DATA_UPPER_OTP_PERLOCK_BIT	U(1)
38d64485e4SEtienne Carriere 
39d64485e4SEtienne Carriere /* BSEC register offset */
404bbd20f1SGatien Chevallier #define BSEC_OTP_CONF_OFF		U(0x000)
414bbd20f1SGatien Chevallier #define BSEC_OTP_CTRL_OFF		U(0x004)
424bbd20f1SGatien Chevallier #define BSEC_OTP_WRDATA_OFF		U(0x008)
434bbd20f1SGatien Chevallier #define BSEC_OTP_STATUS_OFF		U(0x00C)
444bbd20f1SGatien Chevallier #define BSEC_OTP_LOCK_OFF		U(0x010)
454bbd20f1SGatien Chevallier #define BSEC_DEN_OFF			U(0x014)
464bbd20f1SGatien Chevallier #define BSEC_FEN_OFF			U(0x018)
474bbd20f1SGatien Chevallier #define BSEC_DISTURBED_OFF		U(0x01C)
484bbd20f1SGatien Chevallier #define BSEC_DISTURBED1_OFF		U(0x020)
494bbd20f1SGatien Chevallier #define BSEC_DISTURBED2_OFF		U(0x024)
504bbd20f1SGatien Chevallier #define BSEC_ERROR_OFF			U(0x034)
514bbd20f1SGatien Chevallier #define BSEC_ERROR1_OFF			U(0x038)
524bbd20f1SGatien Chevallier #define BSEC_ERROR2_OFF			U(0x03C)
534bbd20f1SGatien Chevallier #define BSEC_WRLOCK_OFF			U(0x04C)
544bbd20f1SGatien Chevallier #define BSEC_WRLOCK1_OFF		U(0x050)
554bbd20f1SGatien Chevallier #define BSEC_WRLOCK2_OFF		U(0x054)
564bbd20f1SGatien Chevallier #define BSEC_SPLOCK_OFF			U(0x064)
574bbd20f1SGatien Chevallier #define BSEC_SPLOCK1_OFF		U(0x068)
584bbd20f1SGatien Chevallier #define BSEC_SPLOCK2_OFF		U(0x06C)
594bbd20f1SGatien Chevallier #define BSEC_SWLOCK_OFF			U(0x07C)
604bbd20f1SGatien Chevallier #define BSEC_SWLOCK1_OFF		U(0x080)
614bbd20f1SGatien Chevallier #define BSEC_SWLOCK2_OFF		U(0x084)
624bbd20f1SGatien Chevallier #define BSEC_SRLOCK_OFF			U(0x094)
634bbd20f1SGatien Chevallier #define BSEC_SRLOCK1_OFF		U(0x098)
644bbd20f1SGatien Chevallier #define BSEC_SRLOCK2_OFF		U(0x09C)
654bbd20f1SGatien Chevallier #define BSEC_JTAG_IN_OFF		U(0x0AC)
664bbd20f1SGatien Chevallier #define BSEC_JTAG_OUT_OFF		U(0x0B0)
674bbd20f1SGatien Chevallier #define BSEC_SCRATCH_OFF		U(0x0B4)
684bbd20f1SGatien Chevallier #define BSEC_OTP_DATA_OFF		U(0x200)
694bbd20f1SGatien Chevallier #define BSEC_IPHW_CFG_OFF		U(0xFF0)
704bbd20f1SGatien Chevallier #define BSEC_IPVR_OFF			U(0xFF4)
714bbd20f1SGatien Chevallier #define BSEC_IP_ID_OFF			U(0xFF8)
724bbd20f1SGatien Chevallier #define BSEC_IP_MAGIC_ID_OFF		U(0xFFC)
73d64485e4SEtienne Carriere 
74d64485e4SEtienne Carriere /* BSEC_CONFIGURATION Register */
75d64485e4SEtienne Carriere #define BSEC_CONF_POWER_UP_MASK		BIT(0)
764bbd20f1SGatien Chevallier #define BSEC_CONF_POWER_UP_SHIFT	U(0)
77d64485e4SEtienne Carriere #define BSEC_CONF_FRQ_MASK		GENMASK_32(2, 1)
784bbd20f1SGatien Chevallier #define BSEC_CONF_FRQ_SHIFT		U(1)
79d64485e4SEtienne Carriere #define BSEC_CONF_PRG_WIDTH_MASK	GENMASK_32(6, 3)
804bbd20f1SGatien Chevallier #define BSEC_CONF_PRG_WIDTH_SHIFT	U(3)
81d64485e4SEtienne Carriere #define BSEC_CONF_TREAD_MASK		GENMASK_32(8, 7)
824bbd20f1SGatien Chevallier #define BSEC_CONF_TREAD_SHIFT		U(7)
83d64485e4SEtienne Carriere 
84d64485e4SEtienne Carriere /* BSEC_CONTROL Register */
854bbd20f1SGatien Chevallier #define BSEC_READ			U(0x000)
864bbd20f1SGatien Chevallier #define BSEC_WRITE			U(0x100)
874bbd20f1SGatien Chevallier #define BSEC_LOCK			U(0x200)
88d64485e4SEtienne Carriere 
89d64485e4SEtienne Carriere /* BSEC_STATUS Register */
907dfc80abSGatien Chevallier #define BSEC_MODE_SECURED		BIT(0)
917dfc80abSGatien Chevallier #define BSEC_MODE_INVALID		BIT(2)
927dfc80abSGatien Chevallier #define BSEC_MODE_BUSY			BIT(3)
937dfc80abSGatien Chevallier #define BSEC_MODE_PROGFAIL		BIT(4)
947dfc80abSGatien Chevallier #define BSEC_MODE_PWR			BIT(5)
95e090bb5aSGatien Chevallier #define BSEC_MODE_CLOSED		BIT(8)
96d64485e4SEtienne Carriere 
9701a06793SPatrick Delaunay /* BSEC_DEBUG bitfields */
9801a06793SPatrick Delaunay #ifdef CFG_STM32MP13
9901a06793SPatrick Delaunay #define BSEC_DEN_ALL_MSK		(GENMASK_32(11, 10) | GENMASK_32(8, 1))
10001a06793SPatrick Delaunay #endif
10101a06793SPatrick Delaunay #ifdef CFG_STM32MP15
10201a06793SPatrick Delaunay #define BSEC_DEN_ALL_MSK		GENMASK_32(11, 1)
10301a06793SPatrick Delaunay #endif
10401a06793SPatrick Delaunay 
105d64485e4SEtienne Carriere /*
106d64485e4SEtienne Carriere  * OTP Lock services definition
107d64485e4SEtienne Carriere  * Value must corresponding to the bit position in the register
108d64485e4SEtienne Carriere  */
1094bbd20f1SGatien Chevallier #define BSEC_LOCK_UPPER_OTP		U(0x00)
1104bbd20f1SGatien Chevallier #define BSEC_LOCK_DEBUG			U(0x02)
1114bbd20f1SGatien Chevallier #define BSEC_LOCK_PROGRAM		U(0x04)
112d64485e4SEtienne Carriere 
113d64485e4SEtienne Carriere /* Timeout when polling on status */
1144bbd20f1SGatien Chevallier #define BSEC_TIMEOUT_US			U(10000)
115890703c3SEtienne Carriere 
116d64485e4SEtienne Carriere struct bsec_dev {
117d64485e4SEtienne Carriere 	struct io_pa_va base;
118d64485e4SEtienne Carriere 	unsigned int upper_base;
119d64485e4SEtienne Carriere 	unsigned int max_id;
120890703c3SEtienne Carriere 	uint32_t *nsec_access;
121d64485e4SEtienne Carriere };
122d64485e4SEtienne Carriere 
123d64485e4SEtienne Carriere /* Only 1 instance of BSEC is expected per platform */
124d64485e4SEtienne Carriere static struct bsec_dev bsec_dev;
125d64485e4SEtienne Carriere 
126d64485e4SEtienne Carriere /* BSEC access protection */
127d64485e4SEtienne Carriere static unsigned int lock = SPINLOCK_UNLOCK;
128d64485e4SEtienne Carriere 
129d64485e4SEtienne Carriere static uint32_t bsec_lock(void)
130d64485e4SEtienne Carriere {
131d64485e4SEtienne Carriere 	return may_spin_lock(&lock);
132d64485e4SEtienne Carriere }
133d64485e4SEtienne Carriere 
134d64485e4SEtienne Carriere static void bsec_unlock(uint32_t exceptions)
135d64485e4SEtienne Carriere {
136d64485e4SEtienne Carriere 	may_spin_unlock(&lock, exceptions);
137d64485e4SEtienne Carriere }
138d64485e4SEtienne Carriere 
139d64485e4SEtienne Carriere static uint32_t otp_max_id(void)
140d64485e4SEtienne Carriere {
141d64485e4SEtienne Carriere 	return bsec_dev.max_id;
142d64485e4SEtienne Carriere }
143d64485e4SEtienne Carriere 
144586eee81SEtienne Carriere static uint32_t otp_upper_base(void)
145586eee81SEtienne Carriere {
146586eee81SEtienne Carriere 	return bsec_dev.upper_base;
147586eee81SEtienne Carriere }
148586eee81SEtienne Carriere 
149d64485e4SEtienne Carriere static uint32_t otp_bank_offset(uint32_t otp_id)
150d64485e4SEtienne Carriere {
151d64485e4SEtienne Carriere 	assert(otp_id <= otp_max_id());
152d64485e4SEtienne Carriere 
153d64485e4SEtienne Carriere 	return ((otp_id & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
154d64485e4SEtienne Carriere 		sizeof(uint32_t);
155d64485e4SEtienne Carriere }
156d64485e4SEtienne Carriere 
157d64485e4SEtienne Carriere static vaddr_t bsec_base(void)
158d64485e4SEtienne Carriere {
159c2e4eb43SAnton Rybakov 	return io_pa_or_va_secure(&bsec_dev.base, BSEC_IP_MAGIC_ID_OFF + 1);
160d64485e4SEtienne Carriere }
161d64485e4SEtienne Carriere 
162d64485e4SEtienne Carriere static uint32_t bsec_status(void)
163d64485e4SEtienne Carriere {
164d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_OTP_STATUS_OFF);
165d64485e4SEtienne Carriere }
166d64485e4SEtienne Carriere 
1677dfc80abSGatien Chevallier static bool state_is_invalid_mode(void)
1687dfc80abSGatien Chevallier {
1697dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_INVALID;
1707dfc80abSGatien Chevallier }
1717dfc80abSGatien Chevallier 
1727dfc80abSGatien Chevallier static bool state_is_secured_mode(void)
1737dfc80abSGatien Chevallier {
1747dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_SECURED;
1757dfc80abSGatien Chevallier }
1767dfc80abSGatien Chevallier 
1777dfc80abSGatien Chevallier static bool state_is_closed_mode(void)
1787dfc80abSGatien Chevallier {
17910fb0d97SGatien Chevallier 	uint32_t otp_cfg = 0;
1807dfc80abSGatien Chevallier 	uint32_t close_mode = 0;
18110fb0d97SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
1827dfc80abSGatien Chevallier 
183e090bb5aSGatien Chevallier 	if (IS_ENABLED(CFG_STM32MP13))
184e090bb5aSGatien Chevallier 		return bsec_status() & BSEC_MODE_CLOSED;
185e090bb5aSGatien Chevallier 
18610fb0d97SGatien Chevallier 	res = stm32_bsec_find_otp_in_nvmem_layout("cfg0_otp", &otp_cfg, NULL);
18710fb0d97SGatien Chevallier 	if (res)
18810fb0d97SGatien Chevallier 		panic("CFG0 OTP not found");
18910fb0d97SGatien Chevallier 
19010fb0d97SGatien Chevallier 	if (stm32_bsec_read_otp(&close_mode, otp_cfg))
1917dfc80abSGatien Chevallier 		panic("Unable to read OTP");
1927dfc80abSGatien Chevallier 
1937dfc80abSGatien Chevallier 	return close_mode & CFG0_OTP_CLOSED_DEVICE;
1947dfc80abSGatien Chevallier }
1957dfc80abSGatien Chevallier 
1961ac4ea14SEtienne Carriere /*
1971ac4ea14SEtienne Carriere  * Check that BSEC interface does not report an error
1981ac4ea14SEtienne Carriere  * @otp_id : OTP number
1991ac4ea14SEtienne Carriere  * @check_disturbed: check only error (false) or all sources (true)
2001ac4ea14SEtienne Carriere  * Return a TEE_Result compliant value
2011ac4ea14SEtienne Carriere  */
2021ac4ea14SEtienne Carriere static TEE_Result check_no_error(uint32_t otp_id, bool check_disturbed)
203d64485e4SEtienne Carriere {
204d64485e4SEtienne Carriere 	uint32_t bit = BIT(otp_id & BSEC_OTP_MASK);
205d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
206d64485e4SEtienne Carriere 
2071ac4ea14SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_ERROR_OFF + bank) & bit)
208d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
209d64485e4SEtienne Carriere 
2101ac4ea14SEtienne Carriere 	if (check_disturbed &&
2111ac4ea14SEtienne Carriere 	    io_read32(bsec_base() + BSEC_DISTURBED_OFF + bank) & bit)
212d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
213d64485e4SEtienne Carriere 
214d64485e4SEtienne Carriere 	return TEE_SUCCESS;
215d64485e4SEtienne Carriere }
216d64485e4SEtienne Carriere 
217d64485e4SEtienne Carriere static TEE_Result power_up_safmem(void)
218d64485e4SEtienne Carriere {
2199a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
220d64485e4SEtienne Carriere 
221d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP_MASK,
222d64485e4SEtienne Carriere 		  BSEC_CONF_POWER_UP_MASK);
223d64485e4SEtienne Carriere 
2249a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
225d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2267dfc80abSGatien Chevallier 		if (bsec_status() & BSEC_MODE_PWR)
227d64485e4SEtienne Carriere 			break;
228d64485e4SEtienne Carriere 
2297dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_PWR)
230d64485e4SEtienne Carriere 		return TEE_SUCCESS;
231d64485e4SEtienne Carriere 
232d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
233d64485e4SEtienne Carriere }
234d64485e4SEtienne Carriere 
235d64485e4SEtienne Carriere static TEE_Result power_down_safmem(void)
236d64485e4SEtienne Carriere {
2379a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
238d64485e4SEtienne Carriere 
239d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, 0, BSEC_CONF_POWER_UP_MASK);
240d64485e4SEtienne Carriere 
2419a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
242d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2437dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_PWR))
244d64485e4SEtienne Carriere 			break;
245d64485e4SEtienne Carriere 
2467dfc80abSGatien Chevallier 	if (!(bsec_status() & BSEC_MODE_PWR))
247d64485e4SEtienne Carriere 		return TEE_SUCCESS;
248d64485e4SEtienne Carriere 
249d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
250d64485e4SEtienne Carriere }
251d64485e4SEtienne Carriere 
2520042538eSEtienne Carriere TEE_Result stm32_bsec_shadow_register(uint32_t otp_id)
253d64485e4SEtienne Carriere {
254d64485e4SEtienne Carriere 	TEE_Result result = 0;
255d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
256d64485e4SEtienne Carriere 	uint64_t timeout_ref = 0;
257ef9888dcSEtienne Carriere 	bool locked = false;
258d64485e4SEtienne Carriere 
259ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
260ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sr_lock(otp_id, &locked);
261ef9888dcSEtienne Carriere 	if (result)
262ef9888dcSEtienne Carriere 		return result;
263d64485e4SEtienne Carriere 
264ef9888dcSEtienne Carriere 	if (locked)
265ef9888dcSEtienne Carriere 		DMSG("BSEC shadow warning: OTP locked");
266d64485e4SEtienne Carriere 
2677dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
2687dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
2697dfc80abSGatien Chevallier 
270d64485e4SEtienne Carriere 	exceptions = bsec_lock();
271d64485e4SEtienne Carriere 
272d64485e4SEtienne Carriere 	result = power_up_safmem();
273d64485e4SEtienne Carriere 	if (result)
2747b05d514SEtienne Carriere 		goto out;
275d64485e4SEtienne Carriere 
276d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_READ);
277d64485e4SEtienne Carriere 
278d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
279d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2807dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
281d64485e4SEtienne Carriere 			break;
282d64485e4SEtienne Carriere 
2837dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
2848afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
285d64485e4SEtienne Carriere 	else
2861ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
287d64485e4SEtienne Carriere 
288d64485e4SEtienne Carriere 	power_down_safmem();
289d64485e4SEtienne Carriere 
2907b05d514SEtienne Carriere out:
291d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
292d64485e4SEtienne Carriere 
293d64485e4SEtienne Carriere 	return result;
294d64485e4SEtienne Carriere }
295d64485e4SEtienne Carriere 
296d64485e4SEtienne Carriere TEE_Result stm32_bsec_read_otp(uint32_t *value, uint32_t otp_id)
297d64485e4SEtienne Carriere {
298d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
299d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
300d64485e4SEtienne Carriere 
3017dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3027dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3037dfc80abSGatien Chevallier 
304d64485e4SEtienne Carriere 	*value = io_read32(bsec_base() + BSEC_OTP_DATA_OFF +
305d64485e4SEtienne Carriere 			   (otp_id * sizeof(uint32_t)));
306d64485e4SEtienne Carriere 
3071ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
308d64485e4SEtienne Carriere }
309d64485e4SEtienne Carriere 
310d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_read_otp(uint32_t *otp_value, uint32_t otp_id)
311d64485e4SEtienne Carriere {
312d64485e4SEtienne Carriere 	TEE_Result result = 0;
313d64485e4SEtienne Carriere 
314d64485e4SEtienne Carriere 	result = stm32_bsec_shadow_register(otp_id);
315d64485e4SEtienne Carriere 	if (result) {
316ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Shadowing Error %#"PRIx32, otp_id, result);
317d64485e4SEtienne Carriere 		return result;
318d64485e4SEtienne Carriere 	}
319d64485e4SEtienne Carriere 
320d64485e4SEtienne Carriere 	result = stm32_bsec_read_otp(otp_value, otp_id);
321d64485e4SEtienne Carriere 	if (result)
322ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Read Error %#"PRIx32, otp_id, result);
323d64485e4SEtienne Carriere 
324d64485e4SEtienne Carriere 	return result;
325d64485e4SEtienne Carriere }
326d64485e4SEtienne Carriere 
327d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_otp(uint32_t value, uint32_t otp_id)
328d64485e4SEtienne Carriere {
329d64485e4SEtienne Carriere 	TEE_Result result = 0;
330d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
331d64485e4SEtienne Carriere 	vaddr_t otp_data_base = bsec_base() + BSEC_OTP_DATA_OFF;
332ef9888dcSEtienne Carriere 	bool locked = false;
333d64485e4SEtienne Carriere 
334ef9888dcSEtienne Carriere 	/* Check if write of OTP is locked, informative only */
335ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sw_lock(otp_id, &locked);
336ef9888dcSEtienne Carriere 	if (result)
337ef9888dcSEtienne Carriere 		return result;
338d64485e4SEtienne Carriere 
339ef9888dcSEtienne Carriere 	if (locked)
340ef9888dcSEtienne Carriere 		DMSG("BSEC write warning: OTP locked");
341d64485e4SEtienne Carriere 
3427dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3437dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3447dfc80abSGatien Chevallier 
345d64485e4SEtienne Carriere 	exceptions = bsec_lock();
346d64485e4SEtienne Carriere 
347d64485e4SEtienne Carriere 	io_write32(otp_data_base + (otp_id * sizeof(uint32_t)), value);
348d64485e4SEtienne Carriere 
349d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
350d64485e4SEtienne Carriere 
3511ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
352d64485e4SEtienne Carriere }
353d64485e4SEtienne Carriere 
3540c30f9eaSEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
355d64485e4SEtienne Carriere TEE_Result stm32_bsec_program_otp(uint32_t value, uint32_t otp_id)
356d64485e4SEtienne Carriere {
357d64485e4SEtienne Carriere 	TEE_Result result = 0;
358d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
359ef9888dcSEtienne Carriere 	uint64_t timeout_ref = 0;
360ef9888dcSEtienne Carriere 	bool locked = false;
361d64485e4SEtienne Carriere 
362ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
363ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sp_lock(otp_id, &locked);
364ef9888dcSEtienne Carriere 	if (result)
365ef9888dcSEtienne Carriere 		return result;
366d64485e4SEtienne Carriere 
367ef9888dcSEtienne Carriere 	if (locked)
368ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: OTP locked");
369d64485e4SEtienne Carriere 
370d64485e4SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_OTP_LOCK_OFF) & BIT(BSEC_LOCK_PROGRAM))
371ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: GPLOCK activated");
372d64485e4SEtienne Carriere 
3737dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3747dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3757dfc80abSGatien Chevallier 
376d64485e4SEtienne Carriere 	exceptions = bsec_lock();
377d64485e4SEtienne Carriere 
378d64485e4SEtienne Carriere 	result = power_up_safmem();
379d64485e4SEtienne Carriere 	if (result)
3807b05d514SEtienne Carriere 		goto out;
381d64485e4SEtienne Carriere 
382d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_WRDATA_OFF, value);
383d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_WRITE);
384d64485e4SEtienne Carriere 
385d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
386d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
3877dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
388d64485e4SEtienne Carriere 			break;
389d64485e4SEtienne Carriere 
3907dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
3918afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
3927dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
3938afb7c41SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
394d64485e4SEtienne Carriere 	else
3951ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
396d64485e4SEtienne Carriere 
397d64485e4SEtienne Carriere 	power_down_safmem();
398d64485e4SEtienne Carriere 
3997b05d514SEtienne Carriere out:
400d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
401d64485e4SEtienne Carriere 
402d64485e4SEtienne Carriere 	return result;
403d64485e4SEtienne Carriere }
404d64485e4SEtienne Carriere 
405d64485e4SEtienne Carriere TEE_Result stm32_bsec_permanent_lock_otp(uint32_t otp_id)
406d64485e4SEtienne Carriere {
407d64485e4SEtienne Carriere 	TEE_Result result = 0;
408d64485e4SEtienne Carriere 	uint32_t data = 0;
409d64485e4SEtienne Carriere 	uint32_t addr = 0;
410d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
411d64485e4SEtienne Carriere 	vaddr_t base = bsec_base();
412586eee81SEtienne Carriere 	uint64_t timeout_ref = 0;
413c6d2483aSGatien Chevallier 	uint32_t upper_base = otp_upper_base();
414d64485e4SEtienne Carriere 
415d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
416d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
417d64485e4SEtienne Carriere 
418c6d2483aSGatien Chevallier 	/*
419c6d2483aSGatien Chevallier 	 * 2 bits per words for lower OTPs: 2:1 Redundancy
420c6d2483aSGatien Chevallier 	 * 1 bit per word for upper OTPs : ECC support
421c6d2483aSGatien Chevallier 	 * e.g with 32 lower and 64 upper OTPs:
422c6d2483aSGatien Chevallier 	 * OTP word to be    ADDR[6:0]   WRDATA[31:0]
423c6d2483aSGatien Chevallier 	 *     locked
424c6d2483aSGatien Chevallier 	 *       0             0x00      0x0000 0003
425c6d2483aSGatien Chevallier 	 *       1             0x00      0x0000 000C
426c6d2483aSGatien Chevallier 	 *      ...             ...              ...
427c6d2483aSGatien Chevallier 	 *       7             0x00      0x0000 C000
428c6d2483aSGatien Chevallier 	 *       8             0x01      0x0000 0003
429c6d2483aSGatien Chevallier 	 *      ...             ...              ...
430c6d2483aSGatien Chevallier 	 *      31             0x03      0x0000 C000
431c6d2483aSGatien Chevallier 	 *      32             0x04      0x0000 0001
432c6d2483aSGatien Chevallier 	 *      33             0x04      0x0000 0002
433c6d2483aSGatien Chevallier 	 *      95             0x07      0x0000 8000
434c6d2483aSGatien Chevallier 	 */
435c6d2483aSGatien Chevallier 	if (otp_id < upper_base) {
436c6d2483aSGatien Chevallier 		addr = otp_id / 8U;
437c6d2483aSGatien Chevallier 		data = DATA_LOWER_OTP_PERLOCK_BIT << ((otp_id * 2U) & 0xF);
438d64485e4SEtienne Carriere 	} else {
439c6d2483aSGatien Chevallier 		addr = upper_base / 8U + (otp_id - upper_base) / 16U;
440c6d2483aSGatien Chevallier 		data = DATA_UPPER_OTP_PERLOCK_BIT << (otp_id & 0xF);
441d64485e4SEtienne Carriere 	}
442d64485e4SEtienne Carriere 
4437dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4447dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4457dfc80abSGatien Chevallier 
446d64485e4SEtienne Carriere 	exceptions = bsec_lock();
447d64485e4SEtienne Carriere 
448d64485e4SEtienne Carriere 	result = power_up_safmem();
449d64485e4SEtienne Carriere 	if (result)
4507b05d514SEtienne Carriere 		goto out;
451d64485e4SEtienne Carriere 
452d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_WRDATA_OFF, data);
453d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK);
454d64485e4SEtienne Carriere 
455d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
456d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
4577dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
458d64485e4SEtienne Carriere 			break;
459d64485e4SEtienne Carriere 
4607dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
4618afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
4627dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
463d64485e4SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
464d64485e4SEtienne Carriere 	else
4651ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, false /* not-disturbed */);
466d64485e4SEtienne Carriere 
467e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
468e090bb5aSGatien Chevallier 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_READ | BSEC_LOCK);
469e090bb5aSGatien Chevallier #endif
470e090bb5aSGatien Chevallier 
471d64485e4SEtienne Carriere 	power_down_safmem();
472d64485e4SEtienne Carriere 
4737b05d514SEtienne Carriere out:
474d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
475d64485e4SEtienne Carriere 
476d64485e4SEtienne Carriere 	return result;
477d64485e4SEtienne Carriere }
478*38df614fSLionel Debieve #endif /*CFG_STM32_BSEC_WRITE*/
479d64485e4SEtienne Carriere 
480d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_debug_conf(uint32_t value)
481d64485e4SEtienne Carriere {
482d64485e4SEtienne Carriere 	TEE_Result result = TEE_ERROR_GENERIC;
483d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
484d64485e4SEtienne Carriere 
48501a06793SPatrick Delaunay 	assert(!(value & ~BSEC_DEN_ALL_MSK));
48601a06793SPatrick Delaunay 
4877dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4887dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4897dfc80abSGatien Chevallier 
490d64485e4SEtienne Carriere 	exceptions = bsec_lock();
491d64485e4SEtienne Carriere 
49201a06793SPatrick Delaunay 	io_clrsetbits32(bsec_base() + BSEC_DEN_OFF, BSEC_DEN_ALL_MSK, value);
493d64485e4SEtienne Carriere 
49401a06793SPatrick Delaunay 	if (stm32_bsec_read_debug_conf() == value)
495d64485e4SEtienne Carriere 		result = TEE_SUCCESS;
496d64485e4SEtienne Carriere 
497d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
498d64485e4SEtienne Carriere 
499d64485e4SEtienne Carriere 	return result;
500d64485e4SEtienne Carriere }
501d64485e4SEtienne Carriere 
502d64485e4SEtienne Carriere uint32_t stm32_bsec_read_debug_conf(void)
503d64485e4SEtienne Carriere {
50401a06793SPatrick Delaunay 	return io_read32(bsec_base() + BSEC_DEN_OFF) & BSEC_DEN_ALL_MSK;
505d64485e4SEtienne Carriere }
506d64485e4SEtienne Carriere 
507ef9888dcSEtienne Carriere static TEE_Result set_bsec_lock(uint32_t otp_id, size_t lock_offset)
508d64485e4SEtienne Carriere {
509d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
510d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
511d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
512d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
513d64485e4SEtienne Carriere 
514ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
515ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
516d64485e4SEtienne Carriere 
5177dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5187dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5197dfc80abSGatien Chevallier 
520d64485e4SEtienne Carriere 	exceptions = bsec_lock();
521d64485e4SEtienne Carriere 
522ef9888dcSEtienne Carriere 	io_write32(lock_addr, otp_mask);
523d64485e4SEtienne Carriere 
524d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
525d64485e4SEtienne Carriere 
526ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
527d64485e4SEtienne Carriere }
528d64485e4SEtienne Carriere 
529ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sr_lock(uint32_t otp_id)
530d64485e4SEtienne Carriere {
531ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SRLOCK_OFF);
532d64485e4SEtienne Carriere }
533d64485e4SEtienne Carriere 
534ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sw_lock(uint32_t otp_id)
535d64485e4SEtienne Carriere {
536ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SWLOCK_OFF);
537d64485e4SEtienne Carriere }
538d64485e4SEtienne Carriere 
539ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sp_lock(uint32_t otp_id)
540d64485e4SEtienne Carriere {
541ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SPLOCK_OFF);
542d64485e4SEtienne Carriere }
543d64485e4SEtienne Carriere 
544ef9888dcSEtienne Carriere static TEE_Result read_bsec_lock(uint32_t otp_id, bool *locked,
545ef9888dcSEtienne Carriere 				 size_t lock_offset)
546d64485e4SEtienne Carriere {
547d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
548d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
549d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
550d64485e4SEtienne Carriere 
551ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
552ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
553ef9888dcSEtienne Carriere 
5547dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5557dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5567dfc80abSGatien Chevallier 
557ef9888dcSEtienne Carriere 	*locked = (io_read32(lock_addr) & otp_mask) != 0;
558ef9888dcSEtienne Carriere 
559ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
560d64485e4SEtienne Carriere }
561d64485e4SEtienne Carriere 
562ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sr_lock(uint32_t otp_id, bool *locked)
563d64485e4SEtienne Carriere {
564ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SRLOCK_OFF);
565d64485e4SEtienne Carriere }
566d64485e4SEtienne Carriere 
567ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sw_lock(uint32_t otp_id, bool *locked)
568d64485e4SEtienne Carriere {
569ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SWLOCK_OFF);
570d64485e4SEtienne Carriere }
571d64485e4SEtienne Carriere 
572ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sp_lock(uint32_t otp_id, bool *locked)
573d64485e4SEtienne Carriere {
574ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SPLOCK_OFF);
575d64485e4SEtienne Carriere }
576d64485e4SEtienne Carriere 
577ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_permanent_lock(uint32_t otp_id, bool *locked)
578d64485e4SEtienne Carriere {
579ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_WRLOCK_OFF);
580d64485e4SEtienne Carriere }
581d64485e4SEtienne Carriere 
582890703c3SEtienne Carriere static size_t nsec_access_array_size(void)
583890703c3SEtienne Carriere {
584586eee81SEtienne Carriere 	size_t upper_count = otp_max_id() - otp_upper_base() + 1;
585890703c3SEtienne Carriere 
5868396f62eSGatien Chevallier 	return ROUNDUP_DIV(upper_count, BSEC_BITS_PER_WORD);
587890703c3SEtienne Carriere }
588890703c3SEtienne Carriere 
589890703c3SEtienne Carriere static bool nsec_access_granted(unsigned int index)
590890703c3SEtienne Carriere {
591890703c3SEtienne Carriere 	uint32_t *array = bsec_dev.nsec_access;
592890703c3SEtienne Carriere 
593890703c3SEtienne Carriere 	return array &&
5948396f62eSGatien Chevallier 	       (index / BSEC_BITS_PER_WORD) < nsec_access_array_size() &&
5958396f62eSGatien Chevallier 	       array[index / BSEC_BITS_PER_WORD] &
5968396f62eSGatien Chevallier 	       BIT(index % BSEC_BITS_PER_WORD);
597890703c3SEtienne Carriere }
598890703c3SEtienne Carriere 
5997dfc80abSGatien Chevallier bool stm32_bsec_can_access_otp(uint32_t otp_id)
6007dfc80abSGatien Chevallier {
6017dfc80abSGatien Chevallier 	return (otp_id <= otp_max_id()) && !state_is_invalid_mode();
6027dfc80abSGatien Chevallier }
6037dfc80abSGatien Chevallier 
604d64485e4SEtienne Carriere bool stm32_bsec_nsec_can_access_otp(uint32_t otp_id)
605d64485e4SEtienne Carriere {
606586eee81SEtienne Carriere 	return otp_id < otp_upper_base() ||
607586eee81SEtienne Carriere 	       nsec_access_granted(otp_id - otp_upper_base());
608d64485e4SEtienne Carriere }
609d64485e4SEtienne Carriere 
61093114f2eSGatien Chevallier struct nvmem_layout {
61193114f2eSGatien Chevallier 	char *name;
61293114f2eSGatien Chevallier 	uint32_t otp_id;
61393114f2eSGatien Chevallier 	size_t bit_len;
61493114f2eSGatien Chevallier };
61593114f2eSGatien Chevallier 
61693114f2eSGatien Chevallier static struct nvmem_layout *nvmem_layout;
61793114f2eSGatien Chevallier static size_t nvmem_layout_count;
61893114f2eSGatien Chevallier 
61993114f2eSGatien Chevallier TEE_Result stm32_bsec_find_otp_in_nvmem_layout(const char *name,
62093114f2eSGatien Chevallier 					       uint32_t *otp_id,
62193114f2eSGatien Chevallier 					       size_t *otp_bit_len)
62293114f2eSGatien Chevallier {
62393114f2eSGatien Chevallier 	size_t i = 0;
62493114f2eSGatien Chevallier 
62593114f2eSGatien Chevallier 	if (!name)
62693114f2eSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
62793114f2eSGatien Chevallier 
62893114f2eSGatien Chevallier 	for (i = 0; i < nvmem_layout_count; i++) {
62993114f2eSGatien Chevallier 		if (!nvmem_layout[i].name || strcmp(name, nvmem_layout[i].name))
63093114f2eSGatien Chevallier 			continue;
63193114f2eSGatien Chevallier 
63293114f2eSGatien Chevallier 		if (otp_id)
63393114f2eSGatien Chevallier 			*otp_id = nvmem_layout[i].otp_id;
63493114f2eSGatien Chevallier 
63593114f2eSGatien Chevallier 		if (otp_bit_len)
63693114f2eSGatien Chevallier 			*otp_bit_len = nvmem_layout[i].bit_len;
63793114f2eSGatien Chevallier 
63893114f2eSGatien Chevallier 		DMSG("nvmem %s = %zu: %"PRId32" %zu", name, i,
63993114f2eSGatien Chevallier 		     nvmem_layout[i].otp_id, nvmem_layout[i].bit_len);
64093114f2eSGatien Chevallier 
64193114f2eSGatien Chevallier 		return TEE_SUCCESS;
64293114f2eSGatien Chevallier 	}
64393114f2eSGatien Chevallier 
64493114f2eSGatien Chevallier 	DMSG("nvmem %s failed", name);
64593114f2eSGatien Chevallier 
64693114f2eSGatien Chevallier 	return TEE_ERROR_ITEM_NOT_FOUND;
6474fb18124SPatrick Delaunay }
6487dfc80abSGatien Chevallier 
649ac16eac3SJose Quaresma TEE_Result stm32_bsec_get_state(enum stm32_bsec_sec_state *state)
6507dfc80abSGatien Chevallier {
6517dfc80abSGatien Chevallier 	if (!state)
6527dfc80abSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
6537dfc80abSGatien Chevallier 
6547dfc80abSGatien Chevallier 	if (state_is_invalid_mode() || !state_is_secured_mode()) {
6557dfc80abSGatien Chevallier 		*state = BSEC_STATE_INVALID;
6567dfc80abSGatien Chevallier 	} else {
6577dfc80abSGatien Chevallier 		if (state_is_closed_mode())
6587dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_CLOSED;
6597dfc80abSGatien Chevallier 		else
6607dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_OPEN;
6617dfc80abSGatien Chevallier 	}
6627dfc80abSGatien Chevallier 
6637dfc80abSGatien Chevallier 	return TEE_SUCCESS;
66493114f2eSGatien Chevallier }
66593114f2eSGatien Chevallier 
666890703c3SEtienne Carriere static void enable_nsec_access(unsigned int otp_id)
667890703c3SEtienne Carriere {
6688396f62eSGatien Chevallier 	unsigned int idx = (otp_id - otp_upper_base()) / BSEC_BITS_PER_WORD;
669890703c3SEtienne Carriere 
670586eee81SEtienne Carriere 	if (otp_id < otp_upper_base())
671890703c3SEtienne Carriere 		return;
672890703c3SEtienne Carriere 
673890703c3SEtienne Carriere 	if (otp_id > otp_max_id() || stm32_bsec_shadow_register(otp_id))
674890703c3SEtienne Carriere 		panic();
675890703c3SEtienne Carriere 
6768396f62eSGatien Chevallier 	bsec_dev.nsec_access[idx] |= BIT(otp_id % BSEC_BITS_PER_WORD);
677890703c3SEtienne Carriere }
678890703c3SEtienne Carriere 
679890703c3SEtienne Carriere static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
680890703c3SEtienne Carriere {
681890703c3SEtienne Carriere 	int bsec_subnode = 0;
682890703c3SEtienne Carriere 
683890703c3SEtienne Carriere 	bsec_dev.nsec_access = calloc(nsec_access_array_size(),
684890703c3SEtienne Carriere 				      sizeof(*bsec_dev.nsec_access));
685890703c3SEtienne Carriere 	if (!bsec_dev.nsec_access)
686890703c3SEtienne Carriere 		panic();
687890703c3SEtienne Carriere 
688890703c3SEtienne Carriere 	fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
68927a02b1eSGatien Chevallier 		unsigned int reg_offset = 0;
69027a02b1eSGatien Chevallier 		unsigned int reg_size = 0;
691890703c3SEtienne Carriere 		unsigned int otp_id = 0;
692890703c3SEtienne Carriere 		unsigned int i = 0;
693890703c3SEtienne Carriere 		size_t size = 0;
694890703c3SEtienne Carriere 
695f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, bsec_subnode);
696f354a5d8SGatien Chevallier 		reg_size = fdt_reg_size(fdt, bsec_subnode);
697890703c3SEtienne Carriere 
69827a02b1eSGatien Chevallier 		assert(reg_offset != DT_INFO_INVALID_REG &&
69927a02b1eSGatien Chevallier 		       reg_size != DT_INFO_INVALID_REG_SIZE);
700890703c3SEtienne Carriere 
70127a02b1eSGatien Chevallier 		otp_id = reg_offset / sizeof(uint32_t);
702890703c3SEtienne Carriere 
703890703c3SEtienne Carriere 		if (otp_id < STM32MP1_UPPER_OTP_START) {
70427a02b1eSGatien Chevallier 			unsigned int otp_end =
70527a02b1eSGatien Chevallier 				ROUNDUP_DIV(reg_offset + reg_size,
70627a02b1eSGatien Chevallier 					    sizeof(uint32_t));
707890703c3SEtienne Carriere 
708890703c3SEtienne Carriere 			if (otp_end > STM32MP1_UPPER_OTP_START) {
709890703c3SEtienne Carriere 				/*
710890703c3SEtienne Carriere 				 * OTP crosses Lower/Upper boundary, consider
711890703c3SEtienne Carriere 				 * only the upper part.
712890703c3SEtienne Carriere 				 */
713890703c3SEtienne Carriere 				otp_id = STM32MP1_UPPER_OTP_START;
71427a02b1eSGatien Chevallier 				reg_size -= (STM32MP1_UPPER_OTP_START *
71527a02b1eSGatien Chevallier 					     sizeof(uint32_t)) - reg_offset;
71627a02b1eSGatien Chevallier 				reg_offset = STM32MP1_UPPER_OTP_START *
717890703c3SEtienne Carriere 					     sizeof(uint32_t);
718890703c3SEtienne Carriere 
719890703c3SEtienne Carriere 				DMSG("OTP crosses Lower/Upper boundary");
720890703c3SEtienne Carriere 			} else {
721890703c3SEtienne Carriere 				continue;
722890703c3SEtienne Carriere 			}
723890703c3SEtienne Carriere 		}
724890703c3SEtienne Carriere 
7250ec45216SGatien Chevallier 		/* Handle different kinds of non-secure accesses */
7260ec45216SGatien Chevallier 		if (fdt_getprop(fdt, bsec_subnode,
7270ec45216SGatien Chevallier 				"st,non-secure-otp-provisioning", NULL)) {
7280ec45216SGatien Chevallier 			bool locked = false;
7290ec45216SGatien Chevallier 			bool locked_2 = false;
7300ec45216SGatien Chevallier 
7310ec45216SGatien Chevallier 			/* Check if write of OTP is locked */
7320ec45216SGatien Chevallier 			if (stm32_bsec_read_permanent_lock(otp_id, &locked))
7330ec45216SGatien Chevallier 				panic("Cannot read permanent lock");
7340ec45216SGatien Chevallier 
7350ec45216SGatien Chevallier 			/*
7360ec45216SGatien Chevallier 			 * Check if fuses of the subnode
7370ec45216SGatien Chevallier 			 * have the same lock status
7380ec45216SGatien Chevallier 			 */
7390ec45216SGatien Chevallier 			for (i = 1; i < (reg_size / sizeof(uint32_t)); i++) {
7400ec45216SGatien Chevallier 				if (stm32_bsec_read_permanent_lock(otp_id + i,
7410ec45216SGatien Chevallier 								   &locked_2))
7420ec45216SGatien Chevallier 					panic("Cannot read permanent lock");
7430ec45216SGatien Chevallier 
7440ec45216SGatien Chevallier 				if (locked != locked_2) {
7450ec45216SGatien Chevallier 					EMSG("Inconsistent status OTP ID %u",
7460ec45216SGatien Chevallier 					     otp_id + i);
7470ec45216SGatien Chevallier 					locked = true;
7480ec45216SGatien Chevallier 				}
7490ec45216SGatien Chevallier 			}
7500ec45216SGatien Chevallier 
7510ec45216SGatien Chevallier 			if (locked) {
7520ec45216SGatien Chevallier 				DMSG("BSEC: OTP locked");
753890703c3SEtienne Carriere 				continue;
7540ec45216SGatien Chevallier 			}
7550ec45216SGatien Chevallier 		} else if (!fdt_getprop(fdt, bsec_subnode, "st,non-secure-otp",
7560ec45216SGatien Chevallier 					NULL)) {
7570ec45216SGatien Chevallier 			continue;
7580ec45216SGatien Chevallier 		}
759890703c3SEtienne Carriere 
76027a02b1eSGatien Chevallier 		if ((reg_offset % sizeof(uint32_t)) ||
76127a02b1eSGatien Chevallier 		    (reg_size % sizeof(uint32_t)))
762890703c3SEtienne Carriere 			panic("Unaligned non-secure OTP");
763890703c3SEtienne Carriere 
76427a02b1eSGatien Chevallier 		size = reg_size / sizeof(uint32_t);
765890703c3SEtienne Carriere 
7667dfc80abSGatien Chevallier 		if (otp_id + size > OTP_MAX_SIZE)
767890703c3SEtienne Carriere 			panic("OTP range oversized");
768890703c3SEtienne Carriere 
769890703c3SEtienne Carriere 		for (i = otp_id; i < otp_id + size; i++)
770890703c3SEtienne Carriere 			enable_nsec_access(i);
771890703c3SEtienne Carriere 	}
772890703c3SEtienne Carriere }
773890703c3SEtienne Carriere 
77493114f2eSGatien Chevallier static void save_dt_nvmem_layout(void *fdt, int bsec_node)
77593114f2eSGatien Chevallier {
77693114f2eSGatien Chevallier 	int cell_max = 0;
77793114f2eSGatien Chevallier 	int cell_cnt = 0;
77893114f2eSGatien Chevallier 	int node = 0;
77993114f2eSGatien Chevallier 
78093114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node)
78193114f2eSGatien Chevallier 		cell_max++;
78293114f2eSGatien Chevallier 	if (!cell_max)
78393114f2eSGatien Chevallier 		return;
78493114f2eSGatien Chevallier 
78593114f2eSGatien Chevallier 	nvmem_layout = calloc(cell_max, sizeof(*nvmem_layout));
78693114f2eSGatien Chevallier 	if (!nvmem_layout)
78793114f2eSGatien Chevallier 		panic();
78893114f2eSGatien Chevallier 
78993114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node) {
79093114f2eSGatien Chevallier 		unsigned int reg_offset = 0;
79193114f2eSGatien Chevallier 		unsigned int reg_length = 0;
79293114f2eSGatien Chevallier 		const char *string = NULL;
79393114f2eSGatien Chevallier 		const char *s = NULL;
79493114f2eSGatien Chevallier 		int len = 0;
79593114f2eSGatien Chevallier 		struct nvmem_layout *layout_cell = &nvmem_layout[cell_cnt];
79693114f2eSGatien Chevallier 
79793114f2eSGatien Chevallier 		string = fdt_get_name(fdt, node, &len);
79893114f2eSGatien Chevallier 		if (!string || !len)
79993114f2eSGatien Chevallier 			continue;
80093114f2eSGatien Chevallier 
801f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, node);
802f354a5d8SGatien Chevallier 		reg_length = fdt_reg_size(fdt, node);
80393114f2eSGatien Chevallier 
80493114f2eSGatien Chevallier 		if (reg_offset == DT_INFO_INVALID_REG ||
80593114f2eSGatien Chevallier 		    reg_length == DT_INFO_INVALID_REG_SIZE) {
80693114f2eSGatien Chevallier 			DMSG("Malformed nvmem %s: ignored", string);
80793114f2eSGatien Chevallier 			continue;
80893114f2eSGatien Chevallier 		}
80993114f2eSGatien Chevallier 
81093114f2eSGatien Chevallier 		if (reg_offset % sizeof(uint32_t)) {
81193114f2eSGatien Chevallier 			DMSG("Misaligned nvmem %s: ignored", string);
81293114f2eSGatien Chevallier 			continue;
81393114f2eSGatien Chevallier 		}
81493114f2eSGatien Chevallier 		layout_cell->otp_id = reg_offset / sizeof(uint32_t);
81593114f2eSGatien Chevallier 		layout_cell->bit_len = reg_length * CHAR_BIT;
81693114f2eSGatien Chevallier 
81793114f2eSGatien Chevallier 		s = strchr(string, '@');
81893114f2eSGatien Chevallier 		if (s)
81993114f2eSGatien Chevallier 			len = s - string;
82093114f2eSGatien Chevallier 
82193114f2eSGatien Chevallier 		layout_cell->name = strndup(string, len);
82293114f2eSGatien Chevallier 		if (!layout_cell->name)
82393114f2eSGatien Chevallier 			panic();
82493114f2eSGatien Chevallier 		cell_cnt++;
82593114f2eSGatien Chevallier 		DMSG("nvmem[%d] = %s %"PRId32" %zu", cell_cnt,
82693114f2eSGatien Chevallier 		     layout_cell->name, layout_cell->otp_id,
82793114f2eSGatien Chevallier 		     layout_cell->bit_len);
82893114f2eSGatien Chevallier 	}
82993114f2eSGatien Chevallier 
83093114f2eSGatien Chevallier 	if (cell_cnt != cell_max) {
83193114f2eSGatien Chevallier 		nvmem_layout = realloc(nvmem_layout,
83293114f2eSGatien Chevallier 				       cell_cnt * sizeof(*nvmem_layout));
83393114f2eSGatien Chevallier 		if (!nvmem_layout)
83493114f2eSGatien Chevallier 			panic();
83593114f2eSGatien Chevallier 	}
83693114f2eSGatien Chevallier 
83793114f2eSGatien Chevallier 	nvmem_layout_count = cell_cnt;
83893114f2eSGatien Chevallier }
83993114f2eSGatien Chevallier 
840890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
841890703c3SEtienne Carriere {
842890703c3SEtienne Carriere 	void *fdt = NULL;
843890703c3SEtienne Carriere 	int node = 0;
844890703c3SEtienne Carriere 	struct dt_node_info bsec_info = { };
845890703c3SEtienne Carriere 
846890703c3SEtienne Carriere 	fdt = get_embedded_dt();
847e090bb5aSGatien Chevallier 	node = fdt_node_offset_by_compatible(fdt, 0, DT_BSEC_COMPAT);
848890703c3SEtienne Carriere 	if (node < 0)
849890703c3SEtienne Carriere 		panic();
850890703c3SEtienne Carriere 
851f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &bsec_info, node);
852890703c3SEtienne Carriere 
853890703c3SEtienne Carriere 	if (bsec_info.reg != bsec_dev.base.pa ||
854890703c3SEtienne Carriere 	    !(bsec_info.status & DT_STATUS_OK_SEC))
855890703c3SEtienne Carriere 		panic();
856890703c3SEtienne Carriere 
857890703c3SEtienne Carriere 	bsec_dt_otp_nsec_access(fdt, node);
85893114f2eSGatien Chevallier 
85993114f2eSGatien Chevallier 	save_dt_nvmem_layout(fdt, node);
860890703c3SEtienne Carriere }
861890703c3SEtienne Carriere 
862d6df31b0SGatien Chevallier static TEE_Result bsec_pm(enum pm_op op, uint32_t pm_hint __unused,
863d6df31b0SGatien Chevallier 			  const struct pm_callback_handle *hdl __unused)
864d6df31b0SGatien Chevallier {
865d6df31b0SGatien Chevallier 	static uint32_t debug_conf;
866d6df31b0SGatien Chevallier 
867d6df31b0SGatien Chevallier 	assert(op == PM_OP_SUSPEND || op == PM_OP_RESUME);
868d6df31b0SGatien Chevallier 
869d6df31b0SGatien Chevallier 	if (op == PM_OP_SUSPEND)
870d6df31b0SGatien Chevallier 		debug_conf = stm32_bsec_read_debug_conf();
871d6df31b0SGatien Chevallier 	else
872d6df31b0SGatien Chevallier 		stm32_bsec_write_debug_conf(debug_conf);
873d6df31b0SGatien Chevallier 
874d6df31b0SGatien Chevallier 	return TEE_SUCCESS;
875d6df31b0SGatien Chevallier }
876d6df31b0SGatien Chevallier DECLARE_KEEP_PAGER(bsec_pm);
877d6df31b0SGatien Chevallier 
878d64485e4SEtienne Carriere static TEE_Result initialize_bsec(void)
879d64485e4SEtienne Carriere {
8807994d842SEtienne Carriere 	struct stm32_bsec_static_cfg cfg = { };
881d64485e4SEtienne Carriere 
882d64485e4SEtienne Carriere 	stm32mp_get_bsec_static_cfg(&cfg);
883d64485e4SEtienne Carriere 
884d64485e4SEtienne Carriere 	bsec_dev.base.pa = cfg.base;
885d64485e4SEtienne Carriere 	bsec_dev.upper_base = cfg.upper_start;
886d64485e4SEtienne Carriere 	bsec_dev.max_id = cfg.max_id;
887d64485e4SEtienne Carriere 
8887dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
8897dfc80abSGatien Chevallier 		panic();
8907dfc80abSGatien Chevallier 
891890703c3SEtienne Carriere 	initialize_bsec_from_dt();
892890703c3SEtienne Carriere 
893d6df31b0SGatien Chevallier 	register_pm_core_service_cb(bsec_pm, NULL, "stm32_bsec");
894d6df31b0SGatien Chevallier 
895d64485e4SEtienne Carriere 	return TEE_SUCCESS;
896d64485e4SEtienne Carriere }
897d64485e4SEtienne Carriere 
8988c8316dbSEtienne Carriere early_init(initialize_bsec);
899