xref: /optee_os/core/drivers/stm32_bsec.c (revision 10fb0d97186d3a8072299950ca3ba07f3c241a2a)
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 
97d64485e4SEtienne Carriere /*
98d64485e4SEtienne Carriere  * OTP Lock services definition
99d64485e4SEtienne Carriere  * Value must corresponding to the bit position in the register
100d64485e4SEtienne Carriere  */
1014bbd20f1SGatien Chevallier #define BSEC_LOCK_UPPER_OTP		U(0x00)
1024bbd20f1SGatien Chevallier #define BSEC_LOCK_DEBUG			U(0x02)
1034bbd20f1SGatien Chevallier #define BSEC_LOCK_PROGRAM		U(0x04)
104d64485e4SEtienne Carriere 
105d64485e4SEtienne Carriere /* Timeout when polling on status */
1064bbd20f1SGatien Chevallier #define BSEC_TIMEOUT_US			U(10000)
107890703c3SEtienne Carriere 
108d64485e4SEtienne Carriere struct bsec_dev {
109d64485e4SEtienne Carriere 	struct io_pa_va base;
110d64485e4SEtienne Carriere 	unsigned int upper_base;
111d64485e4SEtienne Carriere 	unsigned int max_id;
112890703c3SEtienne Carriere 	uint32_t *nsec_access;
113d64485e4SEtienne Carriere };
114d64485e4SEtienne Carriere 
115d64485e4SEtienne Carriere /* Only 1 instance of BSEC is expected per platform */
116d64485e4SEtienne Carriere static struct bsec_dev bsec_dev;
117d64485e4SEtienne Carriere 
118d64485e4SEtienne Carriere /* BSEC access protection */
119d64485e4SEtienne Carriere static unsigned int lock = SPINLOCK_UNLOCK;
120d64485e4SEtienne Carriere 
121d64485e4SEtienne Carriere static uint32_t bsec_lock(void)
122d64485e4SEtienne Carriere {
123d64485e4SEtienne Carriere 	return may_spin_lock(&lock);
124d64485e4SEtienne Carriere }
125d64485e4SEtienne Carriere 
126d64485e4SEtienne Carriere static void bsec_unlock(uint32_t exceptions)
127d64485e4SEtienne Carriere {
128d64485e4SEtienne Carriere 	may_spin_unlock(&lock, exceptions);
129d64485e4SEtienne Carriere }
130d64485e4SEtienne Carriere 
131d64485e4SEtienne Carriere static uint32_t otp_max_id(void)
132d64485e4SEtienne Carriere {
133d64485e4SEtienne Carriere 	return bsec_dev.max_id;
134d64485e4SEtienne Carriere }
135d64485e4SEtienne Carriere 
136586eee81SEtienne Carriere static uint32_t otp_upper_base(void)
137586eee81SEtienne Carriere {
138586eee81SEtienne Carriere 	return bsec_dev.upper_base;
139586eee81SEtienne Carriere }
140586eee81SEtienne Carriere 
141d64485e4SEtienne Carriere static uint32_t otp_bank_offset(uint32_t otp_id)
142d64485e4SEtienne Carriere {
143d64485e4SEtienne Carriere 	assert(otp_id <= otp_max_id());
144d64485e4SEtienne Carriere 
145d64485e4SEtienne Carriere 	return ((otp_id & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
146d64485e4SEtienne Carriere 		sizeof(uint32_t);
147d64485e4SEtienne Carriere }
148d64485e4SEtienne Carriere 
149d64485e4SEtienne Carriere static vaddr_t bsec_base(void)
150d64485e4SEtienne Carriere {
151c2e4eb43SAnton Rybakov 	return io_pa_or_va_secure(&bsec_dev.base, BSEC_IP_MAGIC_ID_OFF + 1);
152d64485e4SEtienne Carriere }
153d64485e4SEtienne Carriere 
154d64485e4SEtienne Carriere static uint32_t bsec_status(void)
155d64485e4SEtienne Carriere {
156d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_OTP_STATUS_OFF);
157d64485e4SEtienne Carriere }
158d64485e4SEtienne Carriere 
1597dfc80abSGatien Chevallier static bool state_is_invalid_mode(void)
1607dfc80abSGatien Chevallier {
1617dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_INVALID;
1627dfc80abSGatien Chevallier }
1637dfc80abSGatien Chevallier 
1647dfc80abSGatien Chevallier static bool state_is_secured_mode(void)
1657dfc80abSGatien Chevallier {
1667dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_SECURED;
1677dfc80abSGatien Chevallier }
1687dfc80abSGatien Chevallier 
1697dfc80abSGatien Chevallier static bool state_is_closed_mode(void)
1707dfc80abSGatien Chevallier {
171*10fb0d97SGatien Chevallier 	uint32_t otp_cfg = 0;
1727dfc80abSGatien Chevallier 	uint32_t close_mode = 0;
173*10fb0d97SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
1747dfc80abSGatien Chevallier 
175e090bb5aSGatien Chevallier 	if (IS_ENABLED(CFG_STM32MP13))
176e090bb5aSGatien Chevallier 		return bsec_status() & BSEC_MODE_CLOSED;
177e090bb5aSGatien Chevallier 
178*10fb0d97SGatien Chevallier 	res = stm32_bsec_find_otp_in_nvmem_layout("cfg0_otp", &otp_cfg, NULL);
179*10fb0d97SGatien Chevallier 	if (res)
180*10fb0d97SGatien Chevallier 		panic("CFG0 OTP not found");
181*10fb0d97SGatien Chevallier 
182*10fb0d97SGatien Chevallier 	if (stm32_bsec_read_otp(&close_mode, otp_cfg))
1837dfc80abSGatien Chevallier 		panic("Unable to read OTP");
1847dfc80abSGatien Chevallier 
1857dfc80abSGatien Chevallier 	return close_mode & CFG0_OTP_CLOSED_DEVICE;
1867dfc80abSGatien Chevallier }
1877dfc80abSGatien Chevallier 
1881ac4ea14SEtienne Carriere /*
1891ac4ea14SEtienne Carriere  * Check that BSEC interface does not report an error
1901ac4ea14SEtienne Carriere  * @otp_id : OTP number
1911ac4ea14SEtienne Carriere  * @check_disturbed: check only error (false) or all sources (true)
1921ac4ea14SEtienne Carriere  * Return a TEE_Result compliant value
1931ac4ea14SEtienne Carriere  */
1941ac4ea14SEtienne Carriere static TEE_Result check_no_error(uint32_t otp_id, bool check_disturbed)
195d64485e4SEtienne Carriere {
196d64485e4SEtienne Carriere 	uint32_t bit = BIT(otp_id & BSEC_OTP_MASK);
197d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
198d64485e4SEtienne Carriere 
1991ac4ea14SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_ERROR_OFF + bank) & bit)
200d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
201d64485e4SEtienne Carriere 
2021ac4ea14SEtienne Carriere 	if (check_disturbed &&
2031ac4ea14SEtienne Carriere 	    io_read32(bsec_base() + BSEC_DISTURBED_OFF + bank) & bit)
204d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
205d64485e4SEtienne Carriere 
206d64485e4SEtienne Carriere 	return TEE_SUCCESS;
207d64485e4SEtienne Carriere }
208d64485e4SEtienne Carriere 
209d64485e4SEtienne Carriere static TEE_Result power_up_safmem(void)
210d64485e4SEtienne Carriere {
211d64485e4SEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
212d64485e4SEtienne Carriere 
213d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP_MASK,
214d64485e4SEtienne Carriere 		  BSEC_CONF_POWER_UP_MASK);
215d64485e4SEtienne Carriere 
216d64485e4SEtienne Carriere 	/*
217d64485e4SEtienne Carriere 	 * If a timeout is detected, test the condition again to consider
218d64485e4SEtienne Carriere 	 * cases where timeout is due to the executing TEE thread rescheduling.
219d64485e4SEtienne Carriere 	 */
220d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2217dfc80abSGatien Chevallier 		if (bsec_status() & BSEC_MODE_PWR)
222d64485e4SEtienne Carriere 			break;
223d64485e4SEtienne Carriere 
2247dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_PWR)
225d64485e4SEtienne Carriere 		return TEE_SUCCESS;
226d64485e4SEtienne Carriere 
227d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
228d64485e4SEtienne Carriere }
229d64485e4SEtienne Carriere 
230d64485e4SEtienne Carriere static TEE_Result power_down_safmem(void)
231d64485e4SEtienne Carriere {
232d64485e4SEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
233d64485e4SEtienne Carriere 
234d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, 0, BSEC_CONF_POWER_UP_MASK);
235d64485e4SEtienne Carriere 
236d64485e4SEtienne Carriere 	/*
237d64485e4SEtienne Carriere 	 * If a timeout is detected, test the condition again to consider
238d64485e4SEtienne Carriere 	 * cases where timeout is due to the executing TEE thread rescheduling.
239d64485e4SEtienne Carriere 	 */
240d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2417dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_PWR))
242d64485e4SEtienne Carriere 			break;
243d64485e4SEtienne Carriere 
2447dfc80abSGatien Chevallier 	if (!(bsec_status() & BSEC_MODE_PWR))
245d64485e4SEtienne Carriere 		return TEE_SUCCESS;
246d64485e4SEtienne Carriere 
247d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
248d64485e4SEtienne Carriere }
249d64485e4SEtienne Carriere 
2500042538eSEtienne Carriere TEE_Result stm32_bsec_shadow_register(uint32_t otp_id)
251d64485e4SEtienne Carriere {
252d64485e4SEtienne Carriere 	TEE_Result result = 0;
253d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
254d64485e4SEtienne Carriere 	uint64_t timeout_ref = 0;
255ef9888dcSEtienne Carriere 	bool locked = false;
256d64485e4SEtienne Carriere 
257ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
258ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sr_lock(otp_id, &locked);
259ef9888dcSEtienne Carriere 	if (result)
260ef9888dcSEtienne Carriere 		return result;
261d64485e4SEtienne Carriere 
262ef9888dcSEtienne Carriere 	if (locked)
263ef9888dcSEtienne Carriere 		DMSG("BSEC shadow warning: OTP locked");
264d64485e4SEtienne Carriere 
2657dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
2667dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
2677dfc80abSGatien Chevallier 
268d64485e4SEtienne Carriere 	exceptions = bsec_lock();
269d64485e4SEtienne Carriere 
270d64485e4SEtienne Carriere 	result = power_up_safmem();
271d64485e4SEtienne Carriere 	if (result)
2727b05d514SEtienne Carriere 		goto out;
273d64485e4SEtienne Carriere 
274d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_READ);
275d64485e4SEtienne Carriere 
276d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
277d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2787dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
279d64485e4SEtienne Carriere 			break;
280d64485e4SEtienne Carriere 
2817dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
2828afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
283d64485e4SEtienne Carriere 	else
2841ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
285d64485e4SEtienne Carriere 
286d64485e4SEtienne Carriere 	power_down_safmem();
287d64485e4SEtienne Carriere 
2887b05d514SEtienne Carriere out:
289d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
290d64485e4SEtienne Carriere 
291d64485e4SEtienne Carriere 	return result;
292d64485e4SEtienne Carriere }
293d64485e4SEtienne Carriere 
294d64485e4SEtienne Carriere TEE_Result stm32_bsec_read_otp(uint32_t *value, uint32_t otp_id)
295d64485e4SEtienne Carriere {
296d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
297d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
298d64485e4SEtienne Carriere 
2997dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3007dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3017dfc80abSGatien Chevallier 
302d64485e4SEtienne Carriere 	*value = io_read32(bsec_base() + BSEC_OTP_DATA_OFF +
303d64485e4SEtienne Carriere 			   (otp_id * sizeof(uint32_t)));
304d64485e4SEtienne Carriere 
3051ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
306d64485e4SEtienne Carriere }
307d64485e4SEtienne Carriere 
308d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_read_otp(uint32_t *otp_value, uint32_t otp_id)
309d64485e4SEtienne Carriere {
310d64485e4SEtienne Carriere 	TEE_Result result = 0;
311d64485e4SEtienne Carriere 
312d64485e4SEtienne Carriere 	result = stm32_bsec_shadow_register(otp_id);
313d64485e4SEtienne Carriere 	if (result) {
314ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Shadowing Error %#"PRIx32, otp_id, result);
315d64485e4SEtienne Carriere 		return result;
316d64485e4SEtienne Carriere 	}
317d64485e4SEtienne Carriere 
318d64485e4SEtienne Carriere 	result = stm32_bsec_read_otp(otp_value, otp_id);
319d64485e4SEtienne Carriere 	if (result)
320ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Read Error %#"PRIx32, otp_id, result);
321d64485e4SEtienne Carriere 
322d64485e4SEtienne Carriere 	return result;
323d64485e4SEtienne Carriere }
324d64485e4SEtienne Carriere 
325d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_otp(uint32_t value, uint32_t otp_id)
326d64485e4SEtienne Carriere {
327d64485e4SEtienne Carriere 	TEE_Result result = 0;
328d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
329d64485e4SEtienne Carriere 	vaddr_t otp_data_base = bsec_base() + BSEC_OTP_DATA_OFF;
330ef9888dcSEtienne Carriere 	bool locked = false;
331d64485e4SEtienne Carriere 
332ef9888dcSEtienne Carriere 	/* Check if write of OTP is locked, informative only */
333ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sw_lock(otp_id, &locked);
334ef9888dcSEtienne Carriere 	if (result)
335ef9888dcSEtienne Carriere 		return result;
336d64485e4SEtienne Carriere 
337ef9888dcSEtienne Carriere 	if (locked)
338ef9888dcSEtienne Carriere 		DMSG("BSEC write warning: OTP locked");
339d64485e4SEtienne Carriere 
3407dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3417dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3427dfc80abSGatien Chevallier 
343d64485e4SEtienne Carriere 	exceptions = bsec_lock();
344d64485e4SEtienne Carriere 
345d64485e4SEtienne Carriere 	io_write32(otp_data_base + (otp_id * sizeof(uint32_t)), value);
346d64485e4SEtienne Carriere 
347d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
348d64485e4SEtienne Carriere 
3491ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
350d64485e4SEtienne Carriere }
351d64485e4SEtienne Carriere 
3520c30f9eaSEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
353d64485e4SEtienne Carriere TEE_Result stm32_bsec_program_otp(uint32_t value, uint32_t otp_id)
354d64485e4SEtienne Carriere {
355d64485e4SEtienne Carriere 	TEE_Result result = 0;
356d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
357ef9888dcSEtienne Carriere 	uint64_t timeout_ref = 0;
358ef9888dcSEtienne Carriere 	bool locked = false;
359d64485e4SEtienne Carriere 
360ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
361ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sp_lock(otp_id, &locked);
362ef9888dcSEtienne Carriere 	if (result)
363ef9888dcSEtienne Carriere 		return result;
364d64485e4SEtienne Carriere 
365ef9888dcSEtienne Carriere 	if (locked)
366ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: OTP locked");
367d64485e4SEtienne Carriere 
368d64485e4SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_OTP_LOCK_OFF) & BIT(BSEC_LOCK_PROGRAM))
369ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: GPLOCK activated");
370d64485e4SEtienne Carriere 
3717dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3727dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3737dfc80abSGatien Chevallier 
374d64485e4SEtienne Carriere 	exceptions = bsec_lock();
375d64485e4SEtienne Carriere 
376d64485e4SEtienne Carriere 	result = power_up_safmem();
377d64485e4SEtienne Carriere 	if (result)
3787b05d514SEtienne Carriere 		goto out;
379d64485e4SEtienne Carriere 
380d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_WRDATA_OFF, value);
381d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_WRITE);
382d64485e4SEtienne Carriere 
383d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
384d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
3857dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
386d64485e4SEtienne Carriere 			break;
387d64485e4SEtienne Carriere 
3887dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
3898afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
3907dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
3918afb7c41SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
392d64485e4SEtienne Carriere 	else
3931ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
394d64485e4SEtienne Carriere 
395d64485e4SEtienne Carriere 	power_down_safmem();
396d64485e4SEtienne Carriere 
3977b05d514SEtienne Carriere out:
398d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
399d64485e4SEtienne Carriere 
400d64485e4SEtienne Carriere 	return result;
401d64485e4SEtienne Carriere }
402301b3eb5SEtienne Carriere #endif /*CFG_STM32_BSEC_WRITE*/
403d64485e4SEtienne Carriere 
404d64485e4SEtienne Carriere TEE_Result stm32_bsec_permanent_lock_otp(uint32_t otp_id)
405d64485e4SEtienne Carriere {
406d64485e4SEtienne Carriere 	TEE_Result result = 0;
407d64485e4SEtienne Carriere 	uint32_t data = 0;
408d64485e4SEtienne Carriere 	uint32_t addr = 0;
409d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
410d64485e4SEtienne Carriere 	vaddr_t base = bsec_base();
411586eee81SEtienne Carriere 	uint64_t timeout_ref = 0;
412c6d2483aSGatien Chevallier 	uint32_t upper_base = otp_upper_base();
413d64485e4SEtienne Carriere 
414d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
415d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
416d64485e4SEtienne Carriere 
417c6d2483aSGatien Chevallier 	/*
418c6d2483aSGatien Chevallier 	 * 2 bits per words for lower OTPs: 2:1 Redundancy
419c6d2483aSGatien Chevallier 	 * 1 bit per word for upper OTPs : ECC support
420c6d2483aSGatien Chevallier 	 * e.g with 32 lower and 64 upper OTPs:
421c6d2483aSGatien Chevallier 	 * OTP word to be    ADDR[6:0]   WRDATA[31:0]
422c6d2483aSGatien Chevallier 	 *     locked
423c6d2483aSGatien Chevallier 	 *       0             0x00      0x0000 0003
424c6d2483aSGatien Chevallier 	 *       1             0x00      0x0000 000C
425c6d2483aSGatien Chevallier 	 *      ...             ...              ...
426c6d2483aSGatien Chevallier 	 *       7             0x00      0x0000 C000
427c6d2483aSGatien Chevallier 	 *       8             0x01      0x0000 0003
428c6d2483aSGatien Chevallier 	 *      ...             ...              ...
429c6d2483aSGatien Chevallier 	 *      31             0x03      0x0000 C000
430c6d2483aSGatien Chevallier 	 *      32             0x04      0x0000 0001
431c6d2483aSGatien Chevallier 	 *      33             0x04      0x0000 0002
432c6d2483aSGatien Chevallier 	 *      95             0x07      0x0000 8000
433c6d2483aSGatien Chevallier 	 */
434c6d2483aSGatien Chevallier 	if (otp_id < upper_base) {
435c6d2483aSGatien Chevallier 		addr = otp_id / 8U;
436c6d2483aSGatien Chevallier 		data = DATA_LOWER_OTP_PERLOCK_BIT << ((otp_id * 2U) & 0xF);
437d64485e4SEtienne Carriere 	} else {
438c6d2483aSGatien Chevallier 		addr = upper_base / 8U + (otp_id - upper_base) / 16U;
439c6d2483aSGatien Chevallier 		data = DATA_UPPER_OTP_PERLOCK_BIT << (otp_id & 0xF);
440d64485e4SEtienne Carriere 	}
441d64485e4SEtienne Carriere 
4427dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4437dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4447dfc80abSGatien Chevallier 
445d64485e4SEtienne Carriere 	exceptions = bsec_lock();
446d64485e4SEtienne Carriere 
447d64485e4SEtienne Carriere 	result = power_up_safmem();
448d64485e4SEtienne Carriere 	if (result)
4497b05d514SEtienne Carriere 		goto out;
450d64485e4SEtienne Carriere 
451d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_WRDATA_OFF, data);
452d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK);
453d64485e4SEtienne Carriere 
454d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
455d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
4567dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
457d64485e4SEtienne Carriere 			break;
458d64485e4SEtienne Carriere 
4597dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
4608afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
4617dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
462d64485e4SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
463d64485e4SEtienne Carriere 	else
4641ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, false /* not-disturbed */);
465d64485e4SEtienne Carriere 
466e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
467e090bb5aSGatien Chevallier 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_READ | BSEC_LOCK);
468e090bb5aSGatien Chevallier #endif
469e090bb5aSGatien Chevallier 
470d64485e4SEtienne Carriere 	power_down_safmem();
471d64485e4SEtienne Carriere 
4727b05d514SEtienne Carriere out:
473d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
474d64485e4SEtienne Carriere 
475d64485e4SEtienne Carriere 	return result;
476d64485e4SEtienne Carriere }
477d64485e4SEtienne Carriere 
478d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_debug_conf(uint32_t value)
479d64485e4SEtienne Carriere {
480d64485e4SEtienne Carriere 	TEE_Result result = TEE_ERROR_GENERIC;
481d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
482d64485e4SEtienne Carriere 
4837dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4847dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4857dfc80abSGatien Chevallier 
486d64485e4SEtienne Carriere 	exceptions = bsec_lock();
487d64485e4SEtienne Carriere 
488d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_DEN_OFF, value);
489d64485e4SEtienne Carriere 
4901ff52b85SGatien Chevallier 	if ((io_read32(bsec_base() + BSEC_DEN_OFF) ^ value) == 0U)
491d64485e4SEtienne Carriere 		result = TEE_SUCCESS;
492d64485e4SEtienne Carriere 
493d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
494d64485e4SEtienne Carriere 
495d64485e4SEtienne Carriere 	return result;
496d64485e4SEtienne Carriere }
497d64485e4SEtienne Carriere 
498d64485e4SEtienne Carriere uint32_t stm32_bsec_read_debug_conf(void)
499d64485e4SEtienne Carriere {
500d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_DEN_OFF);
501d64485e4SEtienne Carriere }
502d64485e4SEtienne Carriere 
503ef9888dcSEtienne Carriere static TEE_Result set_bsec_lock(uint32_t otp_id, size_t lock_offset)
504d64485e4SEtienne Carriere {
505d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
506d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
507d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
508d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
509d64485e4SEtienne Carriere 
510ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
511ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
512d64485e4SEtienne Carriere 
5137dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5147dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5157dfc80abSGatien Chevallier 
516d64485e4SEtienne Carriere 	exceptions = bsec_lock();
517d64485e4SEtienne Carriere 
518ef9888dcSEtienne Carriere 	io_write32(lock_addr, otp_mask);
519d64485e4SEtienne Carriere 
520d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
521d64485e4SEtienne Carriere 
522ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
523d64485e4SEtienne Carriere }
524d64485e4SEtienne Carriere 
525ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sr_lock(uint32_t otp_id)
526d64485e4SEtienne Carriere {
527ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SRLOCK_OFF);
528d64485e4SEtienne Carriere }
529d64485e4SEtienne Carriere 
530ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sw_lock(uint32_t otp_id)
531d64485e4SEtienne Carriere {
532ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SWLOCK_OFF);
533d64485e4SEtienne Carriere }
534d64485e4SEtienne Carriere 
535ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sp_lock(uint32_t otp_id)
536d64485e4SEtienne Carriere {
537ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SPLOCK_OFF);
538d64485e4SEtienne Carriere }
539d64485e4SEtienne Carriere 
540ef9888dcSEtienne Carriere static TEE_Result read_bsec_lock(uint32_t otp_id, bool *locked,
541ef9888dcSEtienne Carriere 				 size_t lock_offset)
542d64485e4SEtienne Carriere {
543d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
544d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
545d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
546d64485e4SEtienne Carriere 
547ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
548ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
549ef9888dcSEtienne Carriere 
5507dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5517dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5527dfc80abSGatien Chevallier 
553ef9888dcSEtienne Carriere 	*locked = (io_read32(lock_addr) & otp_mask) != 0;
554ef9888dcSEtienne Carriere 
555ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
556d64485e4SEtienne Carriere }
557d64485e4SEtienne Carriere 
558ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sr_lock(uint32_t otp_id, bool *locked)
559d64485e4SEtienne Carriere {
560ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SRLOCK_OFF);
561d64485e4SEtienne Carriere }
562d64485e4SEtienne Carriere 
563ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sw_lock(uint32_t otp_id, bool *locked)
564d64485e4SEtienne Carriere {
565ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SWLOCK_OFF);
566d64485e4SEtienne Carriere }
567d64485e4SEtienne Carriere 
568ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sp_lock(uint32_t otp_id, bool *locked)
569d64485e4SEtienne Carriere {
570ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SPLOCK_OFF);
571d64485e4SEtienne Carriere }
572d64485e4SEtienne Carriere 
573ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_permanent_lock(uint32_t otp_id, bool *locked)
574d64485e4SEtienne Carriere {
575ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_WRLOCK_OFF);
576d64485e4SEtienne Carriere }
577d64485e4SEtienne Carriere 
578890703c3SEtienne Carriere static size_t nsec_access_array_size(void)
579890703c3SEtienne Carriere {
580586eee81SEtienne Carriere 	size_t upper_count = otp_max_id() - otp_upper_base() + 1;
581890703c3SEtienne Carriere 
5828396f62eSGatien Chevallier 	return ROUNDUP_DIV(upper_count, BSEC_BITS_PER_WORD);
583890703c3SEtienne Carriere }
584890703c3SEtienne Carriere 
585890703c3SEtienne Carriere static bool nsec_access_granted(unsigned int index)
586890703c3SEtienne Carriere {
587890703c3SEtienne Carriere 	uint32_t *array = bsec_dev.nsec_access;
588890703c3SEtienne Carriere 
589890703c3SEtienne Carriere 	return array &&
5908396f62eSGatien Chevallier 	       (index / BSEC_BITS_PER_WORD) < nsec_access_array_size() &&
5918396f62eSGatien Chevallier 	       array[index / BSEC_BITS_PER_WORD] &
5928396f62eSGatien Chevallier 	       BIT(index % BSEC_BITS_PER_WORD);
593890703c3SEtienne Carriere }
594890703c3SEtienne Carriere 
5957dfc80abSGatien Chevallier bool stm32_bsec_can_access_otp(uint32_t otp_id)
5967dfc80abSGatien Chevallier {
5977dfc80abSGatien Chevallier 	return (otp_id <= otp_max_id()) && !state_is_invalid_mode();
5987dfc80abSGatien Chevallier }
5997dfc80abSGatien Chevallier 
600d64485e4SEtienne Carriere bool stm32_bsec_nsec_can_access_otp(uint32_t otp_id)
601d64485e4SEtienne Carriere {
602586eee81SEtienne Carriere 	return otp_id < otp_upper_base() ||
603586eee81SEtienne Carriere 	       nsec_access_granted(otp_id - otp_upper_base());
604d64485e4SEtienne Carriere }
605d64485e4SEtienne Carriere 
60693114f2eSGatien Chevallier struct nvmem_layout {
60793114f2eSGatien Chevallier 	char *name;
60893114f2eSGatien Chevallier 	uint32_t otp_id;
60993114f2eSGatien Chevallier 	size_t bit_len;
61093114f2eSGatien Chevallier };
61193114f2eSGatien Chevallier 
61293114f2eSGatien Chevallier static struct nvmem_layout *nvmem_layout;
61393114f2eSGatien Chevallier static size_t nvmem_layout_count;
61493114f2eSGatien Chevallier 
61593114f2eSGatien Chevallier TEE_Result stm32_bsec_find_otp_in_nvmem_layout(const char *name,
61693114f2eSGatien Chevallier 					       uint32_t *otp_id,
61793114f2eSGatien Chevallier 					       size_t *otp_bit_len)
61893114f2eSGatien Chevallier {
61993114f2eSGatien Chevallier 	size_t i = 0;
62093114f2eSGatien Chevallier 
62193114f2eSGatien Chevallier 	if (!name)
62293114f2eSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
62393114f2eSGatien Chevallier 
62493114f2eSGatien Chevallier 	for (i = 0; i < nvmem_layout_count; i++) {
62593114f2eSGatien Chevallier 		if (!nvmem_layout[i].name || strcmp(name, nvmem_layout[i].name))
62693114f2eSGatien Chevallier 			continue;
62793114f2eSGatien Chevallier 
62893114f2eSGatien Chevallier 		if (otp_id)
62993114f2eSGatien Chevallier 			*otp_id = nvmem_layout[i].otp_id;
63093114f2eSGatien Chevallier 
63193114f2eSGatien Chevallier 		if (otp_bit_len)
63293114f2eSGatien Chevallier 			*otp_bit_len = nvmem_layout[i].bit_len;
63393114f2eSGatien Chevallier 
63493114f2eSGatien Chevallier 		DMSG("nvmem %s = %zu: %"PRId32" %zu", name, i,
63593114f2eSGatien Chevallier 		     nvmem_layout[i].otp_id, nvmem_layout[i].bit_len);
63693114f2eSGatien Chevallier 
63793114f2eSGatien Chevallier 		return TEE_SUCCESS;
63893114f2eSGatien Chevallier 	}
63993114f2eSGatien Chevallier 
64093114f2eSGatien Chevallier 	DMSG("nvmem %s failed", name);
64193114f2eSGatien Chevallier 
64293114f2eSGatien Chevallier 	return TEE_ERROR_ITEM_NOT_FOUND;
6437dfc80abSGatien Chevallier };
6447dfc80abSGatien Chevallier 
6457dfc80abSGatien Chevallier TEE_Result stm32_bsec_get_state(uint32_t *state)
6467dfc80abSGatien Chevallier {
6477dfc80abSGatien Chevallier 	if (!state)
6487dfc80abSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
6497dfc80abSGatien Chevallier 
6507dfc80abSGatien Chevallier 	if (state_is_invalid_mode() || !state_is_secured_mode()) {
6517dfc80abSGatien Chevallier 		*state = BSEC_STATE_INVALID;
6527dfc80abSGatien Chevallier 	} else {
6537dfc80abSGatien Chevallier 		if (state_is_closed_mode())
6547dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_CLOSED;
6557dfc80abSGatien Chevallier 		else
6567dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_OPEN;
6577dfc80abSGatien Chevallier 	}
6587dfc80abSGatien Chevallier 
6597dfc80abSGatien Chevallier 	return TEE_SUCCESS;
66093114f2eSGatien Chevallier }
66193114f2eSGatien Chevallier 
662cfada897SAlexandru Gagniuc #ifdef CFG_EMBED_DTB
663890703c3SEtienne Carriere static void enable_nsec_access(unsigned int otp_id)
664890703c3SEtienne Carriere {
6658396f62eSGatien Chevallier 	unsigned int idx = (otp_id - otp_upper_base()) / BSEC_BITS_PER_WORD;
666890703c3SEtienne Carriere 
667586eee81SEtienne Carriere 	if (otp_id < otp_upper_base())
668890703c3SEtienne Carriere 		return;
669890703c3SEtienne Carriere 
670890703c3SEtienne Carriere 	if (otp_id > otp_max_id() || stm32_bsec_shadow_register(otp_id))
671890703c3SEtienne Carriere 		panic();
672890703c3SEtienne Carriere 
6738396f62eSGatien Chevallier 	bsec_dev.nsec_access[idx] |= BIT(otp_id % BSEC_BITS_PER_WORD);
674890703c3SEtienne Carriere }
675890703c3SEtienne Carriere 
676890703c3SEtienne Carriere static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
677890703c3SEtienne Carriere {
678890703c3SEtienne Carriere 	int bsec_subnode = 0;
679890703c3SEtienne Carriere 
680890703c3SEtienne Carriere 	bsec_dev.nsec_access = calloc(nsec_access_array_size(),
681890703c3SEtienne Carriere 				      sizeof(*bsec_dev.nsec_access));
682890703c3SEtienne Carriere 	if (!bsec_dev.nsec_access)
683890703c3SEtienne Carriere 		panic();
684890703c3SEtienne Carriere 
685890703c3SEtienne Carriere 	fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
68627a02b1eSGatien Chevallier 		unsigned int reg_offset = 0;
68727a02b1eSGatien Chevallier 		unsigned int reg_size = 0;
688890703c3SEtienne Carriere 		unsigned int otp_id = 0;
689890703c3SEtienne Carriere 		unsigned int i = 0;
690890703c3SEtienne Carriere 		size_t size = 0;
691890703c3SEtienne Carriere 
69227a02b1eSGatien Chevallier 		reg_offset = _fdt_reg_base_address(fdt, bsec_subnode);
69327a02b1eSGatien Chevallier 		reg_size = _fdt_reg_size(fdt, bsec_subnode);
694890703c3SEtienne Carriere 
69527a02b1eSGatien Chevallier 		assert(reg_offset != DT_INFO_INVALID_REG &&
69627a02b1eSGatien Chevallier 		       reg_size != DT_INFO_INVALID_REG_SIZE);
697890703c3SEtienne Carriere 
69827a02b1eSGatien Chevallier 		otp_id = reg_offset / sizeof(uint32_t);
699890703c3SEtienne Carriere 
700890703c3SEtienne Carriere 		if (otp_id < STM32MP1_UPPER_OTP_START) {
70127a02b1eSGatien Chevallier 			unsigned int otp_end =
70227a02b1eSGatien Chevallier 				ROUNDUP_DIV(reg_offset + reg_size,
70327a02b1eSGatien Chevallier 					    sizeof(uint32_t));
704890703c3SEtienne Carriere 
705890703c3SEtienne Carriere 			if (otp_end > STM32MP1_UPPER_OTP_START) {
706890703c3SEtienne Carriere 				/*
707890703c3SEtienne Carriere 				 * OTP crosses Lower/Upper boundary, consider
708890703c3SEtienne Carriere 				 * only the upper part.
709890703c3SEtienne Carriere 				 */
710890703c3SEtienne Carriere 				otp_id = STM32MP1_UPPER_OTP_START;
71127a02b1eSGatien Chevallier 				reg_size -= (STM32MP1_UPPER_OTP_START *
71227a02b1eSGatien Chevallier 					     sizeof(uint32_t)) - reg_offset;
71327a02b1eSGatien Chevallier 				reg_offset = STM32MP1_UPPER_OTP_START *
714890703c3SEtienne Carriere 					     sizeof(uint32_t);
715890703c3SEtienne Carriere 
716890703c3SEtienne Carriere 				DMSG("OTP crosses Lower/Upper boundary");
717890703c3SEtienne Carriere 			} else {
718890703c3SEtienne Carriere 				continue;
719890703c3SEtienne Carriere 			}
720890703c3SEtienne Carriere 		}
721890703c3SEtienne Carriere 
7220ec45216SGatien Chevallier 		/* Handle different kinds of non-secure accesses */
7230ec45216SGatien Chevallier 		if (fdt_getprop(fdt, bsec_subnode,
7240ec45216SGatien Chevallier 				"st,non-secure-otp-provisioning", NULL)) {
7250ec45216SGatien Chevallier 			bool locked = false;
7260ec45216SGatien Chevallier 			bool locked_2 = false;
7270ec45216SGatien Chevallier 
7280ec45216SGatien Chevallier 			/* Check if write of OTP is locked */
7290ec45216SGatien Chevallier 			if (stm32_bsec_read_permanent_lock(otp_id, &locked))
7300ec45216SGatien Chevallier 				panic("Cannot read permanent lock");
7310ec45216SGatien Chevallier 
7320ec45216SGatien Chevallier 			/*
7330ec45216SGatien Chevallier 			 * Check if fuses of the subnode
7340ec45216SGatien Chevallier 			 * have the same lock status
7350ec45216SGatien Chevallier 			 */
7360ec45216SGatien Chevallier 			for (i = 1; i < (reg_size / sizeof(uint32_t)); i++) {
7370ec45216SGatien Chevallier 				if (stm32_bsec_read_permanent_lock(otp_id + i,
7380ec45216SGatien Chevallier 								   &locked_2))
7390ec45216SGatien Chevallier 					panic("Cannot read permanent lock");
7400ec45216SGatien Chevallier 
7410ec45216SGatien Chevallier 				if (locked != locked_2) {
7420ec45216SGatien Chevallier 					EMSG("Inconsistent status OTP ID %u",
7430ec45216SGatien Chevallier 					     otp_id + i);
7440ec45216SGatien Chevallier 					locked = true;
7450ec45216SGatien Chevallier 				}
7460ec45216SGatien Chevallier 			}
7470ec45216SGatien Chevallier 
7480ec45216SGatien Chevallier 			if (locked) {
7490ec45216SGatien Chevallier 				DMSG("BSEC: OTP locked");
750890703c3SEtienne Carriere 				continue;
7510ec45216SGatien Chevallier 			}
7520ec45216SGatien Chevallier 		} else if (!fdt_getprop(fdt, bsec_subnode, "st,non-secure-otp",
7530ec45216SGatien Chevallier 					NULL)) {
7540ec45216SGatien Chevallier 			continue;
7550ec45216SGatien Chevallier 		}
756890703c3SEtienne Carriere 
75727a02b1eSGatien Chevallier 		if ((reg_offset % sizeof(uint32_t)) ||
75827a02b1eSGatien Chevallier 		    (reg_size % sizeof(uint32_t)))
759890703c3SEtienne Carriere 			panic("Unaligned non-secure OTP");
760890703c3SEtienne Carriere 
76127a02b1eSGatien Chevallier 		size = reg_size / sizeof(uint32_t);
762890703c3SEtienne Carriere 
7637dfc80abSGatien Chevallier 		if (otp_id + size > OTP_MAX_SIZE)
764890703c3SEtienne Carriere 			panic("OTP range oversized");
765890703c3SEtienne Carriere 
766890703c3SEtienne Carriere 		for (i = otp_id; i < otp_id + size; i++)
767890703c3SEtienne Carriere 			enable_nsec_access(i);
768890703c3SEtienne Carriere 	}
769890703c3SEtienne Carriere }
770890703c3SEtienne Carriere 
77193114f2eSGatien Chevallier static void save_dt_nvmem_layout(void *fdt, int bsec_node)
77293114f2eSGatien Chevallier {
77393114f2eSGatien Chevallier 	int cell_max = 0;
77493114f2eSGatien Chevallier 	int cell_cnt = 0;
77593114f2eSGatien Chevallier 	int node = 0;
77693114f2eSGatien Chevallier 
77793114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node)
77893114f2eSGatien Chevallier 		cell_max++;
77993114f2eSGatien Chevallier 	if (!cell_max)
78093114f2eSGatien Chevallier 		return;
78193114f2eSGatien Chevallier 
78293114f2eSGatien Chevallier 	nvmem_layout = calloc(cell_max, sizeof(*nvmem_layout));
78393114f2eSGatien Chevallier 	if (!nvmem_layout)
78493114f2eSGatien Chevallier 		panic();
78593114f2eSGatien Chevallier 
78693114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node) {
78793114f2eSGatien Chevallier 		unsigned int reg_offset = 0;
78893114f2eSGatien Chevallier 		unsigned int reg_length = 0;
78993114f2eSGatien Chevallier 		const char *string = NULL;
79093114f2eSGatien Chevallier 		const char *s = NULL;
79193114f2eSGatien Chevallier 		int len = 0;
79293114f2eSGatien Chevallier 		struct nvmem_layout *layout_cell = &nvmem_layout[cell_cnt];
79393114f2eSGatien Chevallier 
79493114f2eSGatien Chevallier 		string = fdt_get_name(fdt, node, &len);
79593114f2eSGatien Chevallier 		if (!string || !len)
79693114f2eSGatien Chevallier 			continue;
79793114f2eSGatien Chevallier 
79893114f2eSGatien Chevallier 		reg_offset = _fdt_reg_base_address(fdt, node);
79993114f2eSGatien Chevallier 		reg_length = _fdt_reg_size(fdt, node);
80093114f2eSGatien Chevallier 
80193114f2eSGatien Chevallier 		if (reg_offset == DT_INFO_INVALID_REG ||
80293114f2eSGatien Chevallier 		    reg_length == DT_INFO_INVALID_REG_SIZE) {
80393114f2eSGatien Chevallier 			DMSG("Malformed nvmem %s: ignored", string);
80493114f2eSGatien Chevallier 			continue;
80593114f2eSGatien Chevallier 		}
80693114f2eSGatien Chevallier 
80793114f2eSGatien Chevallier 		if (reg_offset % sizeof(uint32_t)) {
80893114f2eSGatien Chevallier 			DMSG("Misaligned nvmem %s: ignored", string);
80993114f2eSGatien Chevallier 			continue;
81093114f2eSGatien Chevallier 		}
81193114f2eSGatien Chevallier 		layout_cell->otp_id = reg_offset / sizeof(uint32_t);
81293114f2eSGatien Chevallier 		layout_cell->bit_len = reg_length * CHAR_BIT;
81393114f2eSGatien Chevallier 
81493114f2eSGatien Chevallier 		s = strchr(string, '@');
81593114f2eSGatien Chevallier 		if (s)
81693114f2eSGatien Chevallier 			len = s - string;
81793114f2eSGatien Chevallier 
81893114f2eSGatien Chevallier 		layout_cell->name = strndup(string, len);
81993114f2eSGatien Chevallier 		if (!layout_cell->name)
82093114f2eSGatien Chevallier 			panic();
82193114f2eSGatien Chevallier 		cell_cnt++;
82293114f2eSGatien Chevallier 		DMSG("nvmem[%d] = %s %"PRId32" %zu", cell_cnt,
82393114f2eSGatien Chevallier 		     layout_cell->name, layout_cell->otp_id,
82493114f2eSGatien Chevallier 		     layout_cell->bit_len);
82593114f2eSGatien Chevallier 	}
82693114f2eSGatien Chevallier 
82793114f2eSGatien Chevallier 	if (cell_cnt != cell_max) {
82893114f2eSGatien Chevallier 		nvmem_layout = realloc(nvmem_layout,
82993114f2eSGatien Chevallier 				       cell_cnt * sizeof(*nvmem_layout));
83093114f2eSGatien Chevallier 		if (!nvmem_layout)
83193114f2eSGatien Chevallier 			panic();
83293114f2eSGatien Chevallier 	}
83393114f2eSGatien Chevallier 
83493114f2eSGatien Chevallier 	nvmem_layout_count = cell_cnt;
83593114f2eSGatien Chevallier }
83693114f2eSGatien Chevallier 
837890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
838890703c3SEtienne Carriere {
839890703c3SEtienne Carriere 	void *fdt = NULL;
840890703c3SEtienne Carriere 	int node = 0;
841890703c3SEtienne Carriere 	struct dt_node_info bsec_info = { };
842890703c3SEtienne Carriere 
843890703c3SEtienne Carriere 	fdt = get_embedded_dt();
844e090bb5aSGatien Chevallier 	node = fdt_node_offset_by_compatible(fdt, 0, DT_BSEC_COMPAT);
845890703c3SEtienne Carriere 	if (node < 0)
846890703c3SEtienne Carriere 		panic();
847890703c3SEtienne Carriere 
848890703c3SEtienne Carriere 	_fdt_fill_device_info(fdt, &bsec_info, node);
849890703c3SEtienne Carriere 
850890703c3SEtienne Carriere 	if (bsec_info.reg != bsec_dev.base.pa ||
851890703c3SEtienne Carriere 	    !(bsec_info.status & DT_STATUS_OK_SEC))
852890703c3SEtienne Carriere 		panic();
853890703c3SEtienne Carriere 
854890703c3SEtienne Carriere 	bsec_dt_otp_nsec_access(fdt, node);
85593114f2eSGatien Chevallier 
85693114f2eSGatien Chevallier 	save_dt_nvmem_layout(fdt, node);
857890703c3SEtienne Carriere }
858890703c3SEtienne Carriere #else
859890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
860890703c3SEtienne Carriere {
861890703c3SEtienne Carriere }
862cfada897SAlexandru Gagniuc #endif /*CFG_EMBED_DTB*/
863890703c3SEtienne Carriere 
864d6df31b0SGatien Chevallier static TEE_Result bsec_pm(enum pm_op op, uint32_t pm_hint __unused,
865d6df31b0SGatien Chevallier 			  const struct pm_callback_handle *hdl __unused)
866d6df31b0SGatien Chevallier {
867d6df31b0SGatien Chevallier 	static uint32_t debug_conf;
868d6df31b0SGatien Chevallier 
869d6df31b0SGatien Chevallier 	assert(op == PM_OP_SUSPEND || op == PM_OP_RESUME);
870d6df31b0SGatien Chevallier 
871d6df31b0SGatien Chevallier 	if (op == PM_OP_SUSPEND)
872d6df31b0SGatien Chevallier 		debug_conf = stm32_bsec_read_debug_conf();
873d6df31b0SGatien Chevallier 	else
874d6df31b0SGatien Chevallier 		stm32_bsec_write_debug_conf(debug_conf);
875d6df31b0SGatien Chevallier 
876d6df31b0SGatien Chevallier 	return TEE_SUCCESS;
877d6df31b0SGatien Chevallier }
878d6df31b0SGatien Chevallier DECLARE_KEEP_PAGER(bsec_pm);
879d6df31b0SGatien Chevallier 
880d64485e4SEtienne Carriere static TEE_Result initialize_bsec(void)
881d64485e4SEtienne Carriere {
8827994d842SEtienne Carriere 	struct stm32_bsec_static_cfg cfg = { };
883d64485e4SEtienne Carriere 
884d64485e4SEtienne Carriere 	stm32mp_get_bsec_static_cfg(&cfg);
885d64485e4SEtienne Carriere 
886d64485e4SEtienne Carriere 	bsec_dev.base.pa = cfg.base;
887d64485e4SEtienne Carriere 	bsec_dev.upper_base = cfg.upper_start;
888d64485e4SEtienne Carriere 	bsec_dev.max_id = cfg.max_id;
889d64485e4SEtienne Carriere 
8907dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
8917dfc80abSGatien Chevallier 		panic();
8927dfc80abSGatien Chevallier 
893890703c3SEtienne Carriere 	if (IS_ENABLED(CFG_EMBED_DTB))
894890703c3SEtienne Carriere 		initialize_bsec_from_dt();
895890703c3SEtienne Carriere 
896d6df31b0SGatien Chevallier 	register_pm_core_service_cb(bsec_pm, NULL, "stm32_bsec");
897d6df31b0SGatien Chevallier 
898d64485e4SEtienne Carriere 	return TEE_SUCCESS;
899d64485e4SEtienne Carriere }
900d64485e4SEtienne Carriere 
9018c8316dbSEtienne Carriere early_init(initialize_bsec);
902