xref: /optee_os/core/drivers/stm32_bsec.c (revision 7dfc80ab580541750da0935c623efb5151eea408)
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>
13d64485e4SEtienne Carriere #include <kernel/spinlock.h>
14a2fc83d1SJerome Forissier #include <libfdt.h>
15d64485e4SEtienne Carriere #include <limits.h>
16d64485e4SEtienne Carriere #include <mm/core_memprot.h>
17d64485e4SEtienne Carriere #include <platform_config.h>
18d64485e4SEtienne Carriere #include <stm32_util.h>
19ef9888dcSEtienne Carriere #include <string.h>
20ef9888dcSEtienne Carriere #include <tee_api_defines.h>
21d64485e4SEtienne Carriere #include <types_ext.h>
22d64485e4SEtienne Carriere #include <util.h>
23d64485e4SEtienne Carriere 
24d64485e4SEtienne Carriere #define BSEC_OTP_MASK			GENMASK_32(4, 0)
254bbd20f1SGatien Chevallier #define BSEC_OTP_BANK_SHIFT		U(5)
26d64485e4SEtienne Carriere 
27d64485e4SEtienne Carriere /* Permanent lock bitmasks */
284bbd20f1SGatien Chevallier #define DATA_LOWER_OTP_PERLOCK_BIT	U(3)
294bbd20f1SGatien Chevallier #define DATA_UPPER_OTP_PERLOCK_BIT	U(1)
30d64485e4SEtienne Carriere 
31d64485e4SEtienne Carriere /* BSEC register offset */
324bbd20f1SGatien Chevallier #define BSEC_OTP_CONF_OFF		U(0x000)
334bbd20f1SGatien Chevallier #define BSEC_OTP_CTRL_OFF		U(0x004)
344bbd20f1SGatien Chevallier #define BSEC_OTP_WRDATA_OFF		U(0x008)
354bbd20f1SGatien Chevallier #define BSEC_OTP_STATUS_OFF		U(0x00C)
364bbd20f1SGatien Chevallier #define BSEC_OTP_LOCK_OFF		U(0x010)
374bbd20f1SGatien Chevallier #define BSEC_DEN_OFF			U(0x014)
384bbd20f1SGatien Chevallier #define BSEC_FEN_OFF			U(0x018)
394bbd20f1SGatien Chevallier #define BSEC_DISTURBED_OFF		U(0x01C)
404bbd20f1SGatien Chevallier #define BSEC_DISTURBED1_OFF		U(0x020)
414bbd20f1SGatien Chevallier #define BSEC_DISTURBED2_OFF		U(0x024)
424bbd20f1SGatien Chevallier #define BSEC_ERROR_OFF			U(0x034)
434bbd20f1SGatien Chevallier #define BSEC_ERROR1_OFF			U(0x038)
444bbd20f1SGatien Chevallier #define BSEC_ERROR2_OFF			U(0x03C)
454bbd20f1SGatien Chevallier #define BSEC_WRLOCK_OFF			U(0x04C)
464bbd20f1SGatien Chevallier #define BSEC_WRLOCK1_OFF		U(0x050)
474bbd20f1SGatien Chevallier #define BSEC_WRLOCK2_OFF		U(0x054)
484bbd20f1SGatien Chevallier #define BSEC_SPLOCK_OFF			U(0x064)
494bbd20f1SGatien Chevallier #define BSEC_SPLOCK1_OFF		U(0x068)
504bbd20f1SGatien Chevallier #define BSEC_SPLOCK2_OFF		U(0x06C)
514bbd20f1SGatien Chevallier #define BSEC_SWLOCK_OFF			U(0x07C)
524bbd20f1SGatien Chevallier #define BSEC_SWLOCK1_OFF		U(0x080)
534bbd20f1SGatien Chevallier #define BSEC_SWLOCK2_OFF		U(0x084)
544bbd20f1SGatien Chevallier #define BSEC_SRLOCK_OFF			U(0x094)
554bbd20f1SGatien Chevallier #define BSEC_SRLOCK1_OFF		U(0x098)
564bbd20f1SGatien Chevallier #define BSEC_SRLOCK2_OFF		U(0x09C)
574bbd20f1SGatien Chevallier #define BSEC_JTAG_IN_OFF		U(0x0AC)
584bbd20f1SGatien Chevallier #define BSEC_JTAG_OUT_OFF		U(0x0B0)
594bbd20f1SGatien Chevallier #define BSEC_SCRATCH_OFF		U(0x0B4)
604bbd20f1SGatien Chevallier #define BSEC_OTP_DATA_OFF		U(0x200)
614bbd20f1SGatien Chevallier #define BSEC_IPHW_CFG_OFF		U(0xFF0)
624bbd20f1SGatien Chevallier #define BSEC_IPVR_OFF			U(0xFF4)
634bbd20f1SGatien Chevallier #define BSEC_IP_ID_OFF			U(0xFF8)
644bbd20f1SGatien Chevallier #define BSEC_IP_MAGIC_ID_OFF		U(0xFFC)
65d64485e4SEtienne Carriere 
66d64485e4SEtienne Carriere /* BSEC_CONFIGURATION Register */
67d64485e4SEtienne Carriere #define BSEC_CONF_POWER_UP_MASK		BIT(0)
684bbd20f1SGatien Chevallier #define BSEC_CONF_POWER_UP_SHIFT	U(0)
69d64485e4SEtienne Carriere #define BSEC_CONF_FRQ_MASK		GENMASK_32(2, 1)
704bbd20f1SGatien Chevallier #define BSEC_CONF_FRQ_SHIFT		U(1)
71d64485e4SEtienne Carriere #define BSEC_CONF_PRG_WIDTH_MASK	GENMASK_32(6, 3)
724bbd20f1SGatien Chevallier #define BSEC_CONF_PRG_WIDTH_SHIFT	U(3)
73d64485e4SEtienne Carriere #define BSEC_CONF_TREAD_MASK		GENMASK_32(8, 7)
744bbd20f1SGatien Chevallier #define BSEC_CONF_TREAD_SHIFT		U(7)
75d64485e4SEtienne Carriere 
76d64485e4SEtienne Carriere /* BSEC_CONTROL Register */
774bbd20f1SGatien Chevallier #define BSEC_READ			U(0x000)
784bbd20f1SGatien Chevallier #define BSEC_WRITE			U(0x100)
794bbd20f1SGatien Chevallier #define BSEC_LOCK			U(0x200)
80d64485e4SEtienne Carriere 
81d64485e4SEtienne Carriere /* BSEC_STATUS Register */
82*7dfc80abSGatien Chevallier #define BSEC_MODE_SECURED		BIT(0)
83*7dfc80abSGatien Chevallier #define BSEC_MODE_INVALID		BIT(2)
84*7dfc80abSGatien Chevallier #define BSEC_MODE_BUSY			BIT(3)
85*7dfc80abSGatien Chevallier #define BSEC_MODE_PROGFAIL		BIT(4)
86*7dfc80abSGatien Chevallier #define BSEC_MODE_PWR			BIT(5)
87d64485e4SEtienne Carriere 
88d64485e4SEtienne Carriere /*
89d64485e4SEtienne Carriere  * OTP Lock services definition
90d64485e4SEtienne Carriere  * Value must corresponding to the bit position in the register
91d64485e4SEtienne Carriere  */
924bbd20f1SGatien Chevallier #define BSEC_LOCK_UPPER_OTP		U(0x00)
934bbd20f1SGatien Chevallier #define BSEC_LOCK_DEBUG			U(0x02)
944bbd20f1SGatien Chevallier #define BSEC_LOCK_PROGRAM		U(0x04)
95d64485e4SEtienne Carriere 
96d64485e4SEtienne Carriere /* Timeout when polling on status */
974bbd20f1SGatien Chevallier #define BSEC_TIMEOUT_US			U(10000)
98890703c3SEtienne Carriere 
99d64485e4SEtienne Carriere struct bsec_dev {
100d64485e4SEtienne Carriere 	struct io_pa_va base;
101d64485e4SEtienne Carriere 	unsigned int upper_base;
102d64485e4SEtienne Carriere 	unsigned int max_id;
103890703c3SEtienne Carriere 	uint32_t *nsec_access;
104d64485e4SEtienne Carriere };
105d64485e4SEtienne Carriere 
106d64485e4SEtienne Carriere /* Only 1 instance of BSEC is expected per platform */
107d64485e4SEtienne Carriere static struct bsec_dev bsec_dev;
108d64485e4SEtienne Carriere 
109d64485e4SEtienne Carriere /* BSEC access protection */
110d64485e4SEtienne Carriere static unsigned int lock = SPINLOCK_UNLOCK;
111d64485e4SEtienne Carriere 
112d64485e4SEtienne Carriere static uint32_t bsec_lock(void)
113d64485e4SEtienne Carriere {
114d64485e4SEtienne Carriere 	return may_spin_lock(&lock);
115d64485e4SEtienne Carriere }
116d64485e4SEtienne Carriere 
117d64485e4SEtienne Carriere static void bsec_unlock(uint32_t exceptions)
118d64485e4SEtienne Carriere {
119d64485e4SEtienne Carriere 	may_spin_unlock(&lock, exceptions);
120d64485e4SEtienne Carriere }
121d64485e4SEtienne Carriere 
122d64485e4SEtienne Carriere static uint32_t otp_max_id(void)
123d64485e4SEtienne Carriere {
124d64485e4SEtienne Carriere 	return bsec_dev.max_id;
125d64485e4SEtienne Carriere }
126d64485e4SEtienne Carriere 
127586eee81SEtienne Carriere static uint32_t otp_upper_base(void)
128586eee81SEtienne Carriere {
129586eee81SEtienne Carriere 	return bsec_dev.upper_base;
130586eee81SEtienne Carriere }
131586eee81SEtienne Carriere 
132d64485e4SEtienne Carriere static uint32_t otp_bank_offset(uint32_t otp_id)
133d64485e4SEtienne Carriere {
134d64485e4SEtienne Carriere 	assert(otp_id <= otp_max_id());
135d64485e4SEtienne Carriere 
136d64485e4SEtienne Carriere 	return ((otp_id & ~BSEC_OTP_MASK) >> BSEC_OTP_BANK_SHIFT) *
137d64485e4SEtienne Carriere 		sizeof(uint32_t);
138d64485e4SEtienne Carriere }
139d64485e4SEtienne Carriere 
140d64485e4SEtienne Carriere static vaddr_t bsec_base(void)
141d64485e4SEtienne Carriere {
142c2e4eb43SAnton Rybakov 	return io_pa_or_va_secure(&bsec_dev.base, BSEC_IP_MAGIC_ID_OFF + 1);
143d64485e4SEtienne Carriere }
144d64485e4SEtienne Carriere 
145d64485e4SEtienne Carriere static uint32_t bsec_status(void)
146d64485e4SEtienne Carriere {
147d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_OTP_STATUS_OFF);
148d64485e4SEtienne Carriere }
149d64485e4SEtienne Carriere 
150*7dfc80abSGatien Chevallier static bool state_is_invalid_mode(void)
151*7dfc80abSGatien Chevallier {
152*7dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_INVALID;
153*7dfc80abSGatien Chevallier }
154*7dfc80abSGatien Chevallier 
155*7dfc80abSGatien Chevallier static bool state_is_secured_mode(void)
156*7dfc80abSGatien Chevallier {
157*7dfc80abSGatien Chevallier 	return bsec_status() & BSEC_MODE_SECURED;
158*7dfc80abSGatien Chevallier }
159*7dfc80abSGatien Chevallier 
160*7dfc80abSGatien Chevallier static bool state_is_closed_mode(void)
161*7dfc80abSGatien Chevallier {
162*7dfc80abSGatien Chevallier 	uint32_t close_mode = 0;
163*7dfc80abSGatien Chevallier 
164*7dfc80abSGatien Chevallier 	if (stm32_bsec_read_otp(&close_mode, CFG0_OTP))
165*7dfc80abSGatien Chevallier 		panic("Unable to read OTP");
166*7dfc80abSGatien Chevallier 
167*7dfc80abSGatien Chevallier 	return close_mode & CFG0_OTP_CLOSED_DEVICE;
168*7dfc80abSGatien Chevallier }
169*7dfc80abSGatien Chevallier 
1701ac4ea14SEtienne Carriere /*
1711ac4ea14SEtienne Carriere  * Check that BSEC interface does not report an error
1721ac4ea14SEtienne Carriere  * @otp_id : OTP number
1731ac4ea14SEtienne Carriere  * @check_disturbed: check only error (false) or all sources (true)
1741ac4ea14SEtienne Carriere  * Return a TEE_Result compliant value
1751ac4ea14SEtienne Carriere  */
1761ac4ea14SEtienne Carriere static TEE_Result check_no_error(uint32_t otp_id, bool check_disturbed)
177d64485e4SEtienne Carriere {
178d64485e4SEtienne Carriere 	uint32_t bit = BIT(otp_id & BSEC_OTP_MASK);
179d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
180d64485e4SEtienne Carriere 
1811ac4ea14SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_ERROR_OFF + bank) & bit)
182d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
183d64485e4SEtienne Carriere 
1841ac4ea14SEtienne Carriere 	if (check_disturbed &&
1851ac4ea14SEtienne Carriere 	    io_read32(bsec_base() + BSEC_DISTURBED_OFF + bank) & bit)
186d64485e4SEtienne Carriere 		return TEE_ERROR_GENERIC;
187d64485e4SEtienne Carriere 
188d64485e4SEtienne Carriere 	return TEE_SUCCESS;
189d64485e4SEtienne Carriere }
190d64485e4SEtienne Carriere 
191d64485e4SEtienne Carriere static TEE_Result power_up_safmem(void)
192d64485e4SEtienne Carriere {
193d64485e4SEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
194d64485e4SEtienne Carriere 
195d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, BSEC_CONF_POWER_UP_MASK,
196d64485e4SEtienne Carriere 		  BSEC_CONF_POWER_UP_MASK);
197d64485e4SEtienne Carriere 
198d64485e4SEtienne Carriere 	/*
199d64485e4SEtienne Carriere 	 * If a timeout is detected, test the condition again to consider
200d64485e4SEtienne Carriere 	 * cases where timeout is due to the executing TEE thread rescheduling.
201d64485e4SEtienne Carriere 	 */
202d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
203*7dfc80abSGatien Chevallier 		if (bsec_status() & BSEC_MODE_PWR)
204d64485e4SEtienne Carriere 			break;
205d64485e4SEtienne Carriere 
206*7dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_PWR)
207d64485e4SEtienne Carriere 		return TEE_SUCCESS;
208d64485e4SEtienne Carriere 
209d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
210d64485e4SEtienne Carriere }
211d64485e4SEtienne Carriere 
212d64485e4SEtienne Carriere static TEE_Result power_down_safmem(void)
213d64485e4SEtienne Carriere {
214d64485e4SEtienne Carriere 	uint64_t timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
215d64485e4SEtienne Carriere 
216d64485e4SEtienne Carriere 	io_mask32(bsec_base() + BSEC_OTP_CONF_OFF, 0, BSEC_CONF_POWER_UP_MASK);
217d64485e4SEtienne Carriere 
218d64485e4SEtienne Carriere 	/*
219d64485e4SEtienne Carriere 	 * If a timeout is detected, test the condition again to consider
220d64485e4SEtienne Carriere 	 * cases where timeout is due to the executing TEE thread rescheduling.
221d64485e4SEtienne Carriere 	 */
222d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
223*7dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_PWR))
224d64485e4SEtienne Carriere 			break;
225d64485e4SEtienne Carriere 
226*7dfc80abSGatien Chevallier 	if (!(bsec_status() & BSEC_MODE_PWR))
227d64485e4SEtienne Carriere 		return TEE_SUCCESS;
228d64485e4SEtienne Carriere 
229d64485e4SEtienne Carriere 	return TEE_ERROR_GENERIC;
230d64485e4SEtienne Carriere }
231d64485e4SEtienne Carriere 
232d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_register(uint32_t otp_id)
233d64485e4SEtienne Carriere {
234d64485e4SEtienne Carriere 	TEE_Result result = 0;
235d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
236d64485e4SEtienne Carriere 	uint64_t timeout_ref = 0;
237ef9888dcSEtienne Carriere 	bool locked = false;
238d64485e4SEtienne Carriere 
239ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
240ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sr_lock(otp_id, &locked);
241ef9888dcSEtienne Carriere 	if (result)
242ef9888dcSEtienne Carriere 		return result;
243d64485e4SEtienne Carriere 
244ef9888dcSEtienne Carriere 	if (locked)
245ef9888dcSEtienne Carriere 		DMSG("BSEC shadow warning: OTP locked");
246d64485e4SEtienne Carriere 
247*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
248*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
249*7dfc80abSGatien Chevallier 
250d64485e4SEtienne Carriere 	exceptions = bsec_lock();
251d64485e4SEtienne Carriere 
252d64485e4SEtienne Carriere 	result = power_up_safmem();
253d64485e4SEtienne Carriere 	if (result)
2547b05d514SEtienne Carriere 		goto out;
255d64485e4SEtienne Carriere 
256d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_READ);
257d64485e4SEtienne Carriere 
258d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
259d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
260*7dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
261d64485e4SEtienne Carriere 			break;
262d64485e4SEtienne Carriere 
263*7dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
2648afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
265d64485e4SEtienne Carriere 	else
2661ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
267d64485e4SEtienne Carriere 
268d64485e4SEtienne Carriere 	power_down_safmem();
269d64485e4SEtienne Carriere 
2707b05d514SEtienne Carriere out:
271d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
272d64485e4SEtienne Carriere 
273d64485e4SEtienne Carriere 	return result;
274d64485e4SEtienne Carriere }
275d64485e4SEtienne Carriere 
276d64485e4SEtienne Carriere TEE_Result stm32_bsec_read_otp(uint32_t *value, uint32_t otp_id)
277d64485e4SEtienne Carriere {
278d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
279d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
280d64485e4SEtienne Carriere 
281*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
282*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
283*7dfc80abSGatien Chevallier 
284d64485e4SEtienne Carriere 	*value = io_read32(bsec_base() + BSEC_OTP_DATA_OFF +
285d64485e4SEtienne Carriere 			   (otp_id * sizeof(uint32_t)));
286d64485e4SEtienne Carriere 
2871ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
288d64485e4SEtienne Carriere }
289d64485e4SEtienne Carriere 
290d64485e4SEtienne Carriere TEE_Result stm32_bsec_shadow_read_otp(uint32_t *otp_value, uint32_t otp_id)
291d64485e4SEtienne Carriere {
292d64485e4SEtienne Carriere 	TEE_Result result = 0;
293d64485e4SEtienne Carriere 
294d64485e4SEtienne Carriere 	result = stm32_bsec_shadow_register(otp_id);
295d64485e4SEtienne Carriere 	if (result) {
296ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Shadowing Error %#"PRIx32, otp_id, result);
297d64485e4SEtienne Carriere 		return result;
298d64485e4SEtienne Carriere 	}
299d64485e4SEtienne Carriere 
300d64485e4SEtienne Carriere 	result = stm32_bsec_read_otp(otp_value, otp_id);
301d64485e4SEtienne Carriere 	if (result)
302ef9888dcSEtienne Carriere 		EMSG("BSEC %"PRIu32" Read Error %#"PRIx32, otp_id, result);
303d64485e4SEtienne Carriere 
304d64485e4SEtienne Carriere 	return result;
305d64485e4SEtienne Carriere }
306d64485e4SEtienne Carriere 
307d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_otp(uint32_t value, uint32_t otp_id)
308d64485e4SEtienne Carriere {
309d64485e4SEtienne Carriere 	TEE_Result result = 0;
310d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
311d64485e4SEtienne Carriere 	vaddr_t otp_data_base = bsec_base() + BSEC_OTP_DATA_OFF;
312ef9888dcSEtienne Carriere 	bool locked = false;
313d64485e4SEtienne Carriere 
314ef9888dcSEtienne Carriere 	/* Check if write of OTP is locked, informative only */
315ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sw_lock(otp_id, &locked);
316ef9888dcSEtienne Carriere 	if (result)
317ef9888dcSEtienne Carriere 		return result;
318d64485e4SEtienne Carriere 
319ef9888dcSEtienne Carriere 	if (locked)
320ef9888dcSEtienne Carriere 		DMSG("BSEC write warning: OTP locked");
321d64485e4SEtienne Carriere 
322*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
323*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
324*7dfc80abSGatien Chevallier 
325d64485e4SEtienne Carriere 	exceptions = bsec_lock();
326d64485e4SEtienne Carriere 
327d64485e4SEtienne Carriere 	io_write32(otp_data_base + (otp_id * sizeof(uint32_t)), value);
328d64485e4SEtienne Carriere 
329d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
330d64485e4SEtienne Carriere 
3311ac4ea14SEtienne Carriere 	return TEE_SUCCESS;
332d64485e4SEtienne Carriere }
333d64485e4SEtienne Carriere 
3340c30f9eaSEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
335d64485e4SEtienne Carriere TEE_Result stm32_bsec_program_otp(uint32_t value, uint32_t otp_id)
336d64485e4SEtienne Carriere {
337d64485e4SEtienne Carriere 	TEE_Result result = 0;
338d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
339ef9888dcSEtienne Carriere 	uint64_t timeout_ref = 0;
340ef9888dcSEtienne Carriere 	bool locked = false;
341d64485e4SEtienne Carriere 
342ef9888dcSEtienne Carriere 	/* Check if shadowing of OTP is locked, informative only */
343ef9888dcSEtienne Carriere 	result = stm32_bsec_read_sp_lock(otp_id, &locked);
344ef9888dcSEtienne Carriere 	if (result)
345ef9888dcSEtienne Carriere 		return result;
346d64485e4SEtienne Carriere 
347ef9888dcSEtienne Carriere 	if (locked)
348ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: OTP locked");
349d64485e4SEtienne Carriere 
350d64485e4SEtienne Carriere 	if (io_read32(bsec_base() + BSEC_OTP_LOCK_OFF) & BIT(BSEC_LOCK_PROGRAM))
351ef9888dcSEtienne Carriere 		DMSG("BSEC program warning: GPLOCK activated");
352d64485e4SEtienne Carriere 
353*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
354*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
355*7dfc80abSGatien Chevallier 
356d64485e4SEtienne Carriere 	exceptions = bsec_lock();
357d64485e4SEtienne Carriere 
358d64485e4SEtienne Carriere 	result = power_up_safmem();
359d64485e4SEtienne Carriere 	if (result)
3607b05d514SEtienne Carriere 		goto out;
361d64485e4SEtienne Carriere 
362d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_WRDATA_OFF, value);
363d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_OTP_CTRL_OFF, otp_id | BSEC_WRITE);
364d64485e4SEtienne Carriere 
365d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
366d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
367*7dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
368d64485e4SEtienne Carriere 			break;
369d64485e4SEtienne Carriere 
370*7dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
3718afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
372*7dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
3738afb7c41SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
374d64485e4SEtienne Carriere 	else
3751ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, true /* check-disturbed */);
376d64485e4SEtienne Carriere 
377d64485e4SEtienne Carriere 	power_down_safmem();
378d64485e4SEtienne Carriere 
3797b05d514SEtienne Carriere out:
380d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
381d64485e4SEtienne Carriere 
382d64485e4SEtienne Carriere 	return result;
383d64485e4SEtienne Carriere }
384301b3eb5SEtienne Carriere #endif /*CFG_STM32_BSEC_WRITE*/
385d64485e4SEtienne Carriere 
386d64485e4SEtienne Carriere TEE_Result stm32_bsec_permanent_lock_otp(uint32_t otp_id)
387d64485e4SEtienne Carriere {
388d64485e4SEtienne Carriere 	TEE_Result result = 0;
389d64485e4SEtienne Carriere 	uint32_t data = 0;
390d64485e4SEtienne Carriere 	uint32_t addr = 0;
391d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
392d64485e4SEtienne Carriere 	vaddr_t base = bsec_base();
393586eee81SEtienne Carriere 	uint64_t timeout_ref = 0;
394c6d2483aSGatien Chevallier 	uint32_t upper_base = otp_upper_base();
395d64485e4SEtienne Carriere 
396d64485e4SEtienne Carriere 	if (otp_id > otp_max_id())
397d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
398d64485e4SEtienne Carriere 
399c6d2483aSGatien Chevallier 	/*
400c6d2483aSGatien Chevallier 	 * 2 bits per words for lower OTPs: 2:1 Redundancy
401c6d2483aSGatien Chevallier 	 * 1 bit per word for upper OTPs : ECC support
402c6d2483aSGatien Chevallier 	 * e.g with 32 lower and 64 upper OTPs:
403c6d2483aSGatien Chevallier 	 * OTP word to be    ADDR[6:0]   WRDATA[31:0]
404c6d2483aSGatien Chevallier 	 *     locked
405c6d2483aSGatien Chevallier 	 *       0             0x00      0x0000 0003
406c6d2483aSGatien Chevallier 	 *       1             0x00      0x0000 000C
407c6d2483aSGatien Chevallier 	 *      ...             ...              ...
408c6d2483aSGatien Chevallier 	 *       7             0x00      0x0000 C000
409c6d2483aSGatien Chevallier 	 *       8             0x01      0x0000 0003
410c6d2483aSGatien Chevallier 	 *      ...             ...              ...
411c6d2483aSGatien Chevallier 	 *      31             0x03      0x0000 C000
412c6d2483aSGatien Chevallier 	 *      32             0x04      0x0000 0001
413c6d2483aSGatien Chevallier 	 *      33             0x04      0x0000 0002
414c6d2483aSGatien Chevallier 	 *      95             0x07      0x0000 8000
415c6d2483aSGatien Chevallier 	 */
416c6d2483aSGatien Chevallier 	if (otp_id < upper_base) {
417c6d2483aSGatien Chevallier 		addr = otp_id / 8U;
418c6d2483aSGatien Chevallier 		data = DATA_LOWER_OTP_PERLOCK_BIT << ((otp_id * 2U) & 0xF);
419d64485e4SEtienne Carriere 	} else {
420c6d2483aSGatien Chevallier 		addr = upper_base / 8U + (otp_id - upper_base) / 16U;
421c6d2483aSGatien Chevallier 		data = DATA_UPPER_OTP_PERLOCK_BIT << (otp_id & 0xF);
422d64485e4SEtienne Carriere 	}
423d64485e4SEtienne Carriere 
424*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
425*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
426*7dfc80abSGatien Chevallier 
427d64485e4SEtienne Carriere 	exceptions = bsec_lock();
428d64485e4SEtienne Carriere 
429d64485e4SEtienne Carriere 	result = power_up_safmem();
430d64485e4SEtienne Carriere 	if (result)
4317b05d514SEtienne Carriere 		goto out;
432d64485e4SEtienne Carriere 
433d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_WRDATA_OFF, data);
434d64485e4SEtienne Carriere 	io_write32(base + BSEC_OTP_CTRL_OFF, addr | BSEC_WRITE | BSEC_LOCK);
435d64485e4SEtienne Carriere 
436d64485e4SEtienne Carriere 	timeout_ref = timeout_init_us(BSEC_TIMEOUT_US);
437d64485e4SEtienne Carriere 	while (!timeout_elapsed(timeout_ref))
438*7dfc80abSGatien Chevallier 		if (!(bsec_status() & BSEC_MODE_BUSY))
439d64485e4SEtienne Carriere 			break;
440d64485e4SEtienne Carriere 
441*7dfc80abSGatien Chevallier 	if (bsec_status() & BSEC_MODE_BUSY)
4428afb7c41SEtienne Carriere 		result = TEE_ERROR_BUSY;
443*7dfc80abSGatien Chevallier 	else if (bsec_status() & BSEC_MODE_PROGFAIL)
444d64485e4SEtienne Carriere 		result = TEE_ERROR_BAD_PARAMETERS;
445d64485e4SEtienne Carriere 	else
4461ac4ea14SEtienne Carriere 		result = check_no_error(otp_id, false /* not-disturbed */);
447d64485e4SEtienne Carriere 
448d64485e4SEtienne Carriere 	power_down_safmem();
449d64485e4SEtienne Carriere 
4507b05d514SEtienne Carriere out:
451d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
452d64485e4SEtienne Carriere 
453d64485e4SEtienne Carriere 	return result;
454d64485e4SEtienne Carriere }
455d64485e4SEtienne Carriere 
456301b3eb5SEtienne Carriere #ifdef CFG_STM32_BSEC_WRITE
457d64485e4SEtienne Carriere TEE_Result stm32_bsec_write_debug_conf(uint32_t value)
458d64485e4SEtienne Carriere {
459d64485e4SEtienne Carriere 	TEE_Result result = TEE_ERROR_GENERIC;
460d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
461d64485e4SEtienne Carriere 
462*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
463*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
464*7dfc80abSGatien Chevallier 
465d64485e4SEtienne Carriere 	exceptions = bsec_lock();
466d64485e4SEtienne Carriere 
467d64485e4SEtienne Carriere 	io_write32(bsec_base() + BSEC_DEN_OFF, value);
468d64485e4SEtienne Carriere 
4691ff52b85SGatien Chevallier 	if ((io_read32(bsec_base() + BSEC_DEN_OFF) ^ value) == 0U)
470d64485e4SEtienne Carriere 		result = TEE_SUCCESS;
471d64485e4SEtienne Carriere 
472d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
473d64485e4SEtienne Carriere 
474d64485e4SEtienne Carriere 	return result;
475d64485e4SEtienne Carriere }
476301b3eb5SEtienne Carriere #endif /*CFG_STM32_BSEC_WRITE*/
477d64485e4SEtienne Carriere 
478d64485e4SEtienne Carriere uint32_t stm32_bsec_read_debug_conf(void)
479d64485e4SEtienne Carriere {
480d64485e4SEtienne Carriere 	return io_read32(bsec_base() + BSEC_DEN_OFF);
481d64485e4SEtienne Carriere }
482d64485e4SEtienne Carriere 
483ef9888dcSEtienne Carriere static TEE_Result set_bsec_lock(uint32_t otp_id, size_t lock_offset)
484d64485e4SEtienne Carriere {
485d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
486d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
487d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
488d64485e4SEtienne Carriere 	uint32_t exceptions = 0;
489d64485e4SEtienne Carriere 
490ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
491ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
492d64485e4SEtienne Carriere 
493*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
494*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
495*7dfc80abSGatien Chevallier 
496d64485e4SEtienne Carriere 	exceptions = bsec_lock();
497d64485e4SEtienne Carriere 
498ef9888dcSEtienne Carriere 	io_write32(lock_addr, otp_mask);
499d64485e4SEtienne Carriere 
500d64485e4SEtienne Carriere 	bsec_unlock(exceptions);
501d64485e4SEtienne Carriere 
502ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
503d64485e4SEtienne Carriere }
504d64485e4SEtienne Carriere 
505ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sr_lock(uint32_t otp_id)
506d64485e4SEtienne Carriere {
507ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SRLOCK_OFF);
508d64485e4SEtienne Carriere }
509d64485e4SEtienne Carriere 
510ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sw_lock(uint32_t otp_id)
511d64485e4SEtienne Carriere {
512ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SWLOCK_OFF);
513d64485e4SEtienne Carriere }
514d64485e4SEtienne Carriere 
515ef9888dcSEtienne Carriere TEE_Result stm32_bsec_set_sp_lock(uint32_t otp_id)
516d64485e4SEtienne Carriere {
517ef9888dcSEtienne Carriere 	return set_bsec_lock(otp_id, BSEC_SPLOCK_OFF);
518d64485e4SEtienne Carriere }
519d64485e4SEtienne Carriere 
520ef9888dcSEtienne Carriere static TEE_Result read_bsec_lock(uint32_t otp_id, bool *locked,
521ef9888dcSEtienne Carriere 				 size_t lock_offset)
522d64485e4SEtienne Carriere {
523d64485e4SEtienne Carriere 	uint32_t bank = otp_bank_offset(otp_id);
524d64485e4SEtienne Carriere 	uint32_t otp_mask = BIT(otp_id & BSEC_OTP_MASK);
525d64485e4SEtienne Carriere 	vaddr_t lock_addr = bsec_base() + bank + lock_offset;
526d64485e4SEtienne Carriere 
527ef9888dcSEtienne Carriere 	if (otp_id > STM32MP1_OTP_MAX_ID)
528ef9888dcSEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
529ef9888dcSEtienne Carriere 
530*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
531*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
532*7dfc80abSGatien Chevallier 
533ef9888dcSEtienne Carriere 	*locked = (io_read32(lock_addr) & otp_mask) != 0;
534ef9888dcSEtienne Carriere 
535ef9888dcSEtienne Carriere 	return TEE_SUCCESS;
536d64485e4SEtienne Carriere }
537d64485e4SEtienne Carriere 
538ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sr_lock(uint32_t otp_id, bool *locked)
539d64485e4SEtienne Carriere {
540ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SRLOCK_OFF);
541d64485e4SEtienne Carriere }
542d64485e4SEtienne Carriere 
543ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sw_lock(uint32_t otp_id, bool *locked)
544d64485e4SEtienne Carriere {
545ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SWLOCK_OFF);
546d64485e4SEtienne Carriere }
547d64485e4SEtienne Carriere 
548ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_sp_lock(uint32_t otp_id, bool *locked)
549d64485e4SEtienne Carriere {
550ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_SPLOCK_OFF);
551d64485e4SEtienne Carriere }
552d64485e4SEtienne Carriere 
553ef9888dcSEtienne Carriere TEE_Result stm32_bsec_read_permanent_lock(uint32_t otp_id, bool *locked)
554d64485e4SEtienne Carriere {
555ef9888dcSEtienne Carriere 	return read_bsec_lock(otp_id, locked, BSEC_WRLOCK_OFF);
556d64485e4SEtienne Carriere }
557d64485e4SEtienne Carriere 
558ef9888dcSEtienne Carriere TEE_Result stm32_bsec_otp_lock(uint32_t service)
559d64485e4SEtienne Carriere {
560d64485e4SEtienne Carriere 	vaddr_t addr = bsec_base() + BSEC_OTP_LOCK_OFF;
561d64485e4SEtienne Carriere 
562*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
563*7dfc80abSGatien Chevallier 		return TEE_ERROR_SECURITY;
564*7dfc80abSGatien Chevallier 
565d64485e4SEtienne Carriere 	switch (service) {
566d64485e4SEtienne Carriere 	case BSEC_LOCK_UPPER_OTP:
567ef9888dcSEtienne Carriere 		io_write32(addr, BIT(BSEC_LOCK_UPPER_OTP));
568d64485e4SEtienne Carriere 		break;
569d64485e4SEtienne Carriere 	case BSEC_LOCK_DEBUG:
570ef9888dcSEtienne Carriere 		io_write32(addr, BIT(BSEC_LOCK_DEBUG));
571d64485e4SEtienne Carriere 		break;
572d64485e4SEtienne Carriere 	case BSEC_LOCK_PROGRAM:
573ef9888dcSEtienne Carriere 		io_write32(addr, BIT(BSEC_LOCK_PROGRAM));
574d64485e4SEtienne Carriere 		break;
575d64485e4SEtienne Carriere 	default:
576d64485e4SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
577d64485e4SEtienne Carriere 	}
578d64485e4SEtienne Carriere 
579d64485e4SEtienne Carriere 	return TEE_SUCCESS;
580d64485e4SEtienne Carriere }
581d64485e4SEtienne Carriere 
582890703c3SEtienne Carriere static size_t nsec_access_array_size(void)
583890703c3SEtienne Carriere {
584586eee81SEtienne Carriere 	size_t upper_count = otp_max_id() - otp_upper_base() + 1;
585890703c3SEtienne Carriere 
5868396f62eSGatien Chevallier 	return ROUNDUP_DIV(upper_count, BSEC_BITS_PER_WORD);
587890703c3SEtienne Carriere }
588890703c3SEtienne Carriere 
589890703c3SEtienne Carriere static bool nsec_access_granted(unsigned int index)
590890703c3SEtienne Carriere {
591890703c3SEtienne Carriere 	uint32_t *array = bsec_dev.nsec_access;
592890703c3SEtienne Carriere 
593890703c3SEtienne Carriere 	return array &&
5948396f62eSGatien Chevallier 	       (index / BSEC_BITS_PER_WORD) < nsec_access_array_size() &&
5958396f62eSGatien Chevallier 	       array[index / BSEC_BITS_PER_WORD] &
5968396f62eSGatien Chevallier 	       BIT(index % BSEC_BITS_PER_WORD);
597890703c3SEtienne Carriere }
598890703c3SEtienne Carriere 
599*7dfc80abSGatien Chevallier bool stm32_bsec_can_access_otp(uint32_t otp_id)
600*7dfc80abSGatien Chevallier {
601*7dfc80abSGatien Chevallier 	return (otp_id <= otp_max_id()) && !state_is_invalid_mode();
602*7dfc80abSGatien Chevallier }
603*7dfc80abSGatien Chevallier 
604d64485e4SEtienne Carriere bool stm32_bsec_nsec_can_access_otp(uint32_t otp_id)
605d64485e4SEtienne Carriere {
606586eee81SEtienne Carriere 	return otp_id < otp_upper_base() ||
607586eee81SEtienne Carriere 	       nsec_access_granted(otp_id - otp_upper_base());
608d64485e4SEtienne Carriere }
609d64485e4SEtienne Carriere 
61093114f2eSGatien Chevallier struct nvmem_layout {
61193114f2eSGatien Chevallier 	char *name;
61293114f2eSGatien Chevallier 	uint32_t otp_id;
61393114f2eSGatien Chevallier 	size_t bit_len;
61493114f2eSGatien Chevallier };
61593114f2eSGatien Chevallier 
61693114f2eSGatien Chevallier static struct nvmem_layout *nvmem_layout;
61793114f2eSGatien Chevallier static size_t nvmem_layout_count;
61893114f2eSGatien Chevallier 
61993114f2eSGatien Chevallier TEE_Result stm32_bsec_find_otp_in_nvmem_layout(const char *name,
62093114f2eSGatien Chevallier 					       uint32_t *otp_id,
62193114f2eSGatien Chevallier 					       size_t *otp_bit_len)
62293114f2eSGatien Chevallier {
62393114f2eSGatien Chevallier 	size_t i = 0;
62493114f2eSGatien Chevallier 
62593114f2eSGatien Chevallier 	if (!name)
62693114f2eSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
62793114f2eSGatien Chevallier 
62893114f2eSGatien Chevallier 	for (i = 0; i < nvmem_layout_count; i++) {
62993114f2eSGatien Chevallier 		if (!nvmem_layout[i].name || strcmp(name, nvmem_layout[i].name))
63093114f2eSGatien Chevallier 			continue;
63193114f2eSGatien Chevallier 
63293114f2eSGatien Chevallier 		if (otp_id)
63393114f2eSGatien Chevallier 			*otp_id = nvmem_layout[i].otp_id;
63493114f2eSGatien Chevallier 
63593114f2eSGatien Chevallier 		if (otp_bit_len)
63693114f2eSGatien Chevallier 			*otp_bit_len = nvmem_layout[i].bit_len;
63793114f2eSGatien Chevallier 
63893114f2eSGatien Chevallier 		DMSG("nvmem %s = %zu: %"PRId32" %zu", name, i,
63993114f2eSGatien Chevallier 		     nvmem_layout[i].otp_id, nvmem_layout[i].bit_len);
64093114f2eSGatien Chevallier 
64193114f2eSGatien Chevallier 		return TEE_SUCCESS;
64293114f2eSGatien Chevallier 	}
64393114f2eSGatien Chevallier 
64493114f2eSGatien Chevallier 	DMSG("nvmem %s failed", name);
64593114f2eSGatien Chevallier 
64693114f2eSGatien Chevallier 	return TEE_ERROR_ITEM_NOT_FOUND;
647*7dfc80abSGatien Chevallier };
648*7dfc80abSGatien Chevallier 
649*7dfc80abSGatien Chevallier TEE_Result stm32_bsec_get_state(uint32_t *state)
650*7dfc80abSGatien Chevallier {
651*7dfc80abSGatien Chevallier 	if (!state)
652*7dfc80abSGatien Chevallier 		return TEE_ERROR_BAD_PARAMETERS;
653*7dfc80abSGatien Chevallier 
654*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode() || !state_is_secured_mode()) {
655*7dfc80abSGatien Chevallier 		*state = BSEC_STATE_INVALID;
656*7dfc80abSGatien Chevallier 	} else {
657*7dfc80abSGatien Chevallier 		if (state_is_closed_mode())
658*7dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_CLOSED;
659*7dfc80abSGatien Chevallier 		else
660*7dfc80abSGatien Chevallier 			*state = BSEC_STATE_SEC_OPEN;
661*7dfc80abSGatien Chevallier 	}
662*7dfc80abSGatien Chevallier 
663*7dfc80abSGatien Chevallier 	return TEE_SUCCESS;
66493114f2eSGatien Chevallier }
66593114f2eSGatien Chevallier 
666cfada897SAlexandru Gagniuc #ifdef CFG_EMBED_DTB
667890703c3SEtienne Carriere static void enable_nsec_access(unsigned int otp_id)
668890703c3SEtienne Carriere {
6698396f62eSGatien Chevallier 	unsigned int idx = (otp_id - otp_upper_base()) / BSEC_BITS_PER_WORD;
670890703c3SEtienne Carriere 
671586eee81SEtienne Carriere 	if (otp_id < otp_upper_base())
672890703c3SEtienne Carriere 		return;
673890703c3SEtienne Carriere 
674890703c3SEtienne Carriere 	if (otp_id > otp_max_id() || stm32_bsec_shadow_register(otp_id))
675890703c3SEtienne Carriere 		panic();
676890703c3SEtienne Carriere 
6778396f62eSGatien Chevallier 	bsec_dev.nsec_access[idx] |= BIT(otp_id % BSEC_BITS_PER_WORD);
678890703c3SEtienne Carriere }
679890703c3SEtienne Carriere 
680890703c3SEtienne Carriere static void bsec_dt_otp_nsec_access(void *fdt, int bsec_node)
681890703c3SEtienne Carriere {
682890703c3SEtienne Carriere 	int bsec_subnode = 0;
683890703c3SEtienne Carriere 
684890703c3SEtienne Carriere 	bsec_dev.nsec_access = calloc(nsec_access_array_size(),
685890703c3SEtienne Carriere 				      sizeof(*bsec_dev.nsec_access));
686890703c3SEtienne Carriere 	if (!bsec_dev.nsec_access)
687890703c3SEtienne Carriere 		panic();
688890703c3SEtienne Carriere 
689890703c3SEtienne Carriere 	fdt_for_each_subnode(bsec_subnode, fdt, bsec_node) {
69027a02b1eSGatien Chevallier 		unsigned int reg_offset = 0;
69127a02b1eSGatien Chevallier 		unsigned int reg_size = 0;
692890703c3SEtienne Carriere 		unsigned int otp_id = 0;
693890703c3SEtienne Carriere 		unsigned int i = 0;
694890703c3SEtienne Carriere 		size_t size = 0;
695890703c3SEtienne Carriere 
69627a02b1eSGatien Chevallier 		reg_offset = _fdt_reg_base_address(fdt, bsec_subnode);
69727a02b1eSGatien Chevallier 		reg_size = _fdt_reg_size(fdt, bsec_subnode);
698890703c3SEtienne Carriere 
69927a02b1eSGatien Chevallier 		assert(reg_offset != DT_INFO_INVALID_REG &&
70027a02b1eSGatien Chevallier 		       reg_size != DT_INFO_INVALID_REG_SIZE);
701890703c3SEtienne Carriere 
70227a02b1eSGatien Chevallier 		otp_id = reg_offset / sizeof(uint32_t);
703890703c3SEtienne Carriere 
704890703c3SEtienne Carriere 		if (otp_id < STM32MP1_UPPER_OTP_START) {
70527a02b1eSGatien Chevallier 			unsigned int otp_end =
70627a02b1eSGatien Chevallier 				ROUNDUP_DIV(reg_offset + reg_size,
70727a02b1eSGatien Chevallier 					    sizeof(uint32_t));
708890703c3SEtienne Carriere 
709890703c3SEtienne Carriere 			if (otp_end > STM32MP1_UPPER_OTP_START) {
710890703c3SEtienne Carriere 				/*
711890703c3SEtienne Carriere 				 * OTP crosses Lower/Upper boundary, consider
712890703c3SEtienne Carriere 				 * only the upper part.
713890703c3SEtienne Carriere 				 */
714890703c3SEtienne Carriere 				otp_id = STM32MP1_UPPER_OTP_START;
71527a02b1eSGatien Chevallier 				reg_size -= (STM32MP1_UPPER_OTP_START *
71627a02b1eSGatien Chevallier 					     sizeof(uint32_t)) - reg_offset;
71727a02b1eSGatien Chevallier 				reg_offset = STM32MP1_UPPER_OTP_START *
718890703c3SEtienne Carriere 					     sizeof(uint32_t);
719890703c3SEtienne Carriere 
720890703c3SEtienne Carriere 				DMSG("OTP crosses Lower/Upper boundary");
721890703c3SEtienne Carriere 			} else {
722890703c3SEtienne Carriere 				continue;
723890703c3SEtienne Carriere 			}
724890703c3SEtienne Carriere 		}
725890703c3SEtienne Carriere 
726890703c3SEtienne Carriere 		if (!fdt_getprop(fdt, bsec_subnode, "st,non-secure-otp", NULL))
727890703c3SEtienne Carriere 			continue;
728890703c3SEtienne Carriere 
72927a02b1eSGatien Chevallier 		if ((reg_offset % sizeof(uint32_t)) ||
73027a02b1eSGatien Chevallier 		    (reg_size % sizeof(uint32_t)))
731890703c3SEtienne Carriere 			panic("Unaligned non-secure OTP");
732890703c3SEtienne Carriere 
73327a02b1eSGatien Chevallier 		size = reg_size / sizeof(uint32_t);
734890703c3SEtienne Carriere 
735*7dfc80abSGatien Chevallier 		if (otp_id + size > OTP_MAX_SIZE)
736890703c3SEtienne Carriere 			panic("OTP range oversized");
737890703c3SEtienne Carriere 
738890703c3SEtienne Carriere 		for (i = otp_id; i < otp_id + size; i++)
739890703c3SEtienne Carriere 			enable_nsec_access(i);
740890703c3SEtienne Carriere 	}
741890703c3SEtienne Carriere }
742890703c3SEtienne Carriere 
74393114f2eSGatien Chevallier static void save_dt_nvmem_layout(void *fdt, int bsec_node)
74493114f2eSGatien Chevallier {
74593114f2eSGatien Chevallier 	int cell_max = 0;
74693114f2eSGatien Chevallier 	int cell_cnt = 0;
74793114f2eSGatien Chevallier 	int node = 0;
74893114f2eSGatien Chevallier 
74993114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node)
75093114f2eSGatien Chevallier 		cell_max++;
75193114f2eSGatien Chevallier 	if (!cell_max)
75293114f2eSGatien Chevallier 		return;
75393114f2eSGatien Chevallier 
75493114f2eSGatien Chevallier 	nvmem_layout = calloc(cell_max, sizeof(*nvmem_layout));
75593114f2eSGatien Chevallier 	if (!nvmem_layout)
75693114f2eSGatien Chevallier 		panic();
75793114f2eSGatien Chevallier 
75893114f2eSGatien Chevallier 	fdt_for_each_subnode(node, fdt, bsec_node) {
75993114f2eSGatien Chevallier 		unsigned int reg_offset = 0;
76093114f2eSGatien Chevallier 		unsigned int reg_length = 0;
76193114f2eSGatien Chevallier 		const char *string = NULL;
76293114f2eSGatien Chevallier 		const char *s = NULL;
76393114f2eSGatien Chevallier 		int len = 0;
76493114f2eSGatien Chevallier 		struct nvmem_layout *layout_cell = &nvmem_layout[cell_cnt];
76593114f2eSGatien Chevallier 
76693114f2eSGatien Chevallier 		string = fdt_get_name(fdt, node, &len);
76793114f2eSGatien Chevallier 		if (!string || !len)
76893114f2eSGatien Chevallier 			continue;
76993114f2eSGatien Chevallier 
77093114f2eSGatien Chevallier 		reg_offset = _fdt_reg_base_address(fdt, node);
77193114f2eSGatien Chevallier 		reg_length = _fdt_reg_size(fdt, node);
77293114f2eSGatien Chevallier 
77393114f2eSGatien Chevallier 		if (reg_offset == DT_INFO_INVALID_REG ||
77493114f2eSGatien Chevallier 		    reg_length == DT_INFO_INVALID_REG_SIZE) {
77593114f2eSGatien Chevallier 			DMSG("Malformed nvmem %s: ignored", string);
77693114f2eSGatien Chevallier 			continue;
77793114f2eSGatien Chevallier 		}
77893114f2eSGatien Chevallier 
77993114f2eSGatien Chevallier 		if (reg_offset % sizeof(uint32_t)) {
78093114f2eSGatien Chevallier 			DMSG("Misaligned nvmem %s: ignored", string);
78193114f2eSGatien Chevallier 			continue;
78293114f2eSGatien Chevallier 		}
78393114f2eSGatien Chevallier 		layout_cell->otp_id = reg_offset / sizeof(uint32_t);
78493114f2eSGatien Chevallier 		layout_cell->bit_len = reg_length * CHAR_BIT;
78593114f2eSGatien Chevallier 
78693114f2eSGatien Chevallier 		s = strchr(string, '@');
78793114f2eSGatien Chevallier 		if (s)
78893114f2eSGatien Chevallier 			len = s - string;
78993114f2eSGatien Chevallier 
79093114f2eSGatien Chevallier 		layout_cell->name = strndup(string, len);
79193114f2eSGatien Chevallier 		if (!layout_cell->name)
79293114f2eSGatien Chevallier 			panic();
79393114f2eSGatien Chevallier 		cell_cnt++;
79493114f2eSGatien Chevallier 		DMSG("nvmem[%d] = %s %"PRId32" %zu", cell_cnt,
79593114f2eSGatien Chevallier 		     layout_cell->name, layout_cell->otp_id,
79693114f2eSGatien Chevallier 		     layout_cell->bit_len);
79793114f2eSGatien Chevallier 	}
79893114f2eSGatien Chevallier 
79993114f2eSGatien Chevallier 	if (cell_cnt != cell_max) {
80093114f2eSGatien Chevallier 		nvmem_layout = realloc(nvmem_layout,
80193114f2eSGatien Chevallier 				       cell_cnt * sizeof(*nvmem_layout));
80293114f2eSGatien Chevallier 		if (!nvmem_layout)
80393114f2eSGatien Chevallier 			panic();
80493114f2eSGatien Chevallier 	}
80593114f2eSGatien Chevallier 
80693114f2eSGatien Chevallier 	nvmem_layout_count = cell_cnt;
80793114f2eSGatien Chevallier }
80893114f2eSGatien Chevallier 
809890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
810890703c3SEtienne Carriere {
811890703c3SEtienne Carriere 	void *fdt = NULL;
812890703c3SEtienne Carriere 	int node = 0;
813890703c3SEtienne Carriere 	struct dt_node_info bsec_info = { };
814890703c3SEtienne Carriere 
815890703c3SEtienne Carriere 	fdt = get_embedded_dt();
816890703c3SEtienne Carriere 	node = fdt_node_offset_by_compatible(fdt, 0, "st,stm32mp15-bsec");
817890703c3SEtienne Carriere 	if (node < 0)
818890703c3SEtienne Carriere 		panic();
819890703c3SEtienne Carriere 
820890703c3SEtienne Carriere 	_fdt_fill_device_info(fdt, &bsec_info, node);
821890703c3SEtienne Carriere 
822890703c3SEtienne Carriere 	if (bsec_info.reg != bsec_dev.base.pa ||
823890703c3SEtienne Carriere 	    !(bsec_info.status & DT_STATUS_OK_SEC))
824890703c3SEtienne Carriere 		panic();
825890703c3SEtienne Carriere 
826890703c3SEtienne Carriere 	bsec_dt_otp_nsec_access(fdt, node);
82793114f2eSGatien Chevallier 
82893114f2eSGatien Chevallier 	save_dt_nvmem_layout(fdt, node);
829890703c3SEtienne Carriere }
830890703c3SEtienne Carriere #else
831890703c3SEtienne Carriere static void initialize_bsec_from_dt(void)
832890703c3SEtienne Carriere {
833890703c3SEtienne Carriere }
834cfada897SAlexandru Gagniuc #endif /*CFG_EMBED_DTB*/
835890703c3SEtienne Carriere 
836d64485e4SEtienne Carriere static TEE_Result initialize_bsec(void)
837d64485e4SEtienne Carriere {
8387994d842SEtienne Carriere 	struct stm32_bsec_static_cfg cfg = { };
839d64485e4SEtienne Carriere 
840d64485e4SEtienne Carriere 	stm32mp_get_bsec_static_cfg(&cfg);
841d64485e4SEtienne Carriere 
842d64485e4SEtienne Carriere 	bsec_dev.base.pa = cfg.base;
843d64485e4SEtienne Carriere 	bsec_dev.upper_base = cfg.upper_start;
844d64485e4SEtienne Carriere 	bsec_dev.max_id = cfg.max_id;
845d64485e4SEtienne Carriere 
846*7dfc80abSGatien Chevallier 	if (state_is_invalid_mode())
847*7dfc80abSGatien Chevallier 		panic();
848*7dfc80abSGatien Chevallier 
849890703c3SEtienne Carriere 	if (IS_ENABLED(CFG_EMBED_DTB))
850890703c3SEtienne Carriere 		initialize_bsec_from_dt();
851890703c3SEtienne Carriere 
852d64485e4SEtienne Carriere 	return TEE_SUCCESS;
853d64485e4SEtienne Carriere }
854d64485e4SEtienne Carriere 
8558c8316dbSEtienne Carriere early_init(initialize_bsec);
856