/* * Copyright (c) 2020 Piotr Mienkowski * Copyright (c) 2020 Linaro Limited * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT st_stm32_qspi_nor #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spi_nor.h" #include "jesd216.h" #include LOG_MODULE_REGISTER(flash_stm32_qspi, CONFIG_FLASH_LOG_LEVEL); #define STM32_QSPI_FIFO_THRESHOLD 8 #define STM32_QSPI_CLOCK_PRESCALER_MAX 255 #define STM32_QSPI_USE_DMA DT_NODE_HAS_PROP(DT_INST_PARENT(0), dmas) #if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_qspi_nor) uint32_t table_m_size[] = { LL_DMA_MDATAALIGN_BYTE, LL_DMA_MDATAALIGN_HALFWORD, LL_DMA_MDATAALIGN_WORD, }; uint32_t table_p_size[] = { LL_DMA_PDATAALIGN_BYTE, LL_DMA_PDATAALIGN_HALFWORD, LL_DMA_PDATAALIGN_WORD, }; typedef void (*irq_config_func_t)(const struct device *dev); struct stream { DMA_TypeDef *reg; const struct device *dev; uint32_t channel; struct dma_config cfg; }; struct flash_stm32_qspi_config { QUADSPI_TypeDef *regs; struct stm32_pclken pclken; irq_config_func_t irq_config; size_t flash_size; uint32_t max_frequency; const struct pinctrl_dev_config *pcfg; }; struct flash_stm32_qspi_data { QSPI_HandleTypeDef hqspi; struct k_sem sem; struct k_sem sync; #if defined(CONFIG_FLASH_PAGE_LAYOUT) struct flash_pages_layout layout; #endif struct jesd216_erase_type erase_types[JESD216_NUM_ERASE_TYPES]; /* Number of bytes per page */ uint16_t page_size; int cmd_status; struct stream dma; }; #define DEV_NAME(dev) ((dev)->name) #define DEV_CFG(dev) \ (const struct flash_stm32_qspi_config * const)(dev->config) #define DEV_DATA(dev) \ (struct flash_stm32_qspi_data * const)(dev->data) static inline void qspi_lock_thread(const struct device *dev) { struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); k_sem_take(&dev_data->sem, K_FOREVER); } static inline void qspi_unlock_thread(const struct device *dev) { struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); k_sem_give(&dev_data->sem); } /* * Send a command over QSPI bus. */ static int qspi_send_cmd(const struct device *dev, QSPI_CommandTypeDef *cmd) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); HAL_StatusTypeDef hal_ret; ARG_UNUSED(dev_cfg); LOG_DBG("Instruction 0x%x", cmd->Instruction); dev_data->cmd_status = 0; hal_ret = HAL_QSPI_Command_IT(&dev_data->hqspi, cmd); if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to send QSPI instruction", hal_ret); return -EIO; } LOG_DBG("CCR 0x%x", dev_cfg->regs->CCR); k_sem_take(&dev_data->sync, K_FOREVER); return dev_data->cmd_status; } /* * Perform a read access over QSPI bus. */ static int qspi_read_access(const struct device *dev, QSPI_CommandTypeDef *cmd, uint8_t *data, size_t size) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); HAL_StatusTypeDef hal_ret; ARG_UNUSED(dev_cfg); cmd->NbData = size; dev_data->cmd_status = 0; hal_ret = HAL_QSPI_Command_IT(&dev_data->hqspi, cmd); if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to send QSPI instruction", hal_ret); return -EIO; } #if STM32_QSPI_USE_DMA hal_ret = HAL_QSPI_Receive_DMA(&dev_data->hqspi, data); #else hal_ret = HAL_QSPI_Receive_IT(&dev_data->hqspi, data); #endif if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to read data", hal_ret); return -EIO; } k_sem_take(&dev_data->sync, K_FOREVER); return dev_data->cmd_status; } /* * Perform a write access over QSPI bus. */ static int qspi_write_access(const struct device *dev, QSPI_CommandTypeDef *cmd, const uint8_t *data, size_t size) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); HAL_StatusTypeDef hal_ret; ARG_UNUSED(dev_cfg); LOG_DBG("Instruction 0x%x", cmd->Instruction); cmd->NbData = size; dev_data->cmd_status = 0; hal_ret = HAL_QSPI_Command_IT(&dev_data->hqspi, cmd); if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to send QSPI instruction", hal_ret); return -EIO; } #if STM32_QSPI_USE_DMA hal_ret = HAL_QSPI_Transmit_DMA(&dev_data->hqspi, (uint8_t *)data); #else hal_ret = HAL_QSPI_Transmit_IT(&dev_data->hqspi, (uint8_t *)data); #endif if (hal_ret != HAL_OK) { LOG_ERR("%d: Failed to read data", hal_ret); return -EIO; } LOG_DBG("CCR 0x%x", dev_cfg->regs->CCR); k_sem_take(&dev_data->sync, K_FOREVER); return dev_data->cmd_status; } /* * Read Serial Flash Discovery Parameter */ static int qspi_read_sfdp(const struct device *dev, off_t addr, uint8_t *data, size_t size) { QSPI_CommandTypeDef cmd = { .Instruction = JESD216_CMD_READ_SFDP, .Address = addr, .AddressSize = QSPI_ADDRESS_24_BITS, .DummyCycles = 8, .InstructionMode = QSPI_INSTRUCTION_1_LINE, .AddressMode = QSPI_ADDRESS_1_LINE, .DataMode = QSPI_DATA_1_LINE, }; return qspi_read_access(dev, &cmd, data, size); } static bool qspi_address_is_valid(const struct device *dev, off_t addr, size_t size) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); size_t flash_size = dev_cfg->flash_size; return (addr >= 0) && ((uint64_t)addr + (uint64_t)size <= flash_size); } static int flash_stm32_qspi_read(const struct device *dev, off_t addr, void *data, size_t size) { int ret; if (!qspi_address_is_valid(dev, addr, size)) { LOG_DBG("Error: address or size exceeds expected values: " "addr 0x%lx, size %zu", (long)addr, size); return -EINVAL; } QSPI_CommandTypeDef cmd = { .Instruction = SPI_NOR_CMD_READ, .Address = addr, .AddressSize = QSPI_ADDRESS_24_BITS, .InstructionMode = QSPI_INSTRUCTION_1_LINE, .AddressMode = QSPI_ADDRESS_1_LINE, .DataMode = QSPI_DATA_1_LINE, }; qspi_lock_thread(dev); ret = qspi_read_access(dev, &cmd, data, size); qspi_unlock_thread(dev); return ret; } static int qspi_wait_until_ready(const struct device *dev) { uint8_t reg; int ret; QSPI_CommandTypeDef cmd = { .Instruction = SPI_NOR_CMD_RDSR, .InstructionMode = QSPI_INSTRUCTION_1_LINE, .DataMode = QSPI_DATA_1_LINE, }; do { ret = qspi_read_access(dev, &cmd, ®, sizeof(reg)); } while (!ret && (reg & SPI_NOR_WIP_BIT)); return ret; } static int flash_stm32_qspi_write(const struct device *dev, off_t addr, const void *data, size_t size) { int ret = 0; if (!qspi_address_is_valid(dev, addr, size)) { LOG_DBG("Error: address or size exceeds expected values: " "addr 0x%lx, size %zu", (long)addr, size); return -EINVAL; } QSPI_CommandTypeDef cmd_write_en = { .Instruction = SPI_NOR_CMD_WREN, .InstructionMode = QSPI_INSTRUCTION_1_LINE, }; QSPI_CommandTypeDef cmd_pp = { .Instruction = SPI_NOR_CMD_PP, .AddressSize = QSPI_ADDRESS_24_BITS, .InstructionMode = QSPI_INSTRUCTION_1_LINE, .AddressMode = QSPI_ADDRESS_1_LINE, .DataMode = QSPI_DATA_1_LINE, }; qspi_lock_thread(dev); while (size > 0) { size_t to_write = size; /* Don't write more than a page. */ if (to_write >= SPI_NOR_PAGE_SIZE) { to_write = SPI_NOR_PAGE_SIZE; } /* Don't write across a page boundary */ if (((addr + to_write - 1U) / SPI_NOR_PAGE_SIZE) != (addr / SPI_NOR_PAGE_SIZE)) { to_write = SPI_NOR_PAGE_SIZE - (addr % SPI_NOR_PAGE_SIZE); } ret = qspi_send_cmd(dev, &cmd_write_en); if (ret != 0) { break; } cmd_pp.Address = addr; ret = qspi_write_access(dev, &cmd_pp, data, to_write); if (ret != 0) { break; } size -= to_write; data = (const uint8_t *)data + to_write; addr += to_write; ret = qspi_wait_until_ready(dev); if (ret != 0) { break; } } qspi_unlock_thread(dev); return ret; } static int flash_stm32_qspi_erase(const struct device *dev, off_t addr, size_t size) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); int ret = 0; if (!qspi_address_is_valid(dev, addr, size)) { LOG_DBG("Error: address or size exceeds expected values: " "addr 0x%lx, size %zu", (long)addr, size); return -EINVAL; } QSPI_CommandTypeDef cmd_write_en = { .Instruction = SPI_NOR_CMD_WREN, .InstructionMode = QSPI_INSTRUCTION_1_LINE, }; QSPI_CommandTypeDef cmd_erase = { .Instruction = 0, .AddressSize = QSPI_ADDRESS_24_BITS, .InstructionMode = QSPI_INSTRUCTION_1_LINE, .AddressMode = QSPI_ADDRESS_1_LINE, }; qspi_lock_thread(dev); while ((size > 0) && (ret == 0)) { cmd_erase.Address = addr; qspi_send_cmd(dev, &cmd_write_en); if (size == dev_cfg->flash_size) { /* chip erase */ cmd_erase.Instruction = SPI_NOR_CMD_CE; cmd_erase.AddressMode = QSPI_ADDRESS_NONE; qspi_send_cmd(dev, &cmd_erase); size -= dev_cfg->flash_size; } else { const struct jesd216_erase_type *erase_types = dev_data->erase_types; const struct jesd216_erase_type *bet = NULL; for (uint8_t ei = 0; ei < JESD216_NUM_ERASE_TYPES; ++ei) { const struct jesd216_erase_type *etp = &erase_types[ei]; if ((etp->exp != 0) && SPI_NOR_IS_ALIGNED(addr, etp->exp) && SPI_NOR_IS_ALIGNED(size, etp->exp) && ((bet == NULL) || (etp->exp > bet->exp))) { bet = etp; cmd_erase.Instruction = bet->cmd; } } if (bet != NULL) { qspi_send_cmd(dev, &cmd_erase); addr += BIT(bet->exp); size -= BIT(bet->exp); } else { LOG_ERR("Can't erase %zu at 0x%lx", size, (long)addr); ret = -EINVAL; } } qspi_wait_until_ready(dev); } qspi_unlock_thread(dev); return ret; } static const struct flash_parameters flash_stm32_qspi_parameters = { .write_block_size = 1, .erase_value = 0xff }; static const struct flash_parameters * flash_stm32_qspi_get_parameters(const struct device *dev) { ARG_UNUSED(dev); return &flash_stm32_qspi_parameters; } static void flash_stm32_qspi_isr(const struct device *dev) { struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); HAL_QSPI_IRQHandler(&dev_data->hqspi); } /* This function is executed in the interrupt context */ #if STM32_QSPI_USE_DMA static void qspi_dma_callback(const struct device *dev, void *arg, uint32_t channel, int status) { DMA_HandleTypeDef *hdma = arg; if (status != 0) { LOG_ERR("DMA callback error with channel %d.", channel); } HAL_DMA_IRQHandler(hdma); } #endif __weak HAL_StatusTypeDef HAL_DMA_Abort_IT(DMA_HandleTypeDef *hdma) { return HAL_OK; } /* * Transfer Error callback. */ void HAL_QSPI_ErrorCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); LOG_DBG("Enter"); dev_data->cmd_status = -EIO; k_sem_give(&dev_data->sync); } /* * Command completed callback. */ void HAL_QSPI_CmdCpltCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); k_sem_give(&dev_data->sync); } /* * Rx Transfer completed callback. */ void HAL_QSPI_RxCpltCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); k_sem_give(&dev_data->sync); } /* * Tx Transfer completed callback. */ void HAL_QSPI_TxCpltCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); k_sem_give(&dev_data->sync); } /* * Status Match callback. */ void HAL_QSPI_StatusMatchCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); k_sem_give(&dev_data->sync); } /* * Timeout callback. */ void HAL_QSPI_TimeOutCallback(QSPI_HandleTypeDef *hqspi) { struct flash_stm32_qspi_data *dev_data = CONTAINER_OF(hqspi, struct flash_stm32_qspi_data, hqspi); LOG_DBG("Enter"); dev_data->cmd_status = -EIO; k_sem_give(&dev_data->sync); } #if defined(CONFIG_FLASH_PAGE_LAYOUT) static void flash_stm32_qspi_pages_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size) { struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); *layout = &dev_data->layout; *layout_size = 1; } #endif static const struct flash_driver_api flash_stm32_qspi_driver_api = { .read = flash_stm32_qspi_read, .write = flash_stm32_qspi_write, .erase = flash_stm32_qspi_erase, .get_parameters = flash_stm32_qspi_get_parameters, #if defined(CONFIG_FLASH_PAGE_LAYOUT) .page_layout = flash_stm32_qspi_pages_layout, #endif }; #if defined(CONFIG_FLASH_PAGE_LAYOUT) static int setup_pages_layout(const struct device *dev) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *data = DEV_DATA(dev); const size_t flash_size = dev_cfg->flash_size; uint32_t layout_page_size = data->page_size; uint8_t exp = 0; int rv = 0; /* Find the smallest erase size. */ for (size_t i = 0; i < ARRAY_SIZE(data->erase_types); ++i) { const struct jesd216_erase_type *etp = &data->erase_types[i]; if ((etp->cmd != 0) && ((exp == 0) || (etp->exp < exp))) { exp = etp->exp; } } if (exp == 0) { return -ENOTSUP; } uint32_t erase_size = BIT(exp); /* We need layout page size to be compatible with erase size */ if ((layout_page_size % erase_size) != 0) { LOG_DBG("layout page %u not compatible with erase size %u", layout_page_size, erase_size); LOG_DBG("erase size will be used as layout page size"); layout_page_size = erase_size; } /* Warn but accept layout page sizes that leave inaccessible * space. */ if ((flash_size % layout_page_size) != 0) { LOG_INF("layout page %u wastes space with device size %zu", layout_page_size, flash_size); } data->layout.pages_size = layout_page_size; data->layout.pages_count = flash_size / layout_page_size; LOG_DBG("layout %u x %u By pages", data->layout.pages_count, data->layout.pages_size); return rv; } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ static int spi_nor_process_bfp(const struct device *dev, const struct jesd216_param_header *php, const struct jesd216_bfp *bfp) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *data = DEV_DATA(dev); struct jesd216_erase_type *etp = data->erase_types; const size_t flash_size = jesd216_bfp_density(bfp) / 8U; if (flash_size != dev_cfg->flash_size) { LOG_ERR("Unexpected flash size: %u", flash_size); } LOG_INF("%s: %u MiBy flash", dev->name, (uint32_t)(flash_size >> 20)); /* Copy over the erase types, preserving their order. (The * Sector Map Parameter table references them by index.) */ memset(data->erase_types, 0, sizeof(data->erase_types)); for (uint8_t ti = 1; ti <= ARRAY_SIZE(data->erase_types); ++ti) { if (jesd216_bfp_erase(bfp, ti, etp) == 0) { LOG_DBG("Erase %u with %02x", (uint32_t)BIT(etp->exp), etp->cmd); } ++etp; } data->page_size = jesd216_bfp_page_size(php, bfp); LOG_DBG("Page size %u bytes", data->page_size); LOG_DBG("Flash size %u bytes", flash_size); return 0; } static int flash_stm32_qspi_init(const struct device *dev) { const struct flash_stm32_qspi_config *dev_cfg = DEV_CFG(dev); struct flash_stm32_qspi_data *dev_data = DEV_DATA(dev); uint32_t ahb_clock_freq; uint32_t prescaler = 0; int ret; /* Signals configuration */ ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { LOG_ERR("QSPI pinctrl setup failed (%d)", ret); return ret; } #if STM32_QSPI_USE_DMA /* * DMA configuration * Due to use of QSPI HAL API in current driver, * both HAL and Zephyr DMA drivers should be configured. * The required configuration for Zephyr DMA driver should only provide * the minimum information to inform the DMA slot will be in used and * how to route callbacks. */ struct dma_config dma_cfg = dev_data->dma.cfg; static DMA_HandleTypeDef hdma; if (!device_is_ready(dev_data->dma.dev)) { LOG_ERR("%s device not ready", dev_data->dma.dev->name); return -ENODEV; } /* Proceed to the minimum Zephyr DMA driver init */ dma_cfg.user_data = &hdma; /* HACK: This field is used to inform driver that it is overridden */ dma_cfg.linked_channel = STM32_DMA_HAL_OVERRIDE; ret = dma_config(dev_data->dma.dev, dev_data->dma.channel, &dma_cfg); if (ret != 0) { return ret; } /* Proceed to the HAL DMA driver init */ if (dma_cfg.source_data_size != dma_cfg.dest_data_size) { LOG_ERR("Source and destination data sizes not aligned"); return -EINVAL; } int index = find_lsb_set(dma_cfg.source_data_size) - 1; hdma.Init.PeriphDataAlignment = table_p_size[index]; hdma.Init.MemDataAlignment = table_m_size[index]; hdma.Init.PeriphInc = DMA_PINC_DISABLE; hdma.Init.MemInc = DMA_MINC_ENABLE; hdma.Init.Mode = DMA_NORMAL; hdma.Init.Priority = dma_cfg.channel_priority; #ifdef CONFIG_DMA_STM32_V1 /* TODO: Not tested in this configuration */ hdma.Init.Channel = dma_cfg.dma_slot; hdma.Instance = __LL_DMA_GET_STREAM_INSTANCE(dev_data->dma.reg, dev_data->dma.channel); #else hdma.Init.Request = dma_cfg.dma_slot; #ifdef CONFIG_DMAMUX_STM32 /* HAL expects a valid DMA channel (not DAMMUX) */ /* TODO: Get DMA instance from DT */ hdma.Instance = __LL_DMA_GET_CHANNEL_INSTANCE(DMA1, dev_data->dma.channel+1); #else hdma.Instance = __LL_DMA_GET_CHANNEL_INSTANCE(dev_data->dma.reg, dev_data->dma.channel-1); #endif #endif /* CONFIG_DMA_STM32_V1 */ /* Initialize DMA HAL */ __HAL_LINKDMA(&dev_data->hqspi, hdma, hdma); HAL_DMA_Init(&hdma); #endif /* STM32_QSPI_USE_DMA */ /* Clock configuration */ if (clock_control_on(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), (clock_control_subsys_t) &dev_cfg->pclken) != 0) { LOG_DBG("Could not enable QSPI clock"); return -EIO; } if (clock_control_get_rate(DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE), (clock_control_subsys_t) &dev_cfg->pclken, &ahb_clock_freq) < 0) { LOG_DBG("Failed to get AHB clock frequency"); return -EIO; } for (; prescaler <= STM32_QSPI_CLOCK_PRESCALER_MAX; prescaler++) { uint32_t clk = ahb_clock_freq / (prescaler + 1); if (clk <= dev_cfg->max_frequency) { break; } } __ASSERT_NO_MSG(prescaler <= STM32_QSPI_CLOCK_PRESCALER_MAX); /* Initialize QSPI HAL */ dev_data->hqspi.Init.ClockPrescaler = prescaler; dev_data->hqspi.Init.FlashSize = find_lsb_set(dev_cfg->flash_size); HAL_QSPI_Init(&dev_data->hqspi); #if DT_NODE_HAS_PROP(DT_NODELABEL(quadspi), flash_id) uint8_t qspi_flash_id = DT_PROP(DT_NODELABEL(quadspi), flash_id); HAL_QSPI_SetFlashID(&dev_data->hqspi, (qspi_flash_id - 1) << QUADSPI_CR_FSEL_Pos); #endif /* Initialize semaphores */ k_sem_init(&dev_data->sem, 1, 1); k_sem_init(&dev_data->sync, 0, 1); /* Run IRQ init */ dev_cfg->irq_config(dev); /* Run NOR init */ const uint8_t decl_nph = 2; union { /* We only process BFP so use one parameter block */ uint8_t raw[JESD216_SFDP_SIZE(decl_nph)]; struct jesd216_sfdp_header sfdp; } u; const struct jesd216_sfdp_header *hp = &u.sfdp; ret = qspi_read_sfdp(dev, 0, u.raw, sizeof(u.raw)); if (ret != 0) { LOG_ERR("SFDP read failed: %d", ret); return ret; } uint32_t magic = jesd216_sfdp_magic(hp); if (magic != JESD216_SFDP_MAGIC) { LOG_ERR("SFDP magic %08x invalid", magic); return -EINVAL; } LOG_INF("%s: SFDP v %u.%u AP %x with %u PH", dev->name, hp->rev_major, hp->rev_minor, hp->access, 1 + hp->nph); const struct jesd216_param_header *php = hp->phdr; const struct jesd216_param_header *phpe = php + MIN(decl_nph, 1 + hp->nph); while (php != phpe) { uint16_t id = jesd216_param_id(php); LOG_INF("PH%u: %04x rev %u.%u: %u DW @ %x", (php - hp->phdr), id, php->rev_major, php->rev_minor, php->len_dw, jesd216_param_addr(php)); if (id == JESD216_SFDP_PARAM_ID_BFP) { union { uint32_t dw[MIN(php->len_dw, 20)]; struct jesd216_bfp bfp; } u; const struct jesd216_bfp *bfp = &u.bfp; ret = qspi_read_sfdp(dev, jesd216_param_addr(php), (uint8_t *)u.dw, sizeof(u.dw)); if (ret == 0) { ret = spi_nor_process_bfp(dev, php, bfp); } if (ret != 0) { LOG_ERR("SFDP BFP failed: %d", ret); break; } } ++php; } #if defined(CONFIG_FLASH_PAGE_LAYOUT) ret = setup_pages_layout(dev); if (ret != 0) { LOG_ERR("layout setup failed: %d", ret); return -ENODEV; } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ LOG_INF("Device %s initialized", DEV_NAME(dev)); return 0; } #define DMA_CHANNEL_CONFIG(node, dir) \ DT_DMAS_CELL_BY_NAME(node, dir, channel_config) #define QSPI_DMA_CHANNEL_INIT(node, dir) \ .dev = DEVICE_DT_GET(DT_DMAS_CTLR(node)), \ .channel = DT_DMAS_CELL_BY_NAME(node, dir, channel), \ .reg = (DMA_TypeDef *)DT_REG_ADDR( \ DT_PHANDLE_BY_NAME(node, dmas, dir)),\ .cfg = { \ .dma_slot = DT_DMAS_CELL_BY_NAME(node, dir, slot), \ .source_data_size = STM32_DMA_CONFIG_PERIPHERAL_DATA_SIZE( \ DMA_CHANNEL_CONFIG(node, dir)), \ .dest_data_size = STM32_DMA_CONFIG_MEMORY_DATA_SIZE( \ DMA_CHANNEL_CONFIG(node, dir)), \ .channel_priority = STM32_DMA_CONFIG_PRIORITY( \ DMA_CHANNEL_CONFIG(node, dir)), \ .dma_callback = qspi_dma_callback, \ }, \ #define QSPI_DMA_CHANNEL(node, dir) \ .dma = { \ COND_CODE_1(DT_DMAS_HAS_NAME(node, dir), \ (QSPI_DMA_CHANNEL_INIT(node, dir)), \ (NULL)) \ }, #define QSPI_FLASH_MODULE(drv_id, flash_id) \ (DT_DRV_INST(drv_id), qspi_nor_flash_##flash_id) static void flash_stm32_qspi_irq_config_func(const struct device *dev); #define STM32_QSPI_NODE DT_INST_PARENT(0) PINCTRL_DT_DEFINE(STM32_QSPI_NODE); static const struct flash_stm32_qspi_config flash_stm32_qspi_cfg = { .regs = (QUADSPI_TypeDef *)DT_REG_ADDR(STM32_QSPI_NODE), .pclken = { .enr = DT_CLOCKS_CELL(STM32_QSPI_NODE, bits), .bus = DT_CLOCKS_CELL(STM32_QSPI_NODE, bus) }, .irq_config = flash_stm32_qspi_irq_config_func, .flash_size = DT_INST_PROP(0, size) / 8U, .max_frequency = DT_INST_PROP(0, qspi_max_frequency), .pcfg = PINCTRL_DT_DEV_CONFIG_GET(STM32_QSPI_NODE), }; static struct flash_stm32_qspi_data flash_stm32_qspi_dev_data = { .hqspi = { .Instance = (QUADSPI_TypeDef *)DT_REG_ADDR(STM32_QSPI_NODE), .Init = { .FifoThreshold = STM32_QSPI_FIFO_THRESHOLD, .SampleShifting = QSPI_SAMPLE_SHIFTING_NONE, .ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE, .ClockMode = QSPI_CLOCK_MODE_0, }, }, QSPI_DMA_CHANNEL(STM32_QSPI_NODE, tx_rx) }; DEVICE_DT_INST_DEFINE(0, &flash_stm32_qspi_init, NULL, &flash_stm32_qspi_dev_data, &flash_stm32_qspi_cfg, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &flash_stm32_qspi_driver_api); static void flash_stm32_qspi_irq_config_func(const struct device *dev) { IRQ_CONNECT(DT_IRQN(STM32_QSPI_NODE), DT_IRQ(STM32_QSPI_NODE, priority), flash_stm32_qspi_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_IRQN(STM32_QSPI_NODE)); } #endif