altos: Provide ISR-based code paths for SPI
[fw/altos] / src / stm / ao_spi_stm.c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include <ao.h>
19
20 struct ao_spi_stm_info {
21         uint8_t miso_dma_index;
22         uint8_t mosi_dma_index;
23         struct stm_spi *stm_spi;
24 };
25
26 uint8_t ao_spi_mutex[STM_NUM_SPI];
27 static void (*ao_spi_callback[STM_NUM_SPI])(int spi_index);
28
29 static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
30         {
31                 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
32                 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
33                 &stm_spi1
34         },
35         {
36                 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
37                 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
38                 &stm_spi2
39         }
40 };
41
42 static uint8_t  spi_dev_null;
43
44 static void
45 ao_spi_setup_send(void *block, uint16_t len, uint8_t spi_index)
46 {
47         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
48         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
49         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
50
51         /* Set up the transmit DMA to deliver data */
52         ao_dma_set_transfer(mosi_dma_index,
53                             &stm_spi->dr,
54                             block,
55                             len,
56                             (0 << STM_DMA_CCR_MEM2MEM) |
57                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
58                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
59                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
60                             (1 << STM_DMA_CCR_MINC) |
61                             (0 << STM_DMA_CCR_PINC) |
62                             (0 << STM_DMA_CCR_CIRC) |
63                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
64
65         /* Clear RXNE */
66         (void) stm_spi->dr;
67
68         /* Set up the receive DMA -- when this is done, we know the SPI unit
69          * is idle. Without this, we'd have to poll waiting for the BSY bit to
70          * be cleared
71          */
72         ao_dma_set_transfer(miso_dma_index,
73                             &stm_spi->dr,
74                             &spi_dev_null,
75                             len,
76                             (0 << STM_DMA_CCR_MEM2MEM) |
77                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
78                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
79                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
80                             (0 << STM_DMA_CCR_MINC) |
81                             (0 << STM_DMA_CCR_PINC) |
82                             (0 << STM_DMA_CCR_CIRC) |
83                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
84         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
85                         (0 << STM_SPI_CR2_RXNEIE) |
86                         (0 << STM_SPI_CR2_ERRIE) |
87                         (0 << STM_SPI_CR2_SSOE) |
88                         (1 << STM_SPI_CR2_TXDMAEN) |
89                         (1 << STM_SPI_CR2_RXDMAEN));
90 }
91
92 static void
93 ao_spi_start_send(uint8_t spi_index)
94 {
95         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
96         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
97
98         ao_dma_start(miso_dma_index);
99         ao_dma_start(mosi_dma_index);
100 }
101
102 static void
103 ao_spi_finish_send(uint8_t spi_index)
104 {
105         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
106         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
107
108         ao_dma_done_transfer(mosi_dma_index);
109         ao_dma_done_transfer(miso_dma_index);
110 }
111
112 void
113 ao_spi_dma_isr(int spi_index)
114 {
115         ao_spi_callback[spi_index](spi_index);
116         ao_spi_finish_send(spi_index);
117 }
118
119 void
120 ao_spi_queue_send(void *block, uint16_t len, uint8_t spi_index,
121                   void (*callback)(int spi_index))
122 {
123         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
124
125         ao_spi_setup_send(block, len, spi_index);
126         ao_spi_callback[spi_index] = callback;
127         ao_dma_set_isr(miso_dma_index, ao_spi_dma_isr, spi_index);
128         ao_spi_start_send(spi_index);
129 }
130
131 void
132 ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
133 {
134         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
135         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
136
137         ao_spi_setup_send(block, len, spi_index);
138         ao_spi_start_send(spi_index);
139         ao_arch_critical(
140                 while (!ao_dma_done[miso_dma_index])
141                         ao_sleep(&ao_dma_done[miso_dma_index]);
142                 );
143         
144         ao_spi_finish_send(spi_index);
145 }
146
147 void
148 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
149 {
150         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
151         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
152         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
153
154         /* Set up the transmit DMA to deliver data */
155         ao_dma_set_transfer(mosi_dma_index,
156                             &stm_spi->dr,
157                             &value,
158                             len,
159                             (0 << STM_DMA_CCR_MEM2MEM) |
160                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
161                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
162                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
163                             (0 << STM_DMA_CCR_MINC) |
164                             (0 << STM_DMA_CCR_PINC) |
165                             (0 << STM_DMA_CCR_CIRC) |
166                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
167
168         /* Clear RXNE */
169         (void) stm_spi->dr;
170
171         /* Set up the receive DMA -- when this is done, we know the SPI unit
172          * is idle. Without this, we'd have to poll waiting for the BSY bit to
173          * be cleared
174          */
175         ao_dma_set_transfer(miso_dma_index,
176                             &stm_spi->dr,
177                             &spi_dev_null,
178                             len,
179                             (0 << STM_DMA_CCR_MEM2MEM) |
180                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
181                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
182                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
183                             (0 << STM_DMA_CCR_MINC) |
184                             (0 << STM_DMA_CCR_PINC) |
185                             (0 << STM_DMA_CCR_CIRC) |
186                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
187         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
188                         (0 << STM_SPI_CR2_RXNEIE) |
189                         (0 << STM_SPI_CR2_ERRIE) |
190                         (0 << STM_SPI_CR2_SSOE) |
191                         (1 << STM_SPI_CR2_TXDMAEN) |
192                         (1 << STM_SPI_CR2_RXDMAEN));
193         ao_dma_start(miso_dma_index);
194         ao_dma_start(mosi_dma_index);
195         ao_arch_critical(
196                 while (!ao_dma_done[miso_dma_index])
197                         ao_sleep(&ao_dma_done[miso_dma_index]);
198                 );
199         ao_dma_done_transfer(mosi_dma_index);
200         ao_dma_done_transfer(miso_dma_index);
201 }
202
203 static void
204 ao_spi_setup_recv(void *block, uint16_t len, uint8_t spi_index)
205 {
206         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
207         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
208         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
209
210         /* Set up transmit DMA to make the SPI hardware actually run */
211         ao_dma_set_transfer(mosi_dma_index,
212                             &stm_spi->dr,
213                             &spi_dev_null,
214                             len,
215                             (0 << STM_DMA_CCR_MEM2MEM) |
216                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
217                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
218                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
219                             (0 << STM_DMA_CCR_MINC) |
220                             (0 << STM_DMA_CCR_PINC) |
221                             (0 << STM_DMA_CCR_CIRC) |
222                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
223
224         /* Clear RXNE */
225         (void) stm_spi->dr;
226
227         /* Set up the receive DMA to capture data */
228         ao_dma_set_transfer(miso_dma_index,
229                             &stm_spi->dr,
230                             block,
231                             len,
232                             (0 << STM_DMA_CCR_MEM2MEM) |
233                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
234                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
235                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
236                             (1 << STM_DMA_CCR_MINC) |
237                             (0 << STM_DMA_CCR_PINC) |
238                             (0 << STM_DMA_CCR_CIRC) |
239                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
240
241         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
242                         (0 << STM_SPI_CR2_RXNEIE) |
243                         (0 << STM_SPI_CR2_ERRIE) |
244                         (0 << STM_SPI_CR2_SSOE) |
245                         (1 << STM_SPI_CR2_TXDMAEN) |
246                         (1 << STM_SPI_CR2_RXDMAEN));
247 }
248
249 static void
250 ao_spi_start_recv(uint8_t spi_index)
251 {
252         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
253         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
254         ao_dma_start(miso_dma_index);
255         ao_dma_start(mosi_dma_index);
256 }
257
258 static void
259 ao_spi_done_recv(uint8_t spi_index)
260 {
261         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
262         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
263         ao_dma_done_transfer(mosi_dma_index);
264         ao_dma_done_transfer(miso_dma_index);
265 }
266
267 void
268 ao_spi_queue_recv(void *block, uint16_t len, uint8_t spi_index, void (*callback)(int spi_index))
269 {
270         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
271         ao_spi_setup_recv(block, len, spi_index);
272         ao_spi_callback[spi_index] = callback;
273         ao_dma_set_isr(miso_dma_index, ao_spi_dma_isr, spi_index);
274         ao_spi_start_recv(spi_index);
275 }
276
277 void
278 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
279 {
280         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
281         ao_spi_setup_recv(block, len, spi_index);
282         ao_spi_start_recv(spi_index);
283         /* Wait until the SPI unit is done */
284         ao_arch_critical(
285                 while (!ao_dma_done[miso_dma_index])
286                         ao_sleep(&ao_dma_done[miso_dma_index]);
287                 );
288         ao_spi_done_recv(spi_index);
289 }
290
291 void
292 ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
293 {
294         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
295         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
296         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
297
298         /* Set up transmit DMA to send data */
299         ao_dma_set_transfer(mosi_dma_index,
300                             &stm_spi->dr,
301                             out,
302                             len,
303                             (0 << STM_DMA_CCR_MEM2MEM) |
304                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
305                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
306                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
307                             (1 << STM_DMA_CCR_MINC) |
308                             (0 << STM_DMA_CCR_PINC) |
309                             (0 << STM_DMA_CCR_CIRC) |
310                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
311
312         /* Clear RXNE */
313         (void) stm_spi->dr;
314
315         /* Set up the receive DMA to capture data */
316         ao_dma_set_transfer(miso_dma_index,
317                             &stm_spi->dr,
318                             in,
319                             len,
320                             (0 << STM_DMA_CCR_MEM2MEM) |
321                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
322                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
323                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
324                             (1 << STM_DMA_CCR_MINC) |
325                             (0 << STM_DMA_CCR_PINC) |
326                             (0 << STM_DMA_CCR_CIRC) |
327                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
328
329         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
330                         (0 << STM_SPI_CR2_RXNEIE) |
331                         (0 << STM_SPI_CR2_ERRIE) |
332                         (0 << STM_SPI_CR2_SSOE) |
333                         (1 << STM_SPI_CR2_TXDMAEN) |
334                         (1 << STM_SPI_CR2_RXDMAEN));
335         ao_dma_start(miso_dma_index);
336         ao_dma_start(mosi_dma_index);
337
338         /* Wait until the SPI unit is done */
339         ao_arch_critical(
340                 while (!ao_dma_done[miso_dma_index])
341                         ao_sleep(&ao_dma_done[miso_dma_index]);
342                 );
343
344         ao_dma_done_transfer(mosi_dma_index);
345         ao_dma_done_transfer(miso_dma_index);
346 }
347
348 void
349 ao_spi_get(uint8_t spi_index)
350 {
351         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
352
353         ao_mutex_get(&ao_spi_mutex[spi_index]);
354         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
355                         (0 << STM_SPI_CR1_BIDIOE) |
356                         (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
357                         (0 << STM_SPI_CR1_CRCNEXT) |
358                         (0 << STM_SPI_CR1_DFF) |
359                         (0 << STM_SPI_CR1_RXONLY) |
360                         (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
361                         (1 << STM_SPI_CR1_SSI) |                        /*  ... */
362                         (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
363                         (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
364                         (STM_SPI_CR1_BR_PCLK_4 << STM_SPI_CR1_BR) |     /* baud rate to pclk/4 */
365                         (1 << STM_SPI_CR1_MSTR) |
366                         (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
367                         (0 << STM_SPI_CR1_CPHA));
368 }
369
370 void
371 ao_spi_put(uint8_t spi_index)
372 {
373         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
374
375         stm_spi->cr1 = 0;
376         ao_mutex_put(&ao_spi_mutex[spi_index]);
377 }
378
379 static void
380 ao_spi_channel_init(uint8_t spi_index)
381 {
382         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
383
384         stm_spi->cr1 = 0;
385         (void) stm_spi->sr;
386         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
387                         (0 << STM_SPI_CR2_RXNEIE) |
388                         (0 << STM_SPI_CR2_ERRIE) |
389                         (0 << STM_SPI_CR2_SSOE) |
390                         (0 << STM_SPI_CR2_TXDMAEN) |
391                         (0 << STM_SPI_CR2_RXDMAEN));
392 }
393
394 void
395 ao_spi_init(void)
396 {
397 #if HAS_SPI_1
398 # if SPI_1_PA5_PA6_PA7
399         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
400         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
401         stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
402         stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
403 # else
404 #  if SPI_1_PB3_PB4_PB5
405         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
406         stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
407         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
408         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
409 #  else
410 #   if SPI_1_PE13_PE14_PE15
411         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
412         stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
413         stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
414         stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
415 #   else
416 #    error "No SPI_1 port configuration specified"
417 #   endif
418 #  endif
419 # endif
420
421         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
422
423         ao_spi_channel_init(0);
424 #endif
425
426 #if HAS_SPI_2
427 # if SPI_2_PB13_PB14_PB15
428         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
429         stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
430         stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
431         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
432 # else
433 #  if SPI_2_PPD1_PD3_PD4
434         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
435         stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
436         stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
437         stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
438 #  else
439 #   error "No SPI_2 port configuration specified"
440 #  endif
441 # endif
442
443         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
444
445         ao_spi_channel_init(1);
446 #endif
447 }