source: rtems/bsps/arm/raspberrypi/spi/spi.c @ 276afd2b

5
Last change on this file since 276afd2b was 276afd2b, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 23, 2018 at 7:48:52 AM

bsps: Move SPI drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 18.4 KB
Line 
1/**
2 * @file spi.c
3 *
4 * @ingroup raspberrypi_spi
5 *
6 * @brief Support for the SPI bus on the Raspberry Pi GPIO P1 header (model A/B)
7 *        and GPIO J8 header on model B+.
8 */
9
10/*
11 *  Copyright (c) 2014-2015 Andre Marques <andre.lousa.marques at gmail.com>
12 *
13 *  The license and distribution terms for this file may be
14 *  found in the file LICENSE in this distribution or at
15 *  http://www.rtems.org/license/LICENSE.
16 */
17
18/*
19 * STATUS:
20 * - Bi-directional mode untested
21 * - Write-only devices not supported
22 */
23
24#include <bsp.h>
25#include <bsp/raspberrypi.h>
26#include <bsp/gpio.h>
27#include <bsp/rpi-gpio.h>
28#include <bsp/irq.h>
29#include <bsp/spi.h>
30#include <assert.h>
31
32#define SPI_POLLING(condition)                  \
33  while ( condition ) {                         \
34    ;                                           \
35  }
36
37/**
38 * @brief Object containing the SPI bus configuration settings.
39 *
40 * Encapsulates the current SPI bus configuration.
41 */
42typedef struct
43{
44  int initialized;
45  uint8_t bytes_per_char;
46
47  /* Shift to be applied on data transfers with
48   * least significative bit first (LSB) devices. */
49  uint8_t bit_shift;
50  uint32_t dummy_char;
51  uint32_t current_slave_addr;
52  rtems_id task_id;
53  int irq_write;
54} rpi_spi_softc_t;
55
56/**
57 * @brief Object containing the SPI bus description.
58 *
59 * Encapsulates the current SPI bus description.
60 */
61typedef struct
62{
63  rtems_libi2c_bus_t bus_desc;
64  rpi_spi_softc_t softc;
65} rpi_spi_desc_t;
66
67/* If set to FALSE uses 3-wire SPI, with 2 separate data lines (MOSI and MISO),
68 * if set to TRUE uses 2-wire SPI, where the MOSI data line doubles as the
69 * slave out (SO) and slave in (SI) data lines. */
70static bool bidirectional = false;
71
72/* Calculates a clock divider to be used with the GPU core clock rate
73 * to set a SPI clock rate the closest (<=) to a desired frequency. */
74static rtems_status_code rpi_spi_calculate_clock_divider(
75  uint32_t clock_hz,
76  uint16_t *clock_divider
77) {
78  uint16_t divider;
79  uint32_t clock_rate;
80
81  assert( clock_hz > 0 );
82
83  /* Calculates an initial clock divider. */
84  divider = GPU_CORE_CLOCK_RATE / clock_hz;
85
86  /* Because the divider must be a power of two (as per the BCM2835 datasheet),
87   * calculate the next greater power of two. */
88  --divider;
89
90  divider |= (divider >> 1);
91  divider |= (divider >> 2);
92  divider |= (divider >> 4);
93  divider |= (divider >> 8);
94
95  ++divider;
96
97  clock_rate = GPU_CORE_CLOCK_RATE / divider;
98
99  /* If the resulting clock rate is greater than the desired frequency,
100   * try the next greater power of two divider. */
101  while ( clock_rate > clock_hz ) {
102    divider = (divider << 1);
103
104    clock_rate = GPU_CORE_CLOCK_RATE / divider;
105  }
106
107  *clock_divider = divider;
108
109  return RTEMS_SUCCESSFUL;
110}
111
112/**
113 * @brief Set the SPI bus transfer mode.
114 *
115 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
116 * @param[in] tfr_mode Pointer to a libi2c API transfer mode data structure.
117 *
118 * @retval RTEMS_SUCCESSFUL Successfully setup the bus transfer mode as desired.
119 * @retval RTEMS_INVALID_NUMBER This can have two meanings:
120 *                              1. The specified number of bytes per char is not
121 *                                 8, 16, 24 or 32;
122 *                              2. @see rpi_spi_calculate_clock_divider()
123 */
124static rtems_status_code rpi_spi_set_tfr_mode(
125  rtems_libi2c_bus_t *bushdl,
126  const rtems_libi2c_tfr_mode_t *tfr_mode
127) {
128  rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
129  rtems_status_code sc = RTEMS_SUCCESSFUL;
130  uint16_t clock_divider;
131
132  /* Set the dummy character. */
133  softc_ptr->dummy_char = tfr_mode->idle_char;
134
135  /* Calculate the most appropriate clock divider. */
136  sc = rpi_spi_calculate_clock_divider(tfr_mode->baudrate, &clock_divider);
137
138  if ( sc != RTEMS_SUCCESSFUL ) {
139    return sc;
140  }
141
142  /* Set the bus clock divider. */
143  BCM2835_REG(BCM2835_SPI_CLK) = clock_divider;
144
145  /* Calculate how many bytes each character has.
146   * Only multiples of 8 bits are accepted for the transaction. */
147  switch ( tfr_mode->bits_per_char ) {
148    case 8:
149    case 16:
150    case 24:
151    case 32:
152      softc_ptr->bytes_per_char = tfr_mode->bits_per_char / 8;
153      break;
154
155    default:
156      return RTEMS_INVALID_NUMBER;
157  }
158
159  /* Check the data mode (most or least significant bit first) and calculate
160   * the correcting bit shift value to apply on the data before sending. */
161  if ( tfr_mode->lsb_first ) {
162    softc_ptr->bit_shift = 32 - tfr_mode->bits_per_char;
163  }
164  /* If MSB first. */
165  else {
166    softc_ptr->bit_shift = 0;
167  }
168
169  /* Set SPI clock polarity.
170   * If clock_inv is TRUE, the clock is active high.*/
171  if ( tfr_mode->clock_inv ) {
172    /* Rest state of clock is low. */
173    BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 3);
174  }
175  else {
176    /* Rest state of clock is high. */
177    BCM2835_REG(BCM2835_SPI_CS) |= (1 << 3);
178  }
179
180  /* Set SPI clock phase.
181   * If clock_phs is true, clock starts toggling
182   * at the start of the data transfer. */
183  if ( tfr_mode->clock_phs ) {
184    /* First SCLK transition at beginning of data bit. */
185    BCM2835_REG(BCM2835_SPI_CS) |= (1 << 2);
186  }
187  else {
188    /* First SCLK transition at middle of data bit. */
189    BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 2);
190  }
191
192  return sc;
193}
194
195/**
196 * @brief Reads/writes to/from the SPI bus.
197 *
198 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
199 * @param[in] rd_buf Read buffer. If not NULL the function will read from
200 *                   the bus and store the read on this buffer.
201 * @param[in] wr_buf Write buffer. If not NULL the function will write the
202 *                   contents of this buffer to the bus.
203 * @param[in] buffer_size Size of the non-NULL buffer.
204 *
205 * @retval -1 Could not send/receive data to/from the bus.
206 * @retval >=0 The number of bytes read/written.
207 */
208static int rpi_spi_read_write(
209  rtems_libi2c_bus_t * bushdl,
210  unsigned char *rd_buf,
211  const unsigned char *wr_buf,
212  int buffer_size
213) {
214  rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
215
216  uint8_t bytes_per_char = softc_ptr->bytes_per_char;
217  uint8_t bit_shift =  softc_ptr->bit_shift;
218  uint32_t dummy_char = softc_ptr->dummy_char;
219
220  uint32_t bytes_sent = buffer_size;
221  uint32_t fifo_data;
222
223  /* Clear SPI bus FIFOs. */
224  BCM2835_REG(BCM2835_SPI_CS) |= (3 << 4);
225
226  /* Set SPI transfer active. */
227  BCM2835_REG(BCM2835_SPI_CS) |= (1 << 7);
228
229  /* If using the SPI bus in interrupt-driven mode. */
230#if SPI_IO_MODE == 1
231  softc_ptr->irq_write = 1;
232
233  BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
234
235  if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
236    rtems_event_transient_clear();
237
238    return -1;
239  }
240
241  /* If using the bus in polling mode. */
242#else
243  /* Poll TXD bit until there is space to write at least one byte
244   * on the TX FIFO. */
245  SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) == 0);
246#endif
247
248  /* While there is data to be transferred. */
249  while ( buffer_size >= bytes_per_char ) {
250    /* If reading from the bus, send a dummy character to the device. */
251    if ( rd_buf != NULL ) {
252      BCM2835_REG(BCM2835_SPI_FIFO) = dummy_char;
253    }
254    /* If writing to the bus, move the buffer data to the TX FIFO. */
255    else {
256      switch ( bytes_per_char ) {
257        case 1:
258          BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFF) << bit_shift);
259          break;
260
261        case 2:
262          BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFF) << bit_shift);
263          break;
264
265        case 3:
266          BCM2835_REG(BCM2835_SPI_FIFO) = (((*wr_buf) & 0xFFFFFF) << bit_shift);
267          break;
268
269        case 4:
270          BCM2835_REG(BCM2835_SPI_FIFO) = ((*wr_buf) << bit_shift);
271          break;
272
273        default:
274          return -1;
275      }
276
277      wr_buf += bytes_per_char;
278
279      buffer_size -= bytes_per_char;
280    }
281
282    /* If using bi-directional SPI. */
283    if ( bidirectional ) {
284      /* Change bus direction to read from the slave device. */
285      BCM2835_REG(BCM2835_SPI_CS) |= (1 << 12);
286    }
287
288    /* If using the SPI bus in interrupt-driven mode. */
289#if SPI_IO_MODE == 1
290    softc_ptr->irq_write = 0;
291
292    BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
293
294    if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
295      rtems_event_transient_clear();
296
297      return -1;
298    }
299
300    /* If using the bus in polling mode. */
301#else
302    /* Poll the Done bit until the data transfer is complete. */
303    SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0);
304
305    /* Poll the RXD bit until there is at least one byte
306     * on the RX FIFO to be read. */
307    SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) == 0);
308#endif
309
310    /* If writing to the bus, read the dummy char sent by the slave device. */
311    if ( rd_buf == NULL ) {
312      fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF;
313    }
314
315    /* If reading from the bus, retrieve data from the RX FIFO and
316     * store it on the buffer. */
317    if ( rd_buf != NULL ) {
318      switch ( bytes_per_char ) {
319        case 1:
320          fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFF;
321          (*rd_buf) = (fifo_data >> bit_shift);
322          break;
323
324        case 2:
325          fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFF;
326          (*rd_buf) = (fifo_data >> bit_shift);
327          break;
328
329        case 3:
330          fifo_data = BCM2835_REG(BCM2835_SPI_FIFO) & 0xFFFFFF;
331          (*rd_buf) = (fifo_data >> bit_shift);
332          break;
333
334        case 4:
335          fifo_data = BCM2835_REG(BCM2835_SPI_FIFO);
336          (*rd_buf) = (fifo_data >> bit_shift);
337          break;
338
339        default:
340          return -1;
341      }
342
343      rd_buf += bytes_per_char;
344
345      buffer_size -= bytes_per_char;
346    }
347
348    /* If using bi-directional SPI. */
349    if ( bidirectional ) {
350      /* Restore bus direction to write to the slave. */
351      BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 12);
352    }
353  }
354
355  /* If using the SPI bus in interrupt-driven mode. */
356#if SPI_IO_MODE == 1
357  softc_ptr->irq_write = 1;
358
359  BCM2835_REG(BCM2835_SPI_CS) |= (1 << 9);
360
361  if ( rtems_event_transient_receive(RTEMS_WAIT, 0) != RTEMS_SUCCESSFUL ) {
362    rtems_event_transient_clear();
363
364    return -1;
365  }
366
367  /* If using the bus in polling mode. */
368#else
369  /* Poll the Done bit until the data transfer is complete. */
370  SPI_POLLING((BCM2835_REG(BCM2835_SPI_CS) & (1 << 16)) == 0);
371#endif
372
373  bytes_sent -= buffer_size;
374
375  return bytes_sent;
376}
377
378/**
379 * @brief Handler function that is called on any SPI interrupt.
380 *
381 *        There are 2 situations that can generate an interrupt:
382 *
383 *        1. Transfer (read/write) complete;
384 *        2. RX FIFO full.
385 *
386 *        Because the 2. situation is not useful to many applications,
387 *        the only interrupt that is generated and handled is the
388 *        transfer complete interrupt.
389 *
390 *        The objective of the handler is then, depending on the transfer
391 *        context (reading or writing on the bus), to check if there is enough
392 *        space available on the TX FIFO to send data over the bus (if writing)
393 *        or if the slave device has sent enough data to be fetched from the
394 *        RX FIFO (if reading).
395 *
396 *        When any of these two conditions occur, disables further interrupts
397 *        to be generated and sends a waking event to the transfer task
398 *        which will allow the following transfer to proceed.
399 *
400 * @param[in] arg Void pointer to the bus data structure.
401 */
402#if SPI_IO_MODE == 1
403static void spi_handler(void* arg)
404{
405  rpi_spi_softc_t *softc_ptr = (rpi_spi_softc_t *) arg;
406
407  /* If waiting to write to the bus, expect the TXD bit to be set, or
408   * if waiting to read from the bus, expect the RXD bit to be set
409   * before sending a waking event to the transfer task. */
410  if (
411      ( softc_ptr->irq_write == 1 &&
412        (BCM2835_REG(BCM2835_SPI_CS) & (1 << 18)) != 0
413      ) ||
414      ( softc_ptr->irq_write == 0 &&
415        (BCM2835_REG(BCM2835_SPI_CS) & (1 << 17)) != 0
416      )
417  ) {
418    /* Disable the SPI interrupt generation when a transfer is complete. */
419    BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 9);
420
421    /* Allow the transfer process to continue. */
422    rtems_event_transient_send(softc_ptr->task_id);
423  }
424}
425#endif
426
427/**
428 * @brief Low level function to initialize the SPI bus.
429 *        This function is used by the libi2c API.
430 *
431 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
432 *
433 * @retval RTEMS_SUCCESSFUL SPI bus successfully initialized.
434 * @retval Any other status code @see rtems_interrupt_handler_install().
435 */
436static rtems_status_code rpi_libi2c_spi_init(rtems_libi2c_bus_t * bushdl)
437{
438  rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
439  rtems_status_code sc = RTEMS_SUCCESSFUL;
440
441  if ( softc_ptr->initialized == 1 ) {
442    return sc;
443  }
444
445  softc_ptr->initialized = 1;
446
447  /* If using the SPI bus in interrupt-driven mode. */
448#if SPI_IO_MODE == 1
449  softc_ptr->task_id = rtems_task_self();
450
451  sc = rtems_interrupt_handler_install(
452         BCM2835_IRQ_ID_SPI,
453         NULL,
454         RTEMS_INTERRUPT_UNIQUE,
455         (rtems_interrupt_handler) spi_handler,
456         softc_ptr
457       );
458#endif
459
460  return sc;
461}
462
463/**
464 * @brief Low level function that would send a start condition over an I2C bus.
465 *        As it is not required to access a SPI bus it is here just to satisfy
466 *        the libi2c API, which requires this function.
467 *
468 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
469 *
470 * @retval RTEMS_SUCCESSFUL
471 */
472static rtems_status_code rpi_libi2c_spi_send_start(rtems_libi2c_bus_t * bushdl)
473{
474  return RTEMS_SUCCESSFUL;
475}
476
477/**
478 * @brief Low level function that terminates a SPI transfer.
479 *        It stops the SPI transfer and unselects the current SPI slave device.
480 *        This function is used by the libi2c API.
481 *
482 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
483 *
484 * @retval RTEMS_SUCCESSFUL The slave device has been successfully unselected.
485 * @retval RTEMS_INVALID_ADDRESS The stored slave address is neither 0 or 1.
486 */
487static rtems_status_code rpi_libi2c_spi_stop(rtems_libi2c_bus_t * bushdl)
488{
489  rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
490
491  uint32_t addr = softc_ptr->current_slave_addr;
492  uint32_t chip_select_bit = 21 + addr;
493
494  /* Set SPI transfer as not active. */
495  BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << 7);
496
497  /* Unselect the active SPI slave. */
498  switch ( addr ) {
499    case 0:
500    case 1:
501      BCM2835_REG(BCM2835_SPI_CS) |= (1 << chip_select_bit);
502
503      break;
504
505    default:
506      return RTEMS_INVALID_ADDRESS;
507  }
508
509  return RTEMS_SUCCESSFUL;
510}
511
512/**
513 * @brief Low level function which addresses a SPI slave device.
514 *        This function is used by the libi2c API.
515 *
516 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
517 * @param[in] addr SPI slave select line address (0 for CE0 or 1 for CE1).
518 * @param[in] rw This values is unnecessary to address a SPI device and its
519 *               presence here is only to fulfill a libi2c requirement.
520 *
521 * @retval RTEMS_SUCCESSFUL The slave device has been successfully addressed.
522 * @retval RTEMS_INVALID_ADDRESS The received address is neither 0 or 1.
523 */
524static rtems_status_code rpi_libi2c_spi_send_addr(
525  rtems_libi2c_bus_t * bushdl,
526  uint32_t addr,
527  int rw
528) {
529  rpi_spi_softc_t *softc_ptr = &(((rpi_spi_desc_t *)(bushdl))->softc);
530
531  /* Calculates the bit corresponding to the received address
532   * on the SPI control register. */
533  uint32_t chip_select_bit = 21 + addr;
534
535  /* Save which slave will be currently addressed,
536   * so it can be unselected later. */
537  softc_ptr->current_slave_addr = addr;
538
539  /* Select one of the two available SPI slave address lines. */
540  switch ( addr ) {
541    case 0:
542    case 1:
543      BCM2835_REG(BCM2835_SPI_CS) &= ~(1 << chip_select_bit);
544      break;
545
546    default:
547      return RTEMS_INVALID_ADDRESS;
548  }
549
550  return RTEMS_SUCCESSFUL;
551}
552
553/**
554 * @brief Low level function that reads a number of bytes from the SPI bus
555 *        on to a buffer.
556 *        This function is used by the libi2c API.
557 *
558 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
559 * @param[in] bytes Buffer where the data read from the bus will be stored.
560 * @param[in] nbytes Number of bytes to be read from the bus to the bytes buffer.
561 *
562 * @retval @see rpi_spi_read_write().
563 */
564static int rpi_libi2c_spi_read_bytes(
565  rtems_libi2c_bus_t * bushdl,
566  unsigned char *bytes,
567  int nbytes
568) {
569  return rpi_spi_read_write(bushdl, bytes, NULL, nbytes);
570}
571
572/**
573 * @brief Low level function that writes a number of bytes from a buffer
574 *        to the SPI bus.
575 *        This function is used by the libi2c API.
576 *
577 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
578 * @param[in] bytes Buffer with data to send over the SPI bus.
579 * @param[in] nbytes Number of bytes to be written from the bytes buffer
580                     to the bus.
581 *
582 * @retval @see rpi_spi_read_write().
583 */
584static int rpi_libi2c_spi_write_bytes(
585  rtems_libi2c_bus_t * bushdl,
586  unsigned char *bytes,
587  int nbytes
588) {
589  return rpi_spi_read_write(bushdl, NULL, bytes, nbytes);
590}
591
592/**
593 * @brief Low level function that is used to perform ioctl
594 *        operations on the bus. Currently only setups
595 *        the bus transfer mode.
596 *        This function is used by the libi2c API.
597 *
598 * @param[in] bushdl Pointer to the libi2c API bus driver data structure.
599 * @param[in] cmd IOCTL request command.
600 * @param[in] arg Arguments needed to fulfill the requested IOCTL command.
601 *
602 * @retval -1 Unknown request command.
603 * @retval >=0 @see rpi_spi_set_tfr_mode().
604 */
605static int rpi_libi2c_spi_ioctl(rtems_libi2c_bus_t * bushdl, int cmd, void *arg)
606{
607  switch ( cmd ) {
608    case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
609      return rpi_spi_set_tfr_mode(
610               bushdl,
611               (const rtems_libi2c_tfr_mode_t *)arg
612             );
613    default:
614      return -1;
615  }
616
617  return 0;
618}
619
620static rtems_libi2c_bus_ops_t rpi_spi_ops = {
621  .init = rpi_libi2c_spi_init,
622  .send_start = rpi_libi2c_spi_send_start,
623  .send_stop = rpi_libi2c_spi_stop,
624  .send_addr = rpi_libi2c_spi_send_addr,
625  .read_bytes = rpi_libi2c_spi_read_bytes,
626  .write_bytes = rpi_libi2c_spi_write_bytes,
627  .ioctl = rpi_libi2c_spi_ioctl
628};
629
630static rpi_spi_desc_t rpi_spi_bus_desc = {
631  {
632    .ops = &rpi_spi_ops,
633    .size = sizeof(rpi_spi_bus_desc)
634  },
635  {
636    .initialized = 0
637  }
638};
639
640int rpi_spi_init(bool bidirectional_mode)
641{
642  /* Initialize the libi2c API. */
643  rtems_libi2c_initialize();
644
645  /* Enable the SPI interface on the Raspberry Pi. */
646  rtems_gpio_initialize();
647
648  assert ( rpi_gpio_select_spi() == RTEMS_SUCCESSFUL );
649
650  bidirectional = bidirectional_mode;
651
652  /* Clear SPI control register and clear SPI FIFOs. */
653  BCM2835_REG(BCM2835_SPI_CS) = (3 << 4);
654
655  /* Register the SPI bus. */
656  return rtems_libi2c_register_bus("/dev/spi", &(rpi_spi_bus_desc.bus_desc));
657}
Note: See TracBrowser for help on using the repository browser.