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