source: rtems/c/src/lib/libcpu/powerpc/mpc83xx/i2c/mpc83xx_i2cdrv.c @ 55a685b

4.104.114.95
Last change on this file since 55a685b was 55a685b, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 10/25/07 at 16:17:56

added SPI support to libi2c
added IRQ support to MPC83xx i2c driver
added mpc83xx spi driver

  • Property mode set to 100644
File size: 24.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.com/license/LICENSE.                           |
15|                                                                 |
16+-----------------------------------------------------------------+
17| this file contains the MPC83xx I2C driver                       |
18\*===============================================================*/
19#include <stdlib.h>
20#include <bsp.h>
21#include <bsp/irq.h>
22#include <mpc83xx/mpc83xx.h>
23#include <mpc83xx/mpc83xx_i2cdrv.h>
24#include <rtems/error.h>
25#include <rtems/bspIo.h>
26#include <errno.h>
27#include <rtems/libi2c.h>
28
29#define DEBUG
30
31/*=========================================================================*\
32| Function:                                                                 |
33\*-------------------------------------------------------------------------*/
34static int mpc83xx_i2c_find_clock_divider
35(
36/*-------------------------------------------------------------------------*\
37| Purpose:                                                                  |
38|   determine proper divider value                                          |
39+---------------------------------------------------------------------------+
40| Input Parameters:                                                         |
41\*-------------------------------------------------------------------------*/
42 uint8_t *result,                        /* result value                   */
43 int divider                             /* requested divider              */
44)
45/*-------------------------------------------------------------------------*\
46| Return Value:                                                             |
47|    o = ok or error code                                                   |
48\*=========================================================================*/
49{
50  int i;
51  int fdr_val;
52  struct {
53    int divider;
54    int fdr_val;
55  } dividers[] ={
56    {  256,0x20 }, {  288,0x21 }, {  320,0x22 }, {  352,0x23 },
57    {  384,0x00 }, {  416,0x01 }, {  448,0x25 }, {  480,0x02 },
58    {  512,0x26 }, {  576,0x03 }, {  640,0x04 }, {  704,0x05 },
59    {  768,0x29 }, {  832,0x06 }, {  896,0x2a }, { 1024,0x07 },
60    { 1152,0x08 }, { 1280,0x09 }, { 1536,0x0A }, { 1792,0x2E },
61    { 1920,0x0B }, { 2048,0x2F }, { 2304,0x0C }, { 2560,0x0D },
62    { 3072,0x0E }, { 3584,0x32 }, { 3840,0x0F }, { 4096,0x33 },
63    { 4608,0x10 }, { 5120,0x11 }, { 6144,0x12 }, { 7168,0x36 },
64    { 7680,0x13 }, { 8192,0x37 }, { 9216,0x14 }, {10240,0x15 },
65    {12288,0x16 }, {14336,0x3A }, {15360,0x17 }, {16384,0x3B },
66    {18432,0x18 }, {20480,0x19 }, {24576,0x1A }, {28672,0x3E },
67    {30720,0x1B }, {32768,0x3F }, {36864,0x1C }, {40960,0x1D },
68    {49152,0x1E }, {61440,0x1F }
69  };
70
71  for (i = 0, fdr_val = -1; i < sizeof(dividers)/sizeof(dividers[0]); i++) {
72    fdr_val = dividers[i].fdr_val;
73    if (dividers[i].divider >= divider)
74      {
75        break;
76      }
77  }
78  *result = fdr_val;
79  return 0;
80}
81
82/*=========================================================================*\
83| Function:                                                                 |
84\*-------------------------------------------------------------------------*/
85static int mpc83xx_i2c_wait
86(
87/*-------------------------------------------------------------------------*\
88| Purpose:                                                                  |
89|   wait for i2c to become idle                                             |
90+---------------------------------------------------------------------------+
91| Input Parameters:                                                         |
92\*-------------------------------------------------------------------------*/
93 mpc83xx_i2c_softc_t *softc_ptr,          /* handle              */
94 uint8_t              desired_status,     /* desired status word */
95 uint8_t              status_mask         /* status word mask    */
96)
97/*-------------------------------------------------------------------------*\
98| Return Value:                                                             |
99|    o = ok or error code                                                   |
100\*=========================================================================*/
101{
102  uint8_t act_status;
103  rtems_status_code rc;
104  uint32_t tout;
105
106#if defined(DEBUG)
107  printk("mpc83xx_i2c_wait called... ");
108#endif
109
110  if (softc_ptr->initialized) {
111    /*
112     * enable interrupt mask
113     */
114    softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MIEN;
115    rc = rtems_semaphore_obtain(softc_ptr->irq_sema_id,RTEMS_WAIT,100);
116    if (rc != RTEMS_SUCCESSFUL) {
117      return rc;
118    }
119  }
120  else {
121    tout = 0;
122    do {
123      if (tout++ > 1000000) {
124#if defined(DEBUG)
125        printk("... exit with RTEMS_TIMEOUT\r\n");
126#endif
127        return RTEMS_TIMEOUT;
128      }
129    } while (!(softc_ptr->reg_ptr->i2csr & MPC83XX_I2CSR_MIF));
130  }
131  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MIEN;
132
133  act_status = softc_ptr->reg_ptr->i2csr;
134  if ((act_status  & status_mask) != desired_status) {
135#if defined(DEBUG)
136    printk("... exit with RTEMS_IO_ERROR\r\n");
137#endif
138    return RTEMS_IO_ERROR;
139  }
140#if defined(DEBUG)
141        printk("... exit OK\r\n");
142#endif
143  return RTEMS_SUCCESSFUL;
144}
145
146/*=========================================================================*\
147| Function:                                                                 |
148\*-------------------------------------------------------------------------*/
149static void mpc83xx_i2c_irq_handler
150(
151/*-------------------------------------------------------------------------*\
152| Purpose:                                                                  |
153|   handle interrupts                                                       |
154+---------------------------------------------------------------------------+
155| Input Parameters:                                                         |
156\*-------------------------------------------------------------------------*/
157 rtems_irq_hdl_param handle     /* handle, is softc_ptr structure          */
158)
159/*-------------------------------------------------------------------------*\
160| Return Value:                                                             |
161|    <none>                                                                 |
162\*=========================================================================*/
163{
164  mpc83xx_i2c_softc_t *softc_ptr = (mpc83xx_i2c_softc_t *)handle;
165 
166  /*
167   * disable interrupt mask
168   */
169  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MIEN;
170  if (softc_ptr->initialized) {
171    rtems_semaphore_release(softc_ptr->irq_sema_id);
172  } 
173}
174
175/*=========================================================================*\
176| Function:                                                                 |
177\*-------------------------------------------------------------------------*/
178static void mpc83xx_i2c_irq_on_off
179(
180/*-------------------------------------------------------------------------*\
181| Purpose:                                                                  |
182|   enable/disable interrupts (void, handled at different position)         |
183+---------------------------------------------------------------------------+
184| Input Parameters:                                                         |
185\*-------------------------------------------------------------------------*/
186 const
187 rtems_irq_connect_data *irq_conn_data   /* irq connect data                */
188)
189/*-------------------------------------------------------------------------*\
190| Return Value:                                                             |
191|    <none>                                                                 |
192\*=========================================================================*/
193{
194}
195
196
197/*=========================================================================*\
198| Function:                                                                 |
199\*-------------------------------------------------------------------------*/
200static int mpc83xx_i2c_irq_isOn
201(
202/*-------------------------------------------------------------------------*\
203| Purpose:                                                                  |
204|   check state of interrupts, void, done differently                       |
205+---------------------------------------------------------------------------+
206| Input Parameters:                                                         |
207\*-------------------------------------------------------------------------*/
208 const
209 rtems_irq_connect_data *irq_conn_data  /* irq connect data                */
210)
211/*-------------------------------------------------------------------------*\
212| Return Value:                                                             |
213|    TRUE, if enabled                                                       |
214\*=========================================================================*/
215{
216  return (TRUE);
217}
218
219/*=========================================================================*\
220| Function:                                                                 |
221\*-------------------------------------------------------------------------*/
222static void mpc83xx_i2c_install_irq_handler
223(
224/*-------------------------------------------------------------------------*\
225| Purpose:                                                                  |
226|   (un-)install the interrupt handler                                      |
227+---------------------------------------------------------------------------+
228| Input Parameters:                                                         |
229\*-------------------------------------------------------------------------*/
230 mpc83xx_i2c_softc_t *softc_ptr,        /* ptr to control structure        */
231 int install                            /* TRUE: install, FALSE: remove    */
232)
233/*-------------------------------------------------------------------------*\
234| Return Value:                                                             |
235|    <none>                                                                 |
236\*=========================================================================*/
237{
238  rtems_status_code rc = RTEMS_SUCCESSFUL;
239
240  rtems_irq_connect_data irq_conn_data = {
241    softc_ptr->irq_number,
242    mpc83xx_i2c_irq_handler,           /* rtems_irq_hdl           */
243    (rtems_irq_hdl_param)softc_ptr,    /* (rtems_irq_hdl_param)   */
244    mpc83xx_i2c_irq_on_off,            /* (rtems_irq_enable)      */
245    mpc83xx_i2c_irq_on_off,            /* (rtems_irq_disable)     */
246    mpc83xx_i2c_irq_isOn               /* (rtems_irq_is_enabled)  */
247  };
248
249  /*
250   * (un-)install handler for I2C device
251   */
252  if (install) {
253    /*
254     * create semaphore for IRQ synchronization
255     */
256    rc = rtems_semaphore_create(rtems_build_name('i','2','c','s'),
257                                0,
258                                RTEMS_FIFO
259                                | RTEMS_SIMPLE_BINARY_SEMAPHORE,
260                                0,
261                                &softc_ptr->irq_sema_id);
262    if (rc != RTEMS_SUCCESSFUL) {
263      rtems_panic("I2C: cannot create semaphore");
264    }
265    if (!BSP_install_rtems_irq_handler (&irq_conn_data)) {
266      rtems_panic("I2C: cannot install IRQ handler");
267    }
268  }
269  else {
270    if (!BSP_remove_rtems_irq_handler (&irq_conn_data)) {
271      rtems_panic("I2C: cannot uninstall IRQ handler");
272    }
273    /*
274     * delete sync semaphore
275     */
276    if (softc_ptr->irq_sema_id != 0) {
277      rc = rtems_semaphore_delete(softc_ptr->irq_sema_id);
278      if (rc != RTEMS_SUCCESSFUL) {
279        rtems_panic("I2C: cannot delete semaphore");
280      }
281    }
282  }
283}
284
285/*=========================================================================*\
286| Function:                                                                 |
287\*-------------------------------------------------------------------------*/
288static rtems_status_code mpc83xx_i2c_init
289(
290/*-------------------------------------------------------------------------*\
291| Purpose:                                                                  |
292|   initialize the driver                                                   |
293+---------------------------------------------------------------------------+
294| Input Parameters:                                                         |
295\*-------------------------------------------------------------------------*/
296 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
297)
298/*-------------------------------------------------------------------------*\
299| Return Value:                                                             |
300|    o = ok or error code                                                   |
301\*=========================================================================*/
302{
303  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
304  uint8_t fdr_val;
305  int errval;
306#if defined(DEBUG)
307  printk("mpc83xx_i2c_init called... ");
308#endif
309  /*
310   * init HW registers
311   */
312  /*
313   * init frequency divider to 100kHz
314   */
315  errval = mpc83xx_i2c_find_clock_divider(&fdr_val,
316                                          BSP_CSB_CLK_FRQ/3/100000);
317  if (errval != 0) {
318    return errval;
319  }
320  softc_ptr->reg_ptr->i2cfdr = fdr_val;
321  /*
322   * init digital filter sampling rate
323   */
324  softc_ptr->reg_ptr->i2cdfsrr = 0x10 ; /* no special filtering needed */
325  /*
326   * set own slave address to broadcast (0x00)
327   */
328  softc_ptr->reg_ptr->i2cadr = 0x00 ;
329
330  /*
331   * set control register to module enable
332   */
333  softc_ptr->reg_ptr->i2ccr = MPC83XX_I2CCR_MEN;
334 
335  /*
336   * init interrupt stuff
337   */
338  mpc83xx_i2c_install_irq_handler(softc_ptr,TRUE);
339
340  /*
341   * mark, that we have initialized
342   */
343  softc_ptr->initialized = TRUE;
344#if defined(DEBUG)
345  printk("... exit OK\r\n");
346#endif
347  return RTEMS_SUCCESSFUL;
348}
349
350/*=========================================================================*\
351| Function:                                                                 |
352\*-------------------------------------------------------------------------*/
353static rtems_status_code mpc83xx_i2c_send_start
354(
355/*-------------------------------------------------------------------------*\
356| Purpose:                                                                  |
357|   send a start condition to bus                                           |
358+---------------------------------------------------------------------------+
359| Input Parameters:                                                         |
360\*-------------------------------------------------------------------------*/
361 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
362)
363/*-------------------------------------------------------------------------*\
364| Return Value:                                                             |
365|    o = ok or error code                                                   |
366\*=========================================================================*/
367{
368  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
369
370#if defined(DEBUG)
371  printk("mpc83xx_i2c_send_start called... ");
372#endif
373  if (0 != (softc_ptr->reg_ptr->i2ccr & MPC83XX_I2CCR_MSTA)) {
374    /*
375     * already started, so send a "repeated start"
376     */
377    softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_RSTA;
378  }
379  else {
380    softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MSTA;
381  }
382
383#if defined(DEBUG)
384  printk("... exit OK\r\n");
385#endif
386  return 0;
387}
388
389/*=========================================================================*\
390| Function:                                                                 |
391\*-------------------------------------------------------------------------*/
392static rtems_status_code mpc83xx_i2c_send_stop
393(
394/*-------------------------------------------------------------------------*\
395| Purpose:                                                                  |
396|   send a stop condition to bus                                            |
397+---------------------------------------------------------------------------+
398| Input Parameters:                                                         |
399\*-------------------------------------------------------------------------*/
400 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
401)
402/*-------------------------------------------------------------------------*\
403| Return Value:                                                             |
404|    o = ok or error code                                                   |
405\*=========================================================================*/
406{
407  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
408
409#if defined(DEBUG)
410  printk("mpc83xx_i2c_send_stop called... ");
411#endif
412  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MSTA;
413
414#if defined(DEBUG)
415  printk("... exit OK\r\n");
416#endif
417  return 0;
418}
419
420/*=========================================================================*\
421| Function:                                                                 |
422\*-------------------------------------------------------------------------*/
423static rtems_status_code mpc83xx_i2c_send_addr
424(
425/*-------------------------------------------------------------------------*\
426| Purpose:                                                                  |
427|   address a slave device on the bus                                       |
428+---------------------------------------------------------------------------+
429| Input Parameters:                                                         |
430\*-------------------------------------------------------------------------*/
431 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
432 uint32_t addr,                          /* address to send on bus         */
433 int rw                                  /* 0=write,1=read                 */
434)
435/*-------------------------------------------------------------------------*\
436| Return Value:                                                             |
437|    o = ok or error code                                                   |
438\*=========================================================================*/
439{
440  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
441  rtems_boolean long_addr = FALSE;
442  uint8_t addr_byte;
443  rtems_status_code rc;
444
445#if defined(DEBUG)
446  printk("mpc83xx_i2c_send_addr called... ");
447#endif
448  softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MTX;
449  /*
450   * determine, whether short or long address is needed, determine rd/wr
451   */
452  if (addr > 0x7f) {
453    long_addr = TRUE;
454    addr_byte = (0xf0
455                 | ((addr >> 7) & 0x06)
456                 | ((rw) ? 1 : 0));
457    /*
458     * send first byte
459     */
460    softc_ptr->reg_ptr->i2cdr = addr_byte;
461    /*
462     * wait for successful transfer
463     */
464    rc = mpc83xx_i2c_wait(softc_ptr,
465                          MPC83XX_I2CSR_MCF,
466                          MPC83XX_I2CSR_MCF);
467    if (rc != RTEMS_SUCCESSFUL) {
468#if defined(DEBUG)
469      printk("... exit rc=%d\r\n",rc);
470#endif
471      return rc;
472    }
473  }
474  addr_byte = (0xf0
475               | ((addr >> 7) & 0x06)
476               | ((rw) ? 1 : 0));
477  /*
478   * send (final) byte
479   */
480  addr_byte = ((addr << 1)
481               | ((rw) ? 1 : 0));
482
483  softc_ptr->reg_ptr->i2cdr = addr_byte;
484  /*
485   * wait for successful transfer
486   */
487  rc = mpc83xx_i2c_wait(softc_ptr,
488                        MPC83XX_I2CSR_MCF,
489                        MPC83XX_I2CSR_MCF);
490
491#if defined(DEBUG)
492  printk("... exit rc=%d\r\n",rc);
493#endif
494  return rc;
495}
496
497/*=========================================================================*\
498| Function:                                                                 |
499\*-------------------------------------------------------------------------*/
500static int mpc83xx_i2c_read_bytes
501(
502/*-------------------------------------------------------------------------*\
503| Purpose:                                                                  |
504|   receive some bytes from I2C device                                      |
505+---------------------------------------------------------------------------+
506| Input Parameters:                                                         |
507\*-------------------------------------------------------------------------*/
508 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
509 unsigned char *buf,                     /* buffer to store bytes          */
510 int len                                 /* number of bytes to receive     */
511)
512/*-------------------------------------------------------------------------*\
513| Return Value:                                                             |
514|    number of bytes received or (negative) error code                      |
515\*=========================================================================*/
516{
517  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
518  rtems_status_code rc;
519  unsigned char *p = buf;
520  unsigned char dummy;
521
522#if defined(DEBUG)
523  printk("mpc83xx_i2c_read_bytes called... ");
524#endif
525  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MTX;
526  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_TXAK;
527  /*
528   * we need a dummy transfer here to start the first read
529   */
530  dummy = softc_ptr->reg_ptr->i2cdr;
531
532  while (len-- > 0) {
533    if (len == 0) {
534      /*
535       * last byte is not acknowledged
536       */
537      softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_TXAK;
538    }
539    /*
540     * wait 'til end of transfer
541     */
542    rc = mpc83xx_i2c_wait(softc_ptr,
543                          MPC83XX_I2CSR_MCF,
544                          MPC83XX_I2CSR_MCF);
545    if (rc != RTEMS_SUCCESSFUL) {
546#if defined(DEBUG)
547      printk("... exit rc=%d\r\n",-rc);
548#endif
549      return -rc;
550    }
551    *p++ = softc_ptr->reg_ptr->i2cdr;
552     
553  }
554#if defined(DEBUG)
555  printk("... exit OK, rc=%d\r\n",p-buf);
556#endif
557  return p - buf;
558}
559
560/*=========================================================================*\
561| Function:                                                                 |
562\*-------------------------------------------------------------------------*/
563static int mpc83xx_i2c_write_bytes
564(
565/*-------------------------------------------------------------------------*\
566| Purpose:                                                                  |
567|   send some bytes to I2C device                                           |
568+---------------------------------------------------------------------------+
569| Input Parameters:                                                         |
570\*-------------------------------------------------------------------------*/
571 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
572 unsigned char *buf,                     /* buffer to send                 */
573 int len                                 /* number of bytes to send        */
574
575)
576/*-------------------------------------------------------------------------*\
577| Return Value:                                                             |
578|    number of bytes sent or (negative) error code                          |
579\*=========================================================================*/
580{
581  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
582  rtems_status_code rc;
583  unsigned char *p = buf;
584
585#if defined(DEBUG)
586  printk("mpc83xx_i2c_write_bytes called... ");
587#endif
588  softc_ptr->reg_ptr->i2ccr =
589    (softc_ptr->reg_ptr->i2ccr & ~MPC83XX_I2CCR_TXAK) | MPC83XX_I2CCR_MTX;
590  while (len-- > 0) {
591    softc_ptr->reg_ptr->i2cdr = *p++;
592    /*
593     * wait 'til end of transfer
594     */
595    rc = mpc83xx_i2c_wait(softc_ptr,
596                          MPC83XX_I2CSR_MCF,
597                          MPC83XX_I2CSR_MCF);
598    if (rc != RTEMS_SUCCESSFUL) {
599#if defined(DEBUG)
600      printk("... exit rc=%d\r\n",-rc);
601#endif
602      return -rc;
603    }
604  }
605#if defined(DEBUG)
606  printk("... exit OK, rc=%d\r\n",p-buf);
607#endif
608  return p - buf;
609}
610
611rtems_libi2c_bus_ops_t mpc83xx_i2c_ops = {
612  init:        mpc83xx_i2c_init,
613  send_start:  mpc83xx_i2c_send_start,
614  send_stop:   mpc83xx_i2c_send_stop,
615  send_addr:   mpc83xx_i2c_send_addr,
616  read_bytes:  mpc83xx_i2c_read_bytes,
617  write_bytes: mpc83xx_i2c_write_bytes,
618};
619
Note: See TracBrowser for help on using the repository browser.