Files
lk/platform/stm32f7xx/spi.c

313 lines
8.2 KiB
C

#include <assert.h>
#include <debug.h>
#include <stdlib.h>
#include <string.h>
#include <target/gpioconfig.h>
#include <platform/spi.h>
#include <platform/stm32.h>
static struct spi_bus spi_busses[] = {
{ .spi = SPI1 },
{ .spi = SPI2 },
{ .spi = SPI3 },
{ .spi = SPI4 },
{ .spi = SPI5 },
{ .spi = SPI6 }
};
static inline int get_bus_idx(SPI_TypeDef *spi)
{
for (unsigned int i = 0; i < (sizeof(spi_busses)/sizeof(spi_busses[0])); i++) {
if (spi_busses[i].spi == spi) {
return i;
}
}
return INVALID_SPI_BUS;
}
static bool wait_for_bus_ready(SPI_HandleTypeDef *handle)
{
size_t timeout = 1024;
while (timeout && (HAL_SPI_GetState(handle) != HAL_SPI_STATE_READY)) {
timeout--;
}
if (timeout == 0) {
return false;
}
return true;
}
status_t spi_init (SPI_HandleTypeDef *handle)
{
DEBUG_ASSERT(handle);
status_t ret = NO_ERROR;
int bus = get_bus_idx(handle->Instance);
if (bus == INVALID_SPI_BUS) {
return ERR_NOT_FOUND;
}
mutex_init(&(spi_busses[bus].lock));
if (HAL_SPI_Init(handle) != HAL_OK) {
ret = ERR_GENERIC;
}
return ret;
}
status_t spi_write(SPI_HandleTypeDef *handle, uint8_t *data, size_t len, uint32_t cs)
{
DEBUG_ASSERT(handle);
DEBUG_ASSERT(data);
DEBUG_ASSERT(len);
status_t ret = NO_ERROR;
int bus = get_bus_idx(handle->Instance);
if (bus == INVALID_SPI_BUS) {
return ERR_NOT_FOUND;
}
mutex_acquire(&(spi_busses[bus].lock));
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
HAL_StatusTypeDef foo;
/* The lock may have been released while the hardware is still processing a transaction */
/*if (!wait_for_bus_ready(handle)) {
ret = ERR_BUSY;
} else {*/
if ((foo = HAL_SPI_Transmit(handle, data, len, HAL_MAX_DELAY)) != HAL_OK) {
printf("foo %d\n", foo);
ret = ERR_IO;
}
// }
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
mutex_release(&(spi_busses[bus].lock));
return ret;
}
status_t spi_read(SPI_HandleTypeDef *handle, uint8_t *data, size_t len, uint32_t cs)
{
DEBUG_ASSERT(handle);
DEBUG_ASSERT(data);
DEBUG_ASSERT(len);
status_t ret = NO_ERROR;
int bus = get_bus_idx(handle->Instance);
if (bus == INVALID_SPI_BUS) {
return ERR_NOT_FOUND;
}
mutex_acquire(&(spi_busses[bus].lock));
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
if (HAL_SPI_Receive(handle, data, len, HAL_MAX_DELAY) != HAL_OK) {
ret = ERR_IO;
}
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
mutex_release(&(spi_busses[bus].lock));
return ret;
}
status_t spi_transaction(SPI_HandleTypeDef *handle, uint8_t *wdata, uint8_t *rdata, size_t len, uint32_t cs)
{
DEBUG_ASSERT(handle);
DEBUG_ASSERT(wdata);
DEBUG_ASSERT(rdata);
DEBUG_ASSERT(len);
status_t ret = NO_ERROR;
int bus = get_bus_idx(handle->Instance);
if (bus == INVALID_SPI_BUS) {
return ERR_NOT_FOUND;
}
mutex_acquire(&(spi_busses[bus].lock));
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
if (HAL_SPI_TransmitReceive(handle, wdata, rdata, len, HAL_MAX_DELAY) != HAL_OK) {
ret = ERR_IO;
}
if (handle->Init.NSS == SPI_NSS_SOFT) {
gpio_set(cs, (handle->Init.CLKPolarity == 0) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
mutex_release(&(spi_busses[bus].lock));
return ret;
}
#if WITH_LIB_CONSOLE
#include <lib/console.h>
/* Bus configurations for testing. Although it requires a recompilation per setting tweaked,
* it saves us from having to write a great deal of configuration code via an awkward console
* interface with many arguments.
*
* XXX: This doesn't handle soft NSS on its own.
*/
// This test code is primarily for DartuinoP0 and the GPIO will only exist for that target
#define STM32F7_SPI_BUS_CNT 6
static SPI_HandleTypeDef handles[STM32F7_SPI_BUS_CNT] = {
[0] = { 0 },
[1] = {
.Instance = SPI2,
.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4,
.Init.Direction = SPI_DIRECTION_1LINE,
.Init.CLKPhase = SPI_PHASE_1EDGE,
.Init.CLKPolarity = SPI_POLARITY_LOW,
.Init.DataSize = SPI_DATASIZE_8BIT,
.Init.FirstBit = SPI_FIRSTBIT_LSB,
.Init.TIMode = SPI_TIMODE_DISABLE,
.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.Init.CRCPolynomial = 7,
.Init.NSS = SPI_NSS_SOFT,
.Init.Mode = SPI_MODE_MASTER,
},
[2] = { 0 },
[3] = { 0 },
[4] = { 0 },
[5] = {
.Instance = SPI5,
.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4,
.Init.Direction = SPI_DIRECTION_1LINE,
.Init.CLKPhase = SPI_PHASE_1EDGE,
.Init.CLKPolarity = SPI_POLARITY_LOW,
.Init.DataSize = SPI_DATASIZE_8BIT,
.Init.FirstBit = SPI_FIRSTBIT_MSB,
.Init.TIMode = SPI_TIMODE_DISABLE,
.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.Init.CRCPolynomial = 7,
.Init.NSS = SPI_NSS_SOFT,
.Init.Mode = SPI_MODE_MASTER,
},
};
// GPIO to whack for soft chip select
static unsigned int handle_nss[STM32F7_SPI_BUS_CNT] = {
[5] = GPIO(GPIO_PORT_K, 0), // NRF_CS on Dartuino P0
};
/* Check if bus is valid, initialize it if needed */
static inline bool check_bus(uint8_t bus)
{
if (bus < STM32F7_SPI_BUS_CNT && handles[bus].Instance == 0) {
return false;
}
if (HAL_SPI_GetState(&handles[bus]) == HAL_SPI_STATE_RESET) {
HAL_SPI_Init(&handles[bus]);
}
return true;
}
static void print_spi_usage(void)
{
printf("spi read <bus> <len>\n");
printf("\tRead <len> bytes from <bus>\n");
printf("spi spew <bus> <len> <n> <delay>\n");
printf("\tWrite <len> bytes <n> times with a delay of <delay> ms between over <bus>\n");
}
#define IS_CMD(_str, _argc) (argc >= _argc && (strcmp(_str, argv[1].str) == 0))
static int cmd_spi(int argc, const cmd_args *argv)
{
status_t ret = NO_ERROR;
// spi read <bus> <len>
if (IS_CMD("read", 4)) {
uint8_t bus = argv[2].i;
size_t len = MIN(1024, argv[3].i);
uint8_t *data = (uint8_t *)malloc(len);
if (!data) {
ret = ERR_NO_MEMORY;
goto read_err;
}
if (!check_bus(bus)) {
printf("Error: bus %u is invalid\n", bus);
ret = ERR_INVALID_ARGS;
goto read_err;
}
if (spi_read(&handles[bus], data, len, handle_nss[bus]) != NO_ERROR) {
printf("spi read error\n");
ret = ERR_IO;
goto read_err;
}
hexdump8(data, len);
read_err:
free(data);
// spi spew <bus> <len> <iterations> <delay>
} else if (IS_CMD("spew", 6)) {
uint8_t bus = argv[2].i;
size_t len = MIN(1024, argv[3].i);
uint32_t cnt = argv[4].i;
uint32_t delay = argv[5].i;
uint8_t *data = (uint8_t *)malloc(len);
if (cnt == 0) {
cnt = UINT32_MAX;
}
for (uint32_t i = 0; i < len; i++) {
data[i] = i % 255;
}
for (uint32_t i = 0; i < cnt; i++) {
int spi_ret;
if ((spi_ret = spi_write(&handles[bus], data, len, handle_nss[bus])) != NO_ERROR) {
printf("spi write error: %d\n", spi_ret);
ret = ERR_IO;
goto spew_err;
}
if (delay > 0) {
thread_sleep(delay);
}
}
spew_err:
free(data);
} else {
print_spi_usage();
}
return ret;
}
STATIC_COMMAND_START
STATIC_COMMAND("spi", "spi commands for stm32f7", &cmd_spi)
STATIC_COMMAND_END(spi);
#endif // WITH_LIB_CONSOLE