improve test procedures for TeleMega
[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 static uint8_t          ao_spi_mutex[STM_NUM_SPI];
27 static uint8_t          ao_spi_index[STM_NUM_SPI];
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 void
45 ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
46 {
47         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
48         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
49         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(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         ao_dma_start(miso_dma_index);
91         ao_dma_start(mosi_dma_index);
92         ao_arch_critical(
93                 while (!ao_dma_done[miso_dma_index])
94                         ao_sleep(&ao_dma_done[miso_dma_index]);
95                 );
96         ao_dma_done_transfer(mosi_dma_index);
97         ao_dma_done_transfer(miso_dma_index);
98 }
99
100 void
101 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
102 {
103         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
104         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
105         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
106
107         /* Set up the transmit DMA to deliver data */
108         ao_dma_set_transfer(mosi_dma_index,
109                             &stm_spi->dr,
110                             &value,
111                             len,
112                             (0 << STM_DMA_CCR_MEM2MEM) |
113                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
114                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
115                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
116                             (0 << STM_DMA_CCR_MINC) |
117                             (0 << STM_DMA_CCR_PINC) |
118                             (0 << STM_DMA_CCR_CIRC) |
119                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
120
121         /* Clear RXNE */
122         (void) stm_spi->dr;
123
124         /* Set up the receive DMA -- when this is done, we know the SPI unit
125          * is idle. Without this, we'd have to poll waiting for the BSY bit to
126          * be cleared
127          */
128         ao_dma_set_transfer(miso_dma_index,
129                             &stm_spi->dr,
130                             &spi_dev_null,
131                             len,
132                             (0 << STM_DMA_CCR_MEM2MEM) |
133                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
134                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
135                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
136                             (0 << STM_DMA_CCR_MINC) |
137                             (0 << STM_DMA_CCR_PINC) |
138                             (0 << STM_DMA_CCR_CIRC) |
139                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
140         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
141                         (0 << STM_SPI_CR2_RXNEIE) |
142                         (0 << STM_SPI_CR2_ERRIE) |
143                         (0 << STM_SPI_CR2_SSOE) |
144                         (1 << STM_SPI_CR2_TXDMAEN) |
145                         (1 << STM_SPI_CR2_RXDMAEN));
146         ao_dma_start(miso_dma_index);
147         ao_dma_start(mosi_dma_index);
148         ao_arch_critical(
149                 while (!ao_dma_done[miso_dma_index])
150                         ao_sleep(&ao_dma_done[miso_dma_index]);
151                 );
152         ao_dma_done_transfer(mosi_dma_index);
153         ao_dma_done_transfer(miso_dma_index);
154 }
155
156 void
157 ao_spi_send_sync(void *block, uint16_t len, uint8_t spi_index)
158 {
159         uint8_t         *b = block;
160         struct stm_spi  *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
161
162         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
163                         (0 << STM_SPI_CR2_RXNEIE) |
164                         (0 << STM_SPI_CR2_ERRIE) |
165                         (0 << STM_SPI_CR2_SSOE) |
166                         (0 << STM_SPI_CR2_TXDMAEN) |
167                         (0 << STM_SPI_CR2_RXDMAEN));
168
169         /* Clear RXNE */
170         (void) stm_spi->dr;
171
172         while (len--) {
173                 while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
174                 stm_spi->dr = *b++;
175         }
176 }
177
178 void
179 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
180 {
181         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
182         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
183         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
184
185         spi_dev_null = 0xff;
186
187         /* Set up transmit DMA to make the SPI hardware actually run */
188         ao_dma_set_transfer(mosi_dma_index,
189                             &stm_spi->dr,
190                             &spi_dev_null,
191                             len,
192                             (0 << STM_DMA_CCR_MEM2MEM) |
193                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
194                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
195                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
196                             (0 << STM_DMA_CCR_MINC) |
197                             (0 << STM_DMA_CCR_PINC) |
198                             (0 << STM_DMA_CCR_CIRC) |
199                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
200
201         /* Clear RXNE */
202         (void) stm_spi->dr;
203
204         /* Set up the receive DMA to capture data */
205         ao_dma_set_transfer(miso_dma_index,
206                             &stm_spi->dr,
207                             block,
208                             len,
209                             (0 << STM_DMA_CCR_MEM2MEM) |
210                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
211                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
212                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
213                             (1 << STM_DMA_CCR_MINC) |
214                             (0 << STM_DMA_CCR_PINC) |
215                             (0 << STM_DMA_CCR_CIRC) |
216                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
217
218         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
219                         (0 << STM_SPI_CR2_RXNEIE) |
220                         (0 << STM_SPI_CR2_ERRIE) |
221                         (0 << STM_SPI_CR2_SSOE) |
222                         (1 << STM_SPI_CR2_TXDMAEN) |
223                         (1 << STM_SPI_CR2_RXDMAEN));
224         ao_dma_start(miso_dma_index);
225         ao_dma_start(mosi_dma_index);
226
227         /* Wait until the SPI unit is done */
228         ao_arch_critical(
229                 while (!ao_dma_done[miso_dma_index])
230                         ao_sleep(&ao_dma_done[miso_dma_index]);
231                 );
232
233         ao_dma_done_transfer(mosi_dma_index);
234         ao_dma_done_transfer(miso_dma_index);
235 }
236
237 void
238 ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
239 {
240         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
241         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
242         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
243
244         /* Set up transmit DMA to send data */
245         ao_dma_set_transfer(mosi_dma_index,
246                             &stm_spi->dr,
247                             out,
248                             len,
249                             (0 << STM_DMA_CCR_MEM2MEM) |
250                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
251                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
252                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
253                             (1 << STM_DMA_CCR_MINC) |
254                             (0 << STM_DMA_CCR_PINC) |
255                             (0 << STM_DMA_CCR_CIRC) |
256                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
257
258         /* Clear RXNE */
259         (void) stm_spi->dr;
260
261         /* Set up the receive DMA to capture data */
262         ao_dma_set_transfer(miso_dma_index,
263                             &stm_spi->dr,
264                             in,
265                             len,
266                             (0 << STM_DMA_CCR_MEM2MEM) |
267                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
268                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
269                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
270                             (1 << STM_DMA_CCR_MINC) |
271                             (0 << STM_DMA_CCR_PINC) |
272                             (0 << STM_DMA_CCR_CIRC) |
273                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
274
275         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
276                         (0 << STM_SPI_CR2_RXNEIE) |
277                         (0 << STM_SPI_CR2_ERRIE) |
278                         (0 << STM_SPI_CR2_SSOE) |
279                         (1 << STM_SPI_CR2_TXDMAEN) |
280                         (1 << STM_SPI_CR2_RXDMAEN));
281         ao_dma_start(miso_dma_index);
282         ao_dma_start(mosi_dma_index);
283
284         /* Wait until the SPI unit is done */
285         ao_arch_critical(
286                 while (!ao_dma_done[miso_dma_index])
287                         ao_sleep(&ao_dma_done[miso_dma_index]);
288                 );
289
290         ao_dma_done_transfer(mosi_dma_index);
291         ao_dma_done_transfer(miso_dma_index);
292 }
293
294 static void
295 ao_spi_disable_index(uint8_t spi_index)
296 {
297         /* Disable current config
298          */
299         switch (AO_SPI_INDEX(spi_index)) {
300         case STM_SPI_INDEX(1):
301                 switch (spi_index) {
302                 case AO_SPI_1_PA5_PA6_PA7:
303                         stm_gpio_set(&stm_gpioa, 5, 1);
304                         stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
305                         stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
306                         stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
307                         break;
308                 case AO_SPI_1_PB3_PB4_PB5:
309                         stm_gpio_set(&stm_gpiob, 3, 1);
310                         stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
311                         stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
312                         stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
313                         break;
314                 case AO_SPI_1_PE13_PE14_PE15:
315                         stm_gpio_set(&stm_gpioe, 13, 1);
316                         stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
317                         stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
318                         stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
319                         break;
320                 }
321                 break;
322         case STM_SPI_INDEX(2):
323                 switch (spi_index) {
324                 case AO_SPI_2_PB13_PB14_PB15:
325                         stm_gpio_set(&stm_gpiob, 13, 1);
326                         stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
327                         stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
328                         stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
329                         break;
330                 case AO_SPI_2_PD1_PD3_PD4:
331                         stm_gpio_set(&stm_gpiod, 1, 1);
332                         stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
333                         stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
334                         stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
335                         break;
336                 }
337                 break;
338         }
339 }
340
341 static void
342 ao_spi_enable_index(uint8_t spi_index)
343 {
344         switch (AO_SPI_INDEX(spi_index)) {
345         case STM_SPI_INDEX(1):
346                 switch (spi_index) {
347                 case AO_SPI_1_PA5_PA6_PA7:
348                         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
349                         stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
350                         stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
351                         break;
352                 case AO_SPI_1_PB3_PB4_PB5:
353                         stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
354                         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
355                         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
356                         break;
357                 case AO_SPI_1_PE13_PE14_PE15:
358                         stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
359                         stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
360                         stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
361                         break;
362                 }
363                 break;
364         case STM_SPI_INDEX(2):
365                 switch (spi_index) {
366                 case AO_SPI_2_PB13_PB14_PB15:
367                         stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
368                         stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
369                         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
370                         break;
371                 case AO_SPI_2_PD1_PD3_PD4:
372                         stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
373                         stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
374                         stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
375                         break;
376                 }
377                 break;
378         }
379 }
380
381 static void
382 ao_spi_config(uint8_t spi_index, uint32_t speed)
383 {
384         uint8_t         id = AO_SPI_INDEX(spi_index);
385         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
386         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
387                         (0 << STM_SPI_CR1_BIDIOE) |
388                         (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
389                         (0 << STM_SPI_CR1_CRCNEXT) |
390                         (0 << STM_SPI_CR1_DFF) |
391                         (0 << STM_SPI_CR1_RXONLY) |
392                         (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
393                         (1 << STM_SPI_CR1_SSI) |                        /*  ... */
394                         (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
395                         (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
396                         (speed << STM_SPI_CR1_BR) |     /* baud rate to pclk/4 */
397                         (1 << STM_SPI_CR1_MSTR) |
398                         (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
399                         (0 << STM_SPI_CR1_CPHA));
400         if (spi_index != ao_spi_index[id]) {
401
402                 /* Disable old config
403                  */
404                 ao_spi_disable_index(ao_spi_index[id]);
405
406                 /* Enable new config
407                  */
408                 ao_spi_enable_index(spi_index);
409
410                 /* Remember current config
411                  */
412                 ao_spi_index[id] = spi_index;
413         }
414 }
415
416 uint8_t
417 ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
418 {
419         uint8_t         id = AO_SPI_INDEX(spi_index);
420
421         if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
422                 return 0;
423         ao_spi_config(spi_index, speed);
424         return 1;
425 }
426
427 void
428 ao_spi_get(uint8_t spi_index, uint32_t speed)
429 {
430         uint8_t         id = AO_SPI_INDEX(spi_index);
431         ao_mutex_get(&ao_spi_mutex[id]);
432         ao_spi_config(spi_index, speed);
433 }
434
435 void
436 ao_spi_put(uint8_t spi_index)
437 {
438         uint8_t         id = AO_SPI_INDEX(spi_index);
439         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
440
441         stm_spi->cr1 = 0;
442         ao_mutex_put(&ao_spi_mutex[id]);
443 }
444
445 static void
446 ao_spi_channel_init(uint8_t spi_index)
447 {
448         uint8_t         id = AO_SPI_INDEX(spi_index);
449         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
450
451         ao_spi_disable_index(spi_index);
452
453         stm_spi->cr1 = 0;
454         (void) stm_spi->sr;
455         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
456                         (0 << STM_SPI_CR2_RXNEIE) |
457                         (0 << STM_SPI_CR2_ERRIE) |
458                         (0 << STM_SPI_CR2_SSOE) |
459                         (0 << STM_SPI_CR2_TXDMAEN) |
460                         (0 << STM_SPI_CR2_RXDMAEN));
461 }
462
463 void
464 ao_spi_init(void)
465 {
466 #if HAS_SPI_1
467 # if SPI_1_PA5_PA6_PA7
468         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
469         stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
470         stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
471         stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
472 # endif
473 # if SPI_1_PB3_PB4_PB5
474         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
475         stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
476         stm_ospeedr_set(&stm_gpiob, 4, SPI_1_OSPEEDR);
477         stm_ospeedr_set(&stm_gpiob, 5, SPI_1_OSPEEDR);
478 # endif
479 # if SPI_1_PE13_PE14_PE15
480         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
481         stm_ospeedr_set(&stm_gpioe, 13, SPI_1_OSPEEDR);
482         stm_ospeedr_set(&stm_gpioe, 14, SPI_1_OSPEEDR);
483         stm_ospeedr_set(&stm_gpioe, 15, SPI_1_OSPEEDR);
484 # endif
485         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
486         ao_spi_index[0] = AO_SPI_CONFIG_NONE;
487         ao_spi_channel_init(0);
488 #endif
489
490 #if HAS_SPI_2
491 # if SPI_2_PB13_PB14_PB15
492         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
493         stm_ospeedr_set(&stm_gpiob, 13, SPI_2_OSPEEDR);
494         stm_ospeedr_set(&stm_gpiob, 14, SPI_2_OSPEEDR);
495         stm_ospeedr_set(&stm_gpiob, 15, SPI_2_OSPEEDR);
496 # endif
497 # if SPI_2_PD1_PD3_PD4
498         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
499         stm_ospeedr_set(&stm_gpiod, 1, SPI_2_OSPEEDR);
500         stm_ospeedr_set(&stm_gpiod, 3, SPI_2_OSPEEDR);
501         stm_ospeedr_set(&stm_gpiod, 4, SPI_2_OSPEEDR);
502 # endif
503         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
504         ao_spi_index[1] = AO_SPI_CONFIG_NONE;
505         ao_spi_channel_init(1);
506 #endif
507 }