[bsp][CV1800B] add SPI driver for CV1800B
Signed-off-by: Jingbao Qiu <qiujingbao.dlmu@gmail.com>
This commit is contained in:
parent
d856f77a55
commit
c24280f6ef
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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 */
|
|
@ -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)®->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__ */
|
Loading…
Reference in New Issue