1 /* $OpenBSD: tda.c,v 1.4 2008/02/27 17:25:00 robert Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Robert Nagy <robert@openbsd.org> 5 * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/systm.h> 22 #include <sys/kernel.h> 23 #include <sys/device.h> 24 #include <sys/sensors.h> 25 26 #include <machine/autoconf.h> 27 #include <machine/openfirm.h> 28 29 #include <dev/i2c/i2cvar.h> 30 31 /* fan control registers */ 32 #define TDA_SYSFAN_REG 0xf0 33 #define TDA_CPUFAN_REG 0xf2 34 #define TDA_PSFAN_REG 0xf4 35 36 #define TDA_FANSPEED_MIN 0x0c 37 #define TDA_FANSPEED_MAX 0x3f 38 39 #define TDA_PSFAN_ON 0x1f 40 #define TDA_PSFAN_OFF 0x00 41 42 /* Internal and External temperature senor numbers */ 43 #define SENSOR_TEMP_EXT 0 44 #define SENSOR_TEMP_INT 1 45 46 #define CPU_TEMP_MAX (67 * 1000000 + 273150000) 47 #define CPU_TEMP_MIN (57 * 1000000 + 273150000) 48 #define SYS_TEMP_MAX (30 * 1000000 + 273150000) 49 #define SYS_TEMP_MIN (20 * 1000000 + 273150000) 50 51 struct tda_softc { 52 struct device sc_dev; 53 i2c_tag_t sc_tag; 54 i2c_addr_t sc_addr; 55 56 u_int16_t sc_cfan_speed; /* current CPU fan speed */ 57 u_int16_t sc_sfan_speed; /* current SYS fan speed */ 58 59 int sc_nsensors; 60 }; 61 #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 62 63 int tda_match(struct device *, void *, void *); 64 void tda_attach(struct device *, struct device *, void *); 65 66 void tda_setspeed(struct tda_softc *); 67 void tda_adjust(void *); 68 69 struct cfattach tda_ca = { 70 sizeof(struct tda_softc), tda_match, tda_attach 71 }; 72 73 struct cfdriver tda_cd = { 74 NULL, "tda", DV_DULL 75 }; 76 77 void *tda_cookie; 78 79 int 80 tda_match(struct device *parent, void *match, void *aux) 81 { 82 struct i2c_attach_args *ia = aux; 83 char name[32]; 84 85 if (strcmp(ia->ia_name, "tda8444") != 0) 86 return (0); 87 88 /* Only attach on the Sun Blade 1000/2000. */ 89 if (OF_getprop(findroot(), "name", name, sizeof(name)) <= 0) 90 return (0); 91 if (strcmp(name, "SUNW,Sun-Blade-1000") != 0) 92 return (0); 93 94 return (1); 95 } 96 97 void 98 tda_attach(struct device *parent, struct device *self, void *aux) 99 { 100 struct tda_softc *sc = (struct tda_softc *)self; 101 struct i2c_attach_args *ia = aux; 102 int i; 103 104 sc->sc_tag = ia->ia_tag; 105 sc->sc_addr = ia->ia_addr; 106 107 printf("\n"); 108 109 /* 110 * Set the fans to maximum speed and save the power levels; 111 * the controller is write-only. 112 */ 113 sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX; 114 tda_setspeed(sc); 115 116 /* Get the number of sensor devices. */ 117 for (i = 0; i < MAXSENSORDEVICES; i++) { 118 if (sensordev_get(i) == NULL) 119 break; 120 } 121 sc->sc_nsensors = i; 122 123 if (!sc->sc_nsensors) { 124 printf("%s: no temperature sensors found\n", DEVNAME(sc)); 125 return; 126 } 127 128 if (sensor_task_register(sc, tda_adjust, 10) == NULL) { 129 printf("%s: unable to register update task\n", DEVNAME(sc)); 130 return; 131 } 132 133 tda_cookie = sc; 134 } 135 136 void 137 tda_setspeed(struct tda_softc *sc) 138 { 139 u_int8_t cmd[2]; 140 141 if (sc->sc_cfan_speed < TDA_FANSPEED_MIN) 142 sc->sc_cfan_speed = TDA_FANSPEED_MIN; 143 if (sc->sc_sfan_speed < TDA_FANSPEED_MIN) 144 sc->sc_sfan_speed = TDA_FANSPEED_MIN; 145 if (sc->sc_cfan_speed > TDA_FANSPEED_MAX) 146 sc->sc_cfan_speed = TDA_FANSPEED_MAX; 147 if (sc->sc_sfan_speed > TDA_FANSPEED_MAX) 148 sc->sc_sfan_speed = TDA_FANSPEED_MAX; 149 150 iic_acquire_bus(sc->sc_tag, 0); 151 152 cmd[0] = TDA_CPUFAN_REG; 153 cmd[1] = sc->sc_cfan_speed; 154 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 155 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 156 printf("%s: cannot write cpu-fan register\n", 157 DEVNAME(sc)); 158 iic_release_bus(sc->sc_tag, 0); 159 return; 160 } 161 162 cmd[0] = TDA_SYSFAN_REG; 163 cmd[1] = sc->sc_sfan_speed; 164 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 165 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 166 printf("%s: cannot write system-fan register\n", 167 DEVNAME(sc)); 168 iic_release_bus(sc->sc_tag, 0); 169 return; 170 } 171 172 iic_release_bus(sc->sc_tag, 0); 173 174 #if 0 175 printf("%s: changed fan speed to cpu=%d system=%d\n", 176 DEVNAME(sc), sc->sc_cfan_speed, sc->sc_sfan_speed); 177 #endif 178 179 } 180 181 void 182 tda_adjust(void *v) 183 { 184 struct tda_softc *sc = v; 185 struct ksensor *ks; 186 u_int64_t ctemp, stemp; 187 int i; 188 189 /* Default to running the fans at maximum speed. */ 190 sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX; 191 192 ctemp = stemp = 0; 193 for (i = 0; i < sc->sc_nsensors; i++) { 194 ks = sensor_find(i, SENSOR_TEMP, SENSOR_TEMP_EXT); 195 if (ks == NULL) { 196 printf("%s: failed to get external sensor\n", 197 DEVNAME(sc)); 198 goto out; 199 } 200 ctemp = MAX(ctemp, ks->value); 201 202 ks = sensor_find(i, SENSOR_TEMP, SENSOR_TEMP_INT); 203 if (ks == NULL) { 204 printf("%s: failed to get internal sensors\n", 205 DEVNAME(sc)); 206 goto out; 207 } 208 stemp = MAX(stemp, ks->value); 209 } 210 211 if (ctemp < CPU_TEMP_MIN) 212 sc->sc_cfan_speed = TDA_FANSPEED_MIN; 213 else if (ctemp < CPU_TEMP_MAX) 214 sc->sc_cfan_speed = TDA_FANSPEED_MIN + 215 (ctemp - CPU_TEMP_MIN) * 216 (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 217 (CPU_TEMP_MAX - CPU_TEMP_MIN); 218 219 if (stemp < SYS_TEMP_MIN) 220 sc->sc_sfan_speed = TDA_FANSPEED_MIN; 221 else if (stemp < SYS_TEMP_MAX) 222 sc->sc_sfan_speed = TDA_FANSPEED_MIN + 223 (stemp - SYS_TEMP_MIN) * 224 (TDA_FANSPEED_MAX - TDA_FANSPEED_MIN) / 225 (SYS_TEMP_MAX - SYS_TEMP_MIN); 226 227 out: 228 tda_setspeed(sc); 229 } 230 231 /* This code gets called when we are about to drop to ddb, 232 * in order to operate the fans at full speed during the 233 * timeouts are not working. 234 */ 235 void 236 tda_full_blast() 237 { 238 struct tda_softc *sc = tda_cookie; 239 u_int8_t cmd[2]; 240 241 if (sc == NULL) 242 return; 243 244 sc->sc_cfan_speed = sc->sc_sfan_speed = TDA_FANSPEED_MAX; 245 246 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 247 248 cmd[0] = TDA_CPUFAN_REG; 249 cmd[1] = sc->sc_cfan_speed; 250 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 251 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 252 printf("%s: cannot write cpu-fan register\n", 253 DEVNAME(sc)); 254 iic_release_bus(sc->sc_tag, I2C_F_POLL); 255 return; 256 } 257 258 cmd[0] = TDA_SYSFAN_REG; 259 cmd[1] = sc->sc_sfan_speed; 260 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 261 sc->sc_addr, &cmd, sizeof(cmd), NULL, 0, 0)) { 262 printf("%s: cannot write system-fan register\n", 263 DEVNAME(sc)); 264 iic_release_bus(sc->sc_tag, I2C_F_POLL); 265 return; 266 } 267 268 iic_release_bus(sc->sc_tag, I2C_F_POLL); 269 } 270