source: rtems/c/src/lib/libcpu/powerpc/mpc83xx/i2c/mpc83xx_i2cdrv.c @ 39dfbe16

4.104.114.95
Last change on this file since 39dfbe16 was 39dfbe16, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on Sep 9, 2008 at 1:18:58 PM

minor additions
i2c-driver: wait, 'til stop executed

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