root/dev/i2c/tsl2560.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tsl_match
  2. tsl_attach
  3. tsl_refresh
  4. tsl_lux

    1 /*      $OpenBSD: tsl2560.c,v 1.6 2007/06/24 05:34:35 dlg Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2005 Mark Kettenis
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/param.h>
   20 #include <sys/systm.h>
   21 #include <sys/device.h>
   22 #include <sys/sensors.h>
   23 
   24 #include <dev/i2c/i2cvar.h>
   25 
   26 /* TSL2560/61 registers */
   27 #define TSL2560_REG_CONTROL     0x80
   28 #define  TSL2560_CONTROL_POWER  0x03
   29 #define TSL2560_REG_TIMING      0x81
   30 #define  TSL2560_TIMING_GAIN    0x10 /* high gain (16x) */
   31 #define  TSL2560_TIMING_INTEG0  0x00 /* 13.7ms */
   32 #define  TSL2560_TIMING_INTEG1  0x01 /* 101ms */
   33 #define  TSL2560_TIMING_INTEG2  0x02 /* 402ms */
   34 #define TSL2560_REG_ID          0x8a
   35 #define TSL2560_REG_DATA0       0xac
   36 #define TSL2560_REG_DATA1       0xae
   37 
   38 struct tsl_softc {
   39         struct device sc_dev;
   40         i2c_tag_t sc_tag;
   41         i2c_addr_t sc_addr;
   42 
   43         struct ksensor sc_sensor;
   44         struct ksensordev sc_sensordev;
   45 };
   46 
   47 int     tsl_match(struct device *, void *, void *);
   48 void    tsl_attach(struct device *, struct device *, void *);
   49 
   50 void    tsl_refresh(void *);
   51 u_int64_t tsl_lux(u_int32_t, u_int32_t);
   52 
   53 struct cfattach tsl_ca = {
   54         sizeof(struct tsl_softc), tsl_match, tsl_attach
   55 };
   56 
   57 struct cfdriver tsl_cd = {
   58         NULL, "tsl", DV_DULL
   59 };
   60 
   61 int
   62 tsl_match(struct device *parent, void *match, void *aux)
   63 {
   64         struct i2c_attach_args *ia = aux;
   65 
   66         if (strcmp(ia->ia_name, "tsl2560") == 0)
   67                 return (1);
   68         return (0);
   69 }
   70 
   71 void
   72 tsl_attach(struct device *parent, struct device *self, void *aux)
   73 {
   74         struct tsl_softc *sc = (struct tsl_softc *)self;
   75         struct i2c_attach_args *ia = aux;
   76         u_int8_t cmd, data;
   77 
   78         sc->sc_tag = ia->ia_tag;
   79         sc->sc_addr = ia->ia_addr;
   80 
   81         iic_acquire_bus(sc->sc_tag, 0);
   82         cmd = TSL2560_REG_CONTROL; data = TSL2560_CONTROL_POWER;
   83         if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
   84                      sc->sc_addr, &cmd, 1, &data, 1, 0)) {
   85                 iic_release_bus(sc->sc_tag, 0);
   86                 printf(": power up failed\n");
   87                 return;
   88         }
   89         cmd = TSL2560_REG_TIMING;
   90         data = TSL2560_TIMING_GAIN | TSL2560_TIMING_INTEG2;
   91         if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
   92                      sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
   93                 iic_release_bus(sc->sc_tag, 0);
   94                 printf(": cannot write timing register\n");
   95                 return;
   96         }
   97         cmd = TSL2560_REG_ID;
   98         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
   99                      sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
  100                 iic_release_bus(sc->sc_tag, 0);
  101                 printf(": cannot read ID register\n");
  102                 return;
  103         }
  104         iic_release_bus(sc->sc_tag, 0);
  105 
  106         switch (data >> 4) {
  107         case 0:
  108                 printf(": TSL2560 rev %x", data & 0x0f);
  109                 break;
  110         case 1:
  111                 printf(": TSL2561 rev %x", data & 0x0f);
  112                 break;
  113         default:
  114                 printf(": unknown part number %x", data >> 4);
  115                 break;
  116         }
  117 
  118         /* Initialize sensor data. */
  119         strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
  120             sizeof(sc->sc_sensordev.xname));
  121         sc->sc_sensor.type = SENSOR_LUX;
  122 
  123         if (sensor_task_register(sc, tsl_refresh, 5) == NULL) {
  124                 printf(": unable to register update task\n");
  125                 return;
  126         }
  127 
  128         sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
  129         sensordev_install(&sc->sc_sensordev);
  130 
  131         printf("\n");
  132 }
  133 
  134 void
  135 tsl_refresh(void *arg)
  136 {
  137         struct tsl_softc *sc = arg;
  138         u_int8_t cmd, data[2];
  139         u_int16_t chan0, chan1;
  140 
  141         iic_acquire_bus(sc->sc_tag, 0);
  142         cmd = TSL2560_REG_DATA0;
  143         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  144                      sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
  145                 iic_release_bus(sc->sc_tag, 0);
  146                 sc->sc_sensor.flags |= SENSOR_FINVALID;
  147                 return;
  148         }
  149         chan0 = data[1] << 8 | data[0];
  150         cmd = TSL2560_REG_DATA1;
  151         if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
  152                      sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
  153                 iic_release_bus(sc->sc_tag, 0);
  154                 sc->sc_sensor.flags |= SENSOR_FINVALID;
  155                 return;
  156         }
  157         chan1 = data[1] << 8 | data[0];
  158         iic_release_bus(sc->sc_tag, 0);
  159 
  160         sc->sc_sensor.value = tsl_lux(chan0, chan1);
  161         sc->sc_sensor.flags &= ~SENSOR_FINVALID;
  162 }
  163 
  164 /* Precision for fixed-point math. */
  165 #define TSL2560_RATIO_SCALE     9
  166 #define TSL2560_LUX_SCALE       14
  167 
  168 /*
  169  * The TSL2560/2561 comes in a ChipScale or TMB-6 package and the
  170  * calibration is slightly different for each package.  The constants
  171  * below are for the TMB-6 package.
  172  */
  173 
  174 #define TSL2560_K1T     0x0040  /* 0.125 * (1 << TSL2560_RATIO_SCALE) */
  175 #define TSL2560_B1T     0x01f2  /* 0.0304 * (1 << TSL2560_LUX_SCALE) */
  176 #define TSL2560_M1T     0x01be  /* 0.0272 * (1 << TSL2560_LUX_SCALE) */
  177 
  178 #define TSL2560_K2T     0x0080  /* 0.250 * (1 << TSL2560_RATIO_SCALE) */
  179 #define TSL2560_B2T     0x0214  /* 0.0324 * (1 << TSL2560_LUX_SCALE) */
  180 #define TSL2560_M2T     0x02d1  /* 0.0440 * (1 << TSL2560_LUX_SCALE) */
  181 
  182 #define TSL2560_K3T     0x00c0  /* 0.375 * (1 << TSL2560_RATIO_SCALE) */
  183 #define TSL2560_B3T     0x023f  /* 0.0351 * (1 << TSL2560_LUX_SCALE) */
  184 #define TSL2560_M3T     0x037b  /* 0.0544 * (1 << TSL2560_LUX_SCALE) */
  185 
  186 #define TSL2560_K4T     0x0080  /* 0.50 * (1 << TSL2560_RATIO_SCALE) */
  187 #define TSL2560_B4T     0x0214  /* 0.0381 * (1 << TSL2560_LUX_SCALE) */
  188 #define TSL2560_M4T     0x02d1  /* 0.0624 * (1 << TSL2560_LUX_SCALE) */
  189 
  190 #define TSL2560_K5T     0x0138  /* 0.61 * (1 << TSL2560_RATIO_SCALE) */
  191 #define TSL2560_B5T     0x016f  /* 0.0224 * (1 << TSL2560_LUX_SCALE) */
  192 #define TSL2560_M5T     0x01fc  /* 0.0310 * (1 << TSL2560_LUX_SCALE) */
  193 
  194 #define TSL2560_K6T     0x0100  /* 0.80 * (1 << TSL2560_RATIO_SCALE) */
  195 #define TSL2560_B6T     0x0270  /* 0.0128 * (1 << TSL2560_LUX_SCALE) */
  196 #define TSL2560_M6T     0x03fe  /* 0.0153 * (1 << TSL2560_LUX_SCALE) */
  197 
  198 #define TSL2560_K7T     0x019a  /* 1.3 * (1 << TSL2560_RATIO_SCALE) */
  199 #define TSL2560_B7T     0x0018  /* 0.00146 * (1 << TSL2560_LUX_SCALE) */
  200 #define TSL2560_M7T     0x0012  /* 0.00112 * (1 << TSL2560_LUX_SCALE) */
  201 
  202 u_int64_t
  203 tsl_lux(u_int32_t chan0, u_int32_t chan1)
  204 {
  205         u_int32_t ratio, ratio1;
  206         u_int32_t b, m;
  207         int64_t lux;
  208 
  209         ratio1 = 0;
  210         if (chan0 != 0)
  211                 ratio1 = (chan1 << (TSL2560_RATIO_SCALE + 1)) / chan0;
  212         ratio = (ratio1 + 1) >> 1;
  213 
  214         b = 0, m = 0;
  215         if (ratio <= TSL2560_K1T)
  216                 b = TSL2560_B1T, m = TSL2560_M1T;
  217         else if (ratio <= TSL2560_K2T)
  218                 b = TSL2560_B2T, m = TSL2560_M2T;
  219         else if (ratio <= TSL2560_K3T)
  220                 b = TSL2560_B3T, m = TSL2560_M3T;
  221         else if (ratio <= TSL2560_K4T)
  222                 b = TSL2560_B4T, m = TSL2560_M4T;
  223         else if (ratio <= TSL2560_K5T)
  224                 b = TSL2560_B5T, m = TSL2560_M5T;
  225         else if (ratio <= TSL2560_K6T)
  226                 b = TSL2560_B6T, m = TSL2560_M6T;
  227         else if (ratio <= TSL2560_K7T)
  228                 b = TSL2560_B7T, m = TSL2560_M7T;
  229 
  230         lux = b * chan0 - m * chan1;
  231         if (lux < 0)
  232                 lux = 0;
  233         return ((lux * 1000000) >> TSL2560_LUX_SCALE);
  234 }

/* [<][>][^][v][top][bottom][index][help] */