1/* $NetBSD: tda.c,v 1.14 2020/10/31 13:17:34 jdc Exp $ */ 2/* $OpenBSD: tda.c,v 1.4 2008/02/27 17:25:00 robert Exp $ */ 3 4/* 5 * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> 6 * Copyright (c) 2008 Mark Kettenis <kettenis@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: tda.c,v 1.14 2020/10/31 13:17:34 jdc Exp $"); 23 24#include <sys/param.h> 25#include <sys/systm.h> 26#include <sys/kernel.h> 27#include <sys/device.h> 28#include <dev/sysmon/sysmonvar.h> 29#include <dev/sysmon/sysmon_taskq.h> 30 31#include <machine/autoconf.h> 32#include <machine/openfirm.h> 33 34#include <dev/i2c/i2cvar.h> 35 36/* fan control registers */ 37#define TDA_SYSFAN_REG 0xf0 38#define TDA_CPUFAN_REG 0xf2 39#define TDA_PSFAN_REG 0xf4 40 41#define TDA_FANSPEED_MIN 0x0c 42#define TDA_FANSPEED_MAX 0x3f 43 44#define TDA_PSFAN_ON 0x1f 45#define TDA_PSFAN_OFF 0x00 46 47/* Internal and External temperature sensor numbers */ 48#define SENSOR_TEMP_EXT 0 49#define SENSOR_TEMP_INT 1 50 51/* Fan sensor numbers */ 52#define SENSOR_FAN_CPU 0 53#define SENSOR_FAN_SYS 1 54 55#define DEGC_TO_mK(c) (((c) * 1000000) + 273150000) 56 57#define CPU_TEMP_MAX DEGC_TO_mK(67) 58#define CPU_TEMP_MIN DEGC_TO_mK(57) 59#define SYS_TEMP_MAX DEGC_TO_mK(30) 60#define SYS_TEMP_MIN DEGC_TO_mK(20) 61 62struct tda_softc { 63 device_t sc_dev; 64 i2c_tag_t sc_tag; 65 i2c_addr_t sc_addr; 66 67 u_int16_t sc_cfan_speed; /* current CPU fan speed */ 68 u_int16_t sc_sfan_speed; /* current SYS fan speed */ 69 70 struct sysmon_envsys *sc_sme; 71 envsys_data_t sc_sensor[2]; 72 73 callout_t sc_timer; 74}; 75 76int tda_match(device_t, cfdata_t, void *); 77void tda_attach(device_t, device_t, void *); 78static int tda_detach(device_t, int); 79void tda_refresh(struct sysmon_envsys *, envsys_data_t *); 80 81void tda_setspeed(struct tda_softc *); 82static void tda_adjust(void *); 83static void tda_timeout(void *); 84 85 86CFATTACH_DECL3_NEW(tda, sizeof(struct tda_softc), 87 tda_match, tda_attach, tda_detach, NULL, NULL, NULL, 88 DVF_DETACH_SHUTDOWN); 89 90int 91tda_match(device_t parent, cfdata_t match, void *aux) 92{ 93 struct i2c_attach_args *ia = aux; 94 95 /* Only attach on the Sun Blade 1000/2000. */ 96 if (strcmp(machine_model, "SUNW,Sun-Blade-1000") != 0) 97 return (0); 98 99 /* 100 * No need for "compatible" matching, we know exactly what 101 * firmware calls us. 102 */ 103 if (ia->ia_name == NULL) 104 return(0); 105 106 return strcmp(ia->ia_name, "fan-control") == 0 ? 107 I2C_MATCH_DIRECT_SPECIFIC : 0; 108} 109 110void 111tda_attach(device_t parent, device_t self, void *aux) 112{ 113 struct tda_softc *sc = device_private(self); 114 struct i2c_attach_args *ia = aux; 115 int rc; 116 117 sc->sc_dev = self; 118 sc->sc_tag = ia->ia_tag; 119 sc->sc_addr = ia->ia_addr; 120 121 aprint_normal(": %s\n", ia->ia_name); 122 aprint_naive(": Environment sensor\n"); 123 124 /* 125 * Set the fans to maximum speed and save the power levels; 126 * the controller is write-only. 127 */ 128 sc->sc_cfan_speed = sc->sc_sfan_speed = (TDA_FANSPEED_MAX+TDA_FANSPEED_MIN)/2; 129 tda_setspeed(sc); 130 131 callout_init(&sc->sc_timer, CALLOUT_MPSAFE); 132 callout_reset(&sc->sc_timer, hz*20, tda_timeout, sc); 133 134 /* Initialise sensor data */ 135 sc->sc_sensor[SENSOR_FAN_CPU].state = ENVSYS_SINVALID; 136 sc->sc_sensor[SENSOR_FAN_CPU].units = ENVSYS_INTEGER; 137 sc->sc_sensor[SENSOR_FAN_CPU].flags = ENVSYS_FMONNOTSUPP; 138 strlcpy(sc->sc_sensor[SENSOR_FAN_CPU].desc, 139 "fan.cpu",sizeof("fan.cpu")); 140 sc->sc_sensor[SENSOR_FAN_SYS].state = ENVSYS_SINVALID; 141 sc->sc_sensor[SENSOR_FAN_SYS].units = ENVSYS_INTEGER; 142 sc->sc_sensor[SENSOR_FAN_SYS].flags = ENVSYS_FMONNOTSUPP; 143 strlcpy(sc->sc_sensor[SENSOR_FAN_SYS].desc, 144 "fan.sys",sizeof("fan.sys")); 145 sc->sc_sme = sysmon_envsys_create(); 146 rc = sysmon_envsys_sensor_attach( 147 sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_CPU]); 148 if (rc) { 149 sysmon_envsys_destroy(sc->sc_sme); 150 aprint_error_dev(self, 151 "unable to attach cpu fan at sysmon, error %d\n", rc); 152 return; 153 } 154 rc = sysmon_envsys_sensor_attach( 155 sc->sc_sme, &sc->sc_sensor[SENSOR_FAN_SYS]); 156 if (rc) { 157 sysmon_envsys_destroy(sc->sc_sme); 158 aprint_error_dev(self, 159 "unable to attach sys fan at sysmon, error %d\n", rc); 160 return; 161 } 162 sc->sc_sme->sme_name = device_xname(self); 163 sc->sc_sme->sme_cookie = sc; 164 sc->sc_sme->sme_refresh = tda_refresh; 165 rc = sysmon_envsys_register(sc->sc_sme); 166 if (rc) { 167 aprint_error_dev(self, 168 "unable to register with sysmon, error %d\n", rc); 169 sysmon_envsys_destroy(sc->sc_sme); 170 return; 171 } 172} 173 174int 175tda_detach(device_t self, int flags) 176{ 177 struct tda_softc *sc = device_private(self); 178 179 if (sc->sc_sme != NULL) 180 sysmon_envsys_unregister(sc->sc_sme); 181 182 callout_halt(&sc->sc_timer, NULL); 183 callout_destroy(&sc->sc_timer); 184 185 sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX; 186 tda_setspeed(sc); 187 return 0; 188} 189 190static void 191tda_timeout(void *v) 192{ 193 struct tda_softc *sc = v; 194 195 sysmon_task_queue_sched(0, tda_adjust, sc); 196 callout_reset(&sc->sc_timer, hz*60, tda_timeout, sc); 197} 198 199void 200tda_setspeed(struct tda_softc *sc) 201{ 202 u_int8_t cmd[2]; 203 204 if (sc->sc_cfan_speed < TDA_FANSPEED_MIN) 205 sc->sc_cfan_speed = TDA_FANSPEED_MIN; 206 if (sc->sc_sfan_speed < TDA_FANSPEED_MIN) 207 sc->sc_sfan_speed = TDA_FANSPEED_MIN; 208 if (sc->sc_cfan_speed > TDA_FANSPEED_MAX) 209 sc->sc_cfan_speed = TDA_FANSPEED_MAX; 210 if (sc->sc_sfan_speed > TDA_FANSPEED_MAX) 211 sc->sc_sfan_speed = TDA_FANSPEED_MAX; 212 213 iic_acquire_bus(sc->sc_tag, 0); 214 215 cmd[0] = TDA_CPUFAN_REG; 216 cmd[1] = sc->sc_cfan_speed; 217 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 218 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 219 aprint_error_dev(sc->sc_dev, "cannot write cpu-fan register\n"); 220 iic_release_bus(sc->sc_tag, 0); 221 return; 222 } 223 224 cmd[0] = TDA_SYSFAN_REG; 225 cmd[1] = sc->sc_sfan_speed; 226 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 227 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 228 aprint_error_dev(sc->sc_dev, "cannot write system-fan register\n"); 229 iic_release_bus(sc->sc_tag, 0); 230 return; 231 } 232 233 iic_release_bus(sc->sc_tag, 0); 234 235 aprint_debug_dev(sc->sc_dev, "changed fan speed to cpu=%d system=%d\n", 236 sc->sc_cfan_speed, sc->sc_sfan_speed); 237} 238 239static bool 240is_cpu_sensor(const envsys_data_t *edata) 241{ 242 if (edata->units != ENVSYS_STEMP) 243 return false; 244 return strcmp(edata->desc, "external") == 0; 245} 246 247static bool 248is_system_sensor(const envsys_data_t *edata) 249{ 250 if (edata->units != ENVSYS_STEMP) 251 return false; 252 return strcmp(edata->desc, "internal") == 0; 253} 254 255static void 256tda_adjust(void *v) 257{ 258 struct tda_softc *sc = v; 259 u_int64_t ctemp, stemp; 260 u_int16_t cspeed, sspeed; 261 262 /* Default to running the fans at maximum speed. */ 263 sspeed = cspeed = TDA_FANSPEED_MAX; 264 265 /* fetch maximum current temperature */ 266 ctemp = sysmon_envsys_get_max_value(is_cpu_sensor, true); 267 stemp = sysmon_envsys_get_max_value(is_system_sensor, true); 268 269 /* the predicates for selecting sensors must have gone wrong */ 270 if (ctemp == 0 || stemp == 0) { 271 aprint_error_dev(sc->sc_dev, "skipping temp adjustment" 272 " - no sensor values\n"); 273 return; 274 } 275 276 aprint_debug_dev(sc->sc_dev, "current temperature: cpu %" PRIu64 277 " system %" PRIu64 "\n", 278 ctemp, stemp); 279 280 if (ctemp < CPU_TEMP_MIN) 281 cspeed = TDA_FANSPEED_MIN; 282 else if (ctemp < CPU_TEMP_MAX) 283 cspeed = TDA_FANSPEED_MIN + 284 (ctemp - CPU_TEMP_MIN) * 285 (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 286 (CPU_TEMP_MAX - CPU_TEMP_MIN); 287 288 if (stemp < SYS_TEMP_MIN) 289 sspeed = TDA_FANSPEED_MIN; 290 else if (stemp < SYS_TEMP_MAX) 291 sspeed = TDA_FANSPEED_MIN + 292 (stemp - SYS_TEMP_MIN) * 293 (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 294 (SYS_TEMP_MAX - SYS_TEMP_MIN); 295 296 if (sspeed == sc->sc_sfan_speed && cspeed == sc->sc_cfan_speed) 297 return; 298 299 sc->sc_sfan_speed = sspeed; 300 sc->sc_cfan_speed = cspeed; 301 tda_setspeed(sc); 302} 303 304void 305tda_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 306{ 307 struct tda_softc *sc = sme->sme_cookie; 308 u_int16_t speed; 309 310 if (edata->sensor == SENSOR_FAN_CPU) 311 speed = sc->sc_cfan_speed; 312 else 313 speed = sc->sc_sfan_speed; 314 if (!speed) 315 edata->state = ENVSYS_SINVALID; 316 else { 317 edata->value_cur = speed; 318 edata->state = ENVSYS_SVALID; 319 } 320} 321 322