NetBSD-Bugs archive

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

kern/52972: Support additional devices for dstemp(4)



>Number:         52972
>Category:       kern
>Synopsis:       enhancements for dstemp(4)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Feb 01 23:25:00 +0000 2018
>Originator:     Paul Goyette
>Release:        NetBSD 8.99.7
>Organization:
+------------------+--------------------------+----------------------------+
| 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 |
+------------------+--------------------------+----------------------------+
>Environment:
	
	
System: NetBSD speedy.whooppee.com 8.99.7 NetBSD 8.99.7 (SPEEDY 2017-11-29 22:03:05 UTC) #0: Wed Nov 29 23:14:50 UTC 2017 paul%speedy.whooppee.com@localhost:/build/netbsd-local/obj/amd64/sys/arch/amd64/compile/SPEEDY amd64
Architecture: x86_64
Machine: amd64
>Description:
I've had the following driver sitting in my INBOX for several years;  it
was never imported because the submitter had disappeared and I didn't have
appropriate hardware.

Now that we have a basic driver for one sensor model, it shouldn't be too
difficult to merge this code into the recently-committed dstemp(4) driver!

--------------

/*	$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;
}
	
>How-To-Repeat:
	
>Fix:
	

>Unformatted:
 	
 	


Home | Main Index | Thread Index | Old Index