[BACK]Return to tsllux.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / dev / i2c

Annotation of src/sys/dev/i2c/tsllux.c, Revision 1.2

1.2     ! thorpej     1: /* $NetBSD: tsllux.c,v 1.1 2021/01/04 22:09:35 thorpej Exp $ */
1.1       thorpej     2:
                      3: /*-
                      4:  * Copyright (c) 2018 Jason R. Thorpe
                      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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     17:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     18:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     19:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     20:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     21:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     22:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     23:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     24:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     25:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     26:  * POSSIBILITY OF SUCH DAMAGE.
                     27:  */
                     28:
                     29: #include <sys/cdefs.h>
1.2     ! thorpej    30: __KERNEL_RCSID(0, "$NetBSD: tsllux.c,v 1.1 2021/01/04 22:09:35 thorpej Exp $");
1.1       thorpej    31:
                     32: #include <sys/param.h>
                     33: #include <sys/systm.h>
                     34: #include <sys/device.h>
                     35: #include <sys/conf.h>
                     36: #include <sys/bus.h>
                     37: #include <sys/kernel.h>
                     38: #include <sys/kmem.h>
                     39: #include <sys/mutex.h>
                     40: #include <sys/proc.h>
                     41: #include <sys/sysctl.h>
                     42:
                     43: #include <dev/i2c/i2cvar.h>
                     44: #include <dev/i2c/tsl256xreg.h>
                     45:
                     46: #include <dev/sysmon/sysmonvar.h>
                     47:
                     48: struct tsllux_softc {
                     49:        device_t        sc_dev;
                     50:        i2c_tag_t       sc_i2c;
                     51:        i2c_addr_t      sc_addr;
                     52:
                     53:        uint32_t        sc_poweron;
                     54:
                     55:        /*
                     56:         * Locking order is:
                     57:         *      tsllux mutex -> i2c bus
                     58:         */
                     59:        kmutex_t        sc_lock;
                     60:
                     61:        uint8_t         sc_itime;
                     62:        uint8_t         sc_gain;
                     63:        bool            sc_cs_package;
                     64:        bool            sc_auto_gain;
                     65:
                     66:        struct sysmon_envsys *sc_sme;
                     67:        envsys_data_t   sc_sensor;
                     68:
                     69:        struct sysctllog *sc_sysctllog;
                     70: };
                     71:
                     72: #define        TSLLUX_F_CS_PACKAGE     0x01
                     73:
                     74: static int     tsllux_match(device_t, cfdata_t, void *);
                     75: static void    tsllux_attach(device_t, device_t, void *);
                     76:
                     77: CFATTACH_DECL_NEW(tsllux, sizeof(struct tsllux_softc),
                     78:     tsllux_match, tsllux_attach, NULL, NULL);
                     79:
                     80: static const struct device_compatible_entry tsllux_compat_data[] = {
1.2     ! thorpej    81:        { .compat = "amstaos,tsl2560" },
        !            82:        { .compat = "amstaos,tsl2561" },
        !            83:
        !            84:        { 0 }
1.1       thorpej    85: };
                     86:
                     87: static int     tsllux_read1(struct tsllux_softc *, uint8_t, uint8_t *);
                     88: static int     tsllux_read2(struct tsllux_softc *, uint8_t, uint16_t *);
                     89: static int     tsllux_write1(struct tsllux_softc *, uint8_t, uint8_t);
                     90: #if 0
                     91: static int     tsllux_write2(struct tsllux_softc *, uint8_t, uint16_t);
                     92: #endif
                     93:
                     94: static void    tsllux_sysctl_attach(struct tsllux_softc *);
                     95:
                     96: static int     tsllux_poweron(struct tsllux_softc *);
                     97: static int     tsllux_poweroff(struct tsllux_softc *);
                     98:
                     99: static int     tsllux_set_integration_time(struct tsllux_softc *, uint8_t);
                    100: static int     tsllux_set_gain(struct tsllux_softc *, uint8_t);
                    101: static int     tsllux_set_autogain(struct tsllux_softc *, bool);
                    102:
                    103: static int     tsllux_get_lux(struct tsllux_softc *, uint32_t *,
                    104:                               uint16_t *, uint16_t *);
                    105:
                    106: static void    tsllux_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
                    107:
                    108: static int
                    109: tsllux_match(device_t parent, cfdata_t match, void *aux)
                    110: {
                    111:        struct i2c_attach_args *ia = aux;
                    112:        uint8_t id_reg;
                    113:        int error, match_result;
                    114:
                    115:        if (iic_use_direct_match(ia, match, tsllux_compat_data, &match_result))
                    116:                return (match_result);
                    117:
                    118:        switch (ia->ia_addr) {
                    119:        case TSL256x_SLAVEADDR_GND:
                    120:        case TSL256x_SLAVEADDR_FLOAT:
                    121:        case TSL256x_SLAVEADDR_VDD:
                    122:                break;
                    123:
                    124:        default:
                    125:                return (0);
                    126:        }
                    127:
                    128:        if (iic_acquire_bus(ia->ia_tag, 0) != 0)
                    129:                return (0);
                    130:        error = iic_smbus_read_byte(ia->ia_tag, ia->ia_addr,
                    131:            TSL256x_REG_ID | COMMAND6x_CMD, &id_reg, 0);
                    132:        iic_release_bus(ia->ia_tag, 0);
                    133:
                    134:        if (error)
                    135:                return (0);
                    136:
                    137:        /* XXX This loses if we have a 2560 rev. 0. */
                    138:        if (id_reg == 0)
                    139:                return (I2C_MATCH_ADDRESS_ONLY);
                    140:
                    141:        return (I2C_MATCH_ADDRESS_AND_PROBE);
                    142: }
                    143:
                    144: static void
                    145: tsllux_attach(device_t parent, device_t self, void *aux)
                    146: {
                    147:        struct tsllux_softc *sc = device_private(self);
                    148:        struct i2c_attach_args *ia = aux;
                    149:        bool have_i2c;
                    150:
                    151:        /* XXX IPL_NONE changes when we support threshold interrupts. */
                    152:        mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
                    153:
                    154:        sc->sc_dev = self;
                    155:        sc->sc_i2c = ia->ia_tag;
                    156:        sc->sc_addr = ia->ia_addr;
                    157:
                    158:        if (self->dv_cfdata != NULL &&
                    159:            self->dv_cfdata->cf_flags & TSLLUX_F_CS_PACKAGE)
                    160:                sc->sc_cs_package = true;
                    161:
                    162:        if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
                    163:                return;
                    164:        }
                    165:
                    166:        have_i2c = true;
                    167:
                    168:        /* Power on the device and clear any pending interrupts. */
                    169:        if (tsllux_write1(sc, TSL256x_REG_CONTROL | COMMAND6x_CLEAR,
                    170:                          CONTROL6x_POWER_ON)) {
                    171:                aprint_error_dev(self, ": unable to power on device\n");
                    172:                goto out;
                    173:        }
                    174:        sc->sc_poweron = 1;
                    175:
                    176:        /* Make sure interrupts are disabled. */
                    177:        if (tsllux_write1(sc, TSL256x_REG_INTERRUPT | COMMAND6x_CLEAR, 0)) {
                    178:                aprint_error_dev(self, ": unable to disable interrupts\n");
                    179:                goto out;
                    180:        }
                    181:
                    182:        aprint_naive("\n");
                    183:        aprint_normal(": TSL256x Light-to-Digital converter%s\n",
                    184:                      sc->sc_cs_package ? " (CS package)" : "");
                    185:
                    186:        /* Inititalize timing to reasonable defaults. */
                    187:        sc->sc_auto_gain = true;
                    188:        sc->sc_gain = TIMING6x_GAIN_16X;
                    189:        if (tsllux_set_integration_time(sc, TIMING6x_INTEG_101ms)) {
                    190:                aprint_error_dev(self, ": unable to set integration time\n");
                    191:                goto out;
                    192:        }
                    193:
                    194:        tsllux_poweroff(sc);
                    195:
                    196:        iic_release_bus(ia->ia_tag, 0);
                    197:        have_i2c = false;
                    198:
                    199:        tsllux_sysctl_attach(sc);
                    200:
                    201:        sc->sc_sme = sysmon_envsys_create();
                    202:        sc->sc_sme->sme_name = device_xname(self);
                    203:        sc->sc_sme->sme_cookie = sc;
                    204:        sc->sc_sme->sme_refresh = tsllux_sensors_refresh;
                    205:
                    206:        sc->sc_sensor.units = ENVSYS_LUX;
                    207:        sc->sc_sensor.state = ENVSYS_SINVALID;
                    208:        snprintf(sc->sc_sensor.desc, sizeof(sc->sc_sensor.desc),
                    209:                 "ambient light");
                    210:        sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor);
                    211:
                    212:        sysmon_envsys_register(sc->sc_sme);
                    213:
                    214:  out:
                    215:        if (have_i2c) {
                    216:                if (sc->sc_poweron)
                    217:                        tsllux_poweroff(sc);
                    218:                iic_release_bus(ia->ia_tag, 0);
                    219:        }
                    220: }
                    221:
                    222: static int
                    223: tsllux_sysctl_cs_package(SYSCTLFN_ARGS)
                    224: {
                    225:        struct tsllux_softc *sc;
                    226:        struct sysctlnode node;
                    227:        int error;
                    228:        u_int val;
                    229:
                    230:        node = *rnode;
                    231:        sc = node.sysctl_data;
                    232:
                    233:        mutex_enter(&sc->sc_lock);
                    234:        val = sc->sc_cs_package ? 1 : 0;
                    235:        node.sysctl_data = &val;
                    236:        error = sysctl_lookup(SYSCTLFN_CALL(&node));
                    237:        if (error || newp == NULL) {
                    238:                mutex_exit(&sc->sc_lock);
                    239:                return (error);
                    240:        }
                    241:
                    242:        /* CS package indicator is used only in software; no need for I2C. */
                    243:
                    244:        sc->sc_cs_package = val ? true : false;
                    245:        mutex_exit(&sc->sc_lock);
                    246:
                    247:        return (error);
                    248: }
                    249:
                    250: static int
                    251: tsllux_sysctl_autogain(SYSCTLFN_ARGS)
                    252: {
                    253:        struct tsllux_softc *sc;
                    254:        struct sysctlnode node;
                    255:        int error;
                    256:        u_int val;
                    257:
                    258:        node = *rnode;
                    259:        sc = node.sysctl_data;
                    260:
                    261:        mutex_enter(&sc->sc_lock);
                    262:        val = sc->sc_auto_gain ? 1 : 0;
                    263:        node.sysctl_data = &val;
                    264:        error = sysctl_lookup(SYSCTLFN_CALL(&node));
                    265:        if (error || newp == NULL) {
                    266:                mutex_exit(&sc->sc_lock);
                    267:                return (error);
                    268:        }
                    269:
                    270:        /* Auto-gain is a software feature; no need for I2C. */
                    271:
                    272:        error = tsllux_set_autogain(sc, val ? true : false);
                    273:        mutex_exit(&sc->sc_lock);
                    274:
                    275:        return (error);
                    276: }
                    277:
                    278: static int
                    279: tsllux_sysctl_gain(SYSCTLFN_ARGS)
                    280: {
                    281:        struct tsllux_softc *sc;
                    282:        struct sysctlnode node;
                    283:        int error;
                    284:        u_int val;
                    285:        uint8_t new_gain;
                    286:
                    287:        node = *rnode;
                    288:        sc = node.sysctl_data;
                    289:
                    290:        mutex_enter(&sc->sc_lock);
                    291:
                    292:        switch (sc->sc_gain) {
                    293:        case TIMING6x_GAIN_1X:
                    294:                val = 1;
                    295:                break;
                    296:
                    297:        case TIMING6x_GAIN_16X:
                    298:                val = 16;
                    299:                break;
                    300:
                    301:        default:
                    302:                val = 1;
                    303:                break;
                    304:        }
                    305:        node.sysctl_data = &val;
                    306:        error = sysctl_lookup(SYSCTLFN_CALL(&node));
                    307:        if (error || newp == NULL) {
                    308:                mutex_exit(&sc->sc_lock);
                    309:                return (error);
                    310:        }
                    311:
                    312:        switch (val) {
                    313:        case 1:
                    314:                new_gain = TIMING6x_GAIN_1X;
                    315:                break;
                    316:
                    317:        case 16:
                    318:                new_gain = TIMING6x_GAIN_16X;
                    319:                break;
                    320:
                    321:        default:
                    322:                mutex_exit(&sc->sc_lock);
                    323:                return (EINVAL);
                    324:        }
                    325:
                    326:        if ((error = iic_acquire_bus(sc->sc_i2c, 0)) != 0) {
                    327:                mutex_exit(&sc->sc_lock);
                    328:                return (error);
                    329:        }
                    330:
                    331:        error = tsllux_set_gain(sc, new_gain);
                    332:        iic_release_bus(sc->sc_i2c, 0);
                    333:        mutex_exit(&sc->sc_lock);
                    334:
                    335:        return (error);
                    336: }
                    337:
                    338: static int
                    339: tsllux_sysctl_itime(SYSCTLFN_ARGS)
                    340: {
                    341:        struct tsllux_softc *sc;
                    342:        struct sysctlnode node;
                    343:        int error;
                    344:        u_int val;
                    345:        uint8_t new_itime;
                    346:
                    347:        node = *rnode;
                    348:        sc = node.sysctl_data;
                    349:
                    350:        mutex_enter(&sc->sc_lock);
                    351:
                    352:        switch (sc->sc_itime) {
                    353:        case TIMING6x_INTEG_13_7ms:
                    354:                val = 13;
                    355:                break;
                    356:
                    357:        case TIMING6x_INTEG_101ms:
                    358:                val = 101;
                    359:                break;
                    360:
                    361:        case TIMING6x_INTEG_402ms:
                    362:        default:
                    363:                val = 402;
                    364:                break;
                    365:        }
                    366:        node.sysctl_data = &val;
                    367:        error = sysctl_lookup(SYSCTLFN_CALL(&node));
                    368:        if (error || newp == NULL) {
                    369:                mutex_exit(&sc->sc_lock);
                    370:                return (error);
                    371:        }
                    372:
                    373:        switch (val) {
                    374:        case 13:
                    375:        case 14:
                    376:                new_itime = TIMING6x_INTEG_13_7ms;
                    377:                break;
                    378:
                    379:        case 101:
                    380:                new_itime = TIMING6x_INTEG_101ms;
                    381:                break;
                    382:
                    383:        case 402:
                    384:                new_itime = TIMING6x_INTEG_402ms;
                    385:                break;
                    386:
                    387:        default:
                    388:                mutex_exit(&sc->sc_lock);
                    389:                return (EINVAL);
                    390:        }
                    391:
                    392:        if ((error = iic_acquire_bus(sc->sc_i2c, 0)) != 0) {
                    393:                mutex_exit(&sc->sc_lock);
                    394:                return (error);
                    395:        }
                    396:
                    397:        error = tsllux_set_integration_time(sc, new_itime);
                    398:        iic_release_bus(sc->sc_i2c, 0);
                    399:        mutex_exit(&sc->sc_lock);
                    400:
                    401:        return (error);
                    402: }
                    403:
                    404: static void
                    405: tsllux_sysctl_attach(struct tsllux_softc *sc)
                    406: {
                    407:        struct sysctllog **log = &sc->sc_sysctllog;
                    408:        const struct sysctlnode *rnode, *cnode;
                    409:        int error;
                    410:
                    411:        error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT,
                    412:            CTLTYPE_NODE, device_xname(sc->sc_dev),
                    413:            SYSCTL_DESCR("tsl256x control"),
                    414:            NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
                    415:        if (error)
                    416:                return;
                    417:
                    418:        error = sysctl_createv(log, 0, &rnode, &cnode,
                    419:            CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "cs_package",
                    420:            SYSCTL_DESCR("sensor in Chipscale (CS) package"),
                    421:            tsllux_sysctl_cs_package, 0,
                    422:            (void *)sc, 0, CTL_CREATE, CTL_EOL);
                    423:        if (error)
                    424:                return;
                    425:
                    426:        error = sysctl_createv(log, 0, &rnode, &cnode,
                    427:            CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "auto_gain",
                    428:            SYSCTL_DESCR("auto-gain algorithm enabled"),
                    429:            tsllux_sysctl_autogain, 0,
                    430:            (void *)sc, 0, CTL_CREATE, CTL_EOL);
                    431:        if (error)
                    432:                return;
                    433:
                    434:        error = sysctl_createv(log, 0, &rnode, &cnode,
                    435:            CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "gain",
                    436:            SYSCTL_DESCR("sensor gain"), tsllux_sysctl_gain, 0,
                    437:            (void *)sc, 0, CTL_CREATE, CTL_EOL);
                    438:        if (error)
                    439:                return;
                    440:
                    441:        error = sysctl_createv(log, 0, &rnode, &cnode,
                    442:            CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT,
                    443:            "integration_time",
                    444:            SYSCTL_DESCR("ADC integration time"), tsllux_sysctl_itime, 0,
                    445:            (void *)sc, 0, CTL_CREATE, CTL_EOL);
                    446:        if (error)
                    447:                return;
                    448: }
                    449:
                    450: static void
                    451: tsllux_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
                    452: {
                    453:        struct tsllux_softc *sc = sme->sme_cookie;
                    454:        uint32_t lux;
                    455:        int error;
                    456:
                    457:        if (edata != &sc->sc_sensor) {
                    458:                edata->state = ENVSYS_SINVALID;
                    459:                return;
                    460:        }
                    461:
                    462:        mutex_enter(&sc->sc_lock);
                    463:
                    464:        if ((error = iic_acquire_bus(sc->sc_i2c, 0)) == 0) {
                    465:                error = tsllux_get_lux(sc, &lux, NULL, NULL);
                    466:                iic_release_bus(sc->sc_i2c, 0);
                    467:        }
                    468:
                    469:        if (error) {
                    470:                edata->state = ENVSYS_SINVALID;
                    471:        } else {
                    472:                edata->value_cur = lux;
                    473:                edata->state = ENVSYS_SVALID;
                    474:        }
                    475:
                    476:        mutex_exit(&sc->sc_lock);
                    477: }
                    478:
                    479: /*
                    480:  * Allow pending interrupts to be cleared as part of another operation.
                    481:  */
                    482: #define        REGMASK6x               (COMMAND6x_REGMASK | COMMAND6x_CLEAR)
                    483:
                    484: static int
                    485: tsllux_read1(struct tsllux_softc *sc, uint8_t reg, uint8_t *valp)
                    486: {
                    487:        reg = (reg & REGMASK6x) | COMMAND6x_CMD;
                    488:        return (iic_smbus_read_byte(sc->sc_i2c, sc->sc_addr, reg, valp, 0));
                    489: }
                    490:
                    491: static int
                    492: tsllux_read2(struct tsllux_softc *sc, uint8_t reg, uint16_t *valp)
                    493: {
                    494:        reg = (reg & REGMASK6x) | COMMAND6x_CMD | COMMAND6x_WORD;
                    495:        return (iic_smbus_read_word(sc->sc_i2c, sc->sc_addr, reg, valp, 0));
                    496: }
                    497:
                    498: static int
                    499: tsllux_write1(struct tsllux_softc *sc, uint8_t reg, uint8_t val)
                    500: {
                    501:        reg = (reg & REGMASK6x) | COMMAND6x_CMD;
                    502:        return (iic_smbus_write_byte(sc->sc_i2c, sc->sc_addr, reg, val, 0));
                    503: }
                    504:
                    505: #if 0
                    506: static int
                    507: tsllux_write2(struct tsllux_softc *sc, uint8_t reg, uint16_t val)
                    508: {
                    509:        reg = (reg & REGMASK6x) | COMMAND6x_CMD | COMMAND6x_WORD;
                    510:        return (iic_smbus_write_word(sc->sc_i2c, sc->sc_addr, reg, val, 0));
                    511: }
                    512: #endif
                    513:
                    514: #undef REGMASK
                    515:
                    516: static int
                    517: tsllux_poweron(struct tsllux_softc *sc)
                    518: {
                    519:        int error;
                    520:
                    521:        if (sc->sc_poweron++ == 0) {
                    522:                uint8_t val;
                    523:
                    524:                error = tsllux_write1(sc, TSL256x_REG_CONTROL,
                    525:                                      CONTROL6x_POWER_ON);
                    526:                if (error)
                    527:                        return (error);
                    528:
                    529:                error = tsllux_read1(sc, TSL256x_REG_CONTROL, &val);
                    530:                if (error)
                    531:                        return (error);
                    532:
                    533:                if (val != CONTROL6x_POWER_ON) {
                    534:                        aprint_error_dev(sc->sc_dev,
                    535:                                         "failed to power on sensor\n");
                    536:                        return (EIO);
                    537:                }
                    538:        }
                    539:        return (0);
                    540: }
                    541:
                    542: static int
                    543: tsllux_poweroff(struct tsllux_softc *sc)
                    544: {
                    545:        if (sc->sc_poweron && --sc->sc_poweron == 0)
                    546:                return (tsllux_write1(sc, TSL256x_REG_CONTROL,
                    547:                                      CONTROL6x_POWER_OFF));
                    548:        return (0);
                    549: }
                    550:
                    551: static int
                    552: tsllux_set_integration_time(struct tsllux_softc *sc, uint8_t time)
                    553: {
                    554:        int error;
                    555:
                    556:        switch (time) {
                    557:        case TIMING6x_INTEG_13_7ms:
                    558:        case TIMING6x_INTEG_101ms:
                    559:        case TIMING6x_INTEG_402ms:
                    560:                break;
                    561:
                    562:        default:
                    563:                return (EINVAL);
                    564:        }
                    565:
                    566:        if ((error = tsllux_poweron(sc)) != 0)
                    567:                return (error);
                    568:
                    569:        if ((error = tsllux_write1(sc, TSL256x_REG_TIMING,
                    570:                                   time | sc->sc_gain)) != 0)
                    571:                goto out;
                    572:
                    573:        sc->sc_itime = time;
                    574:
                    575:  out:
                    576:        (void) tsllux_poweroff(sc);
                    577:        return (error);
                    578: }
                    579:
                    580: static int
                    581: tsllux_set_gain0(struct tsllux_softc *sc, uint8_t gain)
                    582: {
                    583:        int error;
                    584:
                    585:        if ((error = tsllux_write1(sc, TSL256x_REG_TIMING,
                    586:                                   sc->sc_itime | gain)) != 0)
                    587:                return (error);
                    588:
                    589:        sc->sc_gain = gain;
                    590:        return (0);
                    591: }
                    592:
                    593: static int
                    594: tsllux_set_gain(struct tsllux_softc *sc, uint8_t gain)
                    595: {
                    596:        int error;
                    597:
                    598:        switch (gain) {
                    599:        case TIMING6x_GAIN_1X:
                    600:        case TIMING6x_GAIN_16X:
                    601:                break;
                    602:
                    603:        default:
                    604:                return (EINVAL);
                    605:        }
                    606:
                    607:        if ((error = tsllux_poweron(sc)) != 0)
                    608:                return (error);
                    609:
                    610:        if ((error = tsllux_set_gain0(sc, gain)) != 0)
                    611:                goto out;
                    612:
                    613:        sc->sc_auto_gain = false;
                    614:
                    615:  out:
                    616:        (void) tsllux_poweroff(sc);
                    617:        return (error);
                    618: }
                    619:
                    620: static int
                    621: tsllux_set_autogain(struct tsllux_softc *sc, bool use_autogain)
                    622: {
                    623:
                    624:        sc->sc_auto_gain = use_autogain;
                    625:        return (0);
                    626: }
                    627:
                    628: static int
                    629: tsllux_wait_for_adcs(struct tsllux_softc *sc)
                    630: {
                    631:        int ms;
                    632:
                    633:        switch (sc->sc_itime) {
                    634:        case TIMING6x_INTEG_13_7ms:
                    635:                /* Wait 15ms for 13.7ms integration */
                    636:                ms = 15;
                    637:                break;
                    638:
                    639:        case TIMING6x_INTEG_101ms:
                    640:                /* Wait 120ms for 101ms integration */
                    641:                ms = 120;
                    642:                break;
                    643:
                    644:        case TIMING6x_INTEG_402ms:
                    645:        default:
                    646:                /* Wait 450ms for 402ms integration */
                    647:                ms = 450;
                    648:                break;
                    649:        }
                    650:
                    651:        if (ms < hztoms(1)) {
                    652:                /* Just busy-wait if we want to wait for less than 1 tick. */
                    653:                delay(ms * 1000);
                    654:        } else {
                    655:                /* Round up one tick for the case where we sleep. */
                    656:                (void) kpause("tslluxwait", false, mstohz(ms) + 1, NULL);
                    657:        }
                    658:
                    659:        return (0);
                    660: }
                    661:
                    662: static int
                    663: tsllux_read_adcs(struct tsllux_softc *sc, uint16_t *adc0valp,
                    664:                 uint16_t *adc1valp)
                    665: {
                    666:        int error;
                    667:
                    668:        if ((error = tsllux_read2(sc, TSL256x_REG_DATA0LOW, adc0valp)) == 0)
                    669:                error = tsllux_read2(sc, TSL256x_REG_DATA1LOW, adc1valp);
                    670:
                    671:        return (error);
                    672: }
                    673:
                    674: /*
                    675:  * The following code is partially derived from Adafruit's TSL2561
                    676:  * driver for Arduino (which was in turn derived from the data sheet),
                    677:  * which carries this notice:
                    678:  *
                    679:  * @file Adafruit_TSL2561_U.cpp
                    680:  *
                    681:  * @mainpage Adafruit TSL2561 Light/Lux sensor driver
                    682:  *
                    683:  * @section intro_sec Introduction
                    684:  *
                    685:  * This is the documentation for Adafruit's TSL2561 driver for the
                    686:  * Arduino platform.  It is designed specifically to work with the
                    687:  * Adafruit TSL2561 breakout: http://www.adafruit.com/products/439
                    688:  *
                    689:  * These sensors use I2C to communicate, 2 pins (SCL+SDA) are required
                    690:  * to interface with the breakout.
                    691:  *
                    692:  * Adafruit invests time and resources providing this open source code,
                    693:  * please support Adafruit and open-source hardware by purchasing
                    694:  * products from Adafruit!
                    695:  *
                    696:  * @section dependencies Dependencies
                    697:  *
                    698:  * This library depends on <a href="https://github.com/adafruit/Adafruit_Sensor">
                    699:  * Adafruit_Sensor</a> being present on your system. Please make sure you have
                    700:  * installed the latest version before using this library.
                    701:  *
                    702:  * @section author Author
                    703:  *
                    704:  * Written by Kevin "KTOWN" Townsend for Adafruit Industries.
                    705:  *
                    706:  * @section license License
                    707:  *
                    708:  * BSD license, all text here must be included in any redistribution.
                    709:  *
                    710:  *   @section  HISTORY
                    711:  *
                    712:  *   v2.0 - Rewrote driver for Adafruit_Sensor and Auto-Gain support, and
                    713:  *          added lux clipping check (returns 0 lux on sensor saturation)
                    714:  *   v1.0 - First release (previously TSL2561)
                    715:  */
                    716:
                    717: static int
                    718: tsllux_read_sensors(struct tsllux_softc *sc, uint16_t *adc0p, uint16_t *adc1p)
                    719: {
                    720:        int error;
                    721:
                    722:        if ((error = tsllux_poweron(sc)) != 0)
                    723:                return (error);
                    724:
                    725:        if ((error = tsllux_wait_for_adcs(sc)) != 0)
                    726:                goto out;
                    727:
                    728:        error = tsllux_read_adcs(sc, adc0p, adc1p);
                    729:
                    730:  out:
                    731:        (void) tsllux_poweroff(sc);
                    732:        return (error);
                    733: }
                    734:
                    735: /*
                    736:  * Auto-gain thresholds:
                    737:  */
                    738: #define        TSL2561_AGC_THI_13MS    (4850)  /* Max value at Ti 13ms = 5047 */
                    739: #define        TSL2561_AGC_TLO_13MS    (100)   /* Min value at Ti 13ms = 100 */
                    740: #define        TSL2561_AGC_THI_101MS   (36000) /* Max value at Ti 101ms = 37177 */
                    741: #define        TSL2561_AGC_TLO_101MS   (200)   /* Min value at Ti 101ms = 200 */
                    742: #define        TSL2561_AGC_THI_402MS   (63000) /* Max value at Ti 402ms = 65535 */
                    743: #define        TSL2561_AGC_TLO_402MS   (500)   /* Min value at Ti 402ms = 500 */
                    744:
                    745: static int
                    746: tsllux_get_sensor_data(struct tsllux_softc *sc, uint16_t *broadband,
                    747:                       uint16_t *ir)
                    748: {
                    749:        int error = 0;
                    750:        uint16_t adc0, adc1;
                    751:        bool did_adjust_gain, valid;
                    752:        uint16_t hi, lo;
                    753:
                    754:        if (sc->sc_auto_gain == false) {
                    755:                error = tsllux_read_sensors(sc, &adc0, &adc1);
                    756:                goto out;
                    757:        }
                    758:
                    759:        /* Set the hi / lo threshold based on current integration time. */
                    760:        switch (sc->sc_itime) {
                    761:        case TIMING6x_INTEG_13_7ms:
                    762:                hi = TSL2561_AGC_THI_13MS;
                    763:                lo = TSL2561_AGC_TLO_13MS;
                    764:                break;
                    765:
                    766:        case TIMING6x_INTEG_101ms:
                    767:                hi = TSL2561_AGC_THI_101MS;
                    768:                lo = TSL2561_AGC_TLO_101MS;
                    769:                break;
                    770:
                    771:        case TIMING6x_INTEG_402ms:
                    772:        default:
                    773:                hi = TSL2561_AGC_THI_402MS;
                    774:                lo = TSL2561_AGC_TLO_402MS;
                    775:        }
                    776:
                    777:        /* Read data and adjust the gain until we have a valid range. */
                    778:        for (valid = false, did_adjust_gain = false; valid == false; ) {
                    779:                if ((error = tsllux_read_sensors(sc, &adc0, &adc1)) != 0)
                    780:                        goto out;
                    781:
                    782:                if (did_adjust_gain == false) {
                    783:                        if (adc0 < lo && sc->sc_gain == TIMING6x_GAIN_1X) {
                    784:                                /* Increase the gain and try again. */
                    785:                                if ((error =
                    786:                                     tsllux_set_gain0(sc,
                    787:                                                      TIMING6x_GAIN_16X)) != 0)
                    788:                                        goto out;
                    789:                                did_adjust_gain = true;
                    790:                        } else if (adc0 > hi &&
                    791:                                   sc->sc_gain == TIMING6x_GAIN_16X) {
                    792:                                /* Decrease the gain and try again. */
                    793:                                if ((error =
                    794:                                     tsllux_set_gain0(sc,
                    795:                                                      TIMING6x_GAIN_1X)) != 0)
                    796:                                        goto out;
                    797:                                did_adjust_gain = true;
                    798:                        } else {
                    799:                                /*
                    800:                                 * Reading is either valid or we're already
                    801:                                 * at the chip's limits.
                    802:                                 */
                    803:                                valid = true;
                    804:                        }
                    805:                } else {
                    806:                        /*
                    807:                         * If we've already adjust the gain once, just
                    808:                         * return the new results.  This avoids endless
                    809:                         * loops where a value is at one extre pre-gain
                    810:                         * and at the other extreme post-gain.
                    811:                         */
                    812:                        valid = true;
                    813:                }
                    814:        }
                    815:
                    816:  out:
                    817:        if (error == 0) {
                    818:                if (broadband != NULL)
                    819:                        *broadband = adc0;
                    820:                if (ir != NULL)
                    821:                        *ir = adc1;
                    822:        }
                    823:        return (error);
                    824: }
                    825:
                    826: /*
                    827:  * Clipping thresholds:
                    828:  */
                    829: #define        TSL2561_CLIPPING_13MS   (4900)
                    830: #define        TSL2561_CLIPPING_101MS  (37000)
                    831: #define        TSL2561_CLIPPING_402MS  (65000)
                    832:
                    833: /*
                    834:  * Scaling factors:
                    835:  */
                    836: #define        TSL2561_LUX_LUXSCALE      (14)     /* Scale by 2^14 */
                    837: #define        TSL2561_LUX_RATIOSCALE    (9)      /* Scale ratio by 2^9 */
                    838: #define        TSL2561_LUX_CHSCALE       (10)     /* Scale channel values by 2^10 */
                    839: #define        TSL2561_LUX_CHSCALE_TINT0 (0x7517) /* 322/11 * 2^TSL2561_LUX_CHSCALE */
                    840: #define        TSL2561_LUX_CHSCALE_TINT1 (0x0FE7) /* 322/81 * 2^TSL2561_LUX_CHSCALE */
                    841:
                    842: /*
                    843:  * Lux factors (the datasheet explains how these magic constants
                    844:  * are used):
                    845:  */
                    846: /* T, FN and CL package values */
                    847: #define TSL2561_LUX_K1T           (0x0040)  /* 0.125 * 2^RATIO_SCALE */
                    848: #define TSL2561_LUX_B1T           (0x01f2)  /* 0.0304 * 2^LUX_SCALE */
                    849: #define TSL2561_LUX_M1T           (0x01be)  /* 0.0272 * 2^LUX_SCALE */
                    850: #define TSL2561_LUX_K2T           (0x0080)  /* 0.250 * 2^RATIO_SCALE */
                    851: #define TSL2561_LUX_B2T           (0x0214)  /* 0.0325 * 2^LUX_SCALE */
                    852: #define TSL2561_LUX_M2T           (0x02d1)  /* 0.0440 * 2^LUX_SCALE */
                    853: #define TSL2561_LUX_K3T           (0x00c0)  /* 0.375 * 2^RATIO_SCALE */
                    854: #define TSL2561_LUX_B3T           (0x023f)  /* 0.0351 * 2^LUX_SCALE */
                    855: #define TSL2561_LUX_M3T           (0x037b)  /* 0.0544 * 2^LUX_SCALE */
                    856: #define TSL2561_LUX_K4T           (0x0100)  /* 0.50 * 2^RATIO_SCALE */
                    857: #define TSL2561_LUX_B4T           (0x0270)  /* 0.0381 * 2^LUX_SCALE */
                    858: #define TSL2561_LUX_M4T           (0x03fe)  /* 0.0624 * 2^LUX_SCALE */
                    859: #define TSL2561_LUX_K5T           (0x0138)  /* 0.61 * 2^RATIO_SCALE */
                    860: #define TSL2561_LUX_B5T           (0x016f)  /* 0.0224 * 2^LUX_SCALE */
                    861: #define TSL2561_LUX_M5T           (0x01fc)  /* 0.0310 * 2^LUX_SCALE */
                    862: #define TSL2561_LUX_K6T           (0x019a)  /* 0.80 * 2^RATIO_SCALE */
                    863: #define TSL2561_LUX_B6T           (0x00d2)  /* 0.0128 * 2^LUX_SCALE */
                    864: #define TSL2561_LUX_M6T           (0x00fb)  /* 0.0153 * 2^LUX_SCALE */
                    865: #define TSL2561_LUX_K7T           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
                    866: #define TSL2561_LUX_B7T           (0x0018)  /* 0.00146 * 2^LUX_SCALE */
                    867: #define TSL2561_LUX_M7T           (0x0012)  /* 0.00112 * 2^LUX_SCALE */
                    868: #define TSL2561_LUX_K8T           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
                    869: #define TSL2561_LUX_B8T           (0x0000)  /* 0.000 * 2^LUX_SCALE */
                    870: #define TSL2561_LUX_M8T           (0x0000)  /* 0.000 * 2^LUX_SCALE */
                    871:
                    872: /* CS package values */
                    873: #define TSL2561_LUX_K1C           (0x0043)  /* 0.130 * 2^RATIO_SCALE */
                    874: #define TSL2561_LUX_B1C           (0x0204)  /* 0.0315 * 2^LUX_SCALE */
                    875: #define TSL2561_LUX_M1C           (0x01ad)  /* 0.0262 * 2^LUX_SCALE */
                    876: #define TSL2561_LUX_K2C           (0x0085)  /* 0.260 * 2^RATIO_SCALE */
                    877: #define TSL2561_LUX_B2C           (0x0228)  /* 0.0337 * 2^LUX_SCALE */
                    878: #define TSL2561_LUX_M2C           (0x02c1)  /* 0.0430 * 2^LUX_SCALE */
                    879: #define TSL2561_LUX_K3C           (0x00c8)  /* 0.390 * 2^RATIO_SCALE */
                    880: #define TSL2561_LUX_B3C           (0x0253)  /* 0.0363 * 2^LUX_SCALE */
                    881: #define TSL2561_LUX_M3C           (0x0363)  /* 0.0529 * 2^LUX_SCALE */
                    882: #define TSL2561_LUX_K4C           (0x010a)  /* 0.520 * 2^RATIO_SCALE */
                    883: #define TSL2561_LUX_B4C           (0x0282)  /* 0.0392 * 2^LUX_SCALE */
                    884: #define TSL2561_LUX_M4C           (0x03df)  /* 0.0605 * 2^LUX_SCALE */
                    885: #define TSL2561_LUX_K5C           (0x014d)  /* 0.65 * 2^RATIO_SCALE */
                    886: #define TSL2561_LUX_B5C           (0x0177)  /* 0.0229 * 2^LUX_SCALE */
                    887: #define TSL2561_LUX_M5C           (0x01dd)  /* 0.0291 * 2^LUX_SCALE */
                    888: #define TSL2561_LUX_K6C           (0x019a)  /* 0.80 * 2^RATIO_SCALE */
                    889: #define TSL2561_LUX_B6C           (0x0101)  /* 0.0157 * 2^LUX_SCALE */
                    890: #define TSL2561_LUX_M6C           (0x0127)  /* 0.0180 * 2^LUX_SCALE */
                    891: #define TSL2561_LUX_K7C           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
                    892: #define TSL2561_LUX_B7C           (0x0037)  /* 0.00338 * 2^LUX_SCALE */
                    893: #define TSL2561_LUX_M7C           (0x002b)  /* 0.00260 * 2^LUX_SCALE */
                    894: #define TSL2561_LUX_K8C           (0x029a)  /* 1.3 * 2^RATIO_SCALE */
                    895: #define TSL2561_LUX_B8C           (0x0000)  /* 0.000 * 2^LUX_SCALE */
                    896: #define TSL2561_LUX_M8C           (0x0000)  /* 0.000 * 2^LUX_SCALE */
                    897:
                    898: struct lux_factor_table_entry {
                    899:        uint16_t        k;
                    900:        uint16_t        b;
                    901:        uint16_t        m;
                    902: };
                    903:
                    904: static const struct lux_factor_table_entry lux_factor_table[] = {
                    905:        { TSL2561_LUX_K1T,      TSL2561_LUX_B1T,        TSL2561_LUX_M1T },
                    906:        { TSL2561_LUX_K2T,      TSL2561_LUX_B2T,        TSL2561_LUX_M2T },
                    907:        { TSL2561_LUX_K3T,      TSL2561_LUX_B3T,        TSL2561_LUX_M3T },
                    908:        { TSL2561_LUX_K4T,      TSL2561_LUX_B4T,        TSL2561_LUX_M4T },
                    909:        { TSL2561_LUX_K5T,      TSL2561_LUX_B5T,        TSL2561_LUX_M5T },
                    910:        { TSL2561_LUX_K6T,      TSL2561_LUX_B6T,        TSL2561_LUX_M6T },
                    911:        { TSL2561_LUX_K7T,      TSL2561_LUX_B7T,        TSL2561_LUX_M7T },
                    912:        { TSL2561_LUX_K8T,      TSL2561_LUX_B8T,        TSL2561_LUX_M8T },
                    913: };
                    914: static const int lux_factor_table_last_entry =
                    915:     (sizeof(lux_factor_table) / sizeof(lux_factor_table[0])) - 1;
                    916:
                    917: static const struct lux_factor_table_entry lux_factor_table_cs_package[] = {
                    918:        { TSL2561_LUX_K1C,      TSL2561_LUX_B1C,        TSL2561_LUX_M1C },
                    919:        { TSL2561_LUX_K2C,      TSL2561_LUX_B2C,        TSL2561_LUX_M2C },
                    920:        { TSL2561_LUX_K3C,      TSL2561_LUX_B3C,        TSL2561_LUX_M3C },
                    921:        { TSL2561_LUX_K4C,      TSL2561_LUX_B4C,        TSL2561_LUX_M4C },
                    922:        { TSL2561_LUX_K5C,      TSL2561_LUX_B5C,        TSL2561_LUX_M5C },
                    923:        { TSL2561_LUX_K6C,      TSL2561_LUX_B6C,        TSL2561_LUX_M6C },
                    924:        { TSL2561_LUX_K7C,      TSL2561_LUX_B7C,        TSL2561_LUX_M7C },
                    925:        { TSL2561_LUX_K8C,      TSL2561_LUX_B8C,        TSL2561_LUX_M8C },
                    926: };
                    927: static const int lux_factor_table_cs_package_last_entry =
                    928:     (sizeof(lux_factor_table_cs_package) /
                    929:      sizeof(lux_factor_table_cs_package[0])) - 1;
                    930:
                    931: static int
                    932: tsllux_get_lux(struct tsllux_softc *sc, uint32_t *luxp,
                    933:               uint16_t *raw_broadband, uint16_t *raw_ir)
                    934: {
                    935:        uint32_t channel0, channel1, scale, ratio, lux = 0;
                    936:        uint16_t broadband, ir;
                    937:        uint16_t clip_threshold;
                    938:        const struct lux_factor_table_entry *table;
                    939:        int idx, last_entry, error;
                    940:        int32_t temp;
                    941:
                    942:        if ((error = tsllux_get_sensor_data(sc, &broadband, &ir)) != 0)
                    943:                return (error);
                    944:
                    945:        if (luxp == NULL) {
                    946:                /*
                    947:                 * Caller doesn't want the calculated Lux value, so
                    948:                 * don't bother calculating it.  Maybe they just want
                    949:                 * the raw sensor data?
                    950:                 */
                    951:                goto out;
                    952:        }
                    953:
                    954:        /*
                    955:         * Check to see if the sensor is saturated.  If so,
                    956:         * just return a "max brightness" value.
                    957:         */
                    958:        switch (sc->sc_itime) {
                    959:        case TIMING6x_INTEG_13_7ms:
                    960:                clip_threshold = TSL2561_CLIPPING_13MS;
                    961:                break;
                    962:
                    963:        case TIMING6x_INTEG_101ms:
                    964:                clip_threshold = TSL2561_CLIPPING_101MS;
                    965:                break;
                    966:
                    967:        case TIMING6x_INTEG_402ms:
                    968:        default:
                    969:                clip_threshold = TSL2561_CLIPPING_402MS;
                    970:                break;
                    971:        }
                    972:
                    973:        if (broadband > clip_threshold || ir > clip_threshold) {
                    974:                lux = 65536;
                    975:                goto out;
                    976:        }
                    977:
                    978:        /* Get correct scale factor based on integration time. */
                    979:        switch (sc->sc_itime) {
                    980:        case TIMING6x_INTEG_13_7ms:
                    981:                scale = TSL2561_LUX_CHSCALE_TINT0;
                    982:                break;
                    983:
                    984:        case TIMING6x_INTEG_101ms:
                    985:                scale = TSL2561_LUX_CHSCALE_TINT1;
                    986:                break;
                    987:
                    988:        case TIMING6x_INTEG_402ms:
                    989:        default:
                    990:                scale = (1 << TSL2561_LUX_CHSCALE);
                    991:        }
                    992:
                    993:        /* Scale for gain. */
                    994:        if (sc->sc_gain == TIMING6x_GAIN_1X)
                    995:                scale <<= 4;
                    996:
                    997:        /* Scale the channel values. */
                    998:        channel0 = ((uint32_t)broadband * scale) >> TSL2561_LUX_CHSCALE;
                    999:        channel1 = ((uint32_t)ir * scale) >> TSL2561_LUX_CHSCALE;
                   1000:
                   1001:        /* Find the ratio of the channel values (ir / broadband) */
                   1002:        if (channel0 != 0)
                   1003:                ratio = (channel1 << (TSL2561_LUX_RATIOSCALE + 1)) / channel0;
                   1004:        else
                   1005:                ratio = 0;
                   1006:
                   1007:        /* Round the ratio value. */
                   1008:        ratio = (ratio + 1) >> 1;
                   1009:
                   1010:        if (sc->sc_cs_package) {
                   1011:                table = lux_factor_table_cs_package;
                   1012:                last_entry = lux_factor_table_cs_package_last_entry;
                   1013:        } else {
                   1014:                table = lux_factor_table;
                   1015:                last_entry = lux_factor_table_last_entry;
                   1016:        }
                   1017:
                   1018:        /*
                   1019:         * The table is arranged such that we compare <= against
                   1020:         * the key, and if all else fails, we use the last entry.
                   1021:         * The pseudo-code in the data sheet shows what's going on.
                   1022:         */
                   1023:        for (idx = 0; idx < last_entry; idx++) {
                   1024:                if (ratio <= table[idx].k)
                   1025:                        break;
                   1026:        }
                   1027:
                   1028:        temp = ((channel0 * table[idx].b) - (channel1 * table[idx].m));
                   1029:
                   1030:        /* Do not allow negative Lux value. */
                   1031:        if (temp < 0)
                   1032:                temp = 0;
                   1033:
                   1034:        /* Round lsb (2^(LUX_SCALE-1)) */
                   1035:        temp += (1 << (TSL2561_LUX_LUXSCALE-1));
                   1036:
                   1037:        /* Strip off fractional portion */
                   1038:        lux = temp >> TSL2561_LUX_LUXSCALE;
                   1039:
                   1040:  out:
                   1041:        if (error == 0) {
                   1042:                if (luxp != NULL)
                   1043:                        *luxp = lux;
                   1044:                if (raw_broadband != NULL)
                   1045:                        *raw_broadband = broadband;
                   1046:                if (raw_ir != NULL)
                   1047:                        *raw_ir = ir;
                   1048:        }
                   1049:        return (error);
                   1050: }

CVSweb <webmaster@jp.NetBSD.org>