aboutsummaryrefslogtreecommitdiff
path: root/src/kern/spi/spi_manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kern/spi/spi_manager.c')
-rw-r--r--src/kern/spi/spi_manager.c151
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;
+}