Browse Source

bsp: cvitek: re-wrote spi driver

Rewrote the spi driver.

Reuse the driver code from https://github.com/sophgo/cvi_alios_open,
which is Apache 2.0 licenced.

Signed-off-by: flyingcys <flyingcys@163.com>
Reviewed-by: Chen Wang <unicorn_wang@outlook.com>
flyingcys 1 year ago
parent
commit
1ef2afbc93

+ 2 - 0
bsp/cvitek/drivers/SConscript

@@ -31,6 +31,8 @@ if GetDepend('BSP_USING_WDT'):
 
 if GetDepend(['BSP_USING_SPI']):
     src += ['drv_spi.c']
+    src += ['libraries/spi/dw_spi.c']
+    CPPPATH += [cwd + r'/libraries/spi']
 
 if GetDepend('BSP_USING_PWM'):
     src += ['drv_pwm.c']

+ 182 - 173
bsp/cvitek/drivers/drv_spi.c

@@ -1,217 +1,231 @@
 /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
+ * 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-03-28     qiujingbao   first version
+ * 2024/06/08     flyingcys    fix transmission failure
  */
 
+#include <rtthread.h>
+#include <rthw.h>
+#include <rtdevice.h>
+
+#include "board.h"
 #include "drv_spi.h"
+
 #include "drv_pinmux.h"
 
-#define DBG_TAG "drv.spi"
-#define DBG_LVL DBG_INFO
+#define DBG_LEVEL   DBG_LOG
 #include <rtdbg.h>
+#define LOG_TAG "drv.spi"
+
+struct _device_spi
+{
+    struct rt_spi_bus spi_bus;
+    struct dw_spi dws;
+    char *device_name;
+};
 
-static struct cv1800_spi cv1800_spi_obj[] =
+static struct _device_spi _spi_obj[] =
 {
-#ifdef BSP_USING_SPI
+#ifdef BSP_USING_SPI0
     {
-        .spi_id = SPI2,
+        .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",
-        .fifo_len = SPI_TXFTLR,
     },
-#endif
+#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 struct spi_regs *get_spi_base(uint8_t spi_id)
+static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
 {
-    struct spi_regs *spi_base = NULL;
+    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;
 
-    switch (spi_id)
+    LOG_D("spi_configure input");
+
+    /* set cs low when spi idle */
+    writel(0, (void *)0x030001d0);
+
+    if (cfg->mode & RT_SPI_SLAVE)
     {
-    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;
+        LOG_E("invalid mode: %d", cfg->mode);
+        return -RT_EINVAL;
     }
 
-    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_reset_chip(dws);
+    spi_hw_init(dws);
+    spi_enable_chip(dws, 0);
 
-    spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data);
+    LOG_D("cfg->max_hz: %d", cfg->max_hz);
+    dw_spi_set_clock(dws, SPI_REF_CLK, cfg->max_hz);
 
-    spi_dev->data_width = configuration->data_width;
+    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;
+    }
 
-    /* disable spi */
-    spi_enable(spi_dev->reg, 0);
+    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;
 
-    /* clear irq */
-    spi_clear_irq(spi_dev->reg, SPI_IRQ_MSAK);
+        case RT_SPI_MODE_1:
+            mode = SPI_FORMAT_CPOL0_CPHA1;
+            break;
 
-    /* set clk */
-    ret = spi_set_frequency(spi_dev->reg, configuration->max_hz);
-    if (ret)
-        return ret;
+        case RT_SPI_MODE_2:
+            mode = SPI_FORMAT_CPOL1_CPHA0;
+            break;
 
-    /* set mode */
-    ret = gen_spi_mode(configuration, &mode);
-    if (ret)
-        return ret;
+        case RT_SPI_MODE_3:
+            mode = SPI_FORMAT_CPOL1_CPHA1;
+            break;
 
-    spi_set_mode(spi_dev->reg, mode);
+        default:
+            LOG_E("spi configure mode error %x\n", cfg->mode);
+            break;
+    }
 
-    /* set cs */
-    spi_enable_cs(spi_dev->reg, 0x1);
+    dw_spi_set_polarity_and_phase(dws, mode);
 
-    spi_enable(spi_dev->reg, 0x1);
+    dw_spi_set_cs(dws, 1, 0);
 
-    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);
+    spi_enable_chip(dws, 1);
 
-    return ret;
+    return RT_EOK;
 }
 
-int hw_spi_recv(struct cv1800_spi *dev) {
-    uint32_t rever;
-    uint32_t tem;
-    int ret;
+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;
 
-    rever = mmio_read_32((uintptr_t)&dev->reg->spi_rxflr);
-    ret = (int)rever;
+    dws->tx = NULL;
+    dws->tx_end = NULL;
+    dws->rx = NULL;
+    dws->rx_end = NULL;
 
-    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;
+    if (tx_buf != NULL) {
+        dws->tx = tx_buf;
+        dws->tx_end = dws->tx + len;
     }
-    return ret;
-}
 
-int hw_spi_send(struct cv1800_spi *dev) {
-    uint32_t txflr;
-    uint32_t max;
-    uint16_t value;
+    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);
 
-    txflr = mmio_read_32((uintptr_t)&dev->reg->spi_txflr);
-    max = dev->fifo_len - txflr;
+    /* cs0 */
+    dw_spi_set_cs(dws, true, 0);
 
-    while (max)
+    /* 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
     {
-        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 -RT_ENOSYS;
     }
 
-    return 0;
+    return RT_EOK;
 }
-static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message) {
-    int ret = 0;
-    struct cv1800_spi *spi_dev;
 
+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);
 
-    spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data);
+    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 != RT_NULL)
+    if (message->send_buf && message->recv_buf)
     {
-        spi_dev->send_buf = message->send_buf;
-        spi_dev->send_end = (void *)((uint8_t *)spi_dev->send_buf + message->length);
-    }
+        ret = dw_spi_transfer_one(dws, message->send_buf, message->recv_buf, message->length, POLL_TRAN);
 
-    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)
+    else if (message->send_buf)
     {
-        while (spi_dev->send_buf != spi_dev->send_end)
-        {
-            hw_spi_send(spi_dev);
-        }
+        ret = dw_spi_transfer_one(dws, message->send_buf, RT_NULL, message->length, POLL_TRAN);
 
-        /* wait for complete */
-        while (mmio_read_32((uintptr_t)&spi_dev->reg->spi_txflr)) {}
-
-        ret = message->length;
     }
-
-    if (message->recv_buf)
+    else if (message->recv_buf)
     {
-        while (spi_dev->recv_buf != spi_dev->recv_end)
-        {
-            hw_spi_recv(spi_dev);
-        }
+        ret = dw_spi_transfer_one(dws, RT_NULL, message->recv_buf, message->length, POLL_TRAN);
 
-        ret = message->length;
     }
 
-    if (message->cs_release && device->cs_pin != PIN_NONE)
-        rt_pin_write(device->cs_pin, PIN_HIGH);
-
-    return ret;
+    return message->length;
 }
 
-const static struct rt_spi_ops drv_spi_ops =
+static const struct rt_spi_ops _spi_ops =
 {
-    drv_spi_configure,
-    spixfer,
+    .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,
@@ -283,54 +297,49 @@ static const char *pinname_whitelist_spi3_cs[] = {
 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);
+    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);
+    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);
+    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);
+    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 spi_regs *reg = NULL;
+    struct dw_spi *dws;
 
     rt_hw_spi_pinmux_config();
 
-    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);
+    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_BOARD_EXPORT(rt_hw_spi_init);

+ 14 - 141
bsp/cvitek/drivers/drv_spi.h

@@ -5,151 +5,24 @@
  *
  * Change Logs:
  * Date           Author       Notes
- * 2024-03-28      qiujingbao   first version
+ * 2024-03-28     qiujingbao   first version
+ * 2024/06/08     flyingcys    fix transmission failure
  */
 #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);
-}
+#include "dw_spi.h"
+
+#define DW_SPI_REG_SIZE             (0x10000UL)
+#define DW_SPI0_BASE                (0x04180000UL)
+#define DW_SPI1_BASE                (DW_SPI0_BASE + 1 * DW_SPI_REG_SIZE)
+#define DW_SPI2_BASE                (DW_SPI0_BASE + 2 * DW_SPI_REG_SIZE)
+#define DW_SPI3_BASE                (DW_SPI0_BASE + 3 * DW_SPI_REG_SIZE)
+
+#define DW_SPI0_IRQn                54
+#define DW_SPI1_IRQn                55
+#define DW_SPI2_IRQn                56
+#define DW_SPI3_IRQn                56
 
 #endif /* __DRV_SPI_H__ */

+ 375 - 0
bsp/cvitek/drivers/libraries/spi/dw_spi.c

@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mmio.h"
+#include "dw_spi.h"
+
+#include <rthw.h>
+
+#ifdef SPI_DEBUG
+#define SP_DEBUG_LOG    printf
+#else
+#define SP_DEBUG_LOG
+#endif
+
+/* Restart the controller, disable all interrupts, clean rx fifo */
+void spi_hw_init(struct dw_spi *dws)
+{
+    /*
+     * Try to detect the FIFO depth if not set by interface driver,
+     * the depth could be from 2 to 256 from HW spec
+     */
+    if (!dws->fifo_len) {
+        uint32_t fifo;
+
+        for (fifo = 1; fifo < 256; fifo++) {
+            dw_writel(dws, CVI_DW_SPI_TXFTLR, fifo);
+            if (fifo != dw_readl(dws, CVI_DW_SPI_TXFTLR))
+                break;
+        }
+        dw_writel(dws, CVI_DW_SPI_TXFTLR, 0);
+
+        dws->fifo_len = (fifo == 1) ? 0 : fifo;
+
+        SP_DEBUG_LOG("Detected FIFO size: %u bytes\n", dws->fifo_len);
+    }
+}
+
+uint32_t min3(uint32_t a, uint32_t b, uint32_t c)
+{
+    uint32_t tmp;
+
+    tmp = (a < b) ? a : b;
+    return (tmp < c) ? tmp : c;
+}
+
+static inline void cpu_relax(void)
+{
+    //asm volatile("" ::: "memory");
+}
+
+static inline uint32_t tx_max(struct dw_spi *dws)
+{
+    uint32_t tx_left, tx_room, rxtx_gap, temp;
+    cpu_relax();
+    tx_left = dws->tx_len;
+    tx_room = dws->fifo_len - dw_readl(dws, CVI_DW_SPI_TXFLR);
+
+    /*
+     * Another concern is about the tx/rx mismatch, we
+     * though to use (dws->fifo_len - rxflr - txflr) as
+     * one maximum value for tx, but it doesn't cover the
+     * data which is out of tx/rx fifo and inside the
+     * shift registers. So a control from sw point of
+     * view is taken.
+     */
+
+    SP_DEBUG_LOG("tx left: %#x, tx room: %#x\n", tx_left, tx_room);
+    if (dws->rx != NULL && dws->tx != NULL) {
+        cpu_relax();
+        rxtx_gap =  dws->fifo_len - (dws->rx_len - dws->tx_len);
+        temp = min3(tx_left, tx_room, (uint32_t)(rxtx_gap));
+    } else {
+        temp = tx_left < tx_room ? tx_left : tx_room;
+    }
+
+    SP_DEBUG_LOG("temp: %#x\n", temp);
+    return temp;
+}
+
+void dw_writer(struct dw_spi *dws)
+{
+    uint32_t max;
+    uint16_t txw = 0;
+
+    max = tx_max(dws);
+    SP_DEBUG_LOG("max: %#x \n", max);
+    while (max--) {
+        if (dws->tx) {
+            if (dws->n_bytes == 1)
+                txw = *(uint8_t *)(dws->tx);
+            else
+                txw = *(uint16_t *)(dws->tx);
+        }
+        dw_writel(dws, CVI_DW_SPI_DR, txw);
+        dws->tx += dws->n_bytes;
+        --dws->tx_len;
+    }
+}
+
+static inline uint32_t rx_max(struct dw_spi *dws)
+{
+    uint32_t temp;
+    uint32_t rx_left = dws->rx_len;
+    uint32_t data_in_fifo = dw_readl(dws, CVI_DW_SPI_RXFLR);
+
+    temp = (rx_left < data_in_fifo ? rx_left : data_in_fifo);
+    SP_DEBUG_LOG("data_in_fifo:%u temp: %u\n", data_in_fifo, temp);
+    return temp;
+}
+
+int dw_spi_check_status(struct dw_spi *dws, bool raw)
+{
+    uint32_t irq_status;
+    int ret = 0;
+
+    if (raw)
+        irq_status = dw_readl(dws, CVI_DW_SPI_RISR);
+    else
+        irq_status = dw_readl(dws, CVI_DW_SPI_ISR);
+
+    if (irq_status & CVI_SPI_INT_RXOI) {
+        SP_DEBUG_LOG("RX FIFO overflow detected\n");
+        ret = -1;
+    }
+
+    if (irq_status & CVI_SPI_INT_RXUI) {
+        SP_DEBUG_LOG("RX FIFO underflow detected\n");
+        ret = -1;
+    }
+
+    if (irq_status & CVI_SPI_INT_TXOI) {
+        SP_DEBUG_LOG("TX FIFO overflow detected\n");
+        ret = -1;
+    }
+
+    if (ret)
+        spi_reset_chip(dws);
+
+    return ret;
+}
+
+void dw_reader(struct dw_spi *dws)
+{
+    uint32_t max;
+    uint16_t rxw;
+
+    max = rx_max(dws);
+    SP_DEBUG_LOG("max: %#x \n", max);
+    while (max--) {
+        rxw = dw_readl(dws, CVI_DW_SPI_DR);
+        if (dws->rx) {
+            if (dws->n_bytes == 1)
+                *(uint8_t *)(dws->rx) = rxw;
+            else
+                *(uint16_t *)(dws->rx) = rxw;
+            dws->rx += dws->n_bytes;
+        }
+        --dws->rx_len;
+    }
+}
+
+int spi_delay_to_ns(struct spi_delay *_delay, struct dw_spi *dws)
+{
+    uint32_t delay = _delay->value;
+    uint32_t unit = _delay->unit;
+    uint32_t hz;
+
+    if (!delay)
+        return 0;
+
+    switch (unit) {
+        case SPI_DELAY_UNIT_USECS:
+            delay *= 1000;
+            break;
+        case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
+            break;
+        case SPI_DELAY_UNIT_SCK:
+            /* clock cycles need to be obtained from spi_transfer */
+            if (!dws)
+                return -1;
+            /* if there is no effective speed know, then approximate
+             * by underestimating with half the requested hz
+             */
+            hz = dws->speed_hz / 2;
+            if (!hz)
+                return -1;
+            delay *= DIV_ROUND_UP(1000000000, hz);
+            break;
+        default:
+            return -EINVAL;
+    }
+
+    return delay;
+}
+
+static void _spi_transfer_delay_ns(uint32_t ns)
+{
+        if (!ns)
+                return;
+        if (ns <= 1000) {
+        rt_hw_us_delay(1);
+        } else {
+                uint32_t us = DIV_ROUND_UP(ns, 1000);
+                rt_hw_us_delay(us);
+        }
+}
+
+int spi_delay_exec(struct spi_delay *_delay, struct dw_spi *dws)
+{
+        int delay;
+
+        if (!_delay)
+                return -1;
+
+        delay = spi_delay_to_ns(_delay, dws);
+        if (delay < 0)
+                return delay;
+
+        _spi_transfer_delay_ns(delay);
+
+        return 0;
+}
+
+int poll_transfer(struct dw_spi *dws)
+{
+    struct spi_delay delay;
+    uint16_t nbits;
+    delay.unit = SPI_DELAY_UNIT_SCK;
+    nbits = dws->n_bytes * BITS_PER_BYTE;
+    int ret = 0;
+
+    do
+    {
+        dw_writer(dws);
+        cpu_relax();
+
+        delay.value = nbits * (dws->rx_len - dws->tx_len);
+        spi_delay_exec(&delay, dws);
+        dw_reader(dws);
+        cpu_relax();
+        ret = dw_spi_check_status(dws, true);
+        if (ret)
+            return ret;
+    } while (dws->rx_len && dws->tx_len);
+
+    return 0;
+}
+
+void set_tran_mode(struct dw_spi *dws)
+{
+    uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
+    uint8_t tmode;
+
+    if (dws->rx && dws->tx) {
+        tmode = CVI_SPI_TMOD_TR;
+    } else if (dws->rx) {
+        tmode = CVI_SPI_TMOD_RO;
+    } else {
+        tmode = CVI_SPI_TMOD_TO;
+    }
+    reg &= ~CVI_SPI_TMOD_MASK;
+    reg |= (tmode << CVI_SPI_TMOD_OFFSET);
+
+    dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
+}
+
+void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master)
+{
+    /* do not support to switch controller mode, it is default master mode */
+}
+
+void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index)
+{
+    uint32_t reg = dw_readl(dws, CVI_DW_SPI_SER);
+
+    if (enable)
+        dw_writel(dws, CVI_DW_SPI_SER, reg | BIT(index));
+    else
+        dw_writel(dws, CVI_DW_SPI_SER, reg & ~BIT(index));
+}
+
+void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format)
+{
+    uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
+    reg &= ~(0x3 << 6);
+
+    switch (format) {
+        case SPI_FORMAT_CPOL0_CPHA0:
+            reg |= (SPI_MODE_0 << 6);
+            break;
+
+        case SPI_FORMAT_CPOL0_CPHA1:
+            reg |= (SPI_MODE_1 << 6);
+            break;
+
+        case SPI_FORMAT_CPOL1_CPHA0:
+            reg |= (SPI_MODE_2 << 6);
+            break;
+
+        case SPI_FORMAT_CPOL1_CPHA1:
+            reg |= (SPI_MODE_3 << 6);
+            break;
+
+        default:
+            reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
+            break;
+    }
+    SP_DEBUG_LOG("set phase and polarity: %#x\n", reg);
+    dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
+}
+
+uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out)
+{
+    uint16_t div;
+
+    div = (DIV_ROUND_UP(clock_in, clock_out) + 1) & 0xfffe;
+    dws->speed_hz = clock_in / div;
+    SP_DEBUG_LOG("clk div value is: %u, hz:%u\n", div, dws->speed_hz);
+    spi_set_clk(dws, div);
+    return dws->speed_hz;
+}
+
+int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size)
+{
+    uint32_t temp = dw_readl(dws, CVI_DW_SPI_CTRLR0);
+    temp &= ~0xf;
+
+    if (size == 8) {
+        dws->n_bytes = 1;
+    } else if (size == 16) {
+        dws->n_bytes = 2;
+    } else {
+        SP_DEBUG_LOG("do not support %u bit data!\n", size);
+        return -1;
+    }
+    temp |= (size - 1);
+    dw_writel(dws, CVI_DW_SPI_CTRLR0, temp);
+    SP_DEBUG_LOG("set data frame len: %#x\n", temp);
+    return 0;
+}
+
+void dw_spi_show_regs(struct dw_spi *dws)
+{
+    SP_DEBUG_LOG("CTRLR0: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR0));
+    SP_DEBUG_LOG("CTRLR1: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR1));
+    SP_DEBUG_LOG("SSIENR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SSIENR));
+    SP_DEBUG_LOG("SER: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SER));
+    SP_DEBUG_LOG("BAUDR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_BAUDR));
+    SP_DEBUG_LOG("TXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFTLR));
+    SP_DEBUG_LOG("RXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFTLR));
+    SP_DEBUG_LOG("TXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFLR));
+    SP_DEBUG_LOG("RXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFLR));
+    SP_DEBUG_LOG("SR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SR));
+    SP_DEBUG_LOG("IMR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_IMR));
+    SP_DEBUG_LOG("ISR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_ISR));
+    SP_DEBUG_LOG("DMACR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMACR));
+    SP_DEBUG_LOG("DMATDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMATDLR));
+    SP_DEBUG_LOG("DMARDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMARDLR));
+}

+ 246 - 0
bsp/cvitek/drivers/libraries/spi/dw_spi.h

@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __DW_SPI_HEADER_H__
+#define __DW_SPI_HEADER_H__
+
+#include "stdint.h"
+#include "stdbool.h"
+
+#define SPI_REGBASE                     0x04180000
+#define SPI_REF_CLK                     187500000
+#define MAX_SPI_NUM                     4
+
+#define CVI_DW_SPI_CTRLR0                   0x00
+#define CVI_DW_SPI_CTRLR1                   0x04
+#define CVI_DW_SPI_SSIENR                   0x08
+#define CVI_DW_SPI_MWCR                     0x0c
+#define CVI_DW_SPI_SER                      0x10
+#define CVI_DW_SPI_BAUDR                    0x14
+#define CVI_DW_SPI_TXFTLR                   0x18
+#define CVI_DW_SPI_RXFTLR                   0x1c
+#define CVI_DW_SPI_TXFLR                    0x20
+#define CVI_DW_SPI_RXFLR                    0x24
+#define CVI_DW_SPI_SR                       0x28
+#define CVI_DW_SPI_IMR                      0x2c
+#define CVI_DW_SPI_ISR                      0x30
+#define CVI_DW_SPI_RISR                     0x34
+#define CVI_DW_SPI_TXOICR                   0x38
+#define CVI_DW_SPI_RXOICR                   0x3c
+#define CVI_DW_SPI_RXUICR                   0x40
+#define CVI_DW_SPI_MSTICR                   0x44
+#define CVI_DW_SPI_ICR                      0x48
+#define CVI_DW_SPI_DMACR                    0x4c
+#define CVI_DW_SPI_DMATDLR                  0x50
+#define CVI_DW_SPI_DMARDLR                  0x54
+#define CVI_DW_SPI_IDR                      0x58
+#define CVI_DW_SPI_VERSION                  0x5c
+#define CVI_DW_SPI_DR                       0x60
+
+/* Bit fields in CTRLR0 */
+#define CVI_SPI_DFS_OFFSET                  0
+
+#define CVI_SPI_FRF_OFFSET                  4
+#define CVI_SPI_FRF_SPI                     0x0
+#define CVI_SPI_FRF_SSP                     0x1
+#define CVI_SPI_FRF_MICROWIRE               0x2
+#define CVI_SPI_FRF_RESV                    0x3
+
+#define CVI_SPI_MODE_OFFSET                 6
+#define CVI_SPI_SCPH_OFFSET                 6
+#define CVI_SPI_SCOL_OFFSET                 7
+
+#define CVI_SPI_TMOD_OFFSET                 8
+#define CVI_SPI_TMOD_MASK                   (0x3 << CVI_SPI_TMOD_OFFSET)
+#define CVI_SPI_TMOD_TR                     0x0             /* xmit & recv */
+#define CVI_SPI_TMOD_TO                     0x1             /* xmit only */
+#define CVI_SPI_TMOD_RO                     0x2             /* recv only */
+#define CVI_SPI_TMOD_EPROMREAD              0x3             /* eeprom read mode */
+
+#define CVI_SPI_SLVOE_OFFSET                10
+#define CVI_SPI_SRL_OFFSET                  11
+#define CVI_SPI_CFS_OFFSET                  12
+
+/* Bit fields in SR, 7 bits */
+#define CVI_SR_MASK                         0x7f
+#define CVI_SR_BUSY                         (1 << 0)
+#define CVI_SR_TF_NOT_FULL                  (1 << 1)
+#define CVI_SR_TF_EMPT                      (1 << 2)
+#define CVI_SR_RF_NOT_EMPT                  (1 << 3)
+#define CVI_SR_RF_FULL                      (1 << 4)
+#define CVI_SR_TX_ERR                       (1 << 5)
+#define SR_DCOL                         (1 << 6)
+
+/* Bit fields in ISR, IMR, RISR, 7 bits */
+#define CVI_SPI_INT_TXEI                    (1 << 0)
+#define CVI_SPI_INT_TXOI                    (1 << 1)
+#define CVI_SPI_INT_RXUI                    (1 << 2)
+#define CVI_SPI_INT_RXOI                    (1 << 3)
+#define CVI_SPI_INT_RXFI                    (1 << 4)
+#define CVI_SPI_INT_MSTI                    (1 << 5)
+
+/* Bit fields in DMACR */
+#define CVI_SPI_DMA_RDMAE                   (1 << 0)
+#define CVI_SPI_DMA_TDMAE                   (1 << 1)
+
+/* TX RX interrupt level threshold, max can be 256 */
+#define CVI_SPI_INT_THRESHOLD               32
+#define BITS_PER_BYTE                       8
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+struct dw_spi {
+    void                    *regs;
+    int                     irq;
+    int                     index;
+    uint32_t                fifo_len;       /* depth of the FIFO buffer */
+    uint16_t                num_cs;        /* supported slave numbers */
+    uint32_t        speed_hz;
+    /* Current message transfer state info */
+    size_t                  len;
+    const void              *tx;
+    const void              *tx_end;
+    void                    *rx;
+    void                    *rx_end;
+    uint32_t        rx_len;
+    uint32_t        tx_len;
+    uint8_t                 n_bytes;       /* current is a 1/2 bytes op */
+    uint32_t                dma_width;
+    int             (*transfer_handler)(struct dw_spi *dws);
+
+    /* Bus interface info */
+    void                    *priv;
+};
+
+struct spi_delay {
+#define SPI_DELAY_UNIT_USECS    0
+#define SPI_DELAY_UNIT_NSECS    1
+#define SPI_DELAY_UNIT_SCK      2
+        uint16_t     value;
+        uint8_t      unit;
+};
+
+#define SPI_CPHA                0x01
+#define SPI_CPOL                0x02
+
+#define SPI_MODE_0              (0|0)
+#define SPI_MODE_1              (0|SPI_CPHA)
+#define SPI_MODE_2              (SPI_CPOL|0)
+#define SPI_MODE_3              (SPI_CPOL|SPI_CPHA)
+
+enum transfer_type {
+    POLL_TRAN = 0,
+    IRQ_TRAN,
+    DMA_TRAN,
+};
+
+enum dw_ssi_type {
+    SSI_MOTO_SPI = 0,
+    SSI_TI_SSP,
+    SSI_NS_MICROWIRE,
+};
+
+#define SPI_FORMAT_CPOL0_CPHA0  0
+#define SPI_FORMAT_CPOL0_CPHA1  1
+#define SPI_FORMAT_CPOL1_CPHA0  2
+#define SPI_FORMAT_CPOL1_CPHA1  3
+
+#ifndef BIT
+#define BIT(_n)  ( 1 << (_n))
+#endif
+
+static void dw_writel(struct dw_spi *dws, uint32_t off, uint32_t val)
+{
+    writel(val, (dws->regs + off));
+}
+
+static uint32_t dw_readl(struct dw_spi *dws, uint32_t off)
+{
+    return readl(dws->regs + off);
+}
+
+static inline void spi_enable_chip(struct dw_spi *dws, int enable)
+{
+    dw_writel(dws, CVI_DW_SPI_SSIENR, (enable ? 1 : 0));
+}
+
+static inline void spi_set_clk(struct dw_spi *dws, uint16_t div)
+{
+    dw_writel(dws, CVI_DW_SPI_BAUDR, div);
+}
+
+/* Disable IRQ bits */
+static inline void spi_mask_intr(struct dw_spi *dws, uint32_t mask)
+{
+    uint32_t new_mask;
+
+    new_mask = dw_readl(dws, CVI_DW_SPI_IMR) & ~mask;
+    dw_writel(dws, CVI_DW_SPI_IMR, new_mask);
+}
+
+static inline uint32_t spi_get_status(struct dw_spi *dws)
+{
+    return dw_readl(dws, CVI_DW_SPI_SR);
+}
+
+/* Enable IRQ bits */
+static inline void spi_umask_intr(struct dw_spi *dws, uint32_t mask)
+{
+    uint32_t new_mask;
+
+    new_mask = dw_readl(dws, CVI_DW_SPI_IMR) | mask;
+    dw_writel(dws, CVI_DW_SPI_IMR, new_mask);
+}
+
+static inline void spi_reset_chip(struct dw_spi *dws)
+{
+    spi_enable_chip(dws, 0);
+    spi_mask_intr(dws, 0xff);
+    dw_readl(dws, CVI_DW_SPI_ICR);
+    dw_writel(dws, CVI_DW_SPI_SER, 0);
+    spi_enable_chip(dws, 1);
+}
+
+static inline void spi_enable_dma(struct dw_spi *dws, uint8_t is_tx, uint8_t op)
+{
+    /* 1: TDMAE, 0: RDMAE */
+    uint32_t val = dw_readl(dws, CVI_DW_SPI_DMACR);
+
+    if (op)
+        val |= 1 << (!!is_tx);
+    else
+        val &= ~(1 << (!!is_tx));
+
+    dw_writel(dws, CVI_DW_SPI_DMACR, val);
+}
+
+static inline void spi_shutdown_chip(struct dw_spi *dws)
+{
+    spi_enable_chip(dws, 0);
+    spi_set_clk(dws, 0);
+}
+
+void spi_hw_init(struct dw_spi *dws);
+void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master);
+void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format);
+uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out);
+int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size);
+void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index);
+void dw_reader(struct dw_spi *dws);
+void dw_writer(struct dw_spi *dws);
+void set_tran_mode(struct dw_spi *dws);
+void dw_spi_show_regs(struct dw_spi *dws);
+int poll_transfer(struct dw_spi *dws);
+int dw_spi_check_status(struct dw_spi *dws, bool raw);
+#endif