Merge pull request #9200 from unicornx/dev-spi-new
bsp: cvitek: re-wrote spi driver
This commit is contained in:
commit
a2f4e120c3
|
@ -31,6 +31,8 @@ if GetDepend('BSP_USING_WDT'):
|
||||||
|
|
||||||
if GetDepend(['BSP_USING_SPI']):
|
if GetDepend(['BSP_USING_SPI']):
|
||||||
src += ['drv_spi.c']
|
src += ['drv_spi.c']
|
||||||
|
src += ['libraries/spi/dw_spi.c']
|
||||||
|
CPPPATH += [cwd + r'/libraries/spi']
|
||||||
|
|
||||||
if GetDepend('BSP_USING_PWM'):
|
if GetDepend('BSP_USING_PWM'):
|
||||||
src += ['drv_pwm.c']
|
src += ['drv_pwm.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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*
|
*
|
||||||
* Change Logs:
|
* Change Logs:
|
||||||
* Date Author Notes
|
* 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_spi.h"
|
||||||
|
|
||||||
#include "drv_pinmux.h"
|
#include "drv_pinmux.h"
|
||||||
|
|
||||||
#define DBG_TAG "drv.spi"
|
#define DBG_LEVEL DBG_LOG
|
||||||
#define DBG_LVL DBG_INFO
|
|
||||||
#include <rtdbg.h>
|
#include <rtdbg.h>
|
||||||
|
#define LOG_TAG "drv.spi"
|
||||||
|
|
||||||
static struct cv1800_spi cv1800_spi_obj[] =
|
struct _device_spi
|
||||||
{
|
{
|
||||||
#ifdef BSP_USING_SPI
|
struct rt_spi_bus spi_bus;
|
||||||
{
|
struct dw_spi dws;
|
||||||
.spi_id = SPI2,
|
char *device_name;
|
||||||
.device_name = "spi2",
|
|
||||||
.fifo_len = SPI_TXFTLR,
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spi_regs *get_spi_base(uint8_t spi_id)
|
static struct _device_spi _spi_obj[] =
|
||||||
{
|
{
|
||||||
struct spi_regs *spi_base = NULL;
|
#ifdef BSP_USING_SPI0
|
||||||
|
|
||||||
switch (spi_id)
|
|
||||||
{
|
{
|
||||||
case SPI0:
|
.dws.regs = (void *)DW_SPI0_BASE,
|
||||||
spi_base = (struct spi_regs *)SPI0_BASE;
|
.dws.irq = DW_SPI0_IRQn,
|
||||||
break;
|
.dws.index = 0,
|
||||||
case SPI1:
|
.device_name = "spi0",
|
||||||
spi_base = (struct spi_regs *)SPI1_BASE;
|
},
|
||||||
break;
|
#endif /* BSP_USING_SPI0 */
|
||||||
case SPI2:
|
#ifdef BSP_USING_SPI1
|
||||||
spi_base = (struct spi_regs *)SPI2_BASE;
|
{
|
||||||
break;
|
.dws.regs = (void *)DW_SPI1_BASE,
|
||||||
case SPI3:
|
.dws.irq = DW_SPI1_IRQn,
|
||||||
spi_base = (struct spi_regs *)SPI3_BASE;
|
.dws.index = 0,
|
||||||
break;
|
.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 */
|
||||||
|
};
|
||||||
|
|
||||||
return spi_base;
|
static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
|
||||||
}
|
|
||||||
|
|
||||||
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 != RT_NULL);
|
||||||
RT_ASSERT(device->bus != RT_NULL);
|
RT_ASSERT(device->bus != RT_NULL);
|
||||||
RT_ASSERT(message != RT_NULL);
|
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
|
||||||
|
RT_ASSERT(cfg != RT_NULL);
|
||||||
|
|
||||||
spi_dev = (struct cv1800_spi *)(device->bus->parent.user_data);
|
rt_err_t ret = RT_EOK;
|
||||||
|
struct _device_spi *spi = (struct _device_spi *)device->bus->parent.user_data;
|
||||||
|
struct dw_spi *dws = &spi->dws;
|
||||||
|
|
||||||
if (message->send_buf != RT_NULL)
|
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)
|
||||||
{
|
{
|
||||||
spi_dev->send_buf = message->send_buf;
|
LOG_E("invalid mode: %d", cfg->mode);
|
||||||
spi_dev->send_end = (void *)((uint8_t *)spi_dev->send_buf + message->length);
|
return -RT_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message->recv_buf != RT_NULL)
|
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)
|
||||||
{
|
{
|
||||||
spi_dev->recv_buf = message->recv_buf;
|
LOG_E("dw_spi_set_data_frame_len failed...\n");
|
||||||
spi_dev->recv_end = (void *)((uint8_t *)spi_dev->recv_buf + message->length);
|
return -RT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if user use their cs */
|
LOG_D("cfg->mode: %08x", cfg->mode);
|
||||||
if (message->cs_take && device->cs_pin != PIN_NONE)
|
switch (cfg->mode & RT_SPI_MODE_3)
|
||||||
rt_pin_write(device->cs_pin, PIN_LOW);
|
|
||||||
|
|
||||||
if (message->send_buf)
|
|
||||||
{
|
{
|
||||||
while (spi_dev->send_buf != spi_dev->send_end)
|
case RT_SPI_MODE_0:
|
||||||
{
|
mode = SPI_FORMAT_CPOL0_CPHA0;
|
||||||
hw_spi_send(spi_dev);
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
/* wait for complete */
|
case RT_SPI_MODE_1:
|
||||||
while (mmio_read_32((uintptr_t)&spi_dev->reg->spi_txflr)) {}
|
mode = SPI_FORMAT_CPOL0_CPHA1;
|
||||||
|
break;
|
||||||
|
|
||||||
ret = message->length;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message->recv_buf)
|
dw_spi_set_polarity_and_phase(dws, mode);
|
||||||
{
|
|
||||||
while (spi_dev->recv_buf != spi_dev->recv_end)
|
|
||||||
{
|
|
||||||
hw_spi_recv(spi_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = message->length;
|
dw_spi_set_cs(dws, 1, 0);
|
||||||
}
|
|
||||||
|
|
||||||
if (message->cs_release && device->cs_pin != PIN_NONE)
|
spi_enable_chip(dws, 1);
|
||||||
rt_pin_write(device->cs_pin, PIN_HIGH);
|
|
||||||
|
|
||||||
return ret;
|
return RT_EOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const static struct rt_spi_ops drv_spi_ops =
|
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)
|
||||||
{
|
{
|
||||||
drv_spi_configure,
|
uint8_t imask = 0;
|
||||||
spixfer,
|
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)
|
#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
|
#ifdef BSP_USING_SPI0
|
||||||
static const char *pinname_whitelist_spi0_sck[] = {
|
static const char *pinname_whitelist_spi0_sck[] = {
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -283,54 +297,49 @@ static const char *pinname_whitelist_spi3_cs[] = {
|
||||||
static void rt_hw_spi_pinmux_config()
|
static void rt_hw_spi_pinmux_config()
|
||||||
{
|
{
|
||||||
#ifdef BSP_USING_SPI0
|
#ifdef BSP_USING_SPI0
|
||||||
pinmux_config(BSP_SPI0_SCK_PINNAME, SPI0_SCK, pinname_whitelist_spi0_sck);
|
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_SDO_PINNAME, SPI0_SDO, pinname_whitelist_spi0_sdo);
|
||||||
pinmux_config(BSP_SPI0_SDI_PINNAME, SPI0_SDI, pinname_whitelist_spi0_sdi);
|
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_CS_PINNAME, SPI0_CS_X, pinname_whitelist_spi0_cs);
|
||||||
#endif /* BSP_USING_SPI0 */
|
#endif /* BSP_USING_SPI0 */
|
||||||
|
|
||||||
#ifdef BSP_USING_SPI1
|
#ifdef BSP_USING_SPI1
|
||||||
pinmux_config(BSP_SPI1_SCK_PINNAME, SPI1_SCK, pinname_whitelist_spi1_sck);
|
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_SDO_PINNAME, SPI1_SDO, pinname_whitelist_spi1_sdo);
|
||||||
pinmux_config(BSP_SPI1_SDI_PINNAME, SPI1_SDI, pinname_whitelist_spi1_sdi);
|
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_CS_PINNAME, SPI1_CS_X, pinname_whitelist_spi1_cs);
|
||||||
#endif /* BSP_USING_SPI1 */
|
#endif /* BSP_USING_SPI1 */
|
||||||
|
|
||||||
#ifdef BSP_USING_SPI2
|
#ifdef BSP_USING_SPI2
|
||||||
pinmux_config(BSP_SPI2_SCK_PINNAME, SPI2_SCK, pinname_whitelist_spi2_sck);
|
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_SDO_PINNAME, SPI2_SDO, pinname_whitelist_spi2_sdo);
|
||||||
pinmux_config(BSP_SPI2_SDI_PINNAME, SPI2_SDI, pinname_whitelist_spi2_sdi);
|
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_CS_PINNAME, SPI2_CS_X, pinname_whitelist_spi2_cs);
|
||||||
#endif /* BSP_USING_SPI2 */
|
#endif /* BSP_USING_SPI2 */
|
||||||
|
|
||||||
#ifdef BSP_USING_SPI3
|
#ifdef BSP_USING_SPI3
|
||||||
pinmux_config(BSP_SPI3_SCK_PINNAME, SPI3_SCK, pinname_whitelist_spi3_sck);
|
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_SDO_PINNAME, SPI3_SDO, pinname_whitelist_spi3_sdo);
|
||||||
pinmux_config(BSP_SPI3_SDI_PINNAME, SPI3_SDI, pinname_whitelist_spi3_sdi);
|
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_CS_PINNAME, SPI3_CS_X, pinname_whitelist_spi3_cs);
|
||||||
#endif /* BSP_USING_SPI3 */
|
#endif /* BSP_USING_SPI3 */
|
||||||
}
|
}
|
||||||
|
|
||||||
int rt_hw_spi_init(void)
|
int rt_hw_spi_init(void)
|
||||||
{
|
{
|
||||||
rt_err_t ret = RT_EOK;
|
rt_err_t ret = RT_EOK;
|
||||||
struct spi_regs *reg = NULL;
|
struct dw_spi *dws;
|
||||||
|
|
||||||
rt_hw_spi_pinmux_config();
|
rt_hw_spi_pinmux_config();
|
||||||
|
|
||||||
for (rt_size_t i = 0; i < sizeof(cv1800_spi_obj) / sizeof(struct cv1800_spi); i++) {
|
for (rt_size_t i = 0; i < sizeof(_spi_obj) / sizeof(struct _device_spi); i++)
|
||||||
/* set reg base addr */
|
{
|
||||||
reg = get_spi_base(cv1800_spi_obj[i].spi_id);
|
_spi_obj[i].spi_bus.parent.user_data = (void *)&_spi_obj[i];
|
||||||
if (!reg)
|
ret = rt_spi_bus_register(&_spi_obj[i].spi_bus, _spi_obj[i].device_name, &_spi_ops);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RT_ASSERT(ret == RT_EOK);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
INIT_BOARD_EXPORT(rt_hw_spi_init);
|
INIT_BOARD_EXPORT(rt_hw_spi_init);
|
||||||
|
|
|
@ -5,151 +5,24 @@
|
||||||
*
|
*
|
||||||
* Change Logs:
|
* Change Logs:
|
||||||
* Date Author Notes
|
* 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__
|
#ifndef __DRV_SPI_H__
|
||||||
#define __DRV_SPI_H__
|
#define __DRV_SPI_H__
|
||||||
|
|
||||||
#include "rtdevice.h"
|
|
||||||
#include <rthw.h>
|
|
||||||
#include <rtthread.h>
|
|
||||||
|
|
||||||
#include "mmio.h"
|
#include "mmio.h"
|
||||||
#include "pinctrl.h"
|
#include "dw_spi.h"
|
||||||
|
|
||||||
#define SPI0 0x0
|
#define DW_SPI_REG_SIZE (0x10000UL)
|
||||||
#define SPI1 0x1
|
#define DW_SPI0_BASE (0x04180000UL)
|
||||||
#define SPI2 0x2
|
#define DW_SPI1_BASE (DW_SPI0_BASE + 1 * DW_SPI_REG_SIZE)
|
||||||
#define SPI3 0x3
|
#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 SPI0_BASE 0x04180000
|
#define DW_SPI0_IRQn 54
|
||||||
#define SPI1_BASE 0x04190000
|
#define DW_SPI1_IRQn 55
|
||||||
#define SPI2_BASE 0x041A0000
|
#define DW_SPI2_IRQn 56
|
||||||
#define SPI3_BASE 0x041B0000
|
#define DW_SPI3_IRQn 56
|
||||||
|
|
||||||
#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__ */
|
#endif /* __DRV_SPI_H__ */
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue