rt-thread/bsp/nuvoton_m487/driver/drv_qspi.c

271 lines
6.9 KiB
C

/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-3-30 bluebear233 first version
*/
#include "NuMicro.h"
#include <rtdevice.h>
#ifdef RT_USING_SPI
/* Private Define ---------------------------------------------------------------*/
#define USEING_QSPI0
/* Private Typedef --------------------------------------------------------------*/
struct m487_qspi
{
struct rt_spi_bus dev;
struct rt_spi_configuration configuration;
QSPI_T *spi_base;
rt_uint8_t init_gpio:1;
};
/* Private functions ------------------------------------------------------------*/
static rt_err_t m487_qspi_bus_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration);
static rt_uint32_t m487_qspi_bus_xfer(struct rt_spi_device *device,
struct rt_spi_message *message);
/* Private Variables ------------------------------------------------------------*/
struct rt_spi_ops m487_spi_poll_ops =
{
.configure = m487_qspi_bus_configure,
.xfer = m487_qspi_bus_xfer,
};
#ifdef USEING_QSPI0
static struct m487_qspi qspi0 =
{
.spi_base = QSPI0,
};
#endif
static rt_err_t m487_qspi_bus_configure(struct rt_spi_device *device,
struct rt_spi_configuration *configuration) {
struct m487_qspi *spi;
uint32_t u32QSPIMode;
uint32_t u32BusClock;
rt_uint8_t init_bus;
spi = (struct m487_qspi *) device->bus;
init_bus = 0;
if (!spi->init_gpio)
{
spi->init_gpio = 1;
init_bus = 1;
if(spi->spi_base == QSPI0)
{
/* Select PCLK0 as the clock source of QSPI0 */
CLK_SetModuleClock(QSPI0_MODULE, CLK_CLKSEL2_QSPI0SEL_PLL,
MODULE_NoMsk);
/* Enable QSPI0 peripheral clock */
CLK_EnableModuleClock(QSPI0_MODULE);
/* Setup QSPI0 multi-function pins */
SYS->GPC_MFPL &= ~(SYS_GPC_MFPL_PC0MFP_Msk | SYS_GPC_MFPL_PC1MFP_Msk
| SYS_GPC_MFPL_PC2MFP_Msk | SYS_GPC_MFPL_PC3MFP_Msk);
SYS->GPC_MFPL |= SYS_GPC_MFPL_PC0MFP_QSPI0_MOSI0 | SYS_GPC_MFPL_PC1MFP_QSPI0_MISO0
| SYS_GPC_MFPL_PC2MFP_QSPI0_CLK | SYS_GPC_MFPL_PC3MFP_QSPI0_SS;
/* Enable SPI0 clock pin (PC2) schmitt trigger */
PC->SMTEN |= GPIO_SMTEN_SMTEN2_Msk;
}
}
if(rt_memcmp(configuration, &spi->configuration, sizeof(*configuration)) != 0)
{
rt_memcpy(&spi->configuration, configuration, sizeof(*configuration));
init_bus = 1;
}
if(init_bus)
{
switch(configuration->mode & RT_SPI_MODE_3)
{
case RT_SPI_MODE_0:u32QSPIMode = QSPI_MODE_0;break;
case RT_SPI_MODE_1:u32QSPIMode = QSPI_MODE_1;break;
case RT_SPI_MODE_2:u32QSPIMode = QSPI_MODE_2;break;
case RT_SPI_MODE_3:u32QSPIMode = QSPI_MODE_3;break;
default:RT_ASSERT(0);
}
u32BusClock = configuration->max_hz;
if(u32BusClock > 50*1000*1000)
{
u32BusClock = 50*1000*1000;
}
QSPI_Open(spi->spi_base, QSPI_MASTER, u32QSPIMode, configuration->data_width, u32BusClock);
QSPI_EnableAutoSS(spi->spi_base, QSPI_SS, QSPI_SS_ACTIVE_LOW);
if(configuration->mode & RT_SPI_MSB)
{
QSPI_SET_MSB_FIRST(spi->spi_base);
}
else
{
QSPI_SET_LSB_FIRST(spi->spi_base);
}
}
return RT_EOK;
}
/**
* @brief SPI bus 轮询
* @param dev : SPI总线设备结构体指针
* @param send_addr : 发送缓冲区地址
* @param recv_addr : 接收缓冲区地址
* @param length : 数据长度
*/
static void qspi_transmission_with_poll(struct m487_qspi *spi_bus,
const uint8_t *send_addr, uint8_t *recv_addr, int length)
{
QSPI_T *spi_base = spi_bus->spi_base;
// 写
if (send_addr != RT_NULL && recv_addr == RT_NULL)
{
while (length--)
{
// 等待TX FIFO 为空
while(QSPI_GET_TX_FIFO_FULL_FLAG(spi_base));
// 输入数据
QSPI_WRITE_TX(spi_base, *send_addr++);
}
// 等待SPI空闲
while(QSPI_IS_BUSY(spi_base));
}
// 读写
else if (send_addr != RT_NULL && recv_addr != RT_NULL)
{
// 清空读FIFO
if(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
QSPI_ClearRxFIFO(spi_base);
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base));
}
while (length--)
{
// 等待TX FIFO 为空
while(QSPI_GET_TX_FIFO_FULL_FLAG(spi_base));
// 输入数据
QSPI_WRITE_TX(spi_base, *send_addr++);
// 读取RX FIFO
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
*recv_addr++ = QSPI_READ_RX(spi_base);
}
}
// 等待SPI空闲
while(QSPI_IS_BUSY(spi_base))
{
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
*recv_addr++ = QSPI_READ_RX(spi_base);
}
}
}
//读
else
{
// 清空读FIFO
if(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
QSPI_ClearRxFIFO(spi_base);
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base));
}
while (length--)
{
// 等待TX FIFO 为空
while(QSPI_GET_TX_FIFO_FULL_FLAG(spi_base));
// 输入数据
QSPI_WRITE_TX(spi_base, 0x00);
// 读取RX FIFO
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
*recv_addr++ = QSPI_READ_RX(spi_base);
}
}
// 等待SPI空闲
while(QSPI_IS_BUSY(spi_base))
{
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
*recv_addr++ = QSPI_READ_RX(spi_base);
}
}
while(!QSPI_GET_RX_FIFO_EMPTY_FLAG(spi_base))
{
*recv_addr++ = QSPI_READ_RX(spi_base);
}
}
}
static rt_uint32_t m487_qspi_bus_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
struct m487_qspi *spi;
spi = (struct m487_qspi *) device->bus;
if (message->cs_take)
{
QSPI_SET_SS_LOW(spi->spi_base);
}
if (message->length > 0)
{
qspi_transmission_with_poll(spi, message->send_buf,
message->recv_buf, message->length);
}
if (message->cs_release)
{
QSPI_SET_SS_HIGH(spi->spi_base);
}
return message->length;
}
static int m487_qspi_register_bus(struct m487_qspi *spi_bus, const char *name)
{
return rt_spi_bus_register(&spi_bus->dev, name, &m487_spi_poll_ops);
}
/**
* 硬件QSPI注册
*/
static int rt_hw_qspi_init(void)
{
#ifdef USEING_QSPI0
m487_qspi_register_bus(&qspi0, "qspi0");
#endif
return 0;
}
INIT_DEVICE_EXPORT(rt_hw_qspi_init);
#endif