altos/telefireone-v1.0: Track ao_led_init API change
[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; 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_pin_config[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 #if DEBUG
46 static struct {
47         uint8_t task;
48         uint8_t which;
49         AO_TICK_TYPE tick;
50         uint16_t len;
51 } spi_tasks[64];
52 static uint8_t  spi_task_index;
53
54 static void
55 validate_spi(struct stm_spi *stm_spi, int which, uint16_t len)
56 {
57         uint32_t        sr = stm_spi->sr;
58
59         if (stm_spi != &stm_spi2)
60                 return;
61         spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0;
62         spi_tasks[spi_task_index].which = which;
63         spi_tasks[spi_task_index].tick = ao_time();
64         spi_tasks[spi_task_index].len = len;
65         spi_task_index = (spi_task_index + 1) & (63);
66         if (sr & (1 << STM_SPI_SR_FRE))
67                 ao_panic(0x40 | 1);
68         if (sr & (1 << STM_SPI_SR_BSY))
69                 ao_panic(0x40 | 2);
70         if (sr & (1 << STM_SPI_SR_OVR))
71                 ao_panic(0x40 | 3);
72         if (sr & (1 << STM_SPI_SR_MODF))
73                 ao_panic(0x40 | 4);
74         if (sr & (1 << STM_SPI_SR_UDR))
75                 ao_panic(0x40 | 5);
76         if ((sr & (1 << STM_SPI_SR_TXE)) == 0)
77                 ao_panic(0x40 | 6);
78         if (sr & (1 << STM_SPI_SR_RXNE))
79                 ao_panic(0x40 | 7);
80         if (which != 5 && which != 6 && which != 13)
81                 if (ao_cur_task->task_id != ao_spi_mutex[1])
82                         ao_panic(0x40 | 8);
83 }
84 #else
85 #define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0)
86 #endif
87
88 static void
89 ao_spi_set_dma_mosi(uint8_t id, const void *data, uint16_t len, uint32_t minc)
90 {
91         struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
92         uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
93
94         ao_dma_set_transfer(mosi_dma_index,
95                             &stm_spi->dr,
96                             (void *) data,
97                             len,
98                             (0 << STM_DMA_CCR_MEM2MEM) |
99                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
100                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
101                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
102                             (minc << STM_DMA_CCR_MINC) |
103                             (0 << STM_DMA_CCR_PINC) |
104                             (0 << STM_DMA_CCR_CIRC) |
105                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
106 }
107
108 static void
109 ao_spi_set_dma_miso(uint8_t id, void *data, uint16_t len, uint32_t minc)
110 {
111         struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
112         uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
113
114         ao_dma_set_transfer(miso_dma_index,
115                             &stm_spi->dr,
116                             data,
117                             len,
118                             (0 << STM_DMA_CCR_MEM2MEM) |
119                             (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
120                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
121                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
122                             (minc << STM_DMA_CCR_MINC) |
123                             (0 << STM_DMA_CCR_PINC) |
124                             (0 << STM_DMA_CCR_CIRC) |
125                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
126 }
127
128 static void
129 ao_spi_run(uint8_t id, uint8_t which, uint16_t len)
130 {
131         struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
132         uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
133         uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
134
135         validate_spi(stm_spi, which, len);
136
137         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
138                         (0 << STM_SPI_CR2_RXNEIE) |
139                         (0 << STM_SPI_CR2_ERRIE) |
140                         (0 << STM_SPI_CR2_SSOE) |
141                         (1 << STM_SPI_CR2_TXDMAEN) |
142                         (1 << STM_SPI_CR2_RXDMAEN));
143
144         ao_dma_start(miso_dma_index);
145         ao_dma_start(mosi_dma_index);
146
147         ao_arch_critical(
148                 while (!ao_dma_done[miso_dma_index])
149                         ao_sleep(&ao_dma_done[miso_dma_index]);
150                 );
151
152         while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0);
153         while (stm_spi->sr & (1 << STM_SPI_SR_BSY));
154
155         validate_spi(stm_spi, which+1, len);
156
157         stm_spi->cr2 = 0;
158
159         ao_dma_done_transfer(mosi_dma_index);
160         ao_dma_done_transfer(miso_dma_index);
161 }
162
163 void
164 ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
165 {
166         uint8_t id = AO_SPI_INDEX(spi_index);
167
168         /* Set up the transmit DMA to deliver data */
169         ao_spi_set_dma_mosi(id, block, len, 1);
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_spi_set_dma_miso(id, &spi_dev_null, len, 0);
176
177         ao_spi_run(id, 1, len);
178 }
179
180 void
181 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
182 {
183         uint8_t id = AO_SPI_INDEX(spi_index);
184
185         /* Set up the transmit DMA to deliver data */
186         ao_spi_set_dma_mosi(id, &value, len, 0);
187
188         /* Set up the receive DMA -- when this is done, we know the SPI unit
189          * is idle. Without this, we'd have to poll waiting for the BSY bit to
190          * be cleared
191          */
192         ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
193
194         ao_spi_run(id, 3, len);
195 }
196
197 void
198 ao_spi_start_bytes(uint8_t spi_index)
199 {
200         uint8_t         id = AO_SPI_INDEX(spi_index);
201         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
202
203         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
204                         (0 << STM_SPI_CR2_RXNEIE) |
205                         (0 << STM_SPI_CR2_ERRIE) |
206                         (0 << STM_SPI_CR2_SSOE) |
207                         (0 << STM_SPI_CR2_TXDMAEN) |
208                         (0 << STM_SPI_CR2_RXDMAEN));
209         validate_spi(stm_spi, 5, 0xffff);
210 }
211
212 void
213 ao_spi_stop_bytes(uint8_t spi_index)
214 {
215         uint8_t         id = AO_SPI_INDEX(spi_index);
216         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
217
218         while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
219                 ;
220         while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
221                 ;
222         /* Clear the OVR flag */
223         (void) stm_spi->dr;
224         (void) stm_spi->sr;
225         validate_spi(stm_spi, 6, 0xffff);
226         stm_spi->cr2 = 0;
227 }
228
229 void
230 ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index)
231 {
232         uint8_t         id = AO_SPI_INDEX(spi_index);
233         const uint8_t   *b = block;
234         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
235
236         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
237                         (0 << STM_SPI_CR2_RXNEIE) |
238                         (0 << STM_SPI_CR2_ERRIE) |
239                         (0 << STM_SPI_CR2_SSOE) |
240                         (0 << STM_SPI_CR2_TXDMAEN) |
241                         (0 << STM_SPI_CR2_RXDMAEN));
242         validate_spi(stm_spi, 7, len);
243         while (len--) {
244                 while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
245                 stm_spi->dr = *b++;
246         }
247         while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
248                 ;
249         while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
250                 ;
251         /* Clear the OVR flag */
252         (void) stm_spi->dr;
253         (void) stm_spi->sr;
254         validate_spi(stm_spi, 8, len);
255 }
256
257 void
258 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
259 {
260         uint8_t         id = AO_SPI_INDEX(spi_index);
261
262         spi_dev_null = 0xff;
263
264         /* Set up transmit DMA to make the SPI hardware actually run */
265         ao_spi_set_dma_mosi(id, &spi_dev_null, len, 0);
266
267         /* Set up the receive DMA to capture data */
268         ao_spi_set_dma_miso(id, block, len, 1);
269
270         ao_spi_run(id, 9, len);
271 }
272
273 void
274 ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
275 {
276         uint8_t         id = AO_SPI_INDEX(spi_index);
277
278         /* Set up transmit DMA to send data */
279         ao_spi_set_dma_mosi(id, out, len, 1);
280
281         /* Set up the receive DMA to capture data */
282         ao_spi_set_dma_miso(id, in, len, 1);
283
284         ao_spi_run(id, 11, len);
285 }
286
287 static void
288 ao_spi_disable_pin_config(uint8_t spi_pin_config)
289 {
290         /* Disable current config
291          */
292         switch (spi_pin_config) {
293         case AO_SPI_1_PA5_PA6_PA7:
294                 stm_gpio_set(&stm_gpioa, 5, 1);
295                 stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
296                 stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
297                 stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
298                 break;
299         case AO_SPI_1_PB3_PB4_PB5:
300                 stm_gpio_set(&stm_gpiob, 3, 1);
301                 stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
302                 stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
303                 stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
304                 break;
305         case AO_SPI_1_PE13_PE14_PE15:
306                 stm_gpio_set(&stm_gpioe, 13, 1);
307                 stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
308                 stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
309                 stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
310                 break;
311         case AO_SPI_2_PB13_PB14_PB15:
312                 stm_gpio_set(&stm_gpiob, 13, 1);
313                 stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
314                 stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
315                 stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
316                 break;
317         case AO_SPI_2_PD1_PD3_PD4:
318                 stm_gpio_set(&stm_gpiod, 1, 1);
319                 stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
320                 stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
321                 stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
322                 break;
323         }
324 }
325
326 static void
327 ao_spi_enable_pin_config(uint8_t spi_pin_config)
328 {
329         /* Enable new config
330          */
331         switch (spi_pin_config) {
332         case AO_SPI_1_PA5_PA6_PA7:
333                 stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
334                 stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
335                 stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
336                 break;
337         case AO_SPI_1_PB3_PB4_PB5:
338                 stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
339                 stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
340                 stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
341                 break;
342         case AO_SPI_1_PE13_PE14_PE15:
343                 stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
344                 stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
345                 stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
346                 break;
347         case AO_SPI_2_PB13_PB14_PB15:
348                 stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
349                 stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
350                 stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
351                 break;
352         case AO_SPI_2_PD1_PD3_PD4:
353                 stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
354                 stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
355                 stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
356                 break;
357         }
358 }
359
360 static void
361 ao_spi_config(uint8_t spi_index, uint32_t speed)
362 {
363         uint8_t         spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
364         uint8_t         id = AO_SPI_INDEX(spi_index);
365         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
366
367         if (spi_pin_config != ao_spi_pin_config[id]) {
368
369                 /* Disable old config
370                  */
371                 ao_spi_disable_pin_config(ao_spi_pin_config[id]);
372
373                 /* Enable new config
374                  */
375                 ao_spi_enable_pin_config(spi_pin_config);
376
377                 /* Remember current config
378                  */
379                 ao_spi_pin_config[id] = spi_pin_config;
380         }
381
382         /* Turn the SPI transceiver on and set the mode */
383         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |           /* Three wire mode */
384                         (0 << STM_SPI_CR1_BIDIOE) |
385                         (0 << STM_SPI_CR1_CRCEN) |              /* CRC disabled */
386                         (0 << STM_SPI_CR1_CRCNEXT) |
387                         (0 << STM_SPI_CR1_DFF) |
388                         (0 << STM_SPI_CR1_RXONLY) |
389                         (1 << STM_SPI_CR1_SSM) |                /* Software SS handling */
390                         (1 << STM_SPI_CR1_SSI) |                /*  ... */
391                         (0 << STM_SPI_CR1_LSBFIRST) |           /* Big endian */
392                         (1 << STM_SPI_CR1_SPE) |                /* Enable SPI unit */
393                         (speed << STM_SPI_CR1_BR) |             /* baud rate to pclk/4 */
394                         (1 << STM_SPI_CR1_MSTR) |
395                         (AO_SPI_CPOL(spi_index) << STM_SPI_CR1_CPOL) |  /* Format */
396                         (AO_SPI_CPHA(spi_index) << STM_SPI_CR1_CPHA));
397         validate_spi(stm_spi, 13, 0);
398 }
399
400 uint8_t
401 ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
402 {
403         uint8_t         id = AO_SPI_INDEX(spi_index);
404
405         if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
406                 return 0;
407         ao_spi_config(spi_index, speed);
408         return 1;
409 }
410
411 void
412 ao_spi_get(uint8_t spi_index, uint32_t speed)
413 {
414         uint8_t         id = AO_SPI_INDEX(spi_index);
415
416         ao_mutex_get(&ao_spi_mutex[id]);
417         ao_spi_config(spi_index, speed);
418 }
419
420 void
421 ao_spi_put(uint8_t spi_index)
422 {
423         uint8_t         id = AO_SPI_INDEX(spi_index);
424         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
425
426         stm_spi->cr1 = 0;
427         ao_mutex_put(&ao_spi_mutex[id]);
428 }
429
430 static void
431 ao_spi_channel_init(uint8_t spi_index)
432 {
433         uint8_t         id = AO_SPI_INDEX(spi_index);
434         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
435
436         ao_spi_disable_pin_config(AO_SPI_PIN_CONFIG(spi_index));
437
438         stm_spi->cr1 = 0;
439         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
440                         (0 << STM_SPI_CR2_RXNEIE) |
441                         (0 << STM_SPI_CR2_ERRIE) |
442                         (0 << STM_SPI_CR2_SSOE) |
443                         (0 << STM_SPI_CR2_TXDMAEN) |
444                         (0 << STM_SPI_CR2_RXDMAEN));
445
446         /* Clear any pending data and error flags */
447         (void) stm_spi->dr;
448         (void) stm_spi->sr;
449 }
450
451 #if DEBUG
452 void
453 ao_spi_dump_cmd(void)
454 {
455         int s;
456
457         for (s = 0; s < 64; s++) {
458                 int i = (spi_task_index + s) & 63;
459                 if (spi_tasks[i].which) {
460                         int t;
461                         const char *name = "(none)";
462                         for (t = 0; t < ao_num_tasks; t++)
463                                 if (ao_tasks[t]->task_id == spi_tasks[i].task) {
464                                         name = ao_tasks[t]->name;
465                                         break;
466                                 }
467                         printf("%2d: %5d task %2d which %2d len %5d %s\n",
468                                s,
469                                spi_tasks[i].tick,
470                                spi_tasks[i].task,
471                                spi_tasks[i].which,
472                                spi_tasks[i].len,
473                                name);
474                 }
475         }
476         for (s = 0; s < STM_NUM_SPI; s++) {
477                 struct stm_spi *spi = ao_spi_stm_info[s].stm_spi;
478
479                 printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d",
480                        s, ao_spi_mutex[s], ao_spi_index[s],
481                        ao_spi_stm_info[s].miso_dma_index,
482                        ao_spi_stm_info[s].mosi_dma_index);
483                 printf(" cr1 %04x cr2 %02x sr %03x\n",
484                        spi->cr1, spi->cr2, spi->sr);
485         }
486
487 }
488
489 static const struct ao_cmds ao_spi_cmds[] = {
490         { ao_spi_dump_cmd,      "S\0Dump SPI status" },
491         { 0, NULL }
492 };
493 #endif
494
495 void
496 ao_spi_init(void)
497 {
498 #if HAS_SPI_1
499 # if SPI_1_PA5_PA6_PA7
500         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
501         stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
502         stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
503         stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
504 # endif
505 # if SPI_1_PB3_PB4_PB5
506         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
507         stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
508         stm_ospeedr_set(&stm_gpiob, 4, SPI_1_OSPEEDR);
509         stm_ospeedr_set(&stm_gpiob, 5, SPI_1_OSPEEDR);
510 # endif
511 # if SPI_1_PE13_PE14_PE15
512         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
513         stm_ospeedr_set(&stm_gpioe, 13, SPI_1_OSPEEDR);
514         stm_ospeedr_set(&stm_gpioe, 14, SPI_1_OSPEEDR);
515         stm_ospeedr_set(&stm_gpioe, 15, SPI_1_OSPEEDR);
516 # endif
517         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
518         ao_spi_pin_config[0] = AO_SPI_CONFIG_NONE;
519         ao_spi_channel_init(0);
520 #endif
521
522 #if HAS_SPI_2
523 # if SPI_2_PB13_PB14_PB15
524         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
525         stm_ospeedr_set(&stm_gpiob, 13, SPI_2_OSPEEDR);
526         stm_ospeedr_set(&stm_gpiob, 14, SPI_2_OSPEEDR);
527         stm_ospeedr_set(&stm_gpiob, 15, SPI_2_OSPEEDR);
528 # endif
529 # if SPI_2_PD1_PD3_PD4
530         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
531         stm_ospeedr_set(&stm_gpiod, 1, SPI_2_OSPEEDR);
532         stm_ospeedr_set(&stm_gpiod, 3, SPI_2_OSPEEDR);
533         stm_ospeedr_set(&stm_gpiod, 4, SPI_2_OSPEEDR);
534 # endif
535         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
536         ao_spi_pin_config[1] = AO_SPI_CONFIG_NONE;
537         ao_spi_channel_init(1);
538 #endif
539 #if DEBUG
540         ao_cmd_register(&ao_spi_cmds[0]);
541 #endif
542 }