Docker-Build mit gcc-arm-none-eabi 4.9-2014q4 (statt Homebrew-Cask) verwendet, Dockerfile/tools/docker.sh dafür aus Task 13 vorgezogen. cmake/sdk.cmake schont gcc 4.9 (kennt -fcommon/-Wno-error=... nicht). Zwei zusätzliche SDK-Stubs (EarlyInit, pre_gpio_config) in src/sys/sdk_stubs.c ergänzt, siehe sdk/PATCHES.md.
1426 lines
43 KiB
C
1426 lines
43 KiB
C
/**
|
||
* @file wm_hostspi.c
|
||
*
|
||
* @brief host spi Driver Module
|
||
*
|
||
* @author dave
|
||
*
|
||
* Copyright (c) 2015 Winner Microelectronics Co., Ltd.
|
||
*/
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include "wm_regs.h"
|
||
#include "wm_irq.h"
|
||
#include "wm_gpio.h"
|
||
#include "wm_hostspi.h"
|
||
#include "wm_dma.h"
|
||
#include "wm_dbg.h"
|
||
#include "wm_mem.h"
|
||
#include "wm_cpu.h"
|
||
#include "wm_spi_hal.h"
|
||
#include "wm_wl_task.h"
|
||
#include "tls_common.h"
|
||
#include "wm_pmu.h"
|
||
|
||
static struct tls_spi_port *spi_port = NULL;
|
||
|
||
#define MSG_QUEUE_SIZE (8)
|
||
|
||
#define SPI_SCHEDULER_STK_SIZE (128)
|
||
static u32 spi_scheduler_stk[SPI_SCHEDULER_STK_SIZE];
|
||
|
||
#define SPI_SCHED_MSG_START_ENGINE (1)
|
||
#define SPI_SCHED_MSG_TX_FIFO_READY (2)
|
||
#define SPI_SCHED_MSG_RX_FIFO_READY (3)
|
||
#define SPI_SCHED_MSG_TRANSFER_COMPLETE (4)
|
||
#define SPI_SCHED_MSG_EXIT (5)
|
||
#define SPI_SCHED_MSG_END (6)
|
||
static void spi_start_transfer(u32 transfer_bytes);
|
||
|
||
int tls_spi_async(struct tls_spi_message *message);
|
||
int tls_spi_sync(struct tls_spi_message *message);
|
||
|
||
#ifdef SPI_USE_DMA
|
||
static void SpiMasterInit(u8 mode, u8 cs_active, u32 fclk)
|
||
{
|
||
tls_sys_clk sysclk;
|
||
|
||
SPIM_CHCFG_REG = SPI_CLEAR_FIFOS;
|
||
while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS);
|
||
|
||
tls_sys_clk_get(&sysclk); //��ȡʵ��Ƶ��
|
||
|
||
SPIM_CLKCFG_REG = sysclk.apbclk*UNIT_MHZ/(fclk*2) - 1;;
|
||
SPIM_SPICFG_REG = 0;
|
||
SPIM_SPICFG_REG = SPI_FRAME_FORMAT_MOTO | SPI_SET_MASTER_SLAVE(SPI_MASTER) | mode;
|
||
SPIM_INTEN_REG = 0xff; /* Disable INT */
|
||
|
||
tls_dma_init();
|
||
}
|
||
|
||
int spiWaitIdle(void)
|
||
{
|
||
unsigned long regVal;
|
||
unsigned long timeout = 0;
|
||
|
||
do
|
||
{
|
||
timeout++;
|
||
if (timeout > 0x4FFFFF) // 5s
|
||
return TLS_SPI_STATUS_EBUSY;
|
||
regVal = SPIM_SPISTATUS_REG;
|
||
}
|
||
while (regVal & (1 << 12));
|
||
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
static int SpiDmaBlockWrite(u8 * data, u32 len, u8 ifusecmd, u32 cmd)
|
||
{
|
||
unsigned char dmaCh = 0;
|
||
struct tls_dma_descriptor DmaDesc;
|
||
u32 txlen, txlenbk;
|
||
u32 i, blocknum, blocksize = 0;
|
||
int ret = TLS_SPI_STATUS_OK;
|
||
int txcmdover = 0;
|
||
|
||
if (NULL == data)
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
if (spiWaitIdle())
|
||
return TLS_SPI_STATUS_EBUSY;
|
||
SPIM_CHCFG_REG = SPI_CLEAR_FIFOS;
|
||
while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS);
|
||
|
||
if (ifusecmd)
|
||
SPIM_TXDATA_REG = cmd;
|
||
|
||
if (len % 4)
|
||
{
|
||
txlen = len & 0xfffffffc; // �����ֵ������
|
||
}
|
||
else
|
||
{
|
||
txlen = len;
|
||
}
|
||
|
||
txlenbk = txlen;
|
||
if (txlen > 0)
|
||
{
|
||
blocknum = txlen / SPI_DMA_MAX_TRANS_SIZE;
|
||
|
||
/* Request DMA Channel */
|
||
dmaCh = tls_dma_request(1,TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_LSSPI_TX) |
|
||
TLS_DMA_FLAGS_HARD_MODE);
|
||
for (i = 0; i <= blocknum; i++)
|
||
{
|
||
DmaDesc.src_addr = (int) (data + i * SPI_DMA_MAX_TRANS_SIZE);
|
||
DmaDesc.dest_addr = HR_SPI_TXDATA_REG;
|
||
blocksize = (txlen > SPI_DMA_MAX_TRANS_SIZE) ? SPI_DMA_MAX_TRANS_SIZE : txlen;
|
||
|
||
if (0 == blocksize)
|
||
break;
|
||
DmaDesc.dma_ctrl = TLS_DMA_DESC_CTRL_SRC_ADD_INC |
|
||
TLS_DMA_DESC_CTRL_DATA_SIZE_WORD |
|
||
TLS_DMA_DESC_CTRL_TOTAL_BYTES(blocksize);
|
||
DmaDesc.valid = TLS_DMA_DESC_VALID;
|
||
DmaDesc.next = NULL;
|
||
tls_dma_start(dmaCh, &DmaDesc, 0);
|
||
/* Enable SPI TX DMA */
|
||
SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0) | SPI_TX_DMA_ON;
|
||
SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff);
|
||
|
||
if (ifusecmd && 0 == i)
|
||
{
|
||
SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON |
|
||
SPI_CONTINUE_MODE | SPI_START |
|
||
SPI_VALID_CLKS_NUM(((blocksize + 4) * 8));
|
||
txcmdover = 1;
|
||
}
|
||
else
|
||
SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON | SPI_CONTINUE_MODE |
|
||
SPI_START | SPI_VALID_CLKS_NUM((blocksize * 8));
|
||
|
||
if (spiWaitIdle())
|
||
{
|
||
ret = TLS_SPI_STATUS_EBUSY;
|
||
break;
|
||
}
|
||
/* Wait Dma Channel Complete and Free Dma channel */
|
||
if (tls_dma_wait_complt(dmaCh))
|
||
{
|
||
ret = TLS_SPI_STATUS_EBUSY;
|
||
break;
|
||
}
|
||
txlen -= blocksize;
|
||
}
|
||
tls_dma_free(dmaCh);
|
||
}
|
||
// tx ����һ�����ֵļ����ֽ�
|
||
if (len > txlenbk)
|
||
{
|
||
u32 word32 = 0;
|
||
int temp = 0;
|
||
for (i = 0; i < (len - txlenbk); i++)
|
||
{
|
||
word32 |= (data[txlenbk + i] << (i * 8));
|
||
}
|
||
SPIM_TXDATA_REG = word32;
|
||
SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0);
|
||
SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff);
|
||
if (ifusecmd && 0 == txcmdover) // ��Ҫ��������������û�з��ͳ�ȥ�����͵��ֽ�����Ҫ����4
|
||
temp = 4;
|
||
SPIM_CHCFG_REG = SPI_FORCE_SPI_CS_OUT | SPI_TX_CHANNEL_ON | SPI_CONTINUE_MODE |
|
||
SPI_START | SPI_VALID_CLKS_NUM(((temp + len - txlenbk) * 8));
|
||
if (spiWaitIdle())
|
||
{
|
||
ret = TLS_SPI_STATUS_EBUSY;
|
||
}
|
||
}
|
||
SPIM_CHCFG_REG = 0x00000000;
|
||
SPIM_MODECFG_REG = 0x00000000;
|
||
SPIM_SPITIMEOUT_REG = 0x00000000;
|
||
|
||
return ret;
|
||
}
|
||
|
||
static int SpiDmaBlockRead(u8 * data, u32 len, u8 * txdata, u8 txlen)
|
||
{
|
||
unsigned char dmaCh = 0;
|
||
struct tls_dma_descriptor DmaDesc;
|
||
u32 word32 = 0;
|
||
u32 i;
|
||
u32 rxlen, rxlenbk;
|
||
u8 blocknum;
|
||
u32 blocksize = 0;
|
||
int ret = TLS_SPI_STATUS_OK;
|
||
|
||
// printf("\nentry SpiDmaBlockRead\n");
|
||
if (NULL == data)
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
if (spiWaitIdle())
|
||
{
|
||
return TLS_SPI_STATUS_EBUSY;
|
||
}
|
||
|
||
SPIM_CHCFG_REG = SPI_CLEAR_FIFOS;
|
||
while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS);
|
||
|
||
if (len % 4)
|
||
{
|
||
rxlen = len & 0xfffffffc; // �����ֵ����ȡ
|
||
}
|
||
else
|
||
{
|
||
rxlen = len;
|
||
}
|
||
rxlenbk = rxlen;
|
||
blocknum = rxlen / SPI_DMA_MAX_TRANS_SIZE;
|
||
|
||
if (txlen > 0 && txlen <= 32)
|
||
{
|
||
for (i = 0; i < txlen; i++)
|
||
{
|
||
if ((i > 0) && (0 == i % 4))
|
||
{
|
||
SPIM_TXDATA_REG = word32;
|
||
word32 = 0;
|
||
}
|
||
word32 |= (txdata[i] << ((i % 4) * 8));
|
||
}
|
||
SPIM_TXDATA_REG = word32;
|
||
}
|
||
|
||
/* Request DMA Channel */
|
||
dmaCh = tls_dma_request(1,TLS_DMA_FLAGS_CHANNEL_SEL(TLS_DMA_SEL_LSSPI_RX) |
|
||
TLS_DMA_FLAGS_HARD_MODE);
|
||
DmaDesc.src_addr = HR_SPI_RXDATA_REG;
|
||
for (i = 0; i <= blocknum; i++)
|
||
{
|
||
if (rxlenbk > 0)
|
||
{
|
||
// ˵�����յ����ݴ���4
|
||
// printf("\ni =%d\n",i);
|
||
DmaDesc.dest_addr = (int) (data + i * SPI_DMA_MAX_TRANS_SIZE);
|
||
blocksize = (rxlen > SPI_DMA_MAX_TRANS_SIZE) ? SPI_DMA_MAX_TRANS_SIZE : rxlen;
|
||
if (0 == blocksize)
|
||
break;
|
||
// printf("\nblocksize= %d\n",blocksize);
|
||
DmaDesc.dma_ctrl =
|
||
TLS_DMA_DESC_CTRL_DEST_ADD_INC | TLS_DMA_DESC_CTRL_BURST_SIZE1 |
|
||
TLS_DMA_DESC_CTRL_DATA_SIZE_WORD |
|
||
TLS_DMA_DESC_CTRL_TOTAL_BYTES(blocksize);
|
||
// word32 = DmaDesc.dma_ctrl;
|
||
// printf("\ndma ctrl = %x\n",DmaDesc.dma_ctrl);
|
||
DmaDesc.valid = TLS_DMA_DESC_VALID;
|
||
DmaDesc.next = NULL;
|
||
tls_dma_start(dmaCh, &DmaDesc, 0);
|
||
/* Enable SPI RX DMA */
|
||
SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0) |
|
||
SPI_RX_DMA_ON;
|
||
}
|
||
else
|
||
{
|
||
SPIM_MODECFG_REG = SPI_RX_TRIGGER_LEVEL(0) | SPI_TX_TRIGGER_LEVEL(0); // rx��������4��byte�����ÿ�DMA
|
||
}
|
||
SPIM_SPITIMEOUT_REG = SPI_TIMER_EN | SPI_TIME_OUT((u32) 0xffff);
|
||
if (0 == blocknum)
|
||
{
|
||
SPIM_CHCFG_REG =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_TX_CHANNEL_ON |
|
||
SPI_CONTINUE_MODE | SPI_START |
|
||
SPI_VALID_CLKS_NUM(((len + txlen) * 8)) | SPI_RX_INVALID_BITS(txlen *8);
|
||
}
|
||
else
|
||
{
|
||
if (0 == i) // ��һ����Ҫ��TX
|
||
{
|
||
SPIM_CHCFG_REG =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_TX_CHANNEL_ON
|
||
| SPI_CONTINUE_MODE | SPI_START |
|
||
SPI_VALID_CLKS_NUM(((blocksize + txlen) *8)) | SPI_RX_INVALID_BITS(txlen * 8);
|
||
}
|
||
else if (i == blocknum)
|
||
{
|
||
SPIM_CHCFG_REG =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE
|
||
| SPI_START | SPI_VALID_CLKS_NUM((blocksize + len - rxlenbk) * 8);
|
||
}
|
||
else
|
||
{
|
||
SPIM_CHCFG_REG =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE
|
||
| SPI_START | SPI_VALID_CLKS_NUM(blocksize * 8);
|
||
}
|
||
}
|
||
if (spiWaitIdle())
|
||
{
|
||
ret = TLS_SPI_STATUS_EBUSY;
|
||
break;
|
||
}
|
||
|
||
/* Wait Dma Channel Complete and Free Dma channel */
|
||
if (tls_dma_wait_complt(dmaCh))
|
||
{
|
||
ret = TLS_SPI_STATUS_EBUSY;
|
||
break;
|
||
}
|
||
rxlen -= blocksize;
|
||
}
|
||
tls_dma_free(dmaCh);
|
||
|
||
if (len > rxlenbk) // ȡ���IJ���һ���ֵļ���byte
|
||
{
|
||
word32 = SPIM_RXDATA_REG;
|
||
*((int *) data + rxlenbk / 4) = word32;
|
||
}
|
||
SPIM_CHCFG_REG = 0x00000000;
|
||
SPIM_MODECFG_REG = 0x00000000;
|
||
SPIM_SPITIMEOUT_REG = 0x00000000;
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
|
||
/**
|
||
* @brief This function is used to set SPI transfer mode.
|
||
*
|
||
* @param[in] type is the transfer type.
|
||
* type == SPI_BYTE_TRANSFER byte transfer
|
||
* type == SPI_WORD_TRANSFER word transfer
|
||
* type == SPI_DMA_TRANSFER DMA transfer
|
||
*
|
||
* @return None
|
||
*
|
||
* @note None
|
||
*/
|
||
void tls_spi_trans_type(u8 type)
|
||
{
|
||
|
||
spi_port->transtype = type;
|
||
if (SPI_WORD_TRANSFER == type)
|
||
{
|
||
spi_set_endian(0);
|
||
}
|
||
else if (SPI_DMA_TRANSFER == type)
|
||
{
|
||
#ifdef SPI_USE_DMA
|
||
SpiMasterInit(spi_port->mode, TLS_SPI_CS_LOW, spi_port->speed_hz);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
static void spi_message_init(struct tls_spi_message *m)
|
||
{
|
||
memset(m, 0, sizeof(*m));
|
||
dl_list_init(&m->transfers);
|
||
}
|
||
|
||
|
||
static void spi_complete(void *arg)
|
||
{
|
||
tls_os_sem_t *sem;
|
||
|
||
sem = (tls_os_sem_t *) arg;
|
||
tls_os_sem_release(sem);
|
||
}
|
||
|
||
static u32 spi_fill_txfifo(struct tls_spi_transfer *current_transfer,
|
||
u32 current_remaining_bytes)
|
||
{
|
||
u8 fifo_level;
|
||
u16 rw_words;
|
||
u16 rw_bytes;
|
||
u8 data8;
|
||
u8 i;
|
||
u32 data32 = 0;
|
||
u32 tx_remaining_bytes;
|
||
if ((current_transfer == NULL) || (current_remaining_bytes == 0))
|
||
return 0;
|
||
|
||
tx_remaining_bytes = current_remaining_bytes;
|
||
|
||
// printf("ready to write to fifo size - %d.\n", tx_remaining_bytes);
|
||
spi_get_status(NULL, NULL, &fifo_level);
|
||
|
||
// TLS_DBGPRT_SPI("\nfifo_level 0= %d\n",fifo_level);
|
||
rw_words =
|
||
((fifo_level > tx_remaining_bytes) ? tx_remaining_bytes : fifo_level) / 4;
|
||
rw_bytes =
|
||
((fifo_level > tx_remaining_bytes) ? tx_remaining_bytes : fifo_level) % 4;
|
||
|
||
// TLS_DBGPRT_SPI("write to spi fifo words - %d, bytes - %d.\n", rw_words,
|
||
// rw_bytes);
|
||
|
||
//�������17us
|
||
for (i = 0; i < rw_words; i++)
|
||
{
|
||
if (current_transfer->tx_buf)
|
||
{
|
||
if (SPI_BYTE_TRANSFER == spi_port->transtype)
|
||
{
|
||
data32 = 0;
|
||
data32 |= ((u8 *) current_transfer->tx_buf +
|
||
(current_transfer->len - tx_remaining_bytes))[0] << 24;
|
||
data32 |= ((u8 *) current_transfer->tx_buf +
|
||
(current_transfer->len - tx_remaining_bytes))[1] << 16;
|
||
data32 |= ((u8 *) current_transfer->tx_buf +
|
||
(current_transfer->len - tx_remaining_bytes))[2] << 8;
|
||
data32 |= ((u8 *) current_transfer->tx_buf +
|
||
(current_transfer->len - tx_remaining_bytes))[3] << 0;
|
||
}
|
||
else if (SPI_WORD_TRANSFER == spi_port->transtype)
|
||
{
|
||
data32 = *((u32 *) ((u8 *) current_transfer->tx_buf +
|
||
current_transfer->len - tx_remaining_bytes));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
data32 = 0xffffffff;
|
||
}
|
||
// TLS_DBGPRT_SPI("write to spi fifo word[%d]: 0x%x.\n", i, data32);
|
||
spi_data_put(data32);
|
||
tx_remaining_bytes -= 4;
|
||
}
|
||
|
||
if (rw_bytes)
|
||
{
|
||
data32 = 0;
|
||
for (i = 0; i < rw_bytes; i++)
|
||
{
|
||
if (current_transfer->tx_buf)
|
||
{
|
||
data8 = ((u8 *) current_transfer->tx_buf)[current_transfer->len -
|
||
tx_remaining_bytes];
|
||
}
|
||
else
|
||
{
|
||
data8 = 0xff;
|
||
}
|
||
if (SPI_BYTE_TRANSFER == spi_port->transtype)
|
||
data32 |= data8 << ((3 - i) * 8);
|
||
else if (SPI_WORD_TRANSFER == spi_port->transtype)
|
||
{
|
||
data32 |= data8 << (i * 8);
|
||
}
|
||
tx_remaining_bytes -= 1;
|
||
}
|
||
// TLS_DBGPRT_SPI("write to spi fifo word: 0x%x.\n", data32);
|
||
spi_data_put(data32);
|
||
}
|
||
|
||
return (current_remaining_bytes - tx_remaining_bytes);
|
||
}
|
||
|
||
static u32 spi_get_rxfifo(struct tls_spi_transfer *current_transfer,
|
||
u32 current_remaining_bytes)
|
||
{
|
||
u8 fifo_level;
|
||
u8 rw_words;
|
||
u8 rw_bytes;
|
||
u8 data8 = 0;
|
||
u8 i;
|
||
u32 data32;
|
||
u32 rx_remaining_bytes;
|
||
|
||
if ((current_transfer == NULL) || (current_remaining_bytes == 0))
|
||
return 0;
|
||
|
||
rx_remaining_bytes = current_remaining_bytes;
|
||
spi_get_status(NULL, &fifo_level, NULL);
|
||
// TLS_DBGPRT_SPI("rx fifo level: %d.\n", fifo_level);
|
||
|
||
rw_words = fifo_level / 4;
|
||
rw_bytes = fifo_level % 4;
|
||
|
||
// TLS_DBGPRT_SPI("rx data: %d words, %d bytes.\n", rw_words, rw_bytes);
|
||
|
||
//���������10us
|
||
for (i = 0; i < rw_words; i++)
|
||
{
|
||
|
||
data32 = spi_data_get();
|
||
|
||
// TLS_DBGPRT_SPI("rx data[%d](w): 0x%08x.\n", i, data32);
|
||
if (current_transfer->rx_buf)
|
||
{
|
||
|
||
if (SPI_BYTE_TRANSFER == spi_port->transtype)
|
||
{
|
||
data32 = swap_32(data32);
|
||
(((u8 *) current_transfer->rx_buf +
|
||
(current_transfer->len - rx_remaining_bytes)))[0] = (u8) data32;
|
||
(((u8 *) current_transfer->rx_buf +
|
||
(current_transfer->len - rx_remaining_bytes)))[1] = (u8) (data32 >> 8);
|
||
(((u8 *) current_transfer->rx_buf +
|
||
(current_transfer->len - rx_remaining_bytes)))[2] = (u8) (data32 >> 16);
|
||
(((u8 *) current_transfer->rx_buf +
|
||
(current_transfer->len - rx_remaining_bytes)))[3] = (u8) (data32 >> 24);
|
||
}
|
||
else if (SPI_WORD_TRANSFER == spi_port->transtype)
|
||
{
|
||
*((u32 *) ((u8 *) current_transfer->rx_buf +
|
||
current_transfer->len - rx_remaining_bytes)) = data32;
|
||
}
|
||
}
|
||
rx_remaining_bytes -= 4;
|
||
|
||
}
|
||
|
||
if (rw_bytes)
|
||
{
|
||
data32 = spi_data_get();
|
||
// TLS_DBGPRT_SPI("\nrx data=%x\n",data32);
|
||
if (current_transfer->rx_buf)
|
||
{
|
||
for (i = 0; i < rw_bytes; i++)
|
||
{
|
||
if (SPI_BYTE_TRANSFER == spi_port->transtype)
|
||
data8 = (u8) (data32 >> ((3 - i) * 8));
|
||
else if (SPI_WORD_TRANSFER == spi_port->transtype)
|
||
data8 = (u8) (data32 >> (i * 8));
|
||
|
||
// TLS_DBGPRT_SPI("rx data[%d](b): 0x%02x.\n", i, data8);
|
||
((u8 *) current_transfer->rx_buf)[current_transfer->len -rx_remaining_bytes] = data8;
|
||
rx_remaining_bytes -= 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
rx_remaining_bytes -= rw_bytes;
|
||
}
|
||
}
|
||
|
||
return (current_remaining_bytes - rx_remaining_bytes);
|
||
}
|
||
|
||
static struct tls_spi_transfer *spi_next_transfer(struct tls_spi_message
|
||
*current_message)
|
||
{
|
||
if (current_message == NULL)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
return dl_list_first(¤t_message->transfers, struct tls_spi_transfer, transfer_list);
|
||
}
|
||
|
||
static struct tls_spi_message *spi_next_message(void)
|
||
{
|
||
struct tls_spi_message *current_message;
|
||
|
||
current_message =
|
||
dl_list_first(&spi_port->wait_queue, struct tls_spi_message, queue);
|
||
if (current_message == NULL)
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
spi_port->current_transfer = spi_next_transfer(current_message);
|
||
current_message->status = SPI_MESSAGE_STATUS_INPROGRESS;
|
||
|
||
return current_message;
|
||
}
|
||
|
||
int gSpiCsFlag = 0;
|
||
static void spi_start_transfer(u32 transfer_bytes)
|
||
{
|
||
if (spi_port->reconfig)
|
||
{
|
||
TLS_DBGPRT_SPI_INFO("reconfig the spi master controller.\n");
|
||
spi_set_mode(spi_port->mode);
|
||
spi_set_chipselect_mode(spi_port->cs_active);
|
||
spi_set_sclk(spi_port->speed_hz);
|
||
|
||
spi_port->reconfig = 0;
|
||
}
|
||
|
||
spi_set_sclk_length(transfer_bytes * 8, 0);
|
||
// if(0 == gSpiCsFlag)
|
||
{
|
||
spi_set_chipselect_mode(SPI_CS_ACTIVE_MODE);
|
||
}
|
||
spi_sclk_start();
|
||
}
|
||
|
||
static void spi_continue_transfer(void)
|
||
{
|
||
struct tls_spi_message *current_message;
|
||
struct tls_spi_transfer *current_transfer;
|
||
u32 transfer_bytes;
|
||
|
||
current_message = spi_port->current_message;
|
||
current_transfer = spi_port->current_transfer;
|
||
if ((current_message == NULL) || (current_transfer == NULL))
|
||
{
|
||
return;
|
||
}
|
||
transfer_bytes =
|
||
spi_get_rxfifo(current_transfer, spi_port->current_remaining_bytes);
|
||
|
||
spi_port->current_remaining_bytes -= transfer_bytes;
|
||
if (spi_port->current_remaining_bytes == 0)
|
||
{
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
|
||
dl_list_del(¤t_transfer->transfer_list);
|
||
spi_port->current_transfer =
|
||
spi_next_transfer(spi_port->current_message);
|
||
if (spi_port->current_transfer == NULL)
|
||
{
|
||
// tls_sys_clk_set(CPU_CLK_40M);
|
||
spi_set_chipselect_mode(SPI_CS_INACTIVE_MODE);
|
||
current_message->status = SPI_MESSAGE_STATUS_DONE;
|
||
dl_list_del(¤t_message->queue);
|
||
spi_port->current_message = spi_next_message();
|
||
}
|
||
|
||
tls_os_sem_release(spi_port->lock);
|
||
|
||
// TLS_DBGPRT_SPI("get the next spi transfer pair.\n");
|
||
current_transfer = spi_port->current_transfer;
|
||
if (current_transfer != NULL)
|
||
{
|
||
spi_port->current_remaining_bytes = current_transfer->len;
|
||
}
|
||
}
|
||
|
||
transfer_bytes =
|
||
spi_fill_txfifo(current_transfer, spi_port->current_remaining_bytes);
|
||
|
||
if (transfer_bytes)
|
||
{
|
||
spi_start_transfer(transfer_bytes);
|
||
}
|
||
|
||
if (current_message->status == SPI_MESSAGE_STATUS_DONE)
|
||
{
|
||
// TLS_DBGPRT_SPI("current spi transaction finish and notify the
|
||
// submitter.\n");
|
||
current_message->complete(current_message->context);
|
||
}
|
||
}
|
||
|
||
static void spi_scheduler(void *data)
|
||
{
|
||
u8 err;
|
||
u32 msg;
|
||
u32 transfer_bytes;
|
||
struct tls_spi_transfer *current_transfer;
|
||
|
||
while (1)
|
||
{
|
||
err = tls_os_queue_receive(spi_port->msg_queue, (void **) &msg, 4, 0);
|
||
if (err == TLS_OS_SUCCESS)
|
||
{
|
||
switch (msg)
|
||
{
|
||
case SPI_SCHED_MSG_START_ENGINE:
|
||
// tls_sys_clk_set(CPU_CLK_80M); //80MHZ
|
||
if (spi_port->current_message)
|
||
{
|
||
TLS_DBGPRT_WARNING
|
||
("spi transaction scheduler is running now!\n");
|
||
break;
|
||
}
|
||
|
||
TLS_DBGPRT_SPI_INFO
|
||
("acquire the first transaction message in waiting queue.\n");
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
|
||
spi_port->current_message = spi_next_message();
|
||
|
||
tls_os_sem_release(spi_port->lock);
|
||
|
||
// TLS_DBGPRT_SPI("acquire the first transfer pair in the
|
||
// current transaction message.\n");
|
||
current_transfer = spi_port->current_transfer;
|
||
if (current_transfer == NULL)
|
||
{
|
||
break;
|
||
}
|
||
spi_port->current_remaining_bytes = current_transfer->len;
|
||
|
||
// TLS_DBGPRT_SPI("current transfer lenght - %d.\n",
|
||
// spi_port->current_remaining_bytes);
|
||
|
||
// TLS_DBGPRT_SPI("fill the tx fifo.\n");
|
||
|
||
transfer_bytes =
|
||
spi_fill_txfifo(current_transfer, spi_port->current_remaining_bytes);
|
||
|
||
// printf("start the spi transfer - %d.\n", transfer_bytes);
|
||
spi_start_transfer(transfer_bytes);
|
||
|
||
break;
|
||
|
||
case SPI_SCHED_MSG_TX_FIFO_READY:
|
||
TLS_DBGPRT_SPI_INFO("process SPI_SCHED_MSG_TX_FIFO_READY.\n");
|
||
break;
|
||
|
||
case SPI_SCHED_MSG_RX_FIFO_READY:
|
||
TLS_DBGPRT_SPI_INFO("process SPI_SCHED_MSG_RX_FIFO_READY.\n");
|
||
break;
|
||
|
||
case SPI_SCHED_MSG_TRANSFER_COMPLETE:
|
||
spi_continue_transfer();
|
||
break;
|
||
|
||
case SPI_SCHED_MSG_EXIT:
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void SPI_LS_IRQHandler(void)
|
||
{
|
||
|
||
u32 int_status;
|
||
u32 int_mask;
|
||
int_status = spi_get_int_status();
|
||
// printf("\nspi int sta=%x\n",int_status);
|
||
spi_clear_int_status(int_status);
|
||
|
||
int_mask = spi_int_mask();
|
||
int_status &= ~int_mask;
|
||
|
||
// printf("spi interrupt - 0x%x.\n", int_status);
|
||
|
||
if (int_status & SPI_INT_TX_FIFO_RDY)
|
||
{
|
||
tls_os_queue_send(spi_port->msg_queue,
|
||
(void *) SPI_SCHED_MSG_TX_FIFO_READY, 4);
|
||
}
|
||
|
||
if (int_status & SPI_INT_RX_FIFO_RDY)
|
||
{
|
||
tls_os_queue_send(spi_port->msg_queue,
|
||
(void *) SPI_SCHED_MSG_RX_FIFO_READY, 4);
|
||
}
|
||
|
||
if (int_status & SPI_INT_TRANSFER_DONE)
|
||
{
|
||
if (SPI_WORD_TRANSFER == spi_port->transtype)
|
||
spi_continue_transfer();
|
||
else
|
||
tls_os_queue_send(spi_port->msg_queue,
|
||
(void *) SPI_SCHED_MSG_TRANSFER_COMPLETE, 4);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief This function is used to setup the spi CPOL,CPHA,cs signal and clock.
|
||
*
|
||
* @param[in] mode is CPOL and CPHA type defined in TLS_SPI_MODE_0 to TLS_SPI_MODE_3
|
||
* @param[in] cs_active is cs mode, defined as TLS_SPI_CS_LOW or TLS_SPI_CS_HIGH
|
||
* @param[in] fclk is spi clock,the unit is HZ.
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if setup success
|
||
* @retval TLS_SPI_STATUS_EMODENOSUPPORT if mode is not support
|
||
* @retval TLS_SPI_STATUS_EINVAL if cs_active is not support
|
||
* @retval TLS_SPI_STATUS_ECLKNOSUPPORT if fclk is not support
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_setup(u8 mode, u8 cs_active, u32 fclk)
|
||
{
|
||
tls_sys_clk sysclk;
|
||
|
||
if ((spi_port->mode == mode) && (spi_port->cs_active == cs_active)
|
||
&& (spi_port->speed_hz == fclk))
|
||
{
|
||
TLS_DBGPRT_WARNING
|
||
("@mode, @cs_activer, @fclk is the same as settings of the current spi master driver!\n");
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
switch (mode)
|
||
{
|
||
case TLS_SPI_MODE_0:
|
||
case TLS_SPI_MODE_1:
|
||
case TLS_SPI_MODE_2:
|
||
case TLS_SPI_MODE_3:
|
||
spi_port->mode = mode;
|
||
break;
|
||
|
||
default:
|
||
TLS_DBGPRT_ERR("@mode is invalid!\n");
|
||
return TLS_SPI_STATUS_EMODENOSUPPORT;
|
||
}
|
||
|
||
if ((cs_active != TLS_SPI_CS_HIGH) && (cs_active != TLS_SPI_CS_LOW))
|
||
{
|
||
TLS_DBGPRT_ERR("@cs_active is invalid!\n");
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
else
|
||
{
|
||
spi_port->cs_active = cs_active;
|
||
}
|
||
|
||
tls_sys_clk_get(&sysclk);
|
||
|
||
if ((fclk < TLS_SPI_FCLK_MIN) || (fclk > sysclk.apbclk*UNIT_MHZ/2)) //TLS_SPI_FCLK_MAX
|
||
{
|
||
TLS_DBGPRT_ERR("@fclk is invalid!\n");
|
||
return TLS_SPI_STATUS_ECLKNOSUPPORT;
|
||
}
|
||
else
|
||
{
|
||
spi_port->speed_hz = fclk;
|
||
}
|
||
|
||
#ifdef SPI_USE_DMA
|
||
if (SPI_DMA_TRANSFER == spi_port->transtype)
|
||
{
|
||
SpiMasterInit(mode, TLS_SPI_CS_LOW, fclk);
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
#endif
|
||
|
||
spi_port->reconfig = 1;
|
||
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief This function is used to synchronous write command then read data by SPI.
|
||
*
|
||
* @param[in] txbuf is the write data buffer.
|
||
* @param[in] n_tx is the write data length.
|
||
* @param[in] rxbuf is the read data buffer.
|
||
* @param[in] n_rx is the read data length.
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if write success.
|
||
* @retval TLS_SPI_STATUS_EINVAL if argument is invalid.
|
||
* @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory.
|
||
* @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed.
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_read_with_cmd(const u8 * txbuf, u32 n_tx, u8 * rxbuf, u32 n_rx)
|
||
{
|
||
int status;
|
||
struct tls_spi_message message;
|
||
struct tls_spi_transfer x[2];
|
||
|
||
if ((txbuf == NULL) || (n_tx == 0) || (rxbuf == NULL) || (n_rx == 0))
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
|
||
#ifdef SPI_USE_DMA
|
||
if (SPI_DMA_TRANSFER == spi_port->transtype)
|
||
{
|
||
if (n_rx > SPI_DMA_BUF_MAX_SIZE || n_tx > SPI_DMA_CMD_MAX_SIZE)
|
||
{
|
||
TLS_DBGPRT_ERR("\nread length too long\n");
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_irq_disable(SPI0_INT);
|
||
|
||
MEMCPY((u8 *) SPI_DMA_CMD_ADDR, txbuf, n_tx);
|
||
SpiDmaBlockRead((u8 *) SPI_DMA_BUF_ADDR, n_rx, (u8 *) SPI_DMA_CMD_ADDR,
|
||
n_tx);
|
||
MEMCPY(rxbuf, (u8 *) SPI_DMA_BUF_ADDR, n_rx);
|
||
{
|
||
uint32_t int_status;
|
||
int_status = spi_get_int_status();
|
||
spi_clear_int_status(int_status);
|
||
tls_irq_enable(SPI_LS_IRQn);
|
||
}
|
||
|
||
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
#endif
|
||
|
||
spi_message_init(&message);
|
||
|
||
memset(x, 0, sizeof(x));
|
||
if (n_tx)
|
||
{
|
||
x[0].len = n_tx;
|
||
x[0].tx_buf = txbuf;
|
||
dl_list_add_tail(&message.transfers, &x[0].transfer_list);
|
||
}
|
||
if (n_rx)
|
||
{
|
||
x[1].len = n_rx;
|
||
x[1].rx_buf = rxbuf;
|
||
dl_list_add_tail(&message.transfers, &x[1].transfer_list);
|
||
}
|
||
|
||
/* do the i/o. */
|
||
status = tls_spi_sync(&message);
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief This function is used to synchronous read data by SPI.
|
||
*
|
||
* @param[in] buf is the buffer for saving SPI data.
|
||
* @param[in] len is the data length.
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if write success.
|
||
* @retval TLS_SPI_STATUS_EINVAL if argument is invalid.
|
||
* @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory.
|
||
* @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed.
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_read(u8 * buf, u32 len)
|
||
{
|
||
struct tls_spi_transfer t;
|
||
struct tls_spi_message m;
|
||
|
||
if ((buf == NULL) || (len == 0))
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
|
||
#ifdef SPI_USE_DMA
|
||
if (SPI_DMA_TRANSFER == spi_port->transtype)
|
||
{
|
||
u32 data32 = 0;
|
||
u16 rxBitLen;
|
||
u32 rdval1 = 0;
|
||
u32 i;
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_irq_disable(SPI_LS_IRQn);
|
||
// ֱ�Ӵ��䣬��������ԭ����DMA����������ȡ4���ֽ����ڵ�����,SPI FIFO��ȡ��λΪword
|
||
if (len <= 4)
|
||
{
|
||
SPIM_CHCFG_REG = SPI_CLEAR_FIFOS;
|
||
while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS);
|
||
|
||
rxBitLen = 8 * len;
|
||
rdval1 =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_CS_LOW | SPI_TX_CHANNEL_ON |
|
||
SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE | SPI_START |
|
||
SPI_VALID_CLKS_NUM(rxBitLen);
|
||
SPIM_CHCFG_REG = rdval1;
|
||
spiWaitIdle();
|
||
SPIM_CHCFG_REG |= SPI_CS_HIGH;
|
||
|
||
data32 = SPIM_RXDATA_REG;
|
||
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
*(buf + i) = (u8) (data32 >> i * 8);
|
||
}
|
||
SPIM_CHCFG_REG = 0x00000000;
|
||
SPIM_MODECFG_REG = 0x00000000;
|
||
}
|
||
else // DMA����
|
||
{
|
||
if (len > SPI_DMA_BUF_MAX_SIZE)
|
||
{
|
||
TLS_DBGPRT_ERR("\nread len too long\n");
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
SpiDmaBlockRead((u8 *) SPI_DMA_BUF_ADDR, len, NULL, 0);
|
||
MEMCPY(buf, (u8 *) SPI_DMA_BUF_ADDR, len);
|
||
}
|
||
{
|
||
uint32_t int_status;
|
||
int_status = spi_get_int_status();
|
||
spi_clear_int_status(int_status);
|
||
tls_irq_enable(SPI_LS_IRQn);
|
||
}
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
#endif
|
||
|
||
memset(&t, 0, sizeof(t));
|
||
t.rx_buf = buf;
|
||
t.len = len;
|
||
|
||
spi_message_init(&m);
|
||
|
||
dl_list_add_tail(&m.transfers, &t.transfer_list);
|
||
|
||
return tls_spi_sync(&m);
|
||
}
|
||
|
||
/**
|
||
* @brief This function is used to synchronous write data by SPI.
|
||
*
|
||
* @param[in] buf is the user data.
|
||
* @param[in] len is the data length.
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if write success.
|
||
* @retval TLS_SPI_STATUS_EINVAL if argument is invalid.
|
||
* @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory.
|
||
* @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed.
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_write(const u8 * buf, u32 len)
|
||
{
|
||
struct tls_spi_transfer t;
|
||
struct tls_spi_message m;
|
||
|
||
if ((buf == NULL) || (len == 0))
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
|
||
#ifdef SPI_USE_DMA
|
||
if (SPI_DMA_TRANSFER == spi_port->transtype)
|
||
{
|
||
u32 data32 = 0;
|
||
u16 txBitLen;
|
||
u32 rdval1 = 0;
|
||
u32 i;
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_irq_disable(SPI_LS_IRQn);
|
||
if (len <= 4) // ֱ�Ӵ��䣬��������ԭ����DMA����������������4���ֽڵ����ݣ�SPI
|
||
{
|
||
SPIM_CHCFG_REG = SPI_CLEAR_FIFOS;
|
||
while (SPIM_CHCFG_REG & SPI_CLEAR_FIFOS);
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
data32 |= (((u8) (buf[i])) << (i * 8));
|
||
}
|
||
SPIM_TXDATA_REG = data32;
|
||
txBitLen = 8 * len;
|
||
rdval1 =
|
||
SPI_FORCE_SPI_CS_OUT | SPI_CS_LOW | SPI_TX_CHANNEL_ON |
|
||
SPI_RX_CHANNEL_ON | SPI_CONTINUE_MODE | SPI_START |
|
||
SPI_VALID_CLKS_NUM(txBitLen);
|
||
SPIM_CHCFG_REG = rdval1;
|
||
spiWaitIdle();
|
||
SPIM_CHCFG_REG |= SPI_CS_HIGH;
|
||
|
||
SPIM_CHCFG_REG = 0x00000000;
|
||
SPIM_MODECFG_REG = 0x00000000;
|
||
}
|
||
else // DMA����
|
||
{
|
||
if (len > SPI_DMA_BUF_MAX_SIZE)
|
||
{
|
||
TLS_DBGPRT_ERR("\nwrite len too long\n");
|
||
{
|
||
uint32_t int_status;
|
||
int_status = spi_get_int_status();
|
||
spi_clear_int_status(int_status);
|
||
tls_irq_enable(SPI_LS_IRQn);
|
||
}
|
||
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
MEMCPY((u8 *) SPI_DMA_BUF_ADDR, buf, len);
|
||
SpiDmaBlockWrite((u8 *) SPI_DMA_BUF_ADDR, len, 0, 0);
|
||
}
|
||
{
|
||
uint32_t int_status;
|
||
int_status = spi_get_int_status();
|
||
spi_clear_int_status(int_status);
|
||
tls_irq_enable(SPI_LS_IRQn);
|
||
}
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
#endif
|
||
memset(&t, 0, sizeof(t));
|
||
t.tx_buf = buf;
|
||
t.len = len;
|
||
|
||
spi_message_init(&m);
|
||
|
||
dl_list_add_tail(&m.transfers, &t.transfer_list);
|
||
|
||
return tls_spi_sync(&m);
|
||
}
|
||
|
||
/**
|
||
* @brief This function is used to synchronous write 32bit command then write data by SPI.
|
||
*
|
||
* @param[in] cmd is the command data.
|
||
* @param[in] n_cmd is the command len,can not bigger than four
|
||
* @param[in] txbuf is the write data buffer.
|
||
* @param[in] n_tx is the write data length.
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if write success.
|
||
* @retval TLS_SPI_STATUS_EINVAL if argument is invalid.
|
||
* @retval TLS_SPI_STATUS_ENOMEM if there is no enough memory.
|
||
* @retval TLS_SPI_STATUS_ESHUTDOWN if SPI driver does not installed.
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_write_with_cmd(const u8 * cmd, u32 n_cmd, const u8 * txbuf,
|
||
u32 n_tx)
|
||
{
|
||
int status;
|
||
struct tls_spi_message message;
|
||
struct tls_spi_transfer x[2];
|
||
|
||
if ((cmd == NULL) || (n_cmd == 0) || (txbuf == NULL) || (n_tx == 0))
|
||
{
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
|
||
#ifdef SPI_USE_DMA
|
||
if (SPI_DMA_TRANSFER == spi_port->transtype)
|
||
{
|
||
if (n_tx > SPI_DMA_BUF_MAX_SIZE)
|
||
{
|
||
TLS_DBGPRT_ERR("\nwriten len too long\n");
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_irq_disable(SPI_LS_IRQn);
|
||
MEMCPY((u8 *) SPI_DMA_BUF_ADDR, (u8 *) cmd, n_cmd);
|
||
MEMCPY((u8 *) (SPI_DMA_BUF_ADDR + n_cmd), txbuf, n_tx);
|
||
SpiDmaBlockWrite((u8 *) SPI_DMA_BUF_ADDR, (n_cmd + n_tx), 0, 0);
|
||
{
|
||
uint32_t int_status;
|
||
int_status = spi_get_int_status();
|
||
spi_clear_int_status(int_status);
|
||
tls_irq_enable(SPI_LS_IRQn);
|
||
}
|
||
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
tls_os_sem_release(spi_port->lock);
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
#endif
|
||
|
||
spi_message_init(&message);
|
||
|
||
memset(x, 0, sizeof(x));
|
||
if (n_cmd)
|
||
{
|
||
x[0].len = n_cmd;
|
||
x[0].tx_buf = (const void *) cmd;
|
||
dl_list_add_tail(&message.transfers, &x[0].transfer_list);
|
||
}
|
||
if (n_tx)
|
||
{
|
||
x[1].len = n_tx;
|
||
x[1].tx_buf = txbuf;
|
||
dl_list_add_tail(&message.transfers, &x[1].transfer_list);
|
||
}
|
||
|
||
/* do the i/o. */
|
||
status = tls_spi_sync(&message);
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @param message
|
||
*
|
||
* @return
|
||
*/
|
||
int tls_spi_sync(struct tls_spi_message *message)
|
||
{
|
||
int status;
|
||
u8 err;
|
||
tls_os_sem_t *sem;
|
||
|
||
err = tls_os_sem_create(&sem, 0);
|
||
if (err != TLS_OS_SUCCESS)
|
||
{
|
||
TLS_DBGPRT_ERR
|
||
("create spi transaction synchronizing semaphore fail!\n");
|
||
return TLS_SPI_STATUS_ENOMEM;
|
||
}
|
||
|
||
message->context = (void *) sem;
|
||
message->complete = spi_complete;
|
||
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
status = tls_spi_async(message);
|
||
if (status == TLS_SPI_STATUS_OK)
|
||
{
|
||
TLS_DBGPRT_SPI_INFO("waiting spi transaction finishing!\n");
|
||
tls_os_sem_acquire(sem, 0);
|
||
}
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
|
||
tls_os_sem_delete(sem);
|
||
message->context = NULL;
|
||
message->complete = NULL;
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @param message
|
||
*
|
||
* @return
|
||
*/
|
||
int tls_spi_async(struct tls_spi_message *message)
|
||
{
|
||
u8 need_sched;
|
||
struct tls_spi_transfer *transfer;
|
||
|
||
if (spi_port == NULL)
|
||
{
|
||
TLS_DBGPRT_ERR("spi master driver module not beed installed!\n");
|
||
return TLS_SPI_STATUS_ESHUTDOWN;
|
||
}
|
||
|
||
if ((message == NULL) || (dl_list_empty(&message->transfers)))
|
||
{
|
||
TLS_DBGPRT_ERR("@message is NULL or @message->transfers is empty!\n");
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
|
||
dl_list_for_each(transfer, &message->transfers, struct tls_spi_transfer,
|
||
transfer_list)
|
||
{
|
||
if (transfer->len == 0)
|
||
{
|
||
TLS_DBGPRT_ERR("\"@transfer->len\" belong to @message is 0!\n");
|
||
return TLS_SPI_STATUS_EINVAL;
|
||
}
|
||
}
|
||
|
||
tls_os_sem_acquire(spi_port->lock, 0);
|
||
|
||
if (dl_list_empty(&spi_port->wait_queue))
|
||
{
|
||
need_sched = 1;
|
||
}
|
||
else
|
||
{
|
||
need_sched = 0;
|
||
}
|
||
message->status = SPI_MESSAGE_STATUS_IDLE;
|
||
dl_list_add_tail(&spi_port->wait_queue, &message->queue);
|
||
|
||
tls_os_sem_release(spi_port->lock);
|
||
|
||
if (need_sched == 1)
|
||
{
|
||
tls_os_queue_send(spi_port->msg_queue,
|
||
(void *) SPI_SCHED_MSG_START_ENGINE, 4);
|
||
}
|
||
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief This function is used to initialize the SPI master driver.
|
||
*
|
||
* @param[in] None
|
||
*
|
||
* @retval TLS_SPI_STATUS_OK if initialize success
|
||
* @retval TLS_SPI_STATUS_EBUSY if SPI is already initialized
|
||
* @retval TLS_SPI_STATUS_ENOMEM if malloc SPI memory fail
|
||
*
|
||
* @note None
|
||
*/
|
||
int tls_spi_init(void)
|
||
{
|
||
u8 err;
|
||
struct tls_spi_port *port;
|
||
|
||
|
||
if (spi_port != NULL)
|
||
{
|
||
TLS_DBGPRT_ERR("spi driver module has been installed!\n");
|
||
return TLS_SPI_STATUS_EBUSY;
|
||
}
|
||
|
||
TLS_DBGPRT_SPI_INFO("initialize spi master driver module.\n");
|
||
|
||
port = (struct tls_spi_port *) tls_mem_alloc(sizeof(struct tls_spi_port));
|
||
if (port == NULL)
|
||
{
|
||
TLS_DBGPRT_ERR("allocate \"struct tls_spi_port\" fail!\n");
|
||
return TLS_SPI_STATUS_ENOMEM;
|
||
}
|
||
|
||
err = tls_os_sem_create(&port->lock, 1);
|
||
if (err != TLS_OS_SUCCESS)
|
||
{
|
||
TLS_DBGPRT_ERR("create semaphore @spi_port->lock fail!\n");
|
||
tls_mem_free(port);
|
||
return TLS_SPI_STATUS_ENOMEM;
|
||
}
|
||
|
||
err = tls_os_queue_create(&port->msg_queue, MSG_QUEUE_SIZE);
|
||
if (err != TLS_OS_SUCCESS)
|
||
{
|
||
TLS_DBGPRT_ERR("create message queue @spi_port->msg_queue fail!\n");
|
||
tls_os_sem_delete(port->lock);
|
||
tls_mem_free(port);
|
||
return TLS_SPI_STATUS_ENOMEM;
|
||
}
|
||
|
||
err = tls_os_task_create(NULL, "HOSTSPI",
|
||
spi_scheduler,
|
||
(void *) spi_port,
|
||
(void *) &spi_scheduler_stk,
|
||
SPI_SCHEDULER_STK_SIZE * sizeof(u32),
|
||
TLS_SPI_SCHEDULER_TASK_PRIO, 0);
|
||
if (err != TLS_OS_SUCCESS)
|
||
{
|
||
TLS_DBGPRT_ERR("create spi master driver scheduler task fail!\n");
|
||
tls_os_sem_delete(port->lock);
|
||
tls_os_queue_delete(port->msg_queue);
|
||
tls_mem_free(port);
|
||
return TLS_SPI_STATUS_ENOMEM;
|
||
}
|
||
|
||
port->speed_hz = SPI_DEFAULT_SPEED; /* Ĭ��2M */
|
||
port->cs_active = SPI_CS_ACTIVE_MODE;
|
||
port->mode = SPI_DEFAULT_MODE; /* CPHA = 0,CPOL = 0 */
|
||
port->reconfig = 0;
|
||
|
||
dl_list_init(&port->wait_queue);
|
||
|
||
port->current_message = NULL;
|
||
port->current_remaining_transfer = 0;
|
||
port->current_transfer = NULL;
|
||
port->current_remaining_bytes = 0;
|
||
|
||
spi_port = port;
|
||
|
||
TLS_DBGPRT_SPI_INFO("initialize spi master controller.\n");
|
||
tls_open_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
spi_clear_fifo();
|
||
spi_set_endian(1);
|
||
tls_spi_trans_type(SPI_BYTE_TRANSFER);
|
||
spi_set_mode(spi_port->mode);
|
||
spi_set_chipselect_mode(SPI_CS_INACTIVE_MODE); /* cs=1 ,Ƭѡ��Ч */
|
||
spi_force_cs_out(1); /* Ƭѡ���������� */
|
||
spi_set_sclk(spi_port->speed_hz);
|
||
|
||
spi_set_tx_trigger_level(0);
|
||
spi_set_rx_trigger_level(7);
|
||
|
||
spi_set_rx_channel(1);
|
||
spi_set_tx_channel(1);
|
||
spi_unmask_int(SPI_INT_TRANSFER_DONE /* | SPI_INT_RX_FIFO_RDY |SPI_INT_TX_FIFO_RDY */ );
|
||
tls_close_peripheral_clock(TLS_PERIPHERAL_TYPE_LSPI);
|
||
|
||
TLS_DBGPRT_SPI_INFO("register spi master interrupt handler.\n");
|
||
|
||
tls_irq_enable(SPI0_INT);
|
||
tls_irq_set_priority(SPI0_INT, IRQ_MAX_SYSCALL_PRIORITY);
|
||
|
||
TLS_DBGPRT_SPI_INFO("spi master driver module initialization finish.\n");
|
||
|
||
return TLS_SPI_STATUS_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @return
|
||
*/
|
||
int tls_spi_exit(void)
|
||
{
|
||
TLS_DBGPRT_SPI_INFO("Not support spi master driver module uninstalled!\n");
|
||
return TLS_SPI_STATUS_EPERM;
|
||
}
|
||
|
||
|
||
|
||
/**********************************************************************************************************
|
||
* Description: This function is used to select SPI slave type.
|
||
*
|
||
* Arguments : slave is the slave type,defined as follow:
|
||
* slave == SPI_SLAVE_FLASH :flash
|
||
* slave == SPI_SLAVE_CARD : sd card
|
||
*
|
||
* Returns : Before communicate with different SPI device, must call the function.
|
||
**********************************************************************************************************/
|
||
void tls_spi_slave_sel(u16 slave)
|
||
{
|
||
// u16 ret;
|
||
/*gpio0����cs�ź�*/
|
||
tls_gpio_cfg((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, WM_GPIO_DIR_OUTPUT,
|
||
WM_GPIO_ATTR_FLOATING);
|
||
if (SPI_SLAVE_FLASH == slave)
|
||
{
|
||
tls_gpio_write((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, 0);
|
||
// ret = tls_gpio_read(SPI_SLAVE_CONTROL_PIN);
|
||
// printf("\nflash gpio 0 ===%d\n",ret);
|
||
}
|
||
else if (SPI_SLAVE_CARD == slave)
|
||
{
|
||
tls_gpio_write((enum tls_io_name) SPI_SLAVE_CONTROL_PIN, 1);
|
||
// ret = tls_gpio_read(SPI_SLAVE_CONTROL_PIN);
|
||
// printf("\ncard gpio 0 ===%d\n",ret);
|
||
}
|
||
}
|