]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_FreedomStudio/freedom-metal/src/drivers/sifive_spi0.c
Update RISCC-V-RV32-SiFive_HiFive1_FreedomStudio project to latest tools and metal...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_FreedomStudio / freedom-metal / src / drivers / sifive_spi0.c
1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine/platform.h>
5
6 #ifdef METAL_SIFIVE_SPI0
7 #include <metal/drivers/sifive_spi0.h>
8 #include <metal/io.h>
9 #include <metal/machine.h>
10 #include <metal/time.h>
11 #include <time.h>
12
13 /* Register fields */
14 #define METAL_SPI_SCKDIV_MASK         0xFFF
15
16 #define METAL_SPI_SCKMODE_PHA_SHIFT   0
17 #define METAL_SPI_SCKMODE_POL_SHIFT   1
18
19 #define METAL_SPI_CSMODE_MASK         3
20 #define METAL_SPI_CSMODE_AUTO         0
21 #define METAL_SPI_CSMODE_HOLD         2
22 #define METAL_SPI_CSMODE_OFF          3
23
24 #define METAL_SPI_PROTO_MASK          3
25 #define METAL_SPI_PROTO_SINGLE        0
26 #define METAL_SPI_PROTO_DUAL          1
27 #define METAL_SPI_PROTO_QUAD          2
28
29 #define METAL_SPI_ENDIAN_LSB          4
30
31 #define METAL_SPI_DISABLE_RX          8
32
33 #define METAL_SPI_FRAME_LEN_SHIFT     16
34 #define METAL_SPI_FRAME_LEN_MASK      (0xF << METAL_SPI_FRAME_LEN_SHIFT)
35
36 #define METAL_SPI_TXDATA_FULL         (1 << 31)
37 #define METAL_SPI_RXDATA_EMPTY        (1 << 31)
38 #define METAL_SPI_TXMARK_MASK         7
39 #define METAL_SPI_TXWM                1
40 #define METAL_SPI_TXRXDATA_MASK       (0xFF)
41
42 #define METAL_SPI_INTERVAL_SHIFT      16
43
44 #define METAL_SPI_CONTROL_IO          0
45 #define METAL_SPI_CONTROL_MAPPED      1
46
47 #define METAL_SPI_REG(offset)   (((unsigned long)control_base + offset))
48 #define METAL_SPI_REGB(offset)  (__METAL_ACCESS_ONCE((__metal_io_u8  *)METAL_SPI_REG(offset)))
49 #define METAL_SPI_REGW(offset)  (__METAL_ACCESS_ONCE((__metal_io_u32 *)METAL_SPI_REG(offset)))
50
51 #define METAL_SPI_RXDATA_TIMEOUT      1
52
53 static int configure_spi(struct __metal_driver_sifive_spi0 *spi, struct metal_spi_config *config)
54 {
55     long control_base = __metal_driver_sifive_spi0_control_base((struct metal_spi *)spi);
56     /* Set protocol */
57     METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_PROTO_MASK);
58     switch (config->protocol) {
59     case METAL_SPI_SINGLE:
60         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
61         break;
62     case METAL_SPI_DUAL:
63         if (config->multi_wire == MULTI_WIRE_ALL)
64             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL;
65         else
66             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
67         break;
68     case METAL_SPI_QUAD:
69         if (config->multi_wire == MULTI_WIRE_ALL)
70             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD;
71         else
72             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_SINGLE;
73         break;
74     default:
75         /* Unsupported value */
76         return -1;
77     }
78
79     /* Set Polarity */
80     if(config->polarity) {
81         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_PHA_SHIFT);
82     } else {
83         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_PHA_SHIFT);
84     }
85
86     /* Set Phase */
87     if(config->phase) {
88         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) |= (1 << METAL_SPI_SCKMODE_POL_SHIFT);
89     } else {
90         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKMODE) &= ~(1 << METAL_SPI_SCKMODE_POL_SHIFT);
91     }
92
93     /* Set Endianness */
94     if(config->little_endian) {
95         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_ENDIAN_LSB;
96     } else {
97         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_ENDIAN_LSB);
98     }
99
100     /* Always populate receive FIFO */
101     METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_DISABLE_RX);
102
103     /* Set CS Active */
104     if(config->cs_active_high) {
105         METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSDEF) = 0;
106     } else {
107         METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSDEF) = 1;
108     }
109
110     /* Set frame length */
111     if((METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) & METAL_SPI_FRAME_LEN_MASK) != (8 << METAL_SPI_FRAME_LEN_SHIFT)) {
112         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_FRAME_LEN_MASK);
113         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= (8 << METAL_SPI_FRAME_LEN_SHIFT);
114     }
115
116     /* Set CS line */
117     METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSID) = 1 << (config->csid);
118
119     /* Toggle off memory-mapped SPI flash mode, toggle on programmable IO mode
120      * It seems that with this line uncommented, the debugger cannot have access 
121      * to the chip at all because it assumes the chip is in memory-mapped mode. 
122      * I have to compile the code with this line commented and launch gdb,
123      * reset cores, reset $pc, set *((int *) 0x20004060) = 0, (set the flash
124      * interface control register to programmable I/O mode) and then continue
125      * Alternative, comment out the "flash" line in openocd.cfg */
126     METAL_SPI_REGW(METAL_SIFIVE_SPI0_FCTRL) = METAL_SPI_CONTROL_IO;
127
128     return 0;
129 }
130
131 static void spi_mode_switch(struct __metal_driver_sifive_spi0 *spi,
132                             struct metal_spi_config *config,
133                             unsigned int trans_stage) {
134     long control_base =
135         __metal_driver_sifive_spi0_control_base((struct metal_spi *)spi);
136
137     if (config->multi_wire == trans_stage) {
138         METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) &= ~(METAL_SPI_PROTO_MASK);
139         switch (config->protocol) {
140         case METAL_SPI_DUAL:
141             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_DUAL;
142             break;
143         case METAL_SPI_QUAD:
144             METAL_SPI_REGW(METAL_SIFIVE_SPI0_FMT) |= METAL_SPI_PROTO_QUAD;
145             break;
146         default:
147             /* Unsupported value */
148             return;
149         }
150     }
151 }
152
153 int __metal_driver_sifive_spi0_transfer(struct metal_spi *gspi,
154                                       struct metal_spi_config *config,
155                                       size_t len,
156                                       char *tx_buf,
157                                       char *rx_buf)
158 {
159     struct __metal_driver_sifive_spi0 *spi = (void *)gspi;
160     long control_base = __metal_driver_sifive_spi0_control_base(gspi);
161     int rc = 0;
162     size_t i = 0;
163
164     rc = configure_spi(spi, config);
165     if(rc != 0) {
166         return rc;
167     }
168
169     /* Hold the chip select line for all len transferred */
170     METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= ~(METAL_SPI_CSMODE_MASK);
171     METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) |= METAL_SPI_CSMODE_HOLD;
172
173     unsigned long rxdata;
174     
175     /* Declare time_t variables to break out of infinite while loop */
176     time_t endwait;
177
178     for (i = 0; i < config->cmd_num; i++) {
179
180         while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
181             ;
182
183         if (tx_buf) {
184             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
185         } else {
186             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
187         }
188
189         endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;
190
191         while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
192                METAL_SPI_RXDATA_EMPTY) {
193             if (metal_time() > endwait) {
194                 METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
195                     ~(METAL_SPI_CSMODE_MASK);
196
197                 return 1;
198             }
199         }
200
201         if (rx_buf) {
202             rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
203         }
204     }
205
206     /* switch to Dual/Quad mode */
207     spi_mode_switch(spi, config, MULTI_WIRE_ADDR_DATA);
208
209     /* Send Addr data */
210     for (; i < (config->cmd_num + config->addr_num); i++) {
211
212         while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
213             ;
214
215         if (tx_buf) {
216             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
217         } else {
218             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
219         }
220
221         endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;
222
223         while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
224                METAL_SPI_RXDATA_EMPTY) {
225             if (metal_time() > endwait) {
226                 METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
227                     ~(METAL_SPI_CSMODE_MASK);
228
229                 return 1;
230             }
231         }
232
233         if (rx_buf) {
234             rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
235         }
236     }
237
238     /* Send Dummy data */
239     for (; i < (config->cmd_num + config->addr_num + config->dummy_num); i++) {
240
241         while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL)
242             ;
243
244         if (tx_buf) {
245             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
246         } else {
247             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
248         }
249
250         endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;
251
252         while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) &
253                METAL_SPI_RXDATA_EMPTY) {
254             if (metal_time() > endwait) {
255                 METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &=
256                     ~(METAL_SPI_CSMODE_MASK);
257                 return 1;
258             }
259         }
260         if (rx_buf) {
261             rx_buf[i] = (char)(rxdata & METAL_SPI_TXRXDATA_MASK);
262         }
263     }
264
265     /* switch to Dual/Quad mode */
266     spi_mode_switch(spi, config, MULTI_WIRE_DATA_ONLY);
267
268     for (; i < len; i++) {
269         /* Master send bytes to the slave */
270
271         /* Wait for TXFIFO to not be full */
272         while (METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXDATA) & METAL_SPI_TXDATA_FULL);
273     
274         /* Transfer byte by modifying the least significant byte in the TXDATA register */
275         if (tx_buf) {
276             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = tx_buf[i];
277         } else {
278             /* Transfer a 0 byte if the sending buffer is NULL */
279             METAL_SPI_REGB(METAL_SIFIVE_SPI0_TXDATA) = 0;
280         }
281
282         /* Master receives bytes from the RX FIFO */
283
284         /* Wait for RXFIFO to not be empty, but break the nested loops if timeout
285          * this timeout method  needs refining, preferably taking into account 
286          * the device specs */
287         endwait = metal_time() + METAL_SPI_RXDATA_TIMEOUT;
288
289         while ((rxdata = METAL_SPI_REGW(METAL_SIFIVE_SPI0_RXDATA)) & METAL_SPI_RXDATA_EMPTY) {
290             if (metal_time() > endwait) {
291                 /* If timeout, deassert the CS */
292                 METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= ~(METAL_SPI_CSMODE_MASK);
293
294                 /* If timeout, return error code 1 immediately */
295                 return 1;
296             }
297         }
298
299         /* Only store the dequeued byte if the receive_buffer is not NULL */
300         if (rx_buf) {
301             rx_buf[i] = (char) (rxdata & METAL_SPI_TXRXDATA_MASK);
302         }
303     }
304
305     /* On the last byte, set CSMODE to auto so that the chip select transitions back to high
306      * The reason that CS pin is not deasserted after transmitting out the byte buffer is timing.
307      * The code on the host side likely executes faster than the ability of FIFO to send out bytes.
308      * After the host iterates through the array, fifo is likely not cleared yet. If host deasserts
309      * the CS pin immediately, the following bytes in the output FIFO will not be sent consecutively. 
310      * There needs to be a better way to handle this. */
311     METAL_SPI_REGW(METAL_SIFIVE_SPI0_CSMODE) &= ~(METAL_SPI_CSMODE_MASK);
312
313     return 0;
314 }
315
316 int __metal_driver_sifive_spi0_get_baud_rate(struct metal_spi *gspi)
317 {
318     struct __metal_driver_sifive_spi0 *spi = (void *)gspi;
319     return spi->baud_rate;
320 }
321
322 int __metal_driver_sifive_spi0_set_baud_rate(struct metal_spi *gspi, int baud_rate)
323 {
324     long control_base = __metal_driver_sifive_spi0_control_base(gspi);
325     struct metal_clock *clock = __metal_driver_sifive_spi0_clock(gspi);
326     struct __metal_driver_sifive_spi0 *spi = (void *)gspi;
327
328     spi->baud_rate = baud_rate;
329
330     if (clock != NULL) {
331         long clock_rate = clock->vtable->get_rate_hz(clock);
332
333         /* Calculate divider */
334         long div = (clock_rate / (2 * baud_rate)) - 1;
335
336         if(div > METAL_SPI_SCKDIV_MASK) {
337             /* The requested baud rate is lower than we can support at
338              * the current clock rate */
339             return -1;
340         }
341
342         /* Set divider */
343         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKDIV) &= ~METAL_SPI_SCKDIV_MASK;
344         METAL_SPI_REGW(METAL_SIFIVE_SPI0_SCKDIV) |= (div & METAL_SPI_SCKDIV_MASK);
345     }
346
347     return 0;
348 }
349
350 static void pre_rate_change_callback_func(void *priv)
351 {
352     long control_base = __metal_driver_sifive_spi0_control_base((struct metal_spi *)priv);
353
354     /* Detect when the TXDATA is empty by setting the transmit watermark count
355      * to one and waiting until an interrupt is pending (indicating an empty TXFIFO) */
356     METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXMARK) &= ~(METAL_SPI_TXMARK_MASK);
357     METAL_SPI_REGW(METAL_SIFIVE_SPI0_TXMARK) |= (METAL_SPI_TXMARK_MASK & 1);
358
359     while((METAL_SPI_REGW(METAL_SIFIVE_SPI0_IP) & METAL_SPI_TXWM) == 0) ;
360 }
361
362 static void post_rate_change_callback_func(void *priv)
363 {
364     struct __metal_driver_sifive_spi0 *spi = priv;
365     metal_spi_set_baud_rate(&spi->spi, spi->baud_rate);
366 }
367
368 void __metal_driver_sifive_spi0_init(struct metal_spi *gspi, int baud_rate)
369 {
370     struct __metal_driver_sifive_spi0 *spi = (void *)(gspi);
371     struct metal_clock *clock = __metal_driver_sifive_spi0_clock(gspi);
372     struct __metal_driver_sifive_gpio0 *pinmux = __metal_driver_sifive_spi0_pinmux(gspi);
373
374     if(clock != NULL) {
375         spi->pre_rate_change_callback.callback = &pre_rate_change_callback_func;
376         spi->pre_rate_change_callback.priv = spi;
377         metal_clock_register_pre_rate_change_callback(clock, &(spi->pre_rate_change_callback));
378
379         spi->post_rate_change_callback.callback = &post_rate_change_callback_func;
380         spi->post_rate_change_callback.priv = spi;
381         metal_clock_register_post_rate_change_callback(clock, &(spi->post_rate_change_callback));
382     }
383
384     metal_spi_set_baud_rate(&(spi->spi), baud_rate);
385
386     if (pinmux != NULL) {
387         long pinmux_output_selector = __metal_driver_sifive_spi0_pinmux_output_selector(gspi);
388         long pinmux_source_selector = __metal_driver_sifive_spi0_pinmux_source_selector(gspi);
389         pinmux->gpio.vtable->enable_io(
390             (struct metal_gpio *) pinmux,
391             pinmux_output_selector,
392             pinmux_source_selector
393         );
394     }
395 }
396
397 __METAL_DEFINE_VTABLE(__metal_driver_vtable_sifive_spi0) = {
398     .spi.init          = __metal_driver_sifive_spi0_init,
399     .spi.transfer      = __metal_driver_sifive_spi0_transfer,
400     .spi.get_baud_rate = __metal_driver_sifive_spi0_get_baud_rate,
401     .spi.set_baud_rate = __metal_driver_sifive_spi0_set_baud_rate,
402 };
403 #endif /* METAL_SIFIVE_SPI0 */
404
405 typedef int no_empty_translation_units;