1 1.20 thorpej /* $NetBSD: pcf8591_envctrl.c,v 1.20 2025/09/17 14:21:36 thorpej Exp $ */ 2 1.1 martin /* $OpenBSD: pcf8591_envctrl.c,v 1.6 2007/10/25 21:17:20 kettenis Exp $ */ 3 1.1 martin 4 1.1 martin /* 5 1.1 martin * Copyright (c) 2006 Damien Miller <djm (at) openbsd.org> 6 1.1 martin * Copyright (c) 2007 Mark Kettenis <kettenis (at) openbsd.org> 7 1.1 martin * 8 1.1 martin * Permission to use, copy, modify, and distribute this software for any 9 1.1 martin * purpose with or without fee is hereby granted, provided that the above 10 1.1 martin * copyright notice and this permission notice appear in all copies. 11 1.1 martin * 12 1.1 martin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 1.1 martin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 1.1 martin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 1.1 martin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 1.1 martin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 1.1 martin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 1.1 martin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 1.1 martin */ 20 1.1 martin 21 1.6 mrg #include <sys/cdefs.h> 22 1.20 thorpej __KERNEL_RCSID(0, "$NetBSD: pcf8591_envctrl.c,v 1.20 2025/09/17 14:21:36 thorpej Exp $"); 23 1.6 mrg 24 1.1 martin #include <sys/param.h> 25 1.1 martin #include <sys/systm.h> 26 1.10 jdc #include <sys/kernel.h> 27 1.1 martin #include <sys/device.h> 28 1.1 martin 29 1.1 martin #include <dev/sysmon/sysmonvar.h> 30 1.10 jdc #include <dev/sysmon/sysmon_taskq.h> 31 1.10 jdc 32 1.10 jdc #include <machine/autoconf.h> 33 1.1 martin 34 1.1 martin #include <dev/ofw/openfirm.h> 35 1.1 martin #include <dev/i2c/i2cvar.h> 36 1.1 martin 37 1.14 jdc #ifdef ECADC_DEBUG 38 1.14 jdc #define DPRINTF printf 39 1.14 jdc #else 40 1.14 jdc #define DPRINTF if (0) printf 41 1.14 jdc #endif 42 1.14 jdc 43 1.10 jdc /* Translation tables contain 254 entries */ 44 1.10 jdc #define XLATE_SIZE 256 45 1.10 jdc #define XLATE_MAX (XLATE_SIZE - 2) 46 1.10 jdc 47 1.1 martin #define PCF8591_CHANNELS 4 48 1.1 martin 49 1.1 martin #define PCF8591_CTRL_CH0 0x00 50 1.1 martin #define PCF8591_CTRL_CH1 0x01 51 1.1 martin #define PCF8591_CTRL_CH2 0x02 52 1.1 martin #define PCF8591_CTRL_CH3 0x03 53 1.1 martin #define PCF8591_CTRL_AUTOINC 0x04 54 1.1 martin #define PCF8591_CTRL_OSCILLATOR 0x40 55 1.1 martin 56 1.10 jdc #define PCF8591_TEMP_SENS 0x00 57 1.16 jdc #define PCF8591_SYS_FAN_CTRL 0x01 58 1.10 jdc 59 1.1 martin struct ecadc_channel { 60 1.1 martin u_int chan_num; 61 1.10 jdc u_int chan_type; 62 1.1 martin envsys_data_t chan_sensor; 63 1.1 martin u_char *chan_xlate; 64 1.1 martin int64_t chan_factor; 65 1.1 martin int64_t chan_min; 66 1.1 martin int64_t chan_warn; 67 1.1 martin int64_t chan_crit; 68 1.10 jdc u_int8_t chan_speed; 69 1.1 martin }; 70 1.1 martin 71 1.1 martin struct ecadc_softc { 72 1.1 martin device_t sc_dev; 73 1.1 martin i2c_tag_t sc_tag; 74 1.1 martin i2c_addr_t sc_addr; 75 1.10 jdc u_char sc_ps_xlate[XLATE_SIZE]; 76 1.10 jdc u_char sc_cpu_xlate[XLATE_SIZE]; 77 1.10 jdc u_char sc_cpu_fan_spd[XLATE_SIZE]; 78 1.1 martin u_int sc_nchan; 79 1.1 martin struct ecadc_channel sc_channels[PCF8591_CHANNELS]; 80 1.1 martin struct sysmon_envsys *sc_sme; 81 1.10 jdc int sc_hastimer; 82 1.10 jdc callout_t sc_timer; 83 1.1 martin }; 84 1.1 martin 85 1.1 martin static int ecadc_match(device_t, cfdata_t, void *); 86 1.1 martin static void ecadc_attach(device_t, device_t, void *); 87 1.10 jdc static int ecadc_detach(device_t, int); 88 1.1 martin static void ecadc_refresh(struct sysmon_envsys *, envsys_data_t *); 89 1.1 martin static void ecadc_get_limits(struct sysmon_envsys *, envsys_data_t *, 90 1.10 jdc sysmon_envsys_lim_t *, u_int32_t *); 91 1.15 jdc static int ecadc_set_fan_speed(struct ecadc_softc *, u_int8_t, u_int8_t); 92 1.10 jdc static void ecadc_timeout(void *); 93 1.10 jdc static void ecadc_fan_adjust(void *); 94 1.10 jdc 95 1.10 jdc CFATTACH_DECL3_NEW(ecadc, sizeof(struct ecadc_softc), 96 1.10 jdc ecadc_match, ecadc_attach, ecadc_detach, NULL, NULL, NULL, 97 1.10 jdc DVF_DETACH_SHUTDOWN); 98 1.1 martin 99 1.9 thorpej static const struct device_compatible_entry compat_data[] = { 100 1.17 thorpej { .compat = "ecadc" }, 101 1.19 thorpej DEVICE_COMPAT_EOL 102 1.8 thorpej }; 103 1.8 thorpej 104 1.1 martin static int 105 1.1 martin ecadc_match(device_t parent, cfdata_t cf, void *aux) 106 1.1 martin { 107 1.1 martin struct i2c_attach_args *ia = aux; 108 1.7 thorpej int match_result; 109 1.1 martin 110 1.13 jdc if (iic_use_direct_match(ia, cf, compat_data, &match_result)) 111 1.13 jdc return match_result; 112 1.7 thorpej 113 1.7 thorpej /* This driver is direct-config only. */ 114 1.1 martin 115 1.1 martin return 0; 116 1.1 martin } 117 1.1 martin 118 1.1 martin static void 119 1.1 martin ecadc_attach(device_t parent, device_t self, void *aux) 120 1.1 martin { 121 1.1 martin struct i2c_attach_args *ia = aux; 122 1.1 martin struct ecadc_softc *sc = device_private(self); 123 1.1 martin u_char term[256]; 124 1.1 martin u_char *cp, *desc; 125 1.1 martin int64_t minv, warnv, crit, num, den; 126 1.1 martin u_int8_t junk[PCF8591_CHANNELS + 1]; 127 1.1 martin envsys_data_t *sensor; 128 1.20 thorpej int len, error, addr, chan; 129 1.20 thorpej int node = devhandle_to_of(device_handle(self)); 130 1.1 martin u_int i; 131 1.1 martin 132 1.1 martin sc->sc_dev = self; 133 1.10 jdc sc->sc_nchan = 0; 134 1.10 jdc sc->sc_hastimer = 0; 135 1.10 jdc 136 1.14 jdc DPRINTF("\n"); 137 1.1 martin if ((len = OF_getprop(node, "thermisters", term, 138 1.1 martin sizeof(term))) < 0) { 139 1.1 martin aprint_error(": couldn't find \"thermisters\" property\n"); 140 1.1 martin return; 141 1.1 martin } 142 1.1 martin 143 1.1 martin if (OF_getprop(node, "cpu-temp-factors", &sc->sc_cpu_xlate[2], 144 1.10 jdc XLATE_MAX) < 0) { 145 1.1 martin aprint_error(": couldn't find \"cpu-temp-factors\" property\n"); 146 1.1 martin return; 147 1.1 martin } 148 1.1 martin sc->sc_cpu_xlate[0] = sc->sc_cpu_xlate[1] = sc->sc_cpu_xlate[2]; 149 1.1 martin 150 1.1 martin /* Only the Sun Enterprise 450 has these. */ 151 1.10 jdc OF_getprop(node, "ps-temp-factors", &sc->sc_ps_xlate[2], XLATE_MAX); 152 1.1 martin sc->sc_ps_xlate[0] = sc->sc_ps_xlate[1] = sc->sc_ps_xlate[2]; 153 1.1 martin 154 1.1 martin cp = term; 155 1.1 martin while (cp < term + len) { 156 1.1 martin addr = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 157 1.1 martin chan = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 158 1.1 martin minv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 159 1.1 martin warnv = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 160 1.1 martin crit = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 161 1.1 martin num = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 162 1.1 martin den = cp[0] << 24 | cp[1] << 16 | cp[2] << 8 | cp[3]; cp += 4; 163 1.1 martin desc = cp; 164 1.1 martin while (cp < term + len && *cp++); 165 1.1 martin 166 1.1 martin if (addr != (ia->ia_addr << 1)) 167 1.1 martin continue; 168 1.1 martin 169 1.1 martin if (num == 0 || den == 0) 170 1.1 martin num = den = 1; 171 1.1 martin 172 1.1 martin sc->sc_channels[sc->sc_nchan].chan_num = chan; 173 1.10 jdc sc->sc_channels[sc->sc_nchan].chan_type = PCF8591_TEMP_SENS; 174 1.1 martin 175 1.1 martin sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor; 176 1.1 martin sensor->units = ENVSYS_STEMP; 177 1.4 jdc sensor->flags |= ENVSYS_FMONLIMITS; 178 1.5 pgoyette sensor->state = ENVSYS_SINVALID; 179 1.1 martin strlcpy(sensor->desc, desc, sizeof(sensor->desc)); 180 1.1 martin 181 1.14 jdc if (strncmp(desc, "CPU", 3) == 0) { 182 1.1 martin sc->sc_channels[sc->sc_nchan].chan_xlate = 183 1.1 martin sc->sc_cpu_xlate; 184 1.14 jdc DPRINTF("%s: " 185 1.14 jdc "added %s sensor (chan %d) with cpu_xlate\n", 186 1.14 jdc device_xname(sc->sc_dev), desc, chan); 187 1.14 jdc } else if (strncmp(desc, "PS", 2) == 0) { 188 1.1 martin sc->sc_channels[sc->sc_nchan].chan_xlate = 189 1.1 martin sc->sc_ps_xlate; 190 1.14 jdc DPRINTF("%s: " 191 1.14 jdc "added %s sensor (chan %d) with ps_xlate\n", 192 1.14 jdc device_xname(sc->sc_dev), desc, chan); 193 1.14 jdc } else { 194 1.1 martin sc->sc_channels[sc->sc_nchan].chan_factor = 195 1.1 martin (1000000 * num) / den; 196 1.14 jdc DPRINTF("%s: " 197 1.14 jdc "added %s sensor (chan %d) without xlate\n", 198 1.14 jdc device_xname(sc->sc_dev), desc, chan); 199 1.14 jdc } 200 1.1 martin sc->sc_channels[sc->sc_nchan].chan_min = 201 1.1 martin 273150000 + 1000000 * minv; 202 1.1 martin sc->sc_channels[sc->sc_nchan].chan_warn = 203 1.1 martin 273150000 + 1000000 * warnv; 204 1.1 martin sc->sc_channels[sc->sc_nchan].chan_crit = 205 1.1 martin 273150000 + 1000000 * crit; 206 1.1 martin sc->sc_nchan++; 207 1.1 martin } 208 1.1 martin 209 1.15 jdc sc->sc_tag = ia->ia_tag; 210 1.15 jdc sc->sc_addr = ia->ia_addr; 211 1.15 jdc 212 1.15 jdc iic_acquire_bus(sc->sc_tag, 0); 213 1.15 jdc 214 1.15 jdc /* Try a read now, so we can fail if this component isn't present */ 215 1.15 jdc if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 216 1.15 jdc NULL, 0, junk, sc->sc_nchan + 1, 0)) { 217 1.15 jdc aprint_normal(": read failed\n"); 218 1.15 jdc iic_release_bus(sc->sc_tag, 0); 219 1.15 jdc return; 220 1.15 jdc } 221 1.15 jdc 222 1.15 jdc iic_release_bus(sc->sc_tag, 0); 223 1.15 jdc 224 1.10 jdc /* 225 1.10 jdc * Fan speed changing information is missing from OFW 226 1.10 jdc * The E250 CPU fan is connected to the sensor at addr 0x4a, channel 1 227 1.10 jdc */ 228 1.10 jdc if (ia->ia_addr == 0x4a && !strcmp(machine_model, "SUNW,Ultra-250") && 229 1.10 jdc OF_getprop(node, "cpu-fan-speeds", &sc->sc_cpu_fan_spd, 230 1.10 jdc XLATE_MAX) > 0) { 231 1.10 jdc sc->sc_channels[sc->sc_nchan].chan_num = 1; 232 1.16 jdc sc->sc_channels[sc->sc_nchan].chan_type = PCF8591_SYS_FAN_CTRL; 233 1.10 jdc sensor = &sc->sc_channels[sc->sc_nchan].chan_sensor; 234 1.10 jdc sensor->units = ENVSYS_INTEGER; 235 1.10 jdc sensor->flags = ENVSYS_FMONNOTSUPP; 236 1.10 jdc sensor->state = ENVSYS_SINVALID; 237 1.16 jdc strlcpy(sensor->desc, "SYSFAN", sizeof(sensor->desc)); 238 1.10 jdc sc->sc_channels[sc->sc_nchan].chan_xlate = sc->sc_cpu_fan_spd; 239 1.14 jdc DPRINTF("%s: " 240 1.14 jdc "added CPUFAN sensor (chan %d) with cpu-fan xlate\n", 241 1.14 jdc device_xname(sc->sc_dev), 242 1.14 jdc sc->sc_channels[sc->sc_nchan].chan_num); 243 1.15 jdc 244 1.15 jdc /* Set the fan to medium speed */ 245 1.15 jdc sc->sc_channels[sc->sc_nchan].chan_speed = 246 1.15 jdc (sc->sc_cpu_fan_spd[0]+sc->sc_cpu_fan_spd[XLATE_MAX])/2; 247 1.15 jdc ecadc_set_fan_speed(sc, sc->sc_channels[sc->sc_nchan].chan_num, 248 1.15 jdc sc->sc_channels[sc->sc_nchan].chan_speed); 249 1.15 jdc 250 1.10 jdc sc->sc_nchan++; 251 1.10 jdc sc->sc_hastimer = 1; 252 1.10 jdc } 253 1.10 jdc 254 1.1 martin /* Hook us into the sysmon_envsys subsystem */ 255 1.1 martin sc->sc_sme = sysmon_envsys_create(); 256 1.1 martin sc->sc_sme->sme_name = device_xname(self); 257 1.1 martin sc->sc_sme->sme_cookie = sc; 258 1.1 martin sc->sc_sme->sme_refresh = ecadc_refresh; 259 1.1 martin sc->sc_sme->sme_get_limits = ecadc_get_limits; 260 1.1 martin 261 1.1 martin /* Initialize sensor data. */ 262 1.1 martin for (i = 0; i < sc->sc_nchan; i++) 263 1.1 martin sysmon_envsys_sensor_attach(sc->sc_sme, 264 1.1 martin &sc->sc_channels[i].chan_sensor); 265 1.1 martin 266 1.1 martin error = sysmon_envsys_register(sc->sc_sme); 267 1.1 martin if (error) { 268 1.1 martin aprint_error_dev(self, "error %d registering with sysmon\n", 269 1.1 martin error); 270 1.1 martin sysmon_envsys_destroy(sc->sc_sme); 271 1.13 jdc sc->sc_sme = NULL; 272 1.1 martin return; 273 1.1 martin } 274 1.10 jdc 275 1.10 jdc if (sc->sc_hastimer) { 276 1.10 jdc callout_init(&sc->sc_timer, CALLOUT_MPSAFE); 277 1.10 jdc callout_reset(&sc->sc_timer, hz*20, ecadc_timeout, sc); 278 1.10 jdc } 279 1.1 martin 280 1.1 martin aprint_naive(": Temp Sensors\n"); 281 1.10 jdc aprint_normal(": %s Temp Sensors (%d channels)\n", ia->ia_name, 282 1.10 jdc sc->sc_nchan); 283 1.10 jdc } 284 1.10 jdc 285 1.10 jdc static int 286 1.10 jdc ecadc_detach(device_t self, int flags) 287 1.10 jdc { 288 1.10 jdc struct ecadc_softc *sc = device_private(self); 289 1.15 jdc int c, i; 290 1.15 jdc 291 1.10 jdc if (sc->sc_hastimer) { 292 1.10 jdc callout_halt(&sc->sc_timer, NULL); 293 1.10 jdc callout_destroy(&sc->sc_timer); 294 1.10 jdc } 295 1.10 jdc 296 1.10 jdc if (sc->sc_sme != NULL) 297 1.11 jdc sysmon_envsys_unregister(sc->sc_sme); 298 1.10 jdc 299 1.15 jdc for (i = 0; i < sc->sc_nchan; i++) { 300 1.15 jdc struct ecadc_channel *chp = &sc->sc_channels[i]; 301 1.15 jdc 302 1.16 jdc if (chp->chan_type == PCF8591_SYS_FAN_CTRL) { 303 1.15 jdc /* Loop in case the bus is busy */ 304 1.15 jdc for (c = 0; c < 5; c++) { 305 1.15 jdc chp->chan_speed = sc->sc_cpu_fan_spd[0]; 306 1.15 jdc if (!ecadc_set_fan_speed(sc, chp->chan_num, 307 1.15 jdc chp->chan_speed)) 308 1.15 jdc return 0; 309 1.15 jdc delay(10000); 310 1.15 jdc } 311 1.16 jdc printf("%s: cannot set fan speed (chan %d)\n", 312 1.16 jdc device_xname(sc->sc_dev), chp->chan_num); 313 1.15 jdc } 314 1.15 jdc } 315 1.15 jdc 316 1.10 jdc return 0; 317 1.1 martin } 318 1.1 martin 319 1.1 martin static void 320 1.1 martin ecadc_refresh(struct sysmon_envsys *sme, envsys_data_t *sensor) 321 1.1 martin { 322 1.1 martin struct ecadc_softc *sc = sme->sme_cookie; 323 1.1 martin u_int i; 324 1.1 martin u_int8_t data[PCF8591_CHANNELS + 1]; 325 1.1 martin u_int8_t ctrl = PCF8591_CTRL_CH0 | PCF8591_CTRL_AUTOINC | 326 1.1 martin PCF8591_CTRL_OSCILLATOR; 327 1.1 martin 328 1.13 jdc if (iic_acquire_bus(sc->sc_tag, 0)) 329 1.13 jdc return; 330 1.1 martin if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 331 1.1 martin &ctrl, 1, NULL, 0, 0)) { 332 1.1 martin iic_release_bus(sc->sc_tag, 0); 333 1.1 martin return; 334 1.1 martin } 335 1.10 jdc /* 336 1.10 jdc * Each data byte that we read is the result of the previous request, 337 1.10 jdc * so read num_channels + 1 and update envsys values from chan + 1. 338 1.10 jdc */ 339 1.1 martin if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 340 1.1 martin NULL, 0, data, PCF8591_CHANNELS + 1, 0)) { 341 1.1 martin iic_release_bus(sc->sc_tag, 0); 342 1.1 martin return; 343 1.1 martin } 344 1.1 martin iic_release_bus(sc->sc_tag, 0); 345 1.1 martin 346 1.10 jdc /* Temperature with/without translation or relative (ADC value) */ 347 1.1 martin for (i = 0; i < sc->sc_nchan; i++) { 348 1.1 martin struct ecadc_channel *chp = &sc->sc_channels[i]; 349 1.1 martin 350 1.10 jdc if (chp->chan_type == PCF8591_TEMP_SENS) { 351 1.10 jdc 352 1.10 jdc /* Encode the raw value to use for the fan control */ 353 1.10 jdc if (chp->chan_xlate) { 354 1.10 jdc int32_t temp; 355 1.10 jdc 356 1.10 jdc temp = 273150000 + 1000000 * 357 1.10 jdc chp->chan_xlate[data[1 + chp->chan_num]]; 358 1.10 jdc temp &= ~0xff; 359 1.10 jdc temp += data[1 + chp->chan_num]; 360 1.10 jdc chp->chan_sensor.value_cur = temp; 361 1.14 jdc DPRINTF("%s: xlate %s sensor = %d" 362 1.14 jdc " (0x%x > 0x%x)\n", 363 1.14 jdc device_xname(sc->sc_dev), 364 1.14 jdc chp->chan_sensor.desc, temp, 365 1.14 jdc data[1 + chp->chan_num], 366 1.14 jdc chp->chan_xlate[data[1 + chp->chan_num]]); 367 1.14 jdc } else { 368 1.10 jdc chp->chan_sensor.value_cur = 273150000 + 369 1.10 jdc chp->chan_factor * data[1 + chp->chan_num]; 370 1.14 jdc DPRINTF("%s: read %s sensor = %d (0x%x)\n", 371 1.14 jdc device_xname(sc->sc_dev), 372 1.14 jdc chp->chan_sensor.desc, 373 1.14 jdc chp->chan_sensor.value_cur, 374 1.14 jdc data[1 + chp->chan_num]); 375 1.14 jdc } 376 1.10 jdc chp->chan_sensor.flags |= ENVSYS_FMONLIMITS; 377 1.10 jdc } 378 1.16 jdc if (chp->chan_type == PCF8591_SYS_FAN_CTRL) 379 1.10 jdc chp->chan_sensor.value_cur = data[1 + chp->chan_num]; 380 1.1 martin 381 1.1 martin chp->chan_sensor.state = ENVSYS_SVALID; 382 1.1 martin } 383 1.1 martin } 384 1.1 martin 385 1.1 martin static void 386 1.1 martin ecadc_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 387 1.10 jdc sysmon_envsys_lim_t *limits, u_int32_t *props) 388 1.1 martin { 389 1.1 martin struct ecadc_softc *sc = sme->sme_cookie; 390 1.1 martin int i; 391 1.1 martin 392 1.1 martin for (i = 0; i < sc->sc_nchan; i++) { 393 1.1 martin if (edata != &sc->sc_channels[i].chan_sensor) 394 1.1 martin continue; 395 1.10 jdc if (sc->sc_channels[i].chan_type == PCF8591_TEMP_SENS) { 396 1.10 jdc *props |= PROP_WARNMIN|PROP_WARNMAX|PROP_CRITMAX; 397 1.10 jdc limits->sel_warnmin = sc->sc_channels[i].chan_min; 398 1.10 jdc limits->sel_warnmax = sc->sc_channels[i].chan_warn; 399 1.10 jdc limits->sel_critmax = sc->sc_channels[i].chan_crit; 400 1.10 jdc } 401 1.1 martin return; 402 1.1 martin } 403 1.1 martin } 404 1.10 jdc 405 1.10 jdc static void 406 1.10 jdc ecadc_timeout(void *v) 407 1.10 jdc { 408 1.10 jdc struct ecadc_softc *sc = v; 409 1.10 jdc 410 1.10 jdc sysmon_task_queue_sched(0, ecadc_fan_adjust, sc); 411 1.10 jdc callout_reset(&sc->sc_timer, hz*60, ecadc_timeout, sc); 412 1.10 jdc } 413 1.10 jdc 414 1.10 jdc static bool 415 1.10 jdc is_cpu_temp(const envsys_data_t *edata) 416 1.10 jdc { 417 1.10 jdc if (edata->units != ENVSYS_STEMP) 418 1.10 jdc return false; 419 1.10 jdc return strncmp(edata->desc, "CPU", 3) == 0; 420 1.10 jdc } 421 1.10 jdc 422 1.16 jdc static bool 423 1.16 jdc is_high_temp(const envsys_data_t *edata) 424 1.16 jdc { 425 1.16 jdc if (edata->units != ENVSYS_INDICATOR) 426 1.16 jdc return false; 427 1.16 jdc return strcmp(edata->desc, "high_temp") == 0; 428 1.16 jdc } 429 1.16 jdc 430 1.16 jdc static bool 431 1.16 jdc is_fan_fail(const envsys_data_t *edata) 432 1.16 jdc { 433 1.16 jdc if (edata->units != ENVSYS_INDICATOR) 434 1.16 jdc return false; 435 1.16 jdc return strcmp(edata->desc, "fan_fail") == 0; 436 1.16 jdc } 437 1.16 jdc 438 1.10 jdc static int 439 1.10 jdc ecadc_set_fan_speed(struct ecadc_softc *sc, u_int8_t chan, u_int8_t val) 440 1.10 jdc { 441 1.10 jdc u_int8_t ctrl = PCF8591_CTRL_AUTOINC | PCF8591_CTRL_OSCILLATOR; 442 1.10 jdc int ret; 443 1.10 jdc 444 1.10 jdc ctrl |= chan; 445 1.13 jdc ret = iic_acquire_bus(sc->sc_tag, 0); 446 1.13 jdc if (ret) { 447 1.16 jdc printf("%s: error acquiring i2c bus (ch %d)\n", 448 1.16 jdc device_xname(sc->sc_dev), chan); 449 1.13 jdc return ret; 450 1.13 jdc } 451 1.10 jdc ret = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 452 1.10 jdc &ctrl, 1, &val, 1, 0); 453 1.10 jdc if (ret) 454 1.16 jdc printf("%s: error changing fan speed (ch %d)\n", 455 1.16 jdc device_xname(sc->sc_dev), chan); 456 1.10 jdc else 457 1.14 jdc DPRINTF("%s changed fan speed (ch %d) to 0x%x\n", 458 1.14 jdc device_xname(sc->sc_dev), chan, val); 459 1.10 jdc iic_release_bus(sc->sc_tag, 0); 460 1.10 jdc return ret; 461 1.10 jdc } 462 1.10 jdc 463 1.10 jdc static void 464 1.10 jdc ecadc_fan_adjust(void *v) 465 1.10 jdc { 466 1.10 jdc struct ecadc_softc *sc = v; 467 1.16 jdc struct ecadc_channel *chp; 468 1.10 jdc int i; 469 1.10 jdc u_int8_t temp, speed; 470 1.16 jdc u_int32_t htemp, ffail; 471 1.10 jdc 472 1.10 jdc for (i = 0; i < sc->sc_nchan; i++) { 473 1.16 jdc chp = &sc->sc_channels[i]; 474 1.16 jdc if (chp->chan_type != PCF8591_SYS_FAN_CTRL) 475 1.16 jdc continue; 476 1.10 jdc 477 1.16 jdc /* Check for high temperature or fan failure */ 478 1.16 jdc htemp = sysmon_envsys_get_max_value(is_high_temp, true); 479 1.16 jdc ffail = sysmon_envsys_get_max_value(is_fan_fail, true); 480 1.16 jdc if (htemp) { 481 1.16 jdc printf("%s: High temperature detected\n", 482 1.16 jdc device_xname(sc->sc_dev)); 483 1.16 jdc /* Set fans to maximum speed */ 484 1.16 jdc speed = sc->sc_cpu_fan_spd[0]; 485 1.16 jdc } else if (ffail) { 486 1.16 jdc printf("%s: Fan failure detected\n", 487 1.16 jdc device_xname(sc->sc_dev)); 488 1.16 jdc /* Set fans to maximum speed */ 489 1.16 jdc speed = sc->sc_cpu_fan_spd[0]; 490 1.16 jdc } else { 491 1.10 jdc /* Extract the raw value from the max CPU temp */ 492 1.10 jdc temp = sysmon_envsys_get_max_value(is_cpu_temp, true) 493 1.16 jdc & 0xff; 494 1.10 jdc if (!temp) { 495 1.16 jdc printf("%s: skipping temp adjustment" 496 1.16 jdc " - no sensor values\n", 497 1.16 jdc device_xname(sc->sc_dev)); 498 1.10 jdc return; 499 1.10 jdc } 500 1.10 jdc if (temp > XLATE_MAX) 501 1.10 jdc temp = XLATE_MAX; 502 1.10 jdc speed = chp->chan_xlate[temp]; 503 1.16 jdc } 504 1.16 jdc if (speed != chp->chan_speed) { 505 1.16 jdc if (!ecadc_set_fan_speed(sc, chp->chan_num, 506 1.16 jdc speed)) 507 1.16 jdc chp->chan_speed = speed; 508 1.10 jdc } 509 1.10 jdc } 510 1.10 jdc } 511