/* * Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-06-15 CDT first version * 2023-09-30 CDT Delete dma transmit interrupt */ /******************************************************************************* * Include files ******************************************************************************/ #include #include #if defined(RT_USING_QSPI) #if defined(BSP_USING_QSPI) #include "drv_qspi.h" #include "board_config.h" /******************************************************************************* * Local type definitions ('typedef') ******************************************************************************/ /******************************************************************************* * Local pre-processor symbols/macros ('#define') ******************************************************************************/ // #define DRV_DEBUG #define LOG_TAG "drv.qspi" #include /* QSPI read/write function */ #define QSPI_READ_FUNC (0U) #define QSPI_WRITE_FUNC (1U) #define QSPI_BASE_BLK_SIZE (0x4000000UL) #define QSPI_MAX_FLASH_ADDR (0xFC000000UL) /* QSPI max division */ #define QSPI_MAX_DIV_VAL (0x3FU) /* Div64 */ /* QSPI read instruction */ #define QSPI_3LINE_STD_RD (0x03U) #define QSPI_3LINE_FAST_RD (0x0BU) #define QSPI_3LINE_DUAL_OUTPUT_FAST_RD (0x3BU) #define QSPI_3LINE_DUAL_IO_FAST_RD (0xBBU) #define QSPI_3LINE_QUAD_OUTPUT_FAST_RD (0x6BU) #define QSPI_3LINE_QUAD_IO_FAST_RD (0xEBU) #define QSPI_4LINE_STD_RD (0x13U) #define QSPI_4LINE_FAST_RD (0x0CU) #define QSPI_4LINE_DUAL_OUTPUT_FAST_RD (0x3CU) #define QSPI_4LINE_DUAL_IO_FAST_RD (0xBCU) #define QSPI_4LINE_QUAD_OUTPUT_FAST_RD (0x6CU) #define QSPI_4LINE_QUAD_IO_FAST_RD (0xECU) /******************************************************************************* * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ extern rt_err_t rt_hw_qspi_board_init(void); /******************************************************************************* * Local function prototypes ('static') ******************************************************************************/ static void qspi_err_irq_handler(void); /******************************************************************************* * Local variable definitions ('static') ******************************************************************************/ #ifndef BSP_QSPI_USING_SOFT_CS static const uint8_t qspi_rom_cmd_list[] = { QSPI_3LINE_STD_RD, QSPI_3LINE_FAST_RD, QSPI_3LINE_DUAL_OUTPUT_FAST_RD, QSPI_3LINE_DUAL_IO_FAST_RD, QSPI_3LINE_QUAD_OUTPUT_FAST_RD, QSPI_3LINE_QUAD_IO_FAST_RD, QSPI_4LINE_STD_RD, QSPI_4LINE_FAST_RD, QSPI_4LINE_DUAL_OUTPUT_FAST_RD, QSPI_4LINE_DUAL_IO_FAST_RD, QSPI_4LINE_QUAD_OUTPUT_FAST_RD, QSPI_4LINE_QUAD_IO_FAST_RD, }; #endif struct rt_spi_bus spi_bus_obj; struct hc32_qspi_bus qspi_bus_obj; /******************************************************************************* * Function implementation - global ('extern') and local ('static') ******************************************************************************/ static int hc32_qspi_init(struct rt_qspi_device *device, struct rt_qspi_configuration *qspi_cfg) { int result = RT_EOK; uint32_t u32Cnt = 1; uint32_t u32BusFreq; stc_qspi_init_t stcQspiInit = QSPI_INIT_PARAMS; RT_ASSERT(device != RT_NULL); RT_ASSERT(qspi_cfg != RT_NULL); struct rt_spi_configuration *cfg = &qspi_cfg->parent; struct hc32_qspi_bus *qspi_bus = device->parent.bus->parent.user_data; /* Get BUS clock */ u32BusFreq = CLK_GetBusClockFreq(CLK_BUS_HCLK); while (cfg->max_hz < u32BusFreq / (u32Cnt + 1U)) { if (++u32Cnt == QSPI_MAX_DIV_VAL) /* Div64 */ { break; } } stcQspiInit.u32ClockDiv = (u32Cnt << QSPI_CR_DIV_POS); if (!(cfg->mode & RT_SPI_CPOL)) { stcQspiInit.u32SpiMode = QSPI_SPI_MD0; /* QSPI MODE0 */ } else { stcQspiInit.u32SpiMode = QSPI_SPI_MD3; /* QSPI MODE3 */ } /* Enable qspi clock */ FCG_Fcg1PeriphClockCmd(qspi_bus->config->clock, ENABLE); /* qspi port init */ rt_hw_qspi_board_init(); /* Init QSPI */ stcQspiInit.u32ReadMode = QSPI_RD_MD_STD_RD; stcQspiInit.u32DummyCycle = QSPI_DUMMY_CYCLE4; stcQspiInit.u32AddrWidth = QSPI_ADDR_WIDTH_24BIT; (void)QSPI_Init(&stcQspiInit); QSPI_SetWpPinLevel(QSPI_WP_PIN_LEVEL); /* Enable error interrupt */ NVIC_EnableIRQ(qspi_bus->config->err_irq.irq_config.irq_num); LOG_D("qspi init success!"); #ifdef BSP_QSPI_USING_DMA struct dma_config *qspi_dma; stc_dma_init_t stcDmaInit; /* Get qspi dma */ qspi_dma = qspi_bus->config->dma_qspi; /* Enable Dma clock */ FCG_Fcg0PeriphClockCmd(qspi_dma->clock, ENABLE); /* Config Dma */ DMA_StructInit(&stcDmaInit); stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT; /* Init Dma */ if (LL_OK != DMA_Init(qspi_dma->Instance, qspi_dma->channel, &stcDmaInit)) { return -RT_ERROR; } DMA_Cmd(qspi_dma->Instance, ENABLE); #endif /* BSP_QSPI_USING_DMA */ return result; } #ifndef BSP_QSPI_USING_SOFT_CS static int32_t hc32_qspi_search_rom_cmd(uint8_t u8Cmd) { uint32_t u32Cnt, u32ListLen; u32ListLen = sizeof(qspi_rom_cmd_list); for (u32Cnt = 0; u32Cnt < u32ListLen; u32Cnt++) { if (u8Cmd == qspi_rom_cmd_list[u32Cnt]) { return LL_OK; } } return LL_ERR; } #endif static int32_t hc32_qspi_send_cmd(struct hc32_qspi_bus *qspi_bus, struct rt_qspi_message *message, uint8_t u8Func) { #ifndef BSP_QSPI_USING_SOFT_CS uint32_t u32ReadMode = QSPI_RD_MD_STD_RD; uint32_t u32DummyCycle = 0U; uint32_t u32AddrWidth; uint8_t u8Instr, u8CalcDummy = 0U; #endif RT_ASSERT(qspi_bus != RT_NULL); RT_ASSERT(message != RT_NULL); #ifndef BSP_QSPI_USING_SOFT_CS CM_QSPI_TypeDef *qspi_instance = qspi_bus->config->Instance; u8Instr = message->instruction.content; if ((QSPI_READ_FUNC == u8Func) && (LL_OK == hc32_qspi_search_rom_cmd(u8Instr))) { if ((message->instruction.qspi_lines != 1) || ((message->address.qspi_lines != 1) && (message->address.qspi_lines != 2) && (message->address.qspi_lines != 4)) || ((message->qspi_data_lines != 1) && (message->qspi_data_lines != 2) && (message->qspi_data_lines != 4))) { return LL_ERR_INVD_PARAM; } if (((message->address.qspi_lines == 2) && ((message->qspi_data_lines == 1) || (message->qspi_data_lines == 4))) || ((message->address.qspi_lines == 4) && ((message->qspi_data_lines == 1) || (message->qspi_data_lines == 2)))) { return LL_ERR_INVD_PARAM; } if ((message->instruction.qspi_lines == 1) && (message->address.qspi_lines == 1) && (message->qspi_data_lines == 1)) { if (message->dummy_cycles != 0) { u32ReadMode += 1U; u8CalcDummy = 1U; } } else { u8CalcDummy = 1U; u32ReadMode = message->qspi_data_lines; if (message->address.qspi_lines == message->qspi_data_lines) { u32ReadMode += 1U; } } if (0U != u8CalcDummy) { if ((message->dummy_cycles < 3) || (message->dummy_cycles > 18)) { return LL_ERR_INVD_PARAM; } u32DummyCycle = (message->dummy_cycles - 3) << QSPI_FCR_DMCYCN_POS; } if (message->address.size == 24) { u32AddrWidth = QSPI_ADDR_WIDTH_24BIT; } else { u32AddrWidth = QSPI_ADDR_WIDTH_32BIT_INSTR_32BIT; } /* configure register */ MODIFY_REG32(qspi_instance->FCR, (QSPI_FCR_DMCYCN | QSPI_FCR_FOUR_BIC | QSPI_FCR_AWSL), (u32DummyCycle | u32AddrWidth)); QSPI_SetReadMode(u32ReadMode); } else #endif { if ((message->instruction.qspi_lines != 0) && (message->instruction.qspi_lines != 1)) { return LL_ERR_INVD_PARAM; } if ((message->address.qspi_lines != 0) && ((message->address.qspi_lines != 1) && (message->address.size != 0))) { return LL_ERR_INVD_PARAM; } if ((message->address.size % 8) != 0) { return LL_ERR_INVD_PARAM; } if ((message->qspi_data_lines != 0) && (message->qspi_data_lines != 1)) { return LL_ERR_INVD_PARAM; } if ((message->dummy_cycles % 8) != 0) { return LL_ERR_INVD_PARAM; } } return LL_OK; } static void hc32_qspi_word_to_byte(uint32_t u32Word, uint8_t *pu8Byte, uint8_t u8Len) { uint8_t u8Count = 0U; uint32_t u32ByteNum = u8Len - 1U; do { pu8Byte[u8Count++] = (uint8_t)(u32Word >> (u32ByteNum * 8U)) & 0xFFU; } while ((u32ByteNum--) != 0UL); } static int32_t hc32_qspi_write_instr(struct hc32_qspi_bus *qspi_bus, uint8_t u8Instr, uint32_t u32InstrLen, uint8_t *pu8Addr, uint32_t u32AddrLen, const uint8_t *pu8WriteBuf, uint32_t u32BufLen) { uint32_t u32Count; int32_t i32Ret = LL_OK; #ifdef BSP_QSPI_USING_DMA struct dma_config *qspi_dma; stc_dma_init_t stcDmaInit; uint32_t u32DmaTransSize; uint32_t u32TxIndex = 0U; rt_uint32_t u32TimeoutCnt; #endif QSPI_EnterDirectCommMode(); if (0UL != u32InstrLen) { QSPI_WriteDirectCommValue(u8Instr); } if ((NULL != pu8Addr) && (0UL != u32AddrLen)) { for (u32Count = 0UL; u32Count < u32AddrLen; u32Count++) { QSPI_WriteDirectCommValue(pu8Addr[u32Count]); } } if ((NULL != pu8WriteBuf) && (0UL != u32BufLen)) { #ifdef BSP_QSPI_USING_DMA qspi_dma = qspi_bus->config->dma_qspi; AOS_SetTriggerEventSrc(qspi_dma->trigger_select, qspi_dma->trigger_event); /* Config Dma */ DMA_StructInit(&stcDmaInit); stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT; stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC; stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_FIX; DMA_Init(qspi_dma->Instance, qspi_dma->channel, &stcDmaInit); while (u32BufLen != 0U) { if (u32BufLen > DMA_DTCTL_BLKSIZE) { u32DmaTransSize = DMA_DTCTL_BLKSIZE; u32BufLen -= DMA_DTCTL_BLKSIZE; } else { u32DmaTransSize = u32BufLen; u32BufLen = 0U; } DMA_ClearTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag); DMA_SetSrcAddr(qspi_dma->Instance, qspi_dma->channel, (uint32_t)&pu8WriteBuf[u32TxIndex]); DMA_SetDestAddr(qspi_dma->Instance, qspi_dma->channel, (uint32_t)&qspi_bus->config->Instance->DCOM); DMA_SetTransCount(qspi_dma->Instance, qspi_dma->channel, 1UL); DMA_SetBlockSize(qspi_dma->Instance, qspi_dma->channel, (uint16_t)u32DmaTransSize); (void)DMA_ChCmd(qspi_dma->Instance, qspi_dma->channel, ENABLE); AOS_SW_Trigger(); /* 1st trigger for DMA */ u32TimeoutCnt = 0U; /* Wait DMA transfer completed */ while ((RESET == DMA_GetTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag)) && (u32TimeoutCnt < qspi_bus->config->timeout)) { rt_thread_mdelay(1); u32TimeoutCnt++; } if (u32TimeoutCnt >= qspi_bus->config->timeout) { i32Ret = LL_ERR_TIMEOUT; break; } u32TxIndex += u32DmaTransSize; } #else for (u32Count = 0UL; u32Count < u32BufLen; u32Count++) { QSPI_WriteDirectCommValue(pu8WriteBuf[u32Count]); } #endif } QSPI_ExitDirectCommMode(); return i32Ret; } static int32_t hc32_qspi_read_instr(struct hc32_qspi_bus *qspi_bus, uint8_t u8Instr, uint32_t u32InstrLen, uint8_t *pu8Addr, uint32_t u32AddrLen, uint8_t *pu8ReadBuf, uint32_t u32BufLen) { uint32_t u32Count; int32_t i32Ret = LL_OK; #ifdef BSP_QSPI_USING_DMA struct dma_config *qspi_dma; stc_dma_init_t stcDmaInit; uint32_t u32DmaTransSize; uint32_t u32RxIndex = 0U; rt_uint32_t u32TimeoutCnt; #endif QSPI_EnterDirectCommMode(); if (0UL != u32InstrLen) { QSPI_WriteDirectCommValue(u8Instr); } if ((NULL != pu8Addr) && (0UL != u32AddrLen)) { for (u32Count = 0UL; u32Count < u32AddrLen; u32Count++) { QSPI_WriteDirectCommValue(pu8Addr[u32Count]); } } if ((NULL != pu8ReadBuf) && (0UL != u32BufLen)) { #ifdef BSP_QSPI_USING_DMA qspi_dma = qspi_bus->config->dma_qspi; AOS_SetTriggerEventSrc(qspi_dma->trigger_select, qspi_dma->trigger_event); /* Config Dma */ DMA_StructInit(&stcDmaInit); stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT; stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_FIX; stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC; DMA_Init(qspi_dma->Instance, qspi_dma->channel, &stcDmaInit); while (u32BufLen != 0U) { if (u32BufLen > DMA_DTCTL_BLKSIZE) { u32DmaTransSize = DMA_DTCTL_BLKSIZE; u32BufLen -= DMA_DTCTL_BLKSIZE; } else { u32DmaTransSize = u32BufLen; u32BufLen = 0U; } DMA_ClearTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag); DMA_SetSrcAddr(qspi_dma->Instance, qspi_dma->channel, (uint32_t)&qspi_bus->config->Instance->DCOM); DMA_SetDestAddr(qspi_dma->Instance, qspi_dma->channel, (uint32_t)&pu8ReadBuf[u32RxIndex]); DMA_SetTransCount(qspi_dma->Instance, qspi_dma->channel, 1UL); DMA_SetBlockSize(qspi_dma->Instance, qspi_dma->channel, (uint16_t)u32DmaTransSize); (void)DMA_ChCmd(qspi_dma->Instance, qspi_dma->channel, ENABLE); AOS_SW_Trigger(); /* 1st trigger for DMA */ u32TimeoutCnt = 0U; /* Wait DMA transfer completed */ while ((RESET == DMA_GetTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag)) && (u32TimeoutCnt < qspi_bus->config->timeout)) { rt_thread_mdelay(1); u32TimeoutCnt++; } if (u32TimeoutCnt >= qspi_bus->config->timeout) { i32Ret = LL_ERR_TIMEOUT; break; } u32RxIndex += u32DmaTransSize; } #else for (u32Count = 0UL; u32Count < u32BufLen; u32Count++) { pu8ReadBuf[u32Count] = QSPI_ReadDirectCommValue(); } #endif } QSPI_ExitDirectCommMode(); return i32Ret; } static int32_t hc32_qspi_write(struct hc32_qspi_bus *qspi_bus, struct rt_qspi_message *message) { uint32_t u32Count = 0U; const rt_uint8_t *tx_buf = message->parent.send_buf; rt_int32_t length = message->parent.length; uint32_t u32Addr = message->address.content; uint8_t u8Instr = message->instruction.content; uint8_t u8AddrBuf[10]; uint32_t u32AddrLen = 0U; uint32_t u32InstrLen = 0U; int32_t i32Ret; RT_ASSERT(qspi_bus != RT_NULL); RT_ASSERT(message != RT_NULL); if (message->instruction.qspi_lines != 0) { u32InstrLen = 1U; } if (message->address.size != 0) { u32AddrLen = message->address.size / 8; hc32_qspi_word_to_byte(u32Addr, u8AddrBuf, u32AddrLen); } for (u32Count = 0; u32Count < (message->dummy_cycles / 8); u32Count++) { u8AddrBuf[u32AddrLen] = 0xFF; u32AddrLen += 1; } i32Ret = hc32_qspi_write_instr(qspi_bus, u8Instr, u32InstrLen, u8AddrBuf, u32AddrLen, tx_buf, length); return i32Ret; } static int32_t hc32_qspi_read(struct hc32_qspi_bus *qspi_bus, struct rt_qspi_message *message) { uint32_t u32Count = 0U; rt_uint8_t *rx_buf = message->parent.recv_buf; rt_int32_t length = message->parent.length; uint32_t u32Addr = message->address.content; uint8_t u8Instr = message->instruction.content; uint8_t u8AddrBuf[10]; uint32_t u32AddrLen = 0U; uint32_t u32InstrLen = 0U; int32_t i32Ret = LL_OK; #ifndef BSP_QSPI_USING_SOFT_CS uint32_t u32ExtBlkStartNum = 0U; uint32_t u32GetSize, u32RxIndex = 0U; #ifdef BSP_QSPI_USING_DMA struct dma_config *qspi_dma; stc_dma_init_t stcDmaInit; uint32_t u32DmaTransSize; rt_uint32_t u32TimeoutCnt; #else __IO uint8_t *pu8Read; #endif #endif RT_ASSERT(qspi_bus != RT_NULL); RT_ASSERT(message != RT_NULL); #ifndef BSP_QSPI_USING_SOFT_CS if (LL_OK == hc32_qspi_search_rom_cmd(u8Instr)) { u32ExtBlkStartNum = u32Addr / QSPI_BASE_BLK_SIZE; if ((u32Addr + length) > QSPI_MAX_FLASH_ADDR) { return LL_ERR_INVD_PARAM; } u32Addr = (u32Addr % QSPI_BASE_BLK_SIZE) + QSPI_ROM_BASE; #ifdef BSP_QSPI_USING_DMA qspi_dma = qspi_bus->config->dma_qspi; AOS_SetTriggerEventSrc(qspi_dma->trigger_select, qspi_dma->trigger_event); /* Config Dma */ DMA_StructInit(&stcDmaInit); stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT; stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC; stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC; DMA_Init(qspi_dma->Instance, qspi_dma->channel, &stcDmaInit); while (length != 0) { QSPI_SelectMemoryBlock((uint8_t)u32ExtBlkStartNum); if ((u32Addr + length) > (QSPI_ROM_END + 1U)) { u32GetSize = (QSPI_ROM_END + 1U) - u32Addr; length -= u32GetSize; } else { u32GetSize = length; length = 0U; } while (u32GetSize != 0U) { if (u32GetSize > DMA_DTCTL_BLKSIZE) { u32DmaTransSize = DMA_DTCTL_BLKSIZE; u32GetSize -= DMA_DTCTL_BLKSIZE; } else { u32DmaTransSize = u32GetSize; u32GetSize = 0U; } DMA_ClearTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag); DMA_SetSrcAddr(qspi_dma->Instance, qspi_dma->channel, u32Addr); DMA_SetDestAddr(qspi_dma->Instance, qspi_dma->channel, (uint32_t)&rx_buf[u32RxIndex]); DMA_SetTransCount(qspi_dma->Instance, qspi_dma->channel, 1UL); DMA_SetBlockSize(qspi_dma->Instance, qspi_dma->channel, (uint16_t)u32DmaTransSize); (void)DMA_ChCmd(qspi_dma->Instance, qspi_dma->channel, ENABLE); AOS_SW_Trigger(); /* 1st trigger for DMA */ u32TimeoutCnt = 0U; /* Wait DMA transfer completed */ while ((RESET == DMA_GetTransCompleteStatus(qspi_dma->Instance, qspi_dma->flag)) && (u32TimeoutCnt < qspi_bus->config->timeout)) { rt_thread_mdelay(1); u32TimeoutCnt++; } if (u32TimeoutCnt >= qspi_bus->config->timeout) { return LL_ERR_TIMEOUT; } u32Addr += u32DmaTransSize; u32RxIndex += u32DmaTransSize; } if (length != 0U) { u32ExtBlkStartNum += 1U; u32Addr = QSPI_ROM_BASE; } } #else while (length != 0) { QSPI_SelectMemoryBlock((uint8_t)u32ExtBlkStartNum); pu8Read = (__IO uint8_t *)u32Addr; if ((u32Addr + length) > (QSPI_ROM_END + 1U)) { u32GetSize = (QSPI_ROM_END + 1U) - u32Addr; length -= u32GetSize; } else { u32GetSize = length; length = 0U; } u32Count = 0U; while (u32Count < u32GetSize) { rx_buf[u32RxIndex++] = *pu8Read++; u32Count++; } if (length != 0U) { u32ExtBlkStartNum += 1U; u32Addr = QSPI_ROM_BASE; } } #endif } else #endif { if (message->instruction.qspi_lines != 0) { u32InstrLen = 1U; } if (message->address.size != 0) { u32AddrLen = message->address.size / 8; hc32_qspi_word_to_byte(u32Addr, u8AddrBuf, u32AddrLen); } for (u32Count = 0; u32Count < (message->dummy_cycles / 8); u32Count++) { u8AddrBuf[u32AddrLen] = 0xFF; u32AddrLen += 1; } i32Ret = hc32_qspi_read_instr(qspi_bus, u8Instr, u32InstrLen, u8AddrBuf, u32AddrLen, rx_buf, length); } return i32Ret; } static rt_ssize_t qspixfer(struct rt_spi_device *device, struct rt_spi_message *message) { RT_ASSERT(device != RT_NULL); RT_ASSERT(device->bus != RT_NULL); rt_size_t len = 0; struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message; struct hc32_qspi_bus *qspi_bus = device->bus->parent.user_data; #ifdef BSP_QSPI_USING_SOFT_CS struct hc32_hw_qspi_cs *cs = device->parent.user_data; #endif rt_int32_t length = message->length; #ifdef BSP_QSPI_USING_SOFT_CS if (message->cs_take) { rt_pin_write(cs->pin, 0); } #endif if (message->send_buf != NULL) /* send data */ { if (LL_OK != hc32_qspi_send_cmd(qspi_bus, qspi_message, QSPI_WRITE_FUNC)) { LOG_E("QSPI rt_qspi_message format error!"); goto __exit; } if (LL_OK == hc32_qspi_write(qspi_bus, qspi_message)) { if (qspi_message->parent.length != 0) { len = length; } else { len = 1; } } else { LOG_E("QSPI send data failed!"); goto __exit; } } else if (message->recv_buf != NULL) /* recv data */ { if (LL_OK != hc32_qspi_send_cmd(qspi_bus, qspi_message, QSPI_READ_FUNC)) { LOG_E("QSPI rt_qspi_message format error!"); goto __exit; } if (LL_OK == hc32_qspi_read(qspi_bus, qspi_message)) { len = length; } else { LOG_E("QSPI recv data failed!"); goto __exit; } } __exit: #ifdef BSP_QSPI_USING_SOFT_CS if (message->cs_release) { rt_pin_write(cs->pin, 1); } #endif return len; } static rt_err_t qspi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) { RT_ASSERT(device != RT_NULL); RT_ASSERT(configuration != RT_NULL); struct rt_qspi_device *qspi_device = (struct rt_qspi_device *)device; return hc32_qspi_init(qspi_device, &qspi_device->config); } static const struct rt_spi_ops hc32_qspi_ops = { .configure = qspi_configure, .xfer = qspixfer, }; static int hc32_qspi_register_bus(struct hc32_qspi_bus *qspi_bus, const char *name) { RT_ASSERT(qspi_bus != RT_NULL); RT_ASSERT(name != RT_NULL); spi_bus_obj.parent.user_data = qspi_bus; return rt_qspi_bus_register(&spi_bus_obj, name, &hc32_qspi_ops); } static void qspi_err_irq_handler(void) { /* enter interrupt */ rt_interrupt_enter(); QSPI_ClearStatus(QSPI_FLAG_ROM_ACCESS_ERR); /* leave interrupt */ rt_interrupt_leave(); } /** * @brief This function attach device to QSPI bus. * @param bus_name QSPI bus name * @param device_name QSPI device name * @param pin QSPI cs pin number * @param data_line_width QSPI data lines width, such as 1, 2, 4 * @param enter_qspi_mode Callback function that lets FLASH enter QSPI mode * @param exit_qspi_mode Callback function that lets FLASH exit QSPI mode * @retval 0 : success * -1 : failed */ rt_err_t rt_hw_qspi_bus_attach_device(const char *bus_name, const char *device_name, rt_uint32_t pin, rt_uint8_t data_line_width, void (*enter_qspi_mode)(), void (*exit_qspi_mode)()) { struct rt_qspi_device *qspi_device = RT_NULL; struct hc32_hw_qspi_cs *cs_pin = RT_NULL; rt_err_t result = RT_EOK; RT_ASSERT(bus_name != RT_NULL); RT_ASSERT(device_name != RT_NULL); RT_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4); qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device)); if (qspi_device == RT_NULL) { LOG_E("no memory, qspi bus attach device failed!"); result = RT_ENOMEM; goto __exit; } cs_pin = (struct hc32_hw_qspi_cs *)rt_malloc(sizeof(struct hc32_hw_qspi_cs)); if (qspi_device == RT_NULL) { LOG_E("no memory, qspi bus attach device failed!"); result = RT_ENOMEM; goto __exit; } qspi_device->enter_qspi_mode = enter_qspi_mode; qspi_device->exit_qspi_mode = exit_qspi_mode; qspi_device->config.qspi_dl_width = data_line_width; cs_pin->pin = pin; #ifdef BSP_QSPI_USING_SOFT_CS rt_pin_mode(pin, PIN_MODE_OUTPUT); rt_pin_write(pin, 1); #endif result = rt_spi_bus_attach_device(&qspi_device->parent, device_name, bus_name, (void *)cs_pin); __exit: if (result != RT_EOK) { if (qspi_device) { rt_free(qspi_device); } if (cs_pin) { rt_free(cs_pin); } } return result; } static void hc32_get_qspi_info(void) { static struct hc32_qspi_config qspi_config = QSPI_BUS_CONFIG; qspi_config.err_irq.irq_callback = qspi_err_irq_handler; #ifdef BSP_QSPI_USING_DMA static struct dma_config qspi_dma = QSPI_DMA_CONFIG; qspi_config.dma_qspi = &qspi_dma; #endif qspi_bus_obj.config = &qspi_config; } static int rt_hw_qspi_bus_init(void) { hc32_get_qspi_info(); /* register the handle */ hc32_install_irq_handler(&qspi_bus_obj.config->err_irq.irq_config, qspi_bus_obj.config->err_irq.irq_callback, RT_FALSE); return hc32_qspi_register_bus(&qspi_bus_obj, "qspi1"); } INIT_BOARD_EXPORT(rt_hw_qspi_bus_init); #endif /* BSP_USING_QSPI */ #endif /* RT_USING_QSPI */