tech-misc archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Ambient light drivers



Hi.

Together with Grégoire we tried to get an ambient light sensor on his laptop
to report something for the operating system. But we failed.

Couple of drivers were written in the process. I'll post them here so that
these are archived if someone finds them useful some day. The first one is
an ACPI driver identified with the ACPI0008 PNP string. The second one is
an I2C driver for the ISL29018 chip from Intersil.

- Jukka
/*      $NetBSD$ */

/*-
 * Copyright (c) 2011 Jukka Ruohonen <jruohonen%iki.fi@localhost>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_als.c,v 1.4 2011/02/16 09:05:12 xxx Exp $");

#include <sys/param.h>
#include <sys/module.h>
#include <sys/mutex.h>

#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>

#include <dev/sysmon/sysmonvar.h>

#define _COMPONENT              ACPI_RESOURCE_COMPONENT
ACPI_MODULE_NAME                ("acpi_als")

#define ACPI_ALS_SENSOR_ILLUM   0
#define ACPI_ALS_SENSOR_TEMP    1
#define ACPI_ALS_SENSOR_CHROM_X 2
#define ACPI_ALS_SENSOR_CHROM_Y 3
#define ACPI_ALS_SENSOR_COUNT   4

#define ACPI_ALS_CHROM_X(x)     (((x) & 0xffff0000) >> 16)
#define ACPI_ALS_CHROM_Y(y)     (((y) & 0x0000ffff))

#define ACPI_ALS_CHROM_MAX      0x2710
#define ACPI_ALS_CHROM_TOPER(x) (((x) * 100) / ACPI_ALS_CHROM_MAX)

#define ACPI_ALS_NOTIFY_ILLUM   0x80
#define ACPI_ALS_NOTIFY_TEMP    0x81
#define ACPI_ALS_NOTIFY_RESP    0x82

struct acpials_softc {
        device_t                 sc_dev;
        struct acpi_devnode     *sc_node;
        struct sysmon_envsys    *sc_sme;
        envsys_data_t            sc_sensor[ACPI_ALS_SENSOR_COUNT];
        kmutex_t                 sc_mtx;
};

const char * const acpi_als_ids[] = {
        "ACPI0008",
        NULL
};

static int      acpials_match(device_t, cfdata_t, void *);
static void     acpials_attach(device_t, device_t, void *);
static int      acpials_detach(device_t, int);
static int      acpials_sensor_init(device_t);
static int32_t  acpials_sensor_get(device_t, const char *);
static int32_t  acpials_sensor_get_illuminance(device_t);
static int32_t  acpials_sensor_get_temperature(device_t);
static int32_t  acpials_sensor_get_chromaticity(device_t);
static void     acpials_sensor_refresh(struct sysmon_envsys *,envsys_data_t *);
static void     acpials_sensor_update(device_t, envsys_data_t *);
static void     acpials_notify_handler(ACPI_HANDLE, uint32_t, void *);

CFATTACH_DECL_NEW(acpials, sizeof(struct acpials_softc),
    acpials_match, acpials_attach, acpials_detach, NULL);

static int
acpials_match(device_t parent, cfdata_t match, void *aux)
{
        struct acpi_attach_args *aa = aux;

        if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
                return 0;

        return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_als_ids);
}

static void
acpials_attach(device_t parent, device_t self, void *aux)
{
        struct acpials_softc *sc = device_private(self);
        struct acpi_attach_args *aa = aux;
        ACPI_INTEGER val = 0;
        ACPI_STATUS rv;

        sc->sc_sme = NULL;
        sc->sc_dev = self;
        sc->sc_node = aa->aa_node;

        aprint_naive("\n");
        aprint_normal(": ACPI Ambient Light Sensor\n");

        mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE);

        (void)acpials_sensor_init(self);
        (void)pmf_device_register(self, NULL, NULL);
        (void)acpi_register_notify(sc->sc_node, acpials_notify_handler);

        rv = acpi_eval_integer(sc->sc_node->ad_handle, "_ALP", &val);

        if (ACPI_SUCCESS(rv) && val != 0 && val < INT_MAX) {

                aprint_debug_dev(self, "polling interval "
                    "%d.%d seconds\n", (int)val / 10, (int)val % 10);
        }
}

static int
acpials_detach(device_t self, int flags)
{
        struct acpials_softc *sc = device_private(self);

        pmf_device_deregister(self);
        acpi_deregister_notify(sc->sc_node);

        if (sc->sc_sme != NULL)
                sysmon_envsys_unregister(sc->sc_sme);

        mutex_destroy(&sc->sc_mtx);

        return 0;
}

static int
acpials_sensor_init(device_t self)
{
        struct acpials_softc *sc = device_private(self);
        envsys_data_t *sensor;
        int32_t val;

        sc->sc_sme = sysmon_envsys_create();

        sc->sc_sme->sme_cookie = sc;
        sc->sc_sme->sme_flags = SME_POLL_ONLY;
        sc->sc_sme->sme_name = device_xname(self);
        sc->sc_sme->sme_refresh = acpials_sensor_refresh;

        val = acpials_sensor_get_illuminance(self);

        if (val != -1) {

                sensor = &sc->sc_sensor[ACPI_ALS_SENSOR_ILLUM];

                sensor->state = ENVSYS_SVALID;
                sensor->units = ENVSYS_INTEGER;
                sensor->value_cur = val;

                (void)strlcpy(sensor->desc, "illuminance", ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        val = acpials_sensor_get_temperature(self);

        if (val != -1) {

                sensor = &sc->sc_sensor[ACPI_ALS_SENSOR_TEMP];

                sensor->state = ENVSYS_SVALID;
                sensor->units = ENVSYS_STEMP;
                sensor->value_cur = val;

                (void)strlcpy(sensor->desc, "temperature", ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        val = acpials_sensor_get_chromaticity(self);

        if (val != -1) {

                sensor = &sc->sc_sensor[ACPI_ALS_SENSOR_CHROM_X];

                sensor->state = ENVSYS_SVALID;
                sensor->units = ENVSYS_INTEGER;

                val = ACPI_ALS_CHROM_X(val);
                sensor->value_cur = ACPI_ALS_CHROM_TOPER(val);

                (void)strlcpy(sensor->desc, "x-chromaticity", ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);

                sensor = &sc->sc_sensor[ACPI_ALS_SENSOR_CHROM_Y];

                sensor->state = ENVSYS_SVALID;
                sensor->units = ENVSYS_INTEGER;

                val = ACPI_ALS_CHROM_Y(val);
                sensor->value_cur = ACPI_ALS_CHROM_TOPER(val);

                (void)strlcpy(sensor->desc, "y-chromaticity", ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        if (sc->sc_sme->sme_nsensors == 0) {
                aprint_error_dev(self, "no sensors found\n");
                sysmon_envsys_destroy(sc->sc_sme);
                sc->sc_sme = NULL;
                return -1;
        }

        return sysmon_envsys_register(sc->sc_sme);
}

static int32_t
acpials_sensor_get(device_t self, const char *path)
{
        struct acpials_softc *sc = device_private(self);
        ACPI_INTEGER val = 0;
        ACPI_STATUS rv;

        rv = acpi_eval_integer(sc->sc_node->ad_handle, path, &val);

        if (ACPI_FAILURE(rv))
                goto fail;

        if (val == 0 || val > INT32_MAX) {
                rv = AE_BAD_VALUE;
                goto fail;
        }

        return val;
fail:
        aprint_debug_dev(self, "failed to evaluate "
            "%s: %s\n", path, AcpiFormatException(rv));

        return -1;
}

static int32_t
acpials_sensor_get_illuminance(device_t self)
{
        /*
         * The _ALI method returns the current ambient light
         * illuminance reading in units of lux (lumen per
         * square meter). The expected values range from about
         * one lux (dark room) to 10000 lux (outdoor daylight).
         */
        return acpials_sensor_get(self, "_ALI");
}

static int32_t
acpials_sensor_get_temperature(device_t self)
{
        int32_t val;

        /*
         * The _ALT method returns the ambient light color
         * temperature in degrees of Kelvin. Lower values
         * indicate warmer light (yellow, red), whereas higher
         * values imply a colder light (blue). According to
         * the specification, the expected range is somewhere
         * near 1500 K (candlelight) to 5500 K (sunlight).
         */
        val = acpials_sensor_get(self, "_ALT");

        return (val < 0) ? -1 : val * 1000000;
}

static int32_t
acpials_sensor_get_chromaticity(device_t self)
{
        int32_t val;

        /*
         * The _ALC method returns the current ambient light
         * chromaticity reading per the CIE Yxy color model.
         * The encoding uses 32-bit integer value, where the
         * x and y coordinates are stored in the high and low
         * words, respectively. Valid values are specified to
         * range from 0 (0x0000) to 1 (0x2710) per word.
         */
        val = acpials_sensor_get(self, "_ALC");

        if (val < 0)
                return -1;

        if (ACPI_ALS_CHROM_X(val) > ACPI_ALS_CHROM_MAX ||
            ACPI_ALS_CHROM_Y(val) > ACPI_ALS_CHROM_MAX)
                return -1;

        return val;
}

static void
acpials_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
        struct acpials_softc *sc = sme->sme_cookie;

        mutex_enter(&sc->sc_mtx);
        acpials_sensor_update(sc->sc_dev, edata);
        mutex_exit(&sc->sc_mtx);
}

static void
acpials_sensor_update(device_t self, envsys_data_t *sensor)
{
        int32_t val;

        sensor->state = ENVSYS_SINVALID;

        switch (sensor->units) {

        case ENVSYS_STEMP:

                val = acpials_sensor_get_temperature(self);

                if (val < 0)
                        return;

                sensor->state = ENVSYS_SVALID;
                sensor->value_cur = val;
                break;

        case ENVSYS_INTEGER:

                if (strcmp(sensor->desc, "illuminance") == 0) {

                        val = acpials_sensor_get_illuminance(self);

                        if (val < 0)
                                return;

                        sensor->state = ENVSYS_SVALID;
                        sensor->value_cur = val;
                        break;
                }

                if (strcmp(sensor->desc, "x-chromaticity") == 0) {

                        val = acpials_sensor_get_chromaticity(self);

                        if (val < 0)
                                return;

                        sensor->state = ENVSYS_SVALID;

                        val = ACPI_ALS_CHROM_X(val);
                        sensor->value_cur = ACPI_ALS_CHROM_TOPER(val);
                        break;
                }

                if (strcmp(sensor->desc, "y-chromaticity") == 0) {

                        val = acpials_sensor_get_chromaticity(self);

                        if (val < 0)
                                return;

                        sensor->state = ENVSYS_SVALID;

                        val = ACPI_ALS_CHROM_X(val);
                        sensor->value_cur = ACPI_ALS_CHROM_TOPER(val);
                        break;
                }
        }
}

static void
acpials_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
{
        struct acpials_softc *sc;
        envsys_data_t *sensor;
        device_t self = aux;
        size_t i;

        sc = device_private(self);

        switch (evt) {

        case ACPI_ALS_NOTIFY_ILLUM:
                sensor = &sc->sc_sensor[ACPI_ALS_SENSOR_ILLUM];

                mutex_enter(&sc->sc_mtx);
                acpials_sensor_update(self, sensor);
                mutex_exit(&sc->sc_mtx);
                break;

        case ACPI_ALS_NOTIFY_TEMP:

                i = ACPI_ALS_SENSOR_TEMP;

                mutex_enter(&sc->sc_mtx);

                while (i < __arraycount(sc->sc_sensor)) {

                        sensor = &sc->sc_sensor[i];
                        acpials_sensor_update(self, sensor);

                        i++;
                }

                mutex_exit(&sc->sc_mtx);
                break;

        case ACPI_ALS_NOTIFY_RESP: /* AE_SUPPORT */
                break;

        default:
                aprint_debug_dev(self, "unknown notify 0x%02X\n", evt);
        }
}

MODULE(MODULE_CLASS_DRIVER, acpials, NULL);

#ifdef _MODULE
#include "ioconf.c"
#endif

static int
acpials_modcmd(modcmd_t cmd, void *aux)
{
        int rv = 0;

        switch (cmd) {

        case MODULE_CMD_INIT:

#ifdef _MODULE
                rv = config_init_component(cfdriver_ioconf_acpials,
                    cfattach_ioconf_acpials, cfdata_ioconf_acpials);
#endif
                break;

        case MODULE_CMD_FINI:

#ifdef _MODULE
                rv = config_fini_component(cfdriver_ioconf_acpials,
                    cfattach_ioconf_acpials, cfdata_ioconf_acpials);
#endif
                break;

        default:
                rv = ENOTTY;
        }

        return rv;
}
/*      $NetBSD$ */

/*-
 * Copyright (c) 2011 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jukka Ruohonen Gregoire Sutre.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_fan.c,v 1.6 2011/06/21 09:49:05 jruoho Exp $");

#include <sys/param.h>
#include <sys/device.h>

#include <dev/i2c/i2cvar.h>
#include <dev/i2c/isl29018reg.h>

#include <dev/sysmon/sysmonvar.h>

#define ISL_SENSOR_IR            0
#define ISL_SENSOR_ALS           1
#define ISL_SENSOR_PROX          2
#define ISL_SENSOR_COUNT         3

#define ISL_IR_NAME             "infrared light"
#define ISL_ALS_NAME            "ambient light"
#define ISL_PROX_NAME           "proximity"

struct isl_softc {
        device_t                 sc_dev;
        i2c_tag_t                sc_tag;
        i2c_addr_t               sc_addr;
        uint8_t                  sc_resol;
        uint16_t                 sc_range;
        envsys_data_t            sc_sensor[ISL_SENSOR_COUNT];
        struct sysmon_envsys    *sc_sme;
};

static int      isl_match(device_t, cfdata_t, void *);
static void     isl_attach(device_t, device_t, void *);
static int      isl_detach(device_t, int);
static bool     isl_init(device_t);
static bool     isl_init_sensors(device_t);
static bool     isl_read(device_t, uint8_t, uint8_t *);
static bool     isl_read_ir(device_t, uint16_t *);
static bool     isl_read_als(device_t, uint16_t *);
static bool     isl_read_prox(device_t, uint16_t *);
static bool     isl_read_sensor(device_t, uint8_t, uint16_t *);
static bool     isl_write(device_t, uint8_t, uint8_t);
static bool     isl_power(device_t, bool);
static void     isl_refresh(struct sysmon_envsys *, envsys_data_t *);
static bool     isl_suspend(device_t, const pmf_qual_t *);
static bool     isl_resume(device_t, const pmf_qual_t *);

CFATTACH_DECL_NEW(isl, sizeof(struct isl_softc),
    isl_match, isl_attach, isl_detach, NULL);

static int
isl_match(device_t parent, cfdata_t cf, void *aux)
{
        struct i2c_attach_args *ia = aux;

        if ((ia->ia_addr & ISL_ADDRMASK) != ISL_ADDR)
                return 0;

        return 1;
}

static void
isl_attach(device_t parent, device_t self, void *aux)
{
        struct isl_softc *sc = device_private(self);
        struct i2c_attach_args *ia = aux;

        sc->sc_dev = self;
        sc->sc_sme = NULL;

        sc->sc_tag = ia->ia_tag;
        sc->sc_addr = ia->ia_addr;

        if (isl_init(self) != true) {
                aprint_error(": failed to initialize device\n");
                return;
        }

        if (isl_init_sensors(self) != true) {
                aprint_error(": failed to initialize sensors\n");
                return;
        }

        aprint_naive("\n");
        aprint_normal(": ambient light sensor\n");
        aprint_verbose_dev(self, "ISL29018, %u bit ADC resolution, "
            "%u full scale lux range\n", sc->sc_resol, sc->sc_range);

        (void)pmf_device_register(self, isl_suspend, isl_resume);
}

static int
isl_detach(device_t self, int flags)
{
        struct isl_softc *sc = device_private(self);

        (void)isl_power(self, false);

        if (sc->sc_sme != NULL)
                sysmon_envsys_unregister(sc->sc_sme);

        return 0;
}

static bool
isl_init(device_t self)
{
        struct isl_softc *sc = device_private(self);
        uint8_t val;

        if (isl_power(self, true) != true)
                return false;

        if (isl_read(self, ISL_REG_CMD2, &val) != true)
                return false;

        switch (__SHIFTOUT(val, ISL_CMD2_RANGE_MASK)) {

        case ISL_CMD2_RANGE_1:
                sc->sc_range = 1000;
                break;

        case ISL_CMD2_RANGE_2:
                sc->sc_range = 4000;
                break;

        case ISL_CMD2_RANGE_3:
                sc->sc_range = 16000;
                break;

        case ISL_CMD2_RANGE_4:
                sc->sc_range = 64000;
                break;
        }

        switch (__SHIFTOUT(val, ISL_CMD2_RESOL_MASK)) {

        case ISL_CMD2_RESOL_4:
                sc->sc_resol = 4;
                break;

        case ISL_CMD2_RESOL_8:
                sc->sc_resol = 8;
                break;

        case ISL_CMD2_RESOL_12:
                sc->sc_resol = 12;
                break;

        case ISL_CMD2_RESOL_16:
                sc->sc_resol = 16;
                break;
        }

        return true;
}

static bool
isl_init_sensors(device_t self)
{
        struct isl_softc *sc = device_private(self);
        envsys_data_t *sensor;
        uint16_t val;

        sc->sc_sme = sysmon_envsys_create();

        sc->sc_sme->sme_cookie = sc;
        sc->sc_sme->sme_flags = SME_POLL_ONLY;
        sc->sc_sme->sme_refresh = isl_refresh;
        sc->sc_sme->sme_name = device_xname(self);

        if (isl_read_ir(self, &val) != false) {

                sensor = &sc->sc_sensor[ISL_SENSOR_IR];

                sensor->value_cur = val;
                sensor->units = ENVSYS_INTEGER;
                sensor->state = ENVSYS_SVALID;

                (void)strlcpy(sensor->desc, ISL_IR_NAME, ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        if (isl_read_als(self, &val) != false) {

                sensor = &sc->sc_sensor[ISL_SENSOR_ALS];

                sensor->value_cur = val;
                sensor->units = ENVSYS_INTEGER;
                sensor->state = ENVSYS_SVALID;

                (void)strlcpy(sensor->desc, ISL_ALS_NAME, ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        if (isl_read_prox(self, &val) != false) {

                sensor = &sc->sc_sensor[ISL_SENSOR_PROX];

                sensor->value_cur = val;
                sensor->units = ENVSYS_INTEGER;
                sensor->state = ENVSYS_SVALID;

                (void)strlcpy(sensor->desc, ISL_PROX_NAME, ENVSYS_DESCLEN);
                (void)sysmon_envsys_sensor_attach(sc->sc_sme, sensor);
        }

        if (sc->sc_sme->sme_nsensors == 0) {
                sysmon_envsys_destroy(sc->sc_sme);
                sc->sc_sme = NULL;
                return false;
        }

        return true;
}

static bool
isl_read(device_t self, uint8_t cmd, uint8_t *valp)
{
        struct isl_softc *sc = device_private(self);
        uint8_t val;

        if (iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, cmd, &val, 0) != 0)
                return false;

        *valp = val;

        return true;
}

static bool
isl_read_ir(device_t self, uint16_t *valp)
{

        if (isl_read_sensor(self, ISL_CMD1_MODE_IRONCE, valp) != true)
                return false;

        return true;
}

static bool
isl_read_als(device_t self, uint16_t *valp)
{
        struct isl_softc *sc = device_private(self);
        uint16_t val;

        if (isl_read_sensor(self, ISL_CMD1_MODE_ALSONCE, &val) != true)
                return false;

        /*
         * The lux is calculated as: (k / 2^n) * data,
         * where k is the range and n is the resolution.
         */
        *valp = (val * sc->sc_range) >> sc->sc_resol;

        return true;
}

static bool
isl_read_prox(device_t self, uint16_t *valp)
{
        uint16_t ir, prox;
        uint8_t mode, val;

        /*
         * Try to sense the proximity as IR from LED
         * with ambient light rejection. The values
         * range from -2^(n-1) to 2^(n-1).
         */
        if (isl_read(self, ISL_REG_CMD2, &val) != true)
                return false;

        mode = __SHIFTOUT(val, ISL_CMD2_PROX_MASK);
        mode |= ISL_CMD2_PROX_IRREJ;

        val &= ~ISL_CMD2_PROX_MASK;
        val |= __SHIFTIN(mode, ISL_CMD2_PROX_MASK);

        if (isl_write(self, ISL_REG_CMD2, val) != true)
                return false;

        if (isl_read_sensor(self, ISL_CMD1_MODE_PROXONCE, &prox) != true)
                return false;

        if (isl_read_ir(self, &ir) != true)
                return false;

        *valp = (ir < prox) ? 0 : prox - ir;

        return true;
}

static bool
isl_read_sensor(device_t self, uint8_t cmd, uint16_t *valp)
{
        uint8_t lsb, mode, msb, val;

        if (isl_read(self, ISL_REG_CMD1, &val) != true)
                return false;

        mode = __SHIFTOUT(val, ISL_CMD1_MODE_MASK);
        mode |= cmd;

        val &= ~ISL_CMD1_MODE_MASK;
        val |= __SHIFTIN(mode, ISL_CMD1_MODE_MASK);

        if (isl_write(self, ISL_REG_CMD1, val) != true)
                return false;

        if (isl_read(self, ISL_REG_DATA1, &lsb) != true)
                return false;

        if (isl_read(self, ISL_REG_DATA1, &msb) != true)
                return false;

        *valp = (msb << 8) | lsb;

        return true;
}

static bool
isl_write(device_t self, uint8_t cmd, uint8_t val)
{
        struct isl_softc *sc = device_private(self);

        if (iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, cmd, val, 0) != 0)
                return false;

        DELAY(90);      /* Some delay is recommended. */

        return true;
}

static bool
isl_power(device_t self, bool enable)
{
        uint8_t mode, val;

        if (isl_read(self, ISL_REG_CMD1, &val) != true)
                return false;

        mode = __SHIFTOUT(val, ISL_CMD1_MODE_MASK);

        if (enable != false)
                mode &= ~ISL_CMD1_MODE_POWERDOWN;
        else
                mode |= ISL_CMD1_MODE_POWERDOWN;

        val &= ~ISL_CMD1_MODE_MASK;
        val |= __SHIFTIN(mode, ISL_CMD1_MODE_MASK);

        return isl_write(self, ISL_REG_CMD1, val);
}

static void
isl_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
        struct isl_softc *sc = sme->sme_cookie;
        uint16_t val;

        if (strcmp(edata->desc, ISL_IR_NAME) == 0) {

                sc->sc_sensor[ISL_SENSOR_IR].state = ENVSYS_SINVALID;

                if (isl_read_ir(sc->sc_dev, &val) != false) {
                        sc->sc_sensor[ISL_SENSOR_IR].value_cur = val;
                        sc->sc_sensor[ISL_SENSOR_IR].state = ENVSYS_SVALID;
                }
        }

        if (strcmp(edata->desc, ISL_ALS_NAME) == 0) {

                sc->sc_sensor[ISL_SENSOR_ALS].state = ENVSYS_SINVALID;

                if (isl_read_als(sc->sc_dev, &val) != false) {
                        sc->sc_sensor[ISL_SENSOR_ALS].value_cur = val;
                        sc->sc_sensor[ISL_SENSOR_ALS].state = ENVSYS_SVALID;
                }
        }

        if (strcmp(edata->desc, ISL_PROX_NAME) == 0) {

                sc->sc_sensor[ISL_SENSOR_PROX].state = ENVSYS_SINVALID;

                if (isl_read_prox(sc->sc_dev, &val) != false) {
                        sc->sc_sensor[ISL_SENSOR_PROX].value_cur = val;
                        sc->sc_sensor[ISL_SENSOR_PROX].state = ENVSYS_SVALID;
                }
        }
}

static bool
isl_suspend(device_t self, const pmf_qual_t *qual)
{
        (void)isl_power(self, false);

        return true;
}

static bool
isl_resume(device_t self, const pmf_qual_t *qual)
{
        (void)isl_power(self, true);

        return true;
}
/*      $NetBSD$ */

/*-
 * Copyright (c) 2011 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jukka Ruohonen Gregoire Sutre.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef _DEV_I2C_ISL29018REG_H
#define _DEV_I2C_ISL29018REG_H

/*
 * Derived from the following:
 *
 * Intersil: ISL29018, Data Sheet,
 * February 11, 2010. FN6619.1.
 */
#define ISL_ADDRMASK            0x7C
#define ISL_ADDR                0x44

#define ISL_REG_CMD1            0x00
#define ISL_REG_CMD2            0x01
#define ISL_REG_DATA1           0x02
#define ISL_REG_DATA2           0x03

#define ISL_CMD1_INTRP_MASK     __BITS(0, 1)
#define ISL_CMD1_INTRP_CYCLE_1  0x00
#define ISL_CMD1_INTRP_CYCLE_4  0x01
#define ISL_CMD1_INTRP_CYCLE_8  0x02
#define ISL_CMD1_INTRP_CYCLE_16 0x03

#define ISL_CMD1_INTRF_MASK     __BIT(2)
#define ISL_CMD1_INTRF_CLEARED  0x00
#define ISL_CMD1_INTRF_TRIGGER  0x01

#define ISL_CMD1_MODE_MASK      __BITS(5, 7)
#define ISL_CMD1_MODE_POWERDOWN 0x00
#define ISL_CMD1_MODE_ALSONCE   0x01
#define ISL_CMD1_MODE_IRONCE    0x02
#define ISL_CMD1_MODE_PROXONCE  0x03
#define ISL_CMD1_MODE_ALSCONT   0x05
#define ISL_CMD1_MODE_IRCONT    0x06
#define ISL_CMD1_MODE_PROXCONT  0x07

#define ISL_CMD2_RANGE_MASK     __BITS(0, 1)
#define ISL_CMD2_RANGE_1        0x00
#define ISL_CMD2_RANGE_2        0x01
#define ISL_CMD2_RANGE_3        0x02
#define ISL_CMD2_RANGE_4        0x03

#define ISL_CMD2_RESOL_MASK     __BITS(2, 3)
#define ISL_CMD2_RESOL_16       0x00
#define ISL_CMD2_RESOL_12       0x01
#define ISL_CMD2_RESOL_8        0x02
#define ISL_CMD2_RESOL_4        0x03

#define ISL_CMD2_AMPL_MASK      __BITS(4, 5)
#define ISL_CMD2_AMPL_12        0x00
#define ISL_CMD2_AMPL_25        0x01
#define ISL_CMD2_AMPL_50        0x02
#define ISL_CMD2_AMPL_100       0x03

#define ISL_CMD2_FREQ_MASK      __BIT(6)
#define ISL_CMD2_FREQ_DC        0x00
#define ISL_CMD2_FREQ_360       0x01

#define ISL_CMD2_PROX_MASK      __BIT(7)
#define ISL_CMD2_PROX_AMBIENT   0x00
#define ISL_CMD2_PROX_IRREJ     0x01

#endif  /* !_DEV_I2C_ISL29018REG_H */


Home | Main Index | Thread Index | Old Index