Module Name:    src
Committed By:   pgoyette
Date:           Sat May  9 15:04:25 UTC 2009

Modified Files:
        src/distrib/sets/lists/man: mi
        src/share/man/man4: Makefile
        src/sys/arch/i386/conf: ALL
        src/sys/dev/i2c: files.i2c
Added Files:
        src/share/man/man4: sdtemp.4
        src/sys/dev/i2c: sdtemp.c sdtemp_reg.h

Log Message:
Initial implementation of sdtemp(4) driver for on-DIMM temp sensor.

(These optional sensors are specified by JEDEC Standard No. 21-C
Section 4-7 and implemented by multiple vendors.  Tested on my
amd64 machine with Kingston KVR1066D3E7S/2G memory which includes
a STMicro STTS424E02 sensor.)


To generate a diff of this commit:
cvs rdiff -u -r1.1135 -r1.1136 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.490 -r1.491 src/share/man/man4/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man4/sdtemp.4
cvs rdiff -u -r1.197 -r1.198 src/sys/arch/i386/conf/ALL
cvs rdiff -u -r1.23 -r1.24 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/sdtemp.c src/sys/dev/i2c/sdtemp_reg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1135 src/distrib/sets/lists/man/mi:1.1136
--- src/distrib/sets/lists/man/mi:1.1135	Fri May  1 22:59:53 2009
+++ src/distrib/sets/lists/man/mi	Sat May  9 15:04:25 2009
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1135 2009/05/01 22:59:53 agc Exp $
+# $NetBSD: mi,v 1.1136 2009/05/09 15:04:25 pgoyette Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1360,6 +1360,7 @@
 ./usr/share/man/cat4/scsi.0			man-sys-catman		.cat
 ./usr/share/man/cat4/scsibus.0			man-sys-catman		.cat
 ./usr/share/man/cat4/sd.0			man-sys-catman		.cat
+./usr/share/man/cat4/sdtemp.0			man-sys-catman		.cat
 ./usr/share/man/cat4/se.0			man-sys-catman		.cat
 ./usr/share/man/cat4/sea.0			man-sys-catman		.cat
 ./usr/share/man/cat4/sec.0			man-sys-catman		.cat
@@ -3888,6 +3889,7 @@
 ./usr/share/man/html4/scsi.html			man-sys-htmlman		html
 ./usr/share/man/html4/scsibus.html		man-sys-htmlman		html
 ./usr/share/man/html4/sd.html			man-sys-htmlman		html
+./usr/share/man/html4/sdtemp.html		man-sys-htmlman		html
 ./usr/share/man/html4/se.html			man-sys-htmlman		html
 ./usr/share/man/html4/sea.html			man-sys-htmlman		html
 ./usr/share/man/html4/sec.html			man-sys-htmlman		html
@@ -6328,6 +6330,7 @@
 ./usr/share/man/man4/scsi.4			man-sys-man		.man
 ./usr/share/man/man4/scsibus.4			man-sys-man		.man
 ./usr/share/man/man4/sd.4			man-sys-man		.man
+./usr/share/man/man4/sdtemp.4			man-sys-man		.man
 ./usr/share/man/man4/se.4			man-sys-man		.man
 ./usr/share/man/man4/sea.4			man-sys-man		.man
 ./usr/share/man/man4/sec.4			man-sys-man		.man

Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.490 src/share/man/man4/Makefile:1.491
--- src/share/man/man4/Makefile:1.490	Tue Apr 21 21:42:53 2009
+++ src/share/man/man4/Makefile	Sat May  9 15:04:25 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.490 2009/04/21 21:42:53 pgoyette Exp $
+#	$NetBSD: Makefile,v 1.491 2009/05/09 15:04:25 pgoyette Exp $
 #	@(#)Makefile	8.1 (Berkeley) 6/18/93
 
 MAN=	aac.4 ac97.4 acardide.4 aceride.4 acphy.4 acpidalb.4 \
@@ -114,7 +114,7 @@
 MAN+=	fwohci.4 fwip.4 sbp.4
 
 # machine-independent I2C devices
-MAN+=	dbcool.4 lmtemp.4 spdmem.4
+MAN+=	dbcool.4 lmtemp.4 sdtemp.4 spdmem.4
 
 # machine-independent SPI devices
 MAN +=	m25p.4 tm121temp.4

Index: src/sys/arch/i386/conf/ALL
diff -u src/sys/arch/i386/conf/ALL:1.197 src/sys/arch/i386/conf/ALL:1.198
--- src/sys/arch/i386/conf/ALL:1.197	Tue Apr 21 22:47:55 2009
+++ src/sys/arch/i386/conf/ALL	Sat May  9 15:04:25 2009
@@ -1,4 +1,4 @@
-# $NetBSD: ALL,v 1.197 2009/04/21 22:47:55 nonaka Exp $
+# $NetBSD: ALL,v 1.198 2009/05/09 15:04:25 pgoyette Exp $
 # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp
 #
 # ALL machine description file
@@ -17,7 +17,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"ALL-$Revision: 1.197 $"
+#ident 		"ALL-$Revision: 1.198 $"
 
 maxusers	32		# estimated number of users
 
@@ -666,8 +666,9 @@
 # VIA VT82C686A/VT8231 Hardware Monitor and Power Management Timer
 viaenv* 	at pci? dev ? function ?
 
-# Serial Presence Detect capable memory modules
+# Serial Presence Detect capable memory modules and optional temp sensors
 spdmem*	at iic? addr 0x50
+sdtemp*	at iic? addr 0x18
 
 # I2O devices
 iop*	at pci? dev ? function ?	# I/O processor

Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.23 src/sys/dev/i2c/files.i2c:1.24
--- src/sys/dev/i2c/files.i2c:1.23	Thu Oct 30 12:52:46 2008
+++ src/sys/dev/i2c/files.i2c	Sat May  9 15:04:25 2009
@@ -1,4 +1,4 @@
-#	$NetBSD: files.i2c,v 1.23 2008/10/30 12:52:46 nakayama Exp $
+#	$NetBSD: files.i2c,v 1.24 2009/05/09 15:04:25 pgoyette Exp $
 
 defflag	opt_i2cbus.h				I2C_SCAN
 define	i2cbus { }
@@ -106,6 +106,11 @@
 attach	spdmem at iic
 file	dev/i2c/spdmem.c		spdmem
 
+# Memory Temp Sensor
+device	sdtemp
+attach	sdtemp at iic
+file	dev/i2c/sdtemp.c		sdtemp
+
 # ADM1021
 device	admtemp: sysmon_envsys
 attach	admtemp at iic

Added files:

Index: src/share/man/man4/sdtemp.4
diff -u /dev/null src/share/man/man4/sdtemp.4:1.1
--- /dev/null	Sat May  9 15:04:26 2009
+++ src/share/man/man4/sdtemp.4	Sat May  9 15:04:25 2009
@@ -0,0 +1,99 @@
+.\"	$NetBSD: sdtemp.4,v 1.1 2009/05/09 15:04:25 pgoyette Exp $
+.\"
+.\" Copyright (c) 2008 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Paul Goyette.
+.\"
+.\" 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.
+.\"
+.Dd May 9, 2009
+.Dt SDTEMP 4
+.Os
+.Sh NAME
+.Nm sdtemp
+.Nd Microchip Technology MCP9805/98242 and compatible memory module
+temperature sensors
+.Sh SYNOPSIS
+.Cd "sdtemp0 at iic? addr 0x18"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Tn Microchip Technology
+MCP9805/98242 and other chips that conform to JEDEC Standard 21-C section
+4.7.
+Memory module temperature sensors are optional on DDR2 and DDR3 SDRAM DIMMs.
+Sensors provided by this driver are accessed through the
+.Xr envsys 4
+API.
+The on-chip thresholds for the Alarm Window and Critical level are
+accessed through the
+.Xr sysctl 8
+utility.
+.Pp
+The
+.Nm
+supports temperature ranges from -256 to +255 degrees C.
+.Sh HARDWARE
+Chips supported by the
+.Nm
+driver include:
+.Pp
+.Bl -item -offset indent
+.It
+.Tn Microchip Technology
+.Em MCP9805
+.It
+.Tn Microchip Technology
+.Em MCP98242
+.It
+.Tn Analog Devices
+.Em ADT7408
+.It
+.Tn NXP Semiconductors
+.Em SE97
+.It
+.Tn NXP Semiconductors
+.Em SE98
+.It
+.Tn STmicroelectronics
+.Em STTS424E02-DA/DN
+.It
+.Tn Catalyst
+.Em CAT34TS02
+.It
+.Tn Catalyst
+.Em CAT6095
+.El
+.Sh SEE ALSO
+.Xr envsys 4 ,
+.Xr envstat 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+device appeared in
+.Nx 6.0 .
+.Sh BUGS
+Interrupt support is unimplemented.

Index: src/sys/dev/i2c/sdtemp.c
diff -u /dev/null src/sys/dev/i2c/sdtemp.c:1.1
--- /dev/null	Sat May  9 15:04:26 2009
+++ src/sys/dev/i2c/sdtemp.c	Sat May  9 15:04:25 2009
@@ -0,0 +1,589 @@
+/*      $NetBSD: sdtemp.c,v 1.1 2009/05/09 15:04:25 pgoyette Exp $        */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Goyette.
+ *
+ * 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: sdtemp.c,v 1.1 2009/05/09 15:04:25 pgoyette Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kmem.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/sysctl.h>
+
+#include <dev/sysmon/sysmonvar.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/sdtemp_reg.h>
+
+struct sdtemp_softc {
+	device_t sc_dev;
+	i2c_tag_t sc_tag;
+	int sc_address;
+
+	struct sysmon_envsys *sc_sme;
+	envsys_data_t *sc_sensor;
+	int sc_resolution;
+	uint16_t sc_capability;
+	uint16_t sc_low_lim, sc_high_lim, sc_crit_lim;
+};
+
+static int  sdtemp_match(device_t, cfdata_t, void *);
+static void sdtemp_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(sdtemp, sizeof(struct sdtemp_softc),
+	sdtemp_match, sdtemp_attach, NULL, NULL);
+
+static void	sdtemp_refresh(struct sysmon_envsys *, envsys_data_t *);
+#ifdef NOT_YET
+static int	sdtemp_read_8(struct sdtemp_softc *, uint8_t, uint8_t *);
+static int	sdtemp_write_8(struct sdtemp_softc *, uint8_t, uint8_t);
+#endif /* NOT YET */
+static int	sdtemp_read_16(struct sdtemp_softc *, uint8_t, uint16_t *);
+static int	sdtemp_write_16(struct sdtemp_softc *, uint8_t, uint16_t);
+static uint32_t	sdtemp_decode_temp(struct sdtemp_softc *, uint16_t);
+static void	sdtemp_set_thresh(struct sdtemp_softc *, int, uint16_t);
+static bool	sdtemp_pmf_suspend(device_t PMF_FN_PROTO);
+static bool	sdtemp_pmf_resume(device_t PMF_FN_PROTO);
+
+SYSCTL_SETUP_PROTO(sysctl_sdtemp_setup);
+static int sdtemp_sysctl_helper(SYSCTLFN_PROTO);
+
+struct sdtemp_dev_entry {
+	const uint16_t sdtemp_mfg_id;
+	const uint8_t  sdtemp_dev_id;
+	const uint8_t  sdtemp_rev_id;
+	const uint8_t  sdtemp_resolution;
+	const char    *sdtemp_desc;
+};
+
+/* sysctl stuff */
+static int hw_node = CTL_EOL;
+
+/*
+ * List of devices known to conform to JEDEC JC42.4
+ *
+ * NOTE: A non-negative value for resolution indicates that the sensor
+ * resolution is fixed at that number of fractional bits;  a negative
+ * value indicates that the sensor needs to be configured.  In either
+ * case, trip-point registers are fixed at two-bit (0.25C) resolution.
+ */
+static const struct sdtemp_dev_entry
+sdtemp_dev_table[] = {
+    { MAXIM_MANUFACTURER_ID, MAX_6604_DEVICE_ID,    0xff, 3,
+	"Maxim MAX604" },
+    { MCP_MANUFACTURER_ID,   MCP_9805_DEVICE_ID,    0xff, 2,
+	"Microchip Tech MCP9805" },
+    { MCP_MANUFACTURER_ID,   MCP_98242_DEVICE_ID,   0xff, -4,
+	"Microchip Tech MCP98242" },
+    { ADT_MANUFACTURER_ID,   ADT_7408_DEVICE_ID,    0xff, 4,
+	"Analog Devices ADT7408" },
+    { NXP_MANUFACTURER_ID,   NXP_SE97_DEVICE_ID,    0xff, 3,
+	"NXP Semiconductors SE97/SE98" },
+    { STTS_MANUFACTURER_ID,  STTS_424E02_DEVICE_ID, 0x00, 2,
+	"STmicroelectronics STTS424E02-DA" }, 
+    { STTS_MANUFACTURER_ID,  STTS_424E02_DEVICE_ID, 0x01, 2,
+	"STmicroelectronics STTS424E02-DN" }, 
+    { CAT_MANUFACTURER_ID,   CAT_34TS02_DEVICE_ID,  0xff, 4,
+	"Catalyst CAT34TS02/CAT6095" },
+    { 0, 0, 0, 2, "Unknown" }
+};
+
+static int
+sdtemp_lookup(uint16_t mfg, uint16_t dev, uint16_t rev)
+{
+	int i;
+
+	for (i = 0; sdtemp_dev_table[i].sdtemp_mfg_id; i++)
+		if (sdtemp_dev_table[i].sdtemp_mfg_id == mfg &&
+		    sdtemp_dev_table[i].sdtemp_dev_id == dev &&
+		    (sdtemp_dev_table[i].sdtemp_rev_id == 0xff ||
+		     sdtemp_dev_table[i].sdtemp_rev_id == rev))
+			break;
+
+	return i;
+}
+
+static int
+sdtemp_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+	uint16_t mfgid, devid;
+	struct sdtemp_softc sc;
+	int i, error;
+
+	sc.sc_tag = ia->ia_tag;
+	sc.sc_address = ia->ia_addr;
+
+	if ((ia->ia_addr & SDTEMP_ADDRMASK) != SDTEMP_ADDR)
+		return 0;
+
+	/* Verify that we can read the manufacturer ID  & Device ID */
+	iic_acquire_bus(sc.sc_tag, 0);
+	error = sdtemp_read_16(&sc, SDTEMP_REG_MFG_ID,  &mfgid) |
+		sdtemp_read_16(&sc, SDTEMP_REG_DEV_REV, &devid);
+	iic_release_bus(sc.sc_tag, 0);
+
+	if (error)
+		return 0;
+
+	i = sdtemp_lookup(mfgid, devid >> 8, devid & 0xff);
+	if (sdtemp_dev_table[i].sdtemp_mfg_id == 0) {
+		aprint_debug("sdtemp: No match for mfg 0x%04x dev 0x%02x "
+		    "rev 0x%02x at address 0x%02x\n", mfgid, devid >> 8,
+		    devid & 0xff, sc.sc_address);
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+sdtemp_attach(device_t parent, device_t self, void *aux)
+{
+	struct sdtemp_softc *sc = device_private(self);
+	struct i2c_attach_args *ia = aux;
+	const struct sysctlnode *node = NULL;
+	uint16_t mfgid, devid;
+	int32_t	dev_sysctl_num;
+	int i, error;
+
+	sc->sc_tag = ia->ia_tag;
+	sc->sc_address = ia->ia_addr;
+	sc->sc_dev = self;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	if ((error = sdtemp_read_16(sc, SDTEMP_REG_MFG_ID,  &mfgid)) != 0 ||
+	    (error = sdtemp_read_16(sc, SDTEMP_REG_DEV_REV, &devid)) != 0) {
+		iic_release_bus(sc->sc_tag, I2C_F_POLL);
+		aprint_error(": attach error %d\n", error);
+		return;
+	}
+	i = sdtemp_lookup(mfgid, devid >> 8, devid & 0xff);
+	sc->sc_resolution =
+	    sdtemp_dev_table[i].sdtemp_resolution;
+
+	aprint_naive(": Temp Sensor\n");
+	aprint_normal(": %s Temp Sensor\n", sdtemp_dev_table[i].sdtemp_desc);
+
+	if (sdtemp_dev_table[i].sdtemp_mfg_id == 0)
+		aprint_debug_dev(self,
+		    "mfg 0x%04x dev 0x%02x rev 0x%02x at addr 0x%02x\n",
+		    mfgid, devid >> 8, devid & 0xff, ia->ia_addr);
+
+	/*
+	 * Alarm capability is required;  if not present, this is likely
+	 * not a real sdtemp device.
+	 */
+	error = sdtemp_read_16(sc, SDTEMP_REG_CAPABILITY, &sc->sc_capability);
+	if (error != 0 || (sc->sc_capability & SDTEMP_CAP_HAS_ALARM) == 0) {
+		iic_release_bus(sc->sc_tag, 0);
+		aprint_error_dev(self,
+		    "required alarm capability not present!\n");
+		return;
+	}
+	/* Set the configuration to defaults. */
+	error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, 0);
+	if (error != 0) {
+		iic_release_bus(sc->sc_tag, 0);
+		aprint_error_dev(self, "error %d writing config register\n",
+		    error);
+		return;
+	}
+	/* If variable resolution, set to max */
+	if (sc->sc_resolution < 0) {
+		sc->sc_resolution = ~sc->sc_resolution;
+		error = sdtemp_write_16(sc, SDTEMP_REG_RESOLUTION,
+					sc->sc_resolution & 0x3);
+		if (error != 0) {
+			iic_release_bus(sc->sc_tag, 0);
+			aprint_error_dev(self,
+			    "error %d writing resolution register\n", error);
+			return;
+		} else
+			sc->sc_resolution++;
+	}
+	iic_release_bus(sc->sc_tag, 0);
+
+	/* Hook us into the sysmon_envsys subsystem */
+	sc->sc_sme = sysmon_envsys_create();
+	sc->sc_sensor = kmem_zalloc(sizeof(envsys_data_t), KM_NOSLEEP);
+	if (!sc->sc_sensor) {
+		aprint_error_dev(self, "unable to allocate sc_sensor\n");
+		goto bad2;
+	}
+
+	/* Initialize sensor data. */
+	sc->sc_sensor->units =  ENVSYS_STEMP;
+	sc->sc_sensor->state = ENVSYS_SINVALID;
+#ifdef ENVSYS_FMONLIMITS
+	sc->sc_sensor->flags |= ENVSYS_FMONLIMITS;
+#else
+	sc->sc_sensor->flags |= ENVSYS_FMONWARNOVER | ENVSYS_FMONWARNUNDER |
+				ENVSYS_FMONCRITOVER;
+#endif
+	(void)strlcpy(sc->sc_sensor->desc, device_xname(self),
+	    sizeof(sc->sc_sensor->desc));
+
+	/* Now attach the sensor */
+	if (sysmon_envsys_sensor_attach(sc->sc_sme, sc->sc_sensor)) {
+		aprint_error_dev(self, "unable to attach sensor\n");
+		goto bad;
+	}
+
+	/* Register the device */
+	sc->sc_sme->sme_name = device_xname(self);
+	sc->sc_sme->sme_cookie = sc;
+	sc->sc_sme->sme_refresh = sdtemp_refresh;
+
+	error = sysmon_envsys_register(sc->sc_sme);
+	if (error) {
+		aprint_error_dev(self, "error %d registering with sysmon\n",
+		    error);
+		goto bad;
+	}
+
+	if (!pmf_device_register(self, sdtemp_pmf_suspend, sdtemp_pmf_resume))
+		aprint_error_dev(self, "couldn't establish power handler\n");
+
+
+	/* Retrieve and display hardware monitor limits */
+	i = 0;
+	aprint_normal_dev(self, "");
+	iic_acquire_bus(sc->sc_tag, 0);
+	if (sdtemp_read_16(sc, SDTEMP_REG_LOWER_LIM, &sc->sc_low_lim) == 0 &&
+	    sc->sc_low_lim != 0) {
+		aprint_normal("low limit %d ", sc->sc_low_lim);
+		i++;
+	}
+	if (sdtemp_read_16(sc, SDTEMP_REG_UPPER_LIM, &sc->sc_high_lim) == 0 &&
+	    sc->sc_high_lim != 0) {
+		aprint_normal("high limit %d ", sc->sc_high_lim);
+		i++;
+	}
+	if (sdtemp_read_16(sc, SDTEMP_REG_CRIT_LIM, &sc->sc_crit_lim) == 0 &&
+	    sc->sc_crit_lim != 0) {
+		aprint_normal("critical limit %d ", sc->sc_crit_lim);
+		i++;
+	}
+	iic_release_bus(sc->sc_tag, 0);
+	if (i == 0)
+		aprint_normal("no hardware limits set\n");
+	else
+		aprint_normal("\n");
+
+	/* Create our sysctl tree.  We just store the softc pointer for
+	 * now;  the sysctl_helper function will take care of creating
+	 * a real string on the fly.  We explicitly specify the new nodes'
+	 * sysctl_num in order to identify the specific limit rather than
+	 * using CTL_CREATE;  this is OK since we're the only place that
+	 * touches the sysctl tree for the device.
+	 */
+
+	if (hw_node != CTL_EOL)
+		sysctl_createv(NULL, 0, NULL, &node, 0,
+		    CTLTYPE_NODE, device_xname(self),
+		    NULL, NULL, 0, NULL, 0,
+		    CTL_HW, CTL_CREATE, CTL_EOL);
+	if (node != NULL) {
+		dev_sysctl_num = node->sysctl_num;
+		sysctl_createv(NULL, 0, NULL, &node, 0,
+		    CTLTYPE_NODE, "limits",
+		    SYSCTL_DESCR("temperature limits"),
+		    NULL, 0, NULL, 0, 
+		    CTL_HW, node->sysctl_num, CTL_CREATE, CTL_EOL);
+	}
+	if (node != NULL) {
+		sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
+		    CTLTYPE_INT, "low_limit",
+		    SYSCTL_DESCR("alarm window lower limit"),
+		    sdtemp_sysctl_helper, 0, sc, sizeof(int),
+		    CTL_HW, dev_sysctl_num, node->sysctl_num,
+			SDTEMP_REG_LOWER_LIM, CTL_EOL);
+		sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
+		    CTLTYPE_INT, "high_limit",
+		    SYSCTL_DESCR("alarm window upper limit"),
+		    sdtemp_sysctl_helper, 0, sc, sizeof(int),
+		    CTL_HW, dev_sysctl_num, node->sysctl_num, 
+			SDTEMP_REG_UPPER_LIM, CTL_EOL);
+		sysctl_createv(NULL, 0, NULL, NULL, CTLFLAG_READWRITE,
+		    CTLTYPE_INT, "crit_limit",
+		    SYSCTL_DESCR("critical alarm limit"),
+		    sdtemp_sysctl_helper, 0, sc, sizeof(int),
+		    CTL_HW, dev_sysctl_num, node->sysctl_num,
+			SDTEMP_REG_CRIT_LIM, CTL_EOL);
+	}
+	return;
+
+bad:
+	kmem_free(sc->sc_sensor, sizeof(envsys_data_t));
+bad2:
+	sysmon_envsys_destroy(sc->sc_sme);
+}
+
+/* Set up the threshold registers */
+static void
+sdtemp_set_thresh(struct sdtemp_softc *sc, int reg, uint16_t val)
+{
+	int error;
+	uint16_t *valp;
+
+	switch (reg) {
+	case SDTEMP_REG_LOWER_LIM:
+		valp = &sc->sc_low_lim;
+		break;
+	case SDTEMP_REG_UPPER_LIM:
+		valp = &sc->sc_high_lim;
+		break;
+	case SDTEMP_REG_CRIT_LIM:
+		valp = &sc->sc_crit_lim;
+		break;
+	default:
+		return;
+	}
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	error = sdtemp_write_16(sc, reg, (val << 4) & SDTEMP_TEMP_MASK);
+	iic_release_bus(sc->sc_tag, 0);
+
+	if (error == 0)
+		*valp = val;
+}
+
+#ifdef NOT_YET	/* All registers on these sensors are 16-bits */
+
+/* Read a 8-bit value from a register */
+static int
+sdtemp_read_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t *valp)
+{
+	int error;
+
+	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+	    sc->sc_address, &reg, 1, valp, sizeof(*valp), 0);
+
+	return error;
+}
+
+static int
+sdtemp_write_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t val)
+{
+	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+	    sc->sc_address, &reg, 1, &val, sizeof(val), 0);
+}
+#endif /* NOT_YET */
+
+/* Read a 16-bit value from a register */
+static int
+sdtemp_read_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t *valp)
+{
+	int error;
+
+	error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
+	    sc->sc_address, &reg, 1, valp, sizeof(*valp), 0);
+	if (error)
+		return error;
+
+	*valp = be16toh(*valp);
+
+	return 0;
+}
+
+static int
+sdtemp_write_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t val)
+{
+	uint16_t temp;
+
+	temp = htobe16(val);
+	return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+	    sc->sc_address, &reg, 1, &temp, sizeof(temp), 0);
+}
+
+static uint32_t
+sdtemp_decode_temp(struct sdtemp_softc *sc, uint16_t temp)
+{
+	uint32_t val;
+	int32_t stemp;
+
+	/* Get only the temperature bits */
+	temp &= SDTEMP_TEMP_MASK;
+
+	/* If necessary, extend the sign bit */
+	if ((sc->sc_capability & SDTEMP_CAP_WIDER_RANGE) &&
+	    (temp & SDTEMP_TEMP_NEGATIVE))
+		temp |= SDTEMP_TEMP_SIGN_EXT;
+
+	/* Mask off only bits valid within current resolution */
+	temp &= ~(0xf >> sc->sc_resolution);
+
+	/* Treat as signed and extend to 32-bits */
+	stemp = (int16_t)temp;
+
+	/* Now convert from 0.0625 (1/16) deg C increments to microKelvins */
+	val = (stemp * 62500) + 273150000;
+
+	return val;
+}
+
+static void
+sdtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
+{
+	struct sdtemp_softc *sc = sme->sme_cookie;
+	uint16_t val;
+	int error;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	error = sdtemp_read_16(sc, SDTEMP_REG_AMBIENT_TEMP, &val);
+	iic_release_bus(sc->sc_tag, 0);
+
+	if (error) {
+		edata->state = ENVSYS_SINVALID;
+		return;
+	}
+
+	edata->value_cur = sdtemp_decode_temp(sc, val);
+
+	/* Now check for limits */
+	if (val & SDTEMP_ABOVE_CRIT)
+		edata->state = ENVSYS_SCRITOVER;
+	else if (val & SDTEMP_ABOVE_UPPER)
+		edata->state = ENVSYS_SWARNOVER;
+	else if (val & SDTEMP_BELOW_LOWER)
+		edata->state = ENVSYS_SWARNUNDER;
+	else
+		edata->state = ENVSYS_SVALID;
+}
+
+SYSCTL_SETUP(sysctl_sdtemp_setup, "sysctl hw.sdtemp subtree setup")
+{
+	const struct sysctlnode *node;
+
+	if (sysctl_createv(clog, 0, NULL, &node,
+			   CTLFLAG_PERMANENT,
+			   CTLTYPE_NODE, "hw", NULL,
+			   NULL, 0, NULL, 0,
+			   CTL_HW, CTL_EOL) != 0)
+		return;
+
+	hw_node = node->sysctl_num;
+}
+
+/*
+ * The sysctl node actually contains just a pointer to our softc.  We
+ * extract the individual limits on the fly, and if necessary replace
+ * the value with the new value specified by the user.
+ *
+ * Inspired by similar code in sys/net/if_tap.c
+ */
+static int
+sdtemp_sysctl_helper(SYSCTLFN_ARGS)
+{
+	struct sdtemp_softc *sc;
+	struct sysctlnode node;
+	int error, reg;
+	uint16_t reg_value;
+	int lim_value;
+
+	node = *rnode;
+	sc = node.sysctl_data;
+	reg = node.sysctl_num;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	error = sdtemp_read_16(sc, reg, &reg_value);
+	iic_release_bus(sc->sc_tag, 0);
+
+#ifdef DEBUG
+	aprint_verbose_dev(sc->sc_dev, "(%s) sc %p reg %d val 0x%04x err %d\n",
+	    __func__, sc, reg, reg_value, error);
+#endif
+
+	if (error == 0) {
+		lim_value = reg_value >> 4;
+		node.sysctl_data = &lim_value;
+		error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	}
+	if (error || newp == NULL)
+		return (error);
+
+	/*
+	 * We're being asked to update the sysctl value, so retrieve
+	 * the new value and check for valid range
+	 */
+	lim_value = *(int *)node.sysctl_data;
+	if (lim_value < -256 || lim_value > 255)
+		return (EINVAL);
+
+	sdtemp_set_thresh(sc, reg, (uint16_t)lim_value);
+
+	return (0);
+}
+
+/*
+ * power management functions
+ *
+ * We go into "shutdown" mode at suspend time, and return to normal
+ * mode upon resume.  This reduces power consumption by disabling
+ * the A/D converter.
+ */
+
+static bool
+sdtemp_pmf_suspend(device_t dev PMF_FN_ARGS)
+{
+	struct sdtemp_softc *sc = device_private(dev);
+	int error;
+	uint16_t config;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config);
+	if (error == 0) {
+		config |= SDTEMP_CONFIG_SHUTDOWN_MODE;
+		error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config);
+	}
+	iic_release_bus(sc->sc_tag, 0);
+	return (error == 0);
+}
+
+static bool
+sdtemp_pmf_resume(device_t dev PMF_FN_ARGS)
+{
+	struct sdtemp_softc *sc = device_private(dev);
+	int error;
+	uint16_t config;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config);
+	if (error == 0) {
+		config &= ~SDTEMP_CONFIG_SHUTDOWN_MODE;
+		error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config);
+	}
+	iic_release_bus(sc->sc_tag, 0);
+	return (error == 0);
+}
Index: src/sys/dev/i2c/sdtemp_reg.h
diff -u /dev/null src/sys/dev/i2c/sdtemp_reg.h:1.1
--- /dev/null	Sat May  9 15:04:26 2009
+++ src/sys/dev/i2c/sdtemp_reg.h	Sat May  9 15:04:25 2009
@@ -0,0 +1,117 @@
+/*	$NetBSD: sdtemp_reg.h,v 1.1 2009/05/09 15:04:25 pgoyette Exp $	*/
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Goyette.
+ *
+ * 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_SDTEMPREG_H
+#define _DEV_I2C_SDTEMPREG_H
+
+/*
+ * Following definitions derived from JEDEC Standard 21-C section 4.7
+ * available at http://www.jedec.org/download/search/4_07R15.pdf
+ */
+#define	SDTEMP_ADDRMASK			0x78
+#define	SDTEMP_ADDR			0x18	/* I2C address 001 1xxx */
+
+#define	SDTEMP_REG_CAPABILITY		0x00
+#define	SDTEMP_REG_CONFIG		0x01
+#define	SDTEMP_REG_UPPER_LIM		0x02
+#define	SDTEMP_REG_LOWER_LIM		0x03
+#define	SDTEMP_REG_CRIT_LIM		0x04
+#define	SDTEMP_REG_AMBIENT_TEMP		0x05
+#define	SDTEMP_REG_MFG_ID		0x06
+#define	SDTEMP_REG_DEV_REV		0x07
+#define	SDTEMP_REG_RESOLUTION		0x08
+
+#define	SDTEMP_CAP_HAS_ALARM		0x0001
+#define	SDTEMP_CAP_ACCURACY_1C		0x0002
+#define	SDTEMP_CAP_WIDER_RANGE		0x0004
+#define	SDTEMP_CAP_RESOLUTION		0x0018
+#define	SDTEMP_CAP_RES_SHIFT		3
+
+#define	SDTEMP_CONFIG_EVENT_MODE	0x0001
+#define	SDTEMP_CONFIG_EVENT_POL_AH	0x0002
+#define	SDTEMP_CONFIG_EVENT_CRIT_ONLY	0x0004
+#define	SDTEMP_CONFIG_EVENT_ENABLED	0x0008
+#define	SDTEMP_CONFIG_EVENT_STATUS	0x0010
+#define	SDTEMP_CONFIG_INT_CLEAR		0x0020
+#define	SDTEMP_CONFIG_WINDOW_LOCKED	0x0040
+#define	SDTEMP_CONFIG_CRITICAL_LOCKED	0x0080
+#define	SDTEMP_CONFIG_SHUTDOWN_MODE	0x0100
+#define	SDTEMP_CONFIG_HYSTERESIS	0x0600
+
+#define	SDTEMP_HYSTERESIS_NONE		0x0000
+#define	SDTEMP_HYSTERESIS_15		0x0200
+#define	SDTEMP_HYSTERESIS_30		0x0400
+#define	SDTEMP_HYSTERESIS_60		0x0600
+
+/*
+ * Temperature is a 12-bit value in the range of -256 <= x < +256 degrees.
+ * Maximum resolution is 0.0625C (1/16th degree, 4 bits), but some devices
+ * may have only 0.2500C or 0.1250C (1 or 2 bits), and some devices may not
+ * be able to represent negative values (not that we'd expect them, anyway).
+ */
+#define	SDTEMP_TEMP_MASK		0x0FFF
+#define	SDTEMP_TEMP_NEGATIVE		0x1000
+#define	SDTEMP_TEMP_SIGN_EXT		0xF000
+
+/*
+ * Status bits set in MCP98XX_REG_AMBIENT_TEMP only
+ */
+#define	SDTEMP_ABOVE_CRIT		0x8000
+#define	SDTEMP_ABOVE_UPPER		0x4000
+#define	SDTEMP_BELOW_LOWER		0x2000
+
+/*
+ * Devices known to conform to JEDEC JC42.4
+ */
+#define	MAXIM_MANUFACTURER_ID		0x004D
+#define	MAX_6604_DEVICE_ID		0x3E
+
+#define	MCP_MANUFACTURER_ID		0x0054
+#define	MCP_9805_DEVICE_ID		0x00
+#define	MCP_98242_DEVICE_ID		0x20
+
+/* According to datasheets, SE97 and SE98 have same ID */
+
+#define	NXP_MANUFACTURER_ID		0x1131
+#define	NXP_SE97_DEVICE_ID		0xA1
+
+#define	ADT_MANUFACTURER_ID		0x11D4
+#define	ADT_7408_DEVICE_ID		0x80
+
+#define	STTS_MANUFACTURER_ID		0x104A
+#define	STTS_424E02_DEVICE_ID		0x00
+
+/* According to datasheets, both the CAT6095 and CAT34TS02 have the same ID */
+
+#define	CAT_MANUFACTURER_ID		0x1B09
+#define	CAT_34TS02_DEVICE_ID		0x08
+
+#endif	/* _DEV_I2C_SDTEMPREG_H */

Reply via email to