source: rtems/c/src/lib/libbsp/m68k/gen68360/spi/mc68360_spidrv.c @ 39d08d55

4.104.114.95
Last change on this file since 39d08d55 was 39d08d55, checked in by Ralf Corsepius <ralf.corsepius@…>, on Sep 6, 2008 at 5:36:55 PM

Convert to "bool".

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