kendryte-standalone-sdk/lib/drivers/spi.c

616 lines
19 KiB
C

/* Copyright 2018 Canaan Inc.
*
* 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.
*/
#include "platform.h"
#include "spi.h"
#include "fpioa.h"
#include "common.h"
#include "sysctl.h"
#include <stddef.h>
#include <stdlib.h>
#define SPI_MAX_NUM 4
volatile struct spi_t *const spi[4] = {
(volatile struct spi_t *)SPI0_BASE_ADDR,
(volatile struct spi_t *)SPI1_BASE_ADDR,
(volatile struct spi_t *)SPI_SLAVE_BASE_ADDR,
(volatile struct spi_t *)SPI3_BASE_ADDR
};
int spi_clk_init(uint8_t spi_bus){
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
sysctl_clock_enable(SYSCTL_CLOCK_SPI0 + spi_bus);
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_SPI0 + spi_bus, 0);
return 0;
}
int spi_init(uint8_t spi_bus){
spi_clk_init(spi_bus);
dmac_init();
return 0;
}
int spi_master_config(uint8_t spi_bus, spi_mode mode, spi_frame_format frame_format, size_t data_bit_length){
configASSERT(data_bit_length >= 4 && data_bit_length <= 32);
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
uint8_t dfs_offset, frf_offset;
switch(spi_bus){
case 0:
case 1:
dfs_offset = 16;
frf_offset = 21;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
dfs_offset = 0;
frf_offset = 22;
break;
}
switch (frame_format)
{
case SPI_FF_DUAL:
configASSERT(data_bit_length % 2 == 0);
break;
case SPI_FF_QUAD:
configASSERT(data_bit_length % 4 == 0);
break;
case SPI_FF_OCTAL:
configASSERT(data_bit_length % 8 == 0);
break;
default:
break;
}
volatile struct spi_t *spi_adapter = spi[spi_bus];
spi_adapter->baudr = 0x14;
spi_adapter->imr = 0x00;
spi_adapter->dmacr = 0x00;
spi_adapter->dmatdlr = 0x10;
spi_adapter->dmardlr = 0x00;
spi_adapter->ser = 0x00;
spi_adapter->ssienr = 0x00;
spi_adapter->ctrlr0 = (mode << 6) | (frame_format << frf_offset) | ((data_bit_length - 1) << dfs_offset);
spi_adapter->spi_ctrlr0 = 0;
return 0;
}
void spi_trans_config(uint8_t spi_bus, size_t instruction_length, size_t address_length,
size_t wait_cycles, spi_addr_inst_trans_mode trans_mode)
{
configASSERT(wait_cycles < (1 << 5));
configASSERT(trans_mode < 3);
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t inst_l;
switch (instruction_length)
{
case 0:
inst_l = 0;
break;
case 4:
inst_l = 1;
break;
case 8:
inst_l = 2;
break;
case 16:
inst_l = 3;
break;
default:
configASSERT(!"Invalid instruction length");
break;
}
configASSERT(address_length % 4 == 0 && address_length <= 60);
uint32_t addr_l = address_length / 4;
spi_handle->spi_ctrlr0 = (wait_cycles << 11) | (inst_l << 8) | (addr_l << 2) | trans_mode;
}
int spi_send_data(uint8_t spi_bus, uint32_t chip_sel, uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
{
uint32_t index, fifo_len;
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
spi_handle->ssienr = 0x01;
while (cmd_len){
spi_handle->dr[0] = *cmd_buff++;
cmd_len--;
}
fifo_len = 32 - spi_handle->txflr;
fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
for (index = 0; index < fifo_len; index++)
spi_handle->dr[0] = *tx_buff++;
tx_len -= fifo_len;
spi_handle->ser = chip_sel;
while (tx_len) {
fifo_len = 32 - spi_handle->txflr;
fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
for (index = 0; index < fifo_len; index++)
spi_handle->dr[0] = *tx_buff++;
tx_len -= fifo_len;
}
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_send_data_dma(dmac_channel_number channel_num, uint8_t spi_bus, uint32_t chip_sel,
uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t *buf = malloc((cmd_len + tx_len) * sizeof(uint32_t));
int i;
for(i = 0; i < cmd_len; i++){
buf[i] = cmd_buff[i];
}
for(i = 0; i < tx_len; i++){
buf[cmd_len + i] = tx_buff[i];
}
spi_handle->dmacr = 0x2; /*enable dma transmit*/
spi_handle->ssienr = 0x01;
sysctl_dma_select(channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
dmac_set_single_mode(channel_num, buf, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, cmd_len + tx_len);
spi_handle->ser = chip_sel;
dmac_wait_done(channel_num);
free((void*)buf);
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_normal_send_dma(dmac_channel_number channel_num, uint8_t spi_bus, uint32_t chip_sel,
void *tx_buff, uint32_t tx_len, spi_transfer_width stw)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t *buf = malloc((tx_len) * sizeof(uint32_t));
int i;
for(i = 0; i < tx_len; i++){
switch(stw){
case SPI_TRANS_SHORT:
buf[i] = ((uint16_t *)tx_buff)[i];
break;
case SPI_TRANS_INT:
buf[i] = ((uint32_t *)tx_buff)[i];
break;
break;
case SPI_TRANS_CHAR:
default:
buf[i] = ((uint8_t *)tx_buff)[i];
break;
}
}
spi_handle->dmacr = 0x2; /*enable dma transmit*/
spi_handle->ssienr = 0x01;
sysctl_dma_select(channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
dmac_set_single_mode(channel_num, buf, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, tx_len);
spi_handle->ser = chip_sel;
dmac_wait_done(channel_num);
free((void*)buf);
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_receive_data(uint8_t spi_bus, uint32_t chip_sel, uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
{
uint32_t index, fifo_len;
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
spi_handle->ctrlr1 = rx_len - 1;
spi_handle->ssienr = 0x01;
while (cmd_len--)
spi_handle->dr[0] = *cmd_buff++;
spi_handle->ser = chip_sel;
while (rx_len) {
fifo_len = spi_handle->rxflr;
fifo_len = fifo_len < rx_len ? fifo_len : rx_len;
for (index = 0; index < fifo_len; index++)
*rx_buff++ = spi_handle->dr[0];
rx_len -= fifo_len;
}
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_receive_data_dma(dmac_channel_number w_channel_num, dmac_channel_number r_channel_num,
uint8_t spi_bus, uint32_t chip_sel, uint8_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t * write_cmd = malloc(sizeof(uint32_t) * (cmd_len + rx_len));
size_t i;
for (i = 0; i < cmd_len; i++)
write_cmd[i] = cmd_buff[i];
spi_handle->ctrlr1 = rx_len - 1;
spi_handle->dmacr = 0x3;
spi_handle->ssienr = 0x01;
spi_handle->ser = chip_sel;
sysctl_dma_select(w_channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
sysctl_dma_select(r_channel_num, SYSCTL_DMA_SELECT_SSI0_RX_REQ + spi_bus * 2);
dmac_set_single_mode(r_channel_num, (void *)(&spi_handle->dr[0]), write_cmd, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, rx_len);
dmac_set_single_mode(w_channel_num, write_cmd, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, cmd_len);
dmac_wait_done(w_channel_num);
dmac_wait_done(r_channel_num);
for(i = 0; i < rx_len; i++){
rx_buff[i] = write_cmd[i];
}
free(write_cmd);
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_special_receive_data(uint8_t spi_bus, uint32_t chip_sel, uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
{
uint32_t index, fifo_len;
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
spi_handle->ctrlr1 = rx_len - 1;
spi_handle->ssienr = 0x01;
while (cmd_len--)
spi_handle->dr[0] = *cmd_buff++;
spi_handle->ser = chip_sel;
while (rx_len) {
fifo_len = spi_handle->rxflr;
fifo_len = fifo_len < rx_len ? fifo_len : rx_len;
for (index = 0; index < fifo_len; index++)
*rx_buff++ = spi_handle->dr[0];
rx_len -= fifo_len;
}
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_special_receive_data_dma(dmac_channel_number w_channel_num, dmac_channel_number r_channel_num,
uint8_t spi_bus, uint32_t chip_sel, uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *rx_buff, uint32_t rx_len)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t * write_cmd = malloc(sizeof(uint32_t) * (cmd_len + rx_len));
size_t i;
for (i = 0; i < cmd_len; i++)
write_cmd[i] = cmd_buff[i];
spi_handle->ctrlr1 = rx_len - 1;
spi_handle->dmacr = 0x3;
spi_handle->ssienr = 0x01;
spi_handle->ser = chip_sel;
sysctl_dma_select(w_channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
sysctl_dma_select(r_channel_num, SYSCTL_DMA_SELECT_SSI0_RX_REQ + spi_bus * 2);
dmac_set_single_mode(r_channel_num, (void *)(&spi_handle->dr[0]), write_cmd, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, rx_len);
dmac_set_single_mode(w_channel_num, write_cmd, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, cmd_len);
dmac_wait_done(w_channel_num);
dmac_wait_done(r_channel_num);
for(i = 0; i < rx_len; i++){
rx_buff[i] = write_cmd[i];
}
free(write_cmd);
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_special_send_data(uint8_t spi_bus, uint32_t chip_sel, uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
{
uint32_t index, fifo_len;
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
spi_handle->ssienr = 0x01;
while (cmd_len--)
spi_handle->dr[0] = *cmd_buff++;
fifo_len = 32 - spi_handle->txflr;
fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
for (index = 0; index < fifo_len; index++)
spi_handle->dr[0] = *tx_buff++;
tx_len -= fifo_len;
spi_handle->ser = chip_sel;
while (tx_len) {
fifo_len = 32 - spi_handle->txflr;
fifo_len = fifo_len < tx_len ? fifo_len : tx_len;
for (index = 0; index < fifo_len; index++)
spi_handle->dr[0] = *tx_buff++;
tx_len -= fifo_len;
}
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_special_send_data_dma(dmac_channel_number channel_num,uint8_t spi_bus, uint32_t chip_sel,
uint32_t *cmd_buff, uint8_t cmd_len, uint8_t *tx_buff, uint32_t tx_len)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t *buf = malloc((cmd_len + tx_len) * sizeof(uint32_t));
int i;
for(i = 0; i < cmd_len; i++){
buf[i] = cmd_buff[i];
}
for(i = 0; i < tx_len; i++){
buf[cmd_len + i] = tx_buff[i];
}
spi_handle->dmacr = 0x2; /*enable dma transmit*/
spi_handle->ssienr = 0x01;
sysctl_dma_select(channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
dmac_set_single_mode(channel_num, buf, (void *)(&spi_handle->dr[0]), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_32, cmd_len + tx_len);
spi_handle->ser = chip_sel;
dmac_wait_done(channel_num);
free((void*)buf);
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
int spi_fill_dma(dmac_channel_number channel_num,uint8_t spi_bus, uint32_t chip_sel,
uint32_t *cmd_buff, uint32_t cmd_len)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
spi_handle->dmacr = 0x2; /*enable dma transmit*/
spi_handle->ssienr = 0x01;
sysctl_dma_select(channel_num, SYSCTL_DMA_SELECT_SSI0_TX_REQ + spi_bus * 2);
dmac_set_single_mode(channel_num, cmd_buff, (void *)(&spi_handle->dr[0]), DMAC_ADDR_NOCHANGE, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, cmd_len);
spi_handle->ser = chip_sel;
dmac_wait_done(channel_num);
while ((spi_handle->sr & 0x05) != 0x04)
;
spi_handle->ser = 0x00;
spi_handle->ssienr = 0x00;
return 0;
}
void spi_set_tmod(uint8_t spi_bus, uint32_t tmod)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint8_t tmod_offset = 0;
switch(spi_bus){
case 0:
case 1:
tmod_offset = 8;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
tmod_offset = 10;
break;
}
set_bit(&spi_handle->ctrlr0, 3 << tmod_offset, tmod << tmod_offset);
}
void spi_set_frame_format(uint8_t spi_bus, uint32_t spi_frf)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint8_t frf_offset = 0;
switch(spi_bus){
case 0:
case 1:
frf_offset = 21;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
frf_offset = 22;
break;
}
set_bit(&spi_handle->ctrlr0, 3 << frf_offset, spi_frf << frf_offset);
}
int spi_get_frame_format(uint8_t spi_bus)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint8_t frf_offset = 0;
switch(spi_bus){
case 0:
case 1:
frf_offset = 21;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
frf_offset = 22;
break;
}
return ((spi_handle->ctrlr0 >> frf_offset) & 0x03);
}
/*
SPI MODE0-3
Serial Clock Polarity
Serial Clock Phase
*/
void spi_set_work_mode(uint8_t spi_bus, spi_mode mode)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
set_bit(&spi_handle->ctrlr0, 0x3 << 6, mode << 6);
}
void spi_set_frame_size(uint8_t spi_bus, uint32_t dfs)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint8_t dfs_offset;
switch(spi_bus){
case 0:
case 1:
dfs_offset = 16;
break;
case 2:
configASSERT(!"Spi Bus 2 Not Support!");
break;
case 3:
default:
dfs_offset = 0;
break;
}
int frame_format = spi_get_frame_format(spi_bus);
switch (frame_format)
{
case SPI_FF_DUAL:
configASSERT(dfs % 2 == 0);
break;
case SPI_FF_QUAD:
configASSERT(dfs % 4 == 0);
break;
case SPI_FF_OCTAL:
configASSERT(dfs % 8 == 0);
break;
default:
break;
}
set_bit(&spi_handle->ctrlr0, 0x1F << dfs_offset, (dfs-1) << dfs_offset);
}
void spi_set_wait_cycles(uint8_t spi_bus, uint32_t wcycles)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
configASSERT(wcycles < (1 << 5));
int frame_format = spi_get_frame_format(spi_bus);
configASSERT(frame_format != SPI_FF_STANDARD);
volatile struct spi_t *spi_handle = spi[spi_bus];
set_bit(&spi_handle->spi_ctrlr0, 0x1F << 11, wcycles << 11);
}
void spi_set_inst_length(uint8_t spi_bus, uint32_t instruction_length)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
int frame_format = spi_get_frame_format(spi_bus);
configASSERT(frame_format != SPI_FF_STANDARD);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t inst_l = 0;
switch (instruction_length)
{
case 0:
inst_l = 0;
break;
case 4:
inst_l = 1;
break;
case 8:
inst_l = 2;
break;
case 16:
inst_l = 3;
break;
default:
configASSERT("Invalid instruction length");
break;
}
set_bit(&spi_handle->spi_ctrlr0, 0x3 << 8, inst_l << 8);
}
void spi_set_address_length(uint8_t spi_bus, uint32_t address_length)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
int frame_format = spi_get_frame_format(spi_bus);
configASSERT(frame_format != SPI_FF_STANDARD);
configASSERT(address_length % 4 == 0 && address_length <= 60);
volatile struct spi_t *spi_handle = spi[spi_bus];
uint32_t addr_l = address_length / 4;
set_bit(&spi_handle->spi_ctrlr0, 0xF << 2, addr_l << 2);
}
void spi_set_trans_mode(uint8_t spi_bus, spi_addr_inst_trans_mode trans_mode)
{
configASSERT(spi_bus < SPI_MAX_NUM && spi_bus != 2);
int frame_format = spi_get_frame_format(spi_bus);
configASSERT(frame_format != SPI_FF_STANDARD);
volatile struct spi_t *spi_handle = spi[spi_bus];
set_bit(&spi_handle->spi_ctrlr0, 0x3 << 0, trans_mode << 0);
}