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