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