You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
7.6 KiB
286 lines
7.6 KiB
/*
|
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2019-07-29 zdzn first version
|
|
*/
|
|
#include "drv_spi.h"
|
|
|
|
#ifdef RT_USING_SPI
|
|
|
|
#define RPI_CORE_CLK_HZ 250000000
|
|
#define BSP_SPI_MAX_HZ (30* 1000 *1000)
|
|
#define SPITIMEOUT 0x0FFF
|
|
|
|
void spi_gpio_write(rt_uint8_t pin, rt_uint8_t val)
|
|
{
|
|
if (val)
|
|
BCM283X_GPIO_GPSET((pin / 32)) = 1 << (pin % 32);
|
|
else
|
|
BCM283X_GPIO_GPCLR((pin / 32)) = 1 << (pin % 32);
|
|
}
|
|
|
|
struct raspi_spi_hw_config
|
|
{
|
|
rt_uint8_t spi_num;
|
|
raspi_gpio_pin sclk_pin;
|
|
raspi_pin_select sclk_mode;
|
|
raspi_gpio_pin mosi_pin;
|
|
raspi_pin_select mosi_mode;
|
|
raspi_gpio_pin miso_pin;
|
|
raspi_pin_select miso_mode;
|
|
#if defined (BSP_USING_SPI0_DEVICE0) || defined (BSP_USING_SPI1_DEVICE0)
|
|
raspi_gpio_pin ce0_pin;
|
|
raspi_pin_select ce0_mode;
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1) || defined (BSP_USING_SPI1_DEVICE1)
|
|
raspi_gpio_pin ce1_pin;
|
|
raspi_pin_select ce1_mode;
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI1_DEVICE2)
|
|
raspi_gpio_pin ce2_pin;
|
|
raspi_pin_select ce2_mode;
|
|
#endif
|
|
};
|
|
|
|
struct raspi_spi_device
|
|
{
|
|
char *device_name;
|
|
struct rt_spi_bus *spi_bus;
|
|
struct rt_spi_device *spi_device;
|
|
raspi_gpio_pin cs_pin;
|
|
};
|
|
|
|
static rt_err_t raspi_spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg)
|
|
{
|
|
RT_ASSERT(cfg != RT_NULL);
|
|
RT_ASSERT(device != RT_NULL);
|
|
rt_uint16_t divider;
|
|
|
|
// spi clear fifo
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= BCM283X_SPI0_CS_CLEAR;
|
|
|
|
if (cfg->mode & RT_SPI_CPOL)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= BCM283X_SPI0_CS_CPOL;
|
|
|
|
if (cfg->mode & RT_SPI_CPHA)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= BCM283X_SPI0_CS_CPHA;
|
|
|
|
if (cfg->mode & RT_SPI_CS_HIGH)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= BCM283X_SPI0_CS_CSPOL;
|
|
|
|
//set clk
|
|
if (cfg->max_hz > BSP_SPI_MAX_HZ)
|
|
cfg->max_hz = BSP_SPI_MAX_HZ;
|
|
|
|
divider = (rt_uint16_t) ((rt_uint32_t) RPI_CORE_CLK_HZ / cfg->max_hz);
|
|
divider &= 0xFFFE;
|
|
|
|
BCM283X_SPI0_CLK(BCM283X_SPI0_BASE) = divider;
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
rt_uint8_t correct_order(rt_uint8_t b, rt_uint8_t flag)
|
|
{
|
|
if (flag)
|
|
return raspi_byte_reverse_table[b];
|
|
else
|
|
return b;
|
|
}
|
|
|
|
static rt_err_t spi_transfernb(rt_uint8_t* tbuf, rt_uint8_t* rbuf, rt_uint32_t len, rt_uint8_t flag)
|
|
{
|
|
rt_uint32_t TXCnt=0;
|
|
rt_uint32_t RXCnt=0;
|
|
|
|
/* Clear TX and RX fifos */
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= (BCM283X_SPI0_CS_CLEAR & BCM283X_SPI0_CS_CLEAR);
|
|
|
|
/* Set TA = 1 */
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= (BCM283X_SPI0_CS_TA & BCM283X_SPI0_CS_TA);
|
|
|
|
|
|
/* Use the FIFO's to reduce the interbyte times */
|
|
while ((TXCnt < len) || (RXCnt < len))
|
|
{
|
|
/* TX fifo not full, so add some more bytes */
|
|
while (((BCM283X_SPI0_CS(BCM283X_SPI0_BASE) & BCM283X_SPI0_CS_TXD)) && (TXCnt < len))
|
|
{
|
|
BCM283X_SPI0_FIFO(BCM283X_SPI0_BASE) = correct_order(tbuf[TXCnt],flag);
|
|
TXCnt++;
|
|
}
|
|
/* Rx fifo not empty, so get the next received bytes */
|
|
while (((BCM283X_SPI0_CS(BCM283X_SPI0_BASE) & BCM283X_SPI0_CS_RXD)) && (RXCnt < len))
|
|
{
|
|
rbuf[RXCnt] = correct_order(BCM283X_SPI0_FIFO(BCM283X_SPI0_BASE),flag);
|
|
RXCnt++;
|
|
}
|
|
}
|
|
/* Wait for DONE to be set */
|
|
while (!(BCM283X_SPI0_CS(BCM283X_SPI0_BASE) & BCM283X_SPI0_CS_DONE));
|
|
|
|
/* Set TA = 0, and also set the barrier */
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= (0 & BCM283X_SPI0_CS_TA);
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_ssize_t raspi_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
|
|
{
|
|
|
|
RT_ASSERT(device != RT_NULL);
|
|
RT_ASSERT(device->bus != RT_NULL);
|
|
RT_ASSERT(device->parent.user_data != RT_NULL);
|
|
RT_ASSERT(message->send_buf != RT_NULL || message->recv_buf != RT_NULL);
|
|
|
|
rt_err_t res;
|
|
rt_uint8_t flag;
|
|
struct rt_spi_configuration config = device->config;
|
|
raspi_gpio_pin cs_pin = (raspi_gpio_pin)device->parent.user_data;
|
|
|
|
if (config.mode & RT_SPI_MSB)
|
|
flag = 0;
|
|
else
|
|
flag = 1;
|
|
if (message->cs_take);
|
|
// (config.mode & RT_SPI_CS_HIGH)?
|
|
// spi_gpio_write(cs_pin, 1):
|
|
// spi_gpio_write(cs_pin, 0);
|
|
|
|
/* deal data */
|
|
res = spi_transfernb((rt_uint8_t *)message->send_buf, (rt_uint8_t *)message->recv_buf,
|
|
(rt_int32_t)message->length, flag);
|
|
|
|
if (message->cs_release)
|
|
(config.mode & RT_SPI_CS_HIGH)?
|
|
spi_gpio_write(cs_pin, 0):
|
|
spi_gpio_write(cs_pin, 1);
|
|
|
|
if (res != RT_EOK)
|
|
return res;
|
|
|
|
return message->length;
|
|
}
|
|
|
|
rt_err_t raspi_spi_bus_attach_device(const char *bus_name, struct raspi_spi_device *device)
|
|
{
|
|
rt_err_t ret;
|
|
RT_ASSERT(device != RT_NULL);
|
|
ret = rt_spi_bus_attach_device(device->spi_device, device->device_name, bus_name, (void *)(device->cs_pin));
|
|
return ret;
|
|
}
|
|
|
|
rt_err_t raspi_spi_hw_init(struct raspi_spi_hw_config *hwcfg)
|
|
{
|
|
GPIO_FSEL(hwcfg->sclk_pin, hwcfg->sclk_mode);
|
|
GPIO_FSEL(hwcfg->miso_pin, hwcfg->miso_mode);
|
|
GPIO_FSEL(hwcfg->mosi_pin, hwcfg->mosi_mode);
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE0)
|
|
GPIO_FSEL(hwcfg->ce0_pin, hwcfg->ce0_mode);
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1)
|
|
GPIO_FSEL(hwcfg->ce1_pin, hwcfg->ce1_mode);
|
|
#endif
|
|
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) = 0;
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) = BCM283X_SPI0_CS_CLEAR;
|
|
|
|
//enable chip select
|
|
#if defined (BSP_USING_SPI0_DEVICE0)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= 0;
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= 0x2;
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE0) && defined (BSP_USING_SPI0_DEVICE1)
|
|
BCM283X_SPI0_CS(BCM283X_SPI0_BASE) |= BCM283X_SPI0_CS_CS;
|
|
#endif
|
|
return RT_EOK;
|
|
}
|
|
|
|
static struct rt_spi_ops raspi_spi_ops =
|
|
{
|
|
.configure = raspi_spi_configure,
|
|
.xfer = raspi_spi_xfer
|
|
};
|
|
|
|
#if defined (BSP_USING_SPI0_BUS)
|
|
#define SPI0_BUS_NAME "spi0"
|
|
#define SPI0_DEVICE0_NAME "spi0.0"
|
|
#define SPI0_DEVICE1_NAME "spi0.1"
|
|
|
|
struct rt_spi_bus spi0_bus;
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE0)
|
|
struct rt_spi_device spi0_device0;
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1)
|
|
static struct rt_spi_device spi0_device1;
|
|
#endif
|
|
|
|
struct raspi_spi_hw_config raspi_spi0_hw =
|
|
{
|
|
.spi_num = 0,
|
|
.sclk_pin = RPI_GPIO_P1_23,
|
|
.sclk_mode = BCM283X_GPIO_FSEL_ALT0,
|
|
.mosi_pin = RPI_GPIO_P1_19,
|
|
.mosi_mode = BCM283X_GPIO_FSEL_ALT0,
|
|
.miso_pin = RPI_GPIO_P1_21,
|
|
.miso_mode = BCM283X_GPIO_FSEL_ALT0,
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE0)
|
|
.ce0_pin = RPI_GPIO_P1_24,
|
|
.ce0_mode = BCM283X_GPIO_FSEL_ALT0,
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1)
|
|
.ce1_pin = RPI_GPIO_P1_26,
|
|
.ce1_mode = BCM283X_GPIO_FSEL_ALT0,
|
|
#endif
|
|
};
|
|
#endif
|
|
|
|
int rt_hw_spi_init(void)
|
|
{
|
|
|
|
#if defined (BSP_USING_SPI0_BUS)
|
|
raspi_spi_hw_init(&raspi_spi0_hw);
|
|
rt_spi_bus_register(&spi0_bus, SPI0_BUS_NAME, &raspi_spi_ops);
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE0)
|
|
struct raspi_spi_device raspi_spi0_device0 =
|
|
{
|
|
.device_name = SPI0_DEVICE0_NAME,
|
|
.spi_bus = &spi0_bus,
|
|
.spi_device = &spi0_device0,
|
|
.cs_pin = raspi_spi0_hw.ce0_pin,
|
|
};
|
|
raspi_spi_bus_attach_device(SPI0_BUS_NAME, &raspi_spi0_device0);
|
|
#endif
|
|
|
|
#if defined (BSP_USING_SPI0_DEVICE1)
|
|
struct raspi_spi_device raspi_spi0_device1 =
|
|
{
|
|
.device_name = SPI0_DEVICE1_NAME,
|
|
.spi_bus = &spi0_bus,
|
|
.spi_device = &spi0_device1,
|
|
.cs_pin = raspi_spi0_hw.ce1_pin,
|
|
};
|
|
raspi_spi_bus_attach_device(SPI0_BUS_NAME, &raspi_spi0_device1);
|
|
#endif
|
|
#endif
|
|
return RT_EOK;
|
|
}
|
|
INIT_DEVICE_EXPORT(rt_hw_spi_init);
|
|
|
|
#endif
|
|
|