xref: /optee_os/core/drivers/stm32_bsec.c (revision 9f0072258fe062fa3932d4eb4d625170b04c25c1)
1d64485e4SEtienne Carriere // SPDX-License-Identifier: BSD-3-Clause
2d64485e4SEtienne Carriere /*
38afb7c41SEtienne Carriere  * Copyright (c) 2017-2021, STMicroelectronics
4d64485e4SEtienne Carriere  */
5d64485e4SEtienne Carriere 
6d64485e4SEtienne Carriere #include <assert.h>
7890703c3SEtienne Carriere #include <config.h>
8d64485e4SEtienne Carriere #include <drivers/stm32_bsec.h>
9d64485e4SEtienne Carriere #include <io.h>
10d64485e4SEtienne Carriere #include <kernel/delay.h>
11890703c3SEtienne Carriere #include <kernel/dt.h>
1265401337SJens Wiklander #include <kernel/boot.h>
13d6df31b0SGatien Chevallier #include <kernel/pm.h>
14d64485e4SEtienne Carriere #include <kernel/spinlock.h>
15a2fc83d1SJerome Forissier #include <libfdt.h>
16d64485e4SEtienne Carriere #include <limits.h>
17d64485e4SEtienne Carriere #include <mm/core_memprot.h>
18d64485e4SEtienne Carriere #include <platform_config.h>
19d64485e4SEtienne Carriere #include <stm32_util.h>
20ef9888dcSEtienne Carriere #include <string.h>
21ef9888dcSEtienne Carriere #include <tee_api_defines.h>
22d64485e4SEtienne Carriere #include <types_ext.h>
23d64485e4SEtienne Carriere #include <util.h>
24d64485e4SEtienne Carriere 
25e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
26e090bb5aSGatien Chevallier #define DT_BSEC_COMPAT "st,stm32mp13-bsec"
27e090bb5aSGatien Chevallier #endif
28e090bb5aSGatien Chevallier #ifdef CFG_STM32MP15
29e090bb5aSGatien Chevallier #define DT_BSEC_COMPAT "st,stm32mp15-bsec"
30e090bb5aSGatien Chevallier #endif
31e090bb5aSGatien Chevallier 
32d64485e4SEtienne Carriere #define BSEC_OTP_MASK			GENMASK_32(4, 0)
334bbd20f1SGatien Chevallier #define BSEC_OTP_BANK_SHIFT		U(5)
34d64485e4SEtienne Carriere 
35d64485e4SEtienne Carriere /* Permanent lock bitmasks */
364bbd20f1SGatien Chevallier #define DATA_LOWER_OTP_PERLOCK_BIT	U(3)
374bbd20f1SGatien Chevallier #define DATA_UPPER_OTP_PERLOCK_BIT	U(1)
38d64485e4SEtienne Carriere 
39d64485e4SEtienne Carriere /* BSEC register offset */
404bbd20f1SGatien Chevallier #define BSEC_OTP_CONF_OFF		U(0x000)
414bbd20f1SGatien Chevallier #define BSEC_OTP_CTRL_OFF		U(0x004)
424bbd20f1SGatien Chevallier #define BSEC_OTP_WRDATA_OFF		U(0x008)
434bbd20f1SGatien Chevallier #define BSEC_OTP_STATUS_OFF		U(0x00C)
444bbd20f1SGatien Chevallier #define BSEC_OTP_LOCK_OFF		U(0x010)
454bbd20f1SGatien Chevallier #define BSEC_DEN_OFF			U(0x014)
464bbd20f1SGatien Chevallier #define BSEC_FEN_OFF			U(0x018)
474bbd20f1SGatien Chevallier #define BSEC_DISTURBED_OFF		U(0x01C)
484bbd20f1SGatien Chevallier #define BSEC_DISTURBED1_OFF		U(0x020)
494bbd20f1SGatien Chevallier #define BSEC_DISTURBED2_OFF		U(0x024)
504bbd20f1SGatien Chevallier #define BSEC_ERROR_OFF			U(0x034)
514bbd20f1SGatien Chevallier #define BSEC_ERROR1_OFF			U(0x038)
524bbd20f1SGatien Chevallier #define BSEC_ERROR2_OFF			U(0x03C)
534bbd20f1SGatien Chevallier #define BSEC_WRLOCK_OFF			U(0x04C)
544bbd20f1SGatien Chevallier #define BSEC_WRLOCK1_OFF		U(0x050)
554bbd20f1SGatien Chevallier #define BSEC_WRLOCK2_OFF		U(0x054)
564bbd20f1SGatien Chevallier #define BSEC_SPLOCK_OFF			U(0x064)
574bbd20f1SGatien Chevallier #define BSEC_SPLOCK1_OFF		U(0x068)
584bbd20f1SGatien Chevallier #define BSEC_SPLOCK2_OFF		U(0x06C)
594bbd20f1SGatien Chevallier #define BSEC_SWLOCK_OFF			U(0x07C)
604bbd20f1SGatien Chevallier #define BSEC_SWLOCK1_OFF		U(0x080)
614bbd20f1SGatien Chevallier #define BSEC_SWLOCK2_OFF		U(0x084)
624bbd20f1SGatien Chevallier #define BSEC_SRLOCK_OFF			U(0x094)
634bbd20f1SGatien Chevallier #define BSEC_SRLOCK1_OFF		U(0x098)
644bbd20f1SGatien Chevallier #define BSEC_SRLOCK2_OFF		U(0x09C)
654bbd20f1SGatien Chevallier #define BSEC_JTAG_IN_OFF		U(0x0AC)
664bbd20f1SGatien Chevallier #define BSEC_JTAG_OUT_OFF		U(0x0B0)
674bbd20f1SGatien Chevallier #define BSEC_SCRATCH_OFF		U(0x0B4)
684bbd20f1SGatien Chevallier #define BSEC_OTP_DATA_OFF		U(0x200)
694bbd20f1SGatien Chevallier #define BSEC_IPHW_CFG_OFF		U(0xFF0)
704bbd20f1SGatien Chevallier #define BSEC_IPVR_OFF			U(0xFF4)
714bbd20f1SGatien Chevallier #define BSEC_IP_ID_OFF			U(0xFF8)
724bbd20f1SGatien Chevallier #define BSEC_IP_MAGIC_ID_OFF		U(0xFFC)
73d64485e4SEtienne Carriere 
74d64485e4SEtienne Carriere /* BSEC_CONFIGURATION Register */
75d64485e4SEtienne Carriere #define BSEC_CONF_POWER_UP_MASK		BIT(0)
764bbd20f1SGatien Chevallier #define BSEC_CONF_POWER_UP_SHIFT	U(0)
77d64485e4SEtienne Carriere #define BSEC_CONF_FRQ_MASK		GENMASK_32(2, 1)
784bbd20f1SGatien Chevallier #define BSEC_CONF_FRQ_SHIFT		U(1)
79d64485e4SEtienne Carriere #define BSEC_CONF_PRG_WIDTH_MASK	GENMASK_32(6, 3)
804bbd20f1SGatien Chevallier #define BSEC_CONF_PRG_WIDTH_SHIFT	U(3)
81d64485e4SEtienne Carriere #define BSEC_CONF_TREAD_MASK		GENMASK_32(8, 7)
824bbd20f1SGatien Chevallier #define BSEC_CONF_TREAD_SHIFT		U(7)
83d64485e4SEtienne Carriere 
84d64485e4SEtienne Carriere /* BSEC_CONTROL Register */
854bbd20f1SGatien Chevallier #define BSEC_READ			U(0x000)
864bbd20f1SGatien Chevallier #define BSEC_WRITE			U(0x100)
874bbd20f1SGatien Chevallier #define BSEC_LOCK			U(0x200)
88d64485e4SEtienne Carriere 
89d64485e4SEtienne Carriere /* BSEC_STATUS Register */
907dfc80abSGatien Chevallier #define BSEC_MODE_SECURED		BIT(0)
917dfc80abSGatien Chevallier #define BSEC_MODE_INVALID		BIT(2)
927dfc80abSGatien Chevallier #define BSEC_MODE_BUSY			BIT(3)
937dfc80abSGatien Chevallier #define BSEC_MODE_PROGFAIL		BIT(4)
947dfc80abSGatien Chevallier #define BSEC_MODE_PWR			BIT(5)
95e090bb5aSGatien Chevallier #define BSEC_MODE_CLOSED		BIT(8)
96d64485e4SEtienne Carriere 
9701a06793SPatrick Delaunay /* BSEC_DEBUG bitfields */
9801a06793SPatrick Delaunay #ifdef CFG_STM32MP13
9901a06793SPatrick Delaunay #define BSEC_DEN_ALL_MSK		(GENMASK_32(11, 10) | GENMASK_32(8, 1))
10001a06793SPatrick Delaunay #endif
10101a06793SPatrick Delaunay #ifdef CFG_STM32MP15
10201a06793SPatrick Delaunay #define BSEC_DEN_ALL_MSK		GENMASK_32(11, 1)
10301a06793SPatrick Delaunay #endif
10401a06793SPatrick Delaunay 
105d64485e4SEtienne Carriere /*
106d64485e4SEtienne Carriere  * OTP Lock services definition
107d64485e4SEtienne Carriere  * Value must corresponding to the bit position in the register
108d64485e4SEtienne Carriere  */
1094bbd20f1SGatien Chevallier #define BSEC_LOCK_UPPER_OTP		U(0x00)
1104bbd20f1SGatien Chevallier #define BSEC_LOCK_DEBUG			U(0x02)
1114bbd20f1SGatien Chevallier #define BSEC_LOCK_PROGRAM		U(0x04)
112d64485e4SEtienne Carriere 
113d64485e4SEtienne Carriere /* Timeout when polling on status */
1144bbd20f1SGatien Chevallier #define BSEC_TIMEOUT_US			U(10000)
115890703c3SEtienne Carriere 
116d64485e4SEtienne Carriere struct bsec_dev {
117d64485e4SEtienne Carriere 	struct io_pa_va base;
118d64485e4SEtienne Carriere 	unsigned int upper_base;
119d64485e4SEtienne Carriere 	unsigned int max_id;
120890703c3SEtienne Carriere 	uint32_t *nsec_access;
121d64485e4SEtienne Carriere };
122d64485e4SEtienne Carriere 
123d64485e4SEtienne Carriere /* Only 1 instance of BSEC is expected per platform */
124d64485e4SEtienne Carriere static struct bsec_dev bsec_dev;
125d64485e4SEtienne Carriere 
126d64485e4SEtienne Carriere /* BSEC access protection */
127d64485e4SEtienne Carriere static unsigned int lock = SPINLOCK_UNLOCK;
128d64485e4SEtienne Carriere 
129d64485e4SEtienne Carriere static uint32_t bsec_lock(void)
130d64485e4SEtienne Carriere {
131d64485e4SEtienne Carriere 	return may_spin_lock(&lock);
132d64485e4SEtienne Carriere }
133d64485e4SEtienne Carriere 
134d64485e4SEtienne Carriere static void bsec_unlock(uint32_t exceptions)
135d64485e4SEtienne Carriere {
136d64485e4SEtienne Carriere 	may_spin_unlock(&lock, exceptions);
137d64485e4SEtienne Carriere }
138d64485e4SEtienne Carriere 
139d64485e4SEtienne Carriere static uint32_t otp_max_id(void)
140d64485e4SEtienne Carriere {
141d64485e4SEtienne Carriere 	return bsec_dev.max_id;
142d64485e4SEtienne Carriere }
143d64485e4SEtienne Carriere 
144586eee81SEtienne Carriere static uint32_t otp_upper_base(void)
145586eee81SEtienne Carriere {
146586eee81SEtienne Carriere 	return bsec_dev.upper_base;
147586eee81SEtienne Carriere }
148586eee81SEtienne Carriere 
149d64485e4SEtienne Carriere static uint32_t otp_bank_offset(uint32_t otp_id)
150d64485e4SEtienne Carriere {
151d64485e4SEtienne Carriere 	assert(otp_id <= otp_max_id());
152d64485e4SEtienne Carriere 
153d64485e4SEtienne Carriere 	return ((otp_id & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
154d64485e4SEtienne Carriere 		sizeof(uint32_t);
155d64485e4SEtienne Carriere }
156d64485e4SEtienne Carriere 
157d64485e4SEtienne Carriere static vaddr_t bsec_base(void)
158d64485e4SEtienne Carriere {
159c2e4eb43SAnton Rybakov 	return io_pa_or_va_secure(&bsec_dev.base, BSEC_IP_MAGIC_ID_OFF + 1);
160d64485e4SEtienne Carriere }
161d64485e4SEtienne Carriere 
162d64485e4SEtienne Carriere static uint32_t bsec_status(void)
163d64485e4SEtienne Carriere {
164d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_OTP_STATUS_OFF);
165d64485e4SEtienne Carriere }
166d64485e4SEtienne Carriere 
1677dfc80abSGatien Chevallier static bool state_is_invalid_mode(void)
1687dfc80abSGatien Chevallier {
1697dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_INVALID;
1707dfc80abSGatien Chevallier }
1717dfc80abSGatien Chevallier 
1727dfc80abSGatien Chevallier static bool state_is_secured_mode(void)
1737dfc80abSGatien Chevallier {
1747dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_SECURED;
1757dfc80abSGatien Chevallier }
1767dfc80abSGatien Chevallier 
1777dfc80abSGatien Chevallier static bool state_is_closed_mode(void)
1787dfc80abSGatien Chevallier {
17910fb0d97SGatien Chevallier 	uint32_t otp_cfg = 0;
1807dfc80abSGatien Chevallier 	uint32_t close_mode = 0;
18110fb0d97SGatien Chevallier 	TEE_Result res = TEE_ERROR_GENERIC;
182*9f007225SPatrick Delaunay 	size_t __maybe_unused sz = 0;
183*9f007225SPatrick Delaunay 	uint8_t __maybe_unused offset = 0;
1847dfc80abSGatien Chevallier 
185e090bb5aSGatien Chevallier 	if (IS_ENABLED(CFG_STM32MP13))
186e090bb5aSGatien Chevallier 		return bsec_status() & BSEC_MODE_CLOSED;
187e090bb5aSGatien Chevallier 
188*9f007225SPatrick Delaunay 	res = stm32_bsec_find_otp_in_nvmem_layout("cfg0_otp", &otp_cfg,
189*9f007225SPatrick Delaunay 						  &offset, &sz);
190*9f007225SPatrick Delaunay 	if (res || sz != 8 || offset)
191*9f007225SPatrick Delaunay 		panic("CFG0 OTP not found or invalid");
19210fb0d97SGatien Chevallier 
19310fb0d97SGatien Chevallier 	if (stm32_bsec_read_otp(&close_mode, otp_cfg))
1947dfc80abSGatien Chevallier 		panic("Unable to read OTP");
1957dfc80abSGatien Chevallier 
1967dfc80abSGatien Chevallier 	return close_mode & CFG0_OTP_CLOSED_DEVICE;
1977dfc80abSGatien Chevallier }
1987dfc80abSGatien Chevallier 
1991ac4ea14SEtienne Carriere /*
2001ac4ea14SEtienne Carriere  * Check that BSEC interface does not report an error
2011ac4ea14SEtienne Carriere  * @otp_id : OTP number
2021ac4ea14SEtienne Carriere  * @check_disturbed: check only error (false) or all sources (true)
2031ac4ea14SEtienne Carriere  * Return a TEE_Result compliant value
2041ac4ea14SEtienne Carriere  */
2051ac4ea14SEtienne Carriere static TEE_Result check_no_error(uint32_t otp_id, bool check_disturbed)
206d64485e4SEtienne Carriere {
207d64485e4SEtienne Carriere 	uint32_t bit = BIT(otp_id & BSEC_OTP_MASK);
208d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
209d64485e4SEtienne Carriere 
2101ac4ea14SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_ERROR_OFF + bank) & bit)
211d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
212d64485e4SEtienne Carriere 
2131ac4ea14SEtienne Carriere 	if (check_disturbed &&
2141ac4ea14SEtienne Carriere 	    io_read32(bsec_base() + BSEC_DISTURBED_OFF + bank) & bit)
215d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
216d64485e4SEtienne Carriere 
217d64485e4SEtienne Carriere 	return TEE_SUCCESS;
218d64485e4SEtienne Carriere }
219d64485e4SEtienne Carriere 
220d64485e4SEtienne Carriere static TEE_Result power_up_safmem(void)
221d64485e4SEtienne Carriere {
2229a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
223d64485e4SEtienne Carriere 
224d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP_MASK,
225d64485e4SEtienne Carriere 		  BSEC_CONF_POWER_UP_MASK);
226d64485e4SEtienne Carriere 
2279a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
228d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2297dfc80abSGatien Chevallier 		if (bsec_status() & BSEC_MODE_PWR)
230d64485e4SEtienne Carriere 			break;
231d64485e4SEtienne Carriere 
2327dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_PWR)
233d64485e4SEtienne Carriere 		return TEE_SUCCESS;
234d64485e4SEtienne Carriere 
235d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
236d64485e4SEtienne Carriere }
237d64485e4SEtienne Carriere 
238d64485e4SEtienne Carriere static TEE_Result power_down_safmem(void)
239d64485e4SEtienne Carriere {
2409a54d484SGatien Chevallier 	uint64_t timeout_ref = 0;
241d64485e4SEtienne Carriere 
242d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, 0, BSEC_CONF_POWER_UP_MASK);
243d64485e4SEtienne Carriere 
2449a54d484SGatien Chevallier 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
245d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2467dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_PWR))
247d64485e4SEtienne Carriere 			break;
248d64485e4SEtienne Carriere 
2497dfc80abSGatien Chevallier 	if (!(bsec_status() & BSEC_MODE_PWR))
250d64485e4SEtienne Carriere 		return TEE_SUCCESS;
251d64485e4SEtienne Carriere 
252d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
253d64485e4SEtienne Carriere }
254d64485e4SEtienne Carriere 
2550042538eSEtienne Carriere TEE_Result stm32_bsec_shadow_register(uint32_t otp_id)
256d64485e4SEtienne Carriere {
257d64485e4SEtienne Carriere 	TEE_Result result = 0;
258d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
259d64485e4SEtienne Carriere 	uint64_t timeout_ref = 0;
260ef9888dcSEtienne Carriere 	bool locked = false;
261d64485e4SEtienne Carriere 
262ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
263ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sr_lock(otp_id, &locked);
264ef9888dcSEtienne Carriere 	if (result)
265ef9888dcSEtienne Carriere 		return result;
266d64485e4SEtienne Carriere 
267ef9888dcSEtienne Carriere 	if (locked)
268ef9888dcSEtienne Carriere 		DMSG("BSEC shadow warning: OTP locked");
269d64485e4SEtienne Carriere 
2707dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
2717dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
2727dfc80abSGatien Chevallier 
273d64485e4SEtienne Carriere 	exceptions = bsec_lock();
274d64485e4SEtienne Carriere 
275d64485e4SEtienne Carriere 	result = power_up_safmem();
276d64485e4SEtienne Carriere 	if (result)
2777b05d514SEtienne Carriere 		goto out;
278d64485e4SEtienne Carriere 
279d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_READ);
280d64485e4SEtienne Carriere 
281d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
282d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
2837dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
284d64485e4SEtienne Carriere 			break;
285d64485e4SEtienne Carriere 
2867dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
2878afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
288d64485e4SEtienne Carriere 	else
2891ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
290d64485e4SEtienne Carriere 
291d64485e4SEtienne Carriere 	power_down_safmem();
292d64485e4SEtienne Carriere 
2937b05d514SEtienne Carriere out:
294d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
295d64485e4SEtienne Carriere 
296d64485e4SEtienne Carriere 	return result;
297d64485e4SEtienne Carriere }
298d64485e4SEtienne Carriere 
299d64485e4SEtienne Carriere TEE_Result stm32_bsec_read_otp(uint32_t *value, uint32_t otp_id)
300d64485e4SEtienne Carriere {
301d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
302d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
303d64485e4SEtienne Carriere 
3047dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3057dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3067dfc80abSGatien Chevallier 
307d64485e4SEtienne Carriere 	*value = io_read32(bsec_base() + BSEC_OTP_DATA_OFF +
308d64485e4SEtienne Carriere 			   (otp_id * sizeof(uint32_t)));
309d64485e4SEtienne Carriere 
3101ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
311d64485e4SEtienne Carriere }
312d64485e4SEtienne Carriere 
313d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_read_otp(uint32_t *otp_value, uint32_t otp_id)
314d64485e4SEtienne Carriere {
315d64485e4SEtienne Carriere 	TEE_Result result = 0;
316d64485e4SEtienne Carriere 
317d64485e4SEtienne Carriere 	result = stm32_bsec_shadow_register(otp_id);
318d64485e4SEtienne Carriere 	if (result) {
319ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Shadowing Error %#"PRIx32, otp_id, result);
320d64485e4SEtienne Carriere 		return result;
321d64485e4SEtienne Carriere 	}
322d64485e4SEtienne Carriere 
323d64485e4SEtienne Carriere 	result = stm32_bsec_read_otp(otp_value, otp_id);
324d64485e4SEtienne Carriere 	if (result)
325ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Read Error %#"PRIx32, otp_id, result);
326d64485e4SEtienne Carriere 
327d64485e4SEtienne Carriere 	return result;
328d64485e4SEtienne Carriere }
329d64485e4SEtienne Carriere 
330d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_otp(uint32_t value, uint32_t otp_id)
331d64485e4SEtienne Carriere {
332d64485e4SEtienne Carriere 	TEE_Result result = 0;
333d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
334d64485e4SEtienne Carriere 	vaddr_t otp_data_base = bsec_base() + BSEC_OTP_DATA_OFF;
335ef9888dcSEtienne Carriere 	bool locked = false;
336d64485e4SEtienne Carriere 
337ef9888dcSEtienne Carriere 	/* Check if write of OTP is locked, informative only */
338ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sw_lock(otp_id, &locked);
339ef9888dcSEtienne Carriere 	if (result)
340ef9888dcSEtienne Carriere 		return result;
341d64485e4SEtienne Carriere 
342ef9888dcSEtienne Carriere 	if (locked)
343ef9888dcSEtienne Carriere 		DMSG("BSEC write warning: OTP locked");
344d64485e4SEtienne Carriere 
3457dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3467dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3477dfc80abSGatien Chevallier 
348d64485e4SEtienne Carriere 	exceptions = bsec_lock();
349d64485e4SEtienne Carriere 
350d64485e4SEtienne Carriere 	io_write32(otp_data_base + (otp_id * sizeof(uint32_t)), value);
351d64485e4SEtienne Carriere 
352d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
353d64485e4SEtienne Carriere 
3541ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
355d64485e4SEtienne Carriere }
356d64485e4SEtienne Carriere 
3570c30f9eaSEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
358d64485e4SEtienne Carriere TEE_Result stm32_bsec_program_otp(uint32_t value, uint32_t otp_id)
359d64485e4SEtienne Carriere {
360d64485e4SEtienne Carriere 	TEE_Result result = 0;
361d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
362ef9888dcSEtienne Carriere 	uint64_t timeout_ref = 0;
363ef9888dcSEtienne Carriere 	bool locked = false;
364d64485e4SEtienne Carriere 
365ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
366ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sp_lock(otp_id, &locked);
367ef9888dcSEtienne Carriere 	if (result)
368ef9888dcSEtienne Carriere 		return result;
369d64485e4SEtienne Carriere 
370ef9888dcSEtienne Carriere 	if (locked)
371ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: OTP locked");
372d64485e4SEtienne Carriere 
373d64485e4SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_OTP_LOCK_OFF) & BIT(BSEC_LOCK_PROGRAM))
374ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: GPLOCK activated");
375d64485e4SEtienne Carriere 
3767dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
3777dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
3787dfc80abSGatien Chevallier 
379d64485e4SEtienne Carriere 	exceptions = bsec_lock();
380d64485e4SEtienne Carriere 
381d64485e4SEtienne Carriere 	result = power_up_safmem();
382d64485e4SEtienne Carriere 	if (result)
3837b05d514SEtienne Carriere 		goto out;
384d64485e4SEtienne Carriere 
385d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_WRDATA_OFF, value);
386d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_WRITE);
387d64485e4SEtienne Carriere 
388d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
389d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
3907dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
391d64485e4SEtienne Carriere 			break;
392d64485e4SEtienne Carriere 
3937dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
3948afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
3957dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
3968afb7c41SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
397d64485e4SEtienne Carriere 	else
3981ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
399d64485e4SEtienne Carriere 
400d64485e4SEtienne Carriere 	power_down_safmem();
401d64485e4SEtienne Carriere 
4027b05d514SEtienne Carriere out:
403d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
404d64485e4SEtienne Carriere 
405d64485e4SEtienne Carriere 	return result;
406d64485e4SEtienne Carriere }
407d64485e4SEtienne Carriere 
408d64485e4SEtienne Carriere TEE_Result stm32_bsec_permanent_lock_otp(uint32_t otp_id)
409d64485e4SEtienne Carriere {
410d64485e4SEtienne Carriere 	TEE_Result result = 0;
411d64485e4SEtienne Carriere 	uint32_t data = 0;
412d64485e4SEtienne Carriere 	uint32_t addr = 0;
413d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
414d64485e4SEtienne Carriere 	vaddr_t base = bsec_base();
415586eee81SEtienne Carriere 	uint64_t timeout_ref = 0;
416c6d2483aSGatien Chevallier 	uint32_t upper_base = otp_upper_base();
417d64485e4SEtienne Carriere 
418d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
419d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
420d64485e4SEtienne Carriere 
421c6d2483aSGatien Chevallier 	/*
422c6d2483aSGatien Chevallier 	 * 2 bits per words for lower OTPs: 2:1 Redundancy
423c6d2483aSGatien Chevallier 	 * 1 bit per word for upper OTPs : ECC support
424c6d2483aSGatien Chevallier 	 * e.g with 32 lower and 64 upper OTPs:
425c6d2483aSGatien Chevallier 	 * OTP word to be    ADDR[6:0]   WRDATA[31:0]
426c6d2483aSGatien Chevallier 	 *     locked
427c6d2483aSGatien Chevallier 	 *       0             0x00      0x0000 0003
428c6d2483aSGatien Chevallier 	 *       1             0x00      0x0000 000C
429c6d2483aSGatien Chevallier 	 *      ...             ...              ...
430c6d2483aSGatien Chevallier 	 *       7             0x00      0x0000 C000
431c6d2483aSGatien Chevallier 	 *       8             0x01      0x0000 0003
432c6d2483aSGatien Chevallier 	 *      ...             ...              ...
433c6d2483aSGatien Chevallier 	 *      31             0x03      0x0000 C000
434c6d2483aSGatien Chevallier 	 *      32             0x04      0x0000 0001
435c6d2483aSGatien Chevallier 	 *      33             0x04      0x0000 0002
436c6d2483aSGatien Chevallier 	 *      95             0x07      0x0000 8000
437c6d2483aSGatien Chevallier 	 */
438c6d2483aSGatien Chevallier 	if (otp_id < upper_base) {
439c6d2483aSGatien Chevallier 		addr = otp_id / 8U;
440c6d2483aSGatien Chevallier 		data = DATA_LOWER_OTP_PERLOCK_BIT << ((otp_id * 2U) & 0xF);
441d64485e4SEtienne Carriere 	} else {
442c6d2483aSGatien Chevallier 		addr = upper_base / 8U + (otp_id - upper_base) / 16U;
443c6d2483aSGatien Chevallier 		data = DATA_UPPER_OTP_PERLOCK_BIT << (otp_id & 0xF);
444d64485e4SEtienne Carriere 	}
445d64485e4SEtienne Carriere 
4467dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4477dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4487dfc80abSGatien Chevallier 
449d64485e4SEtienne Carriere 	exceptions = bsec_lock();
450d64485e4SEtienne Carriere 
451d64485e4SEtienne Carriere 	result = power_up_safmem();
452d64485e4SEtienne Carriere 	if (result)
4537b05d514SEtienne Carriere 		goto out;
454d64485e4SEtienne Carriere 
455d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_WRDATA_OFF, data);
456d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK);
457d64485e4SEtienne Carriere 
458d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
459d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
4607dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
461d64485e4SEtienne Carriere 			break;
462d64485e4SEtienne Carriere 
4637dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
4648afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
4657dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
466d64485e4SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
467d64485e4SEtienne Carriere 	else
4681ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, false /* not-disturbed */);
469d64485e4SEtienne Carriere 
470e090bb5aSGatien Chevallier #ifdef CFG_STM32MP13
471e090bb5aSGatien Chevallier 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_READ | BSEC_LOCK);
472e090bb5aSGatien Chevallier #endif
473e090bb5aSGatien Chevallier 
474d64485e4SEtienne Carriere 	power_down_safmem();
475d64485e4SEtienne Carriere 
4767b05d514SEtienne Carriere out:
477d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
478d64485e4SEtienne Carriere 
479d64485e4SEtienne Carriere 	return result;
480d64485e4SEtienne Carriere }
48138df614fSLionel Debieve #endif /*CFG_STM32_BSEC_WRITE*/
482d64485e4SEtienne Carriere 
483d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_debug_conf(uint32_t value)
484d64485e4SEtienne Carriere {
485d64485e4SEtienne Carriere 	TEE_Result result = TEE_ERROR_GENERIC;
486d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
487d64485e4SEtienne Carriere 
48801a06793SPatrick Delaunay 	assert(!(value & ~BSEC_DEN_ALL_MSK));
48901a06793SPatrick Delaunay 
4907dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
4917dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
4927dfc80abSGatien Chevallier 
493d64485e4SEtienne Carriere 	exceptions = bsec_lock();
494d64485e4SEtienne Carriere 
49501a06793SPatrick Delaunay 	io_clrsetbits32(bsec_base() + BSEC_DEN_OFF, BSEC_DEN_ALL_MSK, value);
496d64485e4SEtienne Carriere 
49701a06793SPatrick Delaunay 	if (stm32_bsec_read_debug_conf() == value)
498d64485e4SEtienne Carriere 		result = TEE_SUCCESS;
499d64485e4SEtienne Carriere 
500d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
501d64485e4SEtienne Carriere 
502d64485e4SEtienne Carriere 	return result;
503d64485e4SEtienne Carriere }
504d64485e4SEtienne Carriere 
505d64485e4SEtienne Carriere uint32_t stm32_bsec_read_debug_conf(void)
506d64485e4SEtienne Carriere {
50701a06793SPatrick Delaunay 	return io_read32(bsec_base() + BSEC_DEN_OFF) & BSEC_DEN_ALL_MSK;
508d64485e4SEtienne Carriere }
509d64485e4SEtienne Carriere 
510ef9888dcSEtienne Carriere static TEE_Result set_bsec_lock(uint32_t otp_id, size_t lock_offset)
511d64485e4SEtienne Carriere {
512d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
513d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
514d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
515d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
516d64485e4SEtienne Carriere 
517ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
518ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
519d64485e4SEtienne Carriere 
5207dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5217dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5227dfc80abSGatien Chevallier 
523d64485e4SEtienne Carriere 	exceptions = bsec_lock();
524d64485e4SEtienne Carriere 
525ef9888dcSEtienne Carriere 	io_write32(lock_addr, otp_mask);
526d64485e4SEtienne Carriere 
527d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
528d64485e4SEtienne Carriere 
529ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
530d64485e4SEtienne Carriere }
531d64485e4SEtienne Carriere 
532ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sr_lock(uint32_t otp_id)
533d64485e4SEtienne Carriere {
534ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SRLOCK_OFF);
535d64485e4SEtienne Carriere }
536d64485e4SEtienne Carriere 
537ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sw_lock(uint32_t otp_id)
538d64485e4SEtienne Carriere {
539ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SWLOCK_OFF);
540d64485e4SEtienne Carriere }
541d64485e4SEtienne Carriere 
542ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sp_lock(uint32_t otp_id)
543d64485e4SEtienne Carriere {
544ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SPLOCK_OFF);
545d64485e4SEtienne Carriere }
546d64485e4SEtienne Carriere 
547ef9888dcSEtienne Carriere static TEE_Result read_bsec_lock(uint32_t otp_id, bool *locked,
548ef9888dcSEtienne Carriere 				 size_t lock_offset)
549d64485e4SEtienne Carriere {
550d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
551d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
552d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
553d64485e4SEtienne Carriere 
554ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
555ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
556ef9888dcSEtienne Carriere 
5577dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
5587dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
5597dfc80abSGatien Chevallier 
560ef9888dcSEtienne Carriere 	*locked = (io_read32(lock_addr) & otp_mask) != 0;
561ef9888dcSEtienne Carriere 
562ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
563d64485e4SEtienne Carriere }
564d64485e4SEtienne Carriere 
565ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sr_lock(uint32_t otp_id, bool *locked)
566d64485e4SEtienne Carriere {
567ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SRLOCK_OFF);
568d64485e4SEtienne Carriere }
569d64485e4SEtienne Carriere 
570ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sw_lock(uint32_t otp_id, bool *locked)
571d64485e4SEtienne Carriere {
572ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SWLOCK_OFF);
573d64485e4SEtienne Carriere }
574d64485e4SEtienne Carriere 
575ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sp_lock(uint32_t otp_id, bool *locked)
576d64485e4SEtienne Carriere {
577ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SPLOCK_OFF);
578d64485e4SEtienne Carriere }
579d64485e4SEtienne Carriere 
580ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_permanent_lock(uint32_t otp_id, bool *locked)
581d64485e4SEtienne Carriere {
582ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_WRLOCK_OFF);
583d64485e4SEtienne Carriere }
584d64485e4SEtienne Carriere 
585890703c3SEtienne Carriere static size_t nsec_access_array_size(void)
586890703c3SEtienne Carriere {
587586eee81SEtienne Carriere 	size_t upper_count = otp_max_id() - otp_upper_base() + 1;
588890703c3SEtienne Carriere 
5898396f62eSGatien Chevallier 	return ROUNDUP_DIV(upper_count, BSEC_BITS_PER_WORD);
590890703c3SEtienne Carriere }
591890703c3SEtienne Carriere 
592890703c3SEtienne Carriere static bool nsec_access_granted(unsigned int index)
593890703c3SEtienne Carriere {
594890703c3SEtienne Carriere 	uint32_t *array = bsec_dev.nsec_access;
595890703c3SEtienne Carriere 
596890703c3SEtienne Carriere 	return array &&
5978396f62eSGatien Chevallier 	       (index / BSEC_BITS_PER_WORD) < nsec_access_array_size() &&
5988396f62eSGatien Chevallier 	       array[index / BSEC_BITS_PER_WORD] &
5998396f62eSGatien Chevallier 	       BIT(index % BSEC_BITS_PER_WORD);
600890703c3SEtienne Carriere }
601890703c3SEtienne Carriere 
6027dfc80abSGatien Chevallier bool stm32_bsec_can_access_otp(uint32_t otp_id)
6037dfc80abSGatien Chevallier {
6047dfc80abSGatien Chevallier 	return (otp_id <= otp_max_id()) && !state_is_invalid_mode();
6057dfc80abSGatien Chevallier }
6067dfc80abSGatien Chevallier 
607d64485e4SEtienne Carriere bool stm32_bsec_nsec_can_access_otp(uint32_t otp_id)
608d64485e4SEtienne Carriere {
609586eee81SEtienne Carriere 	return otp_id < otp_upper_base() ||
610586eee81SEtienne Carriere 	       nsec_access_granted(otp_id - otp_upper_base());
611d64485e4SEtienne Carriere }
612d64485e4SEtienne Carriere 
613*9f007225SPatrick Delaunay /*
614*9f007225SPatrick Delaunay  * struct nvmem_layout - NVMEM cell description
615*9f007225SPatrick Delaunay  * @name: Name of the nvmem node in the DT
616*9f007225SPatrick Delaunay  * @otp_id: BSEC base index for the OTP words
617*9f007225SPatrick Delaunay  * @bit_offset: Bit offset in the OTP word
618*9f007225SPatrick Delaunay  * @bit_len: Bit size of the OTP word
619*9f007225SPatrick Delaunay  */
62093114f2eSGatien Chevallier struct nvmem_layout {
62193114f2eSGatien Chevallier 	char *name;
62293114f2eSGatien Chevallier 	uint32_t otp_id;
623*9f007225SPatrick Delaunay 	uint8_t bit_offset;
62493114f2eSGatien Chevallier 	size_t bit_len;
62593114f2eSGatien Chevallier };
62693114f2eSGatien Chevallier 
62793114f2eSGatien Chevallier static struct nvmem_layout *nvmem_layout;
62893114f2eSGatien Chevallier static size_t nvmem_layout_count;
62993114f2eSGatien Chevallier 
63093114f2eSGatien Chevallier TEE_Result stm32_bsec_find_otp_in_nvmem_layout(const char *name,
63193114f2eSGatien Chevallier 					       uint32_t *otp_id,
632*9f007225SPatrick Delaunay 					       uint8_t *otp_bit_offset,
63393114f2eSGatien Chevallier 					       size_t *otp_bit_len)
63493114f2eSGatien Chevallier {
63593114f2eSGatien Chevallier 	size_t i = 0;
63693114f2eSGatien Chevallier 
63793114f2eSGatien Chevallier 	if (!name)
63893114f2eSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
63993114f2eSGatien Chevallier 
64093114f2eSGatien Chevallier 	for (i = 0; i < nvmem_layout_count; i++) {
64193114f2eSGatien Chevallier 		if (!nvmem_layout[i].name || strcmp(name, nvmem_layout[i].name))
64293114f2eSGatien Chevallier 			continue;
64393114f2eSGatien Chevallier 
64493114f2eSGatien Chevallier 		if (otp_id)
64593114f2eSGatien Chevallier 			*otp_id = nvmem_layout[i].otp_id;
64693114f2eSGatien Chevallier 
647*9f007225SPatrick Delaunay 		if (otp_bit_offset)
648*9f007225SPatrick Delaunay 			*otp_bit_offset = nvmem_layout[i].bit_offset;
649*9f007225SPatrick Delaunay 
65093114f2eSGatien Chevallier 		if (otp_bit_len)
65193114f2eSGatien Chevallier 			*otp_bit_len = nvmem_layout[i].bit_len;
65293114f2eSGatien Chevallier 
653*9f007225SPatrick Delaunay 		DMSG("nvmem[%d] = %s at BSEC word %" PRIu32
654*9f007225SPatrick Delaunay 		     " bits [%" PRIu8 " %zu]",
655*9f007225SPatrick Delaunay 		     i, name, nvmem_layout[i].otp_id,
656*9f007225SPatrick Delaunay 		     nvmem_layout[i].bit_offset, nvmem_layout[i].bit_len);
65793114f2eSGatien Chevallier 
65893114f2eSGatien Chevallier 		return TEE_SUCCESS;
65993114f2eSGatien Chevallier 	}
66093114f2eSGatien Chevallier 
66193114f2eSGatien Chevallier 	DMSG("nvmem %s failed", name);
66293114f2eSGatien Chevallier 
66393114f2eSGatien Chevallier 	return TEE_ERROR_ITEM_NOT_FOUND;
6644fb18124SPatrick Delaunay }
6657dfc80abSGatien Chevallier 
666ac16eac3SJose Quaresma TEE_Result stm32_bsec_get_state(enum stm32_bsec_sec_state *state)
6677dfc80abSGatien Chevallier {
6687dfc80abSGatien Chevallier 	if (!state)
6697dfc80abSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
6707dfc80abSGatien Chevallier 
6717dfc80abSGatien Chevallier 	if (state_is_invalid_mode() || !state_is_secured_mode()) {
6727dfc80abSGatien Chevallier 		*state = BSEC_STATE_INVALID;
6737dfc80abSGatien Chevallier 	} else {
6747dfc80abSGatien Chevallier 		if (state_is_closed_mode())
6757dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_CLOSED;
6767dfc80abSGatien Chevallier 		else
6777dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_OPEN;
6787dfc80abSGatien Chevallier 	}
6797dfc80abSGatien Chevallier 
6807dfc80abSGatien Chevallier 	return TEE_SUCCESS;
68193114f2eSGatien Chevallier }
68293114f2eSGatien Chevallier 
683890703c3SEtienne Carriere static void enable_nsec_access(unsigned int otp_id)
684890703c3SEtienne Carriere {
6858396f62eSGatien Chevallier 	unsigned int idx = (otp_id - otp_upper_base()) / BSEC_BITS_PER_WORD;
686890703c3SEtienne Carriere 
687586eee81SEtienne Carriere 	if (otp_id < otp_upper_base())
688890703c3SEtienne Carriere 		return;
689890703c3SEtienne Carriere 
690890703c3SEtienne Carriere 	if (otp_id > otp_max_id() || stm32_bsec_shadow_register(otp_id))
691890703c3SEtienne Carriere 		panic();
692890703c3SEtienne Carriere 
6938396f62eSGatien Chevallier 	bsec_dev.nsec_access[idx] |= BIT(otp_id % BSEC_BITS_PER_WORD);
694890703c3SEtienne Carriere }
695890703c3SEtienne Carriere 
696890703c3SEtienne Carriere static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
697890703c3SEtienne Carriere {
698890703c3SEtienne Carriere 	int bsec_subnode = 0;
699890703c3SEtienne Carriere 
700890703c3SEtienne Carriere 	bsec_dev.nsec_access = calloc(nsec_access_array_size(),
701890703c3SEtienne Carriere 				      sizeof(*bsec_dev.nsec_access));
702890703c3SEtienne Carriere 	if (!bsec_dev.nsec_access)
703890703c3SEtienne Carriere 		panic();
704890703c3SEtienne Carriere 
705890703c3SEtienne Carriere 	fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
70627a02b1eSGatien Chevallier 		unsigned int reg_offset = 0;
70727a02b1eSGatien Chevallier 		unsigned int reg_size = 0;
708890703c3SEtienne Carriere 		unsigned int otp_id = 0;
709890703c3SEtienne Carriere 		unsigned int i = 0;
710890703c3SEtienne Carriere 		size_t size = 0;
711890703c3SEtienne Carriere 
712f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, bsec_subnode);
713f354a5d8SGatien Chevallier 		reg_size = fdt_reg_size(fdt, bsec_subnode);
714890703c3SEtienne Carriere 
71527a02b1eSGatien Chevallier 		assert(reg_offset != DT_INFO_INVALID_REG &&
71627a02b1eSGatien Chevallier 		       reg_size != DT_INFO_INVALID_REG_SIZE);
717890703c3SEtienne Carriere 
71827a02b1eSGatien Chevallier 		otp_id = reg_offset / sizeof(uint32_t);
719890703c3SEtienne Carriere 
720890703c3SEtienne Carriere 		if (otp_id < STM32MP1_UPPER_OTP_START) {
72127a02b1eSGatien Chevallier 			unsigned int otp_end =
72227a02b1eSGatien Chevallier 				ROUNDUP_DIV(reg_offset + reg_size,
72327a02b1eSGatien Chevallier 					    sizeof(uint32_t));
724890703c3SEtienne Carriere 
725890703c3SEtienne Carriere 			if (otp_end > STM32MP1_UPPER_OTP_START) {
726890703c3SEtienne Carriere 				/*
727890703c3SEtienne Carriere 				 * OTP crosses Lower/Upper boundary, consider
728890703c3SEtienne Carriere 				 * only the upper part.
729890703c3SEtienne Carriere 				 */
730890703c3SEtienne Carriere 				otp_id = STM32MP1_UPPER_OTP_START;
73127a02b1eSGatien Chevallier 				reg_size -= (STM32MP1_UPPER_OTP_START *
73227a02b1eSGatien Chevallier 					     sizeof(uint32_t)) - reg_offset;
73327a02b1eSGatien Chevallier 				reg_offset = STM32MP1_UPPER_OTP_START *
734890703c3SEtienne Carriere 					     sizeof(uint32_t);
735890703c3SEtienne Carriere 
736890703c3SEtienne Carriere 				DMSG("OTP crosses Lower/Upper boundary");
737890703c3SEtienne Carriere 			} else {
738890703c3SEtienne Carriere 				continue;
739890703c3SEtienne Carriere 			}
740890703c3SEtienne Carriere 		}
741890703c3SEtienne Carriere 
7420ec45216SGatien Chevallier 		/* Handle different kinds of non-secure accesses */
7430ec45216SGatien Chevallier 		if (fdt_getprop(fdt, bsec_subnode,
7440ec45216SGatien Chevallier 				"st,non-secure-otp-provisioning", NULL)) {
7450ec45216SGatien Chevallier 			bool locked = false;
7460ec45216SGatien Chevallier 			bool locked_2 = false;
7470ec45216SGatien Chevallier 
7480ec45216SGatien Chevallier 			/* Check if write of OTP is locked */
7490ec45216SGatien Chevallier 			if (stm32_bsec_read_permanent_lock(otp_id, &locked))
7500ec45216SGatien Chevallier 				panic("Cannot read permanent lock");
7510ec45216SGatien Chevallier 
7520ec45216SGatien Chevallier 			/*
7530ec45216SGatien Chevallier 			 * Check if fuses of the subnode
7540ec45216SGatien Chevallier 			 * have the same lock status
7550ec45216SGatien Chevallier 			 */
7560ec45216SGatien Chevallier 			for (i = 1; i < (reg_size / sizeof(uint32_t)); i++) {
7570ec45216SGatien Chevallier 				if (stm32_bsec_read_permanent_lock(otp_id + i,
7580ec45216SGatien Chevallier 								   &locked_2))
7590ec45216SGatien Chevallier 					panic("Cannot read permanent lock");
7600ec45216SGatien Chevallier 
7610ec45216SGatien Chevallier 				if (locked != locked_2) {
7620ec45216SGatien Chevallier 					EMSG("Inconsistent status OTP ID %u",
7630ec45216SGatien Chevallier 					     otp_id + i);
7640ec45216SGatien Chevallier 					locked = true;
7650ec45216SGatien Chevallier 				}
7660ec45216SGatien Chevallier 			}
7670ec45216SGatien Chevallier 
7680ec45216SGatien Chevallier 			if (locked) {
7690ec45216SGatien Chevallier 				DMSG("BSEC: OTP locked");
770890703c3SEtienne Carriere 				continue;
7710ec45216SGatien Chevallier 			}
7720ec45216SGatien Chevallier 		} else if (!fdt_getprop(fdt, bsec_subnode, "st,non-secure-otp",
7730ec45216SGatien Chevallier 					NULL)) {
7740ec45216SGatien Chevallier 			continue;
7750ec45216SGatien Chevallier 		}
776890703c3SEtienne Carriere 
77727a02b1eSGatien Chevallier 		if ((reg_offset % sizeof(uint32_t)) ||
77827a02b1eSGatien Chevallier 		    (reg_size % sizeof(uint32_t)))
779890703c3SEtienne Carriere 			panic("Unaligned non-secure OTP");
780890703c3SEtienne Carriere 
78127a02b1eSGatien Chevallier 		size = reg_size / sizeof(uint32_t);
782890703c3SEtienne Carriere 
7837dfc80abSGatien Chevallier 		if (otp_id + size > OTP_MAX_SIZE)
784890703c3SEtienne Carriere 			panic("OTP range oversized");
785890703c3SEtienne Carriere 
786890703c3SEtienne Carriere 		for (i = otp_id; i < otp_id + size; i++)
787890703c3SEtienne Carriere 			enable_nsec_access(i);
788890703c3SEtienne Carriere 	}
789890703c3SEtienne Carriere }
790890703c3SEtienne Carriere 
79193114f2eSGatien Chevallier static void save_dt_nvmem_layout(void *fdt, int bsec_node)
79293114f2eSGatien Chevallier {
79393114f2eSGatien Chevallier 	int cell_max = 0;
79493114f2eSGatien Chevallier 	int cell_cnt = 0;
79593114f2eSGatien Chevallier 	int node = 0;
79693114f2eSGatien Chevallier 
79793114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node)
79893114f2eSGatien Chevallier 		cell_max++;
79993114f2eSGatien Chevallier 	if (!cell_max)
80093114f2eSGatien Chevallier 		return;
80193114f2eSGatien Chevallier 
80293114f2eSGatien Chevallier 	nvmem_layout = calloc(cell_max, sizeof(*nvmem_layout));
80393114f2eSGatien Chevallier 	if (!nvmem_layout)
80493114f2eSGatien Chevallier 		panic();
80593114f2eSGatien Chevallier 
80693114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node) {
80793114f2eSGatien Chevallier 		unsigned int reg_offset = 0;
80893114f2eSGatien Chevallier 		unsigned int reg_length = 0;
80993114f2eSGatien Chevallier 		const char *string = NULL;
81093114f2eSGatien Chevallier 		const char *s = NULL;
81193114f2eSGatien Chevallier 		int len = 0;
81293114f2eSGatien Chevallier 		struct nvmem_layout *layout_cell = &nvmem_layout[cell_cnt];
813*9f007225SPatrick Delaunay 		uint32_t bits[2] = { };
81493114f2eSGatien Chevallier 
81593114f2eSGatien Chevallier 		string = fdt_get_name(fdt, node, &len);
81693114f2eSGatien Chevallier 		if (!string || !len)
81793114f2eSGatien Chevallier 			continue;
81893114f2eSGatien Chevallier 
819f354a5d8SGatien Chevallier 		reg_offset = fdt_reg_base_address(fdt, node);
820f354a5d8SGatien Chevallier 		reg_length = fdt_reg_size(fdt, node);
82193114f2eSGatien Chevallier 
82293114f2eSGatien Chevallier 		if (reg_offset == DT_INFO_INVALID_REG ||
82393114f2eSGatien Chevallier 		    reg_length == DT_INFO_INVALID_REG_SIZE) {
82493114f2eSGatien Chevallier 			DMSG("Malformed nvmem %s: ignored", string);
82593114f2eSGatien Chevallier 			continue;
82693114f2eSGatien Chevallier 		}
82793114f2eSGatien Chevallier 
82893114f2eSGatien Chevallier 		layout_cell->otp_id = reg_offset / sizeof(uint32_t);
829*9f007225SPatrick Delaunay 		layout_cell->bit_offset = (reg_offset % sizeof(uint32_t)) *
830*9f007225SPatrick Delaunay 					  CHAR_BIT;
83193114f2eSGatien Chevallier 		layout_cell->bit_len = reg_length * CHAR_BIT;
83293114f2eSGatien Chevallier 
833*9f007225SPatrick Delaunay 		if (!fdt_read_uint32_array(fdt, node, "bits", bits, 2)) {
834*9f007225SPatrick Delaunay 			layout_cell->bit_offset += bits[0];
835*9f007225SPatrick Delaunay 			layout_cell->bit_len = bits[1];
836*9f007225SPatrick Delaunay 		}
837*9f007225SPatrick Delaunay 
83893114f2eSGatien Chevallier 		s = strchr(string, '@');
83993114f2eSGatien Chevallier 		if (s)
84093114f2eSGatien Chevallier 			len = s - string;
84193114f2eSGatien Chevallier 
84293114f2eSGatien Chevallier 		layout_cell->name = strndup(string, len);
84393114f2eSGatien Chevallier 		if (!layout_cell->name)
84493114f2eSGatien Chevallier 			panic();
84593114f2eSGatien Chevallier 		cell_cnt++;
846*9f007225SPatrick Delaunay 		DMSG("nvmem[%d] = %s at BSEC word %" PRIu32
847*9f007225SPatrick Delaunay 		     " bits [%" PRIu8 " %zu]",
848*9f007225SPatrick Delaunay 		     cell_cnt, layout_cell->name, layout_cell->otp_id,
849*9f007225SPatrick Delaunay 		     layout_cell->bit_offset, layout_cell->bit_len);
85093114f2eSGatien Chevallier 	}
85193114f2eSGatien Chevallier 
85293114f2eSGatien Chevallier 	if (cell_cnt != cell_max) {
85393114f2eSGatien Chevallier 		nvmem_layout = realloc(nvmem_layout,
85493114f2eSGatien Chevallier 				       cell_cnt * sizeof(*nvmem_layout));
85593114f2eSGatien Chevallier 		if (!nvmem_layout)
85693114f2eSGatien Chevallier 			panic();
85793114f2eSGatien Chevallier 	}
85893114f2eSGatien Chevallier 
85993114f2eSGatien Chevallier 	nvmem_layout_count = cell_cnt;
86093114f2eSGatien Chevallier }
86193114f2eSGatien Chevallier 
862890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
863890703c3SEtienne Carriere {
864890703c3SEtienne Carriere 	void *fdt = NULL;
865890703c3SEtienne Carriere 	int node = 0;
866890703c3SEtienne Carriere 	struct dt_node_info bsec_info = { };
867890703c3SEtienne Carriere 
868890703c3SEtienne Carriere 	fdt = get_embedded_dt();
869e090bb5aSGatien Chevallier 	node = fdt_node_offset_by_compatible(fdt, 0, DT_BSEC_COMPAT);
870890703c3SEtienne Carriere 	if (node < 0)
871890703c3SEtienne Carriere 		panic();
872890703c3SEtienne Carriere 
873f354a5d8SGatien Chevallier 	fdt_fill_device_info(fdt, &bsec_info, node);
874890703c3SEtienne Carriere 
875890703c3SEtienne Carriere 	if (bsec_info.reg != bsec_dev.base.pa ||
876890703c3SEtienne Carriere 	    !(bsec_info.status & DT_STATUS_OK_SEC))
877890703c3SEtienne Carriere 		panic();
878890703c3SEtienne Carriere 
879890703c3SEtienne Carriere 	bsec_dt_otp_nsec_access(fdt, node);
88093114f2eSGatien Chevallier 
88193114f2eSGatien Chevallier 	save_dt_nvmem_layout(fdt, node);
882890703c3SEtienne Carriere }
883890703c3SEtienne Carriere 
884d6df31b0SGatien Chevallier static TEE_Result bsec_pm(enum pm_op op, uint32_t pm_hint __unused,
885d6df31b0SGatien Chevallier 			  const struct pm_callback_handle *hdl __unused)
886d6df31b0SGatien Chevallier {
887d6df31b0SGatien Chevallier 	static uint32_t debug_conf;
888d6df31b0SGatien Chevallier 
889d6df31b0SGatien Chevallier 	assert(op == PM_OP_SUSPEND || op == PM_OP_RESUME);
890d6df31b0SGatien Chevallier 
891d6df31b0SGatien Chevallier 	if (op == PM_OP_SUSPEND)
892d6df31b0SGatien Chevallier 		debug_conf = stm32_bsec_read_debug_conf();
893d6df31b0SGatien Chevallier 	else
894d6df31b0SGatien Chevallier 		stm32_bsec_write_debug_conf(debug_conf);
895d6df31b0SGatien Chevallier 
896d6df31b0SGatien Chevallier 	return TEE_SUCCESS;
897d6df31b0SGatien Chevallier }
898d6df31b0SGatien Chevallier DECLARE_KEEP_PAGER(bsec_pm);
899d6df31b0SGatien Chevallier 
900d64485e4SEtienne Carriere static TEE_Result initialize_bsec(void)
901d64485e4SEtienne Carriere {
9027994d842SEtienne Carriere 	struct stm32_bsec_static_cfg cfg = { };
903d64485e4SEtienne Carriere 
904d64485e4SEtienne Carriere 	stm32mp_get_bsec_static_cfg(&cfg);
905d64485e4SEtienne Carriere 
906d64485e4SEtienne Carriere 	bsec_dev.base.pa = cfg.base;
907d64485e4SEtienne Carriere 	bsec_dev.upper_base = cfg.upper_start;
908d64485e4SEtienne Carriere 	bsec_dev.max_id = cfg.max_id;
909d64485e4SEtienne Carriere 
9107dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
9117dfc80abSGatien Chevallier 		panic();
9127dfc80abSGatien Chevallier 
913890703c3SEtienne Carriere 	initialize_bsec_from_dt();
914890703c3SEtienne Carriere 
915d6df31b0SGatien Chevallier 	register_pm_core_service_cb(bsec_pm, NULL, "stm32_bsec");
916d6df31b0SGatien Chevallier 
917d64485e4SEtienne Carriere 	return TEE_SUCCESS;
918d64485e4SEtienne Carriere }
919d64485e4SEtienne Carriere 
9208c8316dbSEtienne Carriere early_init(initialize_bsec);
921