source: rtems/c/src/lib/libcpu/powerpc/mpc83xx/i2c/mpc83xx_i2cdrv.c @ 267c7c5

4.104.114.84.95
Last change on this file since 267c7c5 was 267c7c5, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 08/10/07 at 08:17:10

added i2c support

  • Property mode set to 100644
File size: 17.8 KB
RevLine 
[267c7c5]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 * XXX: for the beginning, this driver works polled
33 */
34
35/*=========================================================================*\
36| Function:                                                                 |
37\*-------------------------------------------------------------------------*/
38static int mpc83xx_i2c_find_clock_divider
39(
40/*-------------------------------------------------------------------------*\
41| Purpose:                                                                  |
42|   determine proper divider value                                          |
43+---------------------------------------------------------------------------+
44| Input Parameters:                                                         |
45\*-------------------------------------------------------------------------*/
46 uint8_t *result,                        /* result value                   */
47 int divider                             /* requested divider              */
48)
49/*-------------------------------------------------------------------------*\
50| Return Value:                                                             |
51|    o = ok or error code                                                   |
52\*=========================================================================*/
53{
54  int i;
55  int fdr_val;
56  struct {
57    int divider;
58    int fdr_val;
59  } dividers[] ={
60    {  256,0x20 }, {  288,0x21 }, {  320,0x22 }, {  352,0x23 },
61    {  384,0x00 }, {  416,0x01 }, {  448,0x25 }, {  480,0x02 },
62    {  512,0x26 }, {  576,0x03 }, {  640,0x04 }, {  704,0x05 },
63    {  768,0x29 }, {  832,0x06 }, {  896,0x2a }, { 1024,0x07 },
64    { 1152,0x08 }, { 1280,0x09 }, { 1536,0x0A }, { 1792,0x2E },
65    { 1920,0x0B }, { 2048,0x2F }, { 2304,0x0C }, { 2560,0x0D },
66    { 3072,0x0E }, { 3584,0x32 }, { 3840,0x0F }, { 4096,0x33 },
67    { 4608,0x10 }, { 5120,0x11 }, { 6144,0x12 }, { 7168,0x36 },
68    { 7680,0x13 }, { 8192,0x37 }, { 9216,0x14 }, {10240,0x15 },
69    {12288,0x16 }, {14336,0x3A }, {15360,0x17 }, {16384,0x3B },
70    {18432,0x18 }, {20480,0x19 }, {24576,0x1A }, {28672,0x3E },
71    {30720,0x1B }, {32768,0x3F }, {36864,0x1C }, {40960,0x1D },
72    {49152,0x1E }, {61440,0x1F }
73  };
74
75  for (i = 0, fdr_val = -1; i < sizeof(dividers)/sizeof(dividers[0]); i++) {
76    fdr_val = dividers[i].fdr_val;
77    if (dividers[i].divider >= divider)
78      {
79        break;
80      }
81  }
82  *result = fdr_val;
83  return 0;
84}
85
86/*=========================================================================*\
87| Function:                                                                 |
88\*-------------------------------------------------------------------------*/
89static int mpc83xx_i2c_wait
90(
91/*-------------------------------------------------------------------------*\
92| Purpose:                                                                  |
93|   wait for i2c to become idle                                             |
94+---------------------------------------------------------------------------+
95| Input Parameters:                                                         |
96\*-------------------------------------------------------------------------*/
97 mpc83xx_i2c_softc_t *softc_ptr,          /* handle              */
98 uint8_t              desired_status,     /* desired status word */
99 uint8_t              status_mask         /* status word mask    */
100)
101/*-------------------------------------------------------------------------*\
102| Return Value:                                                             |
103|    o = ok or error code                                                   |
104\*=========================================================================*/
105{
106  uint8_t act_status;
107  rtems_status_code rc;
108  uint32_t tout;
109
110#if defined(DEBUG)
111  printk("mpc83xx_i2c_wait called... ");
112#endif
113  softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MIEN;
114
115  if (softc_ptr->initialized) {
116    rc = rtems_semaphore_obtain(softc_ptr->irq_sema_id,RTEMS_WAIT,100);
117    if (rc != RTEMS_SUCCESSFUL) {
118      return rc;
119    }
120  }
121  else {
122    tout = 0;
123    do {
124      if (tout++ > 1000000) {
125#if defined(DEBUG)
126        printk("... exit with RTEMS_TIMEOUT\r\n");
127#endif
128        return RTEMS_TIMEOUT;
129      }
130    } while (!(softc_ptr->reg_ptr->i2csr & MPC83XX_I2CSR_MIF));
131  }
132  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MIEN;
133
134  act_status = softc_ptr->reg_ptr->i2csr & status_mask;
135  if (act_status != desired_status) {
136#if defined(DEBUG)
137    printk("... exit with RTEMS_IO_ERROR\r\n");
138#endif
139    return RTEMS_IO_ERROR;
140  }
141#if defined(DEBUG)
142        printk("... exit OK\r\n");
143#endif
144  return RTEMS_SUCCESSFUL;
145}
146
147/*=========================================================================*\
148| Function:                                                                 |
149\*-------------------------------------------------------------------------*/
150static rtems_status_code mpc83xx_i2c_init
151(
152/*-------------------------------------------------------------------------*\
153| Purpose:                                                                  |
154|   initialize the driver                                                   |
155+---------------------------------------------------------------------------+
156| Input Parameters:                                                         |
157\*-------------------------------------------------------------------------*/
158 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
159)
160/*-------------------------------------------------------------------------*\
161| Return Value:                                                             |
162|    o = ok or error code                                                   |
163\*=========================================================================*/
164{
165  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
166  uint8_t fdr_val;
167  int errval;
168#if defined(DEBUG)
169  printk("mpc83xx_i2c_init called... ");
170#endif
171  /*
172   * init HW registers
173   */
174  /*
175   * init frequency divider to 100kHz
176   */
177  errval = mpc83xx_i2c_find_clock_divider(&fdr_val,
178                                          BSP_CSB_CLK_FRQ/3/100000);
179  if (errval != 0) {
180    return errval;
181  }
182  softc_ptr->reg_ptr->i2cfdr = fdr_val;
183  /*
184   * init digital filter sampling rate
185   */
186  softc_ptr->reg_ptr->i2cdfsrr = 0x10 ; /* no special filtering needed */
187  /*
188   * set own slave address to broadcasr (0x00)
189   */
190  softc_ptr->reg_ptr->i2cadr = 0x00 ;
191
192  /*
193   * set control register to module enable
194   */
195  softc_ptr->reg_ptr->i2ccr = MPC83XX_I2CCR_MEN;
196 
197  /*
198   * FIXME: init interrupt stuff
199   */ 
200  /*
201   * FIXME: init other stuff
202   */
203#if defined(DEBUG)
204  printk("... exit OK\r\n");
205#endif
206  return RTEMS_SUCCESSFUL;
207}
208
209/*=========================================================================*\
210| Function:                                                                 |
211\*-------------------------------------------------------------------------*/
212static rtems_status_code mpc83xx_i2c_send_start
213(
214/*-------------------------------------------------------------------------*\
215| Purpose:                                                                  |
216|   send a start condition to bus                                           |
217+---------------------------------------------------------------------------+
218| Input Parameters:                                                         |
219\*-------------------------------------------------------------------------*/
220 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
221)
222/*-------------------------------------------------------------------------*\
223| Return Value:                                                             |
224|    o = ok or error code                                                   |
225\*=========================================================================*/
226{
227  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
228
229#if defined(DEBUG)
230  printk("mpc83xx_i2c_send_start called... ");
231#endif
232  if (0 != (softc_ptr->reg_ptr->i2ccr & MPC83XX_I2CCR_MSTA)) {
233    /*
234     * already started, so send a "repeated start"
235     */
236    softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_RSTA;
237  }
238  else {
239    softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MSTA;
240  }
241
242#if defined(DEBUG)
243  printk("... exit OK\r\n");
244#endif
245  return 0;
246}
247
248/*=========================================================================*\
249| Function:                                                                 |
250\*-------------------------------------------------------------------------*/
251static rtems_status_code mpc83xx_i2c_send_stop
252(
253/*-------------------------------------------------------------------------*\
254| Purpose:                                                                  |
255|   send a stop condition to bus                                            |
256+---------------------------------------------------------------------------+
257| Input Parameters:                                                         |
258\*-------------------------------------------------------------------------*/
259 rtems_libi2c_bus_t *bh                  /* bus specifier structure        */
260)
261/*-------------------------------------------------------------------------*\
262| Return Value:                                                             |
263|    o = ok or error code                                                   |
264\*=========================================================================*/
265{
266  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
267
268#if defined(DEBUG)
269  printk("mpc83xx_i2c_send_stop called... ");
270#endif
271  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MSTA;
272
273#if defined(DEBUG)
274  printk("... exit OK\r\n");
275#endif
276  return 0;
277}
278
279/*=========================================================================*\
280| Function:                                                                 |
281\*-------------------------------------------------------------------------*/
282static rtems_status_code mpc83xx_i2c_send_addr
283(
284/*-------------------------------------------------------------------------*\
285| Purpose:                                                                  |
286|   address a slave device on the bus                                       |
287+---------------------------------------------------------------------------+
288| Input Parameters:                                                         |
289\*-------------------------------------------------------------------------*/
290 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
291 uint32_t addr,                          /* address to send on bus         */
292 int rw                                  /* 0=write,1=read                 */
293)
294/*-------------------------------------------------------------------------*\
295| Return Value:                                                             |
296|    o = ok or error code                                                   |
297\*=========================================================================*/
298{
299  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
300  rtems_boolean long_addr = FALSE;
301  uint8_t addr_byte;
302  rtems_status_code rc;
303
304#if defined(DEBUG)
305  printk("mpc83xx_i2c_send_addr called... ");
306#endif
307  softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_MTX;
308  /*
309   * determine, whether short or long address is needed, determine rd/wr
310   */
311  if (addr > 0x7f) {
312    long_addr = TRUE;
313    addr_byte = (0xf0
314                 | ((addr >> 7) & 0x06)
315                 | ((rw) ? 1 : 0));
316    /*
317     * send first byte
318     */
319    softc_ptr->reg_ptr->i2cdr = addr_byte;
320    /*
321     * wait for successful transfer
322     */
323    rc = mpc83xx_i2c_wait(softc_ptr,
324                          MPC83XX_I2CSR_MCF,
325                          MPC83XX_I2CSR_MCF);
326    if (rc != RTEMS_SUCCESSFUL) {
327#if defined(DEBUG)
328      printk("... exit rc=%d\r\n",rc);
329#endif
330      return rc;
331    }
332  }
333  addr_byte = (0xf0
334               | ((addr >> 7) & 0x06)
335               | ((rw) ? 1 : 0));
336  /*
337   * send (final) byte
338   */
339  addr_byte = ((addr << 1)
340               | ((rw) ? 1 : 0));
341
342  softc_ptr->reg_ptr->i2cdr = addr_byte;
343  /*
344   * wait for successful transfer
345   */
346  rc = mpc83xx_i2c_wait(softc_ptr,
347                        MPC83XX_I2CSR_MCF,
348                        MPC83XX_I2CSR_MCF);
349
350#if defined(DEBUG)
351  printk("... exit rc=%d\r\n",rc);
352#endif
353  return rc;
354}
355
356/*=========================================================================*\
357| Function:                                                                 |
358\*-------------------------------------------------------------------------*/
359static int mpc83xx_i2c_read_bytes
360(
361/*-------------------------------------------------------------------------*\
362| Purpose:                                                                  |
363|   receive some bytes from I2C device                                      |
364+---------------------------------------------------------------------------+
365| Input Parameters:                                                         |
366\*-------------------------------------------------------------------------*/
367 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
368 unsigned char *buf,                     /* buffer to store bytes          */
369 int len                                 /* number of bytes to receive     */
370)
371/*-------------------------------------------------------------------------*\
372| Return Value:                                                             |
373|    number of bytes received or (negative) error code                      |
374\*=========================================================================*/
375{
376  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
377  rtems_status_code rc;
378  unsigned char *p = buf;
379  unsigned char dummy;
380
381#if defined(DEBUG)
382  printk("mpc83xx_i2c_read_bytes called... ");
383#endif
384  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_MTX;
385  softc_ptr->reg_ptr->i2ccr &= ~MPC83XX_I2CCR_TXAK;
386  /*
387   * we need a dummy transfer here to start the first read
388   */
389  dummy = softc_ptr->reg_ptr->i2cdr;
390
391  while (len-- > 0) {
392    if (len == 0) {
393      /*
394       * last byte is not acknowledged
395       */
396      softc_ptr->reg_ptr->i2ccr |= MPC83XX_I2CCR_TXAK;
397    }
398    /*
399     * wait 'til end of transfer
400     */
401    rc = mpc83xx_i2c_wait(softc_ptr,
402                          MPC83XX_I2CSR_MCF,
403                          MPC83XX_I2CSR_MCF);
404    if (rc != RTEMS_SUCCESSFUL) {
405#if defined(DEBUG)
406      printk("... exit rc=%d\r\n",-rc);
407#endif
408      return -rc;
409    }
410    *p++ = softc_ptr->reg_ptr->i2cdr;
411     
412  }
413#if defined(DEBUG)
414  printk("... exit OK, rc=%d\r\n",p-buf);
415#endif
416  return p - buf;
417}
418
419/*=========================================================================*\
420| Function:                                                                 |
421\*-------------------------------------------------------------------------*/
422static int mpc83xx_i2c_write_bytes
423(
424/*-------------------------------------------------------------------------*\
425| Purpose:                                                                  |
426|   send some bytes to I2C device                                           |
427+---------------------------------------------------------------------------+
428| Input Parameters:                                                         |
429\*-------------------------------------------------------------------------*/
430 rtems_libi2c_bus_t *bh,                 /* bus specifier structure        */
431 unsigned char *buf,                     /* buffer to send                 */
432 int len                                 /* number of bytes to send        */
433
434)
435/*-------------------------------------------------------------------------*\
436| Return Value:                                                             |
437|    number of bytes sent or (negative) error code                          |
438\*=========================================================================*/
439{
440  mpc83xx_i2c_softc_t *softc_ptr = &(((mpc83xx_i2c_desc_t *)(bh))->softc);
441  rtems_status_code rc;
442  unsigned char *p = buf;
443
444#if defined(DEBUG)
445  printk("mpc83xx_i2c_write_bytes called... ");
446#endif
447  softc_ptr->reg_ptr->i2ccr =
448    (softc_ptr->reg_ptr->i2ccr & ~MPC83XX_I2CCR_TXAK) | MPC83XX_I2CCR_MTX;
449  while (len-- > 0) {
450    softc_ptr->reg_ptr->i2cdr = *p++;
451    /*
452     * wait 'til end of transfer
453     */
454    rc = mpc83xx_i2c_wait(softc_ptr,
455                          MPC83XX_I2CSR_MCF,
456                          MPC83XX_I2CSR_MCF);
457    if (rc != RTEMS_SUCCESSFUL) {
458#if defined(DEBUG)
459      printk("... exit rc=%d\r\n",-rc);
460#endif
461      return -rc;
462    }
463  }
464#if defined(DEBUG)
465  printk("... exit OK, rc=%d\r\n",p-buf);
466#endif
467  return p - buf;
468}
469
470rtems_libi2c_bus_ops_t mpc83xx_i2c_ops = {
471  init:        mpc83xx_i2c_init,
472  send_start:  mpc83xx_i2c_send_start,
473  send_stop:   mpc83xx_i2c_send_stop,
474  send_addr:   mpc83xx_i2c_send_addr,
475  read_bytes:  mpc83xx_i2c_read_bytes,
476  write_bytes: mpc83xx_i2c_write_bytes,
477};
478
Note: See TracBrowser for help on using the repository browser.