Index: src/sys/arch/sparc64/sparc64/autoconf.c =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/autoconf.c,v retrieving revision 1.206 diff -u -r1.206 autoconf.c --- src/sys/arch/sparc64/sparc64/autoconf.c 13 Dec 2015 11:51:37 -0000 1.206 +++ src/sys/arch/sparc64/sparc64/autoconf.c 14 Dec 2015 22:43:18 -0000 @@ -1142,6 +1142,58 @@ prop_object_release(cfg); } + + /* + * Add V210/V240 environmental sensors that are not in + * the OFW tree. + */ + if (device_is_a(busdev, "pcfiic") && + (!strcmp(machine_model, "SUNW,Sun-Fire-V240") || + !strcmp(machine_model, "SUNW,Sun-Fire-V210"))) { + prop_dictionary_t props = device_properties(busdev); + prop_array_t cfg = NULL; + prop_dictionary_t sens; + prop_data_t data; + const char name_lm[] = "i2c-lm75"; + const char name_adm[] = "i2c-adm1026"; + + DPRINTF(ACDB_PROBE, ("\nAdding sensors for %s ", + machine_model)); + cfg = prop_dictionary_get(props, "i2c-child-devices"); + if (!cfg) { + cfg = prop_array_create(); + prop_dictionary_set(props, "i2c-child-devices", + cfg); + prop_dictionary_set_bool(props, + "i2c-indirect-config", false); + } + + /* ADM1026 at 0x2e */ + sens = prop_dictionary_create(); + prop_dictionary_set_uint32(sens, "addr", 0x2e); + prop_dictionary_set_uint64(sens, "cookie", 0); + prop_dictionary_set_cstring(sens, "name", + "hardware-monitor"); + data = prop_data_create_data(&name_adm[0], + sizeof(name_adm)); + prop_dictionary_set(sens, "compatible", data); + prop_object_release(data); + prop_array_add(cfg, sens); + prop_object_release(sens); + + /* LM75 at 0x4e */ + sens = prop_dictionary_create(); + prop_dictionary_set_uint32(sens, "addr", 0x4e); + prop_dictionary_set_uint64(sens, "cookie", 0); + prop_dictionary_set_cstring(sens, "name", + "temperature-sensor"); + data = prop_data_create_data(&name_lm[0], + sizeof(name_lm)); + prop_dictionary_set(sens, "compatible", data); + prop_object_release(data); + prop_array_add(cfg, sens); + prop_object_release(sens); + } } /* set properties for PCI framebuffers */ @@ -1205,6 +1257,27 @@ instance = OF_open(name); #endif } + + /* Hardware specific device properties */ + if ((!strcmp(machine_model, "SUNW,Sun-Fire-V240") || + !strcmp(machine_model, "SUNW,Sun-Fire-V210"))) { + device_t busparent = device_parent(busdev); + prop_dictionary_t props = device_properties(dev); + + if (busparent != NULL && device_is_a(busparent, "pcfiic") && + device_is_a(dev, "adm1026hm") && props != NULL) { + prop_dictionary_set_uint8(props, "fan_div2", 0x55); + prop_dictionary_set_bool(props, "multi_read", true); + } + } + if (!strcmp(machine_model, "SUNW,Sun-Fire-V440")) { + device_t busparent = device_parent(busdev); + prop_dictionary_t props = device_properties(dev); + if (busparent != NULL && device_is_a(busparent, "pcfiic") && + device_is_a(dev, "adm1026hm") && props != NULL) { + prop_dictionary_set_bool(props, "multi_read", true); + } + } } /* Index: src/sys/dev/i2c/adm1026.c =================================================================== RCS file: src/sys/dev/i2c/adm1026.c diff -N src/sys/dev/i2c/adm1026.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/sys/dev/i2c/adm1026.c 14 Dec 2015 22:43:18 -0000 @@ -0,0 +1,552 @@ +/*- + * Copyright (c) 2015 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * 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 +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include +#include + +/* Voltage/analog sensors descriptions and registers */ +struct adm1026_volts_info { + const char* desc; + int incr; + uint8_t reg, check_tdm2; +}; + +/* Voltage maximums (in mV) from datasheet table 7 divided by 255 increments */ +static struct adm1026_volts_info adm1026_volts_table[] = { + { "Vbatt", 15624, ADM1026_VBAT_VAL, 0 }, + { "V3.3 standby", 17345, ADM1026_33VSTBY_VAL, 0 }, + { "V3.3 main", 17345, ADM1026_33VMAIN_VAL, 0 }, + { "V5.0", 26016, ADM1026_50V_VAL, 0 }, + { "Vccp", 11718, ADM1026_VCCP_VAL, 0 }, + { "V+12", 62502, ADM1026_12V_VAL, 0 }, + { "V-12", -62502, ADM1026_N12V_VAL, 0 }, + { "V3.0 0", 11718, ADM1026_AIN_VAL(0), 0 }, + { "V3.0 1", 11718, ADM1026_AIN_VAL(1), 0 }, + { "V3.0 2", 11718, ADM1026_AIN_VAL(2), 0 }, + { "V3.0 3", 11718, ADM1026_AIN_VAL(3), 0 }, + { "V3.0 4", 11718, ADM1026_AIN_VAL(4), 0 }, + { "V3.0 5", 11718, ADM1026_AIN_VAL(5), 0 }, + { "V2.5 0", 9765, ADM1026_AIN_VAL(6), 0 }, + { "V2.5 1", 9765, ADM1026_AIN_VAL(7), 0 }, + { "V2.5 2", 9765, ADM1026_AIN8_VAL, 1 }, + { "V2.5 3", 9765, ADM1026_TDM2_AIN9_VAL, 1 } +}; + +/* Maximum number of each type of sensor */ +#define ADM1026_MAX_FANS 8 +#define ADM1026_MAX_TEMPS 3 +#define ADM1026_MAX_VOLTS (sizeof(adm1026_volts_table) / \ + sizeof (adm1026_volts_table[0])) + +/* Map sensor to/from sysmon numbers */ +#define ADM1026_FAN_NUM(x) (x) +#define ADM1026_TEMP_NUM(x) (x + sc->sc_nfans) +#define ADM1026_VOLT_NUM(x) (x + sc->sc_nfans + sc->sc_ntemps) +#define ADM1026_NUM_FAN(x) (x) +#define ADM1026_NUM_TEMP(x) (x - sc->sc_nfans) +#define ADM1026_NUM_VOLT(x) (x - sc->sc_nfans - sc->sc_ntemps) + +struct adm1026_softc { + device_t sc_dev; + i2c_tag_t sc_tag; + int sc_address; + int sc_iic_flags; + bool sc_multi_read; + + uint8_t sc_rev, sc_cfg[2]; + int sc_nfans, sc_ntemps; /* Map sysmon numbers to sensors */ + int sc_fandiv[ADM1026_MAX_FANS], sc_temp_off[ADM1026_MAX_TEMPS]; + struct sysmon_envsys *sc_sme; + envsys_data_t sc_sensor[ADM1026_MAX_FANS + ADM1026_MAX_TEMPS + + ADM1026_MAX_VOLTS]; +}; + +static int adm1026_match(device_t, cfdata_t, void *); +static int adm1026_ident(struct adm1026_softc *sc); +static void adm1026_attach(device_t, device_t, void *); +static int adm1026_detach(device_t, int); +bool adm1026_pmf_suspend(device_t dev, const pmf_qual_t *qual); +bool adm1026_pmf_resume(device_t dev, const pmf_qual_t *qual); + +static void adm1026_setup_fans(struct adm1026_softc *sc, int div2_val); +static void adm1026_setup_temps(struct adm1026_softc *sc); +static void adm1026_setup_volts(struct adm1026_softc *sc); + +void adm1026_refresh(struct sysmon_envsys *sme, envsys_data_t *edata); +static void adm1026_read_fan(struct adm1026_softc *sc, envsys_data_t *edata); +static void adm1026_read_temp(struct adm1026_softc *sc, envsys_data_t *edata); +static void adm1026_read_volt(struct adm1026_softc *sc, envsys_data_t *edata); + +static int adm1026_read_reg(struct adm1026_softc *sc, + uint8_t reg, uint8_t *val); +static int adm1026_write_reg(struct adm1026_softc *sc, + uint8_t reg, uint8_t val); + +CFATTACH_DECL_NEW(adm1026hm, sizeof(struct adm1026_softc), + adm1026_match, adm1026_attach, adm1026_detach, NULL); + +static const char * adm1026_compats[] = { + "i2c-adm1026", + NULL +}; + +static int +adm1026_match(device_t parent, cfdata_t cf, void *aux) +{ + struct i2c_attach_args *ia = aux; + struct adm1026_softc sc; /* For chip ident */ + sc.sc_tag = ia->ia_tag; + sc.sc_address = ia->ia_addr; + sc.sc_iic_flags = 0; + + /* Direct config - match compats */ + if (ia->ia_name) { + if (ia->ia_ncompat > 0) { + if (iic_compat_match(ia, adm1026_compats)) + return 1; + } + /* Indirect config - check address and chip ID/rev. */ + } else { + if ((ia->ia_addr & ADM1026_ADDRMASK) == ADM1026_ADDR && + adm1026_ident(&sc)) + return 1; + } + + return 0; +} + +static int +adm1026_ident(struct adm1026_softc *sc) +{ + uint8_t val; + int err; + + /* Manufacturer ID and revision/stepping */ + err = adm1026_read_reg(sc, ADM1026_ID, &val); + if (err || val != ADM1026_MANF_ID) { + aprint_verbose("adm1026_ident: " + "manufacturer ID invalid or missing\n"); + return 0; + } + err = adm1026_read_reg(sc, ADM1026_REV, &sc->sc_rev); + if (err || ADM1026_REVISION(sc->sc_rev) != ADM1026_MANF_REV) { + aprint_verbose("adm1026_ident: " + "manufacturer revision invalid or missing\n"); + return 0; + } + return 1; +} + +static void +adm1026_attach(device_t parent, device_t self, void *aux) +{ + struct adm1026_softc *sc = device_private(self); + struct i2c_attach_args *ia = aux; + prop_dictionary_t props = device_properties(self); + uint8_t val, fan_div2; + int err, div2_val; + + sc->sc_tag = ia->ia_tag; + sc->sc_address = ia->ia_addr; + sc->sc_dev = self; + sc->sc_iic_flags = I2C_F_POLL; /* Use polling during autoconf */ + + sc->sc_multi_read = false; + prop_dictionary_get_bool(props, "multi_read", &sc->sc_multi_read); + if (prop_dictionary_get_uint8(props, "fan_div2", &fan_div2) != 0) + div2_val = fan_div2; + else + div2_val = -1; + + (void) adm1026_ident(sc); + aprint_normal(": ADM1026 hardware monitor: rev. 0x%x, step. 0x%x\n", + ADM1026_REVISION(sc->sc_rev), ADM1026_STEPPING(sc->sc_rev)); + + /* + * Start monitoring if not already monitoring. + * Wait 1.8s for the fan readings to stabilise. + */ + if ((err = adm1026_read_reg(sc, ADM1026_CONF1, &val)) != 0) { + aprint_error_dev(sc->sc_dev, ": unable to read conf1\n"); + return; + } + if (!(val & ADM1026_CONF1_MONITOR)) { + aprint_normal_dev(sc->sc_dev, + ": starting monitoring, waiting 1.8s for readings\n"); + val |= ADM1026_CONF1_MONITOR; + if ((err = adm1026_write_reg(sc, ADM1026_CONF1, val)) != 0) { + aprint_error_dev(sc->sc_dev, + ": unable to write conf1\n"); + return; + } + delay(1800000); + } + sc->sc_cfg[0] = val; + + sc->sc_sme = sysmon_envsys_create(); + sc->sc_nfans = 0; + adm1026_setup_fans(sc, div2_val); + sc->sc_ntemps = 0; + adm1026_setup_temps(sc); + adm1026_setup_volts(sc); + aprint_normal_dev(self, "%d fans, %d temperatures, %d voltages\n", + sc->sc_nfans, sc->sc_ntemps, sc->sc_ntemps == 3 ? 15 : 17); + + sc->sc_sme->sme_name = device_xname(self); + sc->sc_sme->sme_cookie = sc; + sc->sc_sme->sme_refresh = adm1026_refresh; + if (sysmon_envsys_register(sc->sc_sme)) { + aprint_error_dev(self, + "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + return; + } + + if (!pmf_device_register(self, adm1026_pmf_suspend, adm1026_pmf_resume)) + aprint_error_dev(self, "couldn't establish power handler\n"); + + sc->sc_iic_flags = 0; /* Drop polling flag */ + + return; +} + +/* + * We could stop (suspend/detach) and restart (resume) monitoring, + * but we don't do that because some machines have separate + * management hardware which can read the sensors. + */ +bool +adm1026_pmf_suspend(device_t dev, const pmf_qual_t *qual) +{ + return true; +} + +bool +adm1026_pmf_resume(device_t dev, const pmf_qual_t *qual) +{ + return true; +} + +static int +adm1026_detach(device_t self, int flags) +{ + struct adm1026_softc *sc = device_private(self); + + pmf_device_deregister(self); + + sysmon_envsys_unregister(sc->sc_sme); + sc->sc_sme = NULL; + + return 0; +} + +static void +adm1026_setup_fans(struct adm1026_softc *sc, int div2_val) +{ + int i, err = 0; + uint8_t div1, div2; + + /* Read fan-related registers (configuration and divisors) */ + if ((err = adm1026_read_reg(sc, ADM1026_CONF2, &sc->sc_cfg[1])) != 0) { + aprint_error_dev(sc->sc_dev, "unable to read conf2\n"); + return; + } + if ((err = adm1026_read_reg(sc, ADM1026_FAN_DIV1, &div1)) != 0) { + aprint_error_dev(sc->sc_dev, "unable to read fan_div1\n"); + return; + } + sc->sc_fandiv[0] = 1 << ADM1026_FAN0_DIV(div1); + sc->sc_fandiv[1] = 1 << ADM1026_FAN1_DIV(div1); + sc->sc_fandiv[2] = 1 << ADM1026_FAN2_DIV(div1); + sc->sc_fandiv[3] = 1 << ADM1026_FAN3_DIV(div1); + if (div2_val < 0) { + if ((err = + adm1026_read_reg(sc, ADM1026_FAN_DIV2, &div2)) != 0) { + aprint_error_dev(sc->sc_dev, + "unable to read fan_div2\n"); + return; + } + } else + div2 = div2_val; + sc->sc_fandiv[4] = 1 << ADM1026_FAN4_DIV(div2); + sc->sc_fandiv[5] = 1 << ADM1026_FAN5_DIV(div2); + sc->sc_fandiv[6] = 1 << ADM1026_FAN6_DIV(div2); + sc->sc_fandiv[7] = 1 << ADM1026_FAN7_DIV(div2); + + for (i = 0; i < ADM1026_MAX_FANS; i++) { + sc->sc_sensor[ADM1026_FAN_NUM(i)].state = ENVSYS_SINVALID; + /* Check configuration2 register to see which pins are fans. */ + if (ADM1026_PIN_IS_FAN(sc->sc_cfg[1], i)) { + sc->sc_sensor[ADM1026_FAN_NUM(i)].units = + ENVSYS_SFANRPM; + snprintf(sc->sc_sensor[ADM1026_FAN_NUM(i)].desc, + sizeof(sc->sc_sensor[ADM1026_FAN_NUM(i)].desc), + "fan %d", ADM1026_FAN_NUM(i)); + sc->sc_nfans++; + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[ADM1026_FAN_NUM(i)])) { + sysmon_envsys_destroy(sc->sc_sme); + aprint_error_dev(sc->sc_dev, + "unable to attach fan %d at sysmon\n", i); + return; + } + } + } +} + +static void +adm1026_setup_temps(struct adm1026_softc *sc) +{ + int i; + uint8_t val; + + /* Temperature offsets */ + if (adm1026_read_reg(sc, ADM1026_INT_TEMP_OFF, &val) + != 0) { + aprint_error_dev(sc->sc_dev, "unable to read int temp. off.\n"); + return; + } + if (val & 0x80) + sc->sc_temp_off[0] = 0 - 1000000 * (val & 0x7f); + else + sc->sc_temp_off[0] = 1000000 * (val & 0x7f); + if (adm1026_read_reg(sc, ADM1026_TDM1_OFF, &val) != 0) { + aprint_error_dev(sc->sc_dev, "unable to read tdm1 off.\n"); + return; + } + if (val & 0x80) + sc->sc_temp_off[1] = 0 - 1000000 * (val & 0x7f); + else + sc->sc_temp_off[1] = 1000000 * (val & 0x7f); + if (adm1026_read_reg(sc, ADM1026_TDM2_OFF, &val) != 0) { + aprint_error_dev(sc->sc_dev, "unable to read tdm2 off.\n"); + return; + } + if (val & 0x80) + sc->sc_temp_off[2] = 0 - 1000000 * (val & 0x7f); + else + sc->sc_temp_off[2] = 1000000 * (val & 0x7f); + + strlcpy(sc->sc_sensor[ADM1026_TEMP_NUM(0)].desc, "internal", + sizeof(sc->sc_sensor[ADM1026_TEMP_NUM(0)].desc)); + strlcpy(sc->sc_sensor[ADM1026_TEMP_NUM(1)].desc, "external 1", + sizeof(sc->sc_sensor[ADM1026_TEMP_NUM(1)].desc)); + strlcpy(sc->sc_sensor[ADM1026_TEMP_NUM(2)].desc, "external 2", + sizeof(sc->sc_sensor[ADM1026_TEMP_NUM(2)].desc)); + for (i = 0; i < ADM1026_MAX_TEMPS; i++) { + /* Check configuration1 register to see if TDM2 is configured */ + if (i == 2 && !ADM1026_PIN_IS_TDM2(sc->sc_cfg[0])) + continue; + sc->sc_sensor[ADM1026_TEMP_NUM(i)].units = ENVSYS_STEMP; + sc->sc_sensor[ADM1026_TEMP_NUM(i)].state = ENVSYS_SINVALID; + sc->sc_ntemps++; + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[ADM1026_TEMP_NUM(i)])) { + sysmon_envsys_destroy(sc->sc_sme); + aprint_error_dev(sc->sc_dev, + "unable to attach temp %d at sysmon\n", i); + return; + } + } +} + +static void +adm1026_setup_volts(struct adm1026_softc *sc) +{ + int i; + + for (i = 0; i < ADM1026_MAX_VOLTS; i++) { + /* Check configuration1 register to see if TDM2 is configured */ + if (adm1026_volts_table[i].check_tdm2 && + ADM1026_PIN_IS_TDM2(sc->sc_cfg[0])) + continue; + strlcpy(sc->sc_sensor[ADM1026_VOLT_NUM(i)].desc, + adm1026_volts_table[i].desc, + sizeof(sc->sc_sensor[ADM1026_VOLT_NUM(i)].desc)); + sc->sc_sensor[ADM1026_VOLT_NUM(i)].units = ENVSYS_SVOLTS_DC; + sc->sc_sensor[ADM1026_VOLT_NUM(i)].state = ENVSYS_SINVALID; + if (sysmon_envsys_sensor_attach( + sc->sc_sme, &sc->sc_sensor[ADM1026_VOLT_NUM(i)])) { + sysmon_envsys_destroy(sc->sc_sme); + aprint_error_dev(sc->sc_dev, + "unable to attach volts %d at sysmon\n", i); + return; + } + } +} + +void +adm1026_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) +{ + struct adm1026_softc *sc = sme->sme_cookie; + + if (edata->sensor < sc->sc_nfans) + adm1026_read_fan(sc, edata); + else if (edata->sensor < sc->sc_nfans + sc->sc_ntemps) + adm1026_read_temp(sc, edata); + else + adm1026_read_volt(sc, edata); +} + +static void +adm1026_read_fan(struct adm1026_softc *sc, envsys_data_t *edata) +{ + int fan = ADM1026_NUM_FAN(edata->sensor); + int err; + uint8_t val; + + if ((err = adm1026_read_reg(sc, ADM1026_FAN_VAL(fan), &val)) != 0) { + edata->state = ENVSYS_SINVALID; + return; + } + if (val == 0xff || val == 0x00) /* Fan missing or stopped */ + edata->value_cur = 0; + else + edata->value_cur = 1350000 / (val * sc->sc_fandiv[fan]); + edata->state = ENVSYS_SVALID; +} + +static void +adm1026_read_temp(struct adm1026_softc *sc, envsys_data_t *edata) +{ + int temp = ADM1026_NUM_TEMP(edata->sensor); + int err; + uint8_t val; + + if (temp == 0) + err = adm1026_read_reg(sc, ADM1026_INT_TEMP_VAL, &val); + else if (temp == 1) + err = adm1026_read_reg(sc, ADM1026_TDM1_VAL, &val); + else + err = adm1026_read_reg(sc, ADM1026_TDM2_AIN9_VAL, &val); + if (err) { + edata->state = ENVSYS_SINVALID; + return; + } + + if (val & 0x80) /* Negative temperature */ + edata->value_cur = 273150000 - sc->sc_temp_off[temp] - + 1000000 * (val & 0x7f); + else /* Positive temperature */ + edata->value_cur = 273150000 - sc->sc_temp_off[temp] + + 1000000 * val; + edata->state = ENVSYS_SVALID; +} + +static void +adm1026_read_volt(struct adm1026_softc *sc, envsys_data_t *edata) +{ + int volt = ADM1026_NUM_VOLT(edata->sensor); + int err; + uint8_t val; + + err = adm1026_read_reg(sc, adm1026_volts_table[volt].reg, &val); + if (err) { + edata->state = ENVSYS_SINVALID; + return; + } + /* Vbatt is not valid for < 1.5V */ + if (volt == 0 && val < 0x60) + edata->state = ENVSYS_SINVALID; + edata->value_cur = (int) val * adm1026_volts_table[volt].incr; + edata->state = ENVSYS_SVALID; +} + +static int +adm1026_read_reg(struct adm1026_softc *sc, uint8_t reg, uint8_t *val) +{ +#define ADM1026_READ_RETRIES 4 + int i, j, err = 0; + uint8_t creg, cval, tmp[ADM1026_READ_RETRIES + 1]; + + if ((err = iic_acquire_bus(sc->sc_tag, sc->sc_iic_flags)) != 0) + return err; + /* Standard ADM1026 */ + if (sc->sc_multi_read == false) { + err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, val, 1, 0); + /* + * The ADM1026 found in Sun V210/V240 sometimes gives bogus values. + * We'll read at twice and check that we get (nearly) the same value. + * If not, we'll read another register and then re-read the first. + */ + } else { + if (reg == ADM1026_CONF1) + creg = ADM1026_CONF2; + else + creg = ADM1026_CONF1; + if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, &tmp[0], 1, 0)) != 0) { + iic_release_bus(sc->sc_tag, sc->sc_iic_flags); + return err; + } + for (i = 1; i <= ADM1026_READ_RETRIES; i++) { + if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, ®, 1, &tmp[i], 1, 0)) != 0) + break; + for (j = 0; j < i; j++) + if (abs(tmp[j] - tmp[i]) < 3) { + *val = tmp[i]; + iic_release_bus(sc->sc_tag, + sc->sc_iic_flags); + return 0; + } + if ((err = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, + sc->sc_address, &creg, 1, &cval, 1, 0)) != 0) + break; + err = -1; /* Return error if we don't match. */ + } + } + iic_release_bus(sc->sc_tag, sc->sc_iic_flags); + return err; +} + +static int +adm1026_write_reg(struct adm1026_softc *sc, uint8_t reg, uint8_t val) +{ + int err = 0; + + if ((err = iic_acquire_bus(sc->sc_tag, sc->sc_iic_flags)) != 0) + return err; + err = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, + ®, 1, &val, 1, 0); + iic_release_bus(sc->sc_tag, sc->sc_iic_flags); + return err; +} Index: src/sys/dev/i2c/adm1026reg.h =================================================================== RCS file: src/sys/dev/i2c/adm1026reg.h diff -N src/sys/dev/i2c/adm1026reg.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/sys/dev/i2c/adm1026reg.h 14 Dec 2015 22:43:18 -0000 @@ -0,0 +1,153 @@ +/*- + * Copyright (c) 2015 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * 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_ADM1026REG_H_ +#define _DEV_I2C_ADM1026REG_H_ + +/* + * Register definitions for "ADM1026 Thermal System Management Fan Controller" + * Datasheet available from (URL valid as of December 2015): + * http://www.onsemi.com/pub_link/Collateral/ADM1026-D.PDF + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#define ADM1026_ADDRMASK 0x3f8 +#define ADM1026_ADDR 0x2c + +#define ADM1026_CONF1 0x00 +#define ADM1026_CONF2 0x01 +#define ADM1026_FAN_DIV1 0x02 +#define ADM1026_FAN_DIV2 0x03 +#define ADM1026_DAC_CTRL 0x04 +#define ADM1026_PWM_CTRL 0x05 +#define ADM1026_EEPROM 0x06 +#define ADM1026_TVG_CONF 0x07 /* THERM, V-REF and GPIO16 */ +#define ADM1026_GPIO_CONF1 0x08 +#define ADM1026_GPIO_CONF2 0x09 +#define ADM1026_GPIO_CONF3 0x0a +#define ADM1026_GPIO_CONF4 0x0b +#define ADM1026_EEPROM2 0x0c +#define ADM1026_INT_THERM_MAX 0x0d +#define ADM1026_TDM1_THERM_MAX 0x0e +#define ADM1026_TDM2_THERM_MAX 0x0f +#define ADM1026_INT_FAN_MIN 0x10 +#define ADM1026_TDM1_FAN_MIN 0x11 +#define ADM1026_TDM2_FAN_MIN 0x12 +#define ADM1026_EEPROM3 0x13 +#define ADM1026_TEST1 0x14 +#define ADM1026_TEST2 0x15 +#define ADM1026_ID 0x16 +#define ADM1026_REV 0x17 +#define ADM1026_MASK1 0x18 /* Temperature and supply voltage */ +#define ADM1026_MASK2 0x19 /* Analogue input */ +#define ADM1026_MASK3 0x1a /* Fan */ +#define ADM1026_MASK4 0x1b /* Temp, V-Batt, A-In8, Therm, + AFC, CI, GPIO 16 */ +#define ADM1026_MASK5 0x1c /* GPIO 0-7 */ +#define ADM1026_MASK6 0x1d /* GPIO 8-15 */ +#define ADM1026_INT_TEMP_OFF 0x1e +#define ADM1026_INT_TEMP_VAL 0x1f +#define ADM1026_STAT1 0x20 /* External temp, and supply voltage */ +#define ADM1026_STAT2 0x21 /* Analogue input */ +#define ADM1026_STAT3 0x22 /* Fan */ +#define ADM1026_STAT4 0x23 /* Temp, V-Batt, A-In8, Therm, + AFC, CI, GPIO 16 */ +#define ADM1026_STAT5 0x24 /* GPIO 0-7 */ +#define ADM1026_STAT6 0x25 /* GPIO 8-15 */ +#define ADM1026_VBAT_VAL 0x26 +#define ADM1026_AIN8_VAL 0x27 +#define ADM1026_TDM1_VAL 0x28 +#define ADM1026_TDM2_AIN9_VAL 0x29 +#define ADM1026_33VSTBY_VAL 0x2a +#define ADM1026_33VMAIN_VAL 0x2b +#define ADM1026_50V_VAL 0x2c +#define ADM1026_VCCP_VAL 0x2d +#define ADM1026_12V_VAL 0x2e +#define ADM1026_N12V_VAL 0x2f +/* 8 analog in registers: 0x30 to 0x37 */ +#define ADM1026_AIN_VAL(x) (0x30 + x) +/* 8 fan value registers: 0x38 to 0x3f */ +#define ADM1026_FAN_VAL(x) (0x38 + x) +#define ADM1026_TDM1_MAX 0x40 +#define ADM1026_TDM2_AIN9_MAX 0x41 +#define ADM1026_33V_STBY_MAX 0x42 +#define ADM1026_33V_MAIN_MAX 0x43 +#define ADM1026_50V_MAX 0x44 +#define ADM1026_VCCP_MAX 0x45 +#define ADM1026_12V_MAX 0x46 +#define ADM1026_N12V_MAX 0x47 +#define ADM1026_TDM1_MIN 0x48 +#define ADM1026_TDM2_AIN9_MIN 0x49 +#define ADM1026_33V_STBY_MIN 0x4a +#define ADM1026_33V_MAIN_MIN 0x4b +#define ADM1026_50V_MIN 0x4c +#define ADM1026_VCCP_MIN 0x4d +#define ADM1026_12V_MIN 0x4e +#define ADM1026_N12V_MIN 0x4f +/* 8 analog in high limit registers: 0x50 to 0x57 */ +#define ADM1026_AIN_MAX(x) (0x50 + x) +/* 8 analog in low limit registers: 0x58 to 0x5f */ +#define ADM1026_AIN_MIN(x) (0x58 + x) +/* 8 fan high limit registers: 0x60 to 0x67 (no low limit registers) */ +#define ADM1026_FAN_MAX(x) (0x60 + x) +#define ADM1026_INT_TEMP_MAX 0x68 +#define ADM1026_INT_TEMP_MIN 0x69 +#define ADM1026_VBATT_MAX 0x6a +#define ADM1026_VBATT_MIN 0x6b +#define ADM1026_AIN8_MAX 0x6c +#define ADM1026_AIN8_MIN 0x6d +#define ADM1026_TDM1_OFF 0x6e +#define ADM1026_TDM2_OFF 0x6f + +/* 0x00: Configuration register 1 */ +#define ADM1026_CONF1_MONITOR 0x01 /* Start monitoring */ +#define ADM1026_PIN_IS_TDM2(val) ((val & 0x08) == 0) + +/* 0x01: Configuration register 2 */ +#define ADM1026_PIN_IS_FAN(val, x) ((val & (1 << x)) == 0) + +/* 0x02/0x03: Fan 0-7 divisors */ +#define ADM1026_FAN0_DIV(x) (x & 0x03) +#define ADM1026_FAN1_DIV(x) ((x >> 2) & 0x03) +#define ADM1026_FAN2_DIV(x) ((x >> 4) & 0x03) +#define ADM1026_FAN3_DIV(x) ((x >> 6) & 0x03) +#define ADM1026_FAN4_DIV(x) ADM1026_FAN0_DIV(x) +#define ADM1026_FAN5_DIV(x) ADM1026_FAN1_DIV(x) +#define ADM1026_FAN6_DIV(x) ADM1026_FAN2_DIV(x) +#define ADM1026_FAN7_DIV(x) ADM1026_FAN3_DIV(x) + +/* 0x16/0x17: Manufacturer and revision ID's */ +#define ADM1026_MANF_ID 0x41 /* Manufacturer ID 0x41 */ +#define ADM1026_REVISION(x) ((x & 0xf0) >> 4) +#define ADM1026_MANF_REV 0x04 /* Manufacturer revision 0x04 */ +#define ADM1026_STEPPING(x) (x & 0x0f) + +#endif /* _DEV_I2C_ADM1026REG_H_ */ Index: src/sys/dev/i2c/files.i2c =================================================================== RCS file: /cvsroot/src/sys/dev/i2c/files.i2c,v retrieving revision 1.68 diff -u -r1.68 files.i2c --- src/sys/dev/i2c/files.i2c 21 Nov 2015 10:57:32 -0000 1.68 +++ src/sys/dev/i2c/files.i2c 14 Dec 2015 22:43:18 -0000 @@ -176,6 +176,11 @@ attach admtemp at iic file dev/i2c/adm1021.c admtemp +# ADM1026 hardware monitor +device adm1026hm: sysmon_envsys +attach adm1026hm at iic +file dev/i2c/adm1026.c adm1026hm + # SMSC LPC47M192 hardware monitor device smscmon: sysmon_envsys attach smscmon at iic Index: src/sys/dev/ic/pcf8584.c =================================================================== RCS file: /cvsroot/src/sys/dev/ic/pcf8584.c,v retrieving revision 1.11 diff -u -r1.11 pcf8584.c --- src/sys/dev/ic/pcf8584.c 20 Jan 2014 22:02:32 -0000 1.11 +++ src/sys/dev/ic/pcf8584.c 14 Dec 2015 22:43:19 -0000 @@ -175,14 +175,28 @@ if (sc->sc_master) pcfiic_choose_bus(sc, addr >> 7); - if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0) - return (1); - - if (len > 0) { - if (I2C_OP_WRITE_P(op)) - ret = pcfiic_xmit(sc, addr & 0x7f, buf, len); - else - ret = pcfiic_recv(sc, addr & 0x7f, buf, len); + /* + * If we are writing, write address, cmdbuf, buf. + * If we are reading, write address, cmdbuf, then read address, buf. + */ + if (I2C_OP_WRITE_P(op)) { + if (len > 0) { + uint8_t *tmp; + + tmp = malloc(cmdlen + len, M_DEVBUF, + flags & I2C_F_POLL ? M_NOWAIT : M_WAITOK); + if (tmp == NULL) + return (1); + memcpy(tmp, cmdbuf, cmdlen); + memcpy(tmp + cmdlen, buf, len); + ret = pcfiic_xmit(sc, addr & 0x7f, tmp, cmdlen + len); + free(tmp, M_DEVBUF); + } else + ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen); + } else { + if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen) != 0) + return (1); + ret = pcfiic_recv(sc, addr & 0x7f, buf, len); } return (ret); } Index: src/sys/arch/sparc64/conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/sparc64/conf/GENERIC,v retrieving revision 1.182 diff -u -r1.182 GENERIC --- src/sys/arch/sparc64/conf/GENERIC 26 Sep 2015 11:16:13 -0000 1.182 +++ src/sys/arch/sparc64/conf/GENERIC 14 Dec 2015 22:43:19 -0000 @@ -950,6 +950,7 @@ spdmem* at iic? addr? admtemp* at iic? addr? +adm1026hm* at iic? addr? ecadc* at iic? addr? # envctrl/envctrltwo on E250/E450 lmtemp* at iic? addr? tda* at iic? addr? # fan control on SB1000/2000 Index: src/share/man/man4/adm1026tm.4 =================================================================== RCS file: src/share/man/man4/adm1026tm.4 diff -N src/share/man/man4/adm1026tm.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/share/man/man4/adm1026tm.4 14 Dec 2015 22:43:19 -0000 @@ -0,0 +1,83 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2015 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Julian Coleman. +.\" +.\" 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 December 11, 2015 +.Dt ADM1026HM +.Os +.Sh NAME +.Nm adm1026hm +.Nd Analog Devices ADM1026 complete thermal system management controller +.Sh SYNOPSIS +.Cd "adm1026hm* at iic0 addr 0x2e: ADM1026 hardware monitor: rev. 0x4, step. 0x5" +.Cd "adm1026hm*: 8 fans, 3 temperatures, 15 voltages" +.Sh DESCRIPTION +The +.Nm +driver provides support for the Analog Devices ADM1026 hardware monitor. +The chip possesses 8 fan speed sensors, 3 temperature sensors, +and 17 voltage sensors. +The number of each sensor type configured by the driver depends on the +chip configuration. +.Pp +The values of the sensors are made available through the +.Xr envstat 8 +interface. +.Bl -column "V3.3 standby" "uV DC" "Description" -offset indent +.It Sy "Sensor" Ta Sy "Units" Ta Sy "Description" +.It Li "fan N" Ta "RPM" Ta "Fan 0-7" +.It Li "internal" Ta "C" Ta "Internal temperature" +.It Li "external N" Ta "C" Ta "External temperature 1\(en2" +.It Li "Vbatt" Ta "mV DC" Ta "Battery voltage" +.It Li "V3.3 standby" Ta "mV DC" Ta "3.3V standby voltage" +.It Li "V3.3 main" Ta "mV DC" Ta "3.3V main voltage" +.It Li "V5.0" Ta "mV DC" Ta "5.0V supply voltage" +.It Li "V+12" Ta "mV DC" Ta "+12V supply voltage" +.It Li "V-12" Ta "mV DC" Ta "-12V supply voltage" +.It Li "V3.3 N" Ta "mV DC" Ta "Analog in (3.3V reference) 0\(en5" +.It Li "V2.5 N" Ta "mV DC" Ta "Analog in (2.5V reference) 0\(en3" +.El +.Sh SEE ALSO +.Xr iic 4 , +.Xr intro 4 , +.Xr envstat 8 +.Sh BUGS +It's not possible to determine if either a sensor is not connected, +or the monitored device is producing no output. +Therefore, unconnected sensors will show outputs of 0. +.Pp +The +.Nm +driver does not support checking or altering limit values, interrupt output, +nor the built-in EEPROM. +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Julian Coleman Aq Mt jcoleman@netbsd.org . Index: src/distrib/sets/lists/man/mi =================================================================== RCS file: /cvsroot/src/distrib/sets/lists/man/mi,v retrieving revision 1.1513 diff -u -r1.1513 mi --- src/distrib/sets/lists/man/mi 12 Dec 2015 23:42:43 -0000 1.1513 +++ src/distrib/sets/lists/man/mi 14 Dec 2015 22:43:19 -0000 @@ -720,6 +720,7 @@ ./usr/share/man/cat4/adbkbd.0 man-sys-catman .cat ./usr/share/man/cat4/adbms.0 man-sys-catman .cat ./usr/share/man/cat4/adc.0 man-sys-catman .cat +./usr/share/man/cat4/adm1026hm.0 man-sys-catman .cat ./usr/share/man/cat4/adm1027.0 man-sys-catman .cat ./usr/share/man/cat4/adm1030.0 man-sys-catman .cat ./usr/share/man/cat4/admtemp.0 man-sys-catman .cat @@ -3828,6 +3829,7 @@ ./usr/share/man/html4/adbkbd.html man-sys-htmlman html ./usr/share/man/html4/adbms.html man-sys-htmlman html ./usr/share/man/html4/adc.html man-sys-htmlman html +./usr/share/man/html4/adm1026hm.html man-sys-htmlman html ./usr/share/man/html4/adm1027.html man-sys-htmlman html ./usr/share/man/html4/adm1030.html man-sys-htmlman html ./usr/share/man/html4/admtemp.html man-sys-htmlman html @@ -6630,6 +6632,7 @@ ./usr/share/man/man4/adbkbd.4 man-sys-man .man ./usr/share/man/man4/adbms.4 man-sys-man .man ./usr/share/man/man4/adc.4 man-sys-man .man +./usr/share/man/man4/adm1026hm.4 man-sys-man .man ./usr/share/man/man4/adm1027.4 man-sys-man .man ./usr/share/man/man4/adm1030.4 man-sys-man .man ./usr/share/man/man4/admtemp.4 man-sys-man .man