altos: Add program flash function
[fw/altos] / src / stm / ao_spi_stm_slave.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_slave_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_slave_mutex[STM_NUM_SPI];
27 static uint8_t          ao_spi_slave_index[STM_NUM_SPI];
28
29 static const struct ao_spi_stm_slave_info ao_spi_stm_slave_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_slave_send(void *block, uint16_t len)
46 {
47         struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
48         uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
49         uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_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
101 uint8_t
102 ao_spi_slave_recv(void *block, uint16_t len)
103 {
104         struct stm_spi *stm_spi = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].stm_spi;
105         uint8_t mosi_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].mosi_dma_index;
106         uint8_t miso_dma_index = ao_spi_stm_slave_info[AO_SPI_INDEX(SPI_SLAVE_INDEX)].miso_dma_index;
107
108         /* Set up transmit DMA to make the SPI hardware actually run */
109         ao_dma_set_transfer(mosi_dma_index,
110                             &stm_spi->dr,
111                             &spi_dev_null,
112                             len,
113                             (0 << STM_DMA_CCR_MEM2MEM) |
114                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
115                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
116                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
117                             (0 << STM_DMA_CCR_MINC) |
118                             (0 << STM_DMA_CCR_PINC) |
119                             (0 << STM_DMA_CCR_CIRC) |
120                             (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
121
122         /* Clear RXNE */
123         (void) stm_spi->dr;
124
125         /* Set up the receive DMA to capture data */
126         ao_dma_set_transfer(miso_dma_index,
127                             &stm_spi->dr,
128                             block,
129                             len,
130                             (0 << STM_DMA_CCR_MEM2MEM) |
131                             (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
132                             (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
133                             (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
134                             (1 << STM_DMA_CCR_MINC) |
135                             (0 << STM_DMA_CCR_PINC) |
136                             (0 << STM_DMA_CCR_CIRC) |
137                             (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
138
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
148         /* Wait until the SPI unit is done */
149         ao_arch_critical(
150                 while (!ao_dma_done[miso_dma_index])
151                         ao_sleep(&ao_dma_done[miso_dma_index]);
152                 );
153
154         ao_dma_done_transfer(mosi_dma_index);
155         ao_dma_done_transfer(miso_dma_index);
156 }
157
158 static void
159 ao_spi_slave_disable_index(uint8_t spi_index)
160 {
161         /* Disable current config
162          */
163         switch (AO_SPI_INDEX(spi_index)) {
164         case STM_SPI_INDEX(1):
165                 switch (spi_index) {
166                 case AO_SPI_1_PA5_PA6_PA7:
167                         stm_gpio_set(&stm_gpioa, 5, 1);
168                         stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
169                         stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
170                         stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
171                         break;
172                 case AO_SPI_1_PB3_PB4_PB5:
173                         stm_gpio_set(&stm_gpiob, 3, 1);
174                         stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
175                         stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
176                         stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
177                         break;
178                 case AO_SPI_1_PE13_PE14_PE15:
179                         stm_gpio_set(&stm_gpioe, 13, 1);
180                         stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
181                         stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
182                         stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
183                         break;
184                 }
185                 break;
186         case STM_SPI_INDEX(2):
187                 switch (spi_index) {
188                 case AO_SPI_2_PB13_PB14_PB15:
189                         stm_gpio_set(&stm_gpiob, 13, 1);
190                         stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
191                         stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
192                         stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
193                         break;
194                 case AO_SPI_2_PD1_PD3_PD4:
195                         stm_gpio_set(&stm_gpiod, 1, 1);
196                         stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
197                         stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
198                         stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
199                         break;
200                 }
201                 break;
202         }
203 }
204
205 static void
206 ao_spi_slave_enable_index(uint8_t spi_index)
207 {
208         switch (AO_SPI_INDEX(spi_index)) {
209         case STM_SPI_INDEX(1):
210                 switch (spi_index) {
211                 case AO_SPI_1_PA5_PA6_PA7:
212                         stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
213                         stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
214                         stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
215                         break;
216                 case AO_SPI_1_PB3_PB4_PB5:
217                         stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
218                         stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
219                         stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
220                         break;
221                 case AO_SPI_1_PE13_PE14_PE15:
222                         stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
223                         stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
224                         stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
225                         break;
226                 }
227                 break;
228         case STM_SPI_INDEX(2):
229                 switch (spi_index) {
230                 case AO_SPI_2_PB13_PB14_PB15:
231                         stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
232                         stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
233                         stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
234                         break;
235                 case AO_SPI_2_PD1_PD3_PD4:
236                         stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
237                         stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
238                         stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
239                         break;
240                 }
241                 break;
242         }
243 }
244
245 void
246 ao_spi_slave_get(uint8_t spi_index, uint32_t speed)
247 {
248         uint8_t         id = AO_SPI_INDEX(spi_index);
249         struct stm_spi  *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
250
251         ao_mutex_get(&ao_spi_slave_mutex[id]);
252         stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
253                         (0 << STM_SPI_CR1_BIDIOE) |
254                         (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
255                         (0 << STM_SPI_CR1_CRCNEXT) |
256                         (0 << STM_SPI_CR1_DFF) |
257                         (0 << STM_SPI_CR1_RXONLY) |
258                         (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
259                         (1 << STM_SPI_CR1_SSI) |                        /*  ... */
260                         (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
261                         (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
262                         (speed << STM_SPI_CR1_BR) |     /* baud rate to pclk/4 */
263                         (1 << STM_SPI_CR1_MSTR) |
264                         (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
265                         (0 << STM_SPI_CR1_CPHA));
266         if (spi_index != ao_spi_slave_index[id]) {
267                 
268                 /* Disable old config
269                  */
270                 ao_spi_slave_disable_index(ao_spi_slave_index[id]);
271
272                 /* Enable new config
273                  */
274                 ao_spi_slave_enable_index(spi_index);
275                 
276                 /* Remember current config
277                  */
278                 ao_spi_slave_index[id] = spi_index;
279         }
280 }
281
282 void
283 ao_spi_slave_put(uint8_t spi_index)
284 {
285         uint8_t         id = AO_SPI_INDEX(spi_index);
286         struct stm_spi  *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
287
288         stm_spi->cr1 = 0;
289         ao_mutex_put(&ao_spi_slave_mutex[id]);
290 }
291
292 static void
293 ao_spi_channel_init(uint8_t spi_index)
294 {
295         uint8_t         id = AO_SPI_INDEX(spi_index);
296         struct stm_spi  *stm_spi = ao_spi_stm_slave_info[id].stm_spi;
297
298         ao_spi_slave_disable_index(spi_index);
299
300         stm_spi->cr1 = 0;
301         (void) stm_spi->sr;
302         stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
303                         (0 << STM_SPI_CR2_RXNEIE) |
304                         (0 << STM_SPI_CR2_ERRIE) |
305                         (0 << STM_SPI_CR2_SSOE) |
306                         (0 << STM_SPI_CR2_TXDMAEN) |
307                         (0 << STM_SPI_CR2_RXDMAEN));
308 }
309
310 void
311 ao_spi_slave_init(void)
312 {
313 #if HAS_SPI_SLAVE_1
314 # if SPI_1_PA5_PA6_PA7
315         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
316 # endif
317 # if SPI_1_PB3_PB4_PB5
318         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
319 # endif
320 # if SPI_1_PE13_PE14_PE15
321         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
322 # endif
323         stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
324         ao_spi_slave_index[0] = AO_SPI_CONFIG_NONE;
325         ao_spi_channel_init(0);
326 #endif
327
328 #if HAS_SPI_SLAVE_2
329 # if SPI_2_PB13_PB14_PB15
330         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
331 # endif
332 # if SPI_2_PD1_PD3_PD4
333         stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
334 # endif
335         stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
336         ao_spi_slave_index[1] = AO_SPI_CONFIG_NONE;
337         ao_spi_channel_init(1);
338 #endif
339 }