source: rtems/bsps/powerpc/gen83xx/dev/mpc83xx_spidrv.c @ 8f8ccee

5
Last change on this file since 8f8ccee was 0cab067, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 23, 2018 at 2:54:12 PM

bsps/powerpc: Move libcpu content to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 26.0 KB
Line 
1/*===============================================================*\
2| Project: RTEMS support for MPC83xx                              |
3+-----------------------------------------------------------------+
4|                    Copyright (c) 2007                           |
5|                    Embedded Brains GmbH                         |
6|                    Obere Lagerstr. 30                           |
7|                    D-82178 Puchheim                             |
8|                    Germany                                      |
9|                    rtems@embedded-brains.de                     |
10+-----------------------------------------------------------------+
11| The license and distribution terms for this file may be         |
12| found in the file LICENSE in this distribution or at            |
13|                                                                 |
14| http://www.rtems.org/license/LICENSE.                           |
15|                                                                 |
16+-----------------------------------------------------------------+
17| this file contains the MPC83xx SPI driver                       |
18| NOTE: it uses the same API as the I2C driver                    |
19\*===============================================================*/
20#include <stdlib.h>
21#include <bsp.h>
22#include <bsp/irq.h>
23#include <mpc83xx/mpc83xx.h>
24#include <mpc83xx/mpc83xx_spidrv.h>
25#include <rtems/error.h>
26#include <rtems/bspIo.h>
27#include <errno.h>
28#include <rtems/libi2c.h>
29
30#undef DEBUG
31
32/*=========================================================================*\
33| Function:                                                                 |
34\*-------------------------------------------------------------------------*/
35static rtems_status_code mpc83xx_spi_baud_to_mode
36(
37/*-------------------------------------------------------------------------*\
38| Purpose:                                                                  |
39|   determine proper divider value                                          |
40+---------------------------------------------------------------------------+
41| Input Parameters:                                                         |
42\*-------------------------------------------------------------------------*/
43 uint32_t baudrate,                      /* desired baudrate               */
44 uint32_t base_frq,                      /* input frequency                */
45 uint32_t *spimode                       /* result value                   */
46)
47/*-------------------------------------------------------------------------*\
48| Return Value:                                                             |
49|    o = ok or error code                                                   |
50\*=========================================================================*/
51{
52  uint32_t divider;
53  uint32_t tmpmode = 0;
54  /*
55   * determine clock divider and DIV16 bit
56   */
57  divider = (base_frq+baudrate-1)/baudrate;
58  if (divider > 64) {
59    tmpmode = MPC83XX_SPIMODE_DIV16;
60    divider /= 16;
61  }
62  if ((divider <  1) ||
63      (divider > 64)) {
64    return RTEMS_INVALID_NUMBER;
65  }
66  else {
67    tmpmode |= MPC83XX_SPIMODE_PM(divider/4-1);
68  }
69  *spimode = tmpmode;
70  return RTEMS_SUCCESSFUL;
71}
72
73/*=========================================================================*\
74| Function:                                                                 |
75\*-------------------------------------------------------------------------*/
76static rtems_status_code mpc83xx_spi_char_mode
77(
78/*-------------------------------------------------------------------------*\
79| Purpose:                                                                  |
80|   determine proper value for character size                               |
81+---------------------------------------------------------------------------+
82| Input Parameters:                                                         |
83\*-------------------------------------------------------------------------*/
84 mpc83xx_spi_softc_t *softc_ptr,         /* handle                         */
85 uint32_t bits_per_char,                 /* bits per character             */
86 bool     lsb_first,                     /* TRUE: send LSB first           */
87 uint32_t *spimode                       /* result value                   */
88)
89/*-------------------------------------------------------------------------*\
90| Return Value:                                                             |
91|    o = ok or error code                                                   |
92\*=========================================================================*/
93{
94  uint32_t tmpmode;
95
96  if (bits_per_char == 32) {
97    tmpmode = 0;
98    softc_ptr->bytes_per_char = 4;
99    softc_ptr->bit_shift      = 0;
100  }
101  else {
102    if (lsb_first) {
103      /*
104       * non-reversed data (LSB first): 4..16 bits valid
105       * always aligned to bit 16 of data register
106       */
107      if ((bits_per_char >= 4) &&
108          (bits_per_char <= 16)) {
109        tmpmode = MPC83XX_SPIMODE_LEN( bits_per_char-1);
110        softc_ptr->bytes_per_char = (bits_per_char > 8) ? 2 : 1;
111        softc_ptr->bit_shift      = 16-bits_per_char;
112      }
113      else {
114        return RTEMS_INVALID_NUMBER;
115      }
116    }
117    else {
118      /*
119       * reversed data (MSB first): only 8/16/32 bits valid,
120       * always in lowest bits of data register
121       */
122      if (bits_per_char == 8) {
123        tmpmode = MPC83XX_SPIMODE_LEN(8-1);
124        softc_ptr->bytes_per_char = 1;
125        softc_ptr->bit_shift      = 0;
126      }
127      else if (bits_per_char == 16) {
128        tmpmode = MPC83XX_SPIMODE_LEN(16-1);
129        softc_ptr->bytes_per_char = 2;
130        softc_ptr->bit_shift      = 0;
131      }
132      else {
133        return RTEMS_INVALID_NUMBER;
134      }
135    }
136  }
137
138  *spimode = tmpmode;
139  return 0;
140}
141
142/*=========================================================================*\
143| Function:                                                                 |
144\*-------------------------------------------------------------------------*/
145static int mpc83xx_spi_wait
146(
147/*-------------------------------------------------------------------------*\
148| Purpose:                                                                  |
149|   wait for spi to become idle                                             |
150+---------------------------------------------------------------------------+
151| Input Parameters:                                                         |
152\*-------------------------------------------------------------------------*/
153 mpc83xx_spi_softc_t *softc_ptr,          /* handle              */
154 uint32_t             irq_mask,           /* irq mask to use     */
155 uint32_t             desired_status,     /* desired status word */
156 uint32_t             status_mask         /* status word mask    */
157)
158/*-------------------------------------------------------------------------*\
159| Return Value:                                                             |
160|    o = ok or error code                                                   |
161\*=========================================================================*/
162{
163  uint32_t act_status;
164  rtems_status_code rc;
165  uint32_t tout;
166
167#if defined(DEBUG)
168  printk("mpc83xx_spi_wait called... ");
169#endif
170  if (softc_ptr->initialized) {
171    /*
172     * allow interrupts, when receiver is not empty
173     */
174    softc_ptr->reg_ptr->spim = irq_mask;
175    rc = rtems_semaphore_obtain(softc_ptr->irq_sema_id,RTEMS_WAIT,100);
176    if (rc != RTEMS_SUCCESSFUL) {
177      return rc;
178    }
179  }
180  else {
181    tout = 0;
182    do {
183      if (tout++ > 1000000) {
184#if defined(DEBUG)
185        printk("... exit with RTEMS_TIMEOUT\r\n");
186#endif
187        return RTEMS_TIMEOUT;
188      }
189      /*
190       * wait for SPI to terminate
191       */
192    } while (!(softc_ptr->reg_ptr->spie & MPC83XX_SPIE_NE));
193  }
194
195  act_status = softc_ptr->reg_ptr->spie;
196  if ((act_status  & status_mask)!= desired_status) {
197#if defined(DEBUG)
198    printk("... exit with RTEMS_IO_ERROR,"
199           "act_status=0x%04x,mask=0x%04x,desired_status=0x%04x\r\n",
200           act_status,status_mask,desired_status);
201#endif
202    return RTEMS_IO_ERROR;
203  }
204#if defined(DEBUG)
205        printk("... exit OK\r\n");
206#endif
207  return RTEMS_SUCCESSFUL;
208}
209
210/*=========================================================================*\
211| Function:                                                                 |
212\*-------------------------------------------------------------------------*/
213static void mpc83xx_spi_irq_handler
214(
215/*-------------------------------------------------------------------------*\
216| Purpose:                                                                  |
217|   handle interrupts                                                       |
218+---------------------------------------------------------------------------+
219| Input Parameters:                                                         |
220\*-------------------------------------------------------------------------*/
221 rtems_irq_hdl_param handle     /* handle, is softc_ptr structure          */
222)
223/*-------------------------------------------------------------------------*\
224| Return Value:                                                             |
225|    <none>                                                                 |
226\*=========================================================================*/
227{
228  mpc83xx_spi_softc_t *softc_ptr = (mpc83xx_spi_softc_t *)handle;
229
230  /*
231   * disable interrupt mask
232   */
233  softc_ptr->reg_ptr->spim = 0;
234  if (softc_ptr->initialized) {
235    rtems_semaphore_release(softc_ptr->irq_sema_id);
236  }
237}
238
239/*=========================================================================*\
240| Function:                                                                 |
241\*-------------------------------------------------------------------------*/
242static void mpc83xx_spi_irq_on_off
243(
244/*-------------------------------------------------------------------------*\
245| Purpose:                                                                  |
246|   enable/disable interrupts (void, handled at different position)         |
247+---------------------------------------------------------------------------+
248| Input Parameters:                                                         |
249\*-------------------------------------------------------------------------*/
250 const
251 rtems_irq_connect_data *irq_conn_data   /* irq connect data                */
252)
253/*-------------------------------------------------------------------------*\
254| Return Value:                                                             |
255|    <none>                                                                 |
256\*=========================================================================*/
257{
258}
259
260
261/*=========================================================================*\
262| Function:                                                                 |
263\*-------------------------------------------------------------------------*/
264static int mpc83xx_spi_irq_isOn
265(
266/*-------------------------------------------------------------------------*\
267| Purpose:                                                                  |
268|   check state of interrupts, void, done differently                       |
269+---------------------------------------------------------------------------+
270| Input Parameters:                                                         |
271\*-------------------------------------------------------------------------*/
272 const
273 rtems_irq_connect_data *irq_conn_data  /* irq connect data                */
274)
275/*-------------------------------------------------------------------------*\
276| Return Value:                                                             |
277|    TRUE, if enabled                                                       |
278\*=========================================================================*/
279{
280  return (TRUE);
281}
282
283/*=========================================================================*\
284| Function:                                                                 |
285\*-------------------------------------------------------------------------*/
286static void mpc83xx_spi_install_irq_handler
287(
288/*-------------------------------------------------------------------------*\
289| Purpose:                                                                  |
290|   (un-)install the interrupt handler                                      |
291+---------------------------------------------------------------------------+
292| Input Parameters:                                                         |
293\*-------------------------------------------------------------------------*/
294 mpc83xx_spi_softc_t *softc_ptr,        /* ptr to control structure        */
295 int install                            /* TRUE: install, FALSE: remove    */
296)
297/*-------------------------------------------------------------------------*\
298| Return Value:                                                             |
299|    <none>                                                                 |
300\*=========================================================================*/
301{
302  rtems_status_code rc = RTEMS_SUCCESSFUL;
303
304  rtems_irq_connect_data irq_conn_data = {
305    softc_ptr->irq_number,
306    mpc83xx_spi_irq_handler,        /* rtems_irq_hdl           */
307    (rtems_irq_hdl_param)softc_ptr, /* (rtems_irq_hdl_param)   */
308    mpc83xx_spi_irq_on_off,         /* (rtems_irq_enable)      */
309    mpc83xx_spi_irq_on_off,         /* (rtems_irq_disable)     */
310    mpc83xx_spi_irq_isOn            /* (rtems_irq_is_enabled)  */
311  };
312
313  /*
314   * (un-)install handler for SPI device
315   */
316  if (install) {
317    /*
318     * create semaphore for IRQ synchronization
319     */
320    rc = rtems_semaphore_create(rtems_build_name('s','p','i','s'),
321                                0,
322                                RTEMS_FIFO
323                                | RTEMS_SIMPLE_BINARY_SEMAPHORE,
324                                0,
325                                &softc_ptr->irq_sema_id);
326    if (rc != RTEMS_SUCCESSFUL) {
327      rtems_panic("SPI: cannot create semaphore");
328    }
329    if (!BSP_install_rtems_irq_handler (&irq_conn_data)) {
330      rtems_panic("SPI: cannot install IRQ handler");
331    }
332  }
333  else {
334    if (!BSP_remove_rtems_irq_handler (&irq_conn_data)) {
335      rtems_panic("SPI: cannot uninstall IRQ handler");
336    }
337    /*
338     * delete sync semaphore
339     */
340    if (softc_ptr->irq_sema_id != 0) {
341      rc = rtems_semaphore_delete(softc_ptr->irq_sema_id);
342      if (rc != RTEMS_SUCCESSFUL) {
343        rtems_panic("SPI: cannot delete semaphore");
344      }
345    }
346  }
347}
348
349/*=========================================================================*\
350| Function:                                                                 |
351\*-------------------------------------------------------------------------*/
352rtems_status_code mpc83xx_spi_init
353(
354/*-------------------------------------------------------------------------*\
355| Purpose:                                                                  |
356|   initialize the driver                                                   |
357+---------------------------------------------------------------------------+
358| Input Parameters:                                                         |
359\*-------------------------------------------------------------------------*/
360 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
361)
362/*-------------------------------------------------------------------------*\
363| Return Value:                                                             |
364|    o = ok or error code                                                   |
365\*=========================================================================*/
366{
367  mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc);
368#if defined(DEBUG)
369  printk("mpc83xx_spi_init called... ");
370#endif
371  /*
372   * init HW registers:
373   */
374  /*
375   * FIXME: set default mode in SPIM
376   */
377
378  /*
379   * init interrupt stuff
380   */
381  mpc83xx_spi_install_irq_handler(softc_ptr,TRUE);
382
383  /*
384   * mark, that we have initialized
385   */
386  softc_ptr->initialized = TRUE;
387#if defined(DEBUG)
388  printk("... exit OK\r\n");
389#endif
390  return RTEMS_SUCCESSFUL;
391}
392
393/*=========================================================================*\
394| Function:                                                                 |
395\*-------------------------------------------------------------------------*/
396int mpc83xx_spi_read_write_bytes
397(
398/*-------------------------------------------------------------------------*\
399| Purpose:                                                                  |
400|   transmit/receive some bytes from SPI device                             |
401+---------------------------------------------------------------------------+
402| Input Parameters:                                                         |
403\*-------------------------------------------------------------------------*/
404 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
405 unsigned char *rbuf,                    /* buffer to store bytes          */
406 const unsigned char *tbuf,              /* buffer to send  bytes          */
407 int len                                 /* number of bytes to transceive  */
408)
409/*-------------------------------------------------------------------------*\
410| Return Value:                                                             |
411|    number of bytes received or (negative) error code                      |
412\*=========================================================================*/
413{
414  mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc);
415  rtems_status_code rc;
416  int bc = 0;
417  int bytes_per_char = softc_ptr->bytes_per_char;
418  int bit_shift      = softc_ptr->bit_shift;
419  uint32_t spird_val;
420
421#if defined(DEBUG)
422  printk("mpc83xx_spi_read_write_bytes called... ");
423#endif
424
425  while (len > bytes_per_char-1) {
426    len -= bytes_per_char;
427    /*
428     * mark last byte in SPCOM
429     */
430#if defined(USE_LAST_BIT)
431    softc_ptr->reg_ptr->spcom = (len < bytes_per_char) ? MPC83XX_SPCOM_LST : 0;
432#else
433    softc_ptr->reg_ptr->spcom = 0;
434#endif
435    if (tbuf == NULL) {
436      /*
437       * perform idle char write to read byte
438       */
439      softc_ptr->reg_ptr->spitd = softc_ptr->idle_char << bit_shift;
440    }
441    else {
442      switch(bytes_per_char) {
443      case 1:
444        softc_ptr->reg_ptr->spitd = (*(uint8_t *)tbuf) << bit_shift;
445        break;
446      case 2:
447        softc_ptr->reg_ptr->spitd = (*(uint16_t *)tbuf) << bit_shift;
448        break;
449      case 4:
450        softc_ptr->reg_ptr->spitd = (*(uint32_t *)tbuf) << bit_shift;
451        break;
452      }
453      tbuf += softc_ptr->bytes_per_char;
454    }
455    /*
456     * wait 'til end of transfer
457     */
458#if defined(USE_LAST_BIT)
459    rc = mpc83xx_spi_wait(softc_ptr,
460                          ((len == 0)
461                           ? MPC83XX_SPIE_LT
462                           : MPC83XX_SPIE_NE),
463                          ((len == 0)
464                           ? MPC83XX_SPIE_LT
465                           : MPC83XX_SPIE_NF)
466                          | MPC83XX_SPIE_NE,
467                          MPC83XX_SPIE_LT
468                            | MPC83XX_SPIE_OV
469                          | MPC83XX_SPIE_UN
470                          | MPC83XX_SPIE_NE
471                          | MPC83XX_SPIE_NF);
472    if (len == 0) {
473      /*
474       * clear the "last transfer complete" event
475       */
476      softc_ptr->reg_ptr->spie = MPC83XX_SPIE_LT;
477    }
478#else
479    rc = mpc83xx_spi_wait(softc_ptr,
480                          MPC83XX_SPIE_NE,
481                          MPC83XX_SPIE_NF
482                          | MPC83XX_SPIE_NE,
483                          MPC83XX_SPIE_OV
484                          | MPC83XX_SPIE_UN
485                          | MPC83XX_SPIE_NE
486                          | MPC83XX_SPIE_NF);
487#endif
488    if (rc != RTEMS_SUCCESSFUL) {
489#if defined(DEBUG)
490      printk("... exit rc=%d\r\n",-rc);
491#endif
492      return -rc;
493    }
494    spird_val = softc_ptr->reg_ptr->spird;
495    if (rbuf != NULL) {
496      switch(bytes_per_char) {
497      case 1:
498         (*(uint8_t  *)rbuf) = spird_val >> bit_shift;
499        break;
500      case 2:
501         (*(uint16_t *)rbuf) = spird_val >> bit_shift;
502        break;
503      case 4:
504         (*(uint32_t *)rbuf) = spird_val >> bit_shift;
505        break;
506      }
507      rbuf += bytes_per_char;
508    }
509    bc += bytes_per_char;
510  }
511#if defined(DEBUG)
512  printk("... exit OK, rc=%d\r\n",bc);
513#endif
514  return bc;
515}
516
517/*=========================================================================*\
518| Function:                                                                 |
519\*-------------------------------------------------------------------------*/
520int mpc83xx_spi_read_bytes
521(
522/*-------------------------------------------------------------------------*\
523| Purpose:                                                                  |
524|   receive some bytes from SPI device                                      |
525+---------------------------------------------------------------------------+
526| Input Parameters:                                                         |
527\*-------------------------------------------------------------------------*/
528 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
529 unsigned char *buf,                     /* buffer to store bytes          */
530 int len                                 /* number of bytes to receive     */
531)
532/*-------------------------------------------------------------------------*\
533| Return Value:                                                             |
534|    number of bytes received or (negative) error code                      |
535\*=========================================================================*/
536{
537  return mpc83xx_spi_read_write_bytes(bh,buf,NULL,len);
538}
539
540/*=========================================================================*\
541| Function:                                                                 |
542\*-------------------------------------------------------------------------*/
543int mpc83xx_spi_write_bytes
544(
545/*-------------------------------------------------------------------------*\
546| Purpose:                                                                  |
547|   send some bytes to SPI device                                           |
548+---------------------------------------------------------------------------+
549| Input Parameters:                                                         |
550\*-------------------------------------------------------------------------*/
551 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
552 unsigned char *buf,                     /* buffer to send                 */
553 int len                                 /* number of bytes to send        */
554
555)
556/*-------------------------------------------------------------------------*\
557| Return Value:                                                             |
558|    number of bytes sent or (negative) error code                          |
559\*=========================================================================*/
560{
561  return mpc83xx_spi_read_write_bytes(bh,NULL,buf,len);
562}
563
564/*=========================================================================*\
565| Function:                                                                 |
566\*-------------------------------------------------------------------------*/
567rtems_status_code mpc83xx_spi_set_tfr_mode
568(
569/*-------------------------------------------------------------------------*\
570| Purpose:                                                                  |
571|   set SPI to desired baudrate/clock mode/character mode                   |
572+---------------------------------------------------------------------------+
573| Input Parameters:                                                         |
574\*-------------------------------------------------------------------------*/
575 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
576 const rtems_libi2c_tfr_mode_t *tfr_mode /* transfer mode info             */
577)
578/*-------------------------------------------------------------------------*\
579| Return Value:                                                             |
580|    rtems_status_code                                                      |
581\*=========================================================================*/
582{
583  mpc83xx_spi_softc_t *softc_ptr = &(((mpc83xx_spi_desc_t *)(bh))->softc);
584  uint32_t spimode_baud,spimode;
585  rtems_status_code rc = RTEMS_SUCCESSFUL;
586
587  /* Set idle character */
588  softc_ptr->idle_char = tfr_mode->idle_char;
589
590  /*
591   * FIXME: set proper mode
592   */
593  if (rc == RTEMS_SUCCESSFUL) {
594    rc = mpc83xx_spi_baud_to_mode(tfr_mode->baudrate,
595                                  softc_ptr->base_frq,
596                                  &spimode_baud);
597  }
598  if (rc == RTEMS_SUCCESSFUL) {
599    rc = mpc83xx_spi_char_mode(softc_ptr,
600                               tfr_mode->bits_per_char,
601                               tfr_mode->lsb_first,
602                               &spimode);
603  }
604  if (rc == RTEMS_SUCCESSFUL) {
605    spimode |= spimode_baud;
606    spimode |= MPC83XX_SPIMODE_M_S; /* set master mode */
607    if (!tfr_mode->lsb_first) {
608      spimode |= MPC83XX_SPIMODE_REV;
609    }
610    if (tfr_mode->clock_inv) {
611      spimode |= MPC83XX_SPIMODE_CI;
612    }
613    if (tfr_mode->clock_phs) {
614      spimode |= MPC83XX_SPIMODE_CP;
615    }
616  }
617
618  if (rc == RTEMS_SUCCESSFUL) {
619    /*
620     * disable SPI
621     */
622    softc_ptr->reg_ptr->spmode &= ~MPC83XX_SPIMODE_EN;
623    /*
624     * set new mode and reenable SPI
625     */
626    softc_ptr->reg_ptr->spmode = spimode | MPC83XX_SPIMODE_EN;
627  }
628  return rc;
629}
630
631
632/*=========================================================================*\
633| Function:                                                                 |
634\*-------------------------------------------------------------------------*/
635int mpc83xx_spi_ioctl
636(
637/*-------------------------------------------------------------------------*\
638| Purpose:                                                                  |
639|   perform selected ioctl function for SPI                                 |
640+---------------------------------------------------------------------------+
641| Input Parameters:                                                         |
642\*-------------------------------------------------------------------------*/
643 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
644 int                 cmd,                /* ioctl command code             */
645 void               *arg                 /* additional argument array      */
646)
647/*-------------------------------------------------------------------------*\
648| Return Value:                                                             |
649|    rtems_status_code                                                      |
650\*=========================================================================*/
651{
652  int ret_val = -1;
653
654  switch(cmd) {
655  case RTEMS_LIBI2C_IOCTL_SET_TFRMODE:
656    ret_val =
657      -mpc83xx_spi_set_tfr_mode(bh,
658                                (const rtems_libi2c_tfr_mode_t *)arg);
659    break;
660  case RTEMS_LIBI2C_IOCTL_READ_WRITE:
661    ret_val =
662      mpc83xx_spi_read_write_bytes(bh,
663                                   ((rtems_libi2c_read_write_t *)arg)->rd_buf,
664                                   ((rtems_libi2c_read_write_t *)arg)->wr_buf,
665                                   ((rtems_libi2c_read_write_t *)arg)->byte_cnt);
666    break;
667  default:
668    ret_val = -RTEMS_NOT_DEFINED;
669    break;
670  }
671  return ret_val;
672}
673
674
675
Note: See TracBrowser for help on using the repository browser.