1 /*
2 * Copyright (c) 2016 Erik Gilling
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <platform/spi.h>
9
10 #include <stdbool.h>
11
12 #include <arch/arm/cm.h>
13 #include <kernel/event.h>
14 #include <kernel/mutex.h>
15 #include <platform/dma.h>
16 #include <platform/rcc.h>
17
18 #define SPI_SR_FRLVL_VAL(reg) ((reg) >> 9 & 0x3)
19 #define SPI_SR_FTLVL_VAL(reg) ((reg) >> 11 & 0x3)
20
21 #define SPI_FIFO_EMPTY 0x0
22 #define SPI_FIFO_QUARTER 0x1
23 #define SPI_FIFO_HALF 0x2
24 #define SPI_FIFO_FULL 0x3
25
26 typedef SPI_TypeDef spi_regs_t;
27
28 uint32_t spi_dma_width_flag;
29 mutex_t spi_mutex;
30
spi_init(spi_data_size_t data_size,spi_cpol_t cpol,spi_cpha_t cpha,spi_bit_order_t bit_order,spi_prescaler_t prescaler)31 void spi_init(spi_data_size_t data_size,
32 spi_cpol_t cpol,
33 spi_cpha_t cpha,
34 spi_bit_order_t bit_order,
35 spi_prescaler_t prescaler) {
36
37 spi_regs_t *regs = SPI1;
38 uint16_t temp_reg;
39
40 mutex_init(&spi_mutex);
41
42 stm32_rcc_set_enable(STM32_RCC_CLK_SPI1, true);
43
44 regs->CR1 = cpol | cpha | bit_order | prescaler | SPI_CR1_SSM;
45
46 temp_reg = regs->CR2;
47 temp_reg &= ~(SPI_CR2_DS | SPI_CR2_FRXTH);
48 temp_reg |= data_size;
49 if (data_size < SPI_DATA_SIZE_9) {
50 // RXNE asserted when fifo has at least 8 bits. Defaults to 16bits.
51 temp_reg |= SPI_CR2_FRXTH;
52 spi_dma_width_flag = DMA_FLAG_PERIPH_8_BIT | DMA_FLAG_MEM_8_BIT;
53 } else {
54 spi_dma_width_flag = DMA_FLAG_PERIPH_16_BIT | DMA_FLAG_MEM_16_BIT;
55 }
56 regs->CR2 = temp_reg;
57
58 temp_reg = regs->CR1;
59 temp_reg |= SPI_CR1_MSTR | SPI_CR1_SSI;
60 regs->CR1 = temp_reg;
61 }
62
spi_xfer(const void * tx_buf,void * rx_buf,size_t len)63 ssize_t spi_xfer(const void *tx_buf, void *rx_buf, size_t len) {
64 // Assure only a single transaction is ever active.
65 mutex_acquire(&spi_mutex);
66
67 // Ensure we're idle before starting.
68 dma_wait(DMA_CHANNEL_2);
69 dma_wait(DMA_CHANNEL_3);
70
71 spi_regs_t *regs = SPI1;
72 regs->CR1 &= ~SPI_CR1_SPE;
73
74 // Make sure to start read DMA first.
75 dma_transfer_start(DMA_CHANNEL_2,
76 (uint32_t)®s->DR, (uint32_t)rx_buf, len,
77 DMA_FLAG_FROM_PERIPH | DMA_FLAG_MEM_INCREMENT
78 | DMA_FLAG_PRIORITY(1) | spi_dma_width_flag);
79 dma_transfer_start(DMA_CHANNEL_3,
80 (uint32_t)®s->DR, (uint32_t)tx_buf, len,
81 DMA_FLAG_FROM_MEM | DMA_FLAG_MEM_INCREMENT
82 | DMA_FLAG_PRIORITY(0) | spi_dma_width_flag);
83 regs->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;
84 regs->CR1 |= SPI_CR1_SPE;
85
86 // We only wait for channel 2 (RX) assuming that TX will be done first.
87 dma_wait(DMA_CHANNEL_2);
88
89 mutex_release(&spi_mutex);
90 return len;
91 }
92
93