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/isl/isl.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) 2015 Michael Gmelin <freebsd@grem.de>
    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$");
   29 
   30 /*
   31  * Driver for intersil I2C ISL29018 Digital Ambient Light Sensor and Proximity
   32  * Sensor with Interrupt Function, only tested connected over SMBus (ig4iic).
   33  *
   34  * Datasheet:
   35  * http://www.intersil.com/en/products/optoelectronics/ambient-light-and-proximity-sensors/light-to-digital-sensors/ISL29018.html
   36  * http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29018.pdf
   37  */
   38 
   39 #include <sys/param.h>
   40 #include <sys/bus.h>
   41 #include <sys/conf.h>
   42 #include <sys/event.h>
   43 #include <sys/fcntl.h>
   44 #include <sys/kernel.h>
   45 #include <sys/lock.h>
   46 #include <sys/lockmgr.h>
   47 #include <sys/malloc.h>
   48 #include <sys/mbuf.h>
   49 #include <sys/module.h>
   50 #include <sys/poll.h>
   51 #include <sys/sx.h>
   52 #include <sys/sysctl.h>
   53 #include <sys/systm.h>
   54 #include <sys/systm.h>
   55 
   56 #include <dev/iicbus/iiconf.h>
   57 #include <dev/iicbus/iicbus.h>
   58 #include <dev/isl/isl.h>
   59 
   60 #include "iicbus_if.h"
   61 #include "bus_if.h"
   62 #include "device_if.h"
   63 
   64 #define ISL_METHOD_ALS          0x10
   65 #define ISL_METHOD_IR           0x11
   66 #define ISL_METHOD_PROX         0x12
   67 #define ISL_METHOD_RESOLUTION   0x13
   68 #define ISL_METHOD_RANGE        0x14
   69 
   70 struct isl_softc {
   71         device_t        dev;
   72         struct sx       isl_sx;
   73 };
   74 
   75 /* Returns < 0 on problem. */
   76 static int isl_read_sensor(device_t dev, uint8_t cmd_mask);
   77 
   78 static int
   79 isl_read_byte(device_t dev, uint8_t reg, uint8_t *val)
   80 {
   81         uint16_t addr = iicbus_get_addr(dev);
   82         struct iic_msg msgs[] = {
   83              { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
   84              { addr, IIC_M_RD, 1, val },
   85         };
   86 
   87         return (iicbus_transfer(dev, msgs, nitems(msgs)));
   88 }
   89 
   90 static int
   91 isl_write_byte(device_t dev, uint8_t reg, uint8_t val)
   92 {
   93         uint16_t addr = iicbus_get_addr(dev);
   94         uint8_t bytes[] = { reg, val };
   95         struct iic_msg msgs[] = {
   96              { addr, IIC_M_WR, nitems(bytes), bytes },
   97         };
   98 
   99         return (iicbus_transfer(dev, msgs, nitems(msgs)));
  100 }
  101 
  102 /*
  103  * Initialize the device
  104  */
  105 static int
  106 init_device(device_t dev, int probe)
  107 {
  108         int error;
  109 
  110         /*
  111          * init procedure: send 0x00 to test ref and cmd reg 1
  112          */
  113         error = isl_write_byte(dev, REG_TEST, 0);
  114         if (error)
  115                 goto done;
  116         error = isl_write_byte(dev, REG_CMD1, 0);
  117         if (error)
  118                 goto done;
  119 
  120         pause("islinit", hz/100);
  121 
  122 done:
  123         if (error && !probe)
  124                 device_printf(dev, "Unable to initialize\n");
  125         return (error);
  126 }
  127 
  128 static int isl_probe(device_t);
  129 static int isl_attach(device_t);
  130 static int isl_detach(device_t);
  131 
  132 static int isl_sysctl(SYSCTL_HANDLER_ARGS);
  133 
  134 static device_method_t isl_methods[] = {
  135         /* device interface */
  136         DEVMETHOD(device_probe,         isl_probe),
  137         DEVMETHOD(device_attach,        isl_attach),
  138         DEVMETHOD(device_detach,        isl_detach),
  139 
  140         DEVMETHOD_END
  141 };
  142 
  143 static driver_t isl_driver = {
  144         "isl",
  145         isl_methods,
  146         sizeof(struct isl_softc),
  147 };
  148 
  149 #if 0
  150 static void
  151 isl_identify(driver_t *driver, device_t parent)
  152 {
  153 
  154         if (device_find_child(parent, "asl", -1)) {
  155                 if (bootverbose)
  156                         printf("asl: device(s) already created\n");
  157                 return;
  158         }
  159 
  160         /* Check if we can communicate to our slave. */
  161         if (init_device(dev, 0x88, 1) == 0)
  162                 BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "isl", -1);
  163 }
  164 #endif
  165 
  166 static int
  167 isl_probe(device_t dev)
  168 {
  169         uint32_t addr = iicbus_get_addr(dev);
  170 
  171         if (addr != 0x88)
  172                 return (ENXIO);
  173         if (init_device(dev, 1) != 0)
  174                 return (ENXIO);
  175         device_set_desc(dev, "ISL Digital Ambient Light Sensor");
  176         return (BUS_PROBE_VENDOR);
  177 }
  178 
  179 static int
  180 isl_attach(device_t dev)
  181 {
  182         struct isl_softc *sc;
  183         struct sysctl_ctx_list *sysctl_ctx;
  184         struct sysctl_oid *sysctl_tree;
  185         int use_als;
  186         int use_ir;
  187         int use_prox;
  188 
  189         sc = device_get_softc(dev);
  190         sc->dev = dev;
  191 
  192         if (init_device(dev, 0) != 0)
  193                 return (ENXIO);
  194 
  195         sx_init(&sc->isl_sx, "ISL read lock");
  196 
  197         sysctl_ctx = device_get_sysctl_ctx(dev);
  198         sysctl_tree = device_get_sysctl_tree(dev);
  199 
  200         use_als = isl_read_sensor(dev, CMD1_MASK_ALS_ONCE) >= 0;
  201         use_ir = isl_read_sensor(dev, CMD1_MASK_IR_ONCE) >= 0;
  202         use_prox = isl_read_sensor(dev, CMD1_MASK_PROX_ONCE) >= 0;
  203 
  204         if (use_als) {
  205                 SYSCTL_ADD_PROC(sysctl_ctx,
  206                     SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "als",
  207                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc,
  208                     ISL_METHOD_ALS, isl_sysctl, "I",
  209                     "Current ALS sensor read-out");
  210         }
  211 
  212         if (use_ir) {
  213                 SYSCTL_ADD_PROC(sysctl_ctx,
  214                     SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "ir",
  215                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc,
  216                     ISL_METHOD_IR, isl_sysctl, "I",
  217                     "Current IR sensor read-out");
  218         }
  219 
  220         if (use_prox) {
  221                 SYSCTL_ADD_PROC(sysctl_ctx,
  222                     SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "prox",
  223                     CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc,
  224                     ISL_METHOD_PROX, isl_sysctl, "I",
  225                     "Current proximity sensor read-out");
  226         }
  227 
  228         SYSCTL_ADD_PROC(sysctl_ctx,
  229             SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "resolution",
  230             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc,
  231             ISL_METHOD_RESOLUTION, isl_sysctl, "I",
  232             "Current proximity sensor resolution");
  233 
  234         SYSCTL_ADD_PROC(sysctl_ctx,
  235             SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "range",
  236             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc,
  237             ISL_METHOD_RANGE, isl_sysctl, "I",
  238             "Current proximity sensor range");
  239 
  240         return (0);
  241 }
  242 
  243 static int
  244 isl_detach(device_t dev)
  245 {
  246         struct isl_softc *sc;
  247 
  248         sc = device_get_softc(dev);
  249         sx_destroy(&sc->isl_sx);
  250 
  251         return (0);
  252 }
  253 
  254 static int
  255 isl_sysctl(SYSCTL_HANDLER_ARGS)
  256 {
  257         static int resolutions[] = { 16, 12, 8, 4};
  258         static int ranges[] = { 1000, 4000, 16000, 64000};
  259 
  260         struct isl_softc *sc;
  261         uint8_t rbyte;
  262         int arg;
  263         int resolution;
  264         int range;
  265 
  266         sc = (struct isl_softc *)oidp->oid_arg1;
  267         arg = -1;
  268 
  269         sx_xlock(&sc->isl_sx);
  270         if (isl_read_byte(sc->dev, REG_CMD2, &rbyte) != 0) {
  271                 sx_xunlock(&sc->isl_sx);
  272                 return (-1);
  273         }
  274         resolution = resolutions[(rbyte & CMD2_MASK_RESOLUTION)
  275                             >> CMD2_SHIFT_RESOLUTION];
  276         range = ranges[(rbyte & CMD2_MASK_RANGE) >> CMD2_SHIFT_RANGE];
  277 
  278         switch (oidp->oid_arg2) {
  279         case ISL_METHOD_ALS:
  280                 arg = (isl_read_sensor(sc->dev,
  281                     CMD1_MASK_ALS_ONCE) * range) >> resolution;
  282                 break;
  283         case ISL_METHOD_IR:
  284                 arg = isl_read_sensor(sc->dev, CMD1_MASK_IR_ONCE);
  285                 break;
  286         case ISL_METHOD_PROX:
  287                 arg = isl_read_sensor(sc->dev, CMD1_MASK_PROX_ONCE);
  288                 break;
  289         case ISL_METHOD_RESOLUTION:
  290                 arg = (1 << resolution);
  291                 break;
  292         case ISL_METHOD_RANGE:
  293                 arg = range;
  294                 break;
  295         }
  296         sx_xunlock(&sc->isl_sx);
  297 
  298         SYSCTL_OUT(req, &arg, sizeof(arg));
  299         return (0);
  300 }
  301 
  302 static int
  303 isl_read_sensor(device_t dev, uint8_t cmd_mask)
  304 {
  305         uint8_t rbyte;
  306         uint8_t cmd;
  307         int ret;
  308 
  309         if (isl_read_byte(dev, REG_CMD1, &rbyte) != 0) {
  310                 device_printf(dev,
  311                     "Couldn't read first byte before issuing command %d\n",
  312                     cmd_mask);
  313                 return (-1);
  314         }
  315 
  316         cmd = (rbyte & 0x1f) | cmd_mask;
  317         if (isl_write_byte(dev, REG_CMD1, cmd) != 0) {
  318                 device_printf(dev, "Couldn't write command %d\n", cmd_mask);
  319                 return (-1);
  320         }
  321 
  322         pause("islconv", hz/10);
  323 
  324         if (isl_read_byte(dev, REG_DATA1, &rbyte) != 0) {
  325                 device_printf(dev,
  326                     "Couldn't read first byte after command %d\n", cmd_mask);
  327                 return (-1);
  328         }
  329 
  330         ret = rbyte;
  331         if (isl_read_byte(dev, REG_DATA2, &rbyte) != 0) {
  332                 device_printf(dev, "Couldn't read second byte after command %d\n", cmd_mask);
  333                 return (-1);
  334         }
  335         ret += rbyte << 8;
  336 
  337         return (ret);
  338 }
  339 
  340 DRIVER_MODULE(isl, iicbus, isl_driver, NULL, NULL);
  341 MODULE_DEPEND(isl, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
  342 MODULE_VERSION(isl, 1);

Cache object: dddd879f1e42dfeb779bc484b6c5735f


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