KT24-1110_65E-HA-651B/cpu/br25/spi.c

676 lines
17 KiB
C
Raw Permalink Normal View History

2024-11-10 10:44:17 +00:00
#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 1pnd
* @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 1pnd
* @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 18使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 dmapnd
* @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,
};