rt-thread/bsp/ck802/libraries/common/spi/dw_spi.c

1238 lines
36 KiB
C

/*
* Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/******************************************************************************
* @file dw_spi.c
* @brief CSI Source File for SPI Driver
* @version V1.0
* @date 02. June 2017
******************************************************************************/
#include "csi_core.h"
#include "drv_spi.h"
#include "dw_spi.h"
#include "drv_gpio.h"
#ifdef CONFIG_SPI_DMA
#include "ck_dmac.h"
#include <string.h>
#endif
#include "soc.h" /*CONFIG_SPI_NUM*/
#define ERR_SPI(errno) (CSI_DRV_ERRNO_SPI_BASE | errno)
#define SPI_NULL_PARAM_CHK(para) \
do { \
if (para == NULL) { \
return ERR_SPI(EDRV_PARAMETER); \
} \
} while (0)
typedef struct {
uint32_t base;
uint32_t irq;
pin_t ssel;
spi_event_cb_t cb_event;
void *cb_arg;
uint32_t send_num;
uint32_t recv_num;
uint8_t *send_buf;
uint8_t *recv_buf;
uint8_t enable_slave;
uint32_t transfer_num;
uint32_t clk_num; //clock number with a process of communication
uint8_t state; //Current SPI state
uint32_t mode; //Current SPI mode
uint8_t ss_mode;
spi_status_t status;
#ifdef CONFIG_SPI_DMA
dmac_handle_t dma_handle;
int32_t dma_tx_id;
int32_t dma_rx_id;
#endif
} dw_spi_priv_t;
static dw_spi_priv_t spi_instance[CONFIG_SPI_NUM];
static gpio_pin_handle_t pgpio_pin_handle;
static const spi_capabilities_t spi_capabilities = {
.simplex = 1, /* Simplex Mode (Master and Slave) */
.ti_ssi = 1, /* TI Synchronous Serial Interface */
.microwire = 1, /* Microwire Interface */
.event_mode_fault = 0 /* Signal Mode Fault event: \ref CSKY_SPI_EVENT_MODE_FAULT */
};
static int32_t dw_spi_set_datawidth(dw_spi_reg_t *addr, DWENUM_SPI_DATAWIDTH datawidth);
static int32_t dw_spi_set_mode(spi_handle_t handle, DWENUM_SPI_MODE mode);
/**
\brief use phobos gpio pin to simulate ss line for hardware controlled Output mode.
*/
static int32_t dw_spi_ss_init(dw_spi_priv_t *spi_priv)
{
csi_gpio_port_initialize(0, NULL);
pgpio_pin_handle = csi_gpio_pin_initialize(spi_priv->ssel);
csi_gpio_pin_config(pgpio_pin_handle, GPIO_MODE_PULLNONE, GPIO_DIRECTION_OUTPUT);
csi_gpio_pin_write(pgpio_pin_handle, true);
spi_priv->ss_mode = SPI_SS_MASTER_HW_OUTPUT;
return 0;
}
/**
\brief control ss line depend on controlled Output mode.
*/
static int32_t dw_spi_ss_control(dw_spi_priv_t *spi_priv, spi_ss_stat_e stat)
{
if (spi_priv->ss_mode == SPI_SS_MASTER_HW_OUTPUT) {
if (stat == SPI_SS_INACTIVE) {
csi_gpio_pin_write(pgpio_pin_handle, true);
} else if (stat == SPI_SS_ACTIVE) {
csi_gpio_pin_write(pgpio_pin_handle, false);
} else {
return -1;
}
}
return 0;
}
#ifdef CONFIG_SPI_DMA
void dw_spi_dma_event_cb(dma_event_e event, int32_t ch)
{
dw_spi_priv_t *spi_priv = NULL;
uint8_t i = 0u;
for (i = 0; i < CONFIG_SPI_NUM; i++) { /* find the SPI id */
spi_priv = &spi_instance[i];
if ((spi_priv->dma_tx_id == ch) || (spi_priv->dma_rx_id == ch)) {
break;
}
}
if (spi_priv->dma_tx_id == ch) {
spi_priv->dma_tx_id = -1;
} else {
spi_priv->dma_rx_id = -1;
}
if (event == DMA_EVENT_TRANSFER_ERROR) { /* DMA transfer ERROR */
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_DATA_LOST, NULL);
}
} else if (event == DMA_EVENT_TRANSFER_DONE) { /* DMA transfer complete */
if (spi_priv->mode == DWENUM_SPI_TXRX) {
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_TRANSFER_COMPLETE, spi_priv->cb_arg);
}
} else if (spi_priv->mode == DWENUM_SPI_TX) {
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
while (addr->SR & DW_SPI_DISABLE);
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_TX_COMPLETE, spi_priv->cb_arg);
}
} else {
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_RX_COMPLETE, spi_priv->cb_arg);
}
}
}
spi_priv->status.busy = 0U;
}
/**
\brief sending data to SPI transmitter with DMA,(received data is ignored).
*/
static int32_t dw_spi_dma_send(dw_spi_priv_t *spi_priv, const void *data, uint32_t num)
{
int32_t ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
spi_priv->dma_tx_id = ch;
dma_config_t config;
config.src_inc = DMA_ADDR_INC;
config.dst_inc = DMA_ADDR_CONSTANT;
config.src_tw = DMA_DATAWIDTH_SIZE8;
config.dst_tw = DMA_DATAWIDTH_SIZE8;
config.hs_if = CKENUM_DMA_SPI1_TX;
config.type = DMA_MEM2PERH;
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
uint8_t *ptxbuffer = (uint8_t *)data;
int32_t ret = csi_dma_config(spi_priv->dma_handle, ch, ptxbuffer, (uint8_t *) & (addr->DR), num, &config, dw_spi_dma_event_cb);
if (ret < 0) {
return ret;
}
addr->SPIENR = DW_SPI_DISABLE; /* enable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_TX);
addr->DMATDLR = 0;
addr->DMACR = DW_SPI_TDMAE;
addr->SER = spi_priv->enable_slave;
addr->SPIENR = DW_SPI_ENABLE;
csi_dma_start(spi_priv->dma_handle, ch);
while (csi_dma_get_status(spi_priv->dma_handle, ch) != DMA_STATE_DONE);
while (addr->SR & DW_SPI_BUSY);
csi_dma_stop(spi_priv->dma_handle, ch);
csi_dma_release_channel(spi_priv->dma_handle, ch);
addr->SPIENR = DW_SPI_DISABLE;
addr->SER = 0;
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
spi_priv->status.busy = 0U;
return 0;
}
/**
\brief receiving data from SPI receiver with DMA.
*/
static int32_t dw_spi_dma_receive(dw_spi_priv_t *spi_priv, void *data, uint32_t num)
{
int32_t ch;
ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
spi_priv->dma_tx_id = ch;
dma_config_t config;
config.src_inc = DMA_ADDR_CONSTANT;
config.dst_inc = DMA_ADDR_INC;
config.src_tw = DMA_DATAWIDTH_SIZE8;
config.dst_tw = DMA_DATAWIDTH_SIZE8;
config.hs_if = CKENUM_DMA_SPI1_RX;
config.type = DMA_PERH2MEM;
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
uint8_t *prx_buffer = (uint8_t *)data;
spi_priv->recv_buf = prx_buffer;
spi_priv->clk_num = num;
spi_priv->recv_num = num;
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
int32_t ret;
while (spi_priv->clk_num) {
if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV / 2) {
spi_priv->recv_num = DW_SPI_FIFO_MAX_LV / 2;
}
ret = csi_dma_config(spi_priv->dma_handle, ch, (uint8_t *) & (addr->DR), spi_priv->recv_buf, spi_priv->recv_num, &config, dw_spi_dma_event_cb);
if (ret < 0) {
return ret;
}
addr->SPIENR = DW_SPI_DISABLE; /* enable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
addr->DMARDLR = spi_priv->recv_num - 1;
addr->CTRLR1 = spi_priv->recv_num - 1;
addr->DMACR = DW_SPI_RDMAE;
addr->SER = spi_priv->enable_slave;
csi_dma_start(spi_priv->dma_handle, ch);
addr->SPIENR = DW_SPI_ENABLE;
addr->DR = DW_SPI_START_RX;
while (csi_dma_get_status(spi_priv->dma_handle, ch) != DMA_STATE_DONE);
csi_dma_stop(spi_priv->dma_handle, ch);
spi_priv->clk_num -= spi_priv->recv_num;
spi_priv->recv_buf += spi_priv->recv_num;
}
addr->SPIENR = DW_SPI_DISABLE;
addr->SER = 0;
csi_dma_release_channel(spi_priv->dma_handle, ch);
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
spi_priv->status.busy = 0U;
return 0;
}
/**
\brief sending/receiving data to/from SPI transmitter/receiver with DMA.
*/
static int32_t dw_spi_dma_transfer(dw_spi_priv_t *spi_priv, const void *data_out, void *data_in, uint32_t num_out, uint32_t num_in)
{
int32_t tx_ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
if (tx_ch < 0) {
return tx_ch;
}
spi_priv->dma_tx_id = tx_ch;
int32_t rx_ch = csi_dma_alloc_channel(spi_priv->dma_handle, -1);
if (rx_ch < 0) {
return rx_ch;
}
spi_priv->dma_rx_id = rx_ch;
dma_config_t tx_config, rx_config;
tx_config.src_inc = DMA_ADDR_INC;
tx_config.dst_inc = DMA_ADDR_CONSTANT;
tx_config.src_tw = DMA_DATAWIDTH_SIZE8;
tx_config.dst_tw = DMA_DATAWIDTH_SIZE8;
tx_config.hs_if = CKENUM_DMA_SPI1_TX;
tx_config.type = DMA_MEM2PERH;
rx_config.src_inc = DMA_ADDR_CONSTANT;
rx_config.dst_inc = DMA_ADDR_INC;
rx_config.src_tw = DMA_DATAWIDTH_SIZE8;
rx_config.dst_tw = DMA_DATAWIDTH_SIZE8;
rx_config.hs_if = CKENUM_DMA_SPI1_RX;
rx_config.type = DMA_PERH2MEM;
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
uint8_t *ptx_buffer = (uint8_t *)data_out;
uint8_t *prx_buffer = (uint8_t *)data_in;
spi_priv->send_buf = ptx_buffer;
spi_priv->recv_buf = prx_buffer;
spi_priv->send_num = num_out;
spi_priv->recv_num = num_in;
spi_priv->clk_num = num_in;
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
int32_t ret;
uint8_t dma_send_buf[DW_SPI_FIFO_MAX_LV];
while (spi_priv->clk_num) {
if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
} else {
spi_priv->transfer_num = spi_priv->clk_num;
}
if (spi_priv->send_num >= spi_priv->transfer_num) {
memcpy(dma_send_buf, spi_priv->send_buf, spi_priv->transfer_num);
spi_priv->send_num -= spi_priv->transfer_num;
spi_priv->send_buf += spi_priv->transfer_num;
} else {
if (spi_priv->send_num > 0) {
memcpy(dma_send_buf, spi_priv->send_buf, spi_priv->send_num);
spi_priv->send_buf = NULL;
}
memset(&dma_send_buf[spi_priv->send_num], 0, spi_priv->transfer_num - spi_priv->send_num);
spi_priv->send_num = 0;
}
ret = csi_dma_config(spi_priv->dma_handle, tx_ch, dma_send_buf, (uint8_t *) & (addr->DR), spi_priv->transfer_num, &tx_config, dw_spi_dma_event_cb);
if (ret < 0) {
return ret;
}
ret = csi_dma_config(spi_priv->dma_handle, rx_ch, (uint8_t *) & (addr->DR), spi_priv->recv_buf, spi_priv->transfer_num, &rx_config, dw_spi_dma_event_cb);
if (ret < 0) {
return ret;
}
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
addr->DMARDLR = (spi_priv->transfer_num - 1) % 16; /* set dma receive data level */
addr->DMATDLR = (spi_priv->transfer_num - 1) % 16 + 1; /* set dma transmit data level */
addr->DMACR = DW_SPI_RDMAE | DW_SPI_TDMAE;
dw_spi_set_mode(spi_priv, DWENUM_SPI_TXRX);
addr->SER = spi_priv->enable_slave;
addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
ret = csi_dma_start(spi_priv->dma_handle, tx_ch);
if (ret < 0) {
return ret;
}
while (csi_dma_get_status(spi_priv->dma_handle, tx_ch) != DMA_STATE_DONE);
ret = csi_dma_start(spi_priv->dma_handle, rx_ch);
if (ret < 0) {
return ret;
}
while (csi_dma_get_status(spi_priv->dma_handle, rx_ch) != DMA_STATE_DONE);
spi_priv->recv_buf += spi_priv->transfer_num;
spi_priv->recv_num -= spi_priv->transfer_num;
spi_priv->clk_num -= spi_priv->transfer_num;
addr->SPIENR = DW_SPI_DISABLE;
csi_dma_stop(spi_priv->dma_handle, tx_ch);
csi_dma_stop(spi_priv->dma_handle, rx_ch);
}
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
addr->SER = 0;
csi_dma_release_channel(spi_priv->dma_handle, tx_ch);
csi_dma_release_channel(spi_priv->dma_handle, rx_ch);
spi_priv->status.busy = 0U;
return 0;
}
#endif
/**
\brief Set the SPI datawidth.
\param[in] addr pointer to register address
\param[in] datawidth date frame size
\return error code
*/
static int32_t dw_spi_set_datawidth(dw_spi_reg_t *addr, DWENUM_SPI_DATAWIDTH datawidth)
{
if ((datawidth >= DWENUM_SPI_DATASIZE_4) && (datawidth <= DWENUM_SPI_DATASIZE_16)) {
uint16_t temp = addr->CTRLR0;
temp &= 0xfff0; /* temp has the value of CTRLR0 with DFS being cleared.*/
temp |= (datawidth); /* get the final CTRLR0 after datawidth config. */
addr->CTRLR0 = temp; /* write CTRLR0 */
return 0;
}
return -1;
}
/**
\brief Set the SPI clock divider.
\param[in] addr pointer to register address
\param[in] baud spi baud rate
\param[in] apbfreq sysclk for spi module.
\return error code
*/
static int32_t dw_spi_set_baudrate(dw_spi_reg_t *addr, int32_t baud, int32_t apbfreq)
{
int32_t sckdv = apbfreq / baud;
if (sckdv < 0x10000) {
addr->BAUDR = sckdv;
} else {
return -1;
}
return 0;
}
/**
\brief Set the SPI polarity.
\param[in] addr pointer to register address
\param[in] polarity spi polarity
\return error code
*/
static int32_t dw_spi_set_polarity(dw_spi_reg_t *addr, DWENUM_SPI_POLARITY polarity)
{
/* To config the polarity, we can set the SCPOL bit(CTRLR0[7]) as below:
* 0 - inactive state of serial clock is low
* 1 - inactive state of serial clock is high
*/
switch (polarity) {
case DWENUM_SPI_CLOCK_POLARITY_LOW:
addr->CTRLR0 &= (~DW_SPI_POLARITY);
break;
case DWENUM_SPI_CLOCK_POLARITY_HIGH:
addr->CTRLR0 |= DW_SPI_POLARITY;
break;
default:
return -1;
}
return 0;
}
/**
\brief Set the SPI Phase.
\param[in] addr pointer to register address
\param[in] phase Serial clock phase
\return error code
*/
static int32_t dw_spi_set_phase(dw_spi_reg_t *addr, DWENUM_SPI_PHASE phase)
{
switch (phase) {
case DWENUM_SPI_CLOCK_PHASE_MIDDLE:
addr->CTRLR0 &= (~DW_SPI_PHASE);
break;
case DWENUM_SPI_CLOCK_PHASE_START:
addr->CTRLR0 |= DW_SPI_PHASE;
break;
default:
return -1;
}
return 0;
}
/**
\brief Set the SPI mode.
\param[in] addr pointer to register address
\param[in] mode SPI_Mode
\return error code
*/
static int32_t dw_spi_set_mode(spi_handle_t handle, DWENUM_SPI_MODE mode)
{
dw_spi_priv_t *spi_priv = handle;
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
/* It is impossible to write to this register when the SSI is enabled.*/
/* we can set the TMOD to config transfer mode as below:
* TMOD_BIT9 TMOD_BIT8 transfer mode
* 0 0 transmit & receive
* 0 1 transmit only
* 1 0 receive only
* 1 1 reserved
*/
switch (mode) {
case DWENUM_SPI_TXRX:
addr->CTRLR0 &= (~DW_SPI_TMOD_BIT8);
addr->CTRLR0 &= (~DW_SPI_TMOD_BIT9);
break;
case DWENUM_SPI_TX:
addr->CTRLR0 |= DW_SPI_TMOD_BIT8;
addr->CTRLR0 &= (~DW_SPI_TMOD_BIT9);
break;
case DWENUM_SPI_RX:
addr->CTRLR0 &= (~DW_SPI_TMOD_BIT8);
addr->CTRLR0 |= DW_SPI_TMOD_BIT9;
break;
default:
addr->CTRLR0 |= DW_SPI_TMOD_BIT8;
addr->CTRLR0 |= DW_SPI_TMOD_BIT9;
break;
}
spi_priv->mode = mode;
return 0;
}
/**
\brief interrupt service function for receive FIFO full interrupt .
\param[in] spi_priv pointer to spi private.
*/
static void dw_spi_intr_rx_full(dw_spi_priv_t *spi_priv)
{
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
uint8_t temp = addr->ICR;
uint8_t *pbuffer = spi_priv->recv_buf;
uint32_t length = spi_priv->recv_num;
uint8_t rxnum;
rxnum = addr->RXFLR;
uint32_t i = 0u;
for (i = 0; i < rxnum; i++) {
*pbuffer = addr->DR;
pbuffer++;
}
length -= rxnum;
if (length < DW_SPI_FIFO_MAX_LV) {
addr->RXFTLR = length - 1;
}
if (length <= 0) {
temp = addr->IMR;
temp &= 0x2f;
addr->IMR = temp;
addr->SER = 0;
addr->SPIENR = DW_SPI_DISABLE;
spi_priv->status.busy = 0U;
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_RX_COMPLETE, spi_priv->cb_arg);
return;
}
} else {
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
addr->SER = spi_priv->enable_slave; /* enable all cs */
spi_priv->recv_buf = pbuffer;
spi_priv->recv_num = length;
if (spi_priv->recv_num > DW_SPI_FIFO_MAX_LV) {
addr->RXFTLR = DW_SPI_FIFO_MAX_LV - 1;
addr->CTRLR1 = DW_SPI_FIFO_MAX_LV - 1;
} else {
addr->RXFTLR = spi_priv->recv_num - 1;
addr->CTRLR1 = spi_priv->recv_num - 1;
}
addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
addr->DR = DW_SPI_START_RX ;
}
}
/**
\brief interrupt service function for transmit FIFO empty interrupt.
\param[in] spi_priv pointer to spi private.
*/
static void dw_spi_intr_tx_empty(dw_spi_priv_t *spi_priv)
{
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
uint8_t temp = addr->ICR;
/* transfer mode:transmit & receive */
uint32_t i = 0u;
if (spi_priv->
mode == DWENUM_SPI_TXRX) {
/* read data out from rx FIFO */
while (spi_priv->transfer_num) {
*spi_priv->recv_buf = addr->DR;
spi_priv->recv_buf++;
spi_priv->transfer_num--;
}
if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
} else {
spi_priv->transfer_num = spi_priv->clk_num;
}
for (i = 0; i < spi_priv->transfer_num; i++) {
if (spi_priv->send_num == 0) {
addr->DR = 0x0;
} else {
addr->DR = *spi_priv->send_buf;
spi_priv->send_buf++;
}
spi_priv->send_num--;
}
} else { //transfer mode :transmit only
if (spi_priv->clk_num >= DW_SPI_FIFO_MAX_LV) {
spi_priv->transfer_num = DW_SPI_FIFO_MAX_LV;
} else {
spi_priv->transfer_num = spi_priv->clk_num;
}
for (i = 0; i < spi_priv->transfer_num; i++) {
addr->DR = *spi_priv->send_buf;
spi_priv->send_buf++;
spi_priv->send_num--;
}
}
if (spi_priv->clk_num == 0) {
temp = addr->IMR;
temp &= ~DW_SPI_IMR_TXEIM;
addr->IMR = temp;
addr->SER = 0;
addr->SPIENR = DW_SPI_DISABLE;
spi_priv->status.busy = 0U;
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
if (spi_priv->mode == DWENUM_SPI_TXRX) {
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_TRANSFER_COMPLETE, spi_priv->cb_arg);
return;
}
} else {
if (spi_priv->cb_event) {
spi_priv->cb_event(SPI_EVENT_TX_COMPLETE, spi_priv->cb_arg);
return;
}
}
}
spi_priv->clk_num -= spi_priv->transfer_num;
}
/**
\brief handler the interrupt.
\param[in] spi Pointer to \ref SPI_RESOURCES
*/
void dw_spi_irqhandler(int32_t idx)
{
dw_spi_priv_t *spi_priv = &spi_instance[idx];
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
uint32_t intr = addr->ISR;
/* deal with receive FIFO full interrupt */
if (intr & DW_SPI_RXFIFO_FULL) {
dw_spi_intr_rx_full(spi_priv);
}
/* deal with transmit FIFO empty interrupt */
else if (intr & DW_SPI_TXFIFO_EMPTY) {
dw_spi_intr_tx_empty(spi_priv);
}
}
int32_t __attribute__((weak)) target_spi_init(pin_t mosi, pin_t miso, pin_t sclk, pin_t ssel, uint32_t *base, uint32_t *irq)
{
return -1;
}
/**
\brief Initialize SPI Interface. 1. Initializes the resources needed for the SPI interface 2.registers event callback function
\param[in] spi pin of mosi
\param[in] spi pin of miso
\param[in] spi pin of sclk
\param[in] spi pin of ssel
\param[in] cb_event event call back function \ref spi_event_cb_t
\return return spi handle if success
*/
spi_handle_t csi_spi_initialize(pin_t mosi, pin_t miso, pin_t sclk, pin_t ssel, spi_event_cb_t cb_event, void *cb_arg)
{
uint32_t base = 0u;
uint32_t irq = 0u;
int32_t idx = target_spi_init(mosi, miso, sclk, ssel, &base, &irq);
if (idx < 0 || idx >= CONFIG_SPI_NUM) {
return NULL;
}
dw_spi_priv_t *spi_priv = &spi_instance[idx];
spi_priv->base = base;
spi_priv->irq = irq;
spi_priv->ssel = ssel;
spi_priv->cb_event = cb_event;
spi_priv->cb_arg = cb_arg;
spi_priv->status.busy = 0U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
spi_priv->enable_slave = 1U;
spi_priv->state = SPI_INITIALIZED;
drv_nvic_enable_irq(spi_priv->irq);
#ifdef CONFIG_SPI_DMA
spi_priv->dma_handle = csi_dma_initialize(0);
#endif
return (spi_handle_t)spi_priv;
}
/**
\brief De-initialize SPI Interface. stops operation and releases the software resources used by the interface
\param[in] handle spi handle to operate.
\return error code
*/
int32_t csi_spi_uninitialize(spi_handle_t handle)
{
SPI_NULL_PARAM_CHK(handle);
dw_spi_priv_t *spi_priv = handle;
drv_nvic_disable_irq(spi_priv->irq);
spi_priv->cb_event = NULL;
spi_priv->state = 0U;
spi_priv->status.busy = 0U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
#ifdef CONFIG_SPI_DMA
csi_dma_uninitialize(spi_priv->dma_handle);
#endif
return 0;
}
/**
\brief Get driver capabilities.
\param[in] spi instance to operate.
\return \ref spi_capabilities_t
*/
spi_capabilities_t csi_spi_get_capabilities(spi_handle_t handle)
{
return spi_capabilities;
}
/**
\brief config spi mode.
\param[in] handle spi handle to operate.
\param[in] sysclk sysclk for spi module.
\param[in] baud spi baud rate. if negative, then this attribute not changed
\param[in] mode \ref spi_mode_e . if negative, then this attribute not changed
\param[in] format \ref spi_format_e . if negative, then this attribute not changed
\param[in] order \ref spi_bit_order_e . if negative, then this attribute not changed
\param[in] ss_mode \ref spi_ss_mode_e . if negative, then this attribute not changed
\param[in] bit_width spi data bitwidth: (1 ~ SPI_DATAWIDTH_MAX) . if negative, then this attribute not changed
\return error code
*/
int32_t csi_spi_config(spi_handle_t handle,
int32_t sysclk,
int32_t baud,
spi_mode_e mode,
spi_format_e format,
spi_bit_order_e order,
spi_ss_mode_e ss_mode,
int32_t bit_width)
{
SPI_NULL_PARAM_CHK(handle);
dw_spi_priv_t *spi_priv = handle;
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
if ((spi_priv->state & SPI_INITIALIZED) == 0U) {
return ERR_SPI(EDRV_UNSUPPORTED);
}
if (spi_priv->status.busy) {
return ERR_SPI(EDRV_BUSY);
}
spi_priv->status.busy = 0U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
addr->SPIENR = DW_SPI_DISABLE;
addr->IMR = DW_SPI_INT_DISABLE;
int32_t ret = 0;
if (baud >= 0) {
ret = dw_spi_set_baudrate(addr, baud, sysclk);
if (ret < 0) {
return ERR_SPI(EDRV_PARAMETER);
}
}
if (mode >= 0) {
switch (mode) {
case SPI_MODE_MASTER:
break;
default:
return ERR_SPI(EDRV_UNSUPPORTED);
}
}
if (format >= 0) {
switch (format) {
case SPI_FORMAT_CPOL0_CPHA0:
dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_LOW);
dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_MIDDLE);
break;
case SPI_FORMAT_CPOL0_CPHA1:
dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_LOW);
dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_START);
break;
case SPI_FORMAT_CPOL1_CPHA0:
dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_HIGH);
dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_MIDDLE);
break;
case SPI_FORMAT_CPOL1_CPHA1:
dw_spi_set_polarity(addr, DWENUM_SPI_CLOCK_POLARITY_HIGH);
dw_spi_set_phase(addr, DWENUM_SPI_CLOCK_PHASE_START);
break;
default:
return ERR_SPI(EDRV_PARAMETER);
}
}
if (order >= 0) {
//TD:
}
if (ss_mode >= 0) {
switch (ss_mode) {
case SPI_SS_MASTER_SW:
spi_priv->ss_mode = SPI_SS_MASTER_SW;
break;
case SPI_SS_MASTER_HW_OUTPUT:
dw_spi_ss_init(spi_priv);
break;
default:
return ERR_SPI(EDRV_UNSUPPORTED);
}
}
if (bit_width >= 0) {
ret = dw_spi_set_datawidth(addr, bit_width);
if (ret < 0) {
return ERR_SPI(EDRV_PARAMETER);
}
}
spi_priv->state |= SPI_CONFIGURED;
return 0;
}
/**
\brief config spi default tx value.
\param[in] handle spi handle to operate.
\param[in] value default tx value
\return error code
*/
int32_t csi_spi_set_default_tx_value(spi_handle_t handle, uint32_t value)
{
SPI_NULL_PARAM_CHK(handle);
return ERR_SPI(EDRV_UNSUPPORTED);
}
/**
\brief sending data to SPI transmitter,(received data is ignored).
if non-blocking mode, this function only start the sending,
\ref spi_event_e is signaled when operation completes or error happens.
\ref csi_spi_get_status can indicates operation status.
if blocking mode, this function return after operation completes or error happens.
\param[in] handle spi handle to operate.
\param[in] data Pointer to buffer with data to send to SPI transmitter. data_type is : uint8_t for 1..8 data bits, uint16_t for 9..16 data bits,uint32_t for 17..32 data bits,
\param[in] num Number of data items to send.
\param[in] block_mode blocking and non_blocking to selcect
\return error code
*/
int32_t csi_spi_send(spi_handle_t handle, const void *data, uint32_t num, uint8_t block_mode)
{
if (handle == NULL || data == NULL || num == 0) {
return ERR_SPI(EDRV_PARAMETER);
}
dw_spi_priv_t *spi_priv = handle;
if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
return ERR_SPI(EDRV_UNSUPPORTED);
}
if (spi_priv->status.busy) {
return ERR_SPI(EDRV_BUSY);
}
spi_priv->status.busy = 1U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
#ifdef CONFIG_SPI_DMA
return dw_spi_dma_send(spi_priv, data, num);
#endif
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_TX);
addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
uint8_t *ptxbuffer = (uint8_t *)data;
if (block_mode) {
addr->SER = spi_priv->enable_slave;
addr->TXFTLR = DW_SPI_TXFIFO_LV;
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
spi_priv->send_num = num;
uint32_t once_len , i;
while (spi_priv->send_num) {
once_len = (spi_priv->send_num >= DW_SPI_FIFO_MAX_LV) ?
DW_SPI_FIFO_MAX_LV : spi_priv->send_num;
for (i = 0; i < once_len; i++) {
addr->DR = *ptxbuffer++;
}
while (!(addr->SR & DW_SPI_TFE));
while (addr->SR & DW_SPI_BUSY);
spi_priv->send_num -= once_len;
}
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
spi_priv->status.busy = 0U;
} else {
spi_priv->send_num = num;
spi_priv->clk_num = num;
spi_priv->send_buf = ptxbuffer;
spi_priv->transfer_num = 0;
addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
addr->TXFTLR = DW_SPI_TXFIFO_LV;
addr->SER = spi_priv->enable_slave;
addr->IMR = DW_SPI_IMR_TXEIM;
}
return 0;
}
/**
\brief receiving data from SPI receiver.transmits the default value as specified by csi_spi_set_default_tx_value
if non-blocking mode, this function only start the receiving,
\ref spi_event_e is signaled when operation completes or error happens.
\ref csi_spi_get_status can indicates operation status.
if blocking mode, this function return after operation completes or error happens.
\param[in] handle spi handle to operate.
\param[out] data Pointer to buffer for data to receive from SPI receiver
\param[in] num Number of data items to receive
\param[in] block_mode blocking and non_blocking to selcect
\return error code
*/
int32_t csi_spi_receive(spi_handle_t handle, void *data, uint32_t num, uint8_t block_mode)
{
if (handle == NULL || data == NULL || num == 0) {
return ERR_SPI(EDRV_PARAMETER);
}
dw_spi_priv_t *spi_priv = handle;
if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
return ERR_SPI(EDRV_UNSUPPORTED);
}
if (spi_priv->status.busy) {
return ERR_SPI(EDRV_BUSY);
}
spi_priv->status.busy = 1U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
#ifdef CONFIG_SPI_DMA
return dw_spi_dma_receive(spi_priv, data, num);
#endif
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
uint8_t *prx_buffer = data;
if (block_mode) {
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
spi_priv->recv_buf = prx_buffer;
spi_priv->recv_num = num;
while (spi_priv->recv_num) {
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
addr->SER = spi_priv->enable_slave; /* enable all cs */
uint32_t once_len = (spi_priv->recv_num >= DW_SPI_FIFO_MAX_LV) ?
DW_SPI_FIFO_MAX_LV : spi_priv->recv_num;
addr->CTRLR1 = once_len - 1;
addr->RXFTLR = once_len - 1;
addr->SPIENR = DW_SPI_ENABLE;
addr->DR = 0;
while (addr->RXFLR < once_len);
int i = 0;
for (i = 0; i < once_len; i++) {
*spi_priv->recv_buf++ = addr->DR;
}
spi_priv->recv_num -= once_len;
}
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
spi_priv->status.busy = 0U;
} else {
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
spi_priv->recv_buf = prx_buffer;
spi_priv->recv_num = num;
dw_spi_set_mode(spi_priv, DWENUM_SPI_RX);
addr->SER = spi_priv->enable_slave; /* enable all cs */
if (num > DW_SPI_FIFO_MAX_LV) {
addr->RXFTLR = DW_SPI_FIFO_MAX_LV - 1;
addr->CTRLR1 = DW_SPI_FIFO_MAX_LV - 1;
} else {
addr->RXFTLR = num - 1;
addr->CTRLR1 = num - 1;
}
addr->IMR = DW_SPI_IMR_RXFIM;
addr->SPIENR = DW_SPI_ENABLE; /* enable SPI */
addr->DR = DW_SPI_START_RX ;
}
return 0;
}
/**
\brief sending/receiving data to/from SPI transmitter/receiver.
if non-blocking mode, this function only start the transfer,
\ref spi_event_e is signaled when operation completes or error happens.
\ref csi_spi_get_status can indicates operation status.
if blocking mode, this function return after operation completes or error happens.
\param[in] handle spi handle to operate.
\param[in] data_out Pointer to buffer with data to send to SPI transmitter
\param[out] data_in Pointer to buffer for data to receive from SPI receiver
\param[in] num_out Number of data items to send
\param[in] num_in Number of data items to receive
\param[in] block_mode blocking and non_blocking to selcect
\return error code
*/
int32_t csi_spi_transfer(spi_handle_t handle, const void *data_out, void *data_in, uint32_t num_out, uint32_t num_in, uint8_t block_mode)
{
if (handle == NULL || data_in == NULL || num_out == 0 || num_in == 0 || data_out == NULL) {
return ERR_SPI(EDRV_PARAMETER);
}
dw_spi_priv_t *spi_priv = handle;
if ((spi_priv->state & SPI_CONFIGURED) == 0U) {
return ERR_SPI(EDRV_UNSUPPORTED);
}
if (spi_priv->status.busy) {
return ERR_SPI(EDRV_BUSY);
}
spi_priv->status.busy = 1U;
spi_priv->status.data_lost = 0U;
spi_priv->status.mode_fault = 0U;
#ifdef CONFIG_SPI_DMA
return dw_spi_dma_transfer(spi_priv, data_out, data_in, num_out, num_in);
#endif
dw_spi_ss_control(spi_priv, SPI_SS_ACTIVE);
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
dw_spi_set_mode(spi_priv, DWENUM_SPI_TXRX);
addr->SER = spi_priv->enable_slave; /* enable all cs */
uint8_t *ptx_buffer = (uint8_t *)data_out;
uint8_t *prx_buffer = (uint8_t *)data_in;
uint32_t i = 0u;
if (block_mode) {
for (i = 0; i < num_out; i++) { /* transmit datas in transmit-buffer */
addr->DR = *ptx_buffer;
ptx_buffer++;
}
while ((addr->SR & DW_SPI_BUSY));
while ((addr->SR & DW_SPI_TFE) == 0);
while ((addr->SR & DW_SPI_RFNE) == 0);
addr->SPIENR = DW_SPI_DISABLE; /* disable SPI */
dw_spi_set_mode(addr, DWENUM_SPI_RX);
addr->SPIENR = DW_SPI_ENABLE;
addr->DR = DW_SPI_START_RX;
for (i = 0; i < num_in; i++) {
*prx_buffer = addr->DR;
prx_buffer++;
}
addr->SER = 0;
dw_spi_ss_control(spi_priv, SPI_SS_INACTIVE);
addr->SPIENR = DW_SPI_DISABLE;
spi_priv->status.busy = 0U;
} else {
spi_priv->send_buf = ptx_buffer;
spi_priv->recv_buf = prx_buffer;
spi_priv->send_num = num_out;
spi_priv->recv_num = num_in;
spi_priv->clk_num = num_in;
spi_priv->transfer_num = 0;
addr->TXFTLR = DW_SPI_TXFIFO_LV;
addr->SPIENR = DW_SPI_ENABLE;
/* enable transmit FIFO empty interrupt */
addr->IMR |= DW_SPI_IMR_TXEIM;
}
return 0;
}
/**
\brief abort spi transfer.
\param[in] handle spi handle to operate.
\return error code
*/
int32_t csi_spi_abort_transfer(spi_handle_t handle)
{
SPI_NULL_PARAM_CHK(handle);
dw_spi_priv_t *spi_priv = handle;
dw_spi_reg_t *addr = (dw_spi_reg_t *)(spi_priv->base);
addr->SPIENR = DW_SPI_DISABLE;
spi_priv->status.busy = 0U;
spi_priv->recv_buf = NULL;
spi_priv->recv_num = 0;
return 0;
}
/**
\brief Get SPI status.
\param[in] handle spi handle to operate.
\return SPI status \ref ARM_SPI_STATUS
*/
spi_status_t csi_spi_get_status(spi_handle_t handle)
{
spi_status_t spi_status = {0};
if (handle == NULL) {
return spi_status;
}
dw_spi_priv_t *spi_priv = handle;
return spi_priv->status;
}