| /* |
| * Copyright (c) 2020 Vossloh Cogifer |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT st_stm32h7_flash_controller |
| |
| #include <zephyr/sys/util.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <string.h> |
| #include <zephyr/drivers/flash.h> |
| #include <zephyr/init.h> |
| #include <zephyr/sys/barrier.h> |
| #include <soc.h> |
| #if defined(CONFIG_SOC_SERIES_STM32H7RSX) |
| #include <stm32h7rsxx_ll_bus.h> |
| #include <stm32h7rsxx_ll_utils.h> |
| #else |
| #include <stm32h7xx_ll_bus.h> |
| #include <stm32h7xx_ll_utils.h> |
| #endif /* CONFIG_SOC_SERIES_STM32H7RSX */ |
| |
| #include "flash_stm32.h" |
| #include "stm32_hsem.h" |
| |
| #define LOG_DOMAIN flash_stm32h7 |
| #define LOG_LEVEL CONFIG_FLASH_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(LOG_DOMAIN); |
| |
| /* Let's wait for double the max erase time to be sure that the operation is |
| * completed. |
| */ |
| #define STM32H7_FLASH_TIMEOUT (2 * DT_PROP(DT_INST(0, st_stm32_nv_flash), max_erase_time)) |
| |
| #define STM32H7_M4_FLASH_SIZE DT_PROP_OR(DT_INST(0, st_stm32_nv_flash), bank2_flash_size, 0) |
| #ifdef CONFIG_CPU_CORTEX_M4 |
| #if STM32H7_M4_FLASH_SIZE == 0 |
| #error Flash driver on M4 requires the DT property bank2-flash-size |
| #else |
| #define REAL_FLASH_SIZE_KB (KB(STM32H7_M4_FLASH_SIZE * 2)) |
| #endif |
| #else |
| #define REAL_FLASH_SIZE_KB KB(LL_GetFlashSize()) |
| #endif |
| #define SECTOR_PER_BANK ((REAL_FLASH_SIZE_KB / FLASH_SECTOR_SIZE) / 2) |
| #if defined(DUAL_BANK) |
| #define STM32H7_SERIES_MAX_FLASH_KB KB(2048) |
| #define BANK2_OFFSET (STM32H7_SERIES_MAX_FLASH_KB / 2) |
| /* When flash is dual bank and flash size is smaller than Max flash size of |
| * the serie, there is a discontinuty between bank1 and bank2. |
| */ |
| #define DISCONTINUOUS_BANKS (REAL_FLASH_SIZE_KB < STM32H7_SERIES_MAX_FLASH_KB) |
| #endif |
| |
| struct flash_stm32_sector_t { |
| int sector_index; |
| int bank; |
| volatile uint32_t *cr; |
| volatile uint32_t *sr; |
| }; |
| |
| #if defined(CONFIG_MULTITHREADING) || defined(CONFIG_STM32H7_DUAL_CORE) |
| /* |
| * This is named flash_stm32_sem_take instead of flash_stm32_lock (and |
| * similarly for flash_stm32_sem_give) to avoid confusion with locking |
| * actual flash sectors. |
| */ |
| static inline void _flash_stm32_sem_take(const struct device *dev) |
| { |
| k_sem_take(&FLASH_STM32_PRIV(dev)->sem, K_FOREVER); |
| z_stm32_hsem_lock(CFG_HW_FLASH_SEMID, HSEM_LOCK_WAIT_FOREVER); |
| } |
| |
| static inline void _flash_stm32_sem_give(const struct device *dev) |
| { |
| z_stm32_hsem_unlock(CFG_HW_FLASH_SEMID); |
| k_sem_give(&FLASH_STM32_PRIV(dev)->sem); |
| } |
| |
| #define flash_stm32_sem_init(dev) k_sem_init(&FLASH_STM32_PRIV(dev)->sem, 1, 1) |
| #define flash_stm32_sem_take(dev) _flash_stm32_sem_take(dev) |
| #define flash_stm32_sem_give(dev) _flash_stm32_sem_give(dev) |
| #else |
| #define flash_stm32_sem_init(dev) |
| #define flash_stm32_sem_take(dev) |
| #define flash_stm32_sem_give(dev) |
| #endif |
| |
| bool flash_stm32_valid_range(const struct device *dev, off_t offset, uint32_t len, bool write) |
| { |
| #if defined(DUAL_BANK) |
| if (DISCONTINUOUS_BANKS) { |
| /* |
| * In case of bank1/2 discontinuity, the range should not |
| * start before bank2 and end beyond bank1 at the same time. |
| * Locations beyond bank2 are caught by flash_stm32_range_exists |
| */ |
| if ((offset < BANK2_OFFSET) && (offset + len > REAL_FLASH_SIZE_KB / 2)) { |
| LOG_ERR("Range ovelaps flash bank discontinuity"); |
| return false; |
| } |
| } |
| #endif |
| |
| if (write) { |
| if ((offset % (FLASH_NB_32BITWORD_IN_FLASHWORD * 4)) != 0) { |
| LOG_ERR("Write offset not aligned on flashword length. " |
| "Offset: 0x%lx, flashword length: %d", |
| (unsigned long)offset, FLASH_NB_32BITWORD_IN_FLASHWORD * 4); |
| return false; |
| } |
| } |
| return flash_stm32_range_exists(dev, offset, len); |
| } |
| |
| static int flash_stm32_check_status(const struct device *dev) |
| { |
| FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); |
| /* The hardware corrects single ECC errors and detects double |
| * ECC errors. Corrected data is returned for single ECC |
| * errors, so in this case we just log a warning. |
| */ |
| #ifdef DUAL_BANK |
| uint32_t const error_bank2 = (FLASH_FLAG_ALL_ERRORS_BANK2 & ~FLASH_FLAG_SNECCERR_BANK2); |
| #endif |
| uint32_t sr; |
| |
| #if defined(CONFIG_SOC_SERIES_STM32H7RSX) |
| uint32_t const error_bank = |
| (FLASH_FLAG_ECC_ERRORS & ~FLASH_FLAG_SNECCERR & ~FLASH_FLAG_DBECCERR); |
| |
| /* Read the Interrupt status flags. */ |
| sr = regs->ISR; |
| if (sr & (FLASH_FLAG_SNECCERR)) { |
| uint32_t word = regs->ECCSFADDR & FLASH_ECCSFADDR_SEC_FADD; |
| |
| LOG_WRN("Bank%d ECC error at 0x%08x", 1, |
| word * 4 * FLASH_NB_32BITWORD_IN_FLASHWORD); |
| } |
| |
| if (sr & (FLASH_FLAG_DBECCERR)) { |
| uint32_t word = regs->ECCDFADDR & FLASH_ECCDFADDR_DED_FADD; |
| |
| LOG_WRN("Bank%d ECC error at 0x%08x", 1, |
| word * 4 * FLASH_NB_32BITWORD_IN_FLASHWORD); |
| } |
| |
| /* Clear the ECC flags (including FA) */ |
| regs->ICR = FLASH_FLAG_ECC_ERRORS; |
| if (sr & error_bank) { |
| #else |
| uint32_t const error_bank1 = (FLASH_FLAG_ALL_ERRORS_BANK1 & ~FLASH_FLAG_SNECCERR_BANK1); |
| |
| /* Read the status flags. */ |
| sr = regs->SR1; |
| if (sr & (FLASH_FLAG_SNECCERR_BANK1 | FLASH_FLAG_DBECCERR_BANK1)) { |
| uint32_t word = regs->ECC_FA1 & FLASH_ECC_FA_FAIL_ECC_ADDR; |
| |
| LOG_WRN("Bank%d ECC error at 0x%08x", 1, |
| word * 4 * FLASH_NB_32BITWORD_IN_FLASHWORD); |
| } |
| /* Clear the flags (including FA1R) */ |
| regs->CCR1 = FLASH_FLAG_ALL_BANK1; |
| |
| if (sr & error_bank1) { |
| #endif /* CONFIG_SOC_SERIES_STM32H7RSX */ |
| LOG_ERR("Status Bank%d: 0x%08x", 1, sr); |
| return -EIO; |
| } |
| |
| #ifdef DUAL_BANK |
| sr = regs->SR2; |
| if (sr & (FLASH_FLAG_SNECCERR_BANK1 | FLASH_FLAG_DBECCERR_BANK1)) { |
| uint32_t word = regs->ECC_FA2 & FLASH_ECC_FA_FAIL_ECC_ADDR; |
| |
| LOG_WRN("Bank%d ECC error at 0x%08x", 2, |
| word * 4 * FLASH_NB_32BITWORD_IN_FLASHWORD); |
| } |
| regs->CCR2 = FLASH_FLAG_ALL_BANK2; |
| if (sr & error_bank2) { |
| /* Sometimes the STRBERR is seen when writing to flash |
| * from M4 (upper 128KiB) with code running from lower |
| * 896KiB. Don't know why it happens, but technical |
| * reference manual (section 4.7.4) says application can |
| * ignore this error and continue with normal write. So |
| * check and return here if the error is STRBERR and clear |
| * the error by setting CCR2 bit. |
| */ |
| if (sr & FLASH_FLAG_STRBERR_BANK2) { |
| regs->CCR2 |= FLASH_FLAG_STRBERR_BANK2; |
| return 0; |
| } |
| LOG_ERR("Status Bank%d: 0x%08x", 2, sr); |
| return -EIO; |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| int flash_stm32_wait_flash_idle(const struct device *dev) |
| { |
| int64_t timeout_time = k_uptime_get() + STM32H7_FLASH_TIMEOUT; |
| int rc; |
| |
| rc = flash_stm32_check_status(dev); |
| if (rc < 0) { |
| return -EIO; |
| } |
| #ifdef DUAL_BANK |
| while ((FLASH_STM32_REGS(dev)->SR1 & FLASH_SR_QW) || |
| (FLASH_STM32_REGS(dev)->SR2 & FLASH_SR_QW)) |
| #else |
| while (FLASH_STM32_REGS(dev)->SR1 & FLASH_SR_QW) |
| #endif |
| { |
| if (k_uptime_get() > timeout_time) { |
| LOG_ERR("Timeout! val: %d", STM32H7_FLASH_TIMEOUT); |
| return -EIO; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static struct flash_stm32_sector_t get_sector(const struct device *dev, off_t offset) |
| { |
| struct flash_stm32_sector_t sector; |
| FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); |
| |
| #ifdef DUAL_BANK |
| off_t temp_offset = offset + (CONFIG_FLASH_BASE_ADDRESS & 0xffffff); |
| |
| bool bank_swap; |
| /* Check whether bank1/2 are swapped */ |
| bank_swap = (READ_BIT(FLASH->OPTCR, FLASH_OPTCR_SWAP_BANK) == FLASH_OPTCR_SWAP_BANK); |
| sector.sector_index = offset / FLASH_SECTOR_SIZE; |
| if ((temp_offset < (REAL_FLASH_SIZE_KB / 2)) && !bank_swap) { |
| sector.bank = 1; |
| sector.cr = ®s->CR1; |
| sector.sr = ®s->SR1; |
| } else if ((temp_offset >= BANK2_OFFSET) && bank_swap) { |
| sector.sector_index -= BANK2_OFFSET / FLASH_SECTOR_SIZE; |
| sector.bank = 1; |
| sector.cr = ®s->CR2; |
| sector.sr = ®s->SR2; |
| } else if ((temp_offset < (REAL_FLASH_SIZE_KB / 2)) && bank_swap) { |
| sector.bank = 2; |
| sector.cr = ®s->CR1; |
| sector.sr = ®s->SR1; |
| } else if ((temp_offset >= BANK2_OFFSET) && !bank_swap) { |
| sector.sector_index -= BANK2_OFFSET / FLASH_SECTOR_SIZE; |
| sector.bank = 2; |
| sector.cr = ®s->CR2; |
| sector.sr = ®s->SR2; |
| } else { |
| sector.sector_index = 0; |
| sector.bank = 0; |
| sector.cr = NULL; |
| sector.sr = NULL; |
| } |
| #else |
| if (offset < REAL_FLASH_SIZE_KB) { |
| sector.sector_index = offset / FLASH_SECTOR_SIZE; |
| sector.bank = 1; |
| sector.cr = ®s->CR1; |
| sector.sr = ®s->SR1; |
| } else { |
| sector.sector_index = 0; |
| sector.bank = 0; |
| sector.cr = NULL; |
| sector.sr = NULL; |
| } |
| #endif |
| |
| return sector; |
| } |
| |
| static int erase_sector(const struct device *dev, int offset) |
| { |
| int rc; |
| struct flash_stm32_sector_t sector = get_sector(dev, offset); |
| |
| if (sector.bank == 0) { |
| |
| LOG_ERR("Offset %ld does not exist", (long)offset); |
| return -EINVAL; |
| } |
| |
| /* if the control register is locked, do not fail silently */ |
| if (*(sector.cr) & FLASH_CR_LOCK) { |
| return -EIO; |
| } |
| |
| rc = flash_stm32_wait_flash_idle(dev); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| *(sector.cr) &= ~FLASH_CR_SNB; |
| *(sector.cr) |= (FLASH_CR_SER | ((sector.sector_index << FLASH_CR_SNB_Pos) & FLASH_CR_SNB)); |
| *(sector.cr) |= FLASH_CR_START; |
| /* flush the register write */ |
| barrier_dsync_fence_full(); |
| |
| rc = flash_stm32_wait_flash_idle(dev); |
| *(sector.cr) &= ~(FLASH_CR_SER | FLASH_CR_SNB); |
| |
| return rc; |
| } |
| |
| int flash_stm32_block_erase_loop(const struct device *dev, unsigned int offset, unsigned int len) |
| { |
| unsigned int address = offset; |
| int rc = 0; |
| |
| for (; address <= offset + len - 1; address += FLASH_SECTOR_SIZE) { |
| rc = erase_sector(dev, address); |
| if (rc < 0) { |
| break; |
| } |
| } |
| return rc; |
| } |
| |
| static int wait_write_queue(const struct flash_stm32_sector_t *sector) |
| { |
| int64_t timeout_time = k_uptime_get() + 100; |
| |
| while (*(sector->sr) & FLASH_SR_QW) { |
| if (k_uptime_get() > timeout_time) { |
| LOG_ERR("Timeout! val: %d", 100); |
| return -EIO; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int write_ndwords(const struct device *dev, off_t offset, const uint64_t *data, uint8_t n) |
| { |
| volatile uint64_t *flash = (uint64_t *)(offset + FLASH_STM32_BASE_ADDRESS); |
| int rc; |
| int i; |
| struct flash_stm32_sector_t sector = get_sector(dev, offset); |
| |
| if (sector.bank == 0) { |
| LOG_ERR("Offset %ld does not exist", (long)offset); |
| return -EINVAL; |
| } |
| |
| /* if the control register is locked, do not fail silently */ |
| if (*(sector.cr) & FLASH_CR_LOCK) { |
| return -EIO; |
| } |
| |
| /* Check that no Flash main memory operation is ongoing */ |
| rc = flash_stm32_wait_flash_idle(dev); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| /* Check if 256 bits location is erased */ |
| for (i = 0; i < n; ++i) { |
| if (flash[i] != 0xFFFFFFFFFFFFFFFFUL) { |
| return -EIO; |
| } |
| } |
| |
| /* Set the PG bit */ |
| *(sector.cr) |= FLASH_CR_PG; |
| |
| /* Flush the register write */ |
| barrier_dsync_fence_full(); |
| |
| /* Perform the data write operation at the desired memory address */ |
| for (i = 0; i < n; ++i) { |
| /* Source dword may be unaligned, so take extra care when dereferencing it */ |
| flash[i] = UNALIGNED_GET(data + i); |
| |
| /* Flush the data write */ |
| barrier_dsync_fence_full(); |
| |
| wait_write_queue(§or); |
| } |
| |
| /* Wait until the BSY bit is cleared */ |
| rc = flash_stm32_wait_flash_idle(dev); |
| |
| /* Clear the PG bit */ |
| *(sector.cr) &= (~FLASH_CR_PG); |
| |
| return rc; |
| } |
| |
| int flash_stm32_write_range(const struct device *dev, unsigned int offset, const void *data, |
| unsigned int len) |
| { |
| int rc = 0; |
| int i, j; |
| const uint8_t ndwords = FLASH_NB_32BITWORD_IN_FLASHWORD / 2; |
| const uint8_t nbytes = FLASH_NB_32BITWORD_IN_FLASHWORD * 4; |
| uint8_t unaligned_datas[nbytes]; |
| |
| for (i = 0; i < len && i + nbytes <= len; i += nbytes, offset += nbytes) { |
| rc = write_ndwords(dev, offset, (const uint64_t *)data + (i >> 3), ndwords); |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| |
| /* Handle the remaining bytes if length is not aligned on |
| * FLASH_NB_32BITWORD_IN_FLASHWORD |
| */ |
| if (i < len) { |
| memset(unaligned_datas, 0xff, sizeof(unaligned_datas)); |
| for (j = 0; j < len - i; ++j) { |
| unaligned_datas[j] = ((uint8_t *)data)[i + j]; |
| } |
| rc = write_ndwords(dev, offset, (const uint64_t *)unaligned_datas, ndwords); |
| if (rc < 0) { |
| return rc; |
| } |
| } |
| |
| return rc; |
| } |
| |
| static int flash_stm32h7_write_protection(const struct device *dev, bool enable) |
| { |
| FLASH_TypeDef *regs = FLASH_STM32_REGS(dev); |
| |
| int rc = 0; |
| |
| if (enable) { |
| rc = flash_stm32_wait_flash_idle(dev); |
| if (rc) { |
| return rc; |
| } |
| } |
| |
| /* Bank 1 */ |
| if (enable) { |
| regs->CR1 |= FLASH_CR_LOCK; |
| } else { |
| if (regs->CR1 & FLASH_CR_LOCK) { |
| regs->KEYR1 = FLASH_KEY1; |
| regs->KEYR1 = FLASH_KEY2; |
| } |
| } |
| #ifdef DUAL_BANK |
| /* Bank 2 */ |
| if (enable) { |
| regs->CR2 |= FLASH_CR_LOCK; |
| } else { |
| if (regs->CR2 & FLASH_CR_LOCK) { |
| regs->KEYR2 = FLASH_KEY1; |
| regs->KEYR2 = FLASH_KEY2; |
| } |
| } |
| #endif |
| |
| if (enable) { |
| LOG_DBG("Enable write protection"); |
| } else { |
| LOG_DBG("Disable write protection"); |
| } |
| |
| return rc; |
| } |
| |
| #ifdef CONFIG_CPU_CORTEX_M7 |
| static void flash_stm32h7_flush_caches(const struct device *dev, off_t offset, size_t len) |
| { |
| ARG_UNUSED(dev); |
| |
| if (!(SCB->CCR & SCB_CCR_DC_Msk)) { |
| return; /* Cache not enabled */ |
| } |
| |
| SCB_InvalidateDCache_by_Addr((uint32_t *)(FLASH_STM32_BASE_ADDRESS + offset), len); |
| } |
| #endif /* CONFIG_CPU_CORTEX_M7 */ |
| |
| static int flash_stm32h7_erase(const struct device *dev, off_t offset, size_t len) |
| { |
| int rc, rc2; |
| |
| #ifdef CONFIG_CPU_CORTEX_M7 |
| /* Flush whole sectors */ |
| off_t flush_offset = ROUND_DOWN(offset, FLASH_SECTOR_SIZE); |
| size_t flush_len = ROUND_UP(offset + len - 1, FLASH_SECTOR_SIZE) - flush_offset; |
| #endif /* CONFIG_CPU_CORTEX_M7 */ |
| |
| if (!flash_stm32_valid_range(dev, offset, len, true)) { |
| LOG_ERR("Erase range invalid. Offset: %ld, len: %zu", (long)offset, len); |
| return -EINVAL; |
| } |
| |
| if (!len) { |
| return 0; |
| } |
| |
| flash_stm32_sem_take(dev); |
| |
| LOG_DBG("Erase offset: %ld, len: %zu", (long)offset, len); |
| |
| rc = flash_stm32h7_write_protection(dev, false); |
| if (rc) { |
| goto done; |
| } |
| |
| rc = flash_stm32_block_erase_loop(dev, offset, len); |
| |
| #ifdef CONFIG_CPU_CORTEX_M7 |
| /* Flush cache on all sectors affected by the erase */ |
| flash_stm32h7_flush_caches(dev, flush_offset, flush_len); |
| #elif CONFIG_CPU_CORTEX_M4 |
| if (LL_AHB1_GRP1_IsEnabledClock(LL_AHB1_GRP1_PERIPH_ART) && LL_ART_IsEnabled()) { |
| LOG_ERR("Cortex M4: ART enabled not supported by flash driver"); |
| } |
| #endif /* CONFIG_CPU_CORTEX_M7 */ |
| done: |
| rc2 = flash_stm32h7_write_protection(dev, true); |
| |
| if (!rc) { |
| rc = rc2; |
| } |
| |
| flash_stm32_sem_give(dev); |
| |
| return rc; |
| } |
| |
| static int flash_stm32h7_write(const struct device *dev, off_t offset, const void *data, size_t len) |
| { |
| int rc; |
| |
| if (!flash_stm32_valid_range(dev, offset, len, true)) { |
| LOG_ERR("Write range invalid. Offset: %ld, len: %zu", (long)offset, len); |
| return -EINVAL; |
| } |
| |
| if (!len) { |
| return 0; |
| } |
| |
| flash_stm32_sem_take(dev); |
| |
| LOG_DBG("Write offset: %ld, len: %zu", (long)offset, len); |
| |
| rc = flash_stm32h7_write_protection(dev, false); |
| if (!rc) { |
| rc = flash_stm32_write_range(dev, offset, data, len); |
| } |
| |
| int rc2 = flash_stm32h7_write_protection(dev, true); |
| |
| if (!rc) { |
| rc = rc2; |
| } |
| |
| flash_stm32_sem_give(dev); |
| |
| return rc; |
| } |
| |
| static int flash_stm32h7_read(const struct device *dev, off_t offset, void *data, size_t len) |
| { |
| if (!flash_stm32_valid_range(dev, offset, len, false)) { |
| LOG_ERR("Read range invalid. Offset: %ld, len: %zu", (long)offset, len); |
| return -EINVAL; |
| } |
| |
| if (!len) { |
| return 0; |
| } |
| |
| LOG_DBG("Read offset: %ld, len: %zu", (long)offset, len); |
| |
| /* During the read we mask bus errors and only allow NMI. |
| * |
| * If the flash has a double ECC error then there is normally |
| * a bus fault, but we want to return an error code instead. |
| */ |
| unsigned int irq_lock_key = irq_lock(); |
| |
| __set_FAULTMASK(1); |
| SCB->CCR |= SCB_CCR_BFHFNMIGN_Msk; |
| barrier_dsync_fence_full(); |
| barrier_isync_fence_full(); |
| |
| memcpy(data, (uint8_t *)FLASH_STM32_BASE_ADDRESS + offset, len); |
| |
| __set_FAULTMASK(0); |
| SCB->CCR &= ~SCB_CCR_BFHFNMIGN_Msk; |
| barrier_dsync_fence_full(); |
| barrier_isync_fence_full(); |
| irq_unlock(irq_lock_key); |
| |
| return flash_stm32_check_status(dev); |
| } |
| |
| static const struct flash_parameters flash_stm32h7_parameters = { |
| .write_block_size = FLASH_STM32_WRITE_BLOCK_SIZE, |
| .erase_value = 0xff, |
| }; |
| |
| static const struct flash_parameters *flash_stm32h7_get_parameters(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return &flash_stm32h7_parameters; |
| } |
| |
| void flash_stm32_page_layout(const struct device *dev, const struct flash_pages_layout **layout, |
| size_t *layout_size) |
| { |
| ARG_UNUSED(dev); |
| |
| #if defined(DUAL_BANK) |
| static struct flash_pages_layout stm32h7_flash_layout[3]; |
| |
| if (DISCONTINUOUS_BANKS) { |
| if (stm32h7_flash_layout[0].pages_count == 0) { |
| /* Bank1 */ |
| stm32h7_flash_layout[0].pages_count = SECTOR_PER_BANK; |
| stm32h7_flash_layout[0].pages_size = FLASH_SECTOR_SIZE; |
| /* |
| * Dummy page corresponding to discontinuity |
| * between bank1/2 |
| */ |
| stm32h7_flash_layout[1].pages_count = 1; |
| stm32h7_flash_layout[1].pages_size = |
| BANK2_OFFSET - (SECTOR_PER_BANK * FLASH_SECTOR_SIZE); |
| /* Bank2 */ |
| stm32h7_flash_layout[2].pages_count = SECTOR_PER_BANK; |
| stm32h7_flash_layout[2].pages_size = FLASH_SECTOR_SIZE; |
| } |
| *layout_size = ARRAY_SIZE(stm32h7_flash_layout); |
| } else { |
| if (stm32h7_flash_layout[0].pages_count == 0) { |
| stm32h7_flash_layout[0].pages_count = |
| REAL_FLASH_SIZE_KB / FLASH_SECTOR_SIZE; |
| stm32h7_flash_layout[0].pages_size = FLASH_SECTOR_SIZE; |
| } |
| *layout_size = 1; |
| } |
| #else |
| static struct flash_pages_layout stm32h7_flash_layout[1]; |
| |
| if (stm32h7_flash_layout[0].pages_count == 0) { |
| stm32h7_flash_layout[0].pages_count = REAL_FLASH_SIZE_KB / FLASH_SECTOR_SIZE; |
| stm32h7_flash_layout[0].pages_size = FLASH_SECTOR_SIZE; |
| } |
| *layout_size = ARRAY_SIZE(stm32h7_flash_layout); |
| #endif |
| *layout = stm32h7_flash_layout; |
| } |
| |
| static struct flash_stm32_priv flash_data = { |
| .regs = (FLASH_TypeDef *)DT_INST_REG_ADDR(0), |
| #if DT_NODE_HAS_PROP(DT_INST(0, st_stm32h7_flash_controller), clocks) |
| .pclken = {.bus = DT_INST_CLOCKS_CELL(0, bus), .enr = DT_INST_CLOCKS_CELL(0, bits)}, |
| #endif |
| }; |
| |
| static const struct flash_driver_api flash_stm32h7_api = { |
| .erase = flash_stm32h7_erase, |
| .write = flash_stm32h7_write, |
| .read = flash_stm32h7_read, |
| .get_parameters = flash_stm32h7_get_parameters, |
| #ifdef CONFIG_FLASH_PAGE_LAYOUT |
| .page_layout = flash_stm32_page_layout, |
| #endif |
| }; |
| |
| static int stm32h7_flash_init(const struct device *dev) |
| { |
| #if DT_NODE_HAS_PROP(DT_INST(0, st_stm32h7_flash_controller), clocks) |
| /* Only stm32h7 dual core devices have the clocks property */ |
| struct flash_stm32_priv *p = FLASH_STM32_PRIV(dev); |
| const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); |
| |
| if (!device_is_ready(clk)) { |
| LOG_ERR("clock control device not ready"); |
| return -ENODEV; |
| } |
| |
| /* enable clock : enable the RCC_AHB3ENR_FLASHEN bit */ |
| if (clock_control_on(clk, (clock_control_subsys_t)&p->pclken) != 0) { |
| LOG_ERR("Failed to enable clock"); |
| return -EIO; |
| } |
| #endif |
| flash_stm32_sem_init(dev); |
| |
| LOG_DBG("Flash initialized. BS: %zu", flash_stm32h7_parameters.write_block_size); |
| |
| #if ((CONFIG_FLASH_LOG_LEVEL >= LOG_LEVEL_DBG) && CONFIG_FLASH_PAGE_LAYOUT) |
| const struct flash_pages_layout *layout; |
| size_t layout_size; |
| |
| flash_stm32_page_layout(dev, &layout, &layout_size); |
| for (size_t i = 0; i < layout_size; i++) { |
| LOG_DBG("Block %zu: bs: %zu count: %zu", i, layout[i].pages_size, |
| layout[i].pages_count); |
| } |
| #endif |
| |
| return flash_stm32h7_write_protection(dev, false); |
| } |
| |
| DEVICE_DT_INST_DEFINE(0, stm32h7_flash_init, NULL, &flash_data, NULL, POST_KERNEL, |
| CONFIG_FLASH_INIT_PRIORITY, &flash_stm32h7_api); |