Merge branch 'master' into mm-ms5611
[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_recv(void *block, uint16_t len, uint8_t spi_index)
158 {
159         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
160         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
161         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
162
163         /* Set up transmit DMA to make the SPI hardware actually run */
164         ao_dma_set_transfer(mosi_dma_index,
165                             &stm_spi->dr,
166                             &spi_dev_null,
167                             len,
168                             (0 << STM_DMA_CCR_MEM2MEM) |
169                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
170                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
171                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
172                             (0 << STM_DMA_CCR_MINC) |
173                             (0 << STM_DMA_CCR_PINC) |
174                             (0 << STM_DMA_CCR_CIRC) |
175                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
176
177         /* Clear RXNE */
178         (void) stm_spi->dr;
179
180         /* Set up the receive DMA to capture data */
181         ao_dma_set_transfer(miso_dma_index,
182                             &stm_spi->dr,
183                             block,
184                             len,
185                             (0 << STM_DMA_CCR_MEM2MEM) |
186                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
187                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
188                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
189                             (1 << STM_DMA_CCR_MINC) |
190                             (0 << STM_DMA_CCR_PINC) |
191                             (0 << STM_DMA_CCR_CIRC) |
192                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
193
194         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
195                         (0 << STM_SPI_CR2_RXNEIE) |
196                         (0 << STM_SPI_CR2_ERRIE) |
197                         (0 << STM_SPI_CR2_SSOE) |
198                         (1 << STM_SPI_CR2_TXDMAEN) |
199                         (1 << STM_SPI_CR2_RXDMAEN));
200         ao_dma_start(miso_dma_index);
201         ao_dma_start(mosi_dma_index);
202
203         /* Wait until the SPI unit is done */
204         ao_arch_critical(
205                 while (!ao_dma_done[miso_dma_index])
206                         ao_sleep(&ao_dma_done[miso_dma_index]);
207                 );
208
209         ao_dma_done_transfer(mosi_dma_index);
210         ao_dma_done_transfer(miso_dma_index);
211 }
212
213 void
214 ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
215 {
216         struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
217         uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
218         uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
219
220         /* Set up transmit DMA to send data */
221         ao_dma_set_transfer(mosi_dma_index,
222                             &stm_spi->dr,
223                             out,
224                             len,
225                             (0 << STM_DMA_CCR_MEM2MEM) |
226                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
227                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
228                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
229                             (1 << STM_DMA_CCR_MINC) |
230                             (0 << STM_DMA_CCR_PINC) |
231                             (0 << STM_DMA_CCR_CIRC) |
232                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
233
234         /* Clear RXNE */
235         (void) stm_spi->dr;
236
237         /* Set up the receive DMA to capture data */
238         ao_dma_set_transfer(miso_dma_index,
239                             &stm_spi->dr,
240                             in,
241                             len,
242                             (0 << STM_DMA_CCR_MEM2MEM) |
243                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
244                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
245                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
246                             (1 << STM_DMA_CCR_MINC) |
247                             (0 << STM_DMA_CCR_PINC) |
248                             (0 << STM_DMA_CCR_CIRC) |
249                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
250
251         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
252                         (0 << STM_SPI_CR2_RXNEIE) |
253                         (0 << STM_SPI_CR2_ERRIE) |
254                         (0 << STM_SPI_CR2_SSOE) |
255                         (1 << STM_SPI_CR2_TXDMAEN) |
256                         (1 << STM_SPI_CR2_RXDMAEN));
257         ao_dma_start(miso_dma_index);
258         ao_dma_start(mosi_dma_index);
259
260         /* Wait until the SPI unit is done */
261         ao_arch_critical(
262                 while (!ao_dma_done[miso_dma_index])
263                         ao_sleep(&ao_dma_done[miso_dma_index]);
264                 );
265
266         ao_dma_done_transfer(mosi_dma_index);
267         ao_dma_done_transfer(miso_dma_index);
268 }
269
270 static void
271 ao_spi_disable_index(uint8_t spi_index)
272 {
273         /* Disable current config
274          */
275         switch (AO_SPI_INDEX(spi_index)) {
276         case STM_SPI_INDEX(1):
277                 switch (spi_index) {
278                 case AO_SPI_1_PA5_PA6_PA7:
279                         stm_gpio_set(&stm_gpioa, 5, 1);
280                         stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
281                         stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
282                         stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
283                         break;
284                 case AO_SPI_1_PB3_PB4_PB5:
285                         stm_gpio_set(&stm_gpiob, 3, 1);
286                         stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
287                         stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
288                         stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
289                         break;
290                 case AO_SPI_1_PE13_PE14_PE15:
291                         stm_gpio_set(&stm_gpioe, 13, 1);
292                         stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
293                         stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
294                         stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
295                         break;
296                 }
297                 break;
298         case STM_SPI_INDEX(2):
299                 switch (spi_index) {
300                 case AO_SPI_2_PB13_PB14_PB15:
301                         stm_gpio_set(&stm_gpiob, 13, 1);
302                         stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
303                         stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
304                         stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
305                         break;
306                 case AO_SPI_2_PD1_PD3_PD4:
307                         stm_gpio_set(&stm_gpiod, 1, 1);
308                         stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
309                         stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
310                         stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
311                         break;
312                 }
313                 break;
314         }
315 }
316
317 static void
318 ao_spi_enable_index(uint8_t spi_index)
319 {
320         switch (AO_SPI_INDEX(spi_index)) {
321         case STM_SPI_INDEX(1):
322                 switch (spi_index) {
323                 case AO_SPI_1_PA5_PA6_PA7:
324                         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
325                         stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
326                         stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
327                         break;
328                 case AO_SPI_1_PB3_PB4_PB5:
329                         stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
330                         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
331                         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
332                         break;
333                 case AO_SPI_1_PE13_PE14_PE15:
334                         stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
335                         stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
336                         stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
337                         break;
338                 }
339                 break;
340         case STM_SPI_INDEX(2):
341                 switch (spi_index) {
342                 case AO_SPI_2_PB13_PB14_PB15:
343                         stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
344                         stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
345                         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
346                         break;
347                 case AO_SPI_2_PD1_PD3_PD4:
348                         stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
349                         stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
350                         stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
351                         break;
352                 }
353                 break;
354         }
355 }
356
357 void
358 ao_spi_get(uint8_t spi_index, uint32_t speed)
359 {
360         uint8_t         id = AO_SPI_INDEX(spi_index);
361         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
362
363         ao_mutex_get(&ao_spi_mutex[id]);
364         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
365                         (0 << STM_SPI_CR1_BIDIOE) |
366                         (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
367                         (0 << STM_SPI_CR1_CRCNEXT) |
368                         (0 << STM_SPI_CR1_DFF) |
369                         (0 << STM_SPI_CR1_RXONLY) |
370                         (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
371                         (1 << STM_SPI_CR1_SSI) |                        /*  ... */
372                         (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
373                         (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
374                         (speed << STM_SPI_CR1_BR) |     /* baud rate to pclk/4 */
375                         (1 << STM_SPI_CR1_MSTR) |
376                         (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
377                         (0 << STM_SPI_CR1_CPHA));
378         if (spi_index != ao_spi_index[id]) {
379                 
380                 /* Disable old config
381                  */
382                 ao_spi_disable_index(ao_spi_index[id]);
383
384                 /* Enable new config
385                  */
386                 ao_spi_enable_index(spi_index);
387                 
388                 /* Remember current config
389                  */
390                 ao_spi_index[id] = spi_index;
391         }
392 }
393
394 void
395 ao_spi_put(uint8_t spi_index)
396 {
397         uint8_t         id = AO_SPI_INDEX(spi_index);
398         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
399
400         stm_spi->cr1 = 0;
401         ao_mutex_put(&ao_spi_mutex[id]);
402 }
403
404 static void
405 ao_spi_channel_init(uint8_t spi_index)
406 {
407         uint8_t         id = AO_SPI_INDEX(spi_index);
408         struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
409
410         ao_spi_disable_index(spi_index);
411
412         stm_spi->cr1 = 0;
413         (void) stm_spi->sr;
414         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
415                         (0 << STM_SPI_CR2_RXNEIE) |
416                         (0 << STM_SPI_CR2_ERRIE) |
417                         (0 << STM_SPI_CR2_SSOE) |
418                         (0 << STM_SPI_CR2_TXDMAEN) |
419                         (0 << STM_SPI_CR2_RXDMAEN));
420 }
421
422 void
423 ao_spi_init(void)
424 {
425 #if HAS_SPI_1
426 # if SPI_1_PA5_PA6_PA7
427         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
428 # endif
429 # if SPI_1_PB3_PB4_PB5
430         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
431 # endif
432 # if SPI_1_PE13_PE14_PE15
433         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
434 # endif
435         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
436         ao_spi_index[0] = AO_SPI_CONFIG_NONE;
437         ao_spi_channel_init(0);
438 #endif
439
440 #if HAS_SPI_2
441 # if SPI_2_PB13_PB14_PB15
442         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
443 # endif
444 # if SPI_2_PD1_PD3_PD4
445         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
446 # endif
447         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
448         ao_spi_index[1] = AO_SPI_CONFIG_NONE;
449         ao_spi_channel_init(1);
450 #endif
451 }