#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; }