Module Name:    src
Committed By:   martin
Date:           Sun Feb 28 11:49:44 UTC 2010

Modified Files:
        src/sys/arch/sparc64/conf: GENERIC SCHIZO files.sparc64
Added Files:
        src/sys/arch/sparc64/dev: pcf8591_envctrl.c pcfiic_ebus.c tda.c
Removed Files:
        src/sys/arch/sparc64/dev: envctrl.c envctrlreg.h

Log Message:
Remove the envctrl driver, it is superseeded by the more general pcfiic @
ebus. Add the SB1000/SB2000 fan controll driver (tda). From OpenBSD.
Move config files over to direct configuration of i2c devices.


To generate a diff of this commit:
cvs rdiff -u -r1.117 -r1.118 src/sys/arch/sparc64/conf/GENERIC
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/sparc64/conf/SCHIZO
cvs rdiff -u -r1.124 -r1.125 src/sys/arch/sparc64/conf/files.sparc64
cvs rdiff -u -r1.11 -r0 src/sys/arch/sparc64/dev/envctrl.c
cvs rdiff -u -r1.5 -r0 src/sys/arch/sparc64/dev/envctrlreg.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/sparc64/dev/pcf8591_envctrl.c \
    src/sys/arch/sparc64/dev/pcfiic_ebus.c src/sys/arch/sparc64/dev/tda.c

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

Modified files:

Index: src/sys/arch/sparc64/conf/GENERIC
diff -u src/sys/arch/sparc64/conf/GENERIC:1.117 src/sys/arch/sparc64/conf/GENERIC:1.118
--- src/sys/arch/sparc64/conf/GENERIC:1.117	Sat Feb 13 08:54:58 2010
+++ src/sys/arch/sparc64/conf/GENERIC	Sun Feb 28 11:49:44 2010
@@ -1,4 +1,4 @@
-# $NetBSD: GENERIC,v 1.117 2010/02/13 08:54:58 mrg Exp $
+# $NetBSD: GENERIC,v 1.118 2010/02/28 11:49:44 martin Exp $
 #
 # GENERIC machine description file
 #
@@ -22,7 +22,7 @@
 
 options 	INCLUDE_CONFIG_FILE	# embed config file in kernel binary
 
-#ident 		"GENERIC-$Revision: 1.117 $"
+#ident 		"GENERIC-$Revision: 1.118 $"
 
 maxusers	64
 
@@ -906,17 +906,20 @@
 #### Other device configuration
 
 psm*		at ebus?		# Ultrabook IIi microcontroller
-envctrl*	at ebus?		# Ultra E450 environmental monitoring
+pcfiic*		at ebus?		# various environmental monitoring
+iic*		at pcfiic?
 lom*		at ebus?		# LOMlite lights out management
 
 # Netra X1 / T1 style environmental monitoring
 alipm*		at pci?
 iic*		at alipm?
-spdmem*		at iic? addr 0x54
-spdmem*		at iic? addr 0x55
-spdmem*		at iic? addr 0x56
-spdmem*		at iic? addr 0x57
-admtemp*	at iic? addr 0x18
+
+# I2C devices
+spdmem*		at iic? addr?
+admtemp*	at iic? addr?
+ecadc*		at iic? addr?	# envctrl/envctrltwo on E250/E450
+lmtemp*		at iic? addr?
+tda*		at iic? addr?	# fan controll on SB1000/2000
 
 ### Other pseudo-devices
 

Index: src/sys/arch/sparc64/conf/SCHIZO
diff -u src/sys/arch/sparc64/conf/SCHIZO:1.6 src/sys/arch/sparc64/conf/SCHIZO:1.7
--- src/sys/arch/sparc64/conf/SCHIZO:1.6	Sat Feb 13 22:24:09 2010
+++ src/sys/arch/sparc64/conf/SCHIZO	Sun Feb 28 11:49:44 2010
@@ -1,4 +1,4 @@
-# $NetBSD: SCHIZO,v 1.6 2010/02/13 22:24:09 mrg Exp $
+# $NetBSD: SCHIZO,v 1.7 2010/02/28 11:49:44 martin Exp $
 #
 # GENERIC with schizo support enabled, with the annoying schizo interupt
 # option
@@ -7,7 +7,7 @@
 include 	"arch/sparc64/conf/GENERIC.MP"
 #include 	"arch/sparc64/conf/GENERIC"
 
-#ident		"SCHIZO.$Revision: 1.6 $"
+#ident		"SCHIZO.$Revision: 1.7 $"
 
 options 	CHEETAH
 
@@ -20,6 +20,3 @@
 
 # this crashes when attaching.
 #no machfb
-
-# this matches when it isn't present. ugh.
-no admtemp

Index: src/sys/arch/sparc64/conf/files.sparc64
diff -u src/sys/arch/sparc64/conf/files.sparc64:1.124 src/sys/arch/sparc64/conf/files.sparc64:1.125
--- src/sys/arch/sparc64/conf/files.sparc64:1.124	Mon Feb 22 22:28:58 2010
+++ src/sys/arch/sparc64/conf/files.sparc64	Sun Feb 28 11:49:44 2010
@@ -1,4 +1,4 @@
-#	$NetBSD: files.sparc64,v 1.124 2010/02/22 22:28:58 mrg Exp $
+#	$NetBSD: files.sparc64,v 1.125 2010/02/28 11:49:44 martin Exp $
 
 # @(#)files.sparc64	8.1 (Berkeley) 7/19/93
 # sparc64-specific configuration info
@@ -68,9 +68,17 @@
 attach power at sbus, ebus
 file	arch/sparc64/dev/power.c		power
 
-device envctrl: sysmon_envsys, pcf8584, i2cbus
-attach envctrl at ebus
-file	arch/sparc64/dev/envctrl.c	envctrl
+device	tda
+attach	tda at iic
+file	arch/sparc64/dev/tda.c			tda
+
+device pcfiic: sysmon_envsys, pcf8584, i2cbus
+attach pcfiic at ebus
+file	arch/sparc64/dev/pcfiic_ebus.c	pcfiic
+
+device ecadc
+attach ecadc at iic
+file	arch/sparc64/dev/pcf8591_envctrl.c
 
 device lom: sysmon_envsys, sysmon_wdog
 attach lom at ebus

Added files:

Index: src/sys/arch/sparc64/dev/pcf8591_envctrl.c
diff -u /dev/null src/sys/arch/sparc64/dev/pcf8591_envctrl.c:1.1
--- /dev/null	Sun Feb 28 11:49:44 2010
+++ src/sys/arch/sparc64/dev/pcf8591_envctrl.c	Sun Feb 28 11:49:44 2010
@@ -0,0 +1,255 @@
+/*	$NetBSD: pcf8591_envctrl.c,v 1.1 2010/02/28 11:49:44 martin Exp $	*/
+/*	$OpenBSD: pcf8591_envctrl.c,v 1.6 2007/10/25 21:17:20 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2006 Damien Miller <d...@openbsd.org>
+ * Copyright (c) 2007 Mark Kettenis <kette...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <dev/sysmon/sysmonvar.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/i2c/i2cvar.h>
+
+#define PCF8591_CHANNELS	4
+
+#define PCF8591_CTRL_CH0	0x00
+#define PCF8591_CTRL_CH1	0x01
+#define PCF8591_CTRL_CH2	0x02
+#define PCF8591_CTRL_CH3	0x03
+#define PCF8591_CTRL_AUTOINC	0x04
+#define PCF8591_CTRL_OSCILLATOR	0x40
+
+struct ecadc_channel {
+	u_int		chan_num;
+	envsys_data_t	chan_sensor;
+	u_char		*chan_xlate;
+	int64_t		chan_factor;
+	int64_t		chan_min;
+	int64_t		chan_warn;
+	int64_t		chan_crit;
+};
+
+struct ecadc_softc {
+	device_t		sc_dev;
+	i2c_tag_t		sc_tag;
+	i2c_addr_t		sc_addr;
+	u_char			sc_ps_xlate[256];
+	u_char			sc_cpu_xlate[256];
+	u_int			sc_nchan;
+	struct ecadc_channel	sc_channels[PCF8591_CHANNELS];
+	struct sysmon_envsys	*sc_sme;
+};
+
+static int	ecadc_match(device_t, cfdata_t, void *);
+static void	ecadc_attach(device_t, device_t, void *);
+static void	ecadc_refresh(struct sysmon_envsys *, envsys_data_t *);
+static void	ecadc_get_limits(struct sysmon_envsys *, envsys_data_t *,
+			sysmon_envsys_lim_t *, uint32_t *);
+
+CFATTACH_DECL_NEW(ecadc, sizeof(struct ecadc_softc),
+	ecadc_match, ecadc_attach, NULL, NULL);
+
+static const char * ecadc_compats[] = {
+	"ecadc",
+	NULL
+};
+
+static int
+ecadc_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+
+	if (iic_compat_match(ia, ecadc_compats))
+		return 1;
+
+	return 0;
+}
+
+static void
+ecadc_attach(device_t parent, device_t self, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+	struct ecadc_softc *sc = device_private(self);
+	u_char term[256];
+	u_char *cp, *desc;
+	int64_t minv, warnv, crit, num, den;
+	u_int8_t junk[PCF8591_CHANNELS + 1];
+	envsys_data_t *sensor;
+	int len, error, addr, chan, node = (int)ia->ia_cookie;
+	u_int i;
+
+	sc->sc_dev = self;
+	if ((len = OF_getprop(node, "thermisters", term,
+	    sizeof(term))) < 0) {
+		aprint_error(": couldn't find \"thermisters\" property\n");
+		return;
+	}
+
+	if (OF_getprop(node, "cpu-temp-factors", &sc->sc_cpu_xlate[2],
+	    sizeof(sc->sc_cpu_xlate) - 2) < 0) {
+		aprint_error(": couldn't find \"cpu-temp-factors\" property\n");
+		return;
+	}
+	sc->sc_cpu_xlate[0] = sc->sc_cpu_xlate[1] = sc->sc_cpu_xlate[2];
+
+	/* Only the Sun Enterprise 450 has these. */
+	OF_getprop(node, "ps-temp-factors", &sc->sc_ps_xlate[2],
+	    sizeof(sc->sc_ps_xlate) - 2);
+	sc->sc_ps_xlate[0] = sc->sc_ps_xlate[1] = sc->sc_ps_xlate[2];
+
+	cp = term;
+	while (cp < term + len) {
+		addr = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		chan = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		minv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		warnv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		crit = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		num = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		den = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4;
+		desc = cp;
+		while (cp < term + len && *cp++);
+
+		if (addr != (ia->ia_addr << 1))
+			continue;
+
+		if (num == 0 || den == 0)
+			num = den = 1;
+
+		sc->sc_channels[sc->sc_nchan].chan_num = chan;
+
+		sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor;
+		sensor->units = ENVSYS_STEMP;
+		strlcpy(sensor->desc, desc, sizeof(sensor->desc));
+
+		if (strncmp(desc, "CPU", 3) == 0)
+			sc->sc_channels[sc->sc_nchan].chan_xlate =
+			    sc->sc_cpu_xlate;
+		else if (strncmp(desc, "PS", 2) == 0)
+			sc->sc_channels[sc->sc_nchan].chan_xlate =
+			    sc->sc_ps_xlate;
+		else
+			sc->sc_channels[sc->sc_nchan].chan_factor =
+			    (1000000 * num) / den;
+		sc->sc_channels[sc->sc_nchan].chan_min =
+		    273150000 + 1000000 * minv;
+		sc->sc_channels[sc->sc_nchan].chan_warn =
+		    273150000 + 1000000 * warnv;
+		sc->sc_channels[sc->sc_nchan].chan_crit =
+		    273150000 + 1000000 * crit;
+		sc->sc_nchan++;
+	}
+
+	sc->sc_tag = ia->ia_tag;
+	sc->sc_addr = ia->ia_addr;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+
+	/* Try a read now, so we can fail if it doesn't work */
+	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+	    NULL, 0, junk, sc->sc_nchan + 1, 0)) {
+		aprint_error(": read failed\n");
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+	}
+
+	iic_release_bus(sc->sc_tag, 0);
+
+	/* Hook us into the sysmon_envsys subsystem */
+	sc->sc_sme = sysmon_envsys_create();
+	sc->sc_sme->sme_name = device_xname(self);
+	sc->sc_sme->sme_cookie = sc;
+	sc->sc_sme->sme_flags |= ENVSYS_FMONNOTSUPP;
+	sc->sc_sme->sme_refresh = ecadc_refresh;
+	sc->sc_sme->sme_get_limits = ecadc_get_limits;
+
+	/* Initialize sensor data. */
+	for (i = 0; i < sc->sc_nchan; i++)
+		sysmon_envsys_sensor_attach(sc->sc_sme,
+		    &sc->sc_channels[i].chan_sensor);
+
+	error = sysmon_envsys_register(sc->sc_sme);
+	if (error) {
+		aprint_error_dev(self, "error %d registering with sysmon\n",
+			error);
+		sysmon_envsys_destroy(sc->sc_sme);
+		return;
+	}
+
+	aprint_naive(": Temp Sensors\n");
+	aprint_normal("%s Temp Sensors\n", ia->ia_name);
+}
+
+static void
+ecadc_refresh(struct sysmon_envsys *sme, envsys_data_t *sensor)
+{
+	struct ecadc_softc *sc = sme->sme_cookie;
+	u_int i;
+	u_int8_t data[PCF8591_CHANNELS + 1];
+	u_int8_t ctrl = PCF8591_CTRL_CH0 | PCF8591_CTRL_AUTOINC |
+	    PCF8591_CTRL_OSCILLATOR;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
+	    &ctrl, 1, NULL, 0, 0)) {
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+	}
+	/* NB: first byte out is stale, so read num_channels + 1 */
+	if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
+	    NULL, 0, data, PCF8591_CHANNELS + 1, 0)) {
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+	}
+	iic_release_bus(sc->sc_tag, 0);
+
+	/* We only support temperature channels. */
+	for (i = 0; i < sc->sc_nchan; i++) {
+		struct ecadc_channel *chp = &sc->sc_channels[i];
+
+		if (chp->chan_xlate)
+			chp->chan_sensor.value_cur = 273150000 + 1000000 *
+			    chp->chan_xlate[data[1 + chp->chan_num]];
+		else
+			chp->chan_sensor.value_cur = 273150000 +
+			    chp->chan_factor * data[1 + chp->chan_num];
+
+		chp->chan_sensor.state = ENVSYS_SVALID;
+		chp->chan_sensor.flags &= ~ENVSYS_FNEED_REFRESH;
+		chp->chan_sensor.flags |= ENVSYS_FMONLIMITS;
+	}
+}
+
+static void
+ecadc_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
+		sysmon_envsys_lim_t *limits, uint32_t *props)
+{
+	struct ecadc_softc *sc = sme->sme_cookie;
+	int i;
+
+	for (i = 0; i < sc->sc_nchan; i++) {
+		if (edata != &sc->sc_channels[i].chan_sensor)
+			continue;
+		*props |= PROP_WARNMIN|PROP_WARNMAX|PROP_CRITMAX;
+		limits->sel_warnmin = sc->sc_channels[i].chan_min;
+		limits->sel_warnmax = sc->sc_channels[i].chan_warn;
+		limits->sel_critmax = sc->sc_channels[i].chan_crit;
+		return;
+	}
+}
Index: src/sys/arch/sparc64/dev/pcfiic_ebus.c
diff -u /dev/null src/sys/arch/sparc64/dev/pcfiic_ebus.c:1.1
--- /dev/null	Sun Feb 28 11:49:44 2010
+++ src/sys/arch/sparc64/dev/pcfiic_ebus.c	Sun Feb 28 11:49:44 2010
@@ -0,0 +1,222 @@
+/*	$NetBSD: pcfiic_ebus.c,v 1.1 2010/02/28 11:49:44 martin Exp $	*/
+/*	$OpenBSD: pcfiic_ebus.c,v 1.13 2008/06/08 03:07:40 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2006 David Gwynne <d...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Device specific driver for the EBus i2c devices found on some sun4u
+ * systems. On systems not having a boot-bus controller the i2c devices
+ * are PCF8584.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+#include <machine/openfirm.h>
+#include <machine/autoconf.h>
+
+#include <dev/ebus/ebusreg.h>
+#include <dev/ebus/ebusvar.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/ic/pcf8584var.h>
+
+int	pcfiic_ebus_match(device_t, struct cfdata *, void *);
+void	pcfiic_ebus_attach(device_t, device_t, void *);
+
+struct pcfiic_ebus_softc {
+	struct pcfiic_softc	esc_sc;
+
+	int			esc_node;
+	void			*esc_ih;
+};
+
+CFATTACH_DECL_NEW(pcfiic, sizeof(struct pcfiic_ebus_softc),
+	pcfiic_ebus_match, pcfiic_ebus_attach, NULL, NULL);
+
+static prop_array_t create_dict(device_t);
+static void add_prop(prop_array_t, const char *, const char *, u_int, int);
+static void envctrl_props(prop_array_t, int);
+static void envctrltwo_props(prop_array_t, int);
+
+int
+pcfiic_ebus_match(device_t parent, struct cfdata *match, void *aux)
+{
+	struct ebus_attach_args		*ea = aux;
+	char				compat[32];
+
+	if (strcmp(ea->ea_name, "SUNW,envctrl") == 0 ||
+	    strcmp(ea->ea_name, "SUNW,envctrltwo") == 0)
+		return (1);
+
+	if (strcmp(ea->ea_name, "i2c") != 0)
+		return (0);
+
+	if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == -1)
+		return (0);
+
+	if (strcmp(compat, "pcf8584") == 0 ||
+	    strcmp(compat, "i2cpcf,8584") == 0 ||
+	    strcmp(compat, "SUNW,i2c-pic16f747") == 0 ||
+	    strcmp(compat, "SUNW,bbc-i2c") == 0)
+		return (1);
+
+	return (0);
+}
+
+void
+pcfiic_ebus_attach(device_t parent, device_t self, void *aux)
+{
+	struct pcfiic_ebus_softc	*esc = device_private(self);
+	struct pcfiic_softc		*sc = &esc->esc_sc;
+	struct ebus_attach_args		*ea = aux;
+	char				compat[32];
+	u_int64_t			addr;
+	u_int8_t			clock = PCF_CLOCK_12 | PCF_FREQ_90;
+	int				swapregs = 0;
+
+	if (ea->ea_nreg < 1 || ea->ea_nreg > 2) {
+		printf(": expected 1 or 2 registers, got %d\n", ea->ea_nreg);
+		return;
+	}
+
+	sc->sc_dev = self;
+	if (OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) > 0 &&
+	    strcmp(compat, "SUNW,bbc-i2c") == 0) {
+		/*
+		 * On BBC-based machines, Sun swapped the order of
+		 * the registers on their clone pcf, plus they feed
+		 * it a non-standard clock.
+		 */
+		int clk = prom_getpropint(findroot(), "clock-frequency", 0);
+
+		if (clk < 105000000)
+			clock = PCF_CLOCK_3 | PCF_FREQ_90;
+		else if (clk < 160000000)
+			clock = PCF_CLOCK_4_43 | PCF_FREQ_90;
+		swapregs = 1;
+	}
+
+	if (OF_getprop(ea->ea_node, "own-address", &addr, sizeof(addr)) == -1) {
+		addr = 0xaa;
+	} else if (addr == 0x00 || addr > 0xff) {
+		printf(": invalid address on I2C bus");
+		return;
+	}
+
+	if (bus_space_map(ea->ea_bustag,
+	    EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
+	    ea->ea_reg[0].size, 0, &sc->sc_ioh) == 0) {
+		sc->sc_iot = ea->ea_bustag;
+	} else {
+		printf(": can't map register space\n");
+               	return;
+	}
+
+	if (ea->ea_nreg == 2) {
+		/*
+		 * Second register only occurs on BBC-based machines,
+		 * and is likely not prom mapped
+		*/
+		if (bus_space_map(sc->sc_iot, EBUS_ADDR_FROM_REG(&ea->ea_reg[1]),
+		    ea->ea_reg[1].size, 0, &sc->sc_ioh2) != 0) {
+			printf(": can't map 2nd register space\n");
+			return;
+		}
+		sc->sc_master = 1;
+	}
+
+	if (ea->ea_nintr >= 1)
+		esc->esc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intr[0],
+		    IPL_BIO, pcfiic_intr, sc);
+	else
+		esc->esc_ih = NULL;
+
+
+	if (esc->esc_ih == NULL)
+		sc->sc_poll = 1;
+
+	if (strcmp(ea->ea_name, "SUNW,envctrl") == 0) {
+		envctrl_props(create_dict(self), ea->ea_node);
+		pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0);
+	} else if (strcmp(ea->ea_name, "SUNW,envctrltwo") == 0) {
+		envctrltwo_props(create_dict(self), ea->ea_node);
+		pcfiic_attach(sc, 0x55, PCF_CLOCK_12 | PCF_FREQ_45, 0);
+	} else
+		pcfiic_attach(sc, (i2c_addr_t)(addr >> 1), clock, swapregs);
+}
+
+static prop_array_t
+create_dict(device_t parent)
+{
+	prop_dictionary_t props = device_properties(parent);
+	prop_array_t cfg = prop_dictionary_get(props, "i2c-child-devices");
+	if (cfg) return cfg;
+	cfg = prop_array_create();
+	prop_dictionary_set(props, "i2c-child-devices", cfg);
+	prop_object_release(cfg);
+	return cfg;
+}
+
+static void
+add_prop(prop_array_t c, const char *name, const char *compat, u_int addr,
+	int node)
+{
+	prop_dictionary_t dev;
+	prop_data_t data;
+
+	dev = prop_dictionary_create();
+	prop_dictionary_set_cstring(dev, "name", name);
+	data = prop_data_create_data(compat, strlen(compat)+1);
+	prop_dictionary_set(dev, "compatible", data);
+	prop_object_release(data);
+	prop_dictionary_set_uint32(dev, "addr", addr);
+	prop_dictionary_set_uint64(dev, "cookie", node);
+	prop_array_add(c, dev);
+	prop_object_release(dev);
+}
+
+static void
+envctrl_props(prop_array_t c, int node)
+{
+	/* Power supply 1 temperature. */
+	add_prop(c, "PSU-1", "ecadc", 0x48, node);
+
+	/* Power supply 2 termperature. */
+	add_prop(c, "PSU-2", "ecadc", 0x49, node);
+
+	/* Power supply 3 tempterature. */
+	add_prop(c, "PSU-3", "ecadc", 0x4a, node);
+
+	/* Ambient tempterature. */
+	add_prop(c, "ambient", "i2c-lm75", 0x4d, node);
+
+	/* CPU temperatures. */
+	add_prop(c, "CPU", "ecadc", 0x4f, node);
+}
+
+static void
+envctrltwo_props(prop_array_t c, int node)
+{
+	add_prop(c, "PSU", "ecadc", 0x4a, node);
+	add_prop(c, "CPU", "ecadc", 0x4f, node);
+}
Index: src/sys/arch/sparc64/dev/tda.c
diff -u /dev/null src/sys/arch/sparc64/dev/tda.c:1.1
--- /dev/null	Sun Feb 28 11:49:44 2010
+++ src/sys/arch/sparc64/dev/tda.c	Sun Feb 28 11:49:44 2010
@@ -0,0 +1,228 @@
+/*	$NetBSD: tda.c,v 1.1 2010/02/28 11:49:44 martin Exp $	*/
+/*	$OpenBSD: tda.c,v 1.4 2008/02/27 17:25:00 robert Exp $ */
+
+/*
+ * Copyright (c) 2008 Robert Nagy <rob...@openbsd.org>
+ * Copyright (c) 2008 Mark Kettenis <kette...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/sysmon/sysmon_taskq.h>
+
+#include <machine/autoconf.h>
+#include <machine/openfirm.h>
+
+#include <dev/i2c/i2cvar.h>
+
+/* fan control registers */
+#define TDA_SYSFAN_REG		0xf0
+#define TDA_CPUFAN_REG		0xf2
+#define TDA_PSFAN_REG		0xf4
+
+#define TDA_FANSPEED_MIN        0x0c
+#define TDA_FANSPEED_MAX        0x3f
+
+#define TDA_PSFAN_ON            0x1f
+#define TDA_PSFAN_OFF           0x00
+
+/* Internal and External temperature senor numbers */
+#define SENSOR_TEMP_EXT		0
+#define SENSOR_TEMP_INT		1
+
+#define CPU_TEMP_MAX		(67 * 1000000 + 273150000)
+#define CPU_TEMP_MIN		(57 * 1000000 + 273150000)
+#define SYS_TEMP_MAX		(30 * 1000000 + 273150000)
+#define SYS_TEMP_MIN		(20 * 1000000 + 273150000)
+
+struct tda_softc {
+	device_t		sc_dev;
+	i2c_tag_t		sc_tag;
+	i2c_addr_t		sc_addr;
+
+	u_int16_t		sc_cfan_speed;	/* current CPU fan speed */
+	u_int16_t		sc_sfan_speed;	/* current SYS fan speed */
+
+	callout_t		sc_timer;
+};
+
+int	tda_match(struct device *, struct cfdata *, void *);
+void	tda_attach(struct device *, struct device *, void *);
+
+void	tda_setspeed(struct tda_softc *);
+static void	tda_adjust(void *);
+static void	tda_timeout(void *);
+
+
+CFATTACH_DECL_NEW(tda, sizeof(struct tda_softc),
+	tda_match, tda_attach, NULL, NULL);
+
+int
+tda_match(struct device *parent, struct cfdata *match, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+	char name[32];
+
+	/* Only attach on the Sun Blade 1000/2000. */
+	if (OF_getprop(findroot(), "name", name, sizeof(name)) <= 0)
+		return (0);
+	if (strcmp(name, "SUNW,Sun-Blade-1000") != 0)
+		return (0);
+
+	/*
+	 * No need for "compatible" matching, we know exactly what
+	 * firmware calls us.
+	 */
+	return strcmp(ia->ia_name, "fan-control") == 0;
+}
+
+void
+tda_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct tda_softc *sc = device_private(self);
+	struct i2c_attach_args *ia = aux;
+
+	sc->sc_dev = self;
+	sc->sc_tag = ia->ia_tag;
+	sc->sc_addr = ia->ia_addr;
+
+	aprint_normal(": %s\n", ia->ia_name);
+
+	/*
+	 * Set the fans to maximum speed and save the power levels;
+	 * the controller is write-only.
+	 */
+	sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2;
+	tda_setspeed(sc);
+
+	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
+	callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc);
+}
+
+static void
+tda_timeout(void *v)
+{
+	struct tda_softc *sc = v;
+
+	sysmon_task_queue_sched(0, tda_adjust, sc);
+	callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc);
+}
+
+void
+tda_setspeed(struct tda_softc *sc)
+{
+	u_int8_t cmd[2];
+
+	if (sc->sc_cfan_speed < TDA_FANSPEED_MIN)
+		sc->sc_cfan_speed = TDA_FANSPEED_MIN;
+	if (sc->sc_sfan_speed < TDA_FANSPEED_MIN)
+		sc->sc_sfan_speed = TDA_FANSPEED_MIN;
+	if (sc->sc_cfan_speed > TDA_FANSPEED_MAX)
+		sc->sc_cfan_speed = TDA_FANSPEED_MAX;
+	if (sc->sc_sfan_speed > TDA_FANSPEED_MAX)
+		sc->sc_sfan_speed = TDA_FANSPEED_MAX;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+
+	cmd[0] = TDA_CPUFAN_REG;
+	cmd[1] = sc->sc_cfan_speed;
+	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
+		aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n");
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+        }
+
+	cmd[0] = TDA_SYSFAN_REG;
+	cmd[1] = sc->sc_sfan_speed;
+	if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+	    sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) {
+		aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n");
+		iic_release_bus(sc->sc_tag, 0);
+		return;
+        }
+
+	iic_release_bus(sc->sc_tag, 0);
+
+	aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n",
+		sc->sc_cfan_speed, sc->sc_sfan_speed); 
+}
+
+static bool
+is_cpu_sensor(const envsys_data_t *edata)
+{
+	if (edata->units != ENVSYS_STEMP)
+		return false;
+	return strcmp(edata->desc, "external") == 0;
+}
+
+static bool
+is_system_sensor(const envsys_data_t *edata)
+{
+	if (edata->units != ENVSYS_STEMP)
+		return false;
+	return strcmp(edata->desc, "internal") == 0;
+}
+
+static void
+tda_adjust(void *v)
+{
+	struct tda_softc *sc = v;
+	u_int64_t ctemp, stemp;
+	u_int16_t cspeed, sspeed;
+
+	/* Default to running the fans at maximum speed. */
+	sspeed = cspeed = TDA_FANSPEED_MAX;
+
+	/* fetch maximum current temperature */
+	ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true);
+	stemp = sysmon_envsys_get_max_value(is_system_sensor, true);
+
+	/* the predicates for selecting sensors must have gone wrong */
+	if (ctemp == 0 || stemp == 0) {
+		aprint_error_dev(sc->sc_dev, "skipping temp adjustment"
+			" - no sensor values\n");
+		return;
+	}
+
+	aprint_debug_dev(sc->sc_dev, "current temperature: cpu %lu system %lu\n",
+		ctemp, stemp);
+
+	if (ctemp < CPU_TEMP_MIN)
+		cspeed = TDA_FANSPEED_MIN;
+	else if (ctemp < CPU_TEMP_MAX)
+		cspeed = TDA_FANSPEED_MIN +
+			(ctemp - CPU_TEMP_MIN) * 
+			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 
+			(CPU_TEMP_MAX - CPU_TEMP_MIN);
+
+	if (stemp < SYS_TEMP_MIN)
+		sspeed = TDA_FANSPEED_MIN;
+	else if (stemp < SYS_TEMP_MAX)
+		sc->sc_sfan_speed = TDA_FANSPEED_MIN +
+			(stemp - SYS_TEMP_MIN) * 
+			(TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 
+			(SYS_TEMP_MAX - SYS_TEMP_MIN);
+
+	if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed)
+		return;
+
+	sc->sc_sfan_speed = sspeed;
+	sc->sc_cfan_speed = cspeed;
+	tda_setspeed(sc);
+}

Reply via email to