NetBSD/sys/arch/sparc64/dev/envctrl.c

695 lines
19 KiB
C

/* $NetBSD: envctrl.c,v 1.3 2007/05/02 11:40:44 tnn Exp $ */
/*-
* Copyright (c) 2007 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Tobias Nygren.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 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.
*/
/*
* SUNW,envctrl Sun Ultra Enterprise 450 environmental monitoring driver
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: envctrl.c,v 1.3 2007/05/02 11:40:44 tnn Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/kthread.h>
#include <sys/condvar.h>
#include <sys/mutex.h>
#include <sys/envsys.h>
#include <machine/autoconf.h>
#include <machine/promlib.h>
#include <dev/ebus/ebusreg.h>
#include <dev/ebus/ebusvar.h>
#include <dev/i2c/i2cvar.h>
#include <sparc64/dev/envctrlreg.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/i2c/pcf8583reg.h> /* for WDT */
#include <dev/ic/pcf8584var.h>
static int envctrlmatch(struct device *, struct cfdata *, void *);
static void envctrlattach(struct device *, struct device *, void *);
struct envctrl_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct pcf8584_handle sc_pcfiic;
struct proc *sc_proc;
kcondvar_t sc_sleepcond;
kmutex_t sc_sleepmtx;
struct sysmon_envsys sc_sysmon;
struct envsys_basic_info sc_binfo[13];
struct envsys_tre_data sc_sensor[13];
uint8_t sc_keyswitch;
uint8_t sc_fanstate;
uint8_t sc_ps_state[3];
int sc_ps_temp_factors[256];
int sc_cpu_temp_factors[256];
uint8_t sc_ps_fan_speeds[112];
uint8_t sc_cpu_fan_speeds[112];
};
CFATTACH_DECL(envctrl, sizeof(struct envctrl_softc),
envctrlmatch, envctrlattach, NULL, NULL);
static void deferred_envctrl_thread(void *);
static void envctrl_thread(void *);
static int envctrl_gtredata(struct sysmon_envsys *, struct envsys_tre_data *);
static int envctrl_streinfo(struct sysmon_envsys *, struct envsys_basic_info *);
static void envctrl_sleep(struct envctrl_softc *, int);
static int envctrl_write_1(struct envctrl_softc *, int, uint8_t);
static int envctrl_write_2(struct envctrl_softc *, int, uint8_t, uint8_t);
static int envctrl_read(struct envctrl_softc *, int, uint8_t *, int);
static int envctrl_get_cputemp(struct envctrl_softc *, uint8_t);
static int envctrl_get_pstemp(struct envctrl_softc *, uint8_t);
static int envctrl_get_ambtemp(struct envctrl_softc *);
static int envctrl_set_fanvoltage(struct envctrl_softc *, uint8_t, uint8_t);
static uint8_t envctrl_cputemp_to_voltage(struct envctrl_softc *, int);
static uint8_t envctrl_pstemp_to_voltage(struct envctrl_softc *, int);
static void envctrl_init_components(struct envctrl_softc *);
static int envctrl_init_tables(struct envctrl_softc *, int);
static void envctrl_update_sensors(struct envctrl_softc *);
static void envctrl_interpolate_ob_table(int *, uint8_t *);
static const struct envsys_range envctrl_ranges[] = {
{ 0, 7, ENVSYS_STEMP},
{ 8, 9, ENVSYS_SVOLTS_DC},
{ 10, 11, ENVSYS_INTEGER},
};
static int
envctrlmatch(struct device *parent, struct cfdata *cf, void *aux)
{
struct ebus_attach_args *ea = aux;
return (strcmp("SUNW,envctrl", ea->ea_name) == 0);
}
static void
envctrlattach(struct device *parent, struct device *self, void *aux)
{
struct envctrl_softc *sc = (struct envctrl_softc *)self;
struct ebus_attach_args *ea = aux;
bus_addr_t devaddr;
int i;
sc->sc_iot = ea->ea_bustag;
devaddr = EBUS_ADDR_FROM_REG(&ea->ea_reg[0]);
if (bus_space_map(sc->sc_iot, devaddr, ea->ea_reg[0].size,
0, &sc->sc_ioh) != 0) {
aprint_error(": unable to map device registers\n");
return;
}
if (envctrl_init_tables(sc, ea->ea_node) != 0) {
aprint_error(": unable to initialize tables\n");
return;
}
/*
* initialize envctrl bus
*/
sc->sc_pcfiic.ha_parent = self;
sc->sc_pcfiic.ha_iot = sc->sc_iot;
sc->sc_pcfiic.ha_ioh = sc->sc_ioh;
pcf8584_init(&sc->sc_pcfiic);
if (envctrl_write_1(sc, ENVCTRL_FANFAIL_ADDR, 0xFF)) {
aprint_error(": i2c probe failed\n");
return;
}
aprint_normal("\n%s: Ultra Enterprise 450 environmental monitoring\n",
sc->sc_dev.dv_xname);
envctrl_init_components(sc);
/*
* fill envsys sensor structures
*/
for (i = 0; i < 12; i++) {
sc->sc_binfo[i].sensor = i;
sc->sc_sensor[i].sensor = i;
sc->sc_binfo[i].validflags = ENVSYS_FVALID;
sc->sc_sensor[i].warnflags = ENVSYS_WARN_OK;
sc->sc_sensor[i].validflags = ENVSYS_FVALID;
}
for (i = 0; i < 8; i++)
sc->sc_binfo[i].units = sc->sc_sensor[i].units = ENVSYS_STEMP;
for (i = 0; i < 4; i++)
sprintf(sc->sc_binfo[i].desc, "CPU%i", i);
for (i = 4; i < 7; i++)
sprintf(sc->sc_binfo[i].desc, "PS%i", i - 4);
for (i = 8; i < 10; i++)
sc->sc_binfo[i].units = sc->sc_sensor[i].units =
ENVSYS_SVOLTS_DC;
for (i = 10; i < 12; i++)
sc->sc_binfo[i].units = sc->sc_sensor[i].units =
ENVSYS_INTEGER;
sprintf(sc->sc_binfo[7].desc, "ambient");
sprintf(sc->sc_binfo[8].desc, "cpufan voltage");
sprintf(sc->sc_binfo[9].desc, "psfan voltage");
sprintf(sc->sc_binfo[10].desc, "ps failed");
sprintf(sc->sc_binfo[11].desc, "fans failed");
sc->sc_sysmon.sme_ranges = envctrl_ranges;
sc->sc_sysmon.sme_sensor_info = sc->sc_binfo;
sc->sc_sysmon.sme_sensor_data = sc->sc_sensor;
sc->sc_sysmon.sme_cookie = sc;
sc->sc_sysmon.sme_gtredata = envctrl_gtredata;
sc->sc_sysmon.sme_streinfo = envctrl_streinfo;
sc->sc_sysmon.sme_nsensors = 12;
sc->sc_sysmon.sme_envsys_version = 1000;
if (sysmon_envsys_register(&sc->sc_sysmon))
aprint_error("%s: unable to register with sysmon\n",
sc->sc_dev.dv_xname);
envctrl_update_sensors(sc);
for (i = 0; i < 3; i++) {
aprint_normal("%s: PS %i: ", sc->sc_dev.dv_xname, i);
if (sc->sc_ps_state[i] & ENVCTRL_PS_PRESENT) {
aprint_normal("absent\n");
continue;
}
aprint_normal("%iW, %s",
(sc->sc_ps_state[i] & ENVCTRL_PS_550W) ? 650 : 550,
(sc->sc_ps_state[i] & ENVCTRL_PS_OK) ?
"online" : "FAILED");
if ((sc->sc_ps_state[i] & ENVCTRL_PS_OVERLOAD) == 0)
aprint_normal(" ***OVERLOADED***");
if ((sc->sc_ps_state[i] & ENVCTRL_PS_LOADSHARE_ERROR) == 0)
aprint_normal(" ***LOADSHARE ERROR***");
aprint_normal("\n");
}
aprint_verbose("%s: keyswitch is ", sc->sc_dev.dv_xname);
if ((sc->sc_keyswitch & ENVCTRL_KEY_LOCK) == 0)
aprint_verbose("unlocked\n");
else {
if ((sc->sc_keyswitch & ENVCTRL_KEY_DIAG) == 0)
aprint_verbose("in diagnostic mode\n");
else
aprint_verbose("locked\n");
}
mutex_init(&sc->sc_sleepmtx, MUTEX_DEFAULT, IPL_NONE);
cv_init(&sc->sc_sleepcond, "envidle");
kthread_create(deferred_envctrl_thread, sc);
}
static void
deferred_envctrl_thread(void *arg)
{
int error;
struct envctrl_softc *sc = arg;
error = kthread_create1(envctrl_thread, arg,
&sc->sc_proc, "envctrl");
if (error)
panic("cannot start envctrl thread; error %d", error);
}
static int
envctrl_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
{
struct envctrl_softc *sc = sme->sme_cookie;
*tred = sc->sc_sensor[tred->sensor];
return 0;
}
static int
envctrl_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
{
struct envctrl_softc *sc = sme->sme_cookie;
memcpy(sc->sc_binfo[binfo->sensor].desc, binfo->desc,
sizeof(sc->sc_binfo[binfo->sensor].desc));
sc->sc_binfo[binfo->sensor].desc[
sizeof(sc->sc_binfo[binfo->sensor].desc) - 1] = '\0';
binfo->validflags = ENVSYS_FVALID;
return 0;
}
static void
envctrl_sleep(struct envctrl_softc *sc, int ms)
{
cv_timedwait(&sc->sc_sleepcond, &sc->sc_sleepmtx, mstohz(ms));
}
/*
* read cpu temperature, in microCelcius
*/
static int
envctrl_get_cputemp(struct envctrl_softc *sc, uint8_t cpu)
{
uint8_t r;
if (envctrl_write_1(sc, ENVCTRL_CPUTEMP_ADDR, (1 << 6) | cpu))
return -1;
if (envctrl_read(sc, ENVCTRL_CPUTEMP_ADDR, &r, 1))
return -1;
if (envctrl_write_1(sc, ENVCTRL_CPUTEMP_ADDR, 0))
return -1;
return sc->sc_cpu_temp_factors[r];
}
/*
* read power supply temperature in microCelcius
*/
static int
envctrl_get_pstemp(struct envctrl_softc *sc, uint8_t ps)
{
uint8_t r;
if (envctrl_write_1(sc, ENVCTRL_PS0TEMP_ADDR + ps, (1 << 6)))
return -1;
if (envctrl_read(sc, ENVCTRL_PS0TEMP_ADDR + ps, &r, 1))
return -1;
if (envctrl_write_1(sc, ENVCTRL_PS0TEMP_ADDR + ps, 0))
return -1;
return sc->sc_ps_temp_factors[r];
}
/*
* read ambient temperature in microCelcius
*/
static int
envctrl_get_ambtemp(struct envctrl_softc *sc)
{
int8_t temp[2];
if (envctrl_write_1(sc, ENVCTRL_AMB_ADDR, 0))
return -1;
if (envctrl_read(sc, ENVCTRL_AMB_ADDR, temp, 2))
return -1;
return ((temp[0] << 1) | ((temp[1] >> 7) & 1)) * 500000;
}
/*
* set fan voltage, 0 - 63, scaled to 0V-12V.
*/
static int
envctrl_set_fanvoltage(struct envctrl_softc *sc, uint8_t port, uint8_t value)
{
return envctrl_write_2(sc, ENVCTRL_FANVOLTAGE_ADDR, port, value);
}
static uint8_t
envctrl_cputemp_to_voltage(struct envctrl_softc *sc, int temp)
{
uint8_t ret;
temp -= 20; /* magic offset, from opensolaris */
if (temp < 0)
temp = 0;
if (temp > 111)
temp = 111;
ret = sc->sc_cpu_fan_speeds[temp];
if (ret < ENVCTRL_FANVOLTAGE_MIN)
return ENVCTRL_FANVOLTAGE_MIN;
if (ret > ENVCTRL_FANVOLTAGE_MAX)
return ENVCTRL_FANVOLTAGE_MAX;
return ret;
}
static uint8_t
envctrl_pstemp_to_voltage(struct envctrl_softc *sc, int temp)
{
uint8_t ret;
temp -= 30; /* magic offset, from opensolaris */
if (temp < 0)
temp = 0;
if (temp > 111)
temp = 111;
ret = sc->sc_ps_fan_speeds[temp];
if (ret < ENVCTRL_FANVOLTAGE_MIN)
return ENVCTRL_FANVOLTAGE_MIN;
if (ret > ENVCTRL_FANVOLTAGE_MAX)
return ENVCTRL_FANVOLTAGE_MAX;
return ret;
}
static void
envctrl_init_components(struct envctrl_softc *sc)
{
/* configure ports as pure inputs */
envctrl_write_1(sc, ENVCTRL_FANFAIL_ADDR, ~0);
envctrl_write_1(sc, ENVCTRL_PS0_ADDR, ~0);
envctrl_write_1(sc, ENVCTRL_PS1_ADDR, ~0);
envctrl_write_1(sc, ENVCTRL_PS2_ADDR, ~0);
/* light up power LED */
envctrl_write_1(sc, ENVCTRL_LED_ADDR, ~ENVCTRL_LED_PWR);
/*
* Set fans to full speed. The last two ports are usually not
* connected, but it doesn't hurt to enable them.
*/
envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_CPU, ENVCTRL_FANVOLTAGE_MAX);
envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_PS, ENVCTRL_FANVOLTAGE_MAX);
envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_AFB, ENVCTRL_FANVOLTAGE_MAX);
envctrl_set_fanvoltage(sc, 3, ENVCTRL_FANVOLTAGE_MAX);
/* set fan watchdog timer to a 60 sec timeout */
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x80);
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x80);
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_TIMER, 0);
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_ALMTIMER, 60);
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_ALMCTL, 0xca);
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR, PCF8583_REG_CSR, 0x04);
/* recover from previous watchdog failure, if any */
envctrl_write_1(sc, ENVCTRL_INTR_ADDR, ~0);
envctrl_write_1(sc, ENVCTRL_INTR_ADDR, ~ENVCTRL_INTR_WDT_RST);
envctrl_write_1(sc, ENVCTRL_INTR_ADDR, (uint8_t) ~ENVCTRL_INTR_ENABLE);
}
#if 0
/*
* Service routine for environmental monitoring events. Currently unused.
*/
static void
envctrl_isr(void)
{
uint8_t intstate;
uint8_t v;
envctrl_read(ENVCTRL_INTR_ADDR, 1, &intstate);
if ((intstate & ENVCTRL_INTR_PS0) == 0 ||
(intstate & ENVCTRL_INTR_PS1) == 0 ||
(intstate & ENVCTRL_INTR_PS2) == 0) {
printf("envctrl: power supply event\n");
envctrl_read(ENVCTRL_PS0_ADDR, 1, &v);
envctrl_read(ENVCTRL_PS1_ADDR, 1, &v);
envctrl_read(ENVCTRL_PS2_ADDR, 1, &v);
}
if ((intstate & ENVCTRL_INTR_FANFAIL) == 0) {
printf("envctrl: fan failure event\n");
envctrl_read(ENVCTRL_FANFAIL_ADDR, 1, &v);
}
if ((intstate & ENVCTRL_INTR_UNKNOWN1) == 0) {
}
if ((intstate & ENVCTRL_INTR_UNKNOWN2) == 0) {
}
}
#endif
/*
* Refresh all sensor readings
*/
static void
envctrl_update_sensors(struct envctrl_softc *sc)
{
int cputemp_max;
int pstemp_max;
uint8_t cpufan_voltage;
uint8_t psfan_voltage;
int temp;
int nfail;
int i;
/* read cpu temperatures */
cputemp_max = -1;
for (i = 0; i < 4; i++) {
temp = envctrl_get_cputemp(sc, i);
if (temp != -1) {
if (cputemp_max < temp)
cputemp_max = temp;
sc->sc_sensor[i].cur.data_us = temp + 273150000;
sc->sc_sensor[i].validflags |= ENVSYS_FCURVALID;
} else
sc->sc_sensor[i].validflags &= ~ENVSYS_FCURVALID;
}
/* read power supply state & temperature */
pstemp_max = -1;
nfail = 0;
for (i = 0; i < 3; i++) {
if (envctrl_read(sc, ENVCTRL_PS0_ADDR - i,
&sc->sc_ps_state[i], 1))
sc->sc_ps_state[i] = ENVCTRL_PS_PRESENT;
if ((sc->sc_ps_state[i] & ENVCTRL_PS_PRESENT) == 0) {
if ((sc->sc_ps_state[i] & 0x38) != 0x38)
nfail++;
temp = envctrl_get_pstemp(sc, i);
if (pstemp_max < temp)
pstemp_max = temp;
sc->sc_sensor[i + 4].cur.data_us = temp + 273150000;
sc->sc_sensor[i + 4].validflags |= ENVSYS_FCURVALID;
} else
sc->sc_sensor[i + 4].validflags &= ~ENVSYS_FCURVALID;
}
sc->sc_sensor[10].cur.data_s = nfail;
sc->sc_sensor[10].validflags |= ENVSYS_FCURVALID;
/* read ambient temperature */
temp = envctrl_get_ambtemp(sc);
if (temp != -1) {
sc->sc_sensor[7].cur.data_us = temp + 273150000;
sc->sc_sensor[7].validflags |= ENVSYS_FCURVALID;
} else
sc->sc_sensor[7].validflags &= ~ENVSYS_FCURVALID;
/* read fan state */
if (envctrl_read(sc, ENVCTRL_FANFAIL_ADDR, &sc->sc_fanstate, 1)) {
sc->sc_fanstate = 0;
sc->sc_sensor[11].validflags &= ~ENVSYS_FCURVALID;
} else {
nfail = 0;
for (i = 0; i < 8; i++) {
if (((sc->sc_fanstate >> i) & 1) == 0)
nfail++;
}
sc->sc_sensor[11].cur.data_s = nfail;
sc->sc_sensor[11].validflags |= ENVSYS_FCURVALID;
}
/* read keyswitch */
envctrl_read(sc, ENVCTRL_LED_ADDR, &sc->sc_keyswitch, 1);
/*
* Update fan voltages. If any fans have failed, set voltage
* to max to compensate for lost air flow.
*/
cpufan_voltage = envctrl_cputemp_to_voltage(sc, cputemp_max / 1000000);
if (cputemp_max == -1 || sc->sc_fanstate != 0xFF)
cpufan_voltage = ENVCTRL_FANVOLTAGE_MAX;
psfan_voltage = envctrl_pstemp_to_voltage(sc, pstemp_max / 1000000);
if (pstemp_max == -1 || sc->sc_fanstate != 0xFF)
psfan_voltage = ENVCTRL_FANVOLTAGE_MAX;
envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_CPU, cpufan_voltage);
envctrl_set_fanvoltage(sc, ENVCTRL_FANPORT_PS, psfan_voltage);
sc->sc_sensor[8].cur.data_us = cpufan_voltage * ENVCTRL_UVFACT;
sc->sc_sensor[9].cur.data_us = psfan_voltage * ENVCTRL_UVFACT;
sc->sc_sensor[8].validflags |= ENVSYS_FCURVALID;
sc->sc_sensor[9].validflags |= ENVSYS_FCURVALID;
}
/*
* envctrl worker thread. Reads sensors periodically.
*/
static void
envctrl_thread(void *arg)
{
#ifdef BLINK
int i;
#endif
struct envctrl_softc *sc = arg;
mutex_enter(&sc->sc_sleepmtx);
/*
* Poll sensors every 15 seconds. Optionally blink the activity LED.
*/
for (;;) {
/* kick wdt */
envctrl_write_2(sc, ENVCTRL_WATCHDOG_ADDR,
PCF8583_REG_TIMER, 0);
/* refresh sensor readings, update fan speeds */
envctrl_update_sensors(sc);
#ifdef BLINK
for (i = 0; i < 15; i++) {
envctrl_write_1(sc, ENVCTRL_LED_ADDR,
~(ENVCTRL_LED_PWR | ENVCTRL_LED_ACT));
envctrl_sleep(sc, 500);
envctrl_write_1(sc, ENVCTRL_LED_ADDR,
~ENVCTRL_LED_PWR);
envctrl_sleep(sc, 500);
}
#else
envctrl_sleep(sc, 15 * 1000);
#endif
}
}
/*
* Make a table with interpolated values based on an OPB temperature table.
*/
static void
envctrl_interpolate_ob_table(int *ftbl, uint8_t *utbl)
{
int i;
int e;
int k;
int y;
i = 255;
e = i;
while (e >= 0) {
e = i;
y = utbl[i] * 1000000;
do {
i--;
}
while (utbl[e] == utbl[i] && i > 0);
k = (utbl[i] * 1000000 - y) / (e - i);
while (e >= i) {
ftbl[e] = y;
y += k;
e--;
}
}
}
/*
* Copy temperature tables from OBP. Return 0 on success.
*/
static int
envctrl_init_tables(struct envctrl_softc *sc, int node)
{
uint8_t buf[256];
uint8_t *p;
int nitem;
int err;
p = buf;
nitem = 1;
err = prom_getprop(node, "cpu-temp-factors", 254, &nitem, &p);
if (nitem != 1 || err != 0)
return -1;
buf[255] = buf[253];
buf[254] = buf[253];
envctrl_interpolate_ob_table(sc->sc_cpu_temp_factors, buf);
p = buf;
nitem = 1;
err = prom_getprop(node, "ps-temp-factors", 254, &nitem, &p);
if (nitem != 1 || err != 0)
return -1;
buf[255] = buf[253];
buf[254] = buf[253];
envctrl_interpolate_ob_table(sc->sc_ps_temp_factors, buf);
p = sc->sc_cpu_fan_speeds;
nitem = 1;
err = prom_getprop(node, "cpu-fan-speeds", 112, &nitem, &p);
if (nitem != 1 || err != 0)
return -1;
p = sc->sc_ps_fan_speeds;
nitem = 1;
err = prom_getprop(node, "ps-fan-speeds", 112, &nitem, &p);
if (nitem != 1 || err != 0)
return -1;
return 0;
}
static int
envctrl_write_1(struct envctrl_softc *sc, int addr, uint8_t v1)
{
return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_WRITE_WITH_STOP, addr,
NULL, 0, &v1, 1, cold ? I2C_F_POLL : 0);
}
static int
envctrl_write_2(struct envctrl_softc *sc, int addr, uint8_t v1, uint8_t v2)
{
uint8_t buf[] = {v1, v2};
return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_WRITE_WITH_STOP, addr,
NULL, 0, buf, 2, cold ? I2C_F_POLL : 0);
}
static int
envctrl_read(struct envctrl_softc *sc, int addr, uint8_t *buf, int len)
{
return iic_exec(&sc->sc_pcfiic.ha_i2c, I2C_OP_READ_WITH_STOP, addr,
NULL, 0, buf, len, cold ? I2C_F_POLL : 0);
}