diff --git a/bsp/cvitek/README.md b/bsp/cvitek/README.md index a343b557ce7..6bf2821c965 100755 --- a/bsp/cvitek/README.md +++ b/bsp/cvitek/README.md @@ -56,6 +56,7 @@ $ scons | gpio | 支持 | | | i2c | 支持 | | | adc | 支持 | | +| spi | 支持 | 默认CS引脚,每个数据之间CS会拉高,请根据时序选择GPIO作为CS。若读取数据,tx需持续dummy数据。| ## 支持开发板 - milk-v duo: [https://milkv.io/duo](https://milkv.io/duo) diff --git a/bsp/cvitek/c906_little/board/Kconfig b/bsp/cvitek/c906_little/board/Kconfig index 3780246c591..6a0d10c0f30 100755 --- a/bsp/cvitek/c906_little/board/Kconfig +++ b/bsp/cvitek/c906_little/board/Kconfig @@ -45,6 +45,11 @@ menu "General Drivers Configuration" select RT_USING_ADC default n + config BSP_USING_SPI + bool "Using SPI" + select RT_USING_SPI + default n + menuconfig BSP_USING_WDT bool "Enable Watchdog Timer" select RT_USING_WDT diff --git a/bsp/cvitek/cv180x/board/Kconfig b/bsp/cvitek/cv180x/board/Kconfig index 82f7886f098..a743c804dea 100755 --- a/bsp/cvitek/cv180x/board/Kconfig +++ b/bsp/cvitek/cv180x/board/Kconfig @@ -37,6 +37,11 @@ menu "General Drivers Configuration" select RT_USING_ADC default n + config BSP_USING_SPI + bool "Using SPI" + select RT_USING_SPI + default n + menuconfig BSP_USING_PWM bool "Using PWM" select RT_USING_PWM diff --git a/bsp/cvitek/drivers/SConscript b/bsp/cvitek/drivers/SConscript index f5cde6b1e83..90aa68f80ee 100755 --- a/bsp/cvitek/drivers/SConscript +++ b/bsp/cvitek/drivers/SConscript @@ -30,6 +30,9 @@ if GetDepend('BSP_USING_ADC'): if GetDepend('BSP_USING_WDT'): src += ['drv_wdt.c'] +if GetDepend(['BSP_USING_SPI']): + src += ['drv_spi.c'] + if GetDepend('BSP_USING_PWM'): src += ['drv_pwm.c'] CPPPATH += [cwd + r'/libraries/cv180x/pwm'] diff --git a/bsp/cvitek/drivers/drv_spi.c b/bsp/cvitek/drivers/drv_spi.c new file mode 100644 index 00000000000..aa6b3dd0b9f --- /dev/null +++ b/bsp/cvitek/drivers/drv_spi.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2024-03-28 qiujingbao first version + */ + +#include "drv_spi.h" +#ifdef RT_USING_SPI + +#define DBG_TAG "drv.spi" +#define DBG_LVL DBG_INFO +#include + +static struct cv1800_spi cv1800_spi_obj[] = +{ +#ifdef BSP_USING_SPI + { + .spi_id = SPI2, + .device_name = "spi2", + .fifo_len = SPI_TXFTLR, + }, +#endif +}; + +static struct spi_regs *get_spi_base(uint8_t spi_id) +{ + struct spi_regs *spi_base = NULL; + + switch (spi_id) + { + case SPI0: + spi_base = (struct spi_regs *)SPI0_BASE; + break; + case SPI1: + spi_base = (struct spi_regs *)SPI1_BASE; + break; + case SPI2: + spi_base = (struct spi_regs *)SPI2_BASE; + break; + case SPI3: + spi_base = (struct spi_regs *)SPI3_BASE; + break; + } + + return spi_base; +} + +static rt_err_t drv_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration) +{ + rt_err_t ret = RT_EOK; + struct cv1800_spi *spi_dev = RT_NULL; + uint32_t mode; + + spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data); + + spi_dev->data_width = configuration->data_width; + + /* disable spi */ + spi_enable(spi_dev->reg, 0); + + /* clear irq */ + spi_clear_irq(spi_dev->reg, SPI_IRQ_MSAK); + + /* set clk */ + ret = spi_set_frequency(spi_dev->reg, configuration->max_hz); + if (ret) + return ret; + + /* set mode */ + ret = gen_spi_mode(configuration, &mode); + if (ret) + return ret; + + spi_set_mode(spi_dev->reg, mode); + + /* set cs */ + spi_enable_cs(spi_dev->reg, 0x1); + + spi_enable(spi_dev->reg, 0x1); + + mode = mmio_read_32((uintptr_t)&spi_dev->reg->spi_ctrl0); + LOG_D("mode: %x", mode); + mode = mmio_read_32((uintptr_t)&spi_dev->reg->spi_baudr); + LOG_D("spi_baudr: %x", mode); + + return ret; +} + +int hw_spi_recv(struct cv1800_spi *dev) { + uint32_t rever; + uint32_t tem; + int ret; + + rever = mmio_read_32((uintptr_t)&dev->reg->spi_rxflr); + ret = (int)rever; + + while (rever) + { + tem = mmio_read_32((uintptr_t)&dev->reg->spi_dr); + + if (dev->recv_buf < dev->recv_end) + { + if (dev->data_width == 8) + *(uint8_t *)(dev->recv_buf) = tem; + else + *(uint16_t *)(dev->recv_buf) = tem; + } + else + { + return 0; + } + + rever--; + dev->recv_buf += dev->data_width >> 3; + } + return ret; +} + +int hw_spi_send(struct cv1800_spi *dev) { + uint32_t txflr; + uint32_t max; + uint16_t value; + + txflr = mmio_read_32((uintptr_t)&dev->reg->spi_txflr); + max = dev->fifo_len - txflr; + + while (max) + { + if (dev->send_end - dev->send_buf) + { + if (dev->data_width == 8) + value = *(uint8_t *)(dev->send_buf); + else + value = *(uint16_t *)(dev->send_buf); + } + else + { + return 0; + } + + mmio_write_32((uintptr_t)&dev->reg->spi_dr, value); + dev->send_buf += dev->data_width >> 3; + max--; + } + + return 0; +} +static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message) { + int ret = 0; + struct cv1800_spi *spi_dev; + + RT_ASSERT(device != RT_NULL); + RT_ASSERT(device->bus != RT_NULL); + RT_ASSERT(message != RT_NULL); + + spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data); + + if (message->send_buf != RT_NULL) + { + spi_dev->send_buf = message->send_buf; + spi_dev->send_end = (void *)((uint8_t *)spi_dev->send_buf + message->length); + } + + if (message->recv_buf != RT_NULL) + { + spi_dev->recv_buf = message->recv_buf; + spi_dev->recv_end = (void *)((uint8_t *)spi_dev->recv_buf + message->length); + } + + /* if user use their cs */ + if (message->cs_take && device->cs_pin != PIN_NONE) + rt_pin_write(device->cs_pin, PIN_LOW); + + if (message->send_buf) + { + while (spi_dev->send_buf != spi_dev->send_end) + { + hw_spi_send(spi_dev); + } + + /* wait for complete */ + while (mmio_read_32((uintptr_t)&spi_dev->reg->spi_txflr)) {} + + ret = message->length; + } + + if (message->recv_buf) + { + while (spi_dev->recv_buf != spi_dev->recv_end) + { + hw_spi_recv(spi_dev); + } + + ret = message->length; + } + + if (message->cs_release && device->cs_pin != PIN_NONE) + rt_pin_write(device->cs_pin, PIN_HIGH); + + return ret; +} + +const static struct rt_spi_ops drv_spi_ops = +{ + drv_spi_configure, + spixfer, +}; + +int rt_hw_spi_init(void) +{ + rt_err_t ret = RT_EOK; + struct spi_regs *reg = NULL; + + for (rt_size_t i = 0; i < sizeof(cv1800_spi_obj) / sizeof(struct cv1800_spi); i++) { + /* set reg base addr */ + reg = get_spi_base(cv1800_spi_obj[i].spi_id); + if (!reg) + return -RT_ERROR; + + cv1800_spi_obj[i].reg = reg; + cv1800_spi_obj[i].spi_bus.parent.user_data = &cv1800_spi_obj[i]; + + /* register spix bus */ + ret = rt_spi_bus_register(&cv1800_spi_obj[i].spi_bus, cv1800_spi_obj[i].device_name, &drv_spi_ops); + } + + return ret; +} +INIT_BOARD_EXPORT(rt_hw_spi_init); + +#endif /* RT_USING_SPI */ diff --git a/bsp/cvitek/drivers/drv_spi.h b/bsp/cvitek/drivers/drv_spi.h new file mode 100644 index 00000000000..2e20b15b2eb --- /dev/null +++ b/bsp/cvitek/drivers/drv_spi.h @@ -0,0 +1,155 @@ +/* + * 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 + */ +#ifndef __DRV_SPI_H__ +#define __DRV_SPI_H__ + +#include "rtdevice.h" +#include +#include + +#include "mmio.h" +#include "pinctrl.h" + +#define SPI0 0x0 +#define SPI1 0x1 +#define SPI2 0x2 +#define SPI3 0x3 + +#define SPI0_BASE 0x04180000 +#define SPI1_BASE 0x04190000 +#define SPI2_BASE 0x041A0000 +#define SPI3_BASE 0x041B0000 + +#define SPI_IRQ_MSAK 0x3e +#define SPI_FREQUENCY 187500000 + +/* Transmit FiFO Threshold Level */ +#define SPI_TXFTLR 0xf + +#define SPI_CTRL0_DATA_FREAM_SHIFT 0 +#define SPI_CTRL0_FREAM_FORMAT_SHIFT 4 +#define SPI_CTRL0_CPHA_SHIFT 6 +#define SPI_CTRL0_CPOL_SHIFT 7 +#define SPI_CTRL0_TRANS_MODE 8 +#define SPI_CTRL0_LOOP_SHIFT 11 +#define SPI_CTRL0_CTRL_FREAM_SHIFT 12 + +struct cv1800_spi { + uint8_t spi_id; + char *device_name; + + uint8_t fifo_len; + uint8_t data_width; + + const void *send_buf; + void *recv_buf; + + const void *send_end; + void *recv_end; + + struct rt_spi_bus spi_bus; + struct spi_regs *reg; +}; + +struct spi_regs { + uint32_t spi_ctrl0; // 0x00 + uint32_t spi_ctrl1; // 0x04 + uint32_t spi_ssienr; // 0x08 + uint32_t spi_mwcr; // 0x0c + uint32_t spi_ser; // 0x10 + uint32_t spi_baudr; // 0x14 + uint32_t spi_txftlr; // 0x18 + uint32_t spi_rxftlr; // 0x1c + uint32_t spi_txflr; // 0x20 + uint32_t spi_rxflr; // 0x24 + uint32_t spi_sr; // 0x28 + uint32_t spi_imr; // 0x2c + uint32_t spi_isr; // 0x30 + uint32_t spi_risr; // 0x34 + uint32_t spi_txoicr; // 0x38 + uint32_t spi_rxoicr; // 0x3c + uint32_t spi_rxuicr; // 0x40 + uint32_t spi_msticr; // 0x44 + uint32_t spi_icr; // 0x48 + uint32_t spi_dmacr; // 0x4c + uint32_t spi_dmatdlr; // 0x50 + uint32_t spi_dmardlr; // 0x54 + uint32_t spi_idr; // 0x58 + uint32_t spi_version; // 0x5c + uint32_t spi_dr; // 0x60 + uint32_t spi_rx_sample_dly; // 0xf0 + uint32_t spi_cs_override; // 0xf4 +}; + +uint32_t gen_spi_mode(struct rt_spi_configuration *cfg, uint32_t *mode) +{ + uint32_t value = 0; + + if (cfg->data_width != 8 && cfg->data_width != 16) + return -1; + + value |= (cfg->data_width - 1) >> SPI_CTRL0_DATA_FREAM_SHIFT; + value |= cfg->mode >> SPI_CTRL0_CPHA_SHIFT; + + *mode = value; + return 0; +} + +/* set spi mode */ +static inline void spi_set_mode(struct spi_regs *reg, uint32_t mode) +{ + mmio_write_32((uintptr_t)®->spi_ctrl0, mode); +} + +/* clear irq */ +static inline void spi_clear_irq(struct spi_regs *reg, uint32_t mode) +{ + mmio_write_32((uintptr_t)®->spi_imr, mode); +} + +static inline void spi_enable_cs(struct spi_regs *reg, uint32_t enable) +{ + if (enable) + enable = 0x1; + else + enable = 0x0; + + mmio_write_32((uintptr_t)®->spi_ser, enable); +} + +/* set spi frequency*/ +static inline rt_err_t spi_set_frequency(struct spi_regs *reg, uint32_t speed) +{ + uint16_t value; + + /* The value of the BAUDR register must be an even number between 2-65534 */ + value = SPI_FREQUENCY / speed; + if (value % 2 != 0) + value++; + + if (value < 4 || value > 65534) + value = 4; + + mmio_write_32((uintptr_t)®->spi_baudr, value); + + return RT_EOK; +} + +static inline void spi_enable(struct spi_regs *reg, uint32_t enable) +{ + if (enable) + enable = 0x1; + else + enable = 0x0; + + mmio_write_32((uintptr_t)®->spi_ssienr, enable); +} + +#endif /* __DRV_SPI_H__ */