diff options
Diffstat (limited to 'src/kern/spi/spi_manager.c')
-rw-r--r-- | src/kern/spi/spi_manager.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/kern/spi/spi_manager.c b/src/kern/spi/spi_manager.c new file mode 100644 index 0000000..83b6e1f --- /dev/null +++ b/src/kern/spi/spi_manager.c @@ -0,0 +1,151 @@ +#include "kern/spi/spi_manager.h" + +#include "arch/stm32l4xxx/peripherals/rcc.h" +#include "arch/stm32l4xxx/peripherals/spi.h" +#include "kern/log.h" + +struct spi { + __IO spi_regs_t* regs; + spi_select_t selection; + bool reserved; +}; + +static spi_t spi_buses[N_SPI_SELECT]; + +static void enable_spi_clock(spi_t* spi, int enable) +{ + switch (spi->selection) { + case SPI_SELECT_SPI1: + regset(RCC.apb2en_r, rcc_spi1en, !!enable); + break; + + case SPI_SELECT_SPI2: + regset(RCC.apb1en1_r, rcc_spi3en, !!enable); + break; + + default: + break; + } +} + +static spi_t* configure_spi(spi_t* spi, spi_opts_t* opts, int* ec) +{ + enable_spi_clock(spi, 1); + + uint32_t reg = 0; + regset(reg, spi_ldma_tx, opts->number_of_data_parity_tx); + regset(reg, spi_ldma_rx, opts->number_of_data_parity_rx); + regset(reg, spi_frxth, opts->rx_interrupt_fifo_threshold); + regset(reg, spi_ds, opts->data_size); + regset(reg, spi_txeie, opts->enable_tx_buffer_empty_interrupt); + regset(reg, spi_rxneie, opts->enable_rx_buffer_not_empty_interrupt); + regset(reg, spi_errie, opts->enable_error_interrupt); + regset(reg, spi_frf, opts->frame_format); + regset(reg, spi_nssp, opts->enable_nss_pulse); + regset(reg, spi_ssoe, opts->enable_ss_output); + regset(reg, spi_txdmaen, opts->enable_tx_dma); + regset(reg, spi_rxdmaen, opts->enable_rx_dma); + + spi->regs->c_r2 = reg; + + reg = 0; + regset(reg, spi_bidimode, opts->enable_bidirectional_data_mode); + regset(reg, spi_crcen, opts->enable_crc); + regset(reg, spi_crcnext, 0); + regset(reg, spi_crcl, opts->crc_length); + regset(reg, spi_rxonly, opts->receieve_only); + regset(reg, spi_ssm, opts->enable_software_slave_management); + regset(reg, spi_ssi, opts->internal_slave_select); + regset(reg, spi_lsbfirst, !opts->endianness); + regset(reg, spi_spe, 1); + regset(reg, spi_br, opts->baud); + regset(reg, spi_mstr, !opts->spi_mode); + regset(reg, spi_cpol, opts->polarity); + regset(reg, spi_cpha, opts->clock_phase); + + spi->regs->c_r1 = reg; + + return spi; +} + +static int try_reserve_spi(spi_t* spi) +{ + return !__sync_fetch_and_or(&spi->reserved, 1); +} + +void spi_start(spi_t* spi) +{ + regset(spi->regs->c_r1, spi_spe, 1); +} + +void release_spi(spi_t* spi) +{ + enable_spi_clock(spi, 0); + regset(spi->regs->c_r1, spi_spe, 0); + spi->reserved = 0; +} + +void spi_set_crc_next(spi_t* spi) +{ + regset(spi->regs->c_r1, spi_crcnext, 1); +} + +spi_t* reserve_spi(spi_select_t spi_select, spi_opts_t* opts, int* ec) +{ + *ec = 0; + + if (spi_select < 0 || spi_select >= N_SPI_SELECT) { + *ec = SPI_ERROR_SELECTION_NOT_VALID; + return NULL; + } + + spi_t* ret = &spi_buses[spi_select]; + + if (!try_reserve_spi(ret)) { + *ec = SPI_ERROR_ALREADY_IN_USE; + return NULL; + } + + ret->selection = spi_select; + switch (spi_select) { +#define SPI(sp, ptr) \ + case SPI_SELECT_##sp: \ + ret->regs = &ptr; \ + break; +#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc" +#undef SPI + default: + *ec = SPI_ERROR_SELECTION_NOT_VALID; + return NULL; + } + + return configure_spi(ret, opts, ec); +} + +void spi_write_8_sync(spi_t* spi, uint8_t data) +{ + while (!regget(spi->regs->s_r, spi_txe)) + ; + spi->regs->dl_r = data; +} + +void spi_write_16_sync(spi_t* spi, uint16_t data) +{ + while (!regget(spi->regs->s_r, spi_txe)) + ; + spi->regs->d_r = data; +} + +uint8_t spi_read_8_sync(spi_t* spi) +{ + while (!regget(spi->regs->s_r, spi_rxne)) + ; + return spi->regs->dl_r; +} + +uint16_t spi_read_16_sync(spi_t* spi) +{ + while (!regget(spi->regs->s_r, spi_rxne)) + ; + return spi->regs->d_r; +} |