rt-thread/bsp/essemi/es32f0271/drivers/drv_spi.c

440 lines
13 KiB
C

/*
* Copyright (C) 2018 Shanghai Eastsoft Microelectronics Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-10-23 yuzrain the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <string.h>
#include <rthw.h>
#include "board.h"
#include "drv_spi.h"
#include "md_spi.h"
#include "md_gpio.h"
#ifdef RT_USING_SPI
#define SPITIMEOUT 0x0FFF
static rt_err_t __spi_send(struct rt_spi_device *device, rt_uint8_t *buf,
rt_int32_t len, rt_uint32_t tmout);
static rt_err_t __spi_recv(struct rt_spi_device *device, rt_uint8_t *buf,
rt_int32_t len, rt_uint32_t tmout);
static rt_err_t __spi_send_recv(struct rt_spi_device *device, rt_uint8_t *tbuf,
rt_uint8_t *rbuf, rt_int32_t len, rt_uint32_t tmout);
/**
* @brief: SPI single line send.
* @param: device, pointer to the SPI device
* @param: buf, send data buffer
* @param: len, the length of buf
* @param: tmout, timeout
* @retval: rt_err_t
*/
static rt_err_t __spi_send(struct rt_spi_device *device, rt_uint8_t *buf,
rt_int32_t len, rt_uint32_t tmout)
{
SPI_TypeDef *hspi;
rt_uint32_t rt_timout;
rt_uint8_t temp_data;
/* Get the SPI port */
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
/* Open SPI if it is disabled */
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
while (len > 0)
{
/* Confirm that no data is being transmitted */
rt_timout = tmout;
while (((hspi->STAT & SPI_STAT_TXE_MSK) == 0) && (--rt_timout));
if (rt_timout == 0)
return RT_ETIMEOUT;
/* Send data */
if (device->config.data_width == 8)
{
hspi->DATA = *(rt_uint8_t *)buf;
buf++;
len--;
}
else if (device->config.data_width == 16)
{
hspi->DATA = *(rt_uint16_t *)buf;
buf += 2;
len -= 2;
}
else
return RT_EINVAL;
}
/* At here, we have transmitted all the data.
* The next step is to clear the IT flag.
*/
for (rt_uint8_t i = 0; i < md_spi_get_stat_rxflv(hspi); i++)
temp_data = hspi->DATA;
UNUSED(temp_data);
hspi->ICR = hspi->RIF;
return RT_EOK;
}
/**
* @brief: SPI single line receive.
* @param: device, pointer to the SPI device
* @param: buf, receive data buffer
* @param: len, the length of buf
* @param: tmout, timeout
* @retval: rt_err_t
*/
static rt_err_t __spi_recv(struct rt_spi_device *device, rt_uint8_t *buf,
rt_int32_t len, rt_uint32_t tmout)
{
SPI_TypeDef *hspi;
rt_uint32_t rt_timout;
/* Get the SPI port */
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
/* Open SPI if it is disabled */
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
/* Handle data in __spi_send_recv() function */
if (((device->config.mode & RT_SPI_SLAVE) == 0)
&& ((device->config.mode & RT_SPI_3WIRE) == 0))
__spi_send_recv(device, buf, buf, len, tmout);
while (len > 0)
{
/* Waiting for data */
rt_timout = tmout;
while (((hspi->STAT & SPI_STAT_RXTH_MSK) == 0) && (--rt_timout));
if (rt_timout == 0)
return RT_ETIMEOUT;
/* Send data */
if (device->config.data_width == 8)
{
*(rt_uint8_t *)buf = hspi->DATA;
buf++;
len--;
}
else if (device->config.data_width == 16)
{
*(rt_uint16_t *)buf = hspi->DATA;
buf += 2;
len -= 2;
}
else
return RT_EINVAL;
}
/* At here, we have transmitted all the data.
* The next step is to clear the IT flag.
*/
hspi->ICR = hspi->RIF;
return RT_EOK;
}
/**
* @brief: SPI two line transmission.
* @param: device, pointer to the SPI device
* @param: tbuf, send data buffer
* @param: rbuf, receive data buffer
* @param: len, the length of buf
* @param: tmout, timeout
* @retval: rt_err_t
*/
static rt_err_t __spi_send_recv(struct rt_spi_device *device, rt_uint8_t *tbuf,
rt_uint8_t *rbuf, rt_int32_t len, rt_uint32_t tmout)
{
SPI_TypeDef *hspi;
rt_uint32_t rt_timout;
/* Get the SPI port */
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
/* Open SPI if it is disabled */
if (READ_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK) != SPI_CON1_SPIEN_MSK)
SET_BIT(hspi->CON1, SPI_CON1_SPIEN_MSK);
/* return error if SPI is in 1-line mode */
if ((device->config.mode & RT_SPI_3WIRE) == RT_SPI_3WIRE)
return RT_ERROR;
while (len > 0)
{
/* Confirm that no data is being transmitted */
rt_timout = tmout;
while (((hspi->STAT & SPI_STAT_TXE_MSK) == 0) && (--rt_timout));
if (rt_timout == 0)
return RT_ETIMEOUT;
/* Send data */
if (device->config.data_width == 8)
{
hspi->DATA = *(rt_uint8_t *)tbuf;
tbuf++;
len--;
}
else if (device->config.data_width == 16)
{
hspi->DATA = *(rt_uint16_t *)tbuf;
tbuf += 2;
len -= 2;
}
else
return RT_EINVAL;
/* Waiting for data */
rt_timout = tmout;
while (((hspi->STAT & SPI_STAT_RXTH_MSK) == 0) && (--rt_timout));
if (rt_timout == 0)
return RT_ETIMEOUT;
/* Send data */
if (device->config.data_width == 8)
{
*(rt_uint8_t *)rbuf = hspi->DATA;
rbuf++;
}
else if (device->config.data_width == 16)
{
*(rt_uint16_t *)rbuf = hspi->DATA;
rbuf += 2;
}
}
/* At here, we have transmitted all the data.
* The next step is to clear the IT flag.
*/
hspi->ICR = hspi->RIF;
return RT_EOK;
}
rt_err_t spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *cfg)
{
SPI_TypeDef *hspi;
hspi = (SPI_TypeDef *)device->bus->parent.user_data;
/* Close SPI temporarily */
md_spi_disable_con1_spien(hspi);
/* config spi mode */
if (cfg->mode & RT_SPI_SLAVE)
md_spi_set_con1_mstren(hspi, MD_SPI_MODE_SLAVE);
else
md_spi_set_con1_mstren(hspi, MD_SPI_MODE_MASTER);
/* Config data mode */
if (cfg->mode & RT_SPI_3WIRE)
md_spi_set_con1_bidimode(hspi, MD_SPI_HALF_DUPLEX);
else
md_spi_set_con1_bidimode(hspi, MD_SPI_FULL_DUPLEX);
/* Config data width */
if (cfg->data_width == 8)
md_spi_set_con1_flen(hspi, MD_SPI_FRAME_FORMAT_8BIT);
else if (cfg->data_width == 16)
md_spi_set_con1_flen(hspi, SPI_CON1_FLEN_MSK);
/* Config phase */
if (cfg->mode & RT_SPI_CPHA)
md_spi_set_con1_cpha(hspi, MD_SPI_PHASE_2EDGE);
else
md_spi_set_con1_cpha(hspi, MD_SPI_PHASE_1EDGE);
/* Config polarity */
if (cfg->mode & RT_SPI_CPOL)
md_spi_set_con1_cpol(hspi, MD_SPI_POLARITY_HIGH);
else
md_spi_set_con1_cpol(hspi, MD_SPI_POLARITY_LOW);
/* Config if NSS pin is managed by software */
md_spi_disable_con1_ssen(hspi);
/* config spi clock */
if (cfg->max_hz >= SystemCoreClock / 2)
{
/* pclk1 max speed 48MHz, spi master max speed 10MHz */
if (SystemCoreClock / 2 <= 10000000)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV2);
else if (SystemCoreClock / 4 <= 10000000)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV4);
else
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
}
else if (cfg->max_hz >= SystemCoreClock / 4)
{
/* pclk1 max speed 48MHz, spi master max speed 10MHz */
if (SystemCoreClock / 4 <= 10000000)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV4);
else
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
}
else if (cfg->max_hz >= SystemCoreClock / 8)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV8);
else if (cfg->max_hz >= SystemCoreClock / 16)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV16);
else if (cfg->max_hz >= SystemCoreClock / 32)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV32);
else if (cfg->max_hz >= SystemCoreClock / 64)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV64);
else if (cfg->max_hz >= SystemCoreClock / 128)
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV128);
else
md_spi_set_con1_baud(hspi, MD_SPI_BAUDRATEPRESCALER_DIV256);
/* Enable SPI */
md_spi_enable_con1_spien(hspi);
return RT_EOK;
}
static rt_uint32_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
rt_err_t res;
rt_uint32_t *cs;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
RT_ASSERT(device->bus->parent.user_data != RT_NULL);
RT_ASSERT(message->send_buf != RT_NULL || message->recv_buf != RT_NULL);
cs = (rt_uint32_t *)device->parent.user_data;
/* only send data */
if (message->recv_buf == RT_NULL)
{
if (message->cs_take)
{
rt_pin_write(*cs, 0);
}
res = __spi_send(device, (rt_uint8_t *)message->send_buf, (rt_int32_t)message->length, SPITIMEOUT);
if (message->cs_release)
{
rt_pin_write(*cs, 1);
}
if (res != RT_EOK)
return RT_ERROR;
}
/* only receive data */
if (message->send_buf == RT_NULL)
{
if (message->cs_take)
{
rt_pin_write(*cs, 0);
}
res = __spi_recv(device, (rt_uint8_t *)message->recv_buf, (rt_int32_t)message->length, SPITIMEOUT);
if (message->cs_release)
{
rt_pin_write(*cs, 1);
}
if (res != RT_EOK)
return RT_ERROR;
}
/* send & receive */
else
{
if (message->cs_take)
{
rt_pin_write(*cs, 0);
}
res = __spi_send_recv(device, (rt_uint8_t *)message->send_buf, (rt_uint8_t *)message->recv_buf,
(rt_int32_t)message->length, SPITIMEOUT);
if (message->cs_release)
{
rt_pin_write(*cs, 1);
}
if (res != RT_EOK)
return RT_ERROR;
}
return message->length;
}
const struct rt_spi_ops es32f0_spi_ops =
{
spi_configure,
spixfer,
};
static struct rt_spi_bus _spi_bus1, _spi_bus2;
int es32f0_spi_register_bus(SPI_TypeDef *SPIx, const char *name)
{
struct rt_spi_bus *spi_bus;
if (SPIx == SPI2)
{
/* Open GPIO and SPI clock */
SET_BIT(RCU->APB1EN, RCU_APB1EN_SPI2EN_MSK);
SET_BIT(RCU->AHBEN, RCU_AHBEN_GPBEN_MSK);
/* Config SPI2 GPIO */
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_13, MD_GPIO_MODE_FUNCTION);
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_14, MD_GPIO_MODE_FUNCTION);
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_15, MD_GPIO_MODE_FUNCTION);
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_13, MD_GPIO_AF0);
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_14, MD_GPIO_AF0);
md_gpio_set_function8_15 (GPIOB, MD_GPIO_PIN_15, MD_GPIO_AF0);
/* Remember SPI bus2 */
spi_bus = &_spi_bus2;
}
else if (SPIx == SPI1)
{
/* Open GPIO and SPI clock */
SET_BIT(RCU->APB2EN, RCU_APB2EN_SPI1EN_MSK);
SET_BIT(RCU->AHBEN, RCU_AHBEN_GPBEN_MSK);
/* Config SPI1 GPIO */
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_3, MD_GPIO_MODE_FUNCTION);
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_4, MD_GPIO_MODE_FUNCTION);
md_gpio_set_mode (GPIOB, MD_GPIO_PIN_5, MD_GPIO_MODE_FUNCTION);
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_3, MD_GPIO_AF0);
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_4, MD_GPIO_AF0);
md_gpio_set_function0_7 (GPIOB, MD_GPIO_PIN_5, MD_GPIO_AF0);
/* Remember SPI bus1 */
spi_bus = &_spi_bus1;
}
else
{
return -1;
}
spi_bus->parent.user_data = SPIx;
return rt_spi_bus_register(spi_bus, name, &es32f0_spi_ops);
}
int rt_hw_spi_init(void)
{
int result = 0;
#ifdef BSP_USING_SPI2
result = es32f0_spi_register_bus(SPI2, "spi2");
#endif
#ifdef BSP_USING_SPI1
result = es32f0_spi_register_bus(SPI1, "spi1");
#endif
return result;
}
INIT_BOARD_EXPORT(rt_hw_spi_init);
#endif