2 * Copyright © 2012 Keith Packard <keithp@keithp.com>
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.
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.
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.
20 struct ao_spi_stm_info {
21 uint8_t miso_dma_index;
22 uint8_t mosi_dma_index;
23 struct stm_spi *stm_spi;
26 static uint8_t ao_spi_mutex[STM_NUM_SPI];
27 static uint8_t ao_spi_index[STM_NUM_SPI];
29 static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
31 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
32 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
36 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
37 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
42 static uint8_t spi_dev_null;
51 static uint8_t spi_task_index;
54 validate_spi(struct stm_spi *stm_spi, int which, uint16_t len)
56 uint32_t sr = stm_spi->sr;
58 if (stm_spi != &stm_spi2)
60 spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0;
61 spi_tasks[spi_task_index].which = which;
62 spi_tasks[spi_task_index].tick = ao_time();
63 spi_tasks[spi_task_index].len = len;
64 spi_task_index = (spi_task_index + 1) & (63);
65 if (sr & (1 << STM_SPI_SR_FRE))
67 if (sr & (1 << STM_SPI_SR_BSY))
69 if (sr & (1 << STM_SPI_SR_OVR))
71 if (sr & (1 << STM_SPI_SR_MODF))
73 if (sr & (1 << STM_SPI_SR_UDR))
75 if ((sr & (1 << STM_SPI_SR_TXE)) == 0)
77 if (sr & (1 << STM_SPI_SR_RXNE))
79 if (which != 5 && which != 6 && which != 13)
80 if (ao_cur_task->task_id != ao_spi_mutex[1])
84 #define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0)
88 ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
90 struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
91 uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
92 uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
94 /* Set up the transmit DMA to deliver data */
95 ao_dma_set_transfer(mosi_dma_index,
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 (1 << STM_DMA_CCR_MINC) |
104 (0 << STM_DMA_CCR_PINC) |
105 (0 << STM_DMA_CCR_CIRC) |
106 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
111 /* Set up the receive DMA -- when this is done, we know the SPI unit
112 * is idle. Without this, we'd have to poll waiting for the BSY bit to
115 ao_dma_set_transfer(miso_dma_index,
119 (0 << STM_DMA_CCR_MEM2MEM) |
120 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
121 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
122 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
123 (0 << STM_DMA_CCR_MINC) |
124 (0 << STM_DMA_CCR_PINC) |
125 (0 << STM_DMA_CCR_CIRC) |
126 (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
127 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
128 (0 << STM_SPI_CR2_RXNEIE) |
129 (0 << STM_SPI_CR2_ERRIE) |
130 (0 << STM_SPI_CR2_SSOE) |
131 (1 << STM_SPI_CR2_TXDMAEN) |
132 (1 << STM_SPI_CR2_RXDMAEN));
133 ao_dma_start(miso_dma_index);
134 ao_dma_start(mosi_dma_index);
136 while (!ao_dma_done[miso_dma_index])
137 ao_sleep(&ao_dma_done[miso_dma_index]);
139 ao_dma_done_transfer(mosi_dma_index);
140 ao_dma_done_transfer(miso_dma_index);
144 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
146 struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
147 uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
148 uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
150 /* Set up the transmit DMA to deliver data */
151 ao_dma_set_transfer(mosi_dma_index,
155 (0 << STM_DMA_CCR_MEM2MEM) |
156 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
157 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
158 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
159 (0 << STM_DMA_CCR_MINC) |
160 (0 << STM_DMA_CCR_PINC) |
161 (0 << STM_DMA_CCR_CIRC) |
162 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
167 /* Set up the receive DMA -- when this is done, we know the SPI unit
168 * is idle. Without this, we'd have to poll waiting for the BSY bit to
171 ao_dma_set_transfer(miso_dma_index,
175 (0 << STM_DMA_CCR_MEM2MEM) |
176 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
177 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
178 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
179 (0 << STM_DMA_CCR_MINC) |
180 (0 << STM_DMA_CCR_PINC) |
181 (0 << STM_DMA_CCR_CIRC) |
182 (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
183 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
184 (0 << STM_SPI_CR2_RXNEIE) |
185 (0 << STM_SPI_CR2_ERRIE) |
186 (0 << STM_SPI_CR2_SSOE) |
187 (1 << STM_SPI_CR2_TXDMAEN) |
188 (1 << STM_SPI_CR2_RXDMAEN));
189 ao_dma_start(miso_dma_index);
190 ao_dma_start(mosi_dma_index);
192 while (!ao_dma_done[miso_dma_index])
193 ao_sleep(&ao_dma_done[miso_dma_index]);
195 ao_dma_done_transfer(mosi_dma_index);
196 ao_dma_done_transfer(miso_dma_index);
200 ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index)
203 struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
205 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
206 (0 << STM_SPI_CR2_RXNEIE) |
207 (0 << STM_SPI_CR2_ERRIE) |
208 (0 << STM_SPI_CR2_SSOE) |
209 (0 << STM_SPI_CR2_TXDMAEN) |
210 (0 << STM_SPI_CR2_RXDMAEN));
216 while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
222 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
224 struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
225 uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
226 uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
230 /* Set up transmit DMA to make the SPI hardware actually run */
231 ao_dma_set_transfer(mosi_dma_index,
235 (0 << STM_DMA_CCR_MEM2MEM) |
236 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
237 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
238 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
239 (0 << STM_DMA_CCR_MINC) |
240 (0 << STM_DMA_CCR_PINC) |
241 (0 << STM_DMA_CCR_CIRC) |
242 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
247 /* Set up the receive DMA to capture data */
248 ao_dma_set_transfer(miso_dma_index,
252 (0 << STM_DMA_CCR_MEM2MEM) |
253 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
254 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
255 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
256 (1 << STM_DMA_CCR_MINC) |
257 (0 << STM_DMA_CCR_PINC) |
258 (0 << STM_DMA_CCR_CIRC) |
259 (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
261 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
262 (0 << STM_SPI_CR2_RXNEIE) |
263 (0 << STM_SPI_CR2_ERRIE) |
264 (0 << STM_SPI_CR2_SSOE) |
265 (1 << STM_SPI_CR2_TXDMAEN) |
266 (1 << STM_SPI_CR2_RXDMAEN));
267 ao_dma_start(miso_dma_index);
268 ao_dma_start(mosi_dma_index);
270 /* Wait until the SPI unit is done */
272 while (!ao_dma_done[miso_dma_index])
273 ao_sleep(&ao_dma_done[miso_dma_index]);
276 ao_dma_done_transfer(mosi_dma_index);
277 ao_dma_done_transfer(miso_dma_index);
281 ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
283 struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
284 uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
285 uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
287 /* Set up transmit DMA to send data */
288 ao_dma_set_transfer(mosi_dma_index,
292 (0 << STM_DMA_CCR_MEM2MEM) |
293 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
294 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
295 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
296 (1 << STM_DMA_CCR_MINC) |
297 (0 << STM_DMA_CCR_PINC) |
298 (0 << STM_DMA_CCR_CIRC) |
299 (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
304 /* Set up the receive DMA to capture data */
305 ao_dma_set_transfer(miso_dma_index,
309 (0 << STM_DMA_CCR_MEM2MEM) |
310 (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
311 (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
312 (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
313 (1 << STM_DMA_CCR_MINC) |
314 (0 << STM_DMA_CCR_PINC) |
315 (0 << STM_DMA_CCR_CIRC) |
316 (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
318 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
319 (0 << STM_SPI_CR2_RXNEIE) |
320 (0 << STM_SPI_CR2_ERRIE) |
321 (0 << STM_SPI_CR2_SSOE) |
322 (1 << STM_SPI_CR2_TXDMAEN) |
323 (1 << STM_SPI_CR2_RXDMAEN));
324 ao_dma_start(miso_dma_index);
325 ao_dma_start(mosi_dma_index);
327 /* Wait until the SPI unit is done */
329 while (!ao_dma_done[miso_dma_index])
330 ao_sleep(&ao_dma_done[miso_dma_index]);
333 ao_dma_done_transfer(mosi_dma_index);
334 ao_dma_done_transfer(miso_dma_index);
338 ao_spi_disable_index(uint8_t spi_index)
340 /* Disable current config
342 switch (AO_SPI_INDEX(spi_index)) {
343 case STM_SPI_INDEX(1):
345 case AO_SPI_1_PA5_PA6_PA7:
346 stm_gpio_set(&stm_gpioa, 5, 1);
347 stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
348 stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
349 stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
351 case AO_SPI_1_PB3_PB4_PB5:
352 stm_gpio_set(&stm_gpiob, 3, 1);
353 stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
354 stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
355 stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
357 case AO_SPI_1_PE13_PE14_PE15:
358 stm_gpio_set(&stm_gpioe, 13, 1);
359 stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
360 stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
361 stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
365 case STM_SPI_INDEX(2):
367 case AO_SPI_2_PB13_PB14_PB15:
368 stm_gpio_set(&stm_gpiob, 13, 1);
369 stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
370 stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
371 stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
373 case AO_SPI_2_PD1_PD3_PD4:
374 stm_gpio_set(&stm_gpiod, 1, 1);
375 stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
376 stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
377 stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
385 ao_spi_enable_index(uint8_t spi_index)
387 switch (AO_SPI_INDEX(spi_index)) {
388 case STM_SPI_INDEX(1):
390 case AO_SPI_1_PA5_PA6_PA7:
391 stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
392 stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
393 stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
395 case AO_SPI_1_PB3_PB4_PB5:
396 stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
397 stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
398 stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
400 case AO_SPI_1_PE13_PE14_PE15:
401 stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
402 stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
403 stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
407 case STM_SPI_INDEX(2):
409 case AO_SPI_2_PB13_PB14_PB15:
410 stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
411 stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
412 stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
414 case AO_SPI_2_PD1_PD3_PD4:
415 stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
416 stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
417 stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
425 ao_spi_config(uint8_t spi_index, uint32_t speed)
427 uint8_t id = AO_SPI_INDEX(spi_index);
428 struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
429 stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) | /* Three wire mode */
430 (0 << STM_SPI_CR1_BIDIOE) |
431 (0 << STM_SPI_CR1_CRCEN) | /* CRC disabled */
432 (0 << STM_SPI_CR1_CRCNEXT) |
433 (0 << STM_SPI_CR1_DFF) |
434 (0 << STM_SPI_CR1_RXONLY) |
435 (1 << STM_SPI_CR1_SSM) | /* Software SS handling */
436 (1 << STM_SPI_CR1_SSI) | /* ... */
437 (0 << STM_SPI_CR1_LSBFIRST) | /* Big endian */
438 (1 << STM_SPI_CR1_SPE) | /* Enable SPI unit */
439 (speed << STM_SPI_CR1_BR) | /* baud rate to pclk/4 */
440 (1 << STM_SPI_CR1_MSTR) |
441 (0 << STM_SPI_CR1_CPOL) | /* Format 0 */
442 (0 << STM_SPI_CR1_CPHA));
443 if (spi_index != ao_spi_index[id]) {
445 /* Disable old config
447 ao_spi_disable_index(ao_spi_index[id]);
451 ao_spi_enable_index(spi_index);
453 /* Remember current config
455 ao_spi_index[id] = spi_index;
460 ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
462 uint8_t id = AO_SPI_INDEX(spi_index);
464 if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
466 ao_spi_config(spi_index, speed);
471 ao_spi_get(uint8_t spi_index, uint32_t speed)
473 uint8_t id = AO_SPI_INDEX(spi_index);
474 ao_mutex_get(&ao_spi_mutex[id]);
475 ao_spi_config(spi_index, speed);
479 ao_spi_put(uint8_t spi_index)
481 uint8_t id = AO_SPI_INDEX(spi_index);
482 struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
485 ao_mutex_put(&ao_spi_mutex[id]);
489 ao_spi_channel_init(uint8_t spi_index)
491 uint8_t id = AO_SPI_INDEX(spi_index);
492 struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
494 ao_spi_disable_index(spi_index);
498 stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
499 (0 << STM_SPI_CR2_RXNEIE) |
500 (0 << STM_SPI_CR2_ERRIE) |
501 (0 << STM_SPI_CR2_SSOE) |
502 (0 << STM_SPI_CR2_TXDMAEN) |
503 (0 << STM_SPI_CR2_RXDMAEN));
508 ao_spi_dump_cmd(void)
512 for (s = 0; s < 64; s++) {
513 int i = (spi_task_index + s) & 63;
514 if (spi_tasks[i].which) {
516 const char *name = "(none)";
517 for (t = 0; t < ao_num_tasks; t++)
518 if (ao_tasks[t]->task_id == spi_tasks[i].task) {
519 name = ao_tasks[t]->name;
522 printf("%2d: %5d task %2d which %2d len %5d %s\n",
531 for (s = 0; s < STM_NUM_SPI; s++) {
532 struct stm_spi *spi = ao_spi_stm_info[s].stm_spi;
534 printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d",
535 s, ao_spi_mutex[s], ao_spi_index[s],
536 ao_spi_stm_info[s].miso_dma_index,
537 ao_spi_stm_info[s].mosi_dma_index);
538 printf(" cr1 %04x cr2 %02x sr %03x\n",
539 spi->cr1, spi->cr2, spi->sr);
544 static const struct ao_cmds ao_spi_cmds[] = {
545 { ao_spi_dump_cmd, "S\0Dump SPI status" },
554 # if SPI_1_PA5_PA6_PA7
555 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
556 stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
557 stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
558 stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
560 # if SPI_1_PB3_PB4_PB5
561 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
562 stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
563 stm_ospeedr_set(&stm_gpiob, 4, SPI_1_OSPEEDR);
564 stm_ospeedr_set(&stm_gpiob, 5, SPI_1_OSPEEDR);
566 # if SPI_1_PE13_PE14_PE15
567 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
568 stm_ospeedr_set(&stm_gpioe, 13, SPI_1_OSPEEDR);
569 stm_ospeedr_set(&stm_gpioe, 14, SPI_1_OSPEEDR);
570 stm_ospeedr_set(&stm_gpioe, 15, SPI_1_OSPEEDR);
572 stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
573 ao_spi_index[0] = AO_SPI_CONFIG_NONE;
574 ao_spi_channel_init(0);
578 # if SPI_2_PB13_PB14_PB15
579 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
580 stm_ospeedr_set(&stm_gpiob, 13, SPI_2_OSPEEDR);
581 stm_ospeedr_set(&stm_gpiob, 14, SPI_2_OSPEEDR);
582 stm_ospeedr_set(&stm_gpiob, 15, SPI_2_OSPEEDR);
584 # if SPI_2_PD1_PD3_PD4
585 stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
586 stm_ospeedr_set(&stm_gpiod, 1, SPI_2_OSPEEDR);
587 stm_ospeedr_set(&stm_gpiod, 3, SPI_2_OSPEEDR);
588 stm_ospeedr_set(&stm_gpiod, 4, SPI_2_OSPEEDR);
590 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
591 ao_spi_index[1] = AO_SPI_CONFIG_NONE;
592 ao_spi_channel_init(1);
595 ao_cmd_register(&ao_spi_cmds[0]);