/* * Copyright (c) 2006-2024, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2024-03-28 qiujingbao first version * 2024/06/08 flyingcys fix transmission failure */ #include #include #include #include "board.h" #include "drv_spi.h" #include "drv_pinmux.h" #define DBG_LEVEL DBG_LOG #include #define LOG_TAG "drv.spi" struct _device_spi { struct rt_spi_bus spi_bus; struct dw_spi dws; char *device_name; }; static struct _device_spi _spi_obj[] = { #ifdef BSP_USING_SPI0 { .dws.regs = (void *)DW_SPI0_BASE, .dws.irq = DW_SPI0_IRQn, .dws.index = 0, .device_name = "spi0", }, #endif /* BSP_USING_SPI0 */ #ifdef BSP_USING_SPI1 { .dws.regs = (void *)DW_SPI1_BASE, .dws.irq = DW_SPI1_IRQn, .dws.index = 0, .device_name = "spi1", }, #endif /* BSP_USING_SPI1 */ #ifdef BSP_USING_SPI2 { .dws.regs = (void *)DW_SPI2_BASE, .dws.irq = DW_SPI2_IRQn, .dws.index = 0, .device_name = "spi2", }, #endif /* BSP_USING_SPI2 */ #ifdef BSP_USING_SPI3 { .dws.regs = (void *)DW_SPI3_BASE, .dws.irq = DW_SPI3_IRQn, .dws.index = 0, .device_name = "spi3", }, #endif /* BSP_USING_SPI3 */ }; static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg) { RT_ASSERT(device != RT_NULL); RT_ASSERT(device->bus != RT_NULL); RT_ASSERT(device->bus->parent.user_data != RT_NULL); RT_ASSERT(cfg != RT_NULL); rt_err_t ret = RT_EOK; struct _device_spi *spi = (struct _device_spi *)device->bus->parent.user_data; struct dw_spi *dws = &spi->dws; rt_uint8_t mode; LOG_D("spi_configure input"); /* set cs low when spi idle */ writel(0, (void *)0x030001d0); if (cfg->mode & RT_SPI_SLAVE) { LOG_E("invalid mode: %d", cfg->mode); return -RT_EINVAL; } spi_reset_chip(dws); spi_hw_init(dws); spi_enable_chip(dws, 0); LOG_D("cfg->max_hz: %d", cfg->max_hz); dw_spi_set_clock(dws, SPI_REF_CLK, cfg->max_hz); LOG_D("cfg->data_width: %d", cfg->data_width); if (dw_spi_set_data_frame_len(dws, (uint32_t)cfg->data_width) < 0) { LOG_E("dw_spi_set_data_frame_len failed...\n"); return -RT_ERROR; } LOG_D("cfg->mode: %08x", cfg->mode); switch (cfg->mode & RT_SPI_MODE_3) { case RT_SPI_MODE_0: mode = SPI_FORMAT_CPOL0_CPHA0; break; case RT_SPI_MODE_1: mode = SPI_FORMAT_CPOL0_CPHA1; break; case RT_SPI_MODE_2: mode = SPI_FORMAT_CPOL1_CPHA0; break; case RT_SPI_MODE_3: mode = SPI_FORMAT_CPOL1_CPHA1; break; default: LOG_E("spi configure mode error %x\n", cfg->mode); break; } dw_spi_set_polarity_and_phase(dws, mode); dw_spi_set_cs(dws, 1, 0); spi_enable_chip(dws, 1); return RT_EOK; } static rt_err_t dw_spi_transfer_one(struct dw_spi *dws, const void *tx_buf, void *rx_buf, uint32_t len, enum transfer_type tran_type) { uint8_t imask = 0; uint16_t txlevel = 0; dws->tx = NULL; dws->tx_end = NULL; dws->rx = NULL; dws->rx_end = NULL; if (tx_buf != NULL) { dws->tx = tx_buf; dws->tx_end = dws->tx + len; } if (rx_buf != NULL) { dws->rx = rx_buf; dws->rx_end = dws->rx + len; } dws->rx_len = len / dws->n_bytes; dws->tx_len = len / dws->n_bytes; spi_enable_chip(dws, 0); /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); /* set tran mode */ set_tran_mode(dws); /* cs0 */ dw_spi_set_cs(dws, true, 0); /* enable spi */ spi_enable_chip(dws, 1); rt_hw_us_delay(10); if (tran_type == POLL_TRAN) { if (poll_transfer(dws) < 0) return -RT_ERROR; } else { return -RT_ENOSYS; } return RT_EOK; } static rt_ssize_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message) { RT_ASSERT(device != RT_NULL); RT_ASSERT(device->bus != RT_NULL); RT_ASSERT(device->bus->parent.user_data != RT_NULL); RT_ASSERT(message != RT_NULL); struct _device_spi *spi = (struct _device_spi *)device->bus->parent.user_data; struct dw_spi *dws = &spi->dws; int32_t ret = 0; if (message->send_buf && message->recv_buf) { ret = dw_spi_transfer_one(dws, message->send_buf, message->recv_buf, message->length, POLL_TRAN); } else if (message->send_buf) { ret = dw_spi_transfer_one(dws, message->send_buf, RT_NULL, message->length, POLL_TRAN); } else if (message->recv_buf) { ret = dw_spi_transfer_one(dws, RT_NULL, message->recv_buf, message->length, POLL_TRAN); } return message->length; } static const struct rt_spi_ops _spi_ops = { .configure = spi_configure, .xfer = spi_xfer, }; #if defined(BOARD_TYPE_MILKV_DUO) || defined(BOARD_TYPE_MILKV_DUO_SPINOR) || defined(BOARD_TYPE_MILKV_DUO256M) || defined(BOARD_TYPE_MILKV_DUO256M_SPINOR) // For Duo / Duo 256m, only SPI2 are exported on board. #ifdef BSP_USING_SPI0 static const char *pinname_whitelist_spi0_sck[] = { NULL, }; static const char *pinname_whitelist_spi0_sdo[] = { NULL, }; static const char *pinname_whitelist_spi0_sdi[] = { NULL, }; static const char *pinname_whitelist_spi0_cs[] = { NULL, }; #endif #ifdef BSP_USING_SPI1 static const char *pinname_whitelist_spi1_sck[] = { NULL, }; static const char *pinname_whitelist_spi1_sdo[] = { NULL, }; static const char *pinname_whitelist_spi1_sdi[] = { NULL, }; static const char *pinname_whitelist_spi1_cs[] = { NULL, }; #endif #ifdef BSP_USING_SPI2 static const char *pinname_whitelist_spi2_sck[] = { "SD1_CLK", NULL, }; static const char *pinname_whitelist_spi2_sdo[] = { "SD1_CMD", NULL, }; static const char *pinname_whitelist_spi2_sdi[] = { "SD1_D0", NULL, }; static const char *pinname_whitelist_spi2_cs[] = { "SD1_D3", NULL, }; #endif #ifdef BSP_USING_SPI3 static const char *pinname_whitelist_spi3_sck[] = { NULL, }; static const char *pinname_whitelist_spi3_sdo[] = { NULL, }; static const char *pinname_whitelist_spi3_sdi[] = { NULL, }; static const char *pinname_whitelist_spi3_cs[] = { NULL, }; #endif #else #error "Unsupported board type!" #endif static void rt_hw_spi_pinmux_config() { #ifdef BSP_USING_SPI0 pinmux_config(BSP_SPI0_SCK_PINNAME, SPI0_SCK, pinname_whitelist_spi0_sck); pinmux_config(BSP_SPI0_SDO_PINNAME, SPI0_SDO, pinname_whitelist_spi0_sdo); pinmux_config(BSP_SPI0_SDI_PINNAME, SPI0_SDI, pinname_whitelist_spi0_sdi); pinmux_config(BSP_SPI0_CS_PINNAME, SPI0_CS_X, pinname_whitelist_spi0_cs); #endif /* BSP_USING_SPI0 */ #ifdef BSP_USING_SPI1 pinmux_config(BSP_SPI1_SCK_PINNAME, SPI1_SCK, pinname_whitelist_spi1_sck); pinmux_config(BSP_SPI1_SDO_PINNAME, SPI1_SDO, pinname_whitelist_spi1_sdo); pinmux_config(BSP_SPI1_SDI_PINNAME, SPI1_SDI, pinname_whitelist_spi1_sdi); pinmux_config(BSP_SPI1_CS_PINNAME, SPI1_CS_X, pinname_whitelist_spi1_cs); #endif /* BSP_USING_SPI1 */ #ifdef BSP_USING_SPI2 pinmux_config(BSP_SPI2_SCK_PINNAME, SPI2_SCK, pinname_whitelist_spi2_sck); pinmux_config(BSP_SPI2_SDO_PINNAME, SPI2_SDO, pinname_whitelist_spi2_sdo); pinmux_config(BSP_SPI2_SDI_PINNAME, SPI2_SDI, pinname_whitelist_spi2_sdi); pinmux_config(BSP_SPI2_CS_PINNAME, SPI2_CS_X, pinname_whitelist_spi2_cs); #endif /* BSP_USING_SPI2 */ #ifdef BSP_USING_SPI3 pinmux_config(BSP_SPI3_SCK_PINNAME, SPI3_SCK, pinname_whitelist_spi3_sck); pinmux_config(BSP_SPI3_SDO_PINNAME, SPI3_SDO, pinname_whitelist_spi3_sdo); pinmux_config(BSP_SPI3_SDI_PINNAME, SPI3_SDI, pinname_whitelist_spi3_sdi); pinmux_config(BSP_SPI3_CS_PINNAME, SPI3_CS_X, pinname_whitelist_spi3_cs); #endif /* BSP_USING_SPI3 */ } int rt_hw_spi_init(void) { rt_err_t ret = RT_EOK; struct dw_spi *dws; rt_hw_spi_pinmux_config(); for (rt_size_t i = 0; i < sizeof(_spi_obj) / sizeof(struct _device_spi); i++) { _spi_obj[i].spi_bus.parent.user_data = (void *)&_spi_obj[i]; ret = rt_spi_bus_register(&_spi_obj[i].spi_bus, _spi_obj[i].device_name, &_spi_ops); } RT_ASSERT(ret == RT_EOK); return ret; } INIT_DEVICE_EXPORT(rt_hw_spi_init);