source: rtems/c/src/lib/libcpu/powerpc/mpc83xx/i2c/mpc83xx_i2cdrv.c @ 9a73f421

4.104.114.95
Last change on this file since 9a73f421 was 9a73f421, checked in by Ralf Corsepius <ralf.corsepius@…>, on Sep 2, 2008 at 12:55:44 PM

Eliminate rtems_boolean.

  • Property mode set to 100644
File size: 24.4 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#if defined(DEBUG)
429  printk("... exit OK\r\n");
430#endif
431  return 0;
432}
433
434/*=========================================================================*\
435| Function:                                                                 |
436\*-------------------------------------------------------------------------*/
437static rtems_status_code mpc83xx_i2c_send_addr
438(
439/*-------------------------------------------------------------------------*\
440| Purpose:                                                                  |
441|   address a slave device on the bus                                       |
442+---------------------------------------------------------------------------+
443| Input Parameters:                                                         |
444\*-------------------------------------------------------------------------*/
445 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
446 uint32_t addr,                          /* address to send on bus         */
447 int rw                                  /* 0=write,1=read                 */
448)
449/*-------------------------------------------------------------------------*\
450| Return Value:                                                             |
451|    o = ok or error code                                                   |
452\*=========================================================================*/
453{
454  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
455  bool long_addr = false;
456  uint8_t addr_byte;
457  rtems_status_code rc;
458
459#if defined(DEBUG)
460  printk("mpc83xx_i2c_send_addr called... ");
461#endif
462  softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MTX;
463  /*
464   * determine, whether short or long address is needed, determine rd/wr
465   */
466  if (addr > 0x7f) {
467    long_addr = true;
468    addr_byte = (0xf0 
469                 | ((addr >> 7) & 0x06)
470                 | ((rw) ? 1 : 0));
471    /*
472     * send first byte
473     */
474    softc_ptr->reg_ptr->i2cdr = addr_byte;
475    /*
476     * wait for successful transfer
477     */
478    rc = mpc83xx_i2c_wait(softc_ptr,
479                          MPC83XX_I2CSR_MCF,
480                          MPC83XX_I2CSR_MCF);
481    if (rc != RTEMS_SUCCESSFUL) {
482#if defined(DEBUG)
483      printk("... exit rc=%d\r\n",rc);
484#endif
485      return rc;
486    }
487  }
488  /*
489   * send (final) byte
490   */
491  addr_byte = ((addr << 1) 
492               | ((rw) ? 1 : 0));
493
494  softc_ptr->reg_ptr->i2cdr = addr_byte;
495  /*
496   * wait for successful transfer
497   */
498  rc = mpc83xx_i2c_wait(softc_ptr,
499                        MPC83XX_I2CSR_MCF,
500                        MPC83XX_I2CSR_MCF);
501
502#if defined(DEBUG)
503  printk("... exit rc=%d\r\n",rc);
504#endif
505  return rc;
506}
507
508/*=========================================================================*\
509| Function:                                                                 |
510\*-------------------------------------------------------------------------*/
511static int mpc83xx_i2c_read_bytes
512(
513/*-------------------------------------------------------------------------*\
514| Purpose:                                                                  |
515|   receive some bytes from I2C device                                      |
516+---------------------------------------------------------------------------+
517| Input Parameters:                                                         |
518\*-------------------------------------------------------------------------*/
519 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
520 unsigned char *buf,                     /* buffer to store bytes          */
521 int len                                 /* number of bytes to receive     */
522)
523/*-------------------------------------------------------------------------*\
524| Return Value:                                                             |
525|    number of bytes received or (negative) error code                      |
526\*=========================================================================*/
527{
528  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
529  rtems_status_code rc;
530  unsigned char *p = buf;
531  unsigned char dummy;
532
533#if defined(DEBUG)
534  printk("mpc83xx_i2c_read_bytes called... ");
535#endif
536  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MTX;
537  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_TXAK;
538  /*
539   * FIXME: do we need to deactivate TXAK from the start,
540   * when only one byte is to be received?
541   */
542  /*
543   * we need a dummy transfer here to start the first read
544   */
545  dummy = softc_ptr->reg_ptr->i2cdr;
546
547  while (len-- > 0) {
548    if (len == 0) {
549      /*
550       * last byte is not acknowledged
551       */
552      softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_TXAK;
553    }
554    /*
555     * wait 'til end of transfer
556     */
557    rc = mpc83xx_i2c_wait(softc_ptr,
558                          MPC83XX_I2CSR_MCF,
559                          MPC83XX_I2CSR_MCF);
560    if (rc != RTEMS_SUCCESSFUL) {
561#if defined(DEBUG)
562      printk("... exit rc=%d\r\n",-rc);
563#endif
564      return -rc;
565    }
566    *p++ = softc_ptr->reg_ptr->i2cdr;
567     
568  }
569
570 /*
571  * wait 'til end of last transfer
572  */
573  rc = mpc83xx_i2c_wait(softc_ptr, MPC83XX_I2CSR_MCF, MPC83XX_I2CSR_MCF);
574
575#if defined(DEBUG)
576  printk("... exit OK, rc=%d\r\n",p-buf);
577#endif
578  return p - buf;
579}
580
581/*=========================================================================*\
582| Function:                                                                 |
583\*-------------------------------------------------------------------------*/
584static int mpc83xx_i2c_write_bytes
585(
586/*-------------------------------------------------------------------------*\
587| Purpose:                                                                  |
588|   send some bytes to I2C device                                           |
589+---------------------------------------------------------------------------+
590| Input Parameters:                                                         |
591\*-------------------------------------------------------------------------*/
592 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
593 unsigned char *buf,                     /* buffer to send                 */
594 int len                                 /* number of bytes to send        */
595
596)
597/*-------------------------------------------------------------------------*\
598| Return Value:                                                             |
599|    number of bytes sent or (negative) error code                          |
600\*=========================================================================*/
601{
602  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
603  rtems_status_code rc;
604  unsigned char *p = buf;
605
606#if defined(DEBUG)
607  printk("mpc83xx_i2c_write_bytes called... ");
608#endif
609  softc_ptr->reg_ptr->i2ccr = 
610    (softc_ptr->reg_ptr->i2ccr & ~MPC83XX_I2CCR_TXAK) | MPC83XX_I2CCR_MTX;
611  while (len-- > 0) {
612    softc_ptr->reg_ptr->i2cdr = *p++;
613    /*
614     * wait 'til end of transfer
615     */
616    rc = mpc83xx_i2c_wait(softc_ptr,
617                          MPC83XX_I2CSR_MCF,
618                          MPC83XX_I2CSR_MCF);
619    if (rc != RTEMS_SUCCESSFUL) {
620#if defined(DEBUG)
621      printk("... exit rc=%d\r\n",-rc);
622#endif
623      return -rc;
624    }
625  }
626#if defined(DEBUG)
627  printk("... exit OK, rc=%d\r\n",p-buf);
628#endif
629  return p - buf;
630}
631
632rtems_libi2c_bus_ops_t mpc83xx_i2c_ops = {
633  .init = mpc83xx_i2c_init,
634  .send_start = mpc83xx_i2c_send_start,
635  .send_stop = mpc83xx_i2c_send_stop,
636  .send_addr = mpc83xx_i2c_send_addr,
637  .read_bytes = mpc83xx_i2c_read_bytes,
638  .write_bytes = mpc83xx_i2c_write_bytes,
639};
640
Note: See TracBrowser for help on using the repository browser.