[bsp][CV1800B] add SPI driver for CV1800B

Signed-off-by: Jingbao Qiu <qiujingbao.dlmu@gmail.com>
This commit is contained in:
Jingbao Qiu 2024-03-31 20:28:24 +08:00 committed by Meco Man
parent d856f77a55
commit c24280f6ef
6 changed files with 404 additions and 0 deletions

View File

@ -56,6 +56,7 @@ $ scons
| gpio | 支持 | | | gpio | 支持 | |
| i2c | 支持 | | | i2c | 支持 | |
| adc | 支持 | | | adc | 支持 | |
| spi | 支持 | 默认CS引脚每个数据之间CS会拉高请根据时序选择GPIO作为CS。若读取数据tx需持续dummy数据。|
## 支持开发板 ## 支持开发板
- milk-v duo: [https://milkv.io/duo](https://milkv.io/duo) - milk-v duo: [https://milkv.io/duo](https://milkv.io/duo)

View File

@ -45,6 +45,11 @@ menu "General Drivers Configuration"
select RT_USING_ADC select RT_USING_ADC
default n default n
config BSP_USING_SPI
bool "Using SPI"
select RT_USING_SPI
default n
menuconfig BSP_USING_WDT menuconfig BSP_USING_WDT
bool "Enable Watchdog Timer" bool "Enable Watchdog Timer"
select RT_USING_WDT select RT_USING_WDT

View File

@ -37,6 +37,11 @@ menu "General Drivers Configuration"
select RT_USING_ADC select RT_USING_ADC
default n default n
config BSP_USING_SPI
bool "Using SPI"
select RT_USING_SPI
default n
menuconfig BSP_USING_PWM menuconfig BSP_USING_PWM
bool "Using PWM" bool "Using PWM"
select RT_USING_PWM select RT_USING_PWM

View File

@ -30,6 +30,9 @@ if GetDepend('BSP_USING_ADC'):
if GetDepend('BSP_USING_WDT'): if GetDepend('BSP_USING_WDT'):
src += ['drv_wdt.c'] src += ['drv_wdt.c']
if GetDepend(['BSP_USING_SPI']):
src += ['drv_spi.c']
if GetDepend('BSP_USING_PWM'): if GetDepend('BSP_USING_PWM'):
src += ['drv_pwm.c'] src += ['drv_pwm.c']
CPPPATH += [cwd + r'/libraries/cv180x/pwm'] CPPPATH += [cwd + r'/libraries/cv180x/pwm']

View File

@ -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 */

View File

@ -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__ */