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/iicsmb.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 1998, 2001 Nicolas Souchu
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 /*
   33  * I2C to SMB bridge
   34  *
   35  * Example:
   36  *
   37  *     smb bttv
   38  *       \ /
   39  *      smbus
   40  *       /  \
   41  *    iicsmb bti2c
   42  *       |
   43  *     iicbus
   44  *     /  |  \
   45  *  iicbb pcf ...
   46  *    |
   47  *  lpbb
   48  */
   49 
   50 #include <sys/param.h>
   51 #include <sys/bus.h>
   52 #include <sys/kernel.h>
   53 #include <sys/lock.h>
   54 #include <sys/module.h>
   55 #include <sys/mutex.h>
   56 #include <sys/systm.h>
   57 #include <sys/uio.h>
   58 
   59 #include <dev/iicbus/iiconf.h>
   60 #include <dev/iicbus/iicbus.h>
   61 
   62 #include <dev/smbus/smb.h>
   63 #include <dev/smbus/smbconf.h>
   64 
   65 #include "iicbus_if.h"
   66 #include "smbus_if.h"
   67 
   68 struct iicsmb_softc {
   69 
   70 #define SMB_WAITING_ADDR        0x0
   71 #define SMB_WAITING_LOW         0x1
   72 #define SMB_WAITING_HIGH        0x2
   73 #define SMB_DONE                0x3
   74         int state;
   75 
   76         u_char devaddr;                 /* slave device address */
   77 
   78         char low;                       /* low byte received first */
   79         char high;                      /* high byte */
   80 
   81         struct mtx lock;
   82         device_t smbus;
   83 };
   84 
   85 static int iicsmb_probe(device_t);
   86 static int iicsmb_attach(device_t);
   87 static int iicsmb_detach(device_t);
   88 static void iicsmb_identify(driver_t *driver, device_t parent);
   89 
   90 static int iicsmb_intr(device_t dev, int event, char *buf);
   91 static int iicsmb_callback(device_t dev, int index, void *data);
   92 static int iicsmb_quick(device_t dev, u_char slave, int how);
   93 static int iicsmb_sendb(device_t dev, u_char slave, char byte);
   94 static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
   95 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
   96 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
   97 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
   98 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
   99 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
  100 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
  101 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
  102 
  103 static device_method_t iicsmb_methods[] = {
  104         /* device interface */
  105         DEVMETHOD(device_identify,      iicsmb_identify),
  106         DEVMETHOD(device_probe,         iicsmb_probe),
  107         DEVMETHOD(device_attach,        iicsmb_attach),
  108         DEVMETHOD(device_detach,        iicsmb_detach),
  109 
  110         /* iicbus interface */
  111         DEVMETHOD(iicbus_intr,          iicsmb_intr),
  112 
  113         /* smbus interface */
  114         DEVMETHOD(smbus_callback,       iicsmb_callback),
  115         DEVMETHOD(smbus_quick,          iicsmb_quick),
  116         DEVMETHOD(smbus_sendb,          iicsmb_sendb),
  117         DEVMETHOD(smbus_recvb,          iicsmb_recvb),
  118         DEVMETHOD(smbus_writeb,         iicsmb_writeb),
  119         DEVMETHOD(smbus_writew,         iicsmb_writew),
  120         DEVMETHOD(smbus_readb,          iicsmb_readb),
  121         DEVMETHOD(smbus_readw,          iicsmb_readw),
  122         DEVMETHOD(smbus_pcall,          iicsmb_pcall),
  123         DEVMETHOD(smbus_bwrite,         iicsmb_bwrite),
  124         DEVMETHOD(smbus_bread,          iicsmb_bread),
  125 
  126         DEVMETHOD_END
  127 };
  128 
  129 static driver_t iicsmb_driver = {
  130         "iicsmb",
  131         iicsmb_methods,
  132         sizeof(struct iicsmb_softc),
  133 };
  134 
  135 static void
  136 iicsmb_identify(driver_t *driver, device_t parent)
  137 {
  138 
  139         if (device_find_child(parent, "iicsmb", -1) == NULL)
  140                 BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
  141 }
  142 
  143 static int
  144 iicsmb_probe(device_t dev)
  145 {
  146         device_set_desc(dev, "SMBus over I2C bridge");
  147         return (BUS_PROBE_NOWILDCARD);
  148 }
  149 
  150 static int
  151 iicsmb_attach(device_t dev)
  152 {
  153         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
  154 
  155         mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
  156 
  157         sc->smbus = device_add_child(dev, "smbus", -1);
  158 
  159         /* probe and attach the smbus */
  160         bus_generic_attach(dev);
  161 
  162         return (0);
  163 }
  164 
  165 static int
  166 iicsmb_detach(device_t dev)
  167 {
  168         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
  169 
  170         bus_generic_detach(dev);
  171         device_delete_children(dev);
  172         mtx_destroy(&sc->lock);
  173 
  174         return (0);
  175 }
  176 
  177 /*
  178  * iicsmb_intr()
  179  *
  180  * iicbus interrupt handler
  181  */
  182 static int
  183 iicsmb_intr(device_t dev, int event, char *buf)
  184 {
  185         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
  186 
  187         mtx_lock(&sc->lock);
  188         switch (event) {
  189         case INTR_GENERAL:
  190         case INTR_START:
  191                 sc->state = SMB_WAITING_ADDR;
  192                 break;
  193 
  194         case INTR_STOP:
  195                 /* call smbus intr handler */
  196                 smbus_intr(sc->smbus, sc->devaddr,
  197                                 sc->low, sc->high, SMB_ENOERR);
  198                 break;
  199 
  200         case INTR_RECEIVE:
  201                 switch (sc->state) {
  202                 case SMB_DONE:
  203                         /* XXX too much data, discard */
  204                         printf("%s: too much data from 0x%x\n", __func__,
  205                                 sc->devaddr & 0xff);
  206                         goto end;
  207 
  208                 case SMB_WAITING_ADDR:
  209                         sc->devaddr = (u_char)*buf;
  210                         sc->state = SMB_WAITING_LOW;
  211                         break;
  212 
  213                 case SMB_WAITING_LOW:
  214                         sc->low = *buf;
  215                         sc->state = SMB_WAITING_HIGH;
  216                         break;
  217 
  218                 case SMB_WAITING_HIGH:
  219                         sc->high = *buf;
  220                         sc->state = SMB_DONE;
  221                         break;
  222                 }
  223 end:
  224                 break;
  225 
  226         case INTR_TRANSMIT:
  227         case INTR_NOACK:
  228                 break;
  229 
  230         case INTR_ERROR:
  231                 switch (*buf) {
  232                 case IIC_EBUSERR:
  233                         smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
  234                         break;
  235 
  236                 default:
  237                         printf("%s unknown error 0x%x!\n", __func__,
  238                                                                 (int)*buf);
  239                         break;
  240                 }
  241                 break;
  242 
  243         default:
  244                 panic("%s: unknown event (%d)!", __func__, event);
  245         }
  246         mtx_unlock(&sc->lock);
  247 
  248         return (0);
  249 }
  250 
  251 static int
  252 iicsmb_callback(device_t dev, int index, void *data)
  253 {
  254         device_t parent = device_get_parent(dev);
  255         int error = 0;
  256         int how;
  257 
  258         switch (index) {
  259         case SMB_REQUEST_BUS:
  260                 /* request underlying iicbus */
  261                 how = *(int *)data;
  262                 error = iicbus_request_bus(parent, dev, how);
  263                 break;
  264 
  265         case SMB_RELEASE_BUS:
  266                 /* release underlying iicbus */
  267                 error = iicbus_release_bus(parent, dev);
  268                 break;
  269 
  270         default:
  271                 error = EINVAL;
  272         }
  273 
  274         return (error);
  275 }
  276 
  277 static int
  278 iic2smb_error(int error)
  279 {
  280         switch (error) {
  281         case IIC_NOERR:
  282                 return (SMB_ENOERR);
  283         case IIC_EBUSERR:
  284                 return (SMB_EBUSERR);
  285         case IIC_ENOACK:
  286                 return (SMB_ENOACK);
  287         case IIC_ETIMEOUT:
  288                 return (SMB_ETIMEOUT);
  289         case IIC_EBUSBSY:
  290                 return (SMB_EBUSY);
  291         case IIC_ESTATUS:
  292                 return (SMB_EBUSERR);
  293         case IIC_EUNDERFLOW:
  294                 return (SMB_EBUSERR);
  295         case IIC_EOVERFLOW:
  296                 return (SMB_EBUSERR);
  297         case IIC_ENOTSUPP:
  298                 return (SMB_ENOTSUPP);
  299         case IIC_ENOADDR:
  300                 return (SMB_EBUSERR);
  301         case IIC_ERESOURCE:
  302                 return (SMB_EBUSERR);
  303         default:
  304                 return (SMB_EBUSERR);
  305         }
  306 }
  307 
  308 #define TRANSFER_MSGS(dev, msgs)        iicbus_transfer(dev, msgs, nitems(msgs))
  309 
  310 static int
  311 iicsmb_quick(device_t dev, u_char slave, int how)
  312 {
  313         struct iic_msg msgs[] = {
  314              { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
  315         };
  316         int error;
  317 
  318         switch (how) {
  319         case SMB_QWRITE:
  320         case SMB_QREAD:
  321                 break;
  322         default:
  323                 return (SMB_EINVAL);
  324         }
  325 
  326         error = TRANSFER_MSGS(dev, msgs);
  327         return (iic2smb_error(error));
  328 }
  329 
  330 static int
  331 iicsmb_sendb(device_t dev, u_char slave, char byte)
  332 {
  333         struct iic_msg msgs[] = {
  334              { slave, IIC_M_WR, 1, &byte },
  335         };
  336         int error;
  337 
  338         error = TRANSFER_MSGS(dev, msgs);
  339         return (iic2smb_error(error));
  340 }
  341 
  342 static int
  343 iicsmb_recvb(device_t dev, u_char slave, char *byte)
  344 {
  345         struct iic_msg msgs[] = {
  346              { slave, IIC_M_RD, 1, byte },
  347         };
  348         int error;
  349 
  350         error = TRANSFER_MSGS(dev, msgs);
  351         return (iic2smb_error(error));
  352 }
  353 
  354 static int
  355 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
  356 {
  357         uint8_t bytes[] = { cmd, byte };
  358         struct iic_msg msgs[] = {
  359              { slave, IIC_M_WR, nitems(bytes), bytes },
  360         };
  361         int error;
  362 
  363         error = TRANSFER_MSGS(dev, msgs);
  364         return (iic2smb_error(error));
  365 }
  366 
  367 static int
  368 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
  369 {
  370         uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
  371         struct iic_msg msgs[] = {
  372              { slave, IIC_M_WR, nitems(bytes), bytes },
  373         };
  374         int error;
  375 
  376         error = TRANSFER_MSGS(dev, msgs);
  377         return (iic2smb_error(error));
  378 }
  379 
  380 static int
  381 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
  382 {
  383         struct iic_msg msgs[] = {
  384              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  385              { slave, IIC_M_RD, 1, byte },
  386         };
  387         int error;
  388 
  389         error = TRANSFER_MSGS(dev, msgs);
  390         return (iic2smb_error(error));
  391 }
  392 
  393 static int
  394 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
  395 {
  396         uint8_t buf[2];
  397         struct iic_msg msgs[] = {
  398              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  399              { slave, IIC_M_RD, nitems(buf), buf },
  400         };
  401         int error;
  402 
  403         error = TRANSFER_MSGS(dev, msgs);
  404         if (error == 0)
  405                 *word = ((uint16_t)buf[1] << 8) | buf[0];
  406         return (iic2smb_error(error));
  407 }
  408 
  409 static int
  410 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
  411 {
  412         uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
  413         uint8_t out[2];
  414         struct iic_msg msgs[] = {
  415              { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
  416              { slave, IIC_M_RD, nitems(out), out },
  417         };
  418         int error;
  419 
  420         error = TRANSFER_MSGS(dev, msgs);
  421         if (error == 0)
  422                 *rdata = ((uint16_t)out[1] << 8) | out[0];
  423         return (iic2smb_error(error));
  424 }
  425 
  426 static int
  427 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
  428 {
  429         uint8_t bytes[2] = { cmd, count };
  430         struct iic_msg msgs[] = {
  431              { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
  432              { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
  433         };
  434         int error;
  435 
  436         if (count > SMB_MAXBLOCKSIZE || count == 0)
  437                 return (SMB_EINVAL);
  438         error = TRANSFER_MSGS(dev, msgs);
  439         return (iic2smb_error(error));
  440 }
  441 
  442 static int
  443 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
  444 {
  445         struct iic_msg msgs[] = {
  446              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
  447              { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
  448         };
  449         struct iic_msg block_msg[] = {
  450              { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
  451         };
  452         device_t parent = device_get_parent(dev);
  453         int error;
  454 
  455         /* Have to do this because the command is split in two transfers. */
  456         error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE);
  457         if (error == 0)
  458                 error = TRANSFER_MSGS(dev, msgs);
  459         if (error == 0) {
  460                 /*
  461                  * If the slave offers an empty or a too long reply,
  462                  * read one byte to generate the stop or abort.
  463                  */
  464                 if (*count > SMB_MAXBLOCKSIZE || *count == 0)
  465                         block_msg[0].len = 1;
  466                 else
  467                         block_msg[0].len = *count;
  468                 error = TRANSFER_MSGS(dev, block_msg);
  469                 if (*count > SMB_MAXBLOCKSIZE || *count == 0)
  470                         error = SMB_EINVAL;
  471         }
  472         (void)iicbus_release_bus(parent, dev);
  473         return (iic2smb_error(error));
  474 }
  475 
  476 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0);
  477 DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0);
  478 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  479 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
  480 MODULE_VERSION(iicsmb, 1);

Cache object: b3516971182086e93d0938e61b114eeb


[ 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.