Browse Source

[bsp][CV1800B] add SPI driver for CV1800B

Signed-off-by: Jingbao Qiu <qiujingbao.dlmu@gmail.com>
Jingbao Qiu 1 year ago
parent
commit
249c9ba9cf

+ 1 - 0
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)

+ 5 - 0
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

+ 5 - 0
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

+ 3 - 0
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']

+ 235 - 0
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 <rtdbg.h>
+
+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 */

+ 155 - 0
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 <rthw.h>
+#include <rtthread.h>
+
+#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)&reg->spi_ctrl0, mode);
+}
+
+/* clear irq */
+static inline void spi_clear_irq(struct spi_regs *reg, uint32_t mode)
+{
+    mmio_write_32((uintptr_t)&reg->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)&reg->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)&reg->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)&reg->spi_ssienr, enable);
+}
+
+#endif /* __DRV_SPI_H__ */