altos: Crank down STM SPI speed for MM
[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 uint8_t ao_spi_mutex[STM_NUM_SPI];
27
28 static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
29         {
30                 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
31                 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
32                 &stm_spi1
33         },
34         {
35                 .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
36                 .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
37                 &stm_spi2
38         }
39 };
40
41 static uint8_t  spi_dev_null;
42
43 void
44 ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
45 {
46         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
47         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
48         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
49
50         /* Set up the transmit DMA to deliver data */
51         ao_dma_set_transfer(mosi_dma_index,
52                             &stm_spi->dr,
53                             block,
54                             len,
55                             (0 << STM_DMA_CCR_MEM2MEM) |
56                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
57                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
58                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
59                             (1 << STM_DMA_CCR_MINC) |
60                             (0 << STM_DMA_CCR_PINC) |
61                             (0 << STM_DMA_CCR_CIRC) |
62                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
63
64         /* Clear RXNE */
65         (void) stm_spi->dr;
66
67         /* Set up the receive DMA -- when this is done, we know the SPI unit
68          * is idle. Without this, we'd have to poll waiting for the BSY bit to
69          * be cleared
70          */
71         ao_dma_set_transfer(miso_dma_index,
72                             &stm_spi->dr,
73                             &spi_dev_null,
74                             len,
75                             (0 << STM_DMA_CCR_MEM2MEM) |
76                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
77                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
78                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
79                             (0 << STM_DMA_CCR_MINC) |
80                             (0 << STM_DMA_CCR_PINC) |
81                             (0 << STM_DMA_CCR_CIRC) |
82                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
83         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
84                         (0 << STM_SPI_CR2_RXNEIE) |
85                         (0 << STM_SPI_CR2_ERRIE) |
86                         (0 << STM_SPI_CR2_SSOE) |
87                         (1 << STM_SPI_CR2_TXDMAEN) |
88                         (1 << STM_SPI_CR2_RXDMAEN));
89         ao_dma_start(miso_dma_index);
90         ao_dma_start(mosi_dma_index);
91         ao_arch_critical(
92                 while (!ao_dma_done[miso_dma_index])
93                         ao_sleep(&ao_dma_done[miso_dma_index]);
94                 );
95         ao_dma_done_transfer(mosi_dma_index);
96         ao_dma_done_transfer(miso_dma_index);
97 }
98
99 void
100 ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
101 {
102         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
103         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
104         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
105
106         /* Set up the transmit DMA to deliver data */
107         ao_dma_set_transfer(mosi_dma_index,
108                             &stm_spi->dr,
109                             &value,
110                             len,
111                             (0 << STM_DMA_CCR_MEM2MEM) |
112                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
113                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
114                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
115                             (0 << STM_DMA_CCR_MINC) |
116                             (0 << STM_DMA_CCR_PINC) |
117                             (0 << STM_DMA_CCR_CIRC) |
118                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
119
120         /* Clear RXNE */
121         (void) stm_spi->dr;
122
123         /* Set up the receive DMA -- when this is done, we know the SPI unit
124          * is idle. Without this, we'd have to poll waiting for the BSY bit to
125          * be cleared
126          */
127         ao_dma_set_transfer(miso_dma_index,
128                             &stm_spi->dr,
129                             &spi_dev_null,
130                             len,
131                             (0 << STM_DMA_CCR_MEM2MEM) |
132                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
133                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
134                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
135                             (0 << STM_DMA_CCR_MINC) |
136                             (0 << STM_DMA_CCR_PINC) |
137                             (0 << STM_DMA_CCR_CIRC) |
138                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
139         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
140                         (0 << STM_SPI_CR2_RXNEIE) |
141                         (0 << STM_SPI_CR2_ERRIE) |
142                         (0 << STM_SPI_CR2_SSOE) |
143                         (1 << STM_SPI_CR2_TXDMAEN) |
144                         (1 << STM_SPI_CR2_RXDMAEN));
145         ao_dma_start(miso_dma_index);
146         ao_dma_start(mosi_dma_index);
147         ao_arch_critical(
148                 while (!ao_dma_done[miso_dma_index])
149                         ao_sleep(&ao_dma_done[miso_dma_index]);
150                 );
151         ao_dma_done_transfer(mosi_dma_index);
152         ao_dma_done_transfer(miso_dma_index);
153 }
154
155 void
156 ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
157 {
158         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
159         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
160         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
161
162         /* Set up transmit DMA to make the SPI hardware actually run */
163         ao_dma_set_transfer(mosi_dma_index,
164                             &stm_spi->dr,
165                             &spi_dev_null,
166                             len,
167                             (0 << STM_DMA_CCR_MEM2MEM) |
168                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
169                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
170                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
171                             (0 << STM_DMA_CCR_MINC) |
172                             (0 << STM_DMA_CCR_PINC) |
173                             (0 << STM_DMA_CCR_CIRC) |
174                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
175
176         /* Clear RXNE */
177         (void) stm_spi->dr;
178
179         /* Set up the receive DMA to capture data */
180         ao_dma_set_transfer(miso_dma_index,
181                             &stm_spi->dr,
182                             block,
183                             len,
184                             (0 << STM_DMA_CCR_MEM2MEM) |
185                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
186                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
187                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
188                             (1 << STM_DMA_CCR_MINC) |
189                             (0 << STM_DMA_CCR_PINC) |
190                             (0 << STM_DMA_CCR_CIRC) |
191                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
192
193         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
194                         (0 << STM_SPI_CR2_RXNEIE) |
195                         (0 << STM_SPI_CR2_ERRIE) |
196                         (0 << STM_SPI_CR2_SSOE) |
197                         (1 << STM_SPI_CR2_TXDMAEN) |
198                         (1 << STM_SPI_CR2_RXDMAEN));
199         ao_dma_start(miso_dma_index);
200         ao_dma_start(mosi_dma_index);
201
202         /* Wait until the SPI unit is done */
203         ao_arch_critical(
204                 while (!ao_dma_done[miso_dma_index])
205                         ao_sleep(&ao_dma_done[miso_dma_index]);
206                 );
207
208         ao_dma_done_transfer(mosi_dma_index);
209         ao_dma_done_transfer(miso_dma_index);
210 }
211
212 void
213 ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
214 {
215         struct stm_spi *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
216         uint8_t mosi_dma_index = ao_spi_stm_info[spi_index].mosi_dma_index;
217         uint8_t miso_dma_index = ao_spi_stm_info[spi_index].miso_dma_index;
218
219         /* Set up transmit DMA to send data */
220         ao_dma_set_transfer(mosi_dma_index,
221                             &stm_spi->dr,
222                             out,
223                             len,
224                             (0 << STM_DMA_CCR_MEM2MEM) |
225                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
226                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
227                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
228                             (1 << STM_DMA_CCR_MINC) |
229                             (0 << STM_DMA_CCR_PINC) |
230                             (0 << STM_DMA_CCR_CIRC) |
231                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
232
233         /* Clear RXNE */
234         (void) stm_spi->dr;
235
236         /* Set up the receive DMA to capture data */
237         ao_dma_set_transfer(miso_dma_index,
238                             &stm_spi->dr,
239                             in,
240                             len,
241                             (0 << STM_DMA_CCR_MEM2MEM) |
242                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
243                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
244                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
245                             (1 << STM_DMA_CCR_MINC) |
246                             (0 << STM_DMA_CCR_PINC) |
247                             (0 << STM_DMA_CCR_CIRC) |
248                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
249
250         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
251                         (0 << STM_SPI_CR2_RXNEIE) |
252                         (0 << STM_SPI_CR2_ERRIE) |
253                         (0 << STM_SPI_CR2_SSOE) |
254                         (1 << STM_SPI_CR2_TXDMAEN) |
255                         (1 << STM_SPI_CR2_RXDMAEN));
256         ao_dma_start(miso_dma_index);
257         ao_dma_start(mosi_dma_index);
258
259         /* Wait until the SPI unit is done */
260         ao_arch_critical(
261                 while (!ao_dma_done[miso_dma_index])
262                         ao_sleep(&ao_dma_done[miso_dma_index]);
263                 );
264
265         ao_dma_done_transfer(mosi_dma_index);
266         ao_dma_done_transfer(miso_dma_index);
267 }
268
269 void
270 ao_spi_get(uint8_t spi_index)
271 {
272         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
273
274         ao_mutex_get(&ao_spi_mutex[spi_index]);
275         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
276                         (0 << STM_SPI_CR1_BIDIOE) |
277                         (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
278                         (0 << STM_SPI_CR1_CRCNEXT) |
279                         (0 << STM_SPI_CR1_DFF) |
280                         (0 << STM_SPI_CR1_RXONLY) |
281                         (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
282                         (1 << STM_SPI_CR1_SSI) |                        /*  ... */
283                         (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
284                         (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
285                         (STM_SPI_CR1_BR_PCLK_16 << STM_SPI_CR1_BR) |    /* baud rate to pclk/4 */
286                         (1 << STM_SPI_CR1_MSTR) |
287                         (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
288                         (0 << STM_SPI_CR1_CPHA));
289 }
290
291 void
292 ao_spi_put(uint8_t spi_index)
293 {
294         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
295
296         stm_spi->cr1 = 0;
297         ao_mutex_put(&ao_spi_mutex[spi_index]);
298 }
299
300 static void
301 ao_spi_channel_init(uint8_t spi_index)
302 {
303         struct stm_spi  *stm_spi = ao_spi_stm_info[spi_index].stm_spi;
304
305         stm_spi->cr1 = 0;
306         (void) stm_spi->sr;
307         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
308                         (0 << STM_SPI_CR2_RXNEIE) |
309                         (0 << STM_SPI_CR2_ERRIE) |
310                         (0 << STM_SPI_CR2_SSOE) |
311                         (0 << STM_SPI_CR2_TXDMAEN) |
312                         (0 << STM_SPI_CR2_RXDMAEN));
313 }
314
315 void
316 ao_spi_init(void)
317 {
318 #if HAS_SPI_1
319 # if SPI_1_PA5_PA6_PA7
320         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
321         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
322         stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
323         stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
324 # else
325 #  if SPI_1_PB3_PB4_PB5
326         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
327         stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
328         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
329         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
330 #  else
331 #   if SPI_1_PE13_PE14_PE15
332         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
333         stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
334         stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
335         stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
336 #   else
337 #    error "No SPI_1 port configuration specified"
338 #   endif
339 #  endif
340 # endif
341
342         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
343
344         ao_spi_channel_init(0);
345 #endif
346
347 #if HAS_SPI_2
348 # if SPI_2_PB13_PB14_PB15
349         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
350         stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
351         stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
352         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
353 # else
354 #  if SPI_2_PPD1_PD3_PD4
355         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
356         stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
357         stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
358         stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
359 #  else
360 #   error "No SPI_2 port configuration specified"
361 #  endif
362 # endif
363
364         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
365
366         ao_spi_channel_init(1);
367 #endif
368 }