xref: /optee_os/core/drivers/stm32_bsec.c (revision 9a54d4848dae866aa3f1e79fd4203dfda3c30cd9)
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 {
17110fb0d97SGatien Chevallier 	uint32_t otp_cfg = 0;
1727dfc80abSGatien Chevallier 	uint32_t close_mode = 0;
17310fb0d97SGatien 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 
17810fb0d97SGatien Chevallier 	res = stm32_bsec_find_otp_in_nvmem_layout("cfg0_otp", &otp_cfg, NULL);
17910fb0d97SGatien Chevallier 	if (res)
18010fb0d97SGatien Chevallier 		panic("CFG0 OTP not found");
18110fb0d97SGatien Chevallier 
18210fb0d97SGatien 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 {
211*9a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
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 
216*9a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
217d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2187dfc80abSGatien Chevallier 		if (bsec_status() & BSEC_MODE_PWR)
219d64485e4SEtienne Carriere 			break;
220d64485e4SEtienne Carriere 
2217dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_PWR)
222d64485e4SEtienne Carriere 		return TEE_SUCCESS;
223d64485e4SEtienne Carriere 
224d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
225d64485e4SEtienne Carriere }
226d64485e4SEtienne Carriere 
227d64485e4SEtienne Carriere static TEE_Result power_down_safmem(void)
228d64485e4SEtienne Carriere {
229*9a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
230d64485e4SEtienne Carriere 
231d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, 0, BSEC_CONF_POWER_UP_MASK);
232d64485e4SEtienne Carriere 
233*9a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
234d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2357dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_PWR))
236d64485e4SEtienne Carriere 			break;
237d64485e4SEtienne Carriere 
2387dfc80abSGatien Chevallier 	if (!(bsec_status() & BSEC_MODE_PWR))
239d64485e4SEtienne Carriere 		return TEE_SUCCESS;
240d64485e4SEtienne Carriere 
241d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
242d64485e4SEtienne Carriere }
243d64485e4SEtienne Carriere 
2440042538eSEtienne Carriere TEE_Result stm32_bsec_shadow_register(uint32_t otp_id)
245d64485e4SEtienne Carriere {
246d64485e4SEtienne Carriere 	TEE_Result result = 0;
247d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
248d64485e4SEtienne Carriere 	uint64_t timeout_ref = 0;
249ef9888dcSEtienne Carriere 	bool locked = false;
250d64485e4SEtienne Carriere 
251ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
252ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sr_lock(otp_id, &locked);
253ef9888dcSEtienne Carriere 	if (result)
254ef9888dcSEtienne Carriere 		return result;
255d64485e4SEtienne Carriere 
256ef9888dcSEtienne Carriere 	if (locked)
257ef9888dcSEtienne Carriere 		DMSG("BSEC shadow warning: OTP locked");
258d64485e4SEtienne Carriere 
2597dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
2607dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
2617dfc80abSGatien Chevallier 
262d64485e4SEtienne Carriere 	exceptions = bsec_lock();
263d64485e4SEtienne Carriere 
264d64485e4SEtienne Carriere 	result = power_up_safmem();
265d64485e4SEtienne Carriere 	if (result)
2667b05d514SEtienne Carriere 		goto out;
267d64485e4SEtienne Carriere 
268d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_READ);
269d64485e4SEtienne Carriere 
270d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
271d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2727dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
273d64485e4SEtienne Carriere 			break;
274d64485e4SEtienne Carriere 
2757dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
2768afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
277d64485e4SEtienne Carriere 	else
2781ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
279d64485e4SEtienne Carriere 
280d64485e4SEtienne Carriere 	power_down_safmem();
281d64485e4SEtienne Carriere 
2827b05d514SEtienne Carriere out:
283d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
284d64485e4SEtienne Carriere 
285d64485e4SEtienne Carriere 	return result;
286d64485e4SEtienne Carriere }
287d64485e4SEtienne Carriere 
288d64485e4SEtienne Carriere TEE_Result stm32_bsec_read_otp(uint32_t *value, uint32_t otp_id)
289d64485e4SEtienne Carriere {
290d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
291d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
292d64485e4SEtienne Carriere 
2937dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
2947dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
2957dfc80abSGatien Chevallier 
296d64485e4SEtienne Carriere 	*value = io_read32(bsec_base() + BSEC_OTP_DATA_OFF +
297d64485e4SEtienne Carriere 			   (otp_id * sizeof(uint32_t)));
298d64485e4SEtienne Carriere 
2991ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
300d64485e4SEtienne Carriere }
301d64485e4SEtienne Carriere 
302d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_read_otp(uint32_t *otp_value, uint32_t otp_id)
303d64485e4SEtienne Carriere {
304d64485e4SEtienne Carriere 	TEE_Result result = 0;
305d64485e4SEtienne Carriere 
306d64485e4SEtienne Carriere 	result = stm32_bsec_shadow_register(otp_id);
307d64485e4SEtienne Carriere 	if (result) {
308ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Shadowing Error %#"PRIx32, otp_id, result);
309d64485e4SEtienne Carriere 		return result;
310d64485e4SEtienne Carriere 	}
311d64485e4SEtienne Carriere 
312d64485e4SEtienne Carriere 	result = stm32_bsec_read_otp(otp_value, otp_id);
313d64485e4SEtienne Carriere 	if (result)
314ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Read Error %#"PRIx32, otp_id, result);
315d64485e4SEtienne Carriere 
316d64485e4SEtienne Carriere 	return result;
317d64485e4SEtienne Carriere }
318d64485e4SEtienne Carriere 
319d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_otp(uint32_t value, uint32_t otp_id)
320d64485e4SEtienne Carriere {
321d64485e4SEtienne Carriere 	TEE_Result result = 0;
322d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
323d64485e4SEtienne Carriere 	vaddr_t otp_data_base = bsec_base() + BSEC_OTP_DATA_OFF;
324ef9888dcSEtienne Carriere 	bool locked = false;
325d64485e4SEtienne Carriere 
326ef9888dcSEtienne Carriere 	/* Check if write of OTP is locked, informative only */
327ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sw_lock(otp_id, &locked);
328ef9888dcSEtienne Carriere 	if (result)
329ef9888dcSEtienne Carriere 		return result;
330d64485e4SEtienne Carriere 
331ef9888dcSEtienne Carriere 	if (locked)
332ef9888dcSEtienne Carriere 		DMSG("BSEC write warning: OTP locked");
333d64485e4SEtienne Carriere 
3347dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3357dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3367dfc80abSGatien Chevallier 
337d64485e4SEtienne Carriere 	exceptions = bsec_lock();
338d64485e4SEtienne Carriere 
339d64485e4SEtienne Carriere 	io_write32(otp_data_base + (otp_id * sizeof(uint32_t)), value);
340d64485e4SEtienne Carriere 
341d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
342d64485e4SEtienne Carriere 
3431ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
344d64485e4SEtienne Carriere }
345d64485e4SEtienne Carriere 
3460c30f9eaSEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
347d64485e4SEtienne Carriere TEE_Result stm32_bsec_program_otp(uint32_t value, uint32_t otp_id)
348d64485e4SEtienne Carriere {
349d64485e4SEtienne Carriere 	TEE_Result result = 0;
350d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
351ef9888dcSEtienne Carriere 	uint64_t timeout_ref = 0;
352ef9888dcSEtienne Carriere 	bool locked = false;
353d64485e4SEtienne Carriere 
354ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
355ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sp_lock(otp_id, &locked);
356ef9888dcSEtienne Carriere 	if (result)
357ef9888dcSEtienne Carriere 		return result;
358d64485e4SEtienne Carriere 
359ef9888dcSEtienne Carriere 	if (locked)
360ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: OTP locked");
361d64485e4SEtienne Carriere 
362d64485e4SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_OTP_LOCK_OFF) & BIT(BSEC_LOCK_PROGRAM))
363ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: GPLOCK activated");
364d64485e4SEtienne Carriere 
3657dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3667dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3677dfc80abSGatien Chevallier 
368d64485e4SEtienne Carriere 	exceptions = bsec_lock();
369d64485e4SEtienne Carriere 
370d64485e4SEtienne Carriere 	result = power_up_safmem();
371d64485e4SEtienne Carriere 	if (result)
3727b05d514SEtienne Carriere 		goto out;
373d64485e4SEtienne Carriere 
374d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_WRDATA_OFF, value);
375d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_WRITE);
376d64485e4SEtienne Carriere 
377d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
378d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
3797dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
380d64485e4SEtienne Carriere 			break;
381d64485e4SEtienne Carriere 
3827dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
3838afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
3847dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
3858afb7c41SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
386d64485e4SEtienne Carriere 	else
3871ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
388d64485e4SEtienne Carriere 
389d64485e4SEtienne Carriere 	power_down_safmem();
390d64485e4SEtienne Carriere 
3917b05d514SEtienne Carriere out:
392d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
393d64485e4SEtienne Carriere 
394d64485e4SEtienne Carriere 	return result;
395d64485e4SEtienne Carriere }
396301b3eb5SEtienne Carriere #endif /*CFG_STM32_BSEC_WRITE*/
397d64485e4SEtienne Carriere 
398d64485e4SEtienne Carriere TEE_Result stm32_bsec_permanent_lock_otp(uint32_t otp_id)
399d64485e4SEtienne Carriere {
400d64485e4SEtienne Carriere 	TEE_Result result = 0;
401d64485e4SEtienne Carriere 	uint32_t data = 0;
402d64485e4SEtienne Carriere 	uint32_t addr = 0;
403d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
404d64485e4SEtienne Carriere 	vaddr_t base = bsec_base();
405586eee81SEtienne Carriere 	uint64_t timeout_ref = 0;
406c6d2483aSGatien Chevallier 	uint32_t upper_base = otp_upper_base();
407d64485e4SEtienne Carriere 
408d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
409d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
410d64485e4SEtienne Carriere 
411c6d2483aSGatien Chevallier 	/*
412c6d2483aSGatien Chevallier 	 * 2 bits per words for lower OTPs: 2:1 Redundancy
413c6d2483aSGatien Chevallier 	 * 1 bit per word for upper OTPs : ECC support
414c6d2483aSGatien Chevallier 	 * e.g with 32 lower and 64 upper OTPs:
415c6d2483aSGatien Chevallier 	 * OTP word to be    ADDR[6:0]   WRDATA[31:0]
416c6d2483aSGatien Chevallier 	 *     locked
417c6d2483aSGatien Chevallier 	 *       0             0x00      0x0000 0003
418c6d2483aSGatien Chevallier 	 *       1             0x00      0x0000 000C
419c6d2483aSGatien Chevallier 	 *      ...             ...              ...
420c6d2483aSGatien Chevallier 	 *       7             0x00      0x0000 C000
421c6d2483aSGatien Chevallier 	 *       8             0x01      0x0000 0003
422c6d2483aSGatien Chevallier 	 *      ...             ...              ...
423c6d2483aSGatien Chevallier 	 *      31             0x03      0x0000 C000
424c6d2483aSGatien Chevallier 	 *      32             0x04      0x0000 0001
425c6d2483aSGatien Chevallier 	 *      33             0x04      0x0000 0002
426c6d2483aSGatien Chevallier 	 *      95             0x07      0x0000 8000
427c6d2483aSGatien Chevallier 	 */
428c6d2483aSGatien Chevallier 	if (otp_id < upper_base) {
429c6d2483aSGatien Chevallier 		addr = otp_id / 8U;
430c6d2483aSGatien Chevallier 		data = DATA_LOWER_OTP_PERLOCK_BIT << ((otp_id * 2U) & 0xF);
431d64485e4SEtienne Carriere 	} else {
432c6d2483aSGatien Chevallier 		addr = upper_base / 8U + (otp_id - upper_base) / 16U;
433c6d2483aSGatien Chevallier 		data = DATA_UPPER_OTP_PERLOCK_BIT << (otp_id & 0xF);
434d64485e4SEtienne Carriere 	}
435d64485e4SEtienne Carriere 
4367dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4377dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4387dfc80abSGatien Chevallier 
439d64485e4SEtienne Carriere 	exceptions = bsec_lock();
440d64485e4SEtienne Carriere 
441d64485e4SEtienne Carriere 	result = power_up_safmem();
442d64485e4SEtienne Carriere 	if (result)
4437b05d514SEtienne Carriere 		goto out;
444d64485e4SEtienne Carriere 
445d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_WRDATA_OFF, data);
446d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK);
447d64485e4SEtienne Carriere 
448d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
449d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
4507dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
451d64485e4SEtienne Carriere 			break;
452d64485e4SEtienne Carriere 
4537dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
4548afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
4557dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
456d64485e4SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
457d64485e4SEtienne Carriere 	else
4581ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, false /* not-disturbed */);
459d64485e4SEtienne Carriere 
460e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
461e090bb5aSGatien Chevallier 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_READ | BSEC_LOCK);
462e090bb5aSGatien Chevallier #endif
463e090bb5aSGatien Chevallier 
464d64485e4SEtienne Carriere 	power_down_safmem();
465d64485e4SEtienne Carriere 
4667b05d514SEtienne Carriere out:
467d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
468d64485e4SEtienne Carriere 
469d64485e4SEtienne Carriere 	return result;
470d64485e4SEtienne Carriere }
471d64485e4SEtienne Carriere 
472d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_debug_conf(uint32_t value)
473d64485e4SEtienne Carriere {
474d64485e4SEtienne Carriere 	TEE_Result result = TEE_ERROR_GENERIC;
475d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
476d64485e4SEtienne Carriere 
4777dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4787dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4797dfc80abSGatien Chevallier 
480d64485e4SEtienne Carriere 	exceptions = bsec_lock();
481d64485e4SEtienne Carriere 
482d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_DEN_OFF, value);
483d64485e4SEtienne Carriere 
4841ff52b85SGatien Chevallier 	if ((io_read32(bsec_base() + BSEC_DEN_OFF) ^ value) == 0U)
485d64485e4SEtienne Carriere 		result = TEE_SUCCESS;
486d64485e4SEtienne Carriere 
487d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
488d64485e4SEtienne Carriere 
489d64485e4SEtienne Carriere 	return result;
490d64485e4SEtienne Carriere }
491d64485e4SEtienne Carriere 
492d64485e4SEtienne Carriere uint32_t stm32_bsec_read_debug_conf(void)
493d64485e4SEtienne Carriere {
494d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_DEN_OFF);
495d64485e4SEtienne Carriere }
496d64485e4SEtienne Carriere 
497ef9888dcSEtienne Carriere static TEE_Result set_bsec_lock(uint32_t otp_id, size_t lock_offset)
498d64485e4SEtienne Carriere {
499d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
500d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
501d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
502d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
503d64485e4SEtienne Carriere 
504ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
505ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
506d64485e4SEtienne Carriere 
5077dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5087dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5097dfc80abSGatien Chevallier 
510d64485e4SEtienne Carriere 	exceptions = bsec_lock();
511d64485e4SEtienne Carriere 
512ef9888dcSEtienne Carriere 	io_write32(lock_addr, otp_mask);
513d64485e4SEtienne Carriere 
514d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
515d64485e4SEtienne Carriere 
516ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
517d64485e4SEtienne Carriere }
518d64485e4SEtienne Carriere 
519ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sr_lock(uint32_t otp_id)
520d64485e4SEtienne Carriere {
521ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SRLOCK_OFF);
522d64485e4SEtienne Carriere }
523d64485e4SEtienne Carriere 
524ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sw_lock(uint32_t otp_id)
525d64485e4SEtienne Carriere {
526ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SWLOCK_OFF);
527d64485e4SEtienne Carriere }
528d64485e4SEtienne Carriere 
529ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sp_lock(uint32_t otp_id)
530d64485e4SEtienne Carriere {
531ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SPLOCK_OFF);
532d64485e4SEtienne Carriere }
533d64485e4SEtienne Carriere 
534ef9888dcSEtienne Carriere static TEE_Result read_bsec_lock(uint32_t otp_id, bool *locked,
535ef9888dcSEtienne Carriere 				 size_t lock_offset)
536d64485e4SEtienne Carriere {
537d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
538d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
539d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
540d64485e4SEtienne Carriere 
541ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
542ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
543ef9888dcSEtienne Carriere 
5447dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5457dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5467dfc80abSGatien Chevallier 
547ef9888dcSEtienne Carriere 	*locked = (io_read32(lock_addr) & otp_mask) != 0;
548ef9888dcSEtienne Carriere 
549ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
550d64485e4SEtienne Carriere }
551d64485e4SEtienne Carriere 
552ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sr_lock(uint32_t otp_id, bool *locked)
553d64485e4SEtienne Carriere {
554ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SRLOCK_OFF);
555d64485e4SEtienne Carriere }
556d64485e4SEtienne Carriere 
557ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sw_lock(uint32_t otp_id, bool *locked)
558d64485e4SEtienne Carriere {
559ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SWLOCK_OFF);
560d64485e4SEtienne Carriere }
561d64485e4SEtienne Carriere 
562ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sp_lock(uint32_t otp_id, bool *locked)
563d64485e4SEtienne Carriere {
564ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SPLOCK_OFF);
565d64485e4SEtienne Carriere }
566d64485e4SEtienne Carriere 
567ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_permanent_lock(uint32_t otp_id, bool *locked)
568d64485e4SEtienne Carriere {
569ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_WRLOCK_OFF);
570d64485e4SEtienne Carriere }
571d64485e4SEtienne Carriere 
572890703c3SEtienne Carriere static size_t nsec_access_array_size(void)
573890703c3SEtienne Carriere {
574586eee81SEtienne Carriere 	size_t upper_count = otp_max_id() - otp_upper_base() + 1;
575890703c3SEtienne Carriere 
5768396f62eSGatien Chevallier 	return ROUNDUP_DIV(upper_count, BSEC_BITS_PER_WORD);
577890703c3SEtienne Carriere }
578890703c3SEtienne Carriere 
579890703c3SEtienne Carriere static bool nsec_access_granted(unsigned int index)
580890703c3SEtienne Carriere {
581890703c3SEtienne Carriere 	uint32_t *array = bsec_dev.nsec_access;
582890703c3SEtienne Carriere 
583890703c3SEtienne Carriere 	return array &&
5848396f62eSGatien Chevallier 	       (index / BSEC_BITS_PER_WORD) < nsec_access_array_size() &&
5858396f62eSGatien Chevallier 	       array[index / BSEC_BITS_PER_WORD] &
5868396f62eSGatien Chevallier 	       BIT(index % BSEC_BITS_PER_WORD);
587890703c3SEtienne Carriere }
588890703c3SEtienne Carriere 
5897dfc80abSGatien Chevallier bool stm32_bsec_can_access_otp(uint32_t otp_id)
5907dfc80abSGatien Chevallier {
5917dfc80abSGatien Chevallier 	return (otp_id <= otp_max_id()) && !state_is_invalid_mode();
5927dfc80abSGatien Chevallier }
5937dfc80abSGatien Chevallier 
594d64485e4SEtienne Carriere bool stm32_bsec_nsec_can_access_otp(uint32_t otp_id)
595d64485e4SEtienne Carriere {
596586eee81SEtienne Carriere 	return otp_id < otp_upper_base() ||
597586eee81SEtienne Carriere 	       nsec_access_granted(otp_id - otp_upper_base());
598d64485e4SEtienne Carriere }
599d64485e4SEtienne Carriere 
60093114f2eSGatien Chevallier struct nvmem_layout {
60193114f2eSGatien Chevallier 	char *name;
60293114f2eSGatien Chevallier 	uint32_t otp_id;
60393114f2eSGatien Chevallier 	size_t bit_len;
60493114f2eSGatien Chevallier };
60593114f2eSGatien Chevallier 
60693114f2eSGatien Chevallier static struct nvmem_layout *nvmem_layout;
60793114f2eSGatien Chevallier static size_t nvmem_layout_count;
60893114f2eSGatien Chevallier 
60993114f2eSGatien Chevallier TEE_Result stm32_bsec_find_otp_in_nvmem_layout(const char *name,
61093114f2eSGatien Chevallier 					       uint32_t *otp_id,
61193114f2eSGatien Chevallier 					       size_t *otp_bit_len)
61293114f2eSGatien Chevallier {
61393114f2eSGatien Chevallier 	size_t i = 0;
61493114f2eSGatien Chevallier 
61593114f2eSGatien Chevallier 	if (!name)
61693114f2eSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
61793114f2eSGatien Chevallier 
61893114f2eSGatien Chevallier 	for (i = 0; i < nvmem_layout_count; i++) {
61993114f2eSGatien Chevallier 		if (!nvmem_layout[i].name || strcmp(name, nvmem_layout[i].name))
62093114f2eSGatien Chevallier 			continue;
62193114f2eSGatien Chevallier 
62293114f2eSGatien Chevallier 		if (otp_id)
62393114f2eSGatien Chevallier 			*otp_id = nvmem_layout[i].otp_id;
62493114f2eSGatien Chevallier 
62593114f2eSGatien Chevallier 		if (otp_bit_len)
62693114f2eSGatien Chevallier 			*otp_bit_len = nvmem_layout[i].bit_len;
62793114f2eSGatien Chevallier 
62893114f2eSGatien Chevallier 		DMSG("nvmem %s = %zu: %"PRId32" %zu", name, i,
62993114f2eSGatien Chevallier 		     nvmem_layout[i].otp_id, nvmem_layout[i].bit_len);
63093114f2eSGatien Chevallier 
63193114f2eSGatien Chevallier 		return TEE_SUCCESS;
63293114f2eSGatien Chevallier 	}
63393114f2eSGatien Chevallier 
63493114f2eSGatien Chevallier 	DMSG("nvmem %s failed", name);
63593114f2eSGatien Chevallier 
63693114f2eSGatien Chevallier 	return TEE_ERROR_ITEM_NOT_FOUND;
6377dfc80abSGatien Chevallier };
6387dfc80abSGatien Chevallier 
639ac16eac3SJose Quaresma TEE_Result stm32_bsec_get_state(enum stm32_bsec_sec_state *state)
6407dfc80abSGatien Chevallier {
6417dfc80abSGatien Chevallier 	if (!state)
6427dfc80abSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
6437dfc80abSGatien Chevallier 
6447dfc80abSGatien Chevallier 	if (state_is_invalid_mode() || !state_is_secured_mode()) {
6457dfc80abSGatien Chevallier 		*state = BSEC_STATE_INVALID;
6467dfc80abSGatien Chevallier 	} else {
6477dfc80abSGatien Chevallier 		if (state_is_closed_mode())
6487dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_CLOSED;
6497dfc80abSGatien Chevallier 		else
6507dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_OPEN;
6517dfc80abSGatien Chevallier 	}
6527dfc80abSGatien Chevallier 
6537dfc80abSGatien Chevallier 	return TEE_SUCCESS;
65493114f2eSGatien Chevallier }
65593114f2eSGatien Chevallier 
656890703c3SEtienne Carriere static void enable_nsec_access(unsigned int otp_id)
657890703c3SEtienne Carriere {
6588396f62eSGatien Chevallier 	unsigned int idx = (otp_id - otp_upper_base()) / BSEC_BITS_PER_WORD;
659890703c3SEtienne Carriere 
660586eee81SEtienne Carriere 	if (otp_id < otp_upper_base())
661890703c3SEtienne Carriere 		return;
662890703c3SEtienne Carriere 
663890703c3SEtienne Carriere 	if (otp_id > otp_max_id() || stm32_bsec_shadow_register(otp_id))
664890703c3SEtienne Carriere 		panic();
665890703c3SEtienne Carriere 
6668396f62eSGatien Chevallier 	bsec_dev.nsec_access[idx] |= BIT(otp_id % BSEC_BITS_PER_WORD);
667890703c3SEtienne Carriere }
668890703c3SEtienne Carriere 
669890703c3SEtienne Carriere static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
670890703c3SEtienne Carriere {
671890703c3SEtienne Carriere 	int bsec_subnode = 0;
672890703c3SEtienne Carriere 
673890703c3SEtienne Carriere 	bsec_dev.nsec_access = calloc(nsec_access_array_size(),
674890703c3SEtienne Carriere 				      sizeof(*bsec_dev.nsec_access));
675890703c3SEtienne Carriere 	if (!bsec_dev.nsec_access)
676890703c3SEtienne Carriere 		panic();
677890703c3SEtienne Carriere 
678890703c3SEtienne Carriere 	fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
67927a02b1eSGatien Chevallier 		unsigned int reg_offset = 0;
68027a02b1eSGatien Chevallier 		unsigned int reg_size = 0;
681890703c3SEtienne Carriere 		unsigned int otp_id = 0;
682890703c3SEtienne Carriere 		unsigned int i = 0;
683890703c3SEtienne Carriere 		size_t size = 0;
684890703c3SEtienne Carriere 
685f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, bsec_subnode);
686f354a5d8SGatien Chevallier 		reg_size = fdt_reg_size(fdt, bsec_subnode);
687890703c3SEtienne Carriere 
68827a02b1eSGatien Chevallier 		assert(reg_offset != DT_INFO_INVALID_REG &&
68927a02b1eSGatien Chevallier 		       reg_size != DT_INFO_INVALID_REG_SIZE);
690890703c3SEtienne Carriere 
69127a02b1eSGatien Chevallier 		otp_id = reg_offset / sizeof(uint32_t);
692890703c3SEtienne Carriere 
693890703c3SEtienne Carriere 		if (otp_id < STM32MP1_UPPER_OTP_START) {
69427a02b1eSGatien Chevallier 			unsigned int otp_end =
69527a02b1eSGatien Chevallier 				ROUNDUP_DIV(reg_offset + reg_size,
69627a02b1eSGatien Chevallier 					    sizeof(uint32_t));
697890703c3SEtienne Carriere 
698890703c3SEtienne Carriere 			if (otp_end > STM32MP1_UPPER_OTP_START) {
699890703c3SEtienne Carriere 				/*
700890703c3SEtienne Carriere 				 * OTP crosses Lower/Upper boundary, consider
701890703c3SEtienne Carriere 				 * only the upper part.
702890703c3SEtienne Carriere 				 */
703890703c3SEtienne Carriere 				otp_id = STM32MP1_UPPER_OTP_START;
70427a02b1eSGatien Chevallier 				reg_size -= (STM32MP1_UPPER_OTP_START *
70527a02b1eSGatien Chevallier 					     sizeof(uint32_t)) - reg_offset;
70627a02b1eSGatien Chevallier 				reg_offset = STM32MP1_UPPER_OTP_START *
707890703c3SEtienne Carriere 					     sizeof(uint32_t);
708890703c3SEtienne Carriere 
709890703c3SEtienne Carriere 				DMSG("OTP crosses Lower/Upper boundary");
710890703c3SEtienne Carriere 			} else {
711890703c3SEtienne Carriere 				continue;
712890703c3SEtienne Carriere 			}
713890703c3SEtienne Carriere 		}
714890703c3SEtienne Carriere 
7150ec45216SGatien Chevallier 		/* Handle different kinds of non-secure accesses */
7160ec45216SGatien Chevallier 		if (fdt_getprop(fdt, bsec_subnode,
7170ec45216SGatien Chevallier 				"st,non-secure-otp-provisioning", NULL)) {
7180ec45216SGatien Chevallier 			bool locked = false;
7190ec45216SGatien Chevallier 			bool locked_2 = false;
7200ec45216SGatien Chevallier 
7210ec45216SGatien Chevallier 			/* Check if write of OTP is locked */
7220ec45216SGatien Chevallier 			if (stm32_bsec_read_permanent_lock(otp_id, &locked))
7230ec45216SGatien Chevallier 				panic("Cannot read permanent lock");
7240ec45216SGatien Chevallier 
7250ec45216SGatien Chevallier 			/*
7260ec45216SGatien Chevallier 			 * Check if fuses of the subnode
7270ec45216SGatien Chevallier 			 * have the same lock status
7280ec45216SGatien Chevallier 			 */
7290ec45216SGatien Chevallier 			for (i = 1; i < (reg_size / sizeof(uint32_t)); i++) {
7300ec45216SGatien Chevallier 				if (stm32_bsec_read_permanent_lock(otp_id + i,
7310ec45216SGatien Chevallier 								   &locked_2))
7320ec45216SGatien Chevallier 					panic("Cannot read permanent lock");
7330ec45216SGatien Chevallier 
7340ec45216SGatien Chevallier 				if (locked != locked_2) {
7350ec45216SGatien Chevallier 					EMSG("Inconsistent status OTP ID %u",
7360ec45216SGatien Chevallier 					     otp_id + i);
7370ec45216SGatien Chevallier 					locked = true;
7380ec45216SGatien Chevallier 				}
7390ec45216SGatien Chevallier 			}
7400ec45216SGatien Chevallier 
7410ec45216SGatien Chevallier 			if (locked) {
7420ec45216SGatien Chevallier 				DMSG("BSEC: OTP locked");
743890703c3SEtienne Carriere 				continue;
7440ec45216SGatien Chevallier 			}
7450ec45216SGatien Chevallier 		} else if (!fdt_getprop(fdt, bsec_subnode, "st,non-secure-otp",
7460ec45216SGatien Chevallier 					NULL)) {
7470ec45216SGatien Chevallier 			continue;
7480ec45216SGatien Chevallier 		}
749890703c3SEtienne Carriere 
75027a02b1eSGatien Chevallier 		if ((reg_offset % sizeof(uint32_t)) ||
75127a02b1eSGatien Chevallier 		    (reg_size % sizeof(uint32_t)))
752890703c3SEtienne Carriere 			panic("Unaligned non-secure OTP");
753890703c3SEtienne Carriere 
75427a02b1eSGatien Chevallier 		size = reg_size / sizeof(uint32_t);
755890703c3SEtienne Carriere 
7567dfc80abSGatien Chevallier 		if (otp_id + size > OTP_MAX_SIZE)
757890703c3SEtienne Carriere 			panic("OTP range oversized");
758890703c3SEtienne Carriere 
759890703c3SEtienne Carriere 		for (i = otp_id; i < otp_id + size; i++)
760890703c3SEtienne Carriere 			enable_nsec_access(i);
761890703c3SEtienne Carriere 	}
762890703c3SEtienne Carriere }
763890703c3SEtienne Carriere 
76493114f2eSGatien Chevallier static void save_dt_nvmem_layout(void *fdt, int bsec_node)
76593114f2eSGatien Chevallier {
76693114f2eSGatien Chevallier 	int cell_max = 0;
76793114f2eSGatien Chevallier 	int cell_cnt = 0;
76893114f2eSGatien Chevallier 	int node = 0;
76993114f2eSGatien Chevallier 
77093114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node)
77193114f2eSGatien Chevallier 		cell_max++;
77293114f2eSGatien Chevallier 	if (!cell_max)
77393114f2eSGatien Chevallier 		return;
77493114f2eSGatien Chevallier 
77593114f2eSGatien Chevallier 	nvmem_layout = calloc(cell_max, sizeof(*nvmem_layout));
77693114f2eSGatien Chevallier 	if (!nvmem_layout)
77793114f2eSGatien Chevallier 		panic();
77893114f2eSGatien Chevallier 
77993114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node) {
78093114f2eSGatien Chevallier 		unsigned int reg_offset = 0;
78193114f2eSGatien Chevallier 		unsigned int reg_length = 0;
78293114f2eSGatien Chevallier 		const char *string = NULL;
78393114f2eSGatien Chevallier 		const char *s = NULL;
78493114f2eSGatien Chevallier 		int len = 0;
78593114f2eSGatien Chevallier 		struct nvmem_layout *layout_cell = &nvmem_layout[cell_cnt];
78693114f2eSGatien Chevallier 
78793114f2eSGatien Chevallier 		string = fdt_get_name(fdt, node, &len);
78893114f2eSGatien Chevallier 		if (!string || !len)
78993114f2eSGatien Chevallier 			continue;
79093114f2eSGatien Chevallier 
791f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, node);
792f354a5d8SGatien Chevallier 		reg_length = fdt_reg_size(fdt, node);
79393114f2eSGatien Chevallier 
79493114f2eSGatien Chevallier 		if (reg_offset == DT_INFO_INVALID_REG ||
79593114f2eSGatien Chevallier 		    reg_length == DT_INFO_INVALID_REG_SIZE) {
79693114f2eSGatien Chevallier 			DMSG("Malformed nvmem %s: ignored", string);
79793114f2eSGatien Chevallier 			continue;
79893114f2eSGatien Chevallier 		}
79993114f2eSGatien Chevallier 
80093114f2eSGatien Chevallier 		if (reg_offset % sizeof(uint32_t)) {
80193114f2eSGatien Chevallier 			DMSG("Misaligned nvmem %s: ignored", string);
80293114f2eSGatien Chevallier 			continue;
80393114f2eSGatien Chevallier 		}
80493114f2eSGatien Chevallier 		layout_cell->otp_id = reg_offset / sizeof(uint32_t);
80593114f2eSGatien Chevallier 		layout_cell->bit_len = reg_length * CHAR_BIT;
80693114f2eSGatien Chevallier 
80793114f2eSGatien Chevallier 		s = strchr(string, '@');
80893114f2eSGatien Chevallier 		if (s)
80993114f2eSGatien Chevallier 			len = s - string;
81093114f2eSGatien Chevallier 
81193114f2eSGatien Chevallier 		layout_cell->name = strndup(string, len);
81293114f2eSGatien Chevallier 		if (!layout_cell->name)
81393114f2eSGatien Chevallier 			panic();
81493114f2eSGatien Chevallier 		cell_cnt++;
81593114f2eSGatien Chevallier 		DMSG("nvmem[%d] = %s %"PRId32" %zu", cell_cnt,
81693114f2eSGatien Chevallier 		     layout_cell->name, layout_cell->otp_id,
81793114f2eSGatien Chevallier 		     layout_cell->bit_len);
81893114f2eSGatien Chevallier 	}
81993114f2eSGatien Chevallier 
82093114f2eSGatien Chevallier 	if (cell_cnt != cell_max) {
82193114f2eSGatien Chevallier 		nvmem_layout = realloc(nvmem_layout,
82293114f2eSGatien Chevallier 				       cell_cnt * sizeof(*nvmem_layout));
82393114f2eSGatien Chevallier 		if (!nvmem_layout)
82493114f2eSGatien Chevallier 			panic();
82593114f2eSGatien Chevallier 	}
82693114f2eSGatien Chevallier 
82793114f2eSGatien Chevallier 	nvmem_layout_count = cell_cnt;
82893114f2eSGatien Chevallier }
82993114f2eSGatien Chevallier 
830890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
831890703c3SEtienne Carriere {
832890703c3SEtienne Carriere 	void *fdt = NULL;
833890703c3SEtienne Carriere 	int node = 0;
834890703c3SEtienne Carriere 	struct dt_node_info bsec_info = { };
835890703c3SEtienne Carriere 
836890703c3SEtienne Carriere 	fdt = get_embedded_dt();
837e090bb5aSGatien Chevallier 	node = fdt_node_offset_by_compatible(fdt, 0, DT_BSEC_COMPAT);
838890703c3SEtienne Carriere 	if (node < 0)
839890703c3SEtienne Carriere 		panic();
840890703c3SEtienne Carriere 
841f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &bsec_info, node);
842890703c3SEtienne Carriere 
843890703c3SEtienne Carriere 	if (bsec_info.reg != bsec_dev.base.pa ||
844890703c3SEtienne Carriere 	    !(bsec_info.status & DT_STATUS_OK_SEC))
845890703c3SEtienne Carriere 		panic();
846890703c3SEtienne Carriere 
847890703c3SEtienne Carriere 	bsec_dt_otp_nsec_access(fdt, node);
84893114f2eSGatien Chevallier 
84993114f2eSGatien Chevallier 	save_dt_nvmem_layout(fdt, node);
850890703c3SEtienne Carriere }
851890703c3SEtienne Carriere 
852d6df31b0SGatien Chevallier static TEE_Result bsec_pm(enum pm_op op, uint32_t pm_hint __unused,
853d6df31b0SGatien Chevallier 			  const struct pm_callback_handle *hdl __unused)
854d6df31b0SGatien Chevallier {
855d6df31b0SGatien Chevallier 	static uint32_t debug_conf;
856d6df31b0SGatien Chevallier 
857d6df31b0SGatien Chevallier 	assert(op == PM_OP_SUSPEND || op == PM_OP_RESUME);
858d6df31b0SGatien Chevallier 
859d6df31b0SGatien Chevallier 	if (op == PM_OP_SUSPEND)
860d6df31b0SGatien Chevallier 		debug_conf = stm32_bsec_read_debug_conf();
861d6df31b0SGatien Chevallier 	else
862d6df31b0SGatien Chevallier 		stm32_bsec_write_debug_conf(debug_conf);
863d6df31b0SGatien Chevallier 
864d6df31b0SGatien Chevallier 	return TEE_SUCCESS;
865d6df31b0SGatien Chevallier }
866d6df31b0SGatien Chevallier DECLARE_KEEP_PAGER(bsec_pm);
867d6df31b0SGatien Chevallier 
868d64485e4SEtienne Carriere static TEE_Result initialize_bsec(void)
869d64485e4SEtienne Carriere {
8707994d842SEtienne Carriere 	struct stm32_bsec_static_cfg cfg = { };
871d64485e4SEtienne Carriere 
872d64485e4SEtienne Carriere 	stm32mp_get_bsec_static_cfg(&cfg);
873d64485e4SEtienne Carriere 
874d64485e4SEtienne Carriere 	bsec_dev.base.pa = cfg.base;
875d64485e4SEtienne Carriere 	bsec_dev.upper_base = cfg.upper_start;
876d64485e4SEtienne Carriere 	bsec_dev.max_id = cfg.max_id;
877d64485e4SEtienne Carriere 
8787dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
8797dfc80abSGatien Chevallier 		panic();
8807dfc80abSGatien Chevallier 
881890703c3SEtienne Carriere 	initialize_bsec_from_dt();
882890703c3SEtienne Carriere 
883d6df31b0SGatien Chevallier 	register_pm_core_service_cb(bsec_pm, NULL, "stm32_bsec");
884d6df31b0SGatien Chevallier 
885d64485e4SEtienne Carriere 	return TEE_SUCCESS;
886d64485e4SEtienne Carriere }
887d64485e4SEtienne Carriere 
8888c8316dbSEtienne Carriere early_init(initialize_bsec);
889