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