The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/iicbus/iicbb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1998, 2001 Nicolas Souchu
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD: releng/5.2/sys/dev/iicbus/iicbb.c 119418 2003-08-24 17:55:58Z obrien $");
   29 
   30 /*
   31  * Generic I2C bit-banging code
   32  *
   33  * Example:
   34  *
   35  *      iicbus
   36  *       /  \ 
   37  *    iicbb pcf
   38  *     |  \
   39  *   bti2c lpbb
   40  *
   41  * From Linux I2C generic interface
   42  * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
   43  *
   44  */
   45 
   46 #include <sys/param.h>
   47 #include <sys/kernel.h>
   48 #include <sys/systm.h>
   49 #include <sys/module.h>
   50 #include <sys/bus.h>
   51 #include <sys/uio.h>
   52 
   53 
   54 #include <dev/iicbus/iiconf.h>
   55 #include <dev/iicbus/iicbus.h>
   56 
   57 #include <dev/smbus/smbconf.h>
   58 
   59 #include "iicbus_if.h"
   60 #include "iicbb_if.h"
   61 
   62 struct iicbb_softc {
   63         device_t iicbus;
   64 };
   65 
   66 static int iicbb_probe(device_t);
   67 static int iicbb_attach(device_t);
   68 static int iicbb_detach(device_t);
   69 static int iicbb_print_child(device_t, device_t);
   70 
   71 static int iicbb_callback(device_t, int, caddr_t);
   72 static int iicbb_start(device_t, u_char, int);
   73 static int iicbb_stop(device_t);
   74 static int iicbb_write(device_t, char *, int, int *, int);
   75 static int iicbb_read(device_t, char *, int, int *, int, int);
   76 static int iicbb_reset(device_t, u_char, u_char, u_char *);
   77 
   78 static device_method_t iicbb_methods[] = {
   79         /* device interface */
   80         DEVMETHOD(device_probe,         iicbb_probe),
   81         DEVMETHOD(device_attach,        iicbb_attach),
   82         DEVMETHOD(device_detach,        iicbb_detach),
   83 
   84         /* bus interface */
   85         DEVMETHOD(bus_print_child,      iicbb_print_child),
   86 
   87         /* iicbus interface */
   88         DEVMETHOD(iicbus_callback,      iicbb_callback),
   89         DEVMETHOD(iicbus_start,         iicbb_start),
   90         DEVMETHOD(iicbus_repeated_start, iicbb_start),
   91         DEVMETHOD(iicbus_stop,          iicbb_stop),
   92         DEVMETHOD(iicbus_write,         iicbb_write),
   93         DEVMETHOD(iicbus_read,          iicbb_read),
   94         DEVMETHOD(iicbus_reset,         iicbb_reset),
   95 
   96         { 0, 0 }
   97 };
   98 
   99 driver_t iicbb_driver = {
  100         "iicbb",
  101         iicbb_methods,
  102         sizeof(struct iicbb_softc),
  103 };
  104 
  105 devclass_t iicbb_devclass;
  106 
  107 static int iicbb_probe(device_t dev)
  108 {
  109         device_set_desc(dev, "I2C bit-banging driver");
  110 
  111         return (0);
  112 }
  113 
  114 static int iicbb_attach(device_t dev)
  115 {
  116         struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
  117 
  118         bzero(sc, sizeof(struct iicbb_softc));
  119 
  120         sc->iicbus = device_add_child(dev, "iicbus", -1);
  121 
  122         if (!sc->iicbus)
  123                 return (ENXIO);
  124 
  125         bus_generic_attach(dev);
  126 
  127         return (0);
  128 }
  129 
  130 static int iicbb_detach(device_t dev)
  131 {
  132         struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
  133 
  134         if (sc->iicbus) {
  135                 bus_generic_detach(dev);
  136                 device_delete_child(dev, sc->iicbus);
  137         }
  138 
  139         return (0);
  140 }
  141 
  142 static int
  143 iicbb_print_child(device_t bus, device_t dev)
  144 {
  145         int error;
  146         int retval = 0;
  147         u_char oldaddr;
  148 
  149         retval += bus_print_child_header(bus, dev);
  150         /* retrieve the interface I2C address */
  151         error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr);
  152         if (error == IIC_ENOADDR) {
  153                 retval += printf(" on %s master-only\n",
  154                                  device_get_nameunit(bus));
  155         } else {
  156                 /* restore the address */
  157                 IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL);
  158 
  159                 retval += printf(" on %s addr 0x%x\n",
  160                                  device_get_nameunit(bus), oldaddr & 0xff);
  161         }
  162 
  163         return (retval);
  164 }
  165 
  166 #define IIC_DELAY       10
  167 
  168 #define I2C_SETSDA(dev,val) do {                        \
  169         IICBB_SETSDA(device_get_parent(dev), val);      \
  170         DELAY(IIC_DELAY);                               \
  171         } while (0)
  172 
  173 #define I2C_SETSCL(dev,val) do {                        \
  174         iicbb_setscl(dev, val, 100);                    \
  175         } while (0)
  176 
  177 #define I2C_SET(dev,ctrl,data) do {                     \
  178         I2C_SETSCL(dev, ctrl);                          \
  179         I2C_SETSDA(dev, data);                          \
  180         } while (0)
  181 
  182 #define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
  183 
  184 #define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
  185 
  186 static int i2c_debug = 0;
  187 #define I2C_DEBUG(x)    do {                                    \
  188                                 if (i2c_debug) (x);             \
  189                         } while (0)
  190 
  191 #define I2C_LOG(format,args...) do {                            \
  192                                         printf(format, args);   \
  193                                 } while (0)
  194 
  195 static void iicbb_setscl(device_t dev, int val, int timeout)
  196 {
  197         int k = 0;
  198 
  199         IICBB_SETSCL(device_get_parent(dev), val);
  200         DELAY(IIC_DELAY);
  201 
  202         while (val && !I2C_GETSCL(dev) && k++ < timeout) {
  203                 IICBB_SETSCL(device_get_parent(dev), val);
  204                 DELAY(IIC_DELAY);
  205         }
  206                 
  207         return;
  208 }
  209 
  210 static void iicbb_one(device_t dev, int timeout)
  211 {
  212         I2C_SET(dev,0,1);
  213         I2C_SET(dev,1,1);
  214         I2C_SET(dev,0,1);
  215         return;
  216 }
  217 
  218 static void iicbb_zero(device_t dev, int timeout)
  219 {
  220         I2C_SET(dev,0,0);
  221         I2C_SET(dev,1,0);
  222         I2C_SET(dev,0,0);
  223         return;
  224 }
  225 
  226 /*
  227  * Waiting for ACKNOWLEDGE.
  228  *
  229  * When a chip is being addressed or has received data it will issue an
  230  * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line
  231  * (set it to high level) and then release the CLOCK line.
  232  * Now it must wait for the SLAVE to pull the DATA line low.
  233  * Actually on the bus this looks like a START condition so nothing happens
  234  * because of the fact that the IC's that have not been addressed are doing
  235  * nothing.
  236  *
  237  * When the SLAVE has pulled this line low the MASTER will take the CLOCK
  238  * line low and then the SLAVE will release the SDA (data) line.
  239  */
  240 static int iicbb_ack(device_t dev, int timeout)
  241 {
  242         int noack;
  243         int k = 0;
  244     
  245         I2C_SET(dev,0,1);
  246         I2C_SET(dev,1,1);
  247         do {
  248                 noack = I2C_GETSDA(dev);
  249                 if (!noack)
  250                         break;
  251                 DELAY(10);
  252                 k += 10;
  253         } while (k < timeout);
  254 
  255         I2C_SET(dev,0,1);
  256         I2C_DEBUG(printf("%c ",noack?'-':'+'));
  257 
  258         return (noack);
  259 }
  260 
  261 static void iicbb_sendbyte(device_t dev, u_char data, int timeout)
  262 {
  263         int i;
  264     
  265         for (i=7; i>=0; i--) {
  266                 if (data&(1<<i)) {
  267                         iicbb_one(dev, timeout);
  268                 } else {
  269                         iicbb_zero(dev, timeout);
  270                 }
  271         }
  272         I2C_DEBUG(printf("w%02x",(int)data));
  273         return;
  274 }
  275 
  276 static u_char iicbb_readbyte(device_t dev, int last, int timeout)
  277 {
  278         int i;
  279         unsigned char data=0;
  280     
  281         I2C_SET(dev,0,1);
  282         for (i=7; i>=0; i--) 
  283         {
  284                 I2C_SET(dev,1,1);
  285                 if (I2C_GETSDA(dev))
  286                         data |= (1<<i);
  287                 I2C_SET(dev,0,1);
  288         }
  289         if (last) {
  290                 iicbb_one(dev, timeout);
  291         } else {
  292                 iicbb_zero(dev, timeout);
  293         }
  294         I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+'));
  295         return data;
  296 }
  297 
  298 static int iicbb_callback(device_t dev, int index, caddr_t data)
  299 {
  300         return (IICBB_CALLBACK(device_get_parent(dev), index, data));
  301 }
  302 
  303 static int iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
  304 {
  305         return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr));
  306 }
  307 
  308 static int iicbb_start(device_t dev, u_char slave, int timeout)
  309 {
  310         int error;
  311 
  312         I2C_DEBUG(printf("<"));
  313 
  314         I2C_SET(dev,1,1);
  315         I2C_SET(dev,1,0);
  316         I2C_SET(dev,0,0);
  317 
  318         /* send address */
  319         iicbb_sendbyte(dev, slave, timeout);
  320 
  321         /* check for ack */
  322         if (iicbb_ack(dev, timeout)) {
  323                 error = IIC_ENOACK;
  324                 goto error;
  325         }
  326 
  327         return(0);
  328 
  329 error:
  330         iicbb_stop(dev);
  331         return (error);
  332 }
  333 
  334 static int iicbb_stop(device_t dev)
  335 {
  336         I2C_SET(dev,0,0);
  337         I2C_SET(dev,1,0);
  338         I2C_SET(dev,1,1);
  339         I2C_DEBUG(printf(">"));
  340         return (0);
  341 }
  342 
  343 static int iicbb_write(device_t dev, char * buf, int len, int *sent,
  344                         int timeout)
  345 {
  346         int bytes, error = 0;
  347 
  348         bytes = 0;
  349         while (len) {
  350                 /* send byte */
  351                 iicbb_sendbyte(dev,(u_char)*buf++, timeout);
  352 
  353                 /* check for ack */
  354                 if (iicbb_ack(dev, timeout)) {
  355                         error = IIC_ENOACK;
  356                         goto error;
  357                 }
  358                 bytes ++;
  359                 len --;
  360         }
  361 
  362 error:
  363         *sent = bytes;
  364         return (error);
  365 }
  366 
  367 static int iicbb_read(device_t dev, char * buf, int len, int *read,
  368                         int last, int delay)
  369 {
  370         int bytes;
  371 
  372         bytes = 0;
  373         while (len) {
  374                 /* XXX should insert delay here */
  375                 *buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay);
  376 
  377                 bytes ++;
  378                 len --;
  379         }
  380 
  381         *read = bytes;
  382         return (0);
  383 }
  384 
  385 DRIVER_MODULE(iicbb, bktr, iicbb_driver, iicbb_devclass, 0, 0);
  386 DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
  387 DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0);
  388 
  389 MODULE_DEPEND(iicbb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  390 MODULE_VERSION(iicbb, IICBB_MODVER);

Cache object: 33c1139b502066f0b0082c5a022b0338


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.