288 lines
6.6 KiB
C
288 lines
6.6 KiB
C
|
/*
|
||
|
* Copyright (c) 2006-2020, RT-Thread Development Team
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*
|
||
|
* Change Logs:
|
||
|
* Date Author Notes
|
||
|
* 2020-05-28 hqfang first implementation.
|
||
|
*/
|
||
|
|
||
|
#include "drv_spi.h"
|
||
|
|
||
|
#ifdef RT_USING_SPI
|
||
|
|
||
|
#if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1) && !defined(BSP_USING_SPI2)
|
||
|
#error "Please define at least one BSP_USING_SPIx"
|
||
|
/* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable SPI */
|
||
|
#endif
|
||
|
|
||
|
static struct gd32_spi_config spi_config[] =
|
||
|
{
|
||
|
#ifdef BSP_USING_SPI0
|
||
|
{
|
||
|
"spi0",
|
||
|
SPI0,
|
||
|
},
|
||
|
#endif
|
||
|
#ifdef BSP_USING_SPI1
|
||
|
{
|
||
|
"spi1",
|
||
|
SPI1,
|
||
|
},
|
||
|
#endif
|
||
|
#ifdef BSP_USING_SPI2
|
||
|
{
|
||
|
"spi2",
|
||
|
SPI2,
|
||
|
},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static struct gd32_spi spi_obj[sizeof(spi_config) / sizeof(spi_config[0])] = {0};
|
||
|
|
||
|
static rt_err_t gd32_spi_init(rt_uint32_t spi_periph, struct rt_spi_configuration *cfg)
|
||
|
{
|
||
|
spi_parameter_struct spicfg;
|
||
|
uint32_t apbfreq;
|
||
|
uint32_t scale;
|
||
|
|
||
|
RT_ASSERT(cfg != RT_NULL);
|
||
|
|
||
|
spi_struct_para_init(&spicfg);
|
||
|
|
||
|
if (cfg->data_width != 8 && cfg->data_width != 16)
|
||
|
{
|
||
|
return (-RT_EINVAL);
|
||
|
}
|
||
|
|
||
|
switch (spi_periph)
|
||
|
{
|
||
|
case SPI0:
|
||
|
apbfreq = rcu_clock_freq_get(CK_APB2);
|
||
|
break;
|
||
|
default:
|
||
|
apbfreq = rcu_clock_freq_get(CK_APB1);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
scale = apbfreq / cfg->max_hz;
|
||
|
if (scale <= 2)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_2;
|
||
|
}
|
||
|
else if (scale <= 4)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_4;
|
||
|
}
|
||
|
else if (scale <= 8)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_8;
|
||
|
}
|
||
|
else if (scale <= 16)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_16;
|
||
|
}
|
||
|
else if (scale <= 32)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_32;
|
||
|
}
|
||
|
else if (scale <= 64)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_64;
|
||
|
}
|
||
|
else if (scale <= 128)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_128;
|
||
|
}
|
||
|
else if (scale <= 256)
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_256;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
spicfg.prescale = SPI_PSC_256;
|
||
|
}
|
||
|
|
||
|
if (cfg->data_width == 8)
|
||
|
{
|
||
|
spicfg.frame_size = SPI_FRAMESIZE_8BIT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
spicfg.frame_size = SPI_FRAMESIZE_16BIT;
|
||
|
}
|
||
|
|
||
|
if (cfg->mode & RT_SPI_MSB)
|
||
|
{
|
||
|
spicfg.endian = SPI_ENDIAN_MSB;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
spicfg.endian = SPI_ENDIAN_LSB;
|
||
|
}
|
||
|
|
||
|
spicfg.clock_polarity_phase = 0;
|
||
|
if (cfg->mode & RT_SPI_CPHA)
|
||
|
{
|
||
|
spicfg.clock_polarity_phase |= SPI_CTL0_CKPH;
|
||
|
}
|
||
|
if (cfg->mode & RT_SPI_CPOL)
|
||
|
{
|
||
|
spicfg.clock_polarity_phase |= SPI_CTL0_CKPL;
|
||
|
}
|
||
|
|
||
|
if (cfg->mode & RT_SPI_SLAVE)
|
||
|
{
|
||
|
spicfg.device_mode = SPI_SLAVE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
spicfg.device_mode = SPI_MASTER;
|
||
|
}
|
||
|
spicfg.nss = SPI_NSS_SOFT;
|
||
|
spicfg.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
|
||
|
|
||
|
spi_init(spi_periph, &spicfg);
|
||
|
/* set crc polynomial */
|
||
|
spi_crc_polynomial_set(spi_periph, 7);
|
||
|
|
||
|
return RT_EOK;
|
||
|
}
|
||
|
|
||
|
static rt_err_t gd32_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
|
||
|
{
|
||
|
rt_err_t ret = RT_EOK;
|
||
|
RT_ASSERT(device != RT_NULL);
|
||
|
|
||
|
struct gd32_spi *spi_obj = (struct gd32_spi *)(device->bus->parent.user_data);
|
||
|
struct gd32_spi_config *spi_cfg = (struct gd32_spi_config *)(spi_obj->config);
|
||
|
|
||
|
ret = gd32_spi_init(spi_cfg->spi_periph, cfg);
|
||
|
/* enable SPI */
|
||
|
spi_enable(spi_cfg->spi_periph);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attach the spi device to SPI bus, this function must be used after initialization.
|
||
|
*/
|
||
|
rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_uint32_t pin)
|
||
|
{
|
||
|
rt_err_t ret = RT_EOK;
|
||
|
|
||
|
struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
|
||
|
RT_ASSERT(spi_device != RT_NULL);
|
||
|
|
||
|
struct gd32_spi_cs *cs_pin = (struct gd32_spi_cs *)rt_malloc(sizeof(struct gd32_spi_cs));
|
||
|
RT_ASSERT(cs_pin != RT_NULL);
|
||
|
|
||
|
cs_pin->pin = pin;
|
||
|
rt_pin_mode(pin, PIN_MODE_OUTPUT);
|
||
|
rt_pin_write(pin, PIN_HIGH);
|
||
|
|
||
|
ret = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
rt_size_t gd32_spi_transmit(rt_uint32_t spi_periph, const void *send_buf, void *recv_buf, rt_size_t length)
|
||
|
{
|
||
|
uint8_t *send_buf_8b = (uint8_t *)send_buf;
|
||
|
uint8_t *recv_buf_8b = (uint8_t *)recv_buf;
|
||
|
uint8_t sndbyte = 0xFF, rcvbyte;
|
||
|
rt_size_t idx = 0;
|
||
|
|
||
|
while (idx < length)
|
||
|
{
|
||
|
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE));
|
||
|
if (send_buf_8b)
|
||
|
{
|
||
|
sndbyte = send_buf_8b[idx];
|
||
|
}
|
||
|
spi_i2s_data_transmit(spi_periph, sndbyte);
|
||
|
while (RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE));
|
||
|
rcvbyte = spi_i2s_data_receive(spi_periph);
|
||
|
if (recv_buf_8b)
|
||
|
{
|
||
|
recv_buf_8b[idx] = rcvbyte;
|
||
|
}
|
||
|
idx ++;
|
||
|
}
|
||
|
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static rt_uint32_t gd32_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
||
|
{
|
||
|
rt_uint32_t total_length = 0;
|
||
|
rt_err_t ret = RT_EOK;
|
||
|
|
||
|
RT_ASSERT(device != RT_NULL);
|
||
|
|
||
|
struct gd32_spi *spi_obj = (struct gd32_spi *)(device->bus->parent.user_data);
|
||
|
struct gd32_spi_config *spi_cfg = (struct gd32_spi_config *)(spi_obj->config);
|
||
|
RT_ASSERT(spi_cfg != RT_NULL);
|
||
|
struct gd32_spi_cs *cs = (struct gd32_spi_cs *)(device->parent.user_data);
|
||
|
|
||
|
if (message && message->cs_take)
|
||
|
{
|
||
|
rt_pin_write(cs->pin, PIN_LOW);
|
||
|
}
|
||
|
if (message && message->length)
|
||
|
{
|
||
|
|
||
|
total_length += gd32_spi_transmit(spi_cfg->spi_periph, message->send_buf, \
|
||
|
message->recv_buf, message->length);
|
||
|
}
|
||
|
if (message && message->cs_release)
|
||
|
{
|
||
|
rt_pin_write(cs->pin, PIN_HIGH);
|
||
|
}
|
||
|
return total_length;
|
||
|
}
|
||
|
|
||
|
static const struct rt_spi_ops spi_ops =
|
||
|
{
|
||
|
gd32_spi_configure,
|
||
|
gd32_spi_xfer
|
||
|
};
|
||
|
|
||
|
int rt_hw_spi_init(void)
|
||
|
{
|
||
|
rt_size_t obj_num;
|
||
|
int index;
|
||
|
rt_err_t result = 0;
|
||
|
|
||
|
#ifdef BSP_USING_SPI0
|
||
|
rcu_periph_clock_enable(RCU_SPI0);
|
||
|
#endif
|
||
|
#ifdef BSP_USING_SPI1
|
||
|
rcu_periph_clock_enable(RCU_SPI1);
|
||
|
#endif
|
||
|
#ifdef BSP_USING_SPI2
|
||
|
rcu_periph_clock_enable(RCU_SPI2);
|
||
|
#endif
|
||
|
|
||
|
obj_num = sizeof(spi_obj) / sizeof(struct gd32_spi);
|
||
|
for (index = 0; index < obj_num; index++)
|
||
|
{
|
||
|
/* init spi object */
|
||
|
spi_obj[index].config = &spi_config[index];
|
||
|
spi_obj[index].bus.parent.user_data = &spi_obj[index];
|
||
|
|
||
|
/* register spi device */
|
||
|
result = rt_spi_bus_register(&spi_obj[index].bus,
|
||
|
spi_obj[index].config->name,
|
||
|
&spi_ops);
|
||
|
RT_ASSERT(result == RT_EOK);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
INIT_DEVICE_EXPORT(rt_hw_spi_init);
|
||
|
|
||
|
#endif
|
||
|
/* end of spi driver */
|