source: rtems/cpukit/libi2c/libi2c.c @ 381a2f18

4.104.114.95
Last change on this file since 381a2f18 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: 17.3 KB
Line 
1/* $Id$ */
2
3/* libi2c Implementation */
4
5/*
6 * Authorship
7 * ----------
8 * This software was created by
9 *     Till Straumann <strauman@slac.stanford.edu>, 2005,
10 *         Stanford Linear Accelerator Center, Stanford University.
11 *
12 * Acknowledgement of sponsorship
13 * ------------------------------
14 * This software was produced by
15 *     the Stanford Linear Accelerator Center, Stanford University,
16 *         under Contract DE-AC03-76SFO0515 with the Department of Energy.
17 *
18 * Government disclaimer of liability
19 * ----------------------------------
20 * Neither the United States nor the United States Department of Energy,
21 * nor any of their employees, makes any warranty, express or implied, or
22 * assumes any legal liability or responsibility for the accuracy,
23 * completeness, or usefulness of any data, apparatus, product, or process
24 * disclosed, or represents that its use would not infringe privately owned
25 * rights.
26 *
27 * Stanford disclaimer of liability
28 * --------------------------------
29 * Stanford University makes no representations or warranties, express or
30 * implied, nor assumes any liability for the use of this software.
31 *
32 * Stanford disclaimer of copyright
33 * --------------------------------
34 * Stanford University, owner of the copyright, hereby disclaims its
35 * copyright and all other rights in this software.  Hence, anyone may
36 * freely use it for any purpose without restriction. 
37 *
38 * Maintenance of notices
39 * ----------------------
40 * In the interest of clarity regarding the origin and status of this
41 * SLAC software, this and all the preceding Stanford University notices
42 * are to remain affixed to any copy or derivative of this software made
43 * or distributed by the recipient and are to be affixed to any copy of
44 * software made or distributed by the recipient that contains a copy or
45 * derivative of this software.
46 *
47 * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03
48 */
49/*
50 * adaptations to also handle SPI devices
51 * by Thomas Doerfler, embedded brains GmbH, Puchheim, Germany
52 */
53#if HAVE_CONFIG_H
54#include "config.h"
55#endif
56
57#include <string.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <errno.h>
61#include <assert.h>
62#include <stdarg.h>
63
64#include <rtems.h>
65#include <rtems/error.h>
66#include <rtems/libio.h>
67
68#include <rtems/libi2c.h>
69
70#define DRVNM "libi2c:"
71
72#define MAX_NO_BUSSES   8       /* Also limited by the macro building minor numbers */
73#define MAX_NO_DRIVERS  16      /* Number of high level drivers we support          */
74
75#define MINOR2ADDR(minor)       ((minor)&((1<<10)-1))
76#define MINOR2BUS(minor)        (((minor)>>10)&7)
77#define MINOR2DRV(minor)        ((minor)>>13)
78
79/* Check the 'minor' argument, i.e., verify that
80 * we have a driver connected
81 */
82#define DECL_CHECKED_BH(b, bh, m, s)\
83        unsigned b = MINOR2BUS(m);              \
84        rtems_libi2c_bus_t      *bh;            \
85        if ( b >= MAX_NO_BUSSES || 0 == (bh=busses[b].bush) ) { \
86                return s RTEMS_INVALID_NUMBER;  \
87        }
88
89#define DECL_CHECKED_DRV(d, b, m)       \
90        unsigned d = MINOR2DRV(m);              \
91        unsigned b = MINOR2BUS(m);              \
92        if (   b >= MAX_NO_BUSSES  || 0 == busses[b].bush       \
93                || d >  MAX_NO_DRIVERS || (d && 0 == drvs[d-1].drv )) {\
94                return RTEMS_INVALID_NUMBER;    \
95        }
96
97#define DISPATCH(rval, entry, dflt)     \
98        do {                            \
99                rtems_driver_address_table *ops = drvs[--drv].drv->ops; \
100                rval = ops->entry ? ops->entry(major,minor,arg) : dflt; \
101        } while (0)
102
103
104rtems_device_major_number rtems_libi2c_major;
105
106static struct i2cbus
107{
108  rtems_libi2c_bus_t *bush;
109  volatile rtems_id mutex;      /* lock this across start -> stop */
110  volatile short waiting;
111  volatile char started;
112  char *name;
113} busses[MAX_NO_BUSSES] = { { 0 } };
114
115static struct
116{
117  rtems_libi2c_drv_t *drv;
118} drvs[MAX_NO_DRIVERS] = { { 0} };
119
120static rtems_id libmutex = 0;
121
122#define LOCK(m)         assert(!rtems_semaphore_obtain((m), RTEMS_WAIT, RTEMS_NO_TIMEOUT))
123#define UNLOCK(m)       rtems_semaphore_release((m))
124
125#define LIBLOCK()       LOCK(libmutex)
126#define LIBUNLOCK()     UNLOCK(libmutex)
127
128#define MUTEX_ATTS      \
129        (  RTEMS_PRIORITY                       \
130         | RTEMS_BINARY_SEMAPHORE       \
131         |RTEMS_INHERIT_PRIORITY        \
132         |RTEMS_NO_PRIORITY_CEILING     \
133         |RTEMS_LOCAL )
134
135static rtems_id
136mutexCreate (rtems_name nm)
137{
138  rtems_status_code sc;
139  rtems_id rval;
140  if (RTEMS_SUCCESSFUL !=
141      (sc = rtems_semaphore_create (nm, 1, MUTEX_ATTS, 0, &rval))) {
142    rtems_error (sc, DRVNM " unable to create mutex\n");
143    return 0;
144  }
145  return rval;
146}
147
148/* Lock a bus avoiding to have a mutex, which is mostly
149 * unused, hanging around all the time. We just create
150 * and delete it on the fly...
151 *
152 * ASSUMES: argument checked by caller
153 */
154
155static void
156lock_bus (int busno)
157{
158  struct i2cbus *bus = &busses[busno];
159  LIBLOCK ();
160  if (!bus->waiting) {
161    /* nobody is holding the bus mutex - it's not there. Create it on the fly */
162    if (!
163        (bus->mutex =
164         mutexCreate (rtems_build_name ('i', '2', 'c', '0' + busno)))) {
165      LIBUNLOCK ();
166      rtems_panic (DRVNM " unable to create bus lock");
167    }
168  }
169  /* count number of people waiting on this bus; only the last one deletes the mutex */
170  bus->waiting++;
171  LIBUNLOCK ();
172  /* Now lock this bus */
173  LOCK (bus->mutex);
174}
175
176static void
177unlock_bus (int busno)
178{
179  struct i2cbus *bus = &busses[busno];
180  LIBLOCK ();
181  UNLOCK (bus->mutex);
182  if (!--bus->waiting) {
183    rtems_semaphore_delete (bus->mutex);
184  }
185  LIBUNLOCK ();
186}
187
188/* Note that 'arg' is always passed in as NULL */
189static rtems_status_code
190i2c_init (rtems_device_major_number major, rtems_device_minor_number minor,
191          void *arg)
192{
193  rtems_status_code rval;
194  DECL_CHECKED_DRV (drv, busno, minor)
195
196    if (0 == drv) {
197    rval = 0;
198  } else {
199    /* this is probably never called */
200    DISPATCH (rval, initialization_entry, RTEMS_SUCCESSFUL);
201  }
202  return rval;
203}
204
205static rtems_status_code
206i2c_open (rtems_device_major_number major, rtems_device_minor_number minor,
207          void *arg)
208{
209  rtems_status_code rval;
210  DECL_CHECKED_DRV (drv, busno, minor)
211
212    if (0 == drv) {
213    rval = RTEMS_SUCCESSFUL;
214  } else {
215    DISPATCH (rval, open_entry, RTEMS_SUCCESSFUL);
216  }
217  return rval;
218}
219
220static rtems_status_code
221i2c_close (rtems_device_major_number major, rtems_device_minor_number minor,
222           void *arg)
223{
224  rtems_status_code rval;
225  DECL_CHECKED_DRV (drv, busno, minor)
226
227    if (0 == drv) {
228    rval = RTEMS_SUCCESSFUL;
229  } else {
230    DISPATCH (rval, close_entry, RTEMS_SUCCESSFUL);
231  }
232  return rval;
233}
234
235static rtems_status_code
236i2c_read (rtems_device_major_number major, rtems_device_minor_number minor,
237          void *arg)
238{
239  int rval;                     /* int so we can check for negative value */
240  rtems_libio_rw_args_t *rwargs = arg;
241  DECL_CHECKED_DRV (drv, busno, minor)
242
243    if (0 == rwargs->count) {
244    rwargs->bytes_moved = 0;
245    return RTEMS_SUCCESSFUL;
246  }
247
248  if (0 == drv) {
249    rval =
250      rtems_libi2c_start_read_bytes (minor, (unsigned char *) rwargs->buffer,
251                                     rwargs->count);
252    if (rval >= 0) {
253      rwargs->bytes_moved = rval;
254      rtems_libi2c_send_stop (minor);
255      rval = RTEMS_SUCCESSFUL;
256    } else {
257      rval = -rval;
258    }
259  } else {
260    DISPATCH (rval, read_entry, RTEMS_NOT_IMPLEMENTED);
261  }
262  return rval;
263}
264
265static rtems_status_code
266i2c_write (rtems_device_major_number major, rtems_device_minor_number minor,
267           void *arg)
268{
269  int rval;                     /* int so we can check for negative value */
270  rtems_libio_rw_args_t *rwargs = arg;
271  DECL_CHECKED_DRV (drv, busno, minor)
272
273    if (0 == rwargs->count) {
274    rwargs->bytes_moved = 0;
275    return RTEMS_SUCCESSFUL;
276  }
277
278  if (0 == drv) {
279    rval =
280      rtems_libi2c_start_write_bytes (minor, (unsigned char *) rwargs->buffer,
281                                      rwargs->count);
282    if (rval >= 0) {
283      rwargs->bytes_moved = rval;
284      rtems_libi2c_send_stop (minor);
285      rval = RTEMS_SUCCESSFUL;
286    } else {
287      rval = -rval;
288    }
289  } else {
290    DISPATCH (rval, write_entry, RTEMS_NOT_IMPLEMENTED);
291  }
292  return rval;
293}
294
295static rtems_status_code
296i2c_ioctl (rtems_device_major_number major, rtems_device_minor_number minor,
297           void *arg)
298{
299  rtems_status_code rval;
300  DECL_CHECKED_DRV (drv, busno, minor)
301
302    if (0 == drv) {
303    rval = RTEMS_NOT_IMPLEMENTED;
304  } else {
305    DISPATCH (rval, control_entry, RTEMS_NOT_IMPLEMENTED);
306  }
307  return rval;
308}
309
310
311/* Our ops just dispatch to the registered drivers */
312static rtems_driver_address_table libi2c_io_ops = {
313  initialization_entry:  i2c_init,
314  open_entry:            i2c_open,
315  close_entry:           i2c_close,
316  read_entry:            i2c_read,
317  write_entry:           i2c_write,
318  control_entry:         i2c_ioctl,
319};
320
321
322int
323rtems_libi2c_initialize ()
324{
325  rtems_status_code sc;
326  static boolean is_initialized = FALSE;
327
328  if (is_initialized) {
329    /*
330     * already called before? then skip this step
331     */
332    return 0;
333  }
334 
335  if (!(libmutex = mutexCreate (rtems_build_name ('l', 'I', '2', 'C'))))
336    return -1;
337
338  sc = rtems_io_register_driver (0, &libi2c_io_ops, &rtems_libi2c_major);
339  if (RTEMS_SUCCESSFUL != sc) {
340    fprintf (stderr,
341             DRVNM " Claiming driver slot failed (rtems status code %i)\n",
342             sc);
343    rtems_semaphore_delete (libmutex);
344    libmutex = 0;
345    return -1;
346  }
347  is_initialized = TRUE;
348
349  return 0;
350}
351
352int
353rtems_libi2c_register_bus (char *name, rtems_libi2c_bus_t * bus)
354{
355  int i;
356  rtems_status_code err;
357  char *nmcpy = malloc (name ? strlen (name) + 1 : 20);
358  char tmp, *chpt;
359  struct stat sbuf;
360
361  strcpy (nmcpy, name ? name : "/dev/i2c");
362
363
364  /* check */
365  if ('/' != *nmcpy) {
366    fprintf (stderr,
367             "Bad name; must be an absolute path starting with '/'\n");
368    return -RTEMS_INVALID_NAME;
369  }
370  /* file must not exist */
371  if (!stat (nmcpy, &sbuf)) {
372    fprintf (stderr, "Bad name; file exists already\n");
373    return -RTEMS_INVALID_NAME;
374  }
375
376  /* we already verified that there is at least one '/' */
377  chpt = strrchr (nmcpy, '/') + 1;
378  tmp = *chpt;
379  *chpt = 0;
380  i = stat (nmcpy, &sbuf);
381  *chpt = tmp;
382  if (i) {
383    fprintf (stderr, "Get %s status failed: %s\n",
384             nmcpy,strerror(errno));
385    return -RTEMS_INVALID_NAME;
386  }
387  /* should be a directory since name terminates in '/' */
388
389
390  if (!libmutex) {
391    fprintf (stderr, DRVNM " library not initialized\n");
392    return -RTEMS_NOT_DEFINED;
393  }
394
395  if (bus->size < sizeof (*bus)) {
396    fprintf (stderr, DRVNM " bus-ops size too small -- misconfiguration?\n");
397    return -RTEMS_NOT_CONFIGURED;
398  }
399
400  LIBLOCK ();
401  for (i = 0; i < MAX_NO_BUSSES; i++) {
402    if (!busses[i].bush) {
403      /* found a free slot */
404      busses[i].bush = bus;
405      busses[i].mutex = 0;
406      busses[i].waiting = 0;
407      busses[i].started = 0;
408
409      if (!name)
410        sprintf (nmcpy + strlen (nmcpy), "%i", i);
411
412      if ((err = busses[i].bush->ops->init (busses[i].bush))) {
413        /* initialization failed */
414        i = -err;
415      } else {
416        busses[i].name = nmcpy;;
417        nmcpy = 0;
418      }
419
420      break;
421    }
422  }
423  LIBUNLOCK ();
424
425  if (i >= MAX_NO_BUSSES) {
426    i = -RTEMS_TOO_MANY;
427  }
428
429  free (nmcpy);
430
431  return i;
432}
433
434static int
435not_started (int busno)
436{
437  int rval;
438  lock_bus (busno);
439  rval = !busses[busno].started;
440  unlock_bus (busno);
441  return rval;
442}
443
444rtems_status_code
445rtems_libi2c_send_start (rtems_device_minor_number minor)
446{
447  int rval;
448  DECL_CHECKED_BH (busno, bush, minor, +)
449
450    lock_bus (busno);
451  rval = bush->ops->send_start (bush);
452
453  /* if this failed or is not the first start, unlock */
454  if (rval || busses[busno].started) {
455    /* HMM - what to do if the 1st start failed ?
456     * try to reset...
457     */
458    if (!busses[busno].started) {
459      /* just in case the bus driver fiddles with errno */
460      int errno_saved = errno;
461      bush->ops->init (bush);
462      errno = errno_saved;
463    } else if (rval) {
464      /* failed restart */
465      rtems_libi2c_send_stop (minor);
466    }
467    unlock_bus (busno);
468  } else {
469    /* successful 1st start; keep bus locked until stop is sent */
470    busses[busno].started = 1;
471  }
472  return rval;
473}
474
475rtems_status_code
476rtems_libi2c_send_stop (rtems_device_minor_number minor)
477{
478  rtems_status_code rval;
479  DECL_CHECKED_BH (busno, bush, minor, +)
480
481    if (not_started (busno))
482    return RTEMS_NOT_OWNER_OF_RESOURCE;
483
484  rval = bush->ops->send_stop (bush);
485
486  busses[busno].started = 0;
487
488  unlock_bus (busno);
489  return rval;
490}
491
492rtems_status_code
493rtems_libi2c_send_addr (rtems_device_minor_number minor, int rw)
494{
495  rtems_status_code sc;
496  DECL_CHECKED_BH (busno, bush, minor, +)
497
498    if (not_started (busno))
499    return RTEMS_NOT_OWNER_OF_RESOURCE;
500
501  sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw);
502  if (RTEMS_SUCCESSFUL != sc)
503    rtems_libi2c_send_stop (minor);
504  return sc;
505}
506
507int
508rtems_libi2c_read_bytes (rtems_device_minor_number minor,
509                         unsigned char *bytes,
510                         int nbytes)
511{
512  int sc;
513  DECL_CHECKED_BH (busno, bush, minor, -)
514
515    if (not_started (busno))
516    return -RTEMS_NOT_OWNER_OF_RESOURCE;
517
518  sc = bush->ops->read_bytes (bush, bytes, nbytes);
519  if (sc < 0)
520    rtems_libi2c_send_stop (minor);
521  return sc;
522}
523
524int
525rtems_libi2c_write_bytes (rtems_device_minor_number minor,
526                          unsigned char *bytes,
527                          int nbytes)
528{
529  int sc;
530  DECL_CHECKED_BH (busno, bush, minor, -)
531
532    if (not_started (busno))
533    return -RTEMS_NOT_OWNER_OF_RESOURCE;
534
535  sc = bush->ops->write_bytes (bush, bytes, nbytes);
536  if (sc < 0)
537    rtems_libi2c_send_stop (minor);
538  return sc;
539}
540
541int
542rtems_libi2c_ioctl (rtems_device_minor_number minor,
543                    int cmd,
544                    ...)
545{
546  va_list            ap;
547  int sc = 0;
548  void *args;
549  DECL_CHECKED_BH (busno, bush, minor, -)
550
551    if (not_started (busno))
552    return -RTEMS_NOT_OWNER_OF_RESOURCE;
553
554  va_start(ap, cmd);
555  args = va_arg(ap, void *);
556
557  switch(cmd) {
558    /*
559     * add ioctls defined for this level here:   
560     */
561   
562  case RTEMS_LIBI2C_IOCTL_START_TFM_READ_WRITE:
563    /*
564     * address device, then set transfer mode and perform read_write transfer
565     */
566    /*
567     * perform start/address
568     */
569    if (sc == 0) {
570      sc = rtems_libi2c_send_start (minor);
571    }
572    /*
573     * set tfr mode
574     */
575    if (sc == 0) {
576      sc = bush->ops->ioctl
577        (bush,
578         RTEMS_LIBI2C_IOCTL_SET_TFRMODE,
579         &((rtems_libi2c_tfm_read_write_t *)args)->tfr_mode);
580    }
581    /*
582     * perform read_write
583     */
584    if (sc == 0) {
585      sc = bush->ops->ioctl
586        (bush,
587         RTEMS_LIBI2C_IOCTL_READ_WRITE,
588         &((rtems_libi2c_tfm_read_write_t *)args)->rd_wr);
589    }
590    break;
591  default:
592    sc = bush->ops->ioctl (bush, cmd, args);
593    break;
594  }
595  if (sc < 0)
596    rtems_libi2c_send_stop (minor);
597  return sc;
598}
599
600static int
601do_s_rw (rtems_device_minor_number minor,
602         unsigned char *bytes,
603         int nbytes,
604         int rw)
605{
606  rtems_status_code sc;
607  rtems_libi2c_bus_t *bush;
608
609  if ((sc = rtems_libi2c_send_start (minor)))
610    return -sc;
611
612  /* at this point, we hold the bus and are sure the minor number is valid */
613  bush = busses[MINOR2BUS (minor)].bush;
614
615  if ((sc = bush->ops->send_addr (bush, MINOR2ADDR (minor), rw))) {
616    rtems_libi2c_send_stop (minor);
617    return -sc;
618  }
619
620  if (rw)
621    sc = bush->ops->read_bytes (bush, bytes, nbytes);
622  else
623    sc = bush->ops->write_bytes (bush, bytes, nbytes);
624
625  if (sc < 0) {
626    rtems_libi2c_send_stop (minor);
627  }
628  return sc;
629}
630
631int
632rtems_libi2c_start_read_bytes (rtems_device_minor_number minor,
633                               unsigned char *bytes,
634                               int nbytes)
635{
636  return do_s_rw (minor, bytes, nbytes, 1);
637}
638
639int
640rtems_libi2c_start_write_bytes (rtems_device_minor_number minor,
641                                unsigned char *bytes,
642                                int nbytes)
643{
644  return do_s_rw (minor, bytes, nbytes, 0);
645}
646
647int
648rtems_libi2c_register_drv (char *name, rtems_libi2c_drv_t * drvtbl,
649                           unsigned busno, unsigned i2caddr)
650{
651  int i;
652  rtems_status_code err;
653  rtems_device_minor_number minor;
654
655  if (!libmutex) {
656    fprintf (stderr, DRVNM " library not initialized\n");
657    return -RTEMS_NOT_DEFINED;
658  }
659
660  if (name && strchr (name, '/')) {
661    fprintf (stderr, "Invalid name: '%s' -- must not contain '/'\n", name);
662    return -RTEMS_INVALID_NAME;
663  }
664
665  if (busno >= MAX_NO_BUSSES || !busses[busno].bush || i2caddr >= 1 << 10) {
666    errno = EINVAL;
667    return -RTEMS_INVALID_NUMBER;
668  }
669
670  if (drvtbl->size < sizeof (*drvtbl)) {
671    fprintf (stderr, DRVNM " drv-ops size too small -- misconfiguration?\n");
672    return -RTEMS_NOT_CONFIGURED;
673  }
674
675  /* allocate slot */
676  LIBLOCK ();
677  for (i = 0; i < MAX_NO_DRIVERS; i++) {
678    /* driver # 0 is special, it is the built-in raw driver */
679    if (!drvs[i].drv) {
680      char *str;
681      dev_t dev;
682      uint32_t mode;
683
684      /* found a free slot; encode slot + 1 ! */
685      minor = ((i + 1) << 13) | RTEMS_LIBI2C_MAKE_MINOR (busno, i2caddr);
686
687      if (name) {
688        str = malloc (strlen (busses[busno].name) + strlen (name) + 2);
689        sprintf (str, "%s.%s", busses[busno].name, name);
690
691        dev = rtems_filesystem_make_dev_t (rtems_libi2c_major, minor);
692
693        mode = 0111 | S_IFCHR;
694        if (drvtbl->ops->read_entry)
695          mode |= 0444;
696        if (drvtbl->ops->write_entry)
697          mode |= 0222;
698
699        /* note that 'umask' is applied to 'mode' */
700        if (mknod (str, mode, dev)) {
701          fprintf (stderr,
702                   "Creating device node failed: %s; you can try to do it manually...\n",
703                   strerror (errno));
704        }
705
706        free (str);
707      }
708
709      drvs[i].drv = drvtbl;
710
711      if (drvtbl->ops->initialization_entry)
712        err =
713          drvs[i].drv->ops->initialization_entry (rtems_libi2c_major, minor,
714                                                  0);
715      else
716        err = RTEMS_SUCCESSFUL;
717
718      LIBUNLOCK ();
719      return err ? -err : minor;
720    }
721  }
722  LIBUNLOCK ();
723  return -RTEMS_TOO_MANY;
724}
Note: See TracBrowser for help on using the repository browser.