Files
E3/e3_176_ref/drivers/source/spi/sdrv_spi.c
2025-10-21 19:40:27 +08:00

1942 lines
72 KiB
C

/*
* SEMIDRIVE Copyright Statement
* Copyright (c) SEMIDRIVE. All rights reserved
*
* This software and all rights therein are owned by SEMIDRIVE, and are
* protected by copyright law and other relevant laws, regulations and
* protection. Without SEMIDRIVE's prior written consent and/or related rights,
* please do not use this software or any potion thereof in any form or by any
* means. You may not reproduce, modify or distribute this software except in
* compliance with the License. Unless required by applicable law or agreed to
* in writing, software distributed under the License is distributed on
* an "AS IS" basis, WITHOUT WARRANTIES OF ANY KIND, either express or implied.
*
* You should have received a copy of the License along with this program.
* If not, see <http://www.semidrive.com/licenses/>.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <armv7-r/cache.h>
#include <clock_ip.h>
#include <debug.h>
#include <udelay/udelay.h>
#include "sdrv_spi.h"
#include "common.h"
#include "core_id.h"
#include "irq.h"
#include "irq_num.h"
#include "param.h"
#include "regs_base.h"
#include "sdrv_ckgen.h"
#include "sdrv_dma.h"
#include "sdrv_gpio.h"
#define SPI_IS_ALIGNED(tx_ptr, rx_ptr) \
(IS_ALIGNED((tx_ptr), CONFIG_ARCH_CACHE_LINE) && \
IS_ALIGNED((rx_ptr), CONFIG_ARCH_CACHE_LINE))
#ifndef ENABLE_SLAVE_MCS_TRIG
#define ENABLE_SLAVE_MCS_TRIG 0
#endif
static int spi_handler(uint32_t irq, void *bus);
static uint32_t errta_data __attribute__((__aligned__(32)));
#if (ENABLE_SLAVE_MCS_TRIG == 0)
static inline bool support_large_frame_size(void)
{
#if (CONFIG_E3 || CONFIG_D3)
return IS_P1;
#else
return true;
#endif
}
#endif
static bool spi_fdelay_is_en(struct sdrv_spi *bus)
{
if ((bus->dev_config->fream_delay == 0) ||
(bus->state & BUS_BUSY_DMA_MASK) || (!bus->com_config->is_master) ||
(bus->dev_config->is_soft_cs)) {
return false;
}
return true;
}
static void spi_repeat_cmd(struct sdrv_spi *bus, bool is_last)
{
uint32_t cmd = bus->cur_cmd;
cmd &= (uint32_t)~BM_SPI_CMD_CTRL_LAST;
if (is_last) {
cmd |= BM_SPI_CMD_CTRL_LAST;
}
writel(cmd, bus->base + SPI_TX_FIFO_CMD_OFF);
}
static inline void spi_write(struct sdrv_spi *bus, uint32_t data)
{
writel(data, bus->base + SPI_TX_FIFO_DATA_OFF);
}
static inline uint32_t spi_read(struct sdrv_spi *bus)
{
return readl(bus->base + SPI_RX_FIFO_DATA_OFF);
}
static inline bool spi_can_write(struct sdrv_spi *bus)
{
/*
* Master mode rx fifo full stop to send, but the slave does not have this
* mechanism. Slave should stop writing immediately when it finds that
* rxfifo is full in the process of writing data, and then read rx fifo to
* avoid rx fifo overflow.
*/
bool ret = false;
uint32_t reg_val = readl(bus->base + SPI_FIFO_STAT_OFF);
if (spi_fdelay_is_en(bus)) {
/* Make sure there are two free itme for writing */
ret = (SPI_FIFO_LEN - 2) > GFV_SPI_FIFO_STAT_TX_FIFO_DPTR(reg_val);
} else {
ret =
!(reg_val & (BM_SPI_FIFO_STAT_TX_FULL | BM_SPI_FIFO_STAT_RX_FULL));
}
return ret;
}
static inline bool spi_can_read(struct sdrv_spi *bus)
{
return !(readl(bus->base + SPI_FIFO_STAT_OFF) & BM_SPI_FIFO_STAT_RX_EMPTY);
}
static bool spi_tx_finished(struct sdrv_spi *bus)
{
return !!(readl(bus->base + SPI_IRQ_STAT_OFF) &
BM_SPI_IRQ_STAT_MST_FRM_END);
}
static int32_t spi_write_remain(struct sdrv_spi *bus)
{
uint32_t remain;
uint32_t len = 0;
/* spi_data_width */
uint8_t width_type;
if (!bus)
return -1;
if (!bus->transmit_sche.ptxdata.val)
return 0;
remain = bus->transmit_sche.expect_len - bus->transmit_sche.tx_cur;
if (remain <= 0)
return 0;
width_type = bus->transmit_sche.width_type;
while (remain && spi_can_write(bus)) {
if (bus->state & SPI_STATE_IS_RO_END) {
spi_write(bus, errta_data);
} else {
if (width_type == SPI_DATA_WIDTH_BYTE) {
spi_write(bus, *(bus->transmit_sche.ptxdata.u8_ptr +
bus->transmit_sche.tx_cur));
} else if (width_type == SPI_DATA_WIDTH_HALF_WORD) {
spi_write(bus, *(bus->transmit_sche.ptxdata.u16_ptr +
bus->transmit_sche.tx_cur));
} else {
spi_write(bus, *(bus->transmit_sche.ptxdata.u32_ptr +
bus->transmit_sche.tx_cur));
}
}
bus->transmit_sche.tx_cur++;
len++;
remain--;
if (spi_fdelay_is_en(bus)) {
if (remain) {
spi_repeat_cmd(bus, remain == 1 && !bus->transmit_sche.next &&
bus->transmit_sche.expect_len ==
bus->transmit_sche.len);
}
}
}
return len;
}
static void spi_slave_tx_curr_quirks(struct sdrv_spi *bus)
{
uint8_t pipe_line_remain =
GFV_SPI_FIFO_STAT_TX_FIFO_DPTR(readl(bus->base + SPI_FIFO_STAT_OFF));
if (!bus->transmit_sche.ptxdata.val)
return;
if (!bus->transmit_sche.prxdata.val)
if (bus->transmit_sche.width_type == SPI_DATA_WIDTH_BYTE) {
pipe_line_remain += SPI_PIPE_LINE_SIZE_8B;
} else {
pipe_line_remain += SPI_PIPE_LINE_SIZE_OB;
}
bus->transmit_sche.tx_cur -= pipe_line_remain;
}
static int32_t spi_read_remain(struct sdrv_spi *bus)
{
uint32_t remain;
uint32_t len = 0;
/* spi_data_width */
uint8_t width_type;
if (!bus)
return -1;
if (!bus->transmit_sche.prxdata.val)
return 0;
remain = bus->transmit_sche.expect_len - bus->transmit_sche.rx_cur;
if (remain <= 0)
return 0;
width_type = bus->transmit_sche.width_type;
while (remain && spi_can_read(bus)) {
if (width_type == SPI_DATA_WIDTH_BYTE) {
*(bus->transmit_sche.prxdata.u8_ptr + bus->transmit_sche.rx_cur) =
spi_read(bus);
} else if (width_type == SPI_DATA_WIDTH_HALF_WORD) {
*(bus->transmit_sche.prxdata.u16_ptr + bus->transmit_sche.rx_cur) =
spi_read(bus);
} else {
*(bus->transmit_sche.prxdata.u32_ptr + bus->transmit_sche.rx_cur) =
spi_read(bus);
}
bus->transmit_sche.rx_cur++;
len++;
remain--;
}
return len;
}
static inline void next_transmit(struct spi_transmit_cb *src,
struct spi_transmit_cb *dst)
{
dst->len = src->len;
dst->next = src->next;
dst->prxdata.val = src->prxdata.val;
dst->ptxdata.val = src->ptxdata.val;
dst->cur_remian = dst->len;
dst->rx_cur = 0;
dst->tx_cur = 0;
}
static void sspi_recover(struct sdrv_spi *bus);
static inline void spi_bus_state_clr(struct sdrv_spi *bus,uint8_t need_swrst)
{
const struct spi_device_config *dev_cfg = bus->dev_config;
if (bus->state & SPI_STATE_CS_ACTIVEED) {
if (dev_cfg->cs_pol == CS_ACTIVE_HIGH) {
sdrv_gpio_set_pin_output_level(dev_cfg->cs_sel, 0);
} else {
sdrv_gpio_set_pin_output_level(dev_cfg->cs_sel, 1);
}
}
bus->state &= ~(BUS_BUSY_STATUS_MASK);
bus->state &= ~SPI_SATE_EXT_FLAGS;
#if CONFIG_SPI_ENABLE_DMA
bus->dma_err = 0;
bus->tx_context.is_need_handle = 0;
bus->rx_context.is_need_handle = 0;
#endif
if(need_swrst)
sspi_recover(bus);
}
static int32_t params_check_comm(struct spi_common_config *cfg)
{
if (cfg->base == 0U) {
return -1;
}
if (cfg->clk == NULL) {
return -3;
}
#if CONFIG_SPI_ENABLE_DMA
if (cfg->dma_ins == NULL) {
return -4;
}
#endif
return 0;
}
static void spi_comm_init(struct sdrv_spi *bus)
{
uint32_t reg_val;
const struct spi_common_config *cfg = bus->com_config;
// init reg val
reg_val = readl(bus->base + SPI_CTRL_OFF);
// soft_rst
writel(BM_SPI_CTRL_SW_RST | reg_val, bus->base + SPI_CTRL_OFF);
/* wait hw clr this bit */
while (readl(bus->base + SPI_CTRL_OFF) & BM_SPI_CTRL_SW_RST)
;
if (cfg->is_master == false) {
bus->state |= SPI_STATE_IS_SLAVE;
reg_val |= BM_SPI_CTRL_SLV_MODE;
// reg_val |= BM_SPI_CTRL_SLV_UNS_SIZE_EN;
} else {
reg_val &= ~BM_SPI_CTRL_SLV_MODE;
}
reg_val &= ~FM_SPI_CTRL_NSS_POL;
reg_val &= ~BM_SPI_CTRL_SLV_UNS_SIZE_EN;
/* default is disable DMA req */
reg_val &= ~BM_SPI_CTRL_RX_DMA_EN;
reg_val &= ~BM_SPI_CTRL_TX_DMA_EN;
if (cfg->is_half_mode) {
reg_val |= BM_SPI_CTRL_HALF_MODE;
} else {
reg_val &= ~BM_SPI_CTRL_HALF_MODE;
}
if (cfg->is_spi_mode == false) {
reg_val |= BM_SPI_CTRL_MODE;
/* ssp mode cs Polarity is specified as high effective */
reg_val &= ~FM_SPI_CTRL_NSS_POL;
reg_val |= FV_SPI_CTRL_NSS_POL(0xF);
/* ti mode is not allowed to enable uns_size_mode */
reg_val &= ~BM_SPI_CTRL_SLV_UNS_SIZE_EN;
} else {
reg_val &= ~BM_SPI_CTRL_MODE;
}
/* BM_SPI_CTRL_SSP_CLK_MODE Prohibited to set !!!! */
reg_val &= ~BM_SPI_CTRL_SSP_CLK_MODE;
reg_val &= ~BM_SPI_CTRL_SAMPLE_POINT;
/* idle */
reg_val &= ~FM_SPI_CTRL_IDLE;
reg_val |= FV_SPI_CTRL_IDLE(4);
/* timeout */
reg_val &= ~FM_SPI_CTRL_TIMEOUT;
reg_val |= FV_SPI_CTRL_TIMEOUT(4);
writel(reg_val, bus->base + SPI_CTRL_OFF);
/* deafult mask all irq */
writel(0xFFFFFFFF, bus->base + SPI_IRQ_MASK_OFF);
/* default disable bus */
writel(0, bus->base + SPI_EN_OFF);
/* clear */
writel(0xFFFF, bus->base + SPI_IRQ_STAT_OFF);
}
static void spi_adapted_to_cs_pol(uint32_t base,
const struct spi_device_config *cfg)
{
uint32_t reg_val, pol;
// init reg val
reg_val = readl(base + SPI_CTRL_OFF);
pol = GFV_SPI_CTRL_NSS_POL(reg_val);
if (cfg->cs_pol == CS_ACTIVE_LOW) {
pol &= ~(0x1 << cfg->cs_sel);
} else {
pol |= (0x1 << cfg->cs_sel);
}
reg_val &= ~FM_SPI_CTRL_NSS_POL;
reg_val |= FV_SPI_CTRL_NSS_POL(pol);
writel(reg_val, base + SPI_CTRL_OFF);
}
int32_t spi_comm_deinit(struct sdrv_spi *bus)
{
if (NULL == bus)
return SDRV_STATUS_INVALID_PARAM;
writel(0xFFFFFFFF, bus->base + SPI_IRQ_MASK_OFF);
writel(~BM_SPI_EN_ENABLE, bus->base + SPI_EN_OFF);
// softrst
writel(BM_SPI_CTRL_SW_RST, bus->base + SPI_CTRL_OFF);
// wait softrst hwclr
while (BM_SPI_CTRL_SW_RST & readl(bus->base + SPI_CTRL_OFF))
;
writel(0xFFFF, bus->base + SPI_IRQ_STAT_OFF);
return SDRV_STATUS_OK;
}
static void sspi_recover(struct sdrv_spi *bus)
{
uint32_t reg_val;
/* default disable spi avoid slave mode lost bit */
writel(~BM_SPI_EN_ENABLE, bus->base + SPI_EN_OFF);
// init reg val
reg_val = readl(bus->base + SPI_CTRL_OFF);
reg_val &= ~BM_SPI_CTRL_SLV_UNS_SIZE_EN;
/* only slave mode do this sclk @133M *6cycle = 50ns softrst */
writel(reg_val | BM_SPI_CTRL_SW_RST, bus->base + SPI_CTRL_OFF);
/* wait hw clr this bit */
while (readl(bus->base + SPI_CTRL_OFF) & BM_SPI_CTRL_SW_RST)
;
writel(reg_val, bus->base + SPI_CTRL_OFF);
writel(0xFFFFFFFF, bus->base + SPI_IRQ_MASK_OFF);
writel(0xFFFF, bus->base + SPI_IRQ_STAT_OFF);
}
static inline void spi_irq_update_mask(struct sdrv_spi *bus, uint32_t set_mask,
uint32_t clr_mask)
{
uint32_t reg_val = readl(bus->base + SPI_IRQ_MASK_OFF);
reg_val &= ~(clr_mask);
reg_val |= set_mask;
/* update pre irqs */
writel(reg_val, bus->base + SPI_IRQ_MASK_OFF);
}
/**
* @brief Initialize the SPI module.
*
* @param[in] spi SPI bus
* @param[in] cfg SPI device configuration.
*
* @note SPI device configuration memory must not be recycled during transfer.
* @return 0 is ok, otherwise bus error occurred.
*/
int sdrv_spi_init(struct sdrv_spi *bus, struct spi_common_config *cfg)
{
if (bus == NULL || cfg == NULL)
return SDRV_STATUS_INVALID_PARAM;
if (0 == params_check_comm(cfg)) {
errta_data = 0xFFFFFFFF;
bus->base = cfg->base;
bus->irq = cfg->irq;
bus->com_config = cfg;
bus->dev_config = NULL;
bus->callback = NULL;
bus->state = SPI_STATE_INITED;
spi_comm_init(bus);
bus->max_baudrate =
sdrv_ckgen_get_rate((sdrv_ckgen_node_t *)&(cfg->clk->clk_node)) >>
SPI_INTERNAL_DIV_SHIFT;
bus->min_baudrate =
sdrv_ckgen_get_rate((sdrv_ckgen_node_t *)&(cfg->clk->clk_node)) >>
(SPI_CLK_PRESSCALE_MAX_SHIFT + SPI_INTERNAL_DIV_SHIFT);
if (cfg->is_polling_mode == false) {
if (irq_attach(bus->irq, spi_handler, bus)) {
ssdk_printf(SSDK_ERR, "irq_attach error\n");
return SDRV_SPI_STATUS_FAIL_IRQ_SETUP;
}
#if CONFIG_SPI_LOOPBACK_ON_SAME_CORE
if (!cfg->is_master) {
/*
* If both the master and the slave are processed on the same
* core, the interrupt priority of the slave needs to be higher
* than that of the master to ensure the data processing of the
* slave.
*/
irq_set_priority(bus->irq, irq_get_priority(bus->irq) +
SPI_SLAVE_IRQ_NICE_ADJUST);
}
#endif
irq_enable(bus->irq);
} else {
irq_disable(bus->irq);
}
} else {
return SDRV_SPI_STATUS_PARAM_ILLEGAL;
}
return SDRV_STATUS_OK;
}
/**
* @brief Deinitialize the SPI device.
*
* @param[in] bus SPI bus
*
* @return 0 is ok, otherwise bus error occurred.
*/
int sdrv_spi_deinit(struct sdrv_spi *bus)
{
if (bus == NULL) {
return SDRV_STATUS_INVALID_PARAM;
} else {
spi_comm_deinit(bus);
bus->base = 0;
bus->irq = 0;
bus->com_config = NULL;
bus->dev_config = NULL;
bus->callback = NULL;
bus->state = 0;
}
return SDRV_STATUS_OK;
}
static int32_t params_check_device(struct sdrv_spi *bus,
const struct spi_device_config *dev_cfg)
{
const struct spi_common_config *com_cfg = bus->com_config;
if (com_cfg->is_master == false && bus->dev_config)
return -1;
if (dev_cfg->sclk_freq > bus->max_baudrate ||
bus->min_baudrate > dev_cfg->sclk_freq)
return -2;
if (dev_cfg->width < SPI_DATA_WIDTH_MIN ||
dev_cfg->width > SPI_DATA_WIDTH_MAX)
return -3;
if (dev_cfg->fream_delay &&
(!bus->com_config->is_master || dev_cfg->is_soft_cs))
return -4;
return SDRV_STATUS_OK;
}
/**
* @brief SPI device configuration.
*
* This function select and activate an external device. Data transfers are
* performed according to this device configuration.
*
* @param[in] spi SPI bus
* @param[in] cfg SPI device configuration.
*
* @note SPI device configuration memory must not be recycled during
* transfer.
* @return 0 is ok, and other values mean bus is busy.
*/
int sdrv_spi_config_device(struct sdrv_spi *bus,
const struct spi_device_config *cfg)
{
irq_state_t irq_stat;
int ret = SDRV_STATUS_OK;
if (NULL == bus || NULL == cfg) {
return SDRV_STATUS_INVALID_PARAM;
}
irq_stat = arch_irq_save();
if (bus->state & BUS_BUSY_MASK) {
/* Bus busy */
ret = SDRV_STATUS_BUSY;
}
if (ret == SDRV_STATUS_OK) {
ret = params_check_device(bus, cfg);
}
if (ret == SDRV_STATUS_OK) {
bus->dev_config = cfg;
spi_adapted_to_cs_pol(bus->base, cfg);
}
arch_irq_restore(irq_stat);
return ret;
}
/**
* @brief Setup the async callback handler.
*
* @param[in] spi SPI bus
* @param[in] cb The async callback handler.
*/
void sdrv_spi_attach_async_callback(struct sdrv_spi *bus, spi_callback_t cb)
{
if (NULL != bus) {
bus->callback = cb;
}
}
static void setup_spi_irq(struct sdrv_spi *bus, enum spi_ops_type type)
{
uint32_t reg_val;
uint32_t rx_thrd = 0, tx_thrd = 0;
/* transmit manager unit */
struct spi_transmit_cb *transmit = &bus->transmit_sche;
reg_val = readl(bus->base + SPI_IRQ_STAT_OFF);
/* clr irqs */
writel(reg_val & 0xBFFF, bus->base + SPI_IRQ_STAT_OFF);
reg_val = readl(bus->base + SPI_IRQ_MASK_OFF);
if (transmit->ptxdata.val && type == OP_MODE_IRQ) {
/* if size < spi_FIFO_SIZE only frame done will into irq */
tx_thrd = SPI_FIFO_LEN / 2;
reg_val &= ~BM_SPI_IRQ_MASK_TX_FIFO_PRE_EMPTY;
}
if (transmit->prxdata.val && type == OP_MODE_IRQ) {
rx_thrd = transmit->expect_len >= SPI_FIFO_LEN
? SPI_FIFO_LEN / 2
: transmit->expect_len - 1;
/* fifo remain data this case used frame done to read */
reg_val &= ~BM_SPI_IRQ_MASK_RX_FIFO_PRE_FULL;
}
if ((transmit->prxdata.val || transmit->ptxdata.val) &&
type != OP_MODE_DMA) {
reg_val &= ~BM_SPI_IRQ_MASK_FRM_DONE;
}
if (type == OP_MODE_IRQ) {
/*
* Config thrd. DMA also depends on this reg, so this reg only can
* touch once.
*/
writel(FV_SPI_TX_FIFO_CTRL_THRD(tx_thrd),
bus->base + SPI_TX_FIFO_CTRL_OFF);
writel(FV_SPI_RX_FIFO_CTRL_THRD(rx_thrd),
bus->base + SPI_RX_FIFO_CTRL_OFF);
}
if (bus->state & SPI_STATE_IS_SLAVE) {
reg_val &= ~(BM_SPI_IRQ_MASK_RX_FIFO_OVR | BM_SPI_IRQ_MASK_TX_FIFO_UDR);
}
/*
undef size mode workaround step"
1.enable nss valid irq use to enable cs unvalid irq.
2.enable nss unvalid irq in cs selected irq handler service.
3.handling workaround in cs inactiveed irq handler service.
worksround step1:
enable cs inactive irq disable cs active irq
*/
if (bus->state & SPI_STATE_IS_SLAVE) {
/* undef size mode enables cs active interrupt to enable nss invalid
* interrupt */
if (bus->state & SPI_STATE_IS_UNS_EN) {
reg_val &= ~BM_SPI_IRQ_MASK_SLV_NSS_VLD;
}
}
ssdk_printf(SSDK_DEBUG, "%s t_thrd:%d r_thrd:%d reg:%08x\n", __FUNCTION__,
tx_thrd, rx_thrd, reg_val);
/* enable pre irqs*/
writel(reg_val, bus->base + SPI_IRQ_MASK_OFF);
}
static int32_t update_cmd(struct sdrv_spi *bus)
{
uint32_t fifo_e_timeout;
const struct spi_device_config *config = bus->dev_config;
uint32_t cmd = readl(bus->base + SPI_CMD_CTRL_OFF);
cmd &= ~FM_SPI_CMD_CTRL_FRAM_SIZE;
if (bus->transmit_sche.cur_remian > SPI_FRAME_SIZE_MAX) {
cmd |= FV_SPI_CMD_CTRL_FRAM_SIZE(SPI_FRAME_SIZE_MAX - 1);
bus->transmit_sche.expect_len += SPI_FRAME_SIZE_MAX;
} else {
/* last write */
cmd |= FV_SPI_CMD_CTRL_FRAM_SIZE(bus->transmit_sche.cur_remian - 1);
bus->transmit_sche.expect_len += bus->transmit_sche.cur_remian;
/* last default set will limit 1024 once tansmit */
if (!bus->transmit_sche.next)
cmd |= BM_SPI_CMD_CTRL_LAST;
}
/* The FIFO can write to CMD when it has a free position */
fifo_e_timeout =
BUS_FIFO_EMPTYE_TIMEOUT(config->fream_delay, config->clk2cs_delay,
config->clk2cs_end_delay, config->sclk_freq);
;
while (!spi_can_write(bus) && --fifo_e_timeout) {
udelay(1);
}
if (fifo_e_timeout == 0) {
/* exception event ,soft can't handle this situation */
sspi_recover(bus);
return SDRV_SPI_STATUS_UCMD_TIMEOUT_WRITE;
}
if (spi_fdelay_is_en(bus)) {
spi_repeat_cmd(bus, 1 == bus->transmit_sche.cur_remian);
} else {
/* updata cmd */
writel(cmd, bus->base + SPI_TX_FIFO_CMD_OFF);
bus->cur_cmd = cmd;
}
/* In the worst case, the FIFO is full, and you need to wait until all the
* data in the FIFO is sent */
fifo_e_timeout =
SPI_READ_FIFO_TIMEOUT_MAX(config->fream_delay, config->clk2cs_delay,
config->clk2cs_end_delay, config->sclk_freq);
if (!(bus->state & SPI_STATE_IS_SLAVE)) {
while (!spi_tx_finished(bus) && --fifo_e_timeout) {
udelay(1);
}
}
if (fifo_e_timeout == 0) {
/* exception event ,soft can't handle this situation */
sspi_recover(bus);
return SDRV_SPI_STATUS_UCMD_TIMEOUT_FINISH;
}
/* continue whether set or not */
writel(BM_SPI_IRQ_STAT_MST_FRM_END, bus->base + SPI_IRQ_STAT_OFF);
ssdk_printf(SSDK_DEBUG, "updata cmd %08x \n", cmd);
return 0;
}
static int32_t generate_cmd(struct sdrv_spi *bus)
{
uint32_t cmd = 0, en, fifo_e_timeout;
const struct spi_device_config *config = bus->dev_config;
uint8_t setup = config->clk2cs_delay;
uint8_t hold = config->clk2cs_end_delay;
/* The internal state machine consumes 2 cycles */
uint8_t proi = config->fream_delay > 2 ? config->fream_delay - 2 : 0;
if (config->is_lsb_mode)
cmd |= BM_SPI_CMD_CTRL_LSB;
if (!bus->transmit_sche.ptxdata.val)
cmd |= BM_SPI_CMD_CTRL_TX_MASK;
if (!bus->transmit_sche.prxdata.val)
cmd |= BM_SPI_CMD_CTRL_RX_MASK;
// mode
if (config->cpha == DATA_CPT_ON_SECOND_SCK_EDGE)
cmd |= BM_SPI_CMD_CTRL_SPI_CPHA;
if (config->cpol == SCK_IDLE_HIGH)
cmd |= BM_SPI_CMD_CTRL_SPI_CPOL;
// frame size
if (bus->transmit_sche.len > SPI_FRAME_SIZE_MAX) {
cmd |= FV_SPI_CMD_CTRL_FRAM_SIZE(SPI_FRAME_SIZE_MAX - 1);
bus->transmit_sche.expect_len = SPI_FRAME_SIZE_MAX;
} else {
cmd |= FV_SPI_CMD_CTRL_FRAM_SIZE(bus->transmit_sche.len - 1);
bus->transmit_sche.expect_len = bus->transmit_sche.len;
/* last default set will limit 1024 once tansmit */
if (!bus->transmit_sche.next)
cmd |= BM_SPI_CMD_CTRL_LAST;
}
cmd |= FV_SPI_CMD_CTRL_WORD_SIZE(config->width - 1);
if (bus->com_config->is_master)
cmd |= FV_SPI_CMD_CTRL_PRESSCALE(
(bus->max_baudrate / config->sclk_freq) - 1);
if (config->is_soft_cs) {
cmd |= FV_SPI_CMD_CTRL_NSS(0);
} else {
cmd |= FV_SPI_CMD_CTRL_NSS(config->cs_sel);
}
/*
When the sclk is disturbed, the slave may enter the REAPT state.
At this time, the new transmission CMD write cannot take effect, so the
slave state cannot return to the normal state; therefore, in order to
ensure the robustness of the slave, the reset guarantee is performed
before each transmission starts. Can recover from errors, the cost is
us-level time consumption before starting the transmission.
*/
if (bus->state & SPI_STATE_IS_SLAVE) {
sspi_recover(bus);
} else if (spi_fdelay_is_en(bus)) {
cmd &= (uint32_t)~FM_SPI_CMD_CTRL_FRAM_SIZE;
cmd &= (uint32_t)~BM_SPI_CMD_CTRL_LAST;
} else {
proi = 0;
}
/*
The workaround will be enabled when both conditions are met at the same
time:
1. chip version >1.0.
2. Transmission length greater than SPI_FRAME_SIZE_MAX.
*/
if ((bus->transmit_sche.len > SPI_FRAME_SIZE_MAX) &&
(bus->state & SPI_STATE_IS_SLAVE)) {
#if ENABLE_SLAVE_MCS_TRIG
/* Multiple CS is not supported for undefined lengths */
return SDRV_SPI_STATUS_MODE_ERROR;
#else
if (support_large_frame_size()) {
/* only for 1.1*/
uint32_t val = readl(bus->base + SPI_CTRL_OFF);
writel(val | BM_SPI_CTRL_SLV_UNS_SIZE_EN, bus->base + SPI_CTRL_OFF);
bus->state |= SPI_STATE_IS_UNS_EN;
bus->transmit_sche.expect_len = bus->transmit_sche.len;
/* slave mast only one vector item mean that next == NULL always */
if (!bus->transmit_sche.next)
cmd |= BM_SPI_CMD_CTRL_LAST;
} else {
/*
chip version 1.0 does not support the case where the length of a
single transfer exceeds SPI_FRAME_SIZE_MAX
*/
return SDRV_SPI_STATUS_GCMD_NO_SUPPORT;
}
#endif
}
/* RO Mode errata quirks */
if (bus->state & SPI_STATE_IS_SLAVE) {
setup = 0;
hold = 0;
proi = 0;
} else {
/* workaround the rx only errata */
if (!bus->transmit_sche.ptxdata.val) {
cmd &= ~BM_SPI_CMD_CTRL_TX_MASK;
bus->transmit_sche.ptxdata.val = (uint32_t)&errta_data;
bus->state |= SPI_STATE_BUSY_TX | SPI_STATE_IS_RO_END;
}
}
#if (ENABLE_SLAVE_MCS_TRIG == 0)
if (bus->state & SPI_STATE_IS_SLAVE) {
/* software undef size mode */
bus->state |= SPI_STATE_IS_UNS_EN;
}
#endif
/* The FIFO can write to CMD when it has a free position */
fifo_e_timeout =
BUS_FIFO_EMPTYE_TIMEOUT(config->fream_delay, config->clk2cs_delay,
config->clk2cs_end_delay, config->sclk_freq);
while (!spi_can_write(bus) && --fifo_e_timeout) {
udelay(1);
}
if (fifo_e_timeout == 0) {
/* exception event ,soft can't handle this situation */
sspi_recover(bus);
return SDRV_SPI_STATUS_GCMD_TIMEOUT_WRITE;
}
/* config cmd */
writel(cmd, bus->base + SPI_TX_FIFO_CMD_OFF);
bus->cur_cmd = cmd;
en = readl(bus->base + SPI_EN_OFF);
/* In the worst case, the FIFO is full, and you need to wait until all the
* data in the FIFO is sent */
fifo_e_timeout =
SPI_READ_FIFO_TIMEOUT_MAX(config->fream_delay, config->clk2cs_delay,
config->clk2cs_end_delay, config->sclk_freq);
if (en & BM_SPI_EN_ENABLE) {
if (!(bus->state & SPI_STATE_IS_SLAVE)) {
/* wait last transation finish */
while (!spi_tx_finished(bus) && --fifo_e_timeout) {
udelay(1);
}
}
}
if (fifo_e_timeout == 0) {
/* exception event ,soft can't handle this situation */
sspi_recover(bus);
return SDRV_SPI_STATUS_GCMD_TIMEOUT_FINISH;
}
/* clr master frame end */
writel(BM_SPI_IRQ_STAT_MST_FRM_END, bus->base + SPI_IRQ_STAT_OFF);
/* config Timing */
writel(SPI_TIMMING_CFG(proi, setup, hold), bus->base + SPI_TIM_CTRL_OFF);
/* enable bus */
writel(BM_SPI_EN_ENABLE, bus->base + SPI_EN_OFF);
ssdk_printf(SSDK_DEBUG, "first cmd %08x\n", cmd);
return 0;
}
#if CONFIG_SPI_ENABLE_DMA
static void spi_schduler(struct sdrv_spi *bus);
static inline void disable_dma_req(struct sdrv_spi *bus, uint32_t flags)
{
uint32_t reg_val;
reg_val = readl(bus->base + SPI_CTRL_OFF);
if (flags & SPI_RX_DIR) {
reg_val &= ~BM_SPI_CTRL_RX_DMA_EN;
}
if (flags & SPI_TX_DIR) {
reg_val &= ~BM_SPI_CTRL_TX_DMA_EN;
}
writel(reg_val, bus->base + SPI_CTRL_OFF);
ssdk_printf(SSDK_DEBUG, "spi_dma disable %08x\n", reg_val);
}
static void spi_dma_handle(uint32_t status, uint32_t ch_stat, void *context)
{
uint32_t xfer_bytes;
irq_state_t irq_stat;
struct spi_dma_context *spi_context = (struct spi_dma_context *)context;
struct sdrv_spi *bus = (struct sdrv_spi *)spi_context->bus;
uint8_t width_shift = SPI_GET_WIDTH_SHIFT(bus->transmit_sche.width_type);
/* fixed-length mode need this part handles */
irq_stat = arch_irq_save();
if (spi_context->is_need_handle) {
if (status == SDRV_DMA_COMPLETED) {
if (spi_context->dir == SPI_RX_DIR) {
disable_dma_req(bus, SPI_RX_DIR);
xfer_bytes = sdrv_dma_get_channel_xfer_bytes(&bus->rx_dma_chan);
arch_invalidate_cache_range(
(uint32_t)bus->transmit_sche.prxdata.val +
(bus->transmit_sche.rx_cur << width_shift),
ROUNDUP(xfer_bytes, CONFIG_ARCH_CACHE_LINE));
xfer_bytes = xfer_bytes >> width_shift;
if (xfer_bytes < (bus->transmit_sche.expect_len -
bus->transmit_sche.rx_cur)) {
bus->dma_err |= SPI_RX_DMA_ERR;
}
bus->transmit_sche.rx_cur += xfer_bytes;
bus->transmit_sche.cur_remian =
bus->transmit_sche.len - bus->transmit_sche.rx_cur;
sdrv_dma_stop_channel_xfer(&bus->rx_dma_chan);
} else if (spi_context->dir == SPI_TX_DIR) {
disable_dma_req(bus, SPI_TX_DIR);
xfer_bytes = sdrv_dma_get_channel_xfer_bytes(&bus->tx_dma_chan);
xfer_bytes = xfer_bytes >> width_shift;
if (xfer_bytes < (bus->transmit_sche.expect_len -
bus->transmit_sche.tx_cur)) {
bus->dma_err |= SPI_TX_DMA_ERR;
}
bus->transmit_sche.tx_cur += xfer_bytes;
if (!(bus->state & SPI_STATE_DMA_RX)) {
bus->transmit_sche.cur_remian =
bus->transmit_sche.len - bus->transmit_sche.tx_cur;
}
sdrv_dma_stop_channel_xfer(&bus->tx_dma_chan);
}
} else {
disable_dma_req(bus, SPI_TX_DIR | SPI_RX_DIR);
/* DMA_PENDING DMA_PAUSED */
if (bus->rx_context.is_need_handle) {
sdrv_dma_stop_channel_xfer(&bus->rx_dma_chan);
bus->rx_context.is_need_handle = false;
bus->dma_err |= SPI_RX_DMA_ERR;
}
if (bus->tx_context.is_need_handle) {
sdrv_dma_stop_channel_xfer(&bus->tx_dma_chan);
bus->tx_context.is_need_handle = false;
bus->dma_err |= SPI_TX_DMA_ERR;
}
}
spi_context->is_need_handle = false;
}
arch_irq_restore(irq_stat);
spi_schduler(bus);
}
static void fast_dma_config_tx(struct sdrv_spi *bus, uint32_t ptxdata, uint16_t size)
{
sdrv_dma_channel_t *channel_tx = &bus->tx_dma_chan;
sdrv_dma_set_channel_source_address(channel_tx,ptxdata);
if ((bus->state & (SPI_STATE_IS_SLAVE | SPI_STATE_IS_UNS_EN)) ==
(SPI_STATE_IS_SLAVE | SPI_STATE_IS_UNS_EN)) {
size += SPI_FIFO_LEN >> 1;
}
sdrv_dma_set_channel_buffer_size(channel_tx,SDRV_DMA_LOOP_MODE_2,
size,SPI_FIFO_LEN >> 1);
sdrv_dma_start_channel_xfer(channel_tx);
bus->tx_context.is_need_handle = true;
}
static void fast_dma_config_rx(struct sdrv_spi *bus,uint32_t prxdata, uint16_t size)
{
sdrv_dma_channel_t *channel_rx = &bus->rx_dma_chan;
sdrv_dma_set_channel_destination_address(channel_rx,prxdata);
sdrv_dma_set_channel_buffer_size(channel_rx,SDRV_DMA_LOOP_MODE_2,
size,SPI_FIFO_LEN >> 1);
sdrv_dma_start_channel_xfer(channel_rx);
bus->rx_context.is_need_handle = true;
}
static int32_t setup_spi_dma(struct sdrv_spi *bus, uint32_t ptxdata,
uint32_t prxdata, uint16_t size)
{
int ret = 0;
sdrv_dma_channel_config_t xfer_config;
sdrv_dma_t *dma = bus->com_config->dma_ins;
sdrv_dma_channel_t *chan_tx = 0, *chan_rx = 0;
sdrv_dma_bus_width_e dma_width;
uint8_t width_type = bus->transmit_sche.width_type;
uint8_t data_width_shift = SPI_GET_WIDTH_SHIFT(width_type);
if (!ptxdata && !prxdata) {
ret = -1;
}
if (width_type == SPI_DATA_WIDTH_BYTE) {
dma_width = SDRV_DMA_BUSWIDTH_1_BYTE;
} else if (width_type == SPI_DATA_WIDTH_HALF_WORD) {
dma_width = SDRV_DMA_BUSWIDTH_2_BYTES;
} else {
dma_width = SDRV_DMA_BUSWIDTH_4_BYTES;
}
size <<= data_width_shift;
if (prxdata)
prxdata = prxdata + (bus->transmit_sche.rx_cur << data_width_shift);
if (ptxdata)
ptxdata = ptxdata + (bus->transmit_sche.tx_cur << data_width_shift);
if (!ret && prxdata && (!(bus->state & SPI_STATE_DMA_RX_RD))) {
/* config fifo thrd */
writel(FV_SPI_RX_FIFO_CTRL_THRD((SPI_FIFO_LEN >> 1) - 1),
bus->base + SPI_RX_FIFO_CTRL_OFF);
sdrv_dma_init_channel_config(&xfer_config, dma);
xfer_config.channel_id = bus->com_config->rx_ch_id; /* select channel */
xfer_config.src_port_sel = SDRV_DMA_PORT_AHB32;
xfer_config.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE;
xfer_config.xfer_type =
SDRV_DMA_DIR_DEV2MEM; /* mem2mem or mem2dev or dev2mem */
xfer_config.xfer_mode =
SDRV_DMA_TRANSFER_MODE_SINGLE; /* single or continuous or linklist
*/
xfer_config.src_inc =
SDRV_DMA_ADDR_NO_INC; /* source address increase or not */
xfer_config.dst_inc =
SDRV_DMA_ADDR_INC; /* destination address increase or not */
xfer_config.loop_mode =
SDRV_DMA_LOOP_MODE_2; /* mem2mem(MODE_0) mem2dev/dev2mem(MODE_1) */
xfer_config.dst_cache =
0; /* mem2mem need to set 1, mem2dev/dev2mem set 0 */
xfer_config.interrupt_type = SDRV_DMA_LAST_MAD_DONE; /* set interrupt type */
xfer_config.src_addr =
(paddr_t)bus->base + SPI_RX_FIFO_DATA_OFF; /* set source address */
xfer_config.dst_addr = (paddr_t)prxdata; /* set destination address */
xfer_config.xfer_bytes = size; /* set transfer bytes */
xfer_config.src_width = dma_width;
xfer_config.dst_width = dma_width;
xfer_config.src_burst_len = SPI_FIFO_LEN >> 1;
xfer_config.dst_burst_len = SPI_FIFO_LEN >> 1;
chan_rx = &bus->rx_dma_chan;
if (SDRV_STATUS_OK != sdrv_dma_init_channel(chan_rx, &xfer_config)) {
ret = -2;
} else {
bus->rx_context.dir = SPI_RX_DIR;
bus->rx_context.bus = (void *)bus;
bus->rx_context.is_need_handle = true;
chan_rx->irq_callback = spi_dma_handle;
chan_rx->irq_context = (void *)&bus->rx_context;
}
}else if(bus->state & SPI_STATE_DMA_RX_RD){
chan_rx = &bus->rx_dma_chan;
}
if (!ret && ptxdata && (!(bus->state & SPI_STATE_DMA_TX_RD))) {
/* config fifo thrd */
writel(FV_SPI_TX_FIFO_CTRL_THRD(SPI_FIFO_LEN >> 1),
bus->base + SPI_TX_FIFO_CTRL_OFF);
sdrv_dma_init_channel_config(&xfer_config, dma);
xfer_config.channel_id = bus->com_config->tx_ch_id; /* select channel */
xfer_config.dst_port_sel = SDRV_DMA_PORT_AHB32;
xfer_config.trig_mode = SDRV_DMA_TRIGGER_BY_HARDWARE;
xfer_config.xfer_type =
SDRV_DMA_DIR_MEM2DEV; /* mem2mem or mem2dev or dev2mem */
xfer_config.xfer_mode =
SDRV_DMA_TRANSFER_MODE_SINGLE; /* single or continuous or linklist
*/
if (bus->state & SPI_STATE_IS_RO_END) {
xfer_config.src_inc =
SDRV_DMA_ADDR_NO_INC; /* source address increase or not */
} else {
xfer_config.src_inc =
SDRV_DMA_ADDR_INC; /* source address increase or not */
}
xfer_config.dst_inc =
SDRV_DMA_ADDR_NO_INC; /* destination address increase or not */
xfer_config.loop_mode =
SDRV_DMA_LOOP_MODE_2; /* mem2mem(MODE_0) mem2dev/dev2mem(MODE_1) */
xfer_config.dst_cache =
0; /* mem2mem need to set 1, mem2dev/dev2mem set 0 */
xfer_config.interrupt_type = SDRV_DMA_LAST_MAD_DONE; /* set interrupt type */
xfer_config.src_addr = (paddr_t)ptxdata; /* set source address */
xfer_config.dst_addr =
(paddr_t)bus->base +
SPI_TX_FIFO_DATA_OFF; /* set destination address */
xfer_config.xfer_bytes = size; /* set transfer bytes */
xfer_config.src_width = dma_width;
xfer_config.dst_width = dma_width;
xfer_config.src_burst_len = SPI_FIFO_LEN >> 1;
xfer_config.dst_burst_len = SPI_FIFO_LEN >> 1;
chan_tx = &bus->tx_dma_chan;
/*
Undef size mode need to give more data to tx fifo to avoid udr
of ip bug. always give tx FIFO more 8bytes data, even in the case of
32bit data bit width, it can ensure that there are two redundant data
in txfifo to avoid tx fifo underflow.
*/
if ((bus->state & (SPI_STATE_IS_SLAVE | SPI_STATE_IS_UNS_EN)) ==
(SPI_STATE_IS_SLAVE | SPI_STATE_IS_UNS_EN)) {
xfer_config.xfer_bytes += SPI_FIFO_LEN >> 1;
}
if (SDRV_STATUS_OK != sdrv_dma_init_channel(chan_tx, &xfer_config)) {
ret = -3;
} else {
bus->tx_context.dir = SPI_TX_DIR;
bus->tx_context.bus = (void *)bus;
bus->tx_context.is_need_handle = true;
chan_tx->irq_callback = spi_dma_handle;
chan_tx->irq_context = (void *)&bus->tx_context;
}
}else if(bus->state & SPI_STATE_DMA_TX_RD){
chan_tx = &bus->tx_dma_chan;
}
if (!ret) {
if (prxdata && chan_rx && chan_rx->channel) {
arch_clean_invalidate_cache_range((uint32_t)prxdata, ROUNDUP(size, 32));
sdrv_dma_clear_channel_xfer_bytes(chan_rx);
if(bus->state & SPI_STATE_DMA_RX_RD){
fast_dma_config_rx(bus,prxdata,size);
}else{
sdrv_dma_start_channel_xfer(chan_rx);
bus->state |= SPI_STATE_DMA_RX_RD;
}
}
if (ptxdata && chan_tx && chan_tx->channel) {
arch_clean_cache_range((uint32_t)ptxdata, ROUNDUP(size, 32));
sdrv_dma_clear_channel_xfer_bytes(chan_tx);
if(bus->state & SPI_STATE_DMA_TX_RD){
fast_dma_config_tx(bus,ptxdata,size);
}else{
sdrv_dma_start_channel_xfer(chan_tx);
bus->state |= SPI_STATE_DMA_TX_RD;
}
}
bus->dma_err = 0;
} else {
if (ptxdata && chan_tx && chan_tx->channel)
sdrv_dma_stop_channel_xfer(chan_tx);
if (prxdata && chan_rx && chan_rx->channel)
sdrv_dma_stop_channel_xfer(chan_rx);
bus->rx_context.is_need_handle = false;
bus->tx_context.is_need_handle = false;
}
return ret;
}
static inline void enable_dma_req(struct sdrv_spi *bus)
{
uint32_t reg_val = readl(bus->base + SPI_CTRL_OFF);
if (bus->transmit_sche.prxdata.val) {
reg_val |= BM_SPI_CTRL_RX_DMA_EN;
} else {
reg_val &= ~BM_SPI_CTRL_RX_DMA_EN;
}
if (bus->transmit_sche.ptxdata.val) {
reg_val |= BM_SPI_CTRL_TX_DMA_EN;
} else {
reg_val &= ~BM_SPI_CTRL_TX_DMA_EN;
}
writel(reg_val, bus->base + SPI_CTRL_OFF);
}
#endif
static int transmission_check(struct sdrv_spi *bus, void *tx_buf, void *rx_buf,
uint32_t bytes, bool dma)
{
int ret = 0;
/* default Config or slave mode cfg */
const struct spi_device_config *dev_config;
if (!bus) {
ret = -1;
} else {
dev_config = bus->dev_config;
/* if bus*/
if (!bus->com_config) {
ret = -2;
} else if (!dev_config) {
ret = -3;
} else if (!(bus->state & SPI_STATE_INITED)) {
ret = -4;
} else if (!tx_buf && !rx_buf) {
ret = -5;
} else if (!SPI_IS_VALID_LEN(bytes, dev_config->width)) {
ret = -6;
} else if (dma && !SPI_IS_ALIGNED(tx_buf, rx_buf)) {
ret = -7;
}
}
return ret;
}
static int32_t spi_vector_transmission_ops(struct sdrv_spi *bus,
enum spi_ops_type type,
uint32_t timeout)
{
uint8_t fsm;
uint16_t cnt = 0;
int32_t ret = 0;
irq_state_t irq_stat;
uint32_t timeout_cnt, delay = 0, read_timeout,read_timeout_cnt;
/* default Config or slave mode cfg */
const struct spi_device_config *const dev_config = bus->dev_config;
/* transmit manager unit */
struct spi_transmit_cb *transmit = &bus->transmit_sche;
/* In the worst case, the FIFO is full, and you need to wait until all the
* data in the FIFO is sent */
read_timeout = SPI_READ_FIFO_TIMEOUT_MAX(
dev_config->fream_delay, dev_config->clk2cs_delay,
dev_config->clk2cs_end_delay, dev_config->sclk_freq);
if (!transmit->expect_len) {
/* config cmd */
ret = generate_cmd(bus);
if (ret)
return ret;
/* soft cs */
if (!(bus->state & SPI_STATE_IS_SLAVE)) {
if (!(bus->state & SPI_STATE_CS_ACTIVEED)) {
if (dev_config->is_soft_cs) {
timeout_cnt = 1000;
/* Software CS needs to wait for the SPI sync pad status to
* be ready */
fsm = GFV_SPI_IRQ_STAT_SPI_FSM_ST(
readl(bus->base + SPI_IRQ_STAT_OFF));
while ((fsm != SPI_FSM_WAIT_DATA_STA) &&
(fsm != SPI_FSM_M_STR_STA) &&
(fsm != SPI_FSM_IDLE_STA) && --timeout_cnt) {
fsm = GFV_SPI_IRQ_STAT_SPI_FSM_ST(
readl(bus->base + SPI_IRQ_STAT_OFF));
}
if (timeout_cnt == 0) {
return SDRV_SPI_STATUS_SOFT_CS_TIMEOUT;
}
/* Soft Cs mast active cs after spi mode ready */
if (dev_config->cs_pol == CS_ACTIVE_HIGH) {
sdrv_gpio_set_pin_output_level(dev_config->cs_sel, 1);
} else {
sdrv_gpio_set_pin_output_level(dev_config->cs_sel, 0);
}
bus->state |= SPI_STATE_CS_ACTIVEED;
}
}
/* sample point delay half sclk cycle */
uint32_t reg_val = readl(bus->base + SPI_CTRL_OFF);
if (dev_config->is_tx_delay) {
reg_val |= BM_SPI_CTRL_SAMPLE_POINT;
} else {
reg_val &= ~BM_SPI_CTRL_SAMPLE_POINT;
}
writel(reg_val, bus->base + SPI_CTRL_OFF);
}
} else {
ret = update_cmd(bus);
if (ret)
return ret;
}
if (bus->state & SPI_STATE_IS_RO_END) {
if (type == OP_MODE_IRQ) {
bus->state |= SPI_STATE_IRQ_TX;
}
#if CONFIG_SPI_ENABLE_DMA
else {
bus->state |= SPI_STATE_DMA_TX;
}
#endif
}
if (type == OP_MODE_SYNC) {
/*
(1/baudrate) * width = write once delay
16 ===> 8 makes the bus more efficient in case of CPU overload.
*/
delay = ((dev_config->width + dev_config->fream_delay +
dev_config->clk2cs_delay + dev_config->clk2cs_end_delay) *
8000000 / dev_config->sclk_freq);
while (1) {
timeout_cnt = timeout;
read_timeout_cnt = read_timeout;
/* device mode aways block */
ssdk_printf(SSDK_DEBUG, "Normal mode \n");
while ((transmit->ptxdata.val &&
transmit->tx_cur < transmit->expect_len) ||
(transmit->prxdata.val &&
transmit->rx_cur < transmit->expect_len)) {
spi_write_remain(bus);
spi_read_remain(bus);
/* Improve the efficiency of synchronous transmission of small
data volumes When Tx is finished, do not need delay to wait
for the fifo to be vacant that to avoid the delay of the new
cmd (100us+)
*/
if (transmit->ptxdata.val &&
transmit->tx_cur < transmit->expect_len) {
if (!spi_can_write(bus))
udelay(delay);
} else if (transmit->prxdata.val) {
while (transmit->rx_cur < transmit->expect_len &&
read_timeout_cnt) {
/* Read RxFIFO data as fast as possible */
if (0 == spi_read_remain(bus)) {
read_timeout_cnt--;
udelay(1);
}
}
}
if (timeout_cnt) {
timeout_cnt--;
}
if (timeout_cnt == 0 || read_timeout_cnt == 0) {
ssdk_printf(SSDK_DEBUG, "timeout\n");
return SDRV_SPI_STATUS_SYNC_TIMEOUT;
}
}
if (transmit->expect_len < transmit->len) {
/* update remain and except */
if (transmit->prxdata.val) {
transmit->cur_remian = transmit->len - transmit->rx_cur;
} else {
transmit->cur_remian = transmit->len - transmit->tx_cur;
}
ret = update_cmd(bus);
if (ret)
return ret;
} else {
/* next vector */
if (!transmit->next)
break;
next_transmit(transmit->next, &bus->transmit_sche);
ret = generate_cmd(bus);
if (ret)
return ret;
}
}
/* In the worst case, the FIFO is full, and you need to wait until all
* the data in the FIFO is sent */
uint32_t fifo_e_timeout = SPI_READ_FIFO_TIMEOUT_MAX(
dev_config->fream_delay, dev_config->clk2cs_delay,
dev_config->clk2cs_end_delay, dev_config->sclk_freq);
if ((!(bus->state & SPI_STATE_IS_SLAVE)) && dev_config->is_soft_cs) {
while (!spi_tx_finished(bus) && --fifo_e_timeout) {
udelay(1);
}
}
if (fifo_e_timeout == 0) {
/*
Did not wait until the sending was completed , exception
event soft can't handle this situation.
*/
sspi_recover(bus);
return SDRV_SPI_STATUS_SOFT_CS_WAIT_TIMEOUT;
}
spi_bus_state_clr(bus,false);
ssdk_printf(SSDK_DEBUG, "write %d", bus->transmit_sche.tx_cur);
ssdk_printf(SSDK_DEBUG, "read %d\n", bus->transmit_sche.rx_cur);
} else {
ssdk_printf(SSDK_DEBUG, "Irq mode\n");
/* slave need tx fifo not empty to avoid too early udr */
if (type != OP_MODE_DMA){
spi_write_remain(bus);
setup_spi_irq(bus, type);
}else{
#if CONFIG_SPI_ENABLE_DMA
/* dma mode */
ssdk_printf(SSDK_DEBUG, "DMA mode \n");
if (bus->transmit_sche.prxdata.val) {
cnt = bus->transmit_sche.expect_len - bus->transmit_sche.rx_cur;
} else {
cnt = bus->transmit_sche.expect_len - bus->transmit_sche.tx_cur;
}
ret = setup_spi_dma(bus, bus->transmit_sche.ptxdata.val,
bus->transmit_sche.prxdata.val, cnt);
if (!ret) {
/* Slave mode if a dma completion interrupt occurs before the spi interrupt is configured,
the dma interrupt is processed and returned to continue enabling the spi interrupt,
it will result in a situation where no transmission is made but the interrupt is enabled */
irq_stat = arch_irq_save();
enable_dma_req(bus);
/* for slave */
setup_spi_irq(bus, type);
arch_irq_restore(irq_stat);
} else {
/*
setup dma fail,need mask all irq avoid sync Mode enable
irqs.
*/
return SDRV_SPI_STATUS_FAIL_DMA_SETUP;
}
#else
return SDRV_SPI_STATUS_MODE_ERROR;
#endif
}
}
return SDRV_STATUS_OK;
}
static void hw_spi_clr_irq_state(struct sdrv_spi *bus, uint32_t clr)
{
writel(clr, bus->base + SPI_IRQ_STAT_OFF);
}
static int32_t hw_spi_irq_state(struct sdrv_spi *bus, uint32_t *reg_vel)
{
int32_t ret = 0;
if (!bus)
return -1;
uint8_t max_read_cnt = SPI_FIFO_LEN;
#if CONFIG_SPI_ENABLE_DMA
irq_state_t irq_stat;
uint32_t xfer = 0;
uint8_t data_width_shift =
SPI_GET_WIDTH_SHIFT(bus->transmit_sche.width_type);
#endif
uint32_t irq_s = readl(bus->base + SPI_IRQ_STAT_OFF);
/* Mst fream end clr only on write cmd */
*reg_vel = irq_s;
*reg_vel &= ~BM_SPI_IRQ_STAT_MST_FRM_END;
ssdk_printf(SSDK_DEBUG, "%p sta:%08x \n", bus, irq_s);
if (bus->state & SPI_STATE_IS_SLAVE) {
ret = SPI_TX_FIFO_WRITE | SPI_RX_FIFO_READ;
} else {
if (irq_s &
(BM_SPI_IRQ_STAT_RX_FIFO_PRE_FULL | BM_SPI_IRQ_STAT_FRM_DONE)) {
ret |= SPI_RX_FIFO_READ;
ssdk_printf(SSDK_DEBUG, "rx_pre_full\n");
}
if (irq_s & BM_SPI_IRQ_STAT_TX_FIFO_PRE_EMPTY) {
ret |= SPI_TX_FIFO_WRITE;
ssdk_printf(SSDK_DEBUG, "tx_pre_empty\n");
}
}
if (irq_s & BM_SPI_IRQ_STAT_TX_FIFO_UDR) {
/* workaround spi slave mode will have a udr interrupt after the
* transfer is over */
#if CONFIG_SPI_ENABLE_DMA
irq_stat = arch_irq_save();
if (bus->state & SPI_STATE_DMA_TX) {
xfer = sdrv_dma_get_channel_xfer_bytes(&bus->tx_dma_chan);
bus->transmit_sche.tx_cur = xfer >> data_width_shift;
}
arch_irq_restore(irq_stat);
#endif
if (bus->transmit_sche.tx_cur < bus->transmit_sche.expect_len) {
ret |= SPI_TX_FIFO_UDR;
ssdk_printf(SSDK_DEBUG, "Bus:%p UDF\n", bus);
}
}
if (irq_s & BM_SPI_IRQ_STAT_RX_FIFO_OVR) {
ret |= SPI_RX_FIFO_OVR;
ssdk_printf(SSDK_DEBUG, "Bus:%p OVR\n", bus);
}
uint32_t reg_val = readl(bus->base + SPI_IRQ_MASK_OFF);
if ((bus->state & (SPI_STATE_IS_UNS_EN | SPI_STATE_IS_SLAVE)) ==
(SPI_STATE_IS_UNS_EN | SPI_STATE_IS_SLAVE)) {
if ((irq_s & BM_SPI_IRQ_STAT_SLV_NSS_VLD) &&
(~reg_val & BM_SPI_IRQ_MASK_SLV_NSS_VLD)) {
/*
worksround step2:
enable cs inactive irq disable cs active irq
*/
spi_irq_update_mask(bus, BM_SPI_IRQ_MASK_SLV_NSS_VLD,
BM_SPI_IRQ_MASK_SLV_NSS_INVLD);
}
if (irq_s & BM_SPI_IRQ_STAT_SLV_NSS_INVLD &&
(~reg_val & BM_SPI_IRQ_MASK_SLV_NSS_INVLD)) {
/*
worksround step3:
disable cs inactive irq.
handle spi reset and dma resource release related processing
*/
spi_irq_update_mask(bus, BM_SPI_IRQ_MASK_SLV_NSS_INVLD, 0);
#if CONFIG_SPI_ENABLE_DMA
irq_stat = arch_irq_save();
if (bus->rx_context.is_need_handle) {
disable_dma_req(bus, SPI_RX_DIR);
sdrv_dma_stop_channel_xfer(&bus->rx_dma_chan);
xfer = sdrv_dma_get_channel_xfer_bytes(&bus->rx_dma_chan);
arch_invalidate_cache_range(
(uint32_t)bus->transmit_sche.prxdata.val +
(bus->transmit_sche.rx_cur << data_width_shift),
ROUNDUP(xfer, CONFIG_ARCH_CACHE_LINE));
xfer >>= data_width_shift;
bus->transmit_sche.rx_cur = xfer;
bus->rx_context.is_need_handle = false;
sdrv_dma_clear_channel_xfer_bytes(&bus->rx_dma_chan);
}
if (bus->tx_context.is_need_handle) {
disable_dma_req(bus, SPI_TX_DIR);
sdrv_dma_stop_channel_xfer(&bus->tx_dma_chan);
xfer = sdrv_dma_get_channel_xfer_bytes(&bus->tx_dma_chan);
xfer >>= data_width_shift;
bus->transmit_sche.tx_cur = xfer;
bus->tx_context.is_need_handle = false;
sdrv_dma_clear_channel_xfer_bytes(&bus->tx_dma_chan);
}
arch_irq_restore(irq_stat);
#endif
while (spi_can_read(bus) && max_read_cnt) {
spi_read_remain(bus);
max_read_cnt--;
}
spi_slave_tx_curr_quirks(bus);
/* updata expect_len and len make the transfer end */
if (bus->state & SPI_STATE_BUSY_RX) {
bus->transmit_sche.cur_remian = 0;
bus->transmit_sche.expect_len = bus->transmit_sche.rx_cur;
bus->transmit_sche.len = bus->transmit_sche.expect_len;
}
if ((bus->state & (SPI_STATE_BUSY_RX | SPI_STATE_BUSY_TX)) ==
SPI_STATE_BUSY_TX) {
bus->transmit_sche.cur_remian = 0;
bus->transmit_sche.expect_len = bus->transmit_sche.tx_cur;
}
bus->transmit_sche.len = bus->transmit_sche.expect_len;
bus->state &= ~SPI_STATE_IS_UNS_EN;
/* slave mode undef size cs inactive mean that the transmition is
* over --- for irq */
ret |= SPI_RX_FIFO_READ | SPI_TX_FIFO_WRITE; /* */
}
}
#if CONFIG_SPI_ENABLE_DMA
/* dma error */
ret |= bus->dma_err;
#endif
if(irq_s & BM_SPI_IRQ_STAT_MST_FRM_END)
ret |= SPI_TX_ONLY_DONE;
ssdk_printf(SSDK_DEBUG, "%s ret:%08x\n", __FUNCTION__, ret);
return ret;
}
static void hw_spi_setup_irq_mask(struct sdrv_spi *bus, uint32_t irq_mask)
{
uint32_t reg_val = readl(bus->base + SPI_IRQ_MASK_OFF);
if (irq_mask & SPI_RX_READ_REQ)
reg_val |= BM_SPI_IRQ_MASK_RX_FIFO_PRE_FULL;
if (irq_mask & SPI_TX_WRITE_REQ)
reg_val |= BM_SPI_IRQ_MASK_TX_FIFO_PRE_EMPTY;
/*
In indefinite length mode, the cs invalid interrupt enable
can only be turned off after the cs invalid interrupt occurs so
0xFFFF ---> 0xDFFF.
*/
if (irq_mask & SPI_TRASPORT_FINISH)
reg_val |= ~BM_SPI_IRQ_MASK_SLV_NSS_INVLD;
if (irq_mask & SPI_CS_INVLD_REQ)
reg_val |= BM_SPI_IRQ_MASK_SLV_NSS_INVLD;
ssdk_printf(SSDK_DEBUG, "%s reg:%08x\n", __FUNCTION__, reg_val);
/* disable pre irqs*/
writel(reg_val, bus->base + SPI_IRQ_MASK_OFF);
}
static void spi_bus_abort_processing(struct sdrv_spi *bus)
{
#if CONFIG_SPI_ENABLE_DMA
irq_state_t irq_stat = arch_irq_save();
if (bus->rx_context.is_need_handle) {
disable_dma_req(bus, SPI_RX_DIR);
sdrv_dma_stop_channel_xfer(&bus->rx_dma_chan);
bus->rx_context.is_need_handle = false;
}
if (bus->tx_context.is_need_handle) {
disable_dma_req(bus, SPI_TX_DIR);
sdrv_dma_stop_channel_xfer(&bus->tx_dma_chan);
bus->tx_context.is_need_handle = false;
}
arch_irq_restore(irq_stat);
#endif
spi_bus_state_clr(bus,true);
if (bus->callback)
bus->callback(bus, SPI_TRANS_FAIL);
}
static void spi_schduler(struct sdrv_spi *bus)
{
int32_t ret = -1;
int32_t ops_ret;
uint32_t irq_state;
struct spi_transmit_cb *inherit;
const struct spi_device_config *dev_config = bus->dev_config;
if (!(bus->state & SPI_STATE_INITED)) {
ssdk_printf(SSDK_ERR, "%s error not inited\n", __FUNCTION__);
return;
}
ret = hw_spi_irq_state(bus, &irq_state);
/* Slave Mode OverFlow or UnderFlow error */
if (ret & BUS_FIFO_STATE_ERR_MASK) {
spi_bus_abort_processing(bus);
return;
} else {
if (bus->state & BUS_BUSY_DMA_MASK) {
ret = SPI_TX_FIFO_WRITE | SPI_RX_FIFO_READ;
}
if (bus->state & SPI_STATE_BUSY_TX && (ret & SPI_TX_FIFO_WRITE)) {
if ((bus->transmit_sche.tx_cur < bus->transmit_sche.expect_len) &&
!(bus->state & SPI_STATE_DMA_TX)) {
/* feed data to hw ,dma mode soft do not need do this */
spi_write_remain(bus);
}
if (bus->transmit_sche.tx_cur >= bus->transmit_sche.expect_len) {
/* tx except satisfy ,If you need to receive,
just turn off the send trigger, otherwise all turn off*/
if (bus->state & SPI_STATE_BUSY_RX)
hw_spi_setup_irq_mask(bus, SPI_TX_WRITE_REQ);
else
hw_spi_setup_irq_mask(bus, SPI_TX_WRITE_REQ |
SPI_TRASPORT_FINISH);
if (bus->transmit_sche.expect_len == bus->transmit_sche.len) {
/* vector item end check next need to transmit ? */
if (!bus->transmit_sche.next) {
/*
vector end and not have others need to process
,Tx Finished
*/
if ((bus->state &
(SPI_STATE_CS_ACTIVEED | SPI_STATE_BUSY_RX)) ==
SPI_STATE_CS_ACTIVEED) {
/* SPI_STATE_CS_ACTIVEED only for master do not need
* check spi mode*/
if(ret & SPI_TX_ONLY_DONE){
if (dev_config->cs_pol != CS_ACTIVE_HIGH) {
sdrv_gpio_set_pin_output_level(
dev_config->cs_sel, 1);
} else {
sdrv_gpio_set_pin_output_level(
dev_config->cs_sel, 0);
}
bus->state &= ~SPI_STATE_CS_ACTIVEED;
}else{
/* enable master fream done irq to deassert soft cs */
spi_irq_update_mask(bus, 0,BM_SPI_IRQ_MASK_MST_FRM_END);
return ;
}
}
if(0u == (bus->state & SPI_STATE_IS_UNS_EN)){
bus->state &= ~BUS_STATE_TX_MASK;
/* rx is on going or undefine size not finish ,do not finished the transmitssion */
if (bus->callback &&
(!(bus->state & SPI_STATE_BUSY_RX))) {
bus->callback(bus, SPI_TRANS_DONE);
}
}
ssdk_printf(SSDK_DEBUG, "%s bus%p tx pass\n",
__FUNCTION__, bus);
} else {
/* vector next */
if (bus->state & SPI_STATE_BUSY_RX) {
/* do not start a new transmit, Should be started
when the reception is complete */
} else {
/* inherit from last item dev */
inherit = bus->transmit_sche.next;
inherit->expect_len = 0;
if (bus->state & BUS_BUSY_DMA_MASK) {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_DMA, 0);
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n",
ops_ret);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
} else {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_IRQ, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n",
ops_ret);
}
}
}
} else {
/* vector item continue */
if (bus->state & SPI_STATE_BUSY_RX) {
/* do not start a new transmit, Should be started
when the reception is complete */
} else {
bus->transmit_sche.cur_remian =
bus->transmit_sche.len - bus->transmit_sche.tx_cur;
if (bus->state & BUS_BUSY_DMA_MASK) {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_DMA, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
} else {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_IRQ, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
}
}
}
}
}
if (bus->state & SPI_STATE_BUSY_RX && (ret & SPI_RX_FIFO_READ)) {
/* check next need to transmit ? */
if ((bus->transmit_sche.rx_cur < bus->transmit_sche.expect_len) &&
!(bus->state & SPI_STATE_DMA_RX)) {
/* read data to hw ,dma mode do not need to do anything */
spi_read_remain(bus);
}
/* !!! Cannot be implemented using if-else must independent to check
*/
if (bus->transmit_sche.rx_cur == bus->transmit_sche.expect_len) {
/* rx except satisfy , all turn off*/
hw_spi_setup_irq_mask(bus, SPI_TX_WRITE_REQ | SPI_RX_READ_REQ |
SPI_TRASPORT_FINISH);
if (bus->transmit_sche.expect_len == bus->transmit_sche.len) {
/* vector item end check next need to transmit ? */
if (!bus->transmit_sche.next) {
/*
vector end and not have others need to process
,Rx Finished
*/
if (bus->state & SPI_STATE_CS_ACTIVEED) {
if (dev_config->cs_pol != CS_ACTIVE_HIGH) {
sdrv_gpio_set_pin_output_level(
dev_config->cs_sel, 1);
} else {
sdrv_gpio_set_pin_output_level(
dev_config->cs_sel, 0);
}
}
bus->state &= ~SPI_STATE_CS_ACTIVEED;
bus->state &= ~SPI_STATE_IS_RO_END;
if(0u == (bus->state & SPI_STATE_IS_UNS_EN)){
bus->state &= ~BUS_STATE_RX_MASK;
if (bus->callback)
bus->callback(bus, SPI_TRANS_DONE);
}
ssdk_printf(SSDK_DEBUG, "%s bus%p rx pass\n",
__FUNCTION__, bus);
} else {
inherit = bus->transmit_sche.next;
inherit->expect_len = 0;
/* vector next */
if (bus->state & BUS_BUSY_DMA_MASK) {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_DMA, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
} else {
ops_ret = spi_vector_transmission_ops(
bus, OP_MODE_IRQ, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
}
}
} else {
bus->transmit_sche.cur_remian =
bus->transmit_sche.len - bus->transmit_sche.rx_cur;
/* vector item continue */
if (bus->state & BUS_BUSY_DMA_MASK) {
ops_ret =
spi_vector_transmission_ops(bus, OP_MODE_DMA, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
} else {
ops_ret =
spi_vector_transmission_ops(bus, OP_MODE_IRQ, 0);
if (ops_ret != SDRV_STATUS_OK) {
spi_bus_abort_processing(bus);
return;
}
ssdk_printf(SSDK_DEBUG, "ops_ret %d\n", ops_ret);
}
}
}
}
ssdk_printf(SSDK_DEBUG, "irq_sta reg%08x\n", irq_state);
hw_spi_clr_irq_state(bus, irq_state);
}
}
static int spi_handler(uint32_t irq, void *bus)
{
(void)irq;
if (((struct sdrv_spi *)bus)->com_config->is_polling_mode == false) {
spi_schduler((struct sdrv_spi *)bus);
}
return 0;
}
void sdrv_spi_polling(struct sdrv_spi *bus)
{
if (bus && bus->com_config->is_polling_mode == true) {
spi_schduler(bus);
}
}
static inline void setup_transmit_sche(struct sdrv_spi *bus, void *ptxdata,
void *prxdata, uint32_t size)
{
bus->transmit_sche.prxdata.val = (uint32_t)prxdata;
bus->transmit_sche.ptxdata.val = (uint32_t)ptxdata;
bus->transmit_sche.rx_cur = 0;
bus->transmit_sche.tx_cur = 0;
bus->transmit_sche.next = NULL;
bus->transmit_sche.width_type = SPI_GET_WIDTH_TYPE(bus->dev_config->width);
size >>= SPI_GET_WIDTH_SHIFT(bus->transmit_sche.width_type);
bus->transmit_sche.cur_remian = size;
bus->transmit_sche.len = size;
bus->transmit_sche.expect_len = 0;
}
/**
* @brief SPI sync transmit.
*
* @param[in] spi SPI bus.
* @param[in] tx_buf Pointer to data to transmit, which must be aligned with the
* corresponding data width, otherwise unaligned access will occur and cause an
* exception.
* @param[out] rx_buf Pointer to receive data storage, which must be aligned
* with the corresponding data width, otherwise unaligned access will occur and
* cause an exception.
* @param[in] bytes The amount of data in bytes.
* @param[in] timeout Timeout time >= total transmitted bytes / data-width*2.
* @return 0 is ok, and other values for error.
*/
int sdrv_spi_sync_transmit(struct sdrv_spi *bus, void *tx_buf, void *rx_buf,
uint32_t bytes, uint32_t timeout)
{
int ret = SDRV_STATUS_OK;
irq_state_t irq_stat;
ret = transmission_check(bus, tx_buf, rx_buf, bytes, false);
if (SDRV_STATUS_OK == ret) {
irq_stat = arch_irq_save();
if (bus->state & BUS_BUSY_MASK) {
/* Bus busy */
ret = SDRV_STATUS_BUSY;
arch_irq_restore(irq_stat);
} else {
setup_transmit_sche(bus, tx_buf, rx_buf, bytes);
if (bus->transmit_sche.ptxdata.val)
bus->state |= SPI_STATE_BUSY_TX;
if (bus->transmit_sche.prxdata.val)
bus->state |= SPI_STATE_BUSY_RX;
arch_irq_restore(irq_stat);
ret = spi_vector_transmission_ops(bus, OP_MODE_SYNC, timeout);
if (ret != SDRV_STATUS_OK) {
spi_bus_state_clr(bus,true);
ssdk_printf(SSDK_ERR, "%s error: %d\n", __FUNCTION__, ret);
}
}
} else {
ret = SDRV_SPI_STATUS_PARAM_ILLEGAL;
}
return ret;
}
/**
* @brief SPI async transmit in IRQ or DMA mode.
*
* @param[in] spi SPI bus
* @param[in] tx_buf Pointer to data to transmit, which must be aligned with the
* corresponding data width, otherwise unaligned access will occur and cause an
* exception.
* @param[out] rx_buf Pointer to receive data storage, which must be aligned
* with the corresponding data width, otherwise unaligned access will occur and
* cause an exception.
* @param[in] bytes The amount of data in bytes.
* @param[in] type OP_MODE_IRQ for IRQ Mode. OP_MODE_DMA for DMA Mode.
* @return 0 is ok, and other values for error.
*/
int sdrv_spi_async_transmit(struct sdrv_spi *bus, void *tx_buf, void *rx_buf,
uint32_t bytes, enum spi_ops_type type)
{
int32_t ret = SDRV_STATUS_OK;
irq_state_t irq_stat;
ret = transmission_check(bus, tx_buf, rx_buf, bytes, type == OP_MODE_DMA);
if (SDRV_STATUS_OK == ret) {
irq_stat = arch_irq_save();
if (bus->state & BUS_BUSY_MASK) {
/* Bus busy */
ret = SDRV_STATUS_BUSY;
arch_irq_restore(irq_stat);
} else {
setup_transmit_sche(bus, tx_buf, rx_buf, bytes);
if (bus->transmit_sche.ptxdata.val) {
bus->state |= SPI_STATE_BUSY_TX;
if (type == OP_MODE_DMA) {
bus->state |= SPI_STATE_DMA_TX;
} else {
bus->state |= SPI_STATE_IRQ_TX;
}
}
if (bus->transmit_sche.prxdata.val) {
bus->state |= SPI_STATE_BUSY_RX;
if (type == OP_MODE_DMA) {
bus->state |= SPI_STATE_DMA_RX;
} else {
bus->state |= SPI_STATE_IRQ_RX;
}
}
arch_irq_restore(irq_stat);
ret = spi_vector_transmission_ops(bus, type, 0);
if (ret != SDRV_STATUS_OK) {
spi_bus_state_clr(bus,true);
ssdk_printf(SSDK_ERR, "%s error: %d\n", __FUNCTION__, ret);
}
}
} else {
ret = SDRV_SPI_STATUS_PARAM_ILLEGAL;
}
return ret;
}
/**
* @brief Get SPI bus state
*
* @param[in] spi SPI bus
* @return SPI_BUSY: spi bus is busy. SPI_IDLE: spi bus is idle.
*/
enum spi_bus_state sdrv_spi_get_status(struct sdrv_spi *bus)
{
if (NULL == bus) {
return SPI_BUSY;
}
if (bus->state & BUS_BUSY_STATUS_MASK) {
return SPI_BUSY;
} else {
return SPI_IDLE;
}
}
/**
* @brief Get slave transmit cnt
*
* @param[in] spi SPI bus
* @return uint32_t: transmited bytes.
*/
uint32_t sdrv_spi_slave_get_transmit_len(struct sdrv_spi *bus)
{
uint32_t len = 0;
if (bus && bus->dev_config) {
if (bus->transmit_sche.prxdata.val != NULL) {
len = bus->transmit_sche.rx_cur;
} else {
len = bus->transmit_sche.tx_cur;
}
len <<= SPI_GET_WIDTH_SHIFT(SPI_GET_WIDTH_TYPE(bus->dev_config->width));
}
return len;
}
#ifdef __cplusplus
}
#endif