source: rtems/bsps/arm/beagle/spi/spi.c

Last change on this file was ecf62845, checked in by Pierre-Louis Garnier <garnie_a@…>, on 02/25/19 at 22:30:09

arm/beagle: SPI driver

  • Property mode set to 100644
File size: 14.4 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup arm_beagle
5 *
6 * @brief BeagleBoard SPI bus initialization and API Support.
7 *
8 * Based on bsps/m68k/gen68360/spi/m360_spi.c
9 */
10
11/*
12 * Copyright (c) 2018 Pierre-Louis Garnier <garnie_a@epita.fr>
13 *
14 * The license and distribution terms for this file may be
15 * found in the file LICENSE in this distribution or at
16 * http://www.rtems.org/license/LICENSE.
17 */
18#include <bsp.h>
19#include <bsp/bbb-gpio.h>
20#include <bsp/spi.h>
21#include <errno.h>
22#include <rtems/bspIo.h>
23#include <rtems/error.h>
24#include <rtems/libi2c.h>
25#include <stdio.h>
26#include <stdlib.h>
27
28// #define DEBUG
29// #define TRACE
30
31#define EVENT_TXEMPTY     RTEMS_EVENT_0
32#define EVENT_RXFULL      RTEMS_EVENT_1
33
34static void SPI0ModuleClkConfig(void)
35{
36  /* Writing to MODULEMODE field of AM335X_CM_PER_SPI0_CLKCTRL register. */
37  REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) |=
38    AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE;
39
40  /* Waiting for MODULEMODE field to reflect the written value. */
41  while ( AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE !=
42          ( REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) &
43            AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE ) )
44    continue;
45
46  /*
47   * Waiting for IDLEST field in AM335X_CM_PER_SPI0_CLKCTRL
48   * register to attain desired value.
49   */
50  while ( ( AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST_FUNC <<
51            AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST_SHIFT ) !=
52          ( REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) &
53            AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST ) )
54    continue;
55}
56
57static inline void am335x_spi_clear_irqstatus(uint32_t reg_base, uint32_t irqs)
58{
59  REG(reg_base + AM335X_SPI_SYST) &= ~AM335X_SPI_SYST_SSB;
60  REG(reg_base + AM335X_SPI_IRQSTATUS) = irqs;
61}
62
63static void am335x_spi0_pinmux(void)
64{
65  REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) =
66    (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN);
67
68  REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) =
69    (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN);
70
71  REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D1) =
72    (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN);
73
74  REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_CS0) =
75    (BBB_RXACTIVE | BBB_PU_EN);
76}
77
78static void am335x_spi_reset(uint32_t reg_base)
79{
80  int timeout = BBB_SPI_TIMEOUT;
81
82  REG(reg_base + AM335X_SPI_SYSCONFIG) |= AM335X_SPI_SYSCONFIG_SOFTRESET;
83
84  while ((REG(reg_base + AM335X_SPI_SYSSTATUS) & AM335X_SPI_SYSSTATUS_RESETDONE) == 0 && timeout--) {
85    if (timeout <= 0) {
86      puts("ERROR: Timeout in soft-reset\n");
87
88      return;
89    }
90
91    udelay(1000);
92  }
93}
94
95static void beagle_spi_irq_handler(void *arg)
96{
97  const uint32_t handled_irqs = AM335X_SPI_IRQSTATUS_TX0_EMPTY | AM335X_SPI_IRQSTATUS_RX0_FULL;
98
99  beagle_spi_softc_t *softc_ptr = arg;
100
101  rtems_status_code sc = -1;
102
103  uint32_t irq = 0;
104  uint32_t events = 0;
105
106  uint32_t tmp;
107  while ((tmp = (REG(softc_ptr->regs_base + AM335X_SPI_IRQSTATUS)) & handled_irqs) != 0) {
108    irq |= tmp;
109    am335x_spi_clear_irqstatus(softc_ptr->regs_base, tmp);
110  }
111
112#if defined(TRACE)
113    printk("beagle_spi_irq_handler: AM335X_SPI_IRQSTATUS = 0x%04lx\r\n", irq);
114#endif
115
116  if (irq & AM335X_SPI_IRQSTATUS_TX0_EMPTY) {
117#if defined(TRACE)
118    printk("beagle_spi_irq_handler: sending event TXEMPTY to task_id = %ld\r\n", softc_ptr->task_id);
119#endif
120
121    events |= EVENT_TXEMPTY;
122  }
123
124  if (irq & AM335X_SPI_IRQSTATUS_RX0_FULL) {
125#if defined(TRACE)
126    printk("beagle_spi_irq_handler: sending event RXFULL to task_id = %ld\r\n", softc_ptr->task_id);
127#endif
128
129    events |= EVENT_RXFULL;
130  }
131
132  sc = rtems_event_send(softc_ptr->task_id, events);
133  _Assert(sc == RTEMS_SUCCESSFUL);
134  (void)sc;
135}
136
137/* Initialize the driver
138 *
139 * Returns: o = ok or error code
140 */
141rtems_status_code beagle_spi_init
142(
143 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
144)
145{
146  beagle_spi_softc_t *softc_ptr = &(((beagle_spi_desc_t *)(bh))->softc);
147
148  rtems_status_code rc = RTEMS_SUCCESSFUL;
149
150#if defined(DEBUG)
151  printk("beagle_spi_init called...\r\n");
152#endif
153
154  SPI0ModuleClkConfig();
155  am335x_spi0_pinmux();
156  am335x_spi_reset(softc_ptr->regs_base);
157
158  REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_PIN34;
159  REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_MS; // Master mode
160  REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) |= AM335X_SPI_MODULCTRL_SINGLE; // Single channel
161  REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_PIN34; // SPIEN is usedas a chip select
162  // REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) |= (1 << 3); // Test mode
163
164  // REG(softc_ptr->regs_base + AM335X_SPI_SYST) |= AM335X_SPI_SYST_SPIEN_0; // Not sure about this
165  // REG(softc_ptr->regs_base + AM335X_SPI_SYST) |= AM335X_SPI_SYST_SPIDATDIR0; // Input
166  // REG(softc_ptr->regs_base + AM335X_SPI_SYST) &= ~AM335X_SPI_SYST_SPIDATDIR1; // Output
167
168  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_TRM_MASK;
169  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_DPE0;
170  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_DPE1;
171  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_IS;
172
173  // REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FFEW;
174  // REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FFER;
175  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_WL(8 - 1);
176
177  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_PHA;
178  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_POL;
179
180  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_EPOL;
181  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_CLKD(0x1);
182
183
184  // Setup interrupt
185  rc = rtems_interrupt_handler_install(
186    softc_ptr->irq,
187    NULL,
188    RTEMS_INTERRUPT_UNIQUE,
189    (rtems_interrupt_handler)beagle_spi_irq_handler,
190    softc_ptr
191  );
192
193#if defined(DEBUG)
194  printk("beagle_spi_init done\r\n");
195#endif
196
197  if (rc == RTEMS_SUCCESSFUL) {
198    softc_ptr->initialized = TRUE;
199  }
200
201  return rc;
202}
203
204static int beagle_spi_read_write_bytes(
205 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
206 unsigned char *rx_buf,                  /* buffer to store bytes          */
207 unsigned char *tx_buf,                  /* buffer to send                 */
208 int len                                 /* number of bytes to send        */
209)
210{
211  beagle_spi_softc_t *softc_ptr = &(((beagle_spi_desc_t *)(bh))->softc);
212
213  rtems_status_code sc;
214  rtems_event_set received_events;
215
216#if defined(TRACE)
217  printk("beagle_spi_read_write_bytes called...\r\n");
218#endif
219
220  softc_ptr->task_id = rtems_task_self();
221
222  // Enable IRQs
223
224  if (rx_buf) {
225    am335x_spi_clear_irqstatus(softc_ptr->regs_base, AM335X_SPI_IRQSTATUS_RX0_FULL);
226    REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) |= AM335X_SPI_IRQENABLE_RX0_FULL;
227  }
228
229  if (tx_buf) {
230    am335x_spi_clear_irqstatus(softc_ptr->regs_base, AM335X_SPI_IRQSTATUS_TX0_EMPTY);
231    REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) |= AM335X_SPI_IRQENABLE_TX0_EMPTY;
232  }
233
234  // Enable Channel
235  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FORCE;
236  REG(softc_ptr->regs_base + AM335X_SPI_CH0CTRL) |= AM335X_SPI_CH0CTRL_EN;
237
238  // Main loop
239  for (int i = 0; i < len; i++) {
240    received_events = 0;
241
242    if (tx_buf) {
243      // Wait for IRQ to wake us up (room in TX FIFO)
244#if defined(TRACE)
245      printk("beagle_spi_read_write_bytes: waiting (task_id = %ld)\r\n", softc_ptr->task_id);
246#endif
247
248      sc = rtems_event_receive(EVENT_TXEMPTY, RTEMS_WAIT, BBB_SPI_TIMEOUT, &received_events);
249      if (sc != RTEMS_SUCCESSFUL) {
250        printk("ERROR: beagle_spi_read_write_bytes timed out on tx byte number %d\n", i);
251        return i > 0 ? i : -RTEMS_TIMEOUT;
252      }
253
254      _Assert(received_events == EVENT_TXEMPTY);
255
256#if defined(TRACE)
257      printk("beagle_spi_read_write_bytes: sending byte: i = %d, tx_buf[i] = 0x%x\r\n", i, tx_buf[i]);
258#endif
259
260      REG(softc_ptr->regs_base + AM335X_SPI_TX0) = tx_buf[i];
261    }
262
263    if (rx_buf) {
264      // Wait for IRQ to wake us up (data in RX FIFO)
265      if ((received_events & EVENT_RXFULL) == 0) {
266        sc = rtems_event_receive(EVENT_RXFULL, RTEMS_WAIT, BBB_SPI_TIMEOUT, &received_events);
267        if (sc != RTEMS_SUCCESSFUL) {
268          printk("ERROR: beagle_spi_read_write_bytes timed out on rx byte number %d\n", i);
269          return i > 0 ? i : -RTEMS_TIMEOUT;
270        }
271
272        _Assert(received_events == EVENT_RXFULL);
273      }
274
275      rx_buf[i] = REG(softc_ptr->regs_base + AM335X_SPI_RX0);
276
277#if defined(TRACE)
278      printk("beagle_spi_read_write_bytes: received byte: i = %d, rx_buf[i] = 0x%x\r\n", i, rx_buf[i]);
279#endif
280    }
281  }
282
283  // REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) &= ~AM335X_SPI_IRQENABLE_RX0_FULL;
284  // REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) &= ~AM335X_SPI_IRQENABLE_TX0_EMPTY;
285
286  REG(softc_ptr->regs_base + AM335X_SPI_CH0CTRL) &= ~AM335X_SPI_CH0CTRL_EN;
287  REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_FORCE;
288
289#if defined(TRACE)
290  printk("beagle_spi_read_write_bytes done\r\n");
291#endif
292
293  return len;
294}
295
296/*
297 * Receive some bytes from SPI device
298 *
299 * Returns: number of bytes received or (negative) error code
300 */
301int beagle_spi_read_bytes
302(
303 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
304 unsigned char *buf,                     /* buffer to store bytes          */
305 int len                                 /* number of bytes to receive     */
306)
307{
308  // FIXME
309#if defined(DEBUG)
310  printk("beagle_spi_read_bytes called...\r\n");
311#endif
312
313  int n = beagle_spi_read_write_bytes(bh, buf, NULL, len);
314
315#if defined(DEBUG)
316  printk("beagle_spi_read_bytes done\r\n");
317#endif
318
319  return n;
320}
321
322/*
323 * Send some bytes to SPI device
324 *
325 * Returns: number of bytes sent or (negative) error code
326 */
327int beagle_spi_write_bytes
328(
329 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
330 unsigned char *buf,                     /* buffer to send                 */
331 int len                                 /* number of bytes to send        */
332)
333{
334#if defined(DEBUG)
335  printk("beagle_spi_write_bytes called...\r\n");
336#endif
337
338  int n = beagle_spi_read_write_bytes(bh, NULL, buf, len);
339
340#if defined(DEBUG)
341  printk("beagle_spi_write_bytes done\r\n");
342#endif
343
344  return n;
345}
346
347/*
348 * Perform selected ioctl function for SPI
349 *
350 * Returns: rtems_status_code
351 */
352int beagle_spi_ioctl
353(
354 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
355 int                 cmd,                /* ioctl command code             */
356 void               *arg                 /* additional argument array      */
357)
358{
359  int ret_val = -1;
360
361#if defined(DEBUG)
362  printk("beagle_spi_ioctl called...\r\n");
363#endif
364
365  switch(cmd) { // FIXME: other ioctls
366  case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
367#if defined(TRACE)
368    printk("cmd == RTEMS_LIBI2C_IOCTL_SET_TFRMODE\r\n");
369#endif
370    // FIXME
371    // ret_val =
372    //   -m360_spi_set_tfr_mode(bh,
373    //     (const rtems_libi2c_tfr_mode_t *)arg);
374    break;
375  case RTEMS_LIBI2C_IOCTL_READ_WRITE: {
376#if defined(TRACE)
377    printk("cmd == RTEMS_LIBI2C_IOCTL_READ_WRITE\r\n");
378#endif
379    const rtems_libi2c_read_write_t *cmd = (const rtems_libi2c_read_write_t *)arg;
380
381    ret_val = beagle_spi_read_write_bytes(
382      bh,
383      (unsigned char *)cmd->rd_buf,
384      (unsigned char *)cmd->wr_buf,
385      cmd->byte_cnt
386    );
387    break;
388  }
389  default:
390    ret_val = -RTEMS_NOT_DEFINED;
391    break;
392  }
393
394#if defined(DEBUG)
395  printk("beagle_spi_ioctl done\r\n");
396#endif
397
398  return ret_val;
399}
400
401/*=========================================================================*\
402| Board-specific adaptation functions                                       |
403\*=========================================================================*/
404
405/*
406 * Address a slave device on the bus
407 *
408 * Returns: o = ok or error code
409 */
410static rtems_status_code bsp_spi_sel_addr
411(
412 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
413 uint32_t addr,                          /* address to send on bus         */
414 int rw                                  /* 0=write,1=read                 */
415)
416{
417  if (addr != 0)
418    return RTEMS_NOT_IMPLEMENTED;
419
420  return  RTEMS_SUCCESSFUL;
421}
422
423/*
424 * Dummy function, SPI has no start condition
425 *
426 * Returns: o = ok or error code
427 */
428static rtems_status_code bsp_spi_send_start_dummy
429(
430 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
431)
432{
433#if defined(DEBUG)
434  printk("bsp_spi_send_start_dummy OK\r\n");
435#endif
436
437  return 0;
438}
439
440/*
441 * Deselect SPI
442 *
443 * Returns: o = ok or error code
444 */
445static rtems_status_code bsp_spi_send_stop
446(
447 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
448)
449{
450#if defined(DEBUG)
451  printk("bsp_spi_send_stop called... ");
452#endif
453
454  // FIXME
455
456#if defined(DEBUG)
457  printk("... exit OK\r\n");
458#endif
459  return 0;
460}
461
462/*=========================================================================*\
463| list of handlers                                                          |
464\*=========================================================================*/
465
466rtems_libi2c_bus_ops_t bsp_spi_ops = {
467  init:             beagle_spi_init,
468  send_start:       bsp_spi_send_start_dummy,
469  send_stop:        bsp_spi_send_stop,
470  send_addr:        bsp_spi_sel_addr,
471  read_bytes:       beagle_spi_read_bytes,
472  write_bytes:      beagle_spi_write_bytes,
473  ioctl:            beagle_spi_ioctl
474};
475
476static beagle_spi_desc_t bsp_spi_bus_desc = {
477  {/* public fields */
478    ops:    &bsp_spi_ops,
479    size:   sizeof(bsp_spi_bus_desc)
480  },
481  { /* our private fields */
482    initialized: FALSE,
483  }
484};
485
486/*=========================================================================*\
487| initialization                                                            |
488\*=========================================================================*/
489
490/*
491 * Register SPI bus and devices
492 *
493 * Returns: Bus number or error code
494 */
495rtems_status_code bsp_register_spi
496(
497  const char         *bus_path,
498  uintptr_t           register_base,
499  rtems_vector_number irq
500)
501{
502  int ret_code;
503  int spi_busno;
504
505  beagle_spi_softc_t *softc_ptr = &bsp_spi_bus_desc.softc;
506
507  if (softc_ptr->initialized) {
508    printk("ERROR: Only one SPI bus at a time is supported\n");
509    return -RTEMS_RESOURCE_IN_USE;
510  }
511
512  softc_ptr->regs_base = register_base;
513  softc_ptr->irq = irq;
514
515  /*
516   * init I2C library (if not already done)
517   */
518  rtems_libi2c_initialize();
519
520  /*
521   * register SPI bus
522   */
523  ret_code = rtems_libi2c_register_bus(bus_path,
524                       &(bsp_spi_bus_desc.bus_desc));
525  if (ret_code < 0) {
526    return -ret_code;
527  }
528  spi_busno = ret_code;
529
530#if IS_AM335X
531  // TODO: register board devices
532#endif
533
534  return spi_busno;
535}
Note: See TracBrowser for help on using the repository browser.