| /* | 
 |  * Copyright (c) 2019 Linaro Limited | 
 |  * Copyright (c) 2020 STMicroelectronics | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define LOG_DOMAIN flash_stm32wb | 
 | #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL | 
 | #include <zephyr/logging/log.h> | 
 | LOG_MODULE_REGISTER(LOG_DOMAIN); | 
 |  | 
 | #include <zephyr/kernel.h> | 
 | #include <zephyr/device.h> | 
 | #include <string.h> | 
 | #include <zephyr/drivers/flash.h> | 
 | #include <zephyr/init.h> | 
 | #include <soc.h> | 
 | #include <zephyr/sys/__assert.h> | 
 |  | 
 | #include "flash_stm32.h" | 
 | #include "stm32_hsem.h" | 
 | #if defined(CONFIG_BT) | 
 | #include "shci.h" | 
 | #endif | 
 |  | 
 | #define STM32WBX_PAGE_SHIFT	12 | 
 |  | 
 | /* offset and len must be aligned on 8 for write, | 
 |  * positive and not beyond end of flash | 
 |  */ | 
 | bool flash_stm32_valid_range(const struct device *dev, off_t offset, | 
 | 			     uint32_t len, | 
 | 			     bool write) | 
 | { | 
 | 	return (!write || (offset % 8 == 0 && len % 8 == 0U)) && | 
 | 	       flash_stm32_range_exists(dev, offset, len); | 
 | } | 
 |  | 
 | /* | 
 |  * Up to 255 4K pages | 
 |  */ | 
 | static uint32_t get_page(off_t offset) | 
 | { | 
 | 	return offset >> STM32WBX_PAGE_SHIFT; | 
 | } | 
 |  | 
 | static inline void flush_cache(FLASH_TypeDef *regs) | 
 | { | 
 | 	if (regs->ACR & FLASH_ACR_DCEN) { | 
 | 		regs->ACR &= ~FLASH_ACR_DCEN; | 
 | 		/* Datasheet: DCRST: Data cache reset | 
 | 		 * This bit can be written only when the data cache is disabled | 
 | 		 */ | 
 | 		regs->ACR |= FLASH_ACR_DCRST; | 
 | 		regs->ACR &= ~FLASH_ACR_DCRST; | 
 | 		regs->ACR |= FLASH_ACR_DCEN; | 
 | 	} | 
 |  | 
 | 	if (regs->ACR & FLASH_ACR_ICEN) { | 
 | 		regs->ACR &= ~FLASH_ACR_ICEN; | 
 | 		/* Datasheet: ICRST: Instruction cache reset : | 
 | 		 * This bit can be written only when the instruction cache | 
 | 		 * is disabled | 
 | 		 */ | 
 | 		regs->ACR |= FLASH_ACR_ICRST; | 
 | 		regs->ACR &= ~FLASH_ACR_ICRST; | 
 | 		regs->ACR |= FLASH_ACR_ICEN; | 
 | 	} | 
 | } | 
 |  | 
 | static int write_dword(const struct device *dev, off_t offset, uint64_t val) | 
 | { | 
 | 	volatile uint32_t *flash = (uint32_t *)(offset + CONFIG_FLASH_BASE_ADDRESS); | 
 | 	FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); | 
 | 	uint32_t tmp; | 
 | 	int ret, rc; | 
 | 	uint32_t cpu1_sem_status; | 
 | 	uint32_t cpu2_sem_status = 0; | 
 | 	uint32_t key; | 
 |  | 
 | 	/* if the control register is locked, do not fail silently */ | 
 | 	if (regs->CR & FLASH_CR_LOCK) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* Check if this double word is erased */ | 
 | 	if (flash[0] != 0xFFFFFFFFUL || | 
 | 	    flash[1] != 0xFFFFFFFFUL) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	ret = flash_stm32_check_status(dev); | 
 | 	if (ret < 0) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* Implementation of STM32 AN5289, proposed in STM32WB Cube Application | 
 | 	 * BLE_RfWithFlash | 
 | 	 * https://github.com/STMicroelectronics/STM32CubeWB/tree/master/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash | 
 | 	 */ | 
 |  | 
 | 	do { | 
 | 		/** | 
 | 		 * When the PESD bit mechanism is used by CPU2 to protect its | 
 | 		 * timing, the PESD bit should be polled here. | 
 | 		 * If the PESD is set, the CPU1 will be stalled when reading | 
 | 		 * literals from an ISR that may occur after the flash | 
 | 		 * processing has been requested but suspended due to the PESD | 
 | 		 * bit. | 
 | 		 * | 
 | 		 * Note: This code is required only when the PESD mechanism is | 
 | 		 * used to protect the CPU2 timing. | 
 | 		 * However, keeping that code make it compatible with both | 
 | 		 * mechanisms. | 
 | 		 */ | 
 | 		while (LL_FLASH_IsActiveFlag_OperationSuspended()) { | 
 | 			; | 
 | 		} | 
 |  | 
 | 		/* Enter critical section */ | 
 | 		key = irq_lock(); | 
 |  | 
 | 		/** | 
 | 		 *  Depending on the application implementation, in case a | 
 | 		 *  multitasking is possible with an OS, it should be checked | 
 | 		 *  here if another task in the application disallowed flash | 
 | 		 *  processing to protect some latency in critical code | 
 | 		 *  execution. | 
 | 		 *  When flash processing is ongoing, the CPU cannot access the | 
 | 		 *  flash anymore.Trying to access the flash during that time | 
 | 		 *  stalls the CPU. | 
 | 		 *  The only way for CPU1 to disallow flash processing is to | 
 | 		 *  take CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID. | 
 | 		 */ | 
 | 		cpu1_sem_status = LL_HSEM_GetStatus(HSEM, | 
 | 			CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID); | 
 | 		if (cpu1_sem_status == 0) { | 
 | 			/** | 
 | 			 *  Check now if the CPU2 disallows flash processing to | 
 | 			 *  protect its timing. If the semaphore is locked, the | 
 | 			 *  CPU2 does not allow flash processing | 
 | 			 * | 
 | 			 *  Note: By default, the CPU2 uses the PESD mechanism | 
 | 			 *  to protect its timing, therefore, it is useless to | 
 | 			 *  get/release the semaphore. | 
 | 			 * | 
 | 			 *  However, keeping that code make it compatible with | 
 | 			 *  both mechanisms. | 
 | 			 *  The protection by semaphore is enabled on CPU2 side | 
 | 			 *  with the command SHCI_C2_SetFlashActivityControl() | 
 | 			 * | 
 | 			 */ | 
 | 			cpu2_sem_status = LL_HSEM_1StepLock(HSEM, | 
 | 				CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID); | 
 | 			if (cpu2_sem_status == 0) { | 
 | 				/** | 
 | 				 * When CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is | 
 | 				 * taken, it is allowed to only write one | 
 | 				 * single 64bits data. | 
 | 				 * When several 64bits data need to be erased, | 
 | 				 * the application shall first exit from the | 
 | 				 * critical section and try again. | 
 | 				 */ | 
 | 				/* Set the PG bit */ | 
 | 				regs->CR |= FLASH_CR_PG; | 
 |  | 
 | 				/* Flush the register write */ | 
 | 				tmp = regs->CR; | 
 |  | 
 | 				/* Perform the data write operation at desired | 
 | 				 * memory address | 
 | 				 */ | 
 | 				flash[0] = (uint32_t)val; | 
 | 				flash[1] = (uint32_t)(val >> 32); | 
 |  | 
 | 				/** | 
 | 				 *  Release the semaphore to give the | 
 | 				 *  opportunity to CPU2 to protect its timing | 
 | 				 *  versus the next flash operation by taking | 
 | 				 *  this semaphore. | 
 | 				 *  Note that the CPU2 is polling on this | 
 | 				 *  semaphore so CPU1 shall release it as fast | 
 | 				 *  as possible. | 
 | 				 *  This is why this code is protected by a | 
 | 				 *  critical section. | 
 | 				 */ | 
 | 				LL_HSEM_ReleaseLock(HSEM, | 
 | 					CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, | 
 | 					0); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* Exit critical section */ | 
 | 		irq_unlock(key); | 
 |  | 
 | 	} while (cpu2_sem_status || cpu1_sem_status); | 
 |  | 
 | 	/* Wait until the BSY bit is cleared */ | 
 | 	rc = flash_stm32_wait_flash_idle(dev); | 
 |  | 
 | 	/* Clear the PG bit */ | 
 | 	regs->CR &= (~FLASH_CR_PG); | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int erase_page(const struct device *dev, uint32_t page) | 
 | { | 
 | 	uint32_t cpu1_sem_status; | 
 | 	uint32_t cpu2_sem_status = 0; | 
 | 	uint32_t key; | 
 |  | 
 | 	FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); | 
 | 	int rc; | 
 |  | 
 | 	/* if the control register is locked, do not fail silently */ | 
 | 	if (regs->CR & FLASH_CR_LOCK) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	/* Check that no Flash memory operation is ongoing */ | 
 | 	rc = flash_stm32_wait_flash_idle(dev); | 
 | 	if (rc < 0) { | 
 | 		return rc; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * If an erase operation in Flash memory also concerns data in the data | 
 | 	 * or instruction cache, the user has to ensure that these data | 
 | 	 * are rewritten before they are accessed during code execution. | 
 | 	 */ | 
 | 	flush_cache(regs); | 
 |  | 
 | 	/* Implementation of STM32 AN5289, proposed in STM32WB Cube Application | 
 | 	 * BLE_RfWithFlash | 
 | 	 * https://github.com/STMicroelectronics/STM32CubeWB/tree/master/Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_RfWithFlash | 
 | 	 */ | 
 |  | 
 | 	do { | 
 | 		/** | 
 | 		 * When the PESD bit mechanism is used by CPU2 to protect its | 
 | 		 * timing, the PESD bit should be polled here. | 
 | 		 * If the PESD is set, the CPU1 will be stalled when reading | 
 | 		 * literals from an ISR that may occur after the flash | 
 | 		 * processing has been requested but suspended due to the PESD | 
 | 		 * bit. | 
 | 		 * | 
 | 		 * Note: This code is required only when the PESD mechanism is | 
 | 		 * used to protect the CPU2 timing. | 
 | 		 * However, keeping that code make it compatible with both | 
 | 		 * mechanisms. | 
 | 		 */ | 
 | 		while (LL_FLASH_IsActiveFlag_OperationSuspended()) { | 
 | 			; | 
 | 		} | 
 |  | 
 | 		/* Enter critical section */ | 
 | 		key = irq_lock(); | 
 |  | 
 | 		/** | 
 | 		 *  Depending on the application implementation, in case a | 
 | 		 *  multitasking is possible with an OS, it should be checked | 
 | 		 *  here if another task in the application disallowed flash | 
 | 		 *  processing to protect some latency in critical code | 
 | 		 *  execution. | 
 | 		 *  When flash processing is ongoing, the CPU cannot access the | 
 | 		 *  flash anymore.Trying to access the flash during that time | 
 | 		 *  stalls the CPU. | 
 | 		 *  The only way for CPU1 to disallow flash processing is to | 
 | 		 *  take CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID. | 
 | 		 */ | 
 | 		cpu1_sem_status = LL_HSEM_GetStatus(HSEM, | 
 | 			CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID); | 
 | 		if (cpu1_sem_status == 0) { | 
 | 			/** | 
 | 			 *  Check now if the CPU2 disallows flash processing to | 
 | 			 *  protect its timing. If the semaphore is locked, the | 
 | 			 *  CPU2 does not allow flash processing | 
 | 			 * | 
 | 			 *  Note: By default, the CPU2 uses the PESD mechanism | 
 | 			 *  to protect its timing, therefore, it is useless to | 
 | 			 *  get/release the semaphore. | 
 | 			 * | 
 | 			 *  However, keeping that code make it compatible with | 
 | 			 *  both mechanisms. | 
 | 			 *  The protection by semaphore is enabled on CPU2 side | 
 | 			 *  with the command SHCI_C2_SetFlashActivityControl() | 
 | 			 * | 
 | 			 */ | 
 | 			cpu2_sem_status = LL_HSEM_1StepLock(HSEM, | 
 | 				CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID); | 
 | 			if (cpu2_sem_status == 0) { | 
 | 				/** | 
 | 				 * When CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID is | 
 | 				 * taken, it is allowed to only erase one | 
 | 				 * sector. | 
 | 				 * When several sectors need to be erased, | 
 | 				 * the application shall first exit from the | 
 | 				 * critical section and try again. | 
 | 				 */ | 
 | 				regs->CR |= FLASH_CR_PER; | 
 | 				regs->CR &= ~FLASH_CR_PNB_Msk; | 
 | 				regs->CR |= page << FLASH_CR_PNB_Pos; | 
 |  | 
 | 				regs->CR |= FLASH_CR_STRT; | 
 |  | 
 | 				/** | 
 | 				 *  Release the semaphore to give the | 
 | 				 *  opportunity to CPU2 to protect its timing | 
 | 				 *  versus the next flash operation by taking | 
 | 				 *  this semaphore. | 
 | 				 *  Note that the CPU2 is polling on this | 
 | 				 *  semaphore so CPU1 shall release it as fast | 
 | 				 *  as possible. | 
 | 				 *  This is why this code is protected by a | 
 | 				 *  critical section. | 
 | 				 */ | 
 | 				LL_HSEM_ReleaseLock(HSEM, | 
 | 					CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, | 
 | 					0); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* Exit critical section */ | 
 | 		irq_unlock(key); | 
 |  | 
 | 	} while (cpu2_sem_status || cpu1_sem_status); | 
 |  | 
 |  | 
 | 	/* Wait for the BSY bit */ | 
 | 	rc = flash_stm32_wait_flash_idle(dev); | 
 |  | 
 | 	regs->CR &= ~FLASH_CR_PER; | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | int flash_stm32_block_erase_loop(const struct device *dev, | 
 | 				 unsigned int offset, | 
 | 				 unsigned int len) | 
 | { | 
 | 	int i, rc = 0; | 
 |  | 
 | #if defined(CONFIG_BT) | 
 | 	/** | 
 | 	 *  Notify the CPU2 that some flash erase activity may be executed | 
 | 	 *  On reception of this command, the CPU2 enables the BLE timing | 
 | 	 *  protection versus flash erase processing. | 
 | 	 *  The Erase flash activity will be executed only when the BLE RF is | 
 | 	 *  idle for at least 25ms. | 
 | 	 *  The CPU2 will prevent all flash activity (write or erase) in all | 
 | 	 *  cases when the BL RF Idle is shorter than 25ms. | 
 | 	 */ | 
 | 	SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); | 
 | #endif /* CONFIG_BT */ | 
 |  | 
 | 	i = get_page(offset); | 
 | 	for (; i <= get_page(offset + len - 1) ; ++i) { | 
 | 		rc = erase_page(dev, i); | 
 | 		if (rc < 0) { | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | #if defined(CONFIG_BT) | 
 | 	/** | 
 | 	 *  Notify the CPU2 there will be no request anymore to erase the flash | 
 | 	 *  On reception of this command, the CPU2 disables the BLE timing | 
 | 	 *  protection versus flash erase processing | 
 | 	 */ | 
 | 	SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); | 
 | #endif /* CONFIG_BT */ | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | int flash_stm32_write_range(const struct device *dev, unsigned int offset, | 
 | 			    const void *data, unsigned int len) | 
 | { | 
 | 	int i, rc = 0; | 
 |  | 
 | 	for (i = 0; i < len; i += 8, offset += 8U) { | 
 | 		rc = write_dword(dev, offset, | 
 | 				UNALIGNED_GET((const uint64_t *) data + (i >> 3))); | 
 | 		if (rc < 0) { | 
 | 			return rc; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | void flash_stm32_page_layout(const struct device *dev, | 
 | 			     const struct flash_pages_layout **layout, | 
 | 			     size_t *layout_size) | 
 | { | 
 | 	static struct flash_pages_layout stm32wb_flash_layout = { | 
 | 		.pages_count = 0, | 
 | 		.pages_size = 0, | 
 | 	}; | 
 |  | 
 | 	ARG_UNUSED(dev); | 
 |  | 
 | 	if (stm32wb_flash_layout.pages_count == 0) { | 
 | 		stm32wb_flash_layout.pages_count = FLASH_SIZE / FLASH_PAGE_SIZE; | 
 | 		stm32wb_flash_layout.pages_size = FLASH_PAGE_SIZE; | 
 | 	} | 
 |  | 
 | 	*layout = &stm32wb_flash_layout; | 
 | 	*layout_size = 1; | 
 | } | 
 |  | 
 | int flash_stm32_check_status(const struct device *dev) | 
 | { | 
 | 	FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); | 
 | 	uint32_t error = 0; | 
 |  | 
 | 	/* Save Flash errors */ | 
 | 	error = (regs->SR & FLASH_FLAG_SR_ERRORS); | 
 | 	error |= (regs->ECCR & FLASH_FLAG_ECCC); | 
 |  | 
 | 	/* Clear systematic Option and Engineering bits validity error */ | 
 | 	if (error & FLASH_FLAG_OPTVERR) { | 
 | 		regs->SR |= FLASH_FLAG_SR_ERRORS; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (error) { | 
 | 		return -EIO; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } |