KT24-1110_65E-HA-651B/cpu/br25/spi.c
2024-11-10 18:44:17 +08:00

676 lines
17 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#undef LOG_TAG_CONST
#define LOG_TAG "[SPI]"
#define LOG_ERROR_ENABLE
//#define LOG_DEBUG_ENABLE
#include "system/debug.h"
#include "generic/gpio.h"
#include "asm/clock.h"
#include "asm/spi.h"
#include "update.h"
#define spi_enable(reg) ((reg)->CON |= BIT(0))
#define spi_disable(reg) ((reg)->CON &= ~BIT(0))
#define spi_role_slave(reg) ((reg)->CON |= BIT(1))
#define spi_role_master(reg) ((reg)->CON &= ~BIT(1))
#define spi_cs_en(reg) ((reg)->CON |= BIT(2))
#define spi_cs_dis(reg) ((reg)->CON &= ~BIT(2))
#define spi_bidir(reg) ((reg)->CON |= BIT(3))
#define spi_unidir(reg) ((reg)->CON &= ~BIT(3))
#define spi_smp_edge_rise(reg) ((reg)->CON &= ~BIT(4))
#define spi_smp_edge_fall(reg) ((reg)->CON |= BIT(4))
#define spi_ud_edge_rise(reg) ((reg)->CON &= ~BIT(5))
#define spi_ud_edge_fall(reg) ((reg)->CON |= BIT(5))
#define spi_clk_idle_h(reg) ((reg)->CON |= BIT(6))
#define spi_clk_idle_l(reg) ((reg)->CON &= ~BIT(6))
#define spi_cs_idle_h(reg) ((reg)->CON |= BIT(7))
#define spi_cs_idle_l(reg) ((reg)->CON &= ~BIT(7))
#define spi_data_width(reg, x) ((reg)->CON = (reg->CON&~(3<<10))|((x)<<10))
#define spi_dir_in(reg) ((reg)->CON |= BIT(12))
#define spi_dir_out(reg) ((reg)->CON &= ~BIT(12))
#define spi_ie_en(reg) ((reg)->CON |= BIT(13))
#define spi_ie_dis(reg) ((reg)->CON &= ~BIT(13))
#define spi_clr_pnd(reg) ((reg)->CON |= BIT(14))
#define spi_pnd(reg) ((reg)->CON & BIT(15))
#define spi_w_reg_con(reg, val) ((reg)->CON = (val))
#define spi_r_reg_con(reg) ((reg)->CON)
#define spi_w_reg_buf(reg, val) ((reg)->BUF = (val))
#define spi_r_reg_buf(reg) ((reg)->BUF)
#define spi_w_reg_baud(reg, baud) ((reg)->BAUD = (baud))
#define spi_r_reg_baud(reg) ((reg)->BAUD)
#define spi_w_reg_dma_addr(reg, addr) ((reg)->ADR = (addr))
#define spi_w_reg_dma_cnt(reg, cnt) ((reg)->CNT = (cnt))
#define SPI0_ENABLE 0 //是否使能SPI0使能后需定义spi0_p_data
#define SPI1_ENABLE 1 //是否使能SPI1使能后需定义spi1_p_data
#define SPI2_ENABLE 1 //是否使能SPI2使能后需定义spi2_p_data
static const struct spi_io_mapping spi_io_map[SPI_MAX_HW_NUM] = {
//SPI0
{
.num = 1,
//port A
.io[0] = {
.cs_pin = IO_PORTD_03,
.di_pin = IO_PORTD_02,
.do_pin = IO_PORTD_01,
.clk_pin = IO_PORTD_00,
.d2_pin = IO_PORTB_04,
.d3_pin = IO_PORTC_04,
}
},
//SPI1
{
.num = 2,
//port A
.io[0] = {
.cs_pin = -1,
.di_pin = IO_PORTB_02,
.do_pin = IO_PORTB_01,
.clk_pin = IO_PORTB_00,
.d2_pin = -1,
.d3_pin = -1,
},
//port B
.io[1] = {
.cs_pin = -1,
.di_pin = IO_PORTC_03,
.do_pin = IO_PORTC_05,
.clk_pin = IO_PORTC_04,
.d2_pin = -1,
.d3_pin = -1,
}
},
//SPI2
{
.num = 2,
//port A
.io[0] = {
.cs_pin = -1,
.di_pin = IO_PORTB_05,
.do_pin = IO_PORTB_07,
.clk_pin = IO_PORTB_06,
.d2_pin = -1,
.d3_pin = -1,
},
//port B
.io[1] = {
.cs_pin = -1,
.di_pin = IO_PORTC_01,
.do_pin = IO_PORT_DM,
.clk_pin = IO_PORT_DP,
.d2_pin = -1,
.d3_pin = -1,
}
},
};
static JL_SPI_TypeDef *const spi_regs[SPI_MAX_HW_NUM] = {
JL_SPI0,
JL_SPI1,
JL_SPI2,
};
static u8 spi_get_info_id(spi_dev spi)
{
ASSERT(spi < SPI_MAX_HW_NUM);
return spi;
}
static u8 spi_get_info_port(spi_dev spi)
{
u8 port = (u8) - 1;
u8 id = spi_get_info_id(spi);
switch (id) {
#if SPI0_ENABLE
case SPI0:
port = spi0_p_data.port;
break;
#endif
#if SPI1_ENABLE
case SPI1:
port = spi1_p_data.port;
break;
#endif
#if SPI2_ENABLE
case SPI2:
port = spi2_p_data.port;
break;
#endif
default:
break;
}
ASSERT(port - 'A' < spi_io_map[id].num);
return port;
}
static u8 spi_get_info_mode(spi_dev spi)
{
u8 mode = (u8) - 1;
u8 id = spi_get_info_id(spi);
switch (id) {
#if SPI0_ENABLE
case SPI0:
mode = spi0_p_data.mode;
break;
#endif
#if SPI1_ENABLE
case SPI1:
mode = spi1_p_data.mode;
break;
#endif
#if SPI2_ENABLE
case SPI2:
mode = spi2_p_data.mode;
break;
#endif
default:
break;
}
return mode;
}
static u8 spi_get_info_role(spi_dev spi)
{
u8 role = (u8) - 1;
u8 id = spi_get_info_id(spi);
switch (id) {
#if SPI0_ENABLE
case SPI0:
role = spi0_p_data.role;
break;
#endif
#if SPI1_ENABLE
case SPI1:
role = spi1_p_data.role;
break;
#endif
#if SPI2_ENABLE
case SPI2:
role = spi2_p_data.role;
break;
#endif
default:
break;
}
return role;
}
static u32 spi_get_info_clock(spi_dev spi)
{
u32 clock = (u8) - 1;
u8 id = spi_get_info_id(spi);
switch (id) {
#if SPI0_ENABLE
case SPI0:
clock = spi0_p_data.clk;
break;
#endif
#if SPI1_ENABLE
case SPI1:
clock = spi1_p_data.clk;
break;
#endif
#if SPI2_ENABLE
case SPI2:
clock = spi2_p_data.clk;
break;
#endif
default:
break;
}
return clock;
}
static void spi0_iomc_config(spi_dev spi)
{
u8 port = spi_get_info_port(spi);
if (port == 'A') {
JL_IOMAP->CON0 &= ~BIT(2);
}
}
static void spi1_iomc_config(spi_dev spi)
{
u8 port = spi_get_info_port(spi);
if (port == 'A') {
JL_IOMAP->CON1 &= ~BIT(4);
} else if (port == 'B') {
JL_IOMAP->CON1 |= BIT(4);
}
}
static void spi2_iomc_config(spi_dev spi)
{
u8 port = spi_get_info_port(spi);
if (port == 'A') {
JL_IOMAP->CON1 &= ~BIT(16);
} else if (port == 'B') {
JL_IOMAP->CON1 |= BIT(16);
}
}
static void (*pSPI_IOMC_CONFIG[])(spi_dev spi) = {
spi0_iomc_config,
spi1_iomc_config,
spi2_iomc_config,
};
static void spi_io_port_init(u8 port, u8 dir)
{
if (port != (u8) - 1) {
if (dir == 1) {
gpio_set_direction(port, 1);
} else {
gpio_set_direction(port, 0);
}
gpio_set_die(port, 1);
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 0);
}
}
static void spi_io_port_uninit(u8 port)
{
if (port != (u8) - 1) {
gpio_set_direction(port, 1);
gpio_set_die(port, 0);
gpio_set_pull_up(port, 0);
gpio_set_pull_down(port, 0);
}
}
/*
* @brief 设置波特率
* @parm spi spi句柄
* @parm baud 波特率
* @return 0 成功,< 0 失败
*/
int spi_set_baud(spi_dev spi, u32 baud)
{
//SPICK = sysclk / (SPIx_BAUD + 1)
//=> SPIx_BAUD = sysclk / SPICK - 1
u8 id = spi_get_info_id(spi);
u32 sysclk;
sysclk = clk_get("spi");
log_debug("spi clock source freq %lu", sysclk);
if (sysclk < baud) {
spi_w_reg_baud(spi_regs[id], 0);
return -EINVAL;
}
spi_w_reg_baud(spi_regs[id], sysclk / baud - 1);
return 0;
}
/*
* @brief 获取波特率
* @parm spi spi句柄
* @return 波特率
*/
u32 spi_get_baud(spi_dev spi)
{
u8 id = spi_get_info_id(spi);
return spi_get_info_clock(spi);
}
static int __spi_wait_ok(spi_dev spi, u32 n)
{
u8 id = spi_get_info_id(spi);
u32 baud = spi_get_info_clock(spi);
baud = clk_get("spi") / baud - 1;
u32 retry = baud * (clk_get("sys") / clk_get("spi")) * 8 * n * 5; //500% spi baudate
while (!spi_pnd(spi_regs[id])) {
__asm__ volatile("nop");
if (--retry == 0) {
log_error("spi wait pnd timeout");
return -EFAULT;
}
}
spi_clr_pnd(spi_regs[id]);
return 0;
}
/*
* @brief 发送1个字节
* @parm spi spi句柄
* @parm byte 发送的字节
* @return 0 成功,< 0 失败
*/
int spi_send_byte(spi_dev spi, u8 byte)
{
u8 id = spi_get_info_id(spi);
spi_dir_out(spi_regs[id]);
spi_w_reg_buf(spi_regs[id], byte);
return __spi_wait_ok(spi, 1);
}
/*
* @brief 发送1个字节不等待pnd用于中断
* @parm spi spi句柄
* @parm byte 发送的字节
* @return null
*/
void spi_send_byte_for_isr(spi_dev spi, u8 byte)
{
u8 id = spi_get_info_id(spi);
spi_dir_out(spi_regs[id]);
spi_w_reg_buf(spi_regs[id], byte);
}
/*
* @brief 接收1个字节
* @parm spi spi句柄
* @parm err 返回错误信息若err为非空指针0 成功,< 0 失败,若为空指针,忽略
* @return 接收的字节
*/
u8 spi_recv_byte(spi_dev spi, int *err)
{
u8 id = spi_get_info_id(spi);
int ret;
spi_dir_in(spi_regs[id]);
spi_w_reg_buf(spi_regs[id], 0xff);
ret = __spi_wait_ok(spi, 1);
if (ret) {
err != NULL ? *err = ret : 0;
return 0;
}
err != NULL ? *err = 0 : 0;
return spi_r_reg_buf(spi_regs[id]);
}
/*
* @brief 接收1个字节不等待pnd用于中断
* @parm spi spi句柄
* @return 接收的字节
*/
u8 spi_recv_byte_for_isr(spi_dev spi)
{
u8 id = spi_get_info_id(spi);
spi_dir_in(spi_regs[id]);
return spi_r_reg_buf(spi_regs[id]);
}
/*
* @brief 发送并接收1个字节在8个时钟内完成仅使用于SPI_MODE_BIDIR_1BIT
* @parm spi spi句柄
* @parm byte 发送的字节
* @parm err 返回错误信息若err为非空指针0 成功,< 0 失败,若为空指针,忽略
* @return 接收的字节
*/
u8 spi_send_recv_byte(spi_dev spi, u8 byte, int *err)
{
u8 id = spi_get_info_id(spi);
int ret;
spi_w_reg_buf(spi_regs[id], byte);
ret = __spi_wait_ok(spi, 1);
if (ret) {
err != NULL ? *err = ret : 0;
return 0;
}
err != NULL ? *err = 0 : 0;
return spi_r_reg_buf(spi_regs[id]);
}
/*
* @brief 设置spi[单向/双向,位数]模式
* @parm spi spi句柄
* @parm mode 模式
* @return null
*/
void spi_set_bit_mode(spi_dev spi, int mode)
{
u8 id = spi_get_info_id(spi);
u8 port = spi_get_info_port(spi);
u8 role = spi_get_info_role(spi);
switch (mode) {
case SPI_MODE_BIDIR_1BIT:
spi_bidir(spi_regs[id]);
spi_data_width(spi_regs[id], 0);
break;
case SPI_MODE_UNIDIR_1BIT:
spi_unidir(spi_regs[id]);
spi_data_width(spi_regs[id], 0);
break;
case SPI_MODE_UNIDIR_2BIT:
/* ASSERT(id == 0, "spi%d not support SPI_MODE_UNIDIR_2BIT\n", id); */
spi_unidir(spi_regs[id]);
spi_data_width(spi_regs[id], 1);
break;
case SPI_MODE_UNIDIR_4BIT:
ASSERT(id == 0, "spi%d not support SPI_MODE_UNIDIR_4BIT\n", id);
spi_unidir(spi_regs[id]);
spi_data_width(spi_regs[id], 2);
break;
}
port -= 'A';
if (id == SPI2 && port + 'A' == 'B') {
JL_USB_IO->CON0 |= BIT(11); //IO MODE
spi_io_port_init(spi_io_map[id].io[port].di_pin, 1);
JL_USB_IO->CON0 &= ~BIT(2); //DP DIR OUT
JL_USB_IO->CON0 &= ~BIT(3); //DM DIR OUT
JL_USB_IO->CON0 |= BIT(9); //DP 1.2V digital input en
JL_USB_IO->CON0 |= BIT(10); //DM 1.2V digital input en
} else {
spi_io_port_init(spi_io_map[id].io[port].clk_pin,
role == SPI_ROLE_MASTER ? 0 : 1);
spi_io_port_init(spi_io_map[id].io[port].do_pin, 0);
if (mode == SPI_MODE_BIDIR_1BIT) {
spi_io_port_init(spi_io_map[id].io[port].di_pin, 1);
} else if (mode == SPI_MODE_UNIDIR_2BIT) {
spi_io_port_init(spi_io_map[id].io[port].di_pin, 0);
} else if (mode == SPI_MODE_UNIDIR_4BIT) {
spi_io_port_init(spi_io_map[id].io[port].di_pin, 0);
spi_io_port_init(spi_io_map[id].io[port].d2_pin, 0);
spi_io_port_init(spi_io_map[id].io[port].d3_pin, 0);
}
}
}
/*
* @brief 打开spi
* @parm spi spi句柄
* @return 0 成功,< 0 失败
*/
int spi_open(spi_dev spi)
{
int err;
u8 id = spi_get_info_id(spi);
u8 port = spi_get_info_port(spi);
u8 mode = spi_get_info_mode(spi);
u8 role = spi_get_info_role(spi);
u32 clock = spi_get_info_clock(spi);
pSPI_IOMC_CONFIG[id](spi);
spi_set_bit_mode(spi, mode);
spi_cs_dis(spi_regs[id]);
if (role == SPI_ROLE_MASTER) {
spi_role_master(spi_regs[id]);
} else if (role == SPI_ROLE_SLAVE) {
spi_role_slave(spi_regs[id]);
}
spi_smp_edge_rise(spi_regs[id]);
spi_ud_edge_fall(spi_regs[id]);
spi_cs_idle_h(spi_regs[id]);
spi_clk_idle_l(spi_regs[id]);
spi_clr_pnd(spi_regs[id]);
if ((err = spi_set_baud(spi, clock))) {
log_error("invalid spi baudrate");
/* return 0; */
}
spi_w_reg_buf(spi_regs[id], 0);//设定spi初始化后DO口默认电平为低
spi_enable(spi_regs[id]);
#if 0
printf("spi%d port = '%c'\n", id, port);
printf("spi%d clk = %d\n", id, clock);
printf("spi%d mode = %d\n", id, mode);
printf("spi%d role = %d\n", id, role);
printf("spi%d di_pin = %d\n", id, spi_io_map[id].io[port - 'A'].di_pin);
printf("spi%d clk_pin = %d\n", id, spi_io_map[id].io[port - 'A'].clk_pin);
printf("spi%d do_pin = %d\n", id, spi_io_map[id].io[port - 'A'].do_pin);
printf("spi%d d2_pin = %d\n", id, spi_io_map[id].io[port - 'A'].d2_pin);
printf("spi%d d3_pin = %d\n", id, spi_io_map[id].io[port - 'A'].d3_pin);
printf("spi%d CON = %04x\n", id, spi_r_reg_con(spi_regs[id]));
printf("spi%d IOMC1 = %08x\n", id, JL_IOMAP->CON1);
#endif
return 0;
}
/*
* @brief spi dma接收
* @parm spi spi句柄
* @parm buf 接收缓冲区基地址
* @parm len 期望接收长度
* @return 实际接收长度,< 0表示失败
*/
int spi_dma_recv(spi_dev spi, void *buf, u32 len)
{
u8 id = spi_get_info_id(spi);
/* ASSERT((u32)buf % 4 == 0, "spi dma addr need 4-aligned"); */
spi_dir_in(spi_regs[id]);
spi_w_reg_dma_addr(spi_regs[id], (u32)buf);
spi_w_reg_dma_cnt(spi_regs[id], len);
asm("csync");
if (__spi_wait_ok(spi, len)) {
return -EFAULT;
}
return len;
}
/*
* @brief spi dma发送
* @parm spi spi句柄
* @parm buf 发送缓冲区基地址
* @parm len 期望发送长度
* @return 实际发送长度,< 0表示失败
*/
int spi_dma_send(spi_dev spi, const void *buf, u32 len)
{
u8 id = spi_get_info_id(spi);
/* ASSERT((u32)buf % 4 == 0, "spi dma addr need 4-aligned"); */
spi_dir_out(spi_regs[id]);
spi_w_reg_dma_addr(spi_regs[id], (u32)buf);
spi_w_reg_dma_cnt(spi_regs[id], len);
asm("csync");
if (__spi_wait_ok(spi, len)) {
return -EFAULT;
}
return len;
}
/*
* @brief spi 配置dma不等待pnd用于中断
* @parm spi spi句柄
* @parm buf 缓冲区基地址
* @parm len 期望长度
* @parm rw 1 接收 / 0 发送
* @return null
*/
void spi_dma_set_addr_for_isr(spi_dev spi, void *buf, u32 len, u8 rw)
{
u8 id = spi_get_info_id(spi);
/* ASSERT((u32)buf % 4 == 0, "spi dma addr need 4-aligned"); */
rw ? spi_dir_in(spi_regs[id]) : spi_dir_out(spi_regs[id]);
spi_w_reg_dma_addr(spi_regs[id], (u32)buf);
spi_w_reg_dma_cnt(spi_regs[id], len);
}
/*
* @brief 中断使能
* @parm spi spi句柄
* @parm en 1 使能0 失能
* @return null
*/
void spi_set_ie(spi_dev spi, u8 en)
{
u8 id = spi_get_info_id(spi);
en ? spi_ie_en(spi_regs[id]) : spi_ie_dis(spi_regs[id]);
}
/*
* @brief 判断中断标志
* @parm spi spi句柄
* @return 0 / 1
*/
u8 spi_get_pending(spi_dev spi)
{
u8 id = spi_get_info_id(spi);
return spi_pnd(spi_regs[id]) ? 1 : 0;
}
/*
* @brief 清除中断标志
* @parm spi spi句柄
* @return null
*/
void spi_clear_pending(spi_dev spi)
{
u8 id = spi_get_info_id(spi);
spi_clr_pnd(spi_regs[id]);
}
/*
* @brief 关闭spi
* @parm spi spi句柄
* @return null
*/
void spi_close(spi_dev spi)
{
u8 id = spi_get_info_id(spi);
u8 port = spi_get_info_port(spi);
u8 mode = spi_get_info_mode(spi);
port -= 'A';
if (id == SPI2 && port + 'A' == 'B') {
spi_io_port_uninit(spi_io_map[id].io[port].di_pin);
JL_USB_IO->CON0 |= BIT(2); //DP DIR IN
JL_USB_IO->CON0 |= BIT(3); //DM DIR IN
JL_USB_IO->CON0 &= ~BIT(9); //DP 1.2V digital input dis
JL_USB_IO->CON0 &= ~BIT(10); //DM 1.2V digital input dis
} else {
spi_io_port_uninit(spi_io_map[id].io[port].clk_pin);
spi_io_port_uninit(spi_io_map[id].io[port].do_pin);
if (mode == SPI_MODE_BIDIR_1BIT) {
spi_io_port_uninit(spi_io_map[id].io[port].di_pin);
} else if (mode == SPI_MODE_UNIDIR_2BIT) {
spi_io_port_uninit(spi_io_map[id].io[port].di_pin);
} else if (mode == SPI_MODE_UNIDIR_4BIT) {
spi_io_port_uninit(spi_io_map[id].io[port].di_pin);
spi_io_port_uninit(spi_io_map[id].io[port].d2_pin);
spi_io_port_uninit(spi_io_map[id].io[port].d3_pin);
}
}
spi_disable(spi_regs[id]);
}
void spi_disable_for_ota()
{
for (int i = 0; i < 2; i++) {
spi_disable(spi_regs[i]);
}
}
REGISTER_UPDATE_TARGET(spi_update_target) = {
.name = "spi",
.driver_close = spi_disable_for_ota,
};