Module Name:    src
Committed By:   macallan
Date:           Thu Feb  1 21:44:18 UTC 2018

Modified Files:
        src/sys/dev/i2c: files.i2c
Added Files:
        src/sys/dev/i2c: dstemp.c

Log Message:
add driver for Maxim DS1631 High-Precision Digital Thermometer and
Thermostat
temperature reading only for now

I've had the attached dstemp(4) driver sitting in my INBOX for the last several years. It was submitted to me by Jukka Ruhonen shortly before he disappeared. I never got around to importing it since I didn't have any way to test.

Anyway, Jukka's driver may well contain some additional information that will be useful in the newest incarnation of dstemp(4). I'll still defer against making the changes myself, since I cannot test anything.

Enjoy!


+------------------+--------------------------+----------------------------+
| Paul Goyette     | PGP Key fingerprint:     | E-mail addresses:          |
| (Retired)        | FA29 0E3B 35AF E8AE 6651 | paul at whooppee dot com   |
| Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd dot org |
+------------------+--------------------------+----------------------------+
/*      $NetBSD: dstemp_reg.h,v 0.1 2009/05/11 09:49:48 xxx Exp $ */

/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jukka Ruohonen.
 *
 * 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_DSTEMP_REG_H
#define _DEV_I2C_DSTEMP_REG_H

#include <sys/cdefs.h>
/*
 * Derived from the following datasheets:
 *
 * DS1621 (replaced DS1625):
 *   http://datasheets.maxim-ic.com/en/ds/DS1621.pdf
 *
 * DS1624:
 *   http://datasheets.maxim-ic.com/en/ds/DS1624.pdf
 *
 * DS1631, DS1631A, DS1731:
 *   http://datasheets.maxim-ic.com/en/ds/DS1631-DS1731.pdf
 *
 * DS1721:
 *   http://datasheets.maxim-ic.com/en/ds/DS1721.pdf
 *
 * (Retrieved on Mon May 11 09:49:48 EEST 2009.)
 */

/*
 * The accuracy of the listed chips vary from 9-bit
 * resolution to 13-bit resolution. The MSB will always
 * represent resolution of 1 C, whereas the LSB conveys
 * additional resolution. The following modified example
 * is taken from the datasheet of DS1624:
 *
 *        degC          MSB      LSB       uint16_t
 *        -----       -------- --------    --------
 * MAX:  +125.0       01111101 00000000     0x7D00
 *        +25.0625    00011001 00010000     0x1910
 *        +25.0       00011001 00000000     0x1900
 *         +0.5       00000000 10000000     0x0080
 *          0.0       00000000 00000000     0x0000
 *         -0.5       11111111 10000000     0xFF80
 *        -25.0625    11100110 11110000     0xE6F0
 *        -25.0       11100111 00000000     0xE700
 * MIN:   -55.0       11001001 00000000     0xC900
 *                    S
 *
 * Instead of this 13-bit (0.03125 C) resolution, other
 * listed chips provide varying degrees of accuracy.
 *
 * Conversion times vary substantially between different
 * resolutions. An example from the datasheet of DS1721:
 *
 *       R1  R0  Resol.  Time (ms)
 *       --  --  ------  ---------
 *       0   0   9-bit     93.75
 *       0   1   10-bit   187.50
 *       1   0   11-bit   375.00
 *       1   1   12-bit   750.00
 */

#define DSTEMP_ADDRMASK        0x78
#define DSTEMP_ADDR            0x48 /* 100 1xxx (same for all listed chips) */

#define DS1621_REG_READ_TEMP   0xAA
#define DS1621_REG_ACCESS_TH   0xA1
#define DS1621_REG_ACCESS_TL   0xA2
#define DS1621_REG_ACCESS_CTRL 0xAC
#define DS1621_REG_READ_COUNT  0xA8
#define DS1621_REG_READ_SLOPE  0xA9
#define DS1621_REG_CONV_START  0xEE /* Diverges. */
#define DS1621_REG_CONV_STOP   0x22

#define DS1624_REG_READ_TEMP   0xAA
#define DS1624_REG_ACCESS_CTRL 0xAC
#define DS1624_REG_ACCESS_MEM  0x17
#define DS1624_REG_CONV_START  0xEE
#define DS1624_REG_CONV_STOP   0x22

#define DS1625_REG_READ_TEMP   0xAA
#define DS1625_REG_ACCESS_TH   0xA1
#define DS1625_REG_ACCESS_TL   0xA2
#define DS1625_REG_ACCESS_CTRL 0xAC
#define DS1625_REG_CONV_START  0xEE
#define DS1625_REG_CONV_STOP   0x22

#define DS1631_REG_READ_TEMP   0xAA
#define DS1631_REG_ACCESS_TH   0xA1
#define DS1631_REG_ACCESS_TL   0xA2
#define DS1631_REG_ACCESS_CTRL 0xAC
#define DS1631_REG_SOFT_POR    0x54
#define DS1631_REG_CONV_START  0x51
#define DS1631_REG_CONV_STOP   0x22

#define DS1721_REG_READ_TEMP   0xAA
#define DS1721_REG_ACCESS_TH   0xA1
#define DS1721_REG_ACCESS_TL   0xA2
#define DS1721_REG_ACCESS_CTRL 0xAC
#define DS1721_REG_CONV_START  0x51
#define DS1721_REG_CONV_STOP   0x22

#define DSTEMP_REG_READ_TEMP   DS1621_REG_READ_TEMP
#define DSTEMP_REG_ACCESS_CTRL DS1621_REG_ACCESS_CTRL
#define DSTEMP_REG_CONV_STOP   DS1621_REG_CONV_STOP

#define DS1621_CTRL_1SHOT      __BIT(0)
#define DS1621_CTRL_POL        __BIT(1)
#define DS1621_CTRL_NVB        __BIT(4)
#define DS1621_CTRL_TLF        __BIT(5)
#define DS1621_CTRL_THF        __BIT(6)
#define DS1621_CTRL_DONE       __BIT(7)

#define DS1624_CTRL_1SHOT      __BIT(0)
#define DS1624_CTRL_DONE       __BIT(7)

#define DS1625_CTRL_1SHOT      __BIT(0)
#define DS1625_CTRL_POL        __BIT(1)
#define DS1625_CTRL_NVB        __BIT(4)
#define DS1625_CTRL_TLF        __BIT(5)
#define DS1625_CTRL_THF        __BIT(6)
#define DS1625_CTRL_DONE       __BIT(7)

#define DS1631_CTRL_1SHOT      __BIT(0)
#define DS1631_CTRL_POL        __BIT(1)
#define DS1631_CTRL_R0         __BIT(2)
#define DS1631_CTRL_R1         __BIT(3)
#define DS1631_CTRL_NVB        __BIT(4)
#define DS1631_CTRL_TLF        __BIT(5)
#define DS1631_CTRL_THF        __BIT(6)
#define DS1631_CTRL_DONE       __BIT(7)

#define DS1721_CTRL_1SHOT      __BIT(0)
#define DS1721_CTRL_POL        __BIT(1)
#define DS1721_CTRL_R0         __BIT(2)
#define DS1721_CTRL_R1         __BIT(3)
#define DS1721_CTRL_DONE       __BIT(7)

#define DSTEMP_CTRL_1SHOT      DS1621_CTRL_1SHOT
#define DSTEMP_CTRL_DONE       DS1621_CTRL_DONE

#endif  /* _DEV_I2C_DSTEMP_REG_H */
/*      $NetBSD: dstemp.c,v 0.1 2009/05/11 09:49:48 xxx Exp $ */

/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jukka Ruohonen.
 *
 * 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: dstemp.c,v 0.1 2009/05/11 09:49:48 xxx Exp $");

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

#include <dev/sysmon/sysmonvar.h>

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

struct dstemp_softc {
        device_t              sc_dev;
        i2c_tag_t             sc_tag;
        i2c_addr_t            sc_addr;
        uint8_t               sc_type;
        uint8_t               sc_start;
        envsys_data_t         sc_sensor;
        struct sysmon_envsys *sc_sme;
};

enum {
        DSTEMP_TYPE_DS1621 = 0,
        DSTEMP_TYPE_DS1624,
        DSTEMP_TYPE_DS1625,
        DSTEMP_TYPE_DS1631,
        DSTEMP_TYPE_DS1721
};

static const struct {
        int           type;
        uint8_t       start;
        uint8_t       resol; /* Factory default resolution (bits). */
        const char   *name;
} dstemp_table[] = {

        { DSTEMP_TYPE_DS1621,
          DS1621_REG_CONV_START,  9, "DS1621" },

        { DSTEMP_TYPE_DS1624,
          DS1624_REG_CONV_START, 13, "DS1624" },

        { DSTEMP_TYPE_DS1625,
          DS1625_REG_CONV_START,  9, "DS1625" },

        { DSTEMP_TYPE_DS1631,
          DS1631_REG_CONV_START, 12, "DS1631 or compatible" },

        { DSTEMP_TYPE_DS1721,
          DS1721_REG_CONV_START, 12, "DS1721" },

        { -1, 0, 0, NULL }
};

/*
 * From uK to degC and vice versa.
 */
#define DSTEMP_ENCODE(x) (((((x) - 273150000U) / 62500) << 4) & 0xFFFF);
#define DSTEMP_DECODE(x) ((((int16_t)(x) >> 4) & 0x0FFF) * 62500 + 273150000U)

/*
 * Print uK as degC.
 */
#define DSTEMP_PRINT(x) (((x) - 273150000) / 1000000)

/*
 * We can handle only PROP_WARNMAX and PROP_WARNMIN in hardware.
 */
#define NOPROP (PROP_CRITMAX | PROP_CRITMIN | PROP_BATTCAP | PROP_BATTWARN)

static int  dstemp_lookup(const int);
static bool dstemp_ident(struct dstemp_softc *, const uint8_t);
static int  dstemp_match(device_t, cfdata_t, void *);
static void dstemp_attach(device_t, device_t, void *);
static bool dstemp_init(device_t);
static bool dstemp_init_sysmon(device_t);
static void dstemp_get_limits(struct sysmon_envsys *, envsys_data_t *,
                              sysmon_envsys_lim_t *);
static void dstemp_set_limits(struct sysmon_envsys *, envsys_data_t *,
                              sysmon_envsys_lim_t *);
static bool dstemp_check_limits(struct dstemp_softc *);
static void dstemp_refresh(struct sysmon_envsys *, envsys_data_t *);
static bool dstemp_enable(struct dstemp_softc *, const bool);
static bool dstemp_read_ctrl(struct dstemp_softc *, uint8_t *);
static bool dstemp_write_ctrl(struct dstemp_softc *, const uint8_t);
static bool dstemp_read_16(struct dstemp_softc *, const uint8_t, uint16_t *);
static bool dstemp_write_16(struct dstemp_softc *,
                            const uint8_t, const uint16_t);
static bool dstemp_suspend(device_t PMF_FN_PROTO);
static bool dstemp_resume(device_t PMF_FN_PROTO);

CFATTACH_DECL_NEW(dstemp, sizeof(struct dstemp_softc),
    dstemp_match, dstemp_attach, NULL, NULL);

static int
dstemp_lookup(const int cf_flags)
{
        int i;

        for (i = 0; dstemp_table[i].name != NULL; i++) {

                if (dstemp_table[i].type == cf_flags)
                        return i;
        }

        return -1;
}

/*
 * Since there is no identification register, we use the
 * following simple heuristic as a validation test: either
 *
 *    7  (DS1621 and DS1625),
 *    3  (DS1624) or
 *    4  (DS1631-DS1731 and DS1721)
 *
 * least significant bits should always be zero.
 */
static bool
dstemp_ident(struct dstemp_softc *sc, const uint8_t resol)
{
        uint16_t temp;

        KDASSERT(resol <= 16);

        iic_acquire_bus(sc->sc_tag, 0);

        if (dstemp_read_16(sc, DSTEMP_REG_READ_TEMP, &temp) != true) {
                iic_release_bus(sc->sc_tag, 0);
                return false;
        }

        iic_release_bus(sc->sc_tag, 0);

        if ((temp & (0xFFFF >> resol)) != 0)
                return false;

        return true;
}

static int
dstemp_match(device_t parent, cfdata_t cf, void *aux)
{
        struct dstemp_softc sc;
        struct i2c_attach_args *ia = aux;
        int i;

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

        if ((ia->ia_addr & DSTEMP_ADDRMASK) != DSTEMP_ADDR)
                return 0;

        if ((i = dstemp_lookup(cf->cf_flags)) < 0) {
                aprint_debug("dstemp0: invalid configuration flag\n");
                return 0;
        }

        if (dstemp_ident(&sc, dstemp_table[i].resol) != true) {
                aprint_debug("dstemp0: failed to identify chip\n");
                return 0;
        }

        return 1;
}

static void
dstemp_attach(device_t parent, device_t self, void *aux)
{
        struct dstemp_softc *sc = device_private(self);
        struct i2c_attach_args *ia = aux;
        sysmon_envsys_lim_t limits;
        uint16_t lim;
        int i;

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

        i = dstemp_lookup(device_cfdata(self)->cf_flags);

        KDASSERT(i != -1);

        aprint_naive(": temperature sensor\n");
        aprint_normal(": %s temperature sensor\n", dstemp_table[i].name);

        sc->sc_type = dstemp_table[i].type;
        sc->sc_start = dstemp_table[i].start;

        if (dstemp_init(self) != true) {
                aprint_error_dev(self, "failed to initialize device\n");
                return;
        }

        if (dstemp_init_sysmon(self) != true) {
                aprint_error_dev(self, "failed to initialize sysmon\n");
                return;
        }

        if (pmf_device_register(self, dstemp_suspend, dstemp_resume) != true)
                aprint_error_dev(self, "failed to establish power handler\n");

        if (sc->sc_type == DSTEMP_TYPE_DS1624 ||
            sc->sc_type == DSTEMP_TYPE_DS1721)
                return;

        (void)memset(&limits, 0, sizeof(struct sysmon_envsys_lim));

        dstemp_get_limits(sc->sc_sme, &sc->sc_sensor, &limits);

        if ((limits.sel_flags & PROP_WARNMIN) != 0) {
                lim = DSTEMP_PRINT(limits.sel_warnmin);
                aprint_normal_dev(self, "low limit %u C\n", lim);
        }

        if ((limits.sel_flags & PROP_WARNMAX) != 0) {
                lim = DSTEMP_PRINT(limits.sel_warnmax);
                aprint_normal_dev(self, "high limit %u C\n", lim);
        }
}

static bool
dstemp_init(device_t self)
{
        struct dstemp_softc *sc = device_private(self);
        uint8_t buf, val;

        iic_acquire_bus(sc->sc_tag, I2C_F_POLL);

        if (dstemp_read_ctrl(sc, &buf) != true)
                goto fail;

        val = buf;

        /*
         * We want continuous mode.
         */
        if ((buf & DSTEMP_CTRL_1SHOT) != 0)
                val &= ~DSTEMP_CTRL_1SHOT;

        /*
         * We want full resolution for these chips.
         */
        if (sc->sc_type == DSTEMP_TYPE_DS1631 ||
            sc->sc_type == DSTEMP_TYPE_DS1721) {
                val |= DS1631_CTRL_R0;
                val |= DS1631_CTRL_R1;
        }

        if (buf != val) {

                /*
                 * If we are already operating in continuous
                 * mode, conversions must be stopped before
                 * writing to the configuration register.
                 */
                if ((buf & DSTEMP_CTRL_1SHOT) != 0) {

                        if (dstemp_enable(sc, false) != true)
                                goto fail;
                }

                if (dstemp_write_ctrl(sc, val) != true)
                        goto fail;

                DELAY(10000); /* 10 ms required for next write. */
        }

        if (dstemp_enable(sc, true) != true)
                goto fail;

        iic_release_bus(sc->sc_tag, I2C_F_POLL);

        return true;

fail:
        iic_release_bus(sc->sc_tag, I2C_F_POLL);

        return false;
}

static bool
dstemp_init_sysmon(device_t self)
{
        struct dstemp_softc *sc = device_private(self);

        sc->sc_sme = sysmon_envsys_create();

        sc->sc_sensor.monitor = true;
        sc->sc_sensor.units = ENVSYS_STEMP;
        sc->sc_sensor.state = ENVSYS_SINVALID;
        sc->sc_sensor.flags |= ENVSYS_FMONLIMITS;

        (void)strlcpy(sc->sc_sensor.desc,
            device_xname(self), sizeof(sc->sc_sensor.desc));

        /*
         * Although all supported chips are capable of operating with
         * temperatures below zero, we limit the valid values to [0, 125].
         */
        sc->sc_sensor.value_min = 0;
        sc->sc_sensor.value_max = DSTEMP_DECODE(0x7D00);
        sc->sc_sensor.flags |= ENVSYS_FVALID_MAX | ENVSYS_FVALID_MIN;

        if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0) {
                sysmon_envsys_destroy(sc->sc_sme);
                return false;
        }

        sc->sc_sme->sme_cookie = sc;
        sc->sc_sme->sme_refresh = dstemp_refresh;
        sc->sc_sme->sme_name = device_xname(self);

        sc->sc_sme->sme_get_limits = NULL;
        sc->sc_sme->sme_set_limits = NULL;

        if (sc->sc_type != DSTEMP_TYPE_DS1624 &&
            sc->sc_type != DSTEMP_TYPE_DS1721) {
                sc->sc_sme->sme_get_limits = dstemp_get_limits;
                sc->sc_sme->sme_set_limits = dstemp_set_limits;
        }

        if (sysmon_envsys_register(sc->sc_sme) != 0) {
                sysmon_envsys_destroy(sc->sc_sme);
                return false;
        }

        return true;
}

static void
dstemp_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
                  sysmon_envsys_lim_t *limits)
{
        struct dstemp_softc *sc = sme->sme_cookie;
        uint16_t hi, lo;

        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1624);
        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1721);

        hi = lo = 0xFFFF;

        iic_acquire_bus(sc->sc_tag, 0);

        (void)dstemp_read_16(sc, DS1621_REG_ACCESS_TH, &hi);
        (void)dstemp_read_16(sc, DS1621_REG_ACCESS_TL, &lo);

        iic_release_bus(sc->sc_tag, 0);

        if (hi != 0xFFFF && (hi & __BIT(15)) == 0) {
                limits->sel_warnmax = DSTEMP_DECODE(hi);
                limits->sel_flags |= PROP_WARNMAX;
        }

        if (lo != 0xFFFF && (lo & __BIT(15)) == 0) {
                limits->sel_warnmin = DSTEMP_DECODE(lo);
                limits->sel_flags |= PROP_WARNMIN;
        }

        if (hi == 0xFFFF || lo == 0xFFFF)
                aprint_debug_dev(sc->sc_dev, "failed to get limits\n");
}

static void
dstemp_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
                  sysmon_envsys_lim_t *limits)
{
        struct dstemp_softc *sc = sme->sme_cookie;
        uint16_t lim;

        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1624);
        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1721);

        iic_acquire_bus(sc->sc_tag, 0);

        /*
         * We must stop conversions when making
         * changes to the trip-point registers.
         */
        if (dstemp_enable(sc, false) != true) {
                iic_release_bus(sc->sc_tag, 0);
                return;
        }

        if ((limits->sel_flags & PROP_WARNMAX) != 0) {

                lim = DSTEMP_ENCODE(limits->sel_warnmax);

                if (dstemp_write_16(sc, DS1621_REG_ACCESS_TH, lim) != true)
                        aprint_debug_dev(sc->sc_dev, "failed to set limits\n");
        }

        if ((limits->sel_flags & PROP_WARNMIN) != 0) {

                lim = DSTEMP_ENCODE(limits->sel_warnmin);

                if (dstemp_write_16(sc, DS1621_REG_ACCESS_TL, lim) != true)
                        aprint_debug_dev(sc->sc_dev, "failed to set limits\n");
        }

        if (dstemp_enable(sc, true) != true) {
                iic_release_bus(sc->sc_tag, 0);
                return;
        }

        iic_release_bus(sc->sc_tag, 0);

        /*
         * If and only if PROP_LIMITS is set and
         * we can handle all requested limits,
         * take over the processing of limits.
         */
        limits->sel_flags &= ~PROP_DRIVER_LIMITS;

        if ((limits->sel_flags & NOPROP) != 0)
                return;

        if ((limits->sel_flags & PROP_LIMITS) == 0)
                return;

        limits->sel_flags |= PROP_DRIVER_LIMITS;
}

static bool
dstemp_check_limits(struct dstemp_softc *sc)
{
        uint8_t buf, val;

        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1624);
        KDASSERT(sc->sc_type != DSTEMP_TYPE_DS1721);

        iic_acquire_bus(sc->sc_tag, I2C_F_POLL);

        if (__predict_false(dstemp_read_ctrl(sc, &buf) != true))
                goto fail;

        val = buf;

        if (__predict_false((buf & DS1621_CTRL_THF) != 0)) {
                val &= ~DS1621_CTRL_THF;
                sc->sc_sensor.state = ENVSYS_SWARNOVER;
        }

        if (__predict_false((buf & DS1621_CTRL_TLF) != 0)) {
                val &= ~DS1621_CTRL_TLF;
                sc->sc_sensor.state = ENVSYS_SWARNUNDER;
                KASSERT(sc->sc_sensor.state != ENVSYS_SWARNOVER);
        }

        if (buf != val) {

                if (__predict_false(dstemp_enable(sc, false) != true))
                        goto fail;

                if (__predict_false(dstemp_write_ctrl(sc, val) != true))
                        goto fail;

                if (__predict_false(dstemp_enable(sc, true) != true))
                        goto fail;
        }

        iic_release_bus(sc->sc_tag, 0);

        return true;

fail:
        iic_release_bus(sc->sc_tag, 0);

        return false;
}

static void
dstemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
        struct dstemp_softc *sc = sme->sme_cookie;
        uint16_t temp;
        bool rv;

        iic_acquire_bus(sc->sc_tag, 0);
        rv = dstemp_read_16(sc, DSTEMP_REG_READ_TEMP, &temp);
        iic_release_bus(sc->sc_tag, 0);

        if (__predict_false(rv != true)) {
                aprint_debug_dev(sc->sc_dev, "failed to read temperature\n");
                sc->sc_sensor.state = ENVSYS_SINVALID;
                return;
        }

        /*
         * It is unlikely that NetBSD will ever run in a freezer.
         */
        if (__predict_false((temp & __BIT(15)) != 0)) {
                aprint_debug_dev(sc->sc_dev, "temperature below zero?\n");
                sc->sc_sensor.state = ENVSYS_SINVALID;
                return;
        }

        sc->sc_sensor.state = ENVSYS_SVALID;
        sc->sc_sensor.value_cur = DSTEMP_DECODE(temp);

        if (sc->sc_type == DSTEMP_TYPE_DS1624 ||
            sc->sc_type == DSTEMP_TYPE_DS1721)
                return;

        if ((sc->sc_sensor.upropset & PROP_DRIVER_LIMITS) == 0)
                return;

        KDASSERT((sc->sc_sensor.upropset & NOPROP) == 0);

        if (__predict_false(dstemp_check_limits(sc) != true))
                aprint_debug_dev(sc->sc_dev, "failed to evaluate limits\n");
}

static bool
dstemp_enable(struct dstemp_softc *sc, const bool enable)
{
        bool rv;
        uint8_t cmd;
        const char *str;

        cmd = (enable != true) ? DSTEMP_REG_CONV_STOP : sc->sc_start;

        rv = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
            sc->sc_addr, &cmd, 1, NULL, 0, 0);

        if (__predict_false(rv != 0)) {

                str = (enable != true) ? "disable" : "enable";
                aprint_debug_dev(sc->sc_dev, "failed to %s device\n", str);

                return false;
        }

        return true;
}

static bool
dstemp_read_ctrl(struct dstemp_softc *sc, uint8_t *valp)
{
        uint8_t buf, cmd = DSTEMP_REG_ACCESS_CTRL;

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

        *valp = buf;

        return true;
}

static bool
dstemp_write_ctrl(struct dstemp_softc *sc, const uint8_t val)
{
        uint8_t cmd = DSTEMP_REG_ACCESS_CTRL;

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

        return true;
}


static bool
dstemp_read_16(struct dstemp_softc *sc, const uint8_t cmd, uint16_t *valp)
{
        uint16_t buf;

        if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
                sc->sc_addr, &cmd, 1, &buf, 2, 0) != 0) {
                return false;
        }

        *valp = be16toh(buf);

        return true;
}

static bool
dstemp_write_16(struct dstemp_softc *sc, const uint8_t cmd, const uint16_t val)
{
        uint16_t buf;

        buf = htobe16(val);

        if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
                sc->sc_addr, &cmd, 1, &buf, 2, 0) != 0)
                return false;

        return true;
}

static bool
dstemp_suspend(device_t dv PMF_FN_ARGS)
{
        struct dstemp_softc *sc = device_private(dv);
        bool rv;

        iic_acquire_bus(sc->sc_tag, 0);
        rv = dstemp_enable(sc, false);
        iic_release_bus(sc->sc_tag, 0);

        return rv;
}

static bool
dstemp_resume(device_t dv PMF_FN_ARGS)
{
        struct dstemp_softc *sc = device_private(dv);
        bool rv;

        iic_acquire_bus(sc->sc_tag, 0);
        rv = dstemp_enable(sc, true);
        iic_release_bus(sc->sc_tag, 0);

        return rv;
}

Reply via email to