mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2025-11-16 04:24:33 +00:00
Requirement: The BSP for the k230 platform in the RT-Thread repository does not yet have an spi driver. Solution: Provide spi driver for the k230 platform in the RT-Thread repository. - Supports SPI0(OSPI) controller with 1/2/4/8 data lines. - Supports SPI1(QSPI0) and SPI2(QSPI1) controllers with 1/2/4 data lines. - Implements DMA-based transfers for OSPI, QSPI, and DSPI modes. - Falls back to standard IRQ-driven transfers for legacy SPI mode (single line). - Updates documentation in bsp/README.md Signed-off-by: ChuanN-sudo <fjchuanil@gmail.com>
735 lines
24 KiB
C
735 lines
24 KiB
C
/* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions are met:
|
||
* 1. Redistributions of source code must retain the above copyright
|
||
* notice, this list of conditions and the following disclaimer.
|
||
* 2. Redistributions in binary form must reproduce the above copyright
|
||
* notice, this list of conditions and the following disclaimer in the
|
||
* documentation and/or other materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
/*
|
||
* Copyright (c) 2006-2025 RT-Thread Development Team
|
||
*
|
||
* SPDX-License-Identifier: Apache-2.0
|
||
*/
|
||
|
||
#include <rtthread.h>
|
||
#include <rtdevice.h>
|
||
|
||
#include "drv_spi.h"
|
||
#include <drivers/dev_spi.h>
|
||
#include <string.h>
|
||
#include "drv_gpio.h"
|
||
#include "riscv_io.h"
|
||
#include "board.h"
|
||
#include "ioremap.h"
|
||
#include "sysctl_rst.h"
|
||
#include "sysctl_clk.h"
|
||
#include "cache.h"
|
||
|
||
#define DBG_TAG "spi"
|
||
#include <rtdbg.h>
|
||
|
||
struct k230_spi_dev
|
||
{
|
||
struct rt_spi_bus dev;
|
||
void *base;
|
||
const char *name;
|
||
const char *event_name;
|
||
rt_ubase_t pa_base;
|
||
rt_uint32_t size;
|
||
rt_uint8_t idx;
|
||
rt_uint8_t rdse;
|
||
rt_uint8_t rdsd;
|
||
rt_uint8_t max_line;
|
||
rt_uint32_t max_hz;
|
||
struct rt_event event;
|
||
void *send_buf;
|
||
void *recv_buf;
|
||
rt_size_t send_length;
|
||
rt_size_t recv_length;
|
||
rt_uint8_t cell_size;
|
||
int vector;
|
||
};
|
||
|
||
static rt_err_t k230_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
|
||
{
|
||
RT_ASSERT(device != RT_NULL);
|
||
RT_ASSERT(configuration != RT_NULL);
|
||
struct k230_spi_dev *qspi_bus = (struct k230_spi_dev *)device->bus;
|
||
k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base;
|
||
struct rt_qspi_device *dev = (struct rt_qspi_device *)device;
|
||
struct rt_qspi_configuration *qspi_cfg = &dev->config;
|
||
struct rt_spi_configuration *qspi_cfg_parent = &dev->config.parent;
|
||
|
||
rt_uint8_t dfs, mode, spi_ff;
|
||
rt_uint32_t max_hz, qspi_clk;
|
||
if (qspi_cfg->qspi_dl_width > qspi_bus->max_line || qspi_cfg->qspi_dl_width == 0)
|
||
{
|
||
return -RT_EINVAL;
|
||
}
|
||
if (qspi_cfg_parent->data_width < 4 || qspi_cfg_parent->data_width > 32)
|
||
{
|
||
return -RT_EINVAL;
|
||
}
|
||
|
||
/* Check if the clock frequency exceeds the hardware limits */
|
||
max_hz = qspi_cfg_parent->max_hz;
|
||
if (max_hz > qspi_bus->max_hz)
|
||
{
|
||
max_hz = qspi_bus->max_hz;
|
||
}
|
||
|
||
/* Get QSPI Controller Clock Frequency */
|
||
if (qspi_bus->idx == 0)
|
||
{
|
||
qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI0);
|
||
}
|
||
else if (qspi_bus->idx == 1)
|
||
{
|
||
qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI1);
|
||
}
|
||
else if (qspi_bus->idx == 2)
|
||
{
|
||
qspi_clk = sysctl_clk_get_leaf_freq(SYSCTL_CLK_SSI2);
|
||
}
|
||
else
|
||
{
|
||
return -RT_EINVAL;
|
||
}
|
||
|
||
/* Set SPI_FRF to config SPI mode*/
|
||
if (qspi_cfg->qspi_dl_width == 1)
|
||
{
|
||
spi_ff = SPI_FRF_STD_SPI;
|
||
}
|
||
else if (qspi_cfg->qspi_dl_width == 2)
|
||
{
|
||
spi_ff = SPI_FRF_DUAL_SPI;
|
||
}
|
||
else if (qspi_cfg->qspi_dl_width == 4)
|
||
{
|
||
spi_ff = SPI_FRF_QUAD_SPI;
|
||
}
|
||
else if (qspi_cfg->qspi_dl_width == 8)
|
||
{
|
||
spi_ff = SPI_FRF_OCT_SPI;
|
||
}
|
||
else
|
||
{
|
||
return -RT_EINVAL;
|
||
}
|
||
/*
|
||
* dfs: Data Frame Size, Write dfs into bits 8-9 of the register ctrlr0
|
||
* mode: SPI mode(CPOL and CPHA),Write mode into bits 0-4 of the register ctrlr0
|
||
*/
|
||
mode = qspi_cfg_parent->mode & RT_SPI_MODE_3;
|
||
dfs = qspi_cfg_parent->data_width - 1;
|
||
|
||
qspi_reg->ssienr = 0;
|
||
qspi_reg->ser = 0;
|
||
qspi_reg->baudr = qspi_clk / max_hz;
|
||
qspi_reg->rx_sample_delay = qspi_bus->rdse << 16 | qspi_bus->rdsd;
|
||
qspi_reg->axiawlen = SSIC_AXI_BLW << 8;
|
||
qspi_reg->axiarlen = SSIC_AXI_BLW << 8;
|
||
qspi_reg->ctrlr0 = (dfs) | (mode << 8) | (spi_ff << 22);
|
||
return RT_EOK;
|
||
}
|
||
|
||
static rt_ssize_t k230_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
||
{
|
||
struct k230_spi_dev *qspi_bus = (struct k230_spi_dev *)device->bus;
|
||
k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base;
|
||
struct rt_qspi_device *dev = (struct rt_qspi_device *)device;
|
||
struct rt_qspi_configuration *qspi_cfg = &dev->config;
|
||
struct rt_spi_configuration *qspi_cfg_parent = &dev->config.parent;
|
||
struct rt_qspi_message *msg = (struct rt_qspi_message *)message;
|
||
struct rt_spi_message *msg_parent = message;
|
||
/* Multi-line SPI transfers (2, 4, or 8 lines) DSPI QSPI OSPI */
|
||
if (msg->qspi_data_lines > 1)
|
||
{
|
||
rt_uint8_t trans_type = 0;
|
||
if (msg->qspi_data_lines > qspi_cfg->qspi_dl_width)
|
||
{
|
||
LOG_E("data line is invalid");
|
||
return 0;
|
||
}
|
||
/* Check other parameters */
|
||
if (qspi_cfg_parent->data_width & (msg->qspi_data_lines - 1))
|
||
{
|
||
LOG_E("data line and data width do not match");
|
||
return 0;
|
||
}
|
||
if (msg->instruction.qspi_lines != 1 && msg->instruction.qspi_lines != msg->qspi_data_lines)
|
||
{
|
||
LOG_E("instruction line is invalid");
|
||
return 0;
|
||
}
|
||
if (msg->address.size & 3 || msg->address.size > 32)
|
||
{
|
||
LOG_E("address size is invalid");
|
||
return 0;
|
||
}
|
||
if (msg->address.size && msg->address.qspi_lines != 1 && msg->address.qspi_lines != msg->qspi_data_lines)
|
||
{
|
||
LOG_E("address line is invalid");
|
||
return 0;
|
||
}
|
||
if (msg_parent->length > 0x10000)
|
||
{
|
||
LOG_E("data length is invalid, more than 0x10000");
|
||
return 0;
|
||
}
|
||
if (msg->instruction.qspi_lines != 1)
|
||
{
|
||
trans_type = 2;
|
||
}
|
||
if (msg->address.size)
|
||
{
|
||
if (msg->address.qspi_lines != 1)
|
||
{
|
||
trans_type = trans_type ? trans_type : 1;
|
||
|
||
}
|
||
else if (trans_type != 0)
|
||
{
|
||
LOG_E("instruction or address line is invalid");
|
||
return 0;
|
||
}
|
||
}
|
||
if (msg->dummy_cycles > 31)
|
||
{
|
||
LOG_E("dummy cycle is invalid");
|
||
return 0;
|
||
}
|
||
|
||
rt_uint8_t tmod = msg_parent->recv_buf ? SPI_TMOD_RO : SPI_TMOD_TO;
|
||
rt_size_t length = msg_parent->length;
|
||
rt_size_t txfthr = length > (SSIC_TX_ABW / 2) ? (SSIC_TX_ABW / 2) : length - 1;
|
||
rt_uint8_t cell_size = (qspi_cfg_parent->data_width + 7) >> 3;
|
||
rt_uint8_t *buf = RT_NULL;
|
||
/* Allocate buffer for DMA transfer */
|
||
if (length)
|
||
{
|
||
buf = rt_malloc_align(CACHE_ALIGN_TOP(length * cell_size), L1_CACHE_BYTES);
|
||
if (buf == RT_NULL)
|
||
{
|
||
LOG_E("alloc mem error");
|
||
return 0;
|
||
}
|
||
}
|
||
/* msg->address.size & ~0x03: Round address.size down to the nearest multiple of 4 and write it to ADDR_L[5:2] */
|
||
qspi_reg->spi_ctrlr0 = trans_type | (msg->address.size & ~0x03) | (0x2 << 8) | (msg->dummy_cycles << 11);
|
||
qspi_reg->ctrlr0 &= ~((3 << 22) | (3 << 10));
|
||
|
||
/* Config SPI frame format and transmission mode */
|
||
if (length)
|
||
{
|
||
qspi_reg->ctrlr0 |= (tmod << 10);
|
||
qspi_reg->txftlr = (txfthr << 16) | (SSIC_TX_ABW / 2);
|
||
qspi_reg->rxftlr = (SSIC_RX_ABW - 1);
|
||
qspi_reg->imr = (1 << 11) | (1 << 8);
|
||
qspi_reg->dmacr = (1 << 6) | (3 << 3) | (1 << 2);
|
||
qspi_reg->ctrlr1 = length - 1;
|
||
qspi_reg->spidr = msg->instruction.content;
|
||
qspi_reg->spiar = msg->address.content;
|
||
if (tmod == SPI_TMOD_TO)
|
||
{
|
||
rt_memcpy(buf, msg_parent->send_buf, length * cell_size);
|
||
rt_hw_cpu_dcache_clean(buf, CACHE_ALIGN_TOP(length * cell_size));
|
||
}
|
||
qspi_reg->axiar0 = (rt_uint32_t)((uint64_t)buf);
|
||
qspi_reg->axiar1 = (rt_uint32_t)((uint64_t)buf >> 32);
|
||
}
|
||
else
|
||
{
|
||
tmod = SPI_TMOD_TO;
|
||
qspi_reg->ctrlr0 |= (tmod << 10);
|
||
qspi_reg->txftlr = ((SSIC_TX_ABW - 1) << 16) | (SSIC_TX_ABW - 1);
|
||
qspi_reg->rxftlr = (SSIC_RX_ABW - 1);
|
||
qspi_reg->imr = 0;
|
||
qspi_reg->dmacr = 0;
|
||
}
|
||
rt_event_control(&qspi_bus->event, RT_IPC_CMD_RESET, 0);
|
||
|
||
qspi_reg->ser = 1;
|
||
qspi_reg->ssienr = 1;
|
||
rt_uint32_t event;
|
||
rt_err_t err;
|
||
|
||
/*
|
||
* Config QSPI address and instruction,
|
||
* if data is empty, unable dma and send address and instruction by write data register.
|
||
*/
|
||
if (length)
|
||
{
|
||
err = rt_event_recv(&qspi_bus->event, BIT(SSI_DONE) | BIT(SSI_AXIE),
|
||
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &event);
|
||
}
|
||
else
|
||
{
|
||
err = RT_EOK;
|
||
event = 0;
|
||
qspi_reg->dr[0] = msg->instruction.content;
|
||
length++;
|
||
if (msg->address.size)
|
||
{
|
||
qspi_reg->dr[0] = msg->address.content;
|
||
length++;
|
||
}
|
||
qspi_reg->txftlr = 0;
|
||
/* Wait for SPI transfer to complete (busy-wait) */
|
||
while ((qspi_reg->sr & 0x5) != 0x4)
|
||
{
|
||
/* Busy wait */
|
||
}
|
||
}
|
||
qspi_reg->ser = 0;
|
||
qspi_reg->ssienr = 0;
|
||
if (err == -RT_ETIMEOUT)
|
||
{
|
||
LOG_E("qspi%d transfer data timeout", qspi_bus->idx);
|
||
if (buf)
|
||
{
|
||
rt_free_align(buf);
|
||
}
|
||
return 0;
|
||
}
|
||
if (event & BIT(SSI_AXIE))
|
||
{
|
||
LOG_E("qspi%d dma error", qspi_bus->idx);
|
||
if (buf)
|
||
{
|
||
rt_free_align(buf);
|
||
}
|
||
return 0;
|
||
}
|
||
/* Read data from FIFO */
|
||
if (tmod == SPI_TMOD_RO)
|
||
{
|
||
rt_hw_cpu_dcache_invalidate(buf, CACHE_ALIGN_TOP(length * cell_size));
|
||
rt_memcpy(msg_parent->recv_buf, buf, length * cell_size);
|
||
}
|
||
if (buf)
|
||
{
|
||
rt_free_align(buf);
|
||
}
|
||
return length;
|
||
}
|
||
/* Standard SPI transfers */
|
||
else
|
||
{
|
||
if (msg_parent->length == 0)
|
||
{
|
||
return 0;
|
||
}
|
||
rt_uint8_t cell_size = (qspi_cfg_parent->data_width + 7) >> 3;
|
||
rt_size_t length = msg_parent->length;
|
||
rt_size_t count = length > 0x10000 ? 0x10000 : length;
|
||
rt_size_t send_single = 0, send_length = 0, recv_single = 0, recv_length = 0;
|
||
void *send_buf = (void *)msg_parent->send_buf;
|
||
void *recv_buf = msg_parent->recv_buf;
|
||
rt_uint8_t tmod = send_buf ? SPI_TMOD_TO : SPI_TMOD_EPROMREAD;
|
||
tmod = recv_buf ? tmod & SPI_TMOD_RO : tmod;
|
||
/* Check Qspi Parameters */
|
||
if (tmod == SPI_TMOD_EPROMREAD)
|
||
{
|
||
LOG_E("send_buf and recv_buf cannot both be empty");
|
||
return 0;
|
||
}
|
||
if (tmod == SPI_TMOD_RO && qspi_cfg_parent->data_width == 8)
|
||
{
|
||
if ((msg->address.size & 7) || (msg->dummy_cycles & 7))
|
||
{
|
||
LOG_E("instruction, address, dummy_cycles invalid");
|
||
LOG_E("For read-only mode the instruction, address, dummy_cycles must be set to zero");
|
||
LOG_E("For eeprom-read mode the instruction, address, dummy_cycles must be set to multiples of 8");
|
||
return 0;
|
||
}
|
||
else if (msg->address.size)
|
||
{
|
||
if (length > 0x10000)
|
||
{
|
||
LOG_E("For eeprom-read mode, data length cannot exceed 0x10000");
|
||
return 0;
|
||
}
|
||
tmod = SPI_TMOD_EPROMREAD;
|
||
}
|
||
}
|
||
/* Prepare the send buffer*/
|
||
if (send_buf)
|
||
{
|
||
send_single = count;
|
||
send_buf = rt_malloc(count * cell_size);
|
||
if (send_buf == RT_NULL)
|
||
{
|
||
LOG_E("alloc mem error");
|
||
return 0;
|
||
}
|
||
rt_memcpy(send_buf, msg_parent->send_buf, count * cell_size);
|
||
}
|
||
else if (tmod == SPI_TMOD_EPROMREAD)
|
||
{
|
||
send_single = 1 + msg->address.size / 8 + msg->dummy_cycles / 8;
|
||
send_buf = rt_malloc(send_single);
|
||
if (send_buf == RT_NULL)
|
||
{
|
||
LOG_E("alloc mem error");
|
||
return 0;
|
||
}
|
||
rt_uint8_t *temp = send_buf;
|
||
*temp++ = msg->instruction.content;
|
||
for (int i = msg->address.size / 8; i; i--)
|
||
{
|
||
*temp++ = msg->address.content >> ((i - 1) * 8);
|
||
}
|
||
for (int i = msg->dummy_cycles / 8; i; i--)
|
||
{
|
||
*temp++ = 0xFF;
|
||
}
|
||
}
|
||
/* Prepare the receive buffer*/
|
||
if (recv_buf)
|
||
{
|
||
recv_single = count;
|
||
recv_buf = rt_malloc(count * cell_size);
|
||
if (recv_buf == RT_NULL)
|
||
{
|
||
LOG_E("alloc mem error");
|
||
if (send_buf)
|
||
{
|
||
rt_free(send_buf);
|
||
}
|
||
return 0;
|
||
}
|
||
}
|
||
send_length = 0;
|
||
recv_length = 0;
|
||
qspi_bus->cell_size = cell_size;
|
||
qspi_bus->send_buf = send_buf;
|
||
qspi_bus->recv_buf = recv_buf;
|
||
qspi_bus->send_length = send_single;
|
||
qspi_bus->recv_length = recv_single;
|
||
qspi_reg->ctrlr0 &= ~((3 << 22) | (3 << 10));
|
||
qspi_reg->ctrlr0 |= (tmod << 10);
|
||
qspi_reg->ctrlr1 = count - 1;
|
||
qspi_reg->txftlr = ((SSIC_TX_ABW / 2) << 16) | (SSIC_TX_ABW / 2);
|
||
qspi_reg->rxftlr = count >= (SSIC_RX_ABW / 2) ? (SSIC_RX_ABW / 2 - 1) : count - 1;
|
||
qspi_reg->dmacr = 0;
|
||
/* Interrupt transmit or receive */
|
||
qspi_reg->imr = (1 << 4) | (1 << 0);
|
||
rt_event_control(&qspi_bus->event, RT_IPC_CMD_RESET, 0);
|
||
qspi_reg->ser = 1;
|
||
qspi_reg->ssienr = 1;
|
||
if (tmod == SPI_TMOD_RO)
|
||
qspi_reg->dr[0] = 0;
|
||
rt_uint32_t event;
|
||
rt_err_t err;
|
||
while (RT_TRUE)
|
||
{
|
||
/* Waiting for transfer events */
|
||
err = rt_event_recv(&qspi_bus->event, BIT(SSI_TXE) | BIT(SSI_RXF),
|
||
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 10000, &event);
|
||
if (err == -RT_ETIMEOUT)
|
||
{
|
||
LOG_E("qspi%d transfer data timeout", qspi_bus->idx);
|
||
length = 0;
|
||
if (send_buf)
|
||
{
|
||
rt_free(send_buf);
|
||
}
|
||
if (recv_buf)
|
||
{
|
||
rt_free(recv_buf);
|
||
}
|
||
return 0;
|
||
}
|
||
/* Handle Transmit buffer empty */
|
||
if (event & BIT(SSI_TXE))
|
||
{
|
||
send_length += send_single;
|
||
if (send_length < length && tmod <= SPI_TMOD_TO)
|
||
{
|
||
count = length - send_length;
|
||
count = count > 0x10000 ? 0x10000 : count;
|
||
rt_memcpy(send_buf, msg_parent->send_buf + send_length * cell_size, count * cell_size);
|
||
qspi_bus->send_buf = send_buf;
|
||
qspi_bus->send_length = count;
|
||
send_single = count;
|
||
qspi_reg->txftlr = ((SSIC_TX_ABW / 2) << 16) | (SSIC_TX_ABW / 2);
|
||
if (tmod == SPI_TMOD_TO)
|
||
qspi_reg->imr |= (1 << 0);
|
||
}
|
||
else if (tmod == SPI_TMOD_TO)
|
||
{
|
||
/* Wait for SPI transfer to complete (busy-wait) */
|
||
while ((qspi_reg->sr & 0x5) != 0x4)
|
||
{
|
||
/* Busy wait */
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
/* Handle receive buffer full */
|
||
if (event & BIT(SSI_RXF))
|
||
{
|
||
rt_memcpy(msg_parent->recv_buf + recv_length * cell_size, recv_buf, recv_single * cell_size);
|
||
recv_length += recv_single;
|
||
if (recv_length >= length)
|
||
{
|
||
break;
|
||
}
|
||
count = length - recv_length;
|
||
count = count > 0x10000 ? 0x10000 : count;
|
||
qspi_bus->recv_buf = recv_buf;
|
||
qspi_bus->recv_length = count;
|
||
recv_single = count;
|
||
qspi_reg->rxftlr = count >= (SSIC_RX_ABW / 2) ? (SSIC_RX_ABW / 2 - 1) : count - 1;
|
||
if (tmod == SPI_TMOD_TR)
|
||
{
|
||
qspi_reg->imr |= (1 << 0) | (1 << 4);
|
||
}
|
||
else if (tmod == SPI_TMOD_RO)
|
||
{
|
||
qspi_reg->imr |= (1 << 4);
|
||
qspi_reg->ssienr = 0;
|
||
qspi_reg->ctrlr1 = count - 1;
|
||
qspi_reg->ssienr = 1;
|
||
/* Trigger two dummy transfers to restart SPI read in read-only mode.
|
||
* This is required by the hardware to ensure correct data reception.
|
||
*/
|
||
qspi_reg->dr[0] = 0;
|
||
qspi_reg->dr[0] = 0;
|
||
}
|
||
}
|
||
}
|
||
qspi_reg->ser = 0;
|
||
qspi_reg->ssienr = 0;
|
||
if (send_buf)
|
||
{
|
||
rt_free(send_buf);
|
||
}
|
||
if (recv_buf)
|
||
{
|
||
rt_free(recv_buf);
|
||
}
|
||
return length;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static const struct rt_spi_ops k230_qspi_ops =
|
||
{
|
||
.configure = k230_spi_configure,
|
||
.xfer = k230_spi_xfer,
|
||
};
|
||
|
||
static void k230_spi_irq(int vector, void *param)
|
||
{
|
||
struct k230_spi_dev *qspi_bus = param;
|
||
k230_spi_reg_t *qspi_reg = (k230_spi_reg_t *)qspi_bus->base;
|
||
vector -= IRQN_SPI0;
|
||
vector %= (IRQN_SPI1 - IRQN_SPI0);
|
||
|
||
/* Handle transmit buffer empty interrupt */
|
||
if (vector == SSI_TXE)
|
||
{
|
||
if (qspi_bus->send_buf == RT_NULL)
|
||
{
|
||
qspi_reg->imr &= ~1;
|
||
}
|
||
else if (qspi_bus->cell_size == 1)
|
||
{
|
||
while ((qspi_bus->send_length) && (qspi_reg->sr & 2))
|
||
{
|
||
qspi_reg->dr[0] = *((rt_uint8_t *)qspi_bus->send_buf);
|
||
qspi_bus->send_buf++;
|
||
qspi_bus->send_length--;
|
||
}
|
||
}
|
||
else if (qspi_bus->cell_size == 2)
|
||
{
|
||
while ((qspi_bus->send_length) && (qspi_reg->sr & 2))
|
||
{
|
||
qspi_reg->dr[0] = *((rt_uint16_t *)qspi_bus->send_buf);
|
||
qspi_bus->send_buf += 2;
|
||
qspi_bus->send_length--;
|
||
}
|
||
}
|
||
else if (qspi_bus->cell_size == 4)
|
||
{
|
||
while ((qspi_bus->send_length) && (qspi_reg->sr & 2))
|
||
{
|
||
qspi_reg->dr[0] = *((rt_uint32_t *)qspi_bus->send_buf);
|
||
qspi_bus->send_buf += 4;
|
||
qspi_bus->send_length--;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOG_E("qspi%d datawidth error", qspi_bus->idx);
|
||
}
|
||
if (qspi_bus->send_length == 0)
|
||
{
|
||
if (((qspi_reg->ctrlr0 >> 10) & SPI_TMOD_EPROMREAD) == SPI_TMOD_TO)
|
||
{
|
||
if (qspi_reg->txftlr)
|
||
return;
|
||
}
|
||
qspi_reg->txftlr = 0;
|
||
qspi_reg->imr &= ~1;
|
||
rt_event_send(&qspi_bus->event, BIT(SSI_TXE));
|
||
}
|
||
}
|
||
/* Handle receive buffer full interrupt */
|
||
else if (vector == SSI_RXF)
|
||
{
|
||
if (qspi_bus->recv_buf == RT_NULL)
|
||
{
|
||
qspi_reg->imr &= ~0x10;
|
||
}
|
||
else if (qspi_bus->cell_size == 1)
|
||
{
|
||
while ((qspi_bus->recv_length) && (qspi_reg->sr & 8))
|
||
{
|
||
*((rt_uint8_t *)qspi_bus->recv_buf) = qspi_reg->dr[0];
|
||
qspi_bus->recv_buf++;
|
||
qspi_bus->recv_length--;
|
||
}
|
||
}
|
||
else if (qspi_bus->cell_size == 2)
|
||
{
|
||
while ((qspi_bus->recv_length) && (qspi_reg->sr & 8))
|
||
{
|
||
*((rt_uint16_t *)qspi_bus->recv_buf) = qspi_reg->dr[0];
|
||
qspi_bus->recv_buf += 2;
|
||
qspi_bus->recv_length--;
|
||
}
|
||
}
|
||
else if (qspi_bus->cell_size == 4)
|
||
{
|
||
while ((qspi_bus->recv_length) && (qspi_reg->sr & 8))
|
||
{
|
||
*((rt_uint32_t *)qspi_bus->recv_buf) = qspi_reg->dr[0];
|
||
qspi_bus->recv_buf += 4;
|
||
qspi_bus->recv_length--;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LOG_E("qspi%d datawidth error", qspi_bus->idx);
|
||
}
|
||
if (qspi_bus->recv_length == 0)
|
||
{
|
||
qspi_reg->imr &= ~0x10;
|
||
rt_event_send(&qspi_bus->event, BIT(SSI_RXF));
|
||
}
|
||
else if (qspi_bus->recv_length <= qspi_reg->rxftlr)
|
||
{
|
||
qspi_reg->rxftlr = qspi_bus->recv_length - 1;
|
||
}
|
||
}
|
||
/* Handle transfer complete interrupt */
|
||
else if (vector == SSI_DONE)
|
||
{
|
||
/* Clear transfer done interrupt by reading donecr register */
|
||
(void)qspi_reg->donecr;
|
||
rt_event_send(&qspi_bus->event, BIT(SSI_DONE));
|
||
}
|
||
/* Handle DMA error interrupt */
|
||
else if (vector == SSI_AXIE)
|
||
{
|
||
/* Clear AXI error interrupt by reading axiecr register */
|
||
(void)qspi_reg->axiecr;
|
||
rt_event_send(&qspi_bus->event, BIT(SSI_AXIE));
|
||
}
|
||
}
|
||
static struct k230_spi_dev k230_spi_devs[] =
|
||
{
|
||
#ifdef BSP_USING_SPI0
|
||
{
|
||
.name = "spi0",
|
||
.event_name = "spi0_event",
|
||
.pa_base = SPI_OPI_BASE_ADDR,
|
||
.size = SPI_OPI_IO_SIZE,
|
||
.vector = IRQN_SPI0,
|
||
.idx = 0,
|
||
.rdse = 0,
|
||
.rdsd = 0,
|
||
.max_line = 8,
|
||
.max_hz = 200000000,
|
||
},
|
||
#endif
|
||
#ifdef BSP_USING_SPI1
|
||
{
|
||
.name = "spi1",
|
||
.event_name = "spi1_event",
|
||
.pa_base = SPI_QOPI_BASE_ADDR,
|
||
.size = SPI_QOPI_IO_SIZE / 2,
|
||
.vector = IRQN_SPI1,
|
||
.idx = 1,
|
||
.rdse = 0,
|
||
.rdsd = 0,
|
||
.max_line = 4,
|
||
.max_hz = 100000000,
|
||
},
|
||
#endif
|
||
#ifdef BSP_USING_SPI2
|
||
{
|
||
.name = "spi2",
|
||
.event_name = "spi2_event",
|
||
.pa_base = SPI_QOPI_BASE_ADDR + SPI_QOPI_IO_SIZE / 2,
|
||
.size = SPI_QOPI_IO_SIZE / 2,
|
||
.vector = IRQN_SPI2,
|
||
.idx = 2,
|
||
.rdse = 0,
|
||
.rdsd = 0,
|
||
.max_line = 4,
|
||
.max_hz = 100000000,
|
||
},
|
||
#endif
|
||
};
|
||
|
||
int rt_hw_qspi_bus_init(void)
|
||
{
|
||
rt_err_t ret;
|
||
int i;
|
||
for (i = 0; i < sizeof(k230_spi_devs) / sizeof(k230_spi_devs[0]); i++)
|
||
{
|
||
k230_spi_devs[i].base = rt_ioremap((void *)k230_spi_devs[i].pa_base, k230_spi_devs[i].size);
|
||
ret = rt_qspi_bus_register(&k230_spi_devs[i].dev, k230_spi_devs[i].name, &k230_qspi_ops);
|
||
if (ret)
|
||
{
|
||
LOG_E("%s register fail", k230_spi_devs[i].name);
|
||
return ret;
|
||
}
|
||
rt_event_init(&k230_spi_devs[i].event, k230_spi_devs[i].event_name, RT_IPC_FLAG_PRIO);
|
||
rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_TXE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name);
|
||
rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_TXE);
|
||
rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_RXF, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name);
|
||
rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_RXF);
|
||
rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_DONE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name);
|
||
rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_DONE);
|
||
rt_hw_interrupt_install(k230_spi_devs[i].vector + SSI_AXIE, k230_spi_irq, &k230_spi_devs[i], k230_spi_devs[i].name);
|
||
rt_hw_interrupt_umask(k230_spi_devs[i].vector + SSI_AXIE);
|
||
}
|
||
return RT_EOK;
|
||
}
|
||
INIT_DEVICE_EXPORT(rt_hw_qspi_bus_init);
|