summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2005-11-15 22:23:21 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2005-11-15 22:23:21 +0000
commit863910fb9ce4b2b88c3dbca36d2cdc727c3f22fc (patch)
tree9f19a0ac6d0a0d1ba462c086babdf61839a92de0 /sys
parent817f562ae06104eb92f14b18acb8d89925f9bde6 (diff)
Driver for the TAOS TSL2560/61 light sensors.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/i2c/files.i2c7
-rw-r--r--sys/dev/i2c/tsl2560.c236
2 files changed, 242 insertions, 1 deletions
diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c
index 37651084596..6b4e0bfcdff 100644
--- a/sys/dev/i2c/files.i2c
+++ b/sys/dev/i2c/files.i2c
@@ -1,4 +1,4 @@
-# $OpenBSD: files.i2c,v 1.9 2005/11/15 18:28:05 deraadt Exp $
+# $OpenBSD: files.i2c,v 1.10 2005/11/15 22:23:20 kettenis Exp $
# $NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $
define i2c {[addr = -1], [size = -1]}
@@ -33,3 +33,8 @@ file dev/i2c/maxim6690.c maxtmp
device adc
attach adc at i2c
file dev/i2c/ad741x.c adc
+
+# TAOS TSL2560/61 light-to-digital converter
+device tsl
+attach tsl at i2c
+file dev/i2c/tsl2560.c tsl
diff --git a/sys/dev/i2c/tsl2560.c b/sys/dev/i2c/tsl2560.c
new file mode 100644
index 00000000000..a72cf8bd881
--- /dev/null
+++ b/sys/dev/i2c/tsl2560.c
@@ -0,0 +1,236 @@
+/* $OpenBSD: tsl2560.c,v 1.1 2005/11/15 22:23:20 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2005 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+
+#include <dev/i2c/i2cvar.h>
+
+/* TSL2560/61 registers */
+#define TSL2560_REG_CONTROL 0x80
+#define TSL2560_CONTROL_POWER 0x03
+#define TSL2560_REG_TIMING 0x81
+#define TSL2560_TIMING_GAIN 0x10 /* high gain (16x) */
+#define TSL2560_TIMING_INTEG0 0x00 /* 13.7ms */
+#define TSL2560_TIMING_INTEG1 0x01 /* 101ms */
+#define TSL2560_TIMING_INTEG2 0x02 /* 402ms */
+#define TSL2560_REG_ID 0x8a
+#define TSL2560_REG_DATA0 0xac
+#define TSL2560_REG_DATA1 0xae
+
+struct tsl_softc {
+ struct device sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+
+ struct sensor sc_sensor;
+};
+
+int tsl_match(struct device *, void *, void *);
+void tsl_attach(struct device *, struct device *, void *);
+
+void tsl_refresh(void *);
+u_int64_t tsl_lux(u_int32_t, u_int32_t);
+
+struct cfattach tsl_ca = {
+ sizeof(struct tsl_softc), tsl_match, tsl_attach
+};
+
+struct cfdriver tsl_cd = {
+ NULL, "tsl", DV_DULL
+};
+
+int
+tsl_match(struct device *parent, void *match, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+
+ if (ia->ia_compat) {
+ if (strcmp(ia->ia_compat, "tsl2560") == 0)
+ return (1);
+ return (0);
+ }
+ return (1); /* accept the address given */
+}
+
+void
+tsl_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct tsl_softc *sc = (struct tsl_softc *)self;
+ struct i2c_attach_args *ia = aux;
+ u_int8_t cmd, data;
+
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+
+ iic_acquire_bus(sc->sc_tag, 0);
+ cmd = TSL2560_REG_CONTROL; data = TSL2560_CONTROL_POWER;
+ if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+ sc->sc_addr, &cmd, 1, &data, 1, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ printf(": power up failed\n");
+ return;
+ }
+ cmd = TSL2560_REG_TIMING;
+ data = TSL2560_TIMING_GAIN | TSL2560_TIMING_INTEG2;
+ if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+ sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ printf(": cannot write timing register\n");
+ return;
+ }
+ cmd = TSL2560_REG_ID;
+ if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+ sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ printf(": cannot read ID register\n");
+ return;
+ }
+ iic_release_bus(sc->sc_tag, 0);
+
+ switch (data >> 4) {
+ case 0:
+ printf(": TSL2560 rev %x", data & 0x0f);
+ break;
+ case 1:
+ printf(": TSL2561 rev %x", data & 0x0f);
+ break;
+ default:
+ printf(": unknown part number %x", data >> 4);
+ break;
+ }
+
+ /* Initialize sensor data. */
+ strlcpy(sc->sc_sensor.device, sc->sc_dev.dv_xname,
+ sizeof(sc->sc_sensor.device));
+ sc->sc_sensor.type = SENSOR_LUX;
+ strlcpy(sc->sc_sensor.desc, "LUX", sizeof(sc->sc_sensor.desc));
+
+ if (sensor_task_register(sc, tsl_refresh, 5)) {
+ printf(": unable to register update task\n");
+ return;
+ }
+
+ SENSOR_ADD(&sc->sc_sensor);
+
+ printf("\n");
+}
+
+void
+tsl_refresh(void *arg)
+{
+ struct tsl_softc *sc = arg;
+ u_int8_t cmd, data[2];
+ u_int16_t chan0, chan1;
+
+ iic_acquire_bus(sc->sc_tag, 0);
+ cmd = TSL2560_REG_DATA0;
+ if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+ sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ sc->sc_sensor.flags |= SENSOR_FINVALID;
+ return;
+ }
+ chan0 = data[1] << 8 | data[0];
+ cmd = TSL2560_REG_DATA1;
+ if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+ sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
+ iic_release_bus(sc->sc_tag, 0);
+ sc->sc_sensor.flags |= SENSOR_FINVALID;
+ return;
+ }
+ chan1 = data[1] << 8 | data[0];
+ iic_release_bus(sc->sc_tag, 0);
+
+ sc->sc_sensor.value = tsl_lux(chan0, chan1);
+ sc->sc_sensor.flags &= ~SENSOR_FINVALID;
+}
+
+/* Precision for fixed-point math. */
+#define TSL2560_RATIO_SCALE 9
+#define TSL2560_LUX_SCALE 14
+
+/*
+ * The TSL2560/2561 comes in a ChipScale or TMB-6 package and the
+ * calibration is slightly different for each package. The constants
+ * below are for the TMB-6 package.
+ */
+
+#define TSL2560_K1T 0x0040 /* 0.125 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B1T 0x01f2 /* 0.0304 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M1T 0x01be /* 0.0272 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K2T 0x0080 /* 0.250 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B2T 0x0214 /* 0.0324 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M2T 0x02d1 /* 0.0440 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K3T 0x00c0 /* 0.375 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B3T 0x023f /* 0.0351 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M3T 0x037b /* 0.0544 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K4T 0x0080 /* 0.50 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B4T 0x0214 /* 0.0381 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M4T 0x02d1 /* 0.0624 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K5T 0x0138 /* 0.61 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B5T 0x016f /* 0.0224 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M5T 0x01fc /* 0.0310 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K6T 0x0100 /* 0.80 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B6T 0x0270 /* 0.0128 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M6T 0x03fe /* 0.0153 * (1 << TSL2560_LUX_SCALE) */
+
+#define TSL2560_K7T 0x019a /* 1.3 * (1 << TSL2560_RATIO_SCALE) */
+#define TSL2560_B7T 0x0018 /* 0.00146 * (1 << TSL2560_LUX_SCALE) */
+#define TSL2560_M7T 0x0012 /* 0.00112 * (1 << TSL2560_LUX_SCALE) */
+
+u_int64_t
+tsl_lux(u_int32_t chan0, u_int32_t chan1)
+{
+ u_int32_t ratio, ratio1;
+ u_int32_t b, m;
+ int64_t lux;
+
+ ratio1 = 0;
+ if (chan0 != 0)
+ ratio1 = (chan1 << (TSL2560_RATIO_SCALE + 1)) / chan0;
+ ratio = (ratio1 + 1) >> 1;
+
+ b = 0, m = 0;
+ if (ratio <= TSL2560_K1T)
+ b = TSL2560_B1T, m = TSL2560_M1T;
+ else if (ratio <= TSL2560_K2T)
+ b = TSL2560_B2T, m = TSL2560_M2T;
+ else if (ratio <= TSL2560_K3T)
+ b = TSL2560_B3T, m = TSL2560_M3T;
+ else if (ratio <= TSL2560_K4T)
+ b = TSL2560_B4T, m = TSL2560_M4T;
+ else if (ratio <= TSL2560_K5T)
+ b = TSL2560_B5T, m = TSL2560_M5T;
+ else if (ratio <= TSL2560_K6T)
+ b = TSL2560_B6T, m = TSL2560_M6T;
+ else if (ratio <= TSL2560_K7T)
+ b = TSL2560_B7T, m = TSL2560_M7T;
+
+ lux = b * chan0 - m * chan1;
+ if (lux < 0)
+ lux = 0;
+ return ((lux * 1000000) >> TSL2560_LUX_SCALE);
+}