1 /*- 2 * Copyright (c) 2013 Phileas Fogg 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/kernel.h> 30 #include <sys/device.h> 31 #include <sys/proc.h> 32 #include <sys/mutex.h> 33 #include <sys/time.h> 34 #include <sys/sysctl.h> 35 36 #include <machine/autoconf.h> 37 38 #include <dev/ofw/openfirm.h> 39 #include <dev/i2c/i2cvar.h> 40 #include <dev/sysmon/sysmonvar.h> 41 #include <dev/sysmon/sysmon_taskq.h> 42 43 #include <macppc/dev/smuiicvar.h> 44 45 #include "opt_smusat.h" 46 47 extern int smu_get_datablock(int, uint8_t *, size_t); 48 49 enum { 50 SMUSAT_SENSOR_TEMP, 51 SMUSAT_SENSOR_CURRENT, 52 SMUSAT_SENSOR_VOLTAGE, 53 SMUSAT_SENSOR_POWER, 54 }; 55 56 struct smusat_softc; 57 58 struct smusat_sensor { 59 struct smusat_softc *sc; 60 61 char location[32]; 62 int type; 63 int reg; 64 int zone; 65 int shift; 66 int offset; 67 int scale; 68 int current_value; 69 }; 70 71 #define SMUSAT_MAX_SENSORS 16 72 #define SMUSAT_MAX_SME_SENSORS SMUSAT_MAX_SENSORS 73 74 struct smusat_softc { 75 device_t sc_dev; 76 int sc_node; 77 i2c_addr_t sc_addr; 78 uint8_t sc_cache[16]; 79 time_t sc_last_update; 80 struct i2c_controller *sc_i2c; 81 struct sysctlnode *sc_sysctl_me; 82 83 int sc_num_sensors; 84 struct smusat_sensor sc_sensors[SMUSAT_MAX_SENSORS]; 85 86 struct sysmon_envsys *sc_sme; 87 envsys_data_t sc_sme_sensors[SMUSAT_MAX_SME_SENSORS]; 88 }; 89 90 #ifdef SMUSAT_DEBUG 91 #define DPRINTF printf 92 #else 93 #define DPRINTF while (0) printf 94 #endif 95 96 static int smusat_match(device_t, struct cfdata *, void *); 97 static void smusat_attach(device_t, device_t, void *); 98 static void smusat_setup_sme(struct smusat_softc *); 99 static void smusat_sme_refresh(struct sysmon_envsys *, envsys_data_t *); 100 static int smusat_sensors_update(struct smusat_softc *); 101 static int smusat_sensor_read(struct smusat_sensor *, int *); 102 static int smusat_sysctl_sensor_value(SYSCTLFN_ARGS); 103 104 CFATTACH_DECL_NEW(smusat, sizeof(struct smusat_softc), 105 smusat_match, smusat_attach, NULL, NULL); 106 107 static const struct device_compatible_entry compat_data[] = { 108 { .compat = "sat" }, 109 { .compat = "smu-sat" }, 110 DEVICE_COMPAT_EOL 111 }; 112 113 static int 114 smusat_match(device_t parent, struct cfdata *cf, void *aux) 115 { 116 struct i2c_attach_args *ia = aux; 117 int match_result; 118 119 if (iic_use_direct_match(ia, cf, compat_data, &match_result)) 120 return match_result; 121 122 if (ia->ia_addr == 0x58) 123 return I2C_MATCH_ADDRESS_ONLY; 124 125 return 0; 126 } 127 128 static void 129 smusat_attach(device_t parent, device_t self, void *aux) 130 { 131 struct i2c_attach_args *ia = aux; 132 struct smusat_softc *sc = device_private(self); 133 struct smusat_sensor *sensor; 134 struct sysctlnode *sysctl_sensors, *sysctl_sensor, *sysctl_node; 135 char type[32], sysctl_sensor_name[32]; 136 int node, i, j; 137 138 sc->sc_dev = self; 139 sc->sc_node = ia->ia_cookie; 140 sc->sc_addr = ia->ia_addr; 141 sc->sc_i2c = ia->ia_tag; 142 143 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me, 144 CTLFLAG_READWRITE, 145 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 146 NULL, 0, NULL, 0, 147 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 148 149 for (node = OF_child(sc->sc_node); 150 (node != 0) && (sc->sc_num_sensors < SMUSAT_MAX_SENSORS); 151 node = OF_peer(node)) { 152 sensor = &sc->sc_sensors[sc->sc_num_sensors]; 153 sensor->sc = sc; 154 155 memset(sensor->location, 0, sizeof(sensor->location)); 156 OF_getprop(node, "location", sensor->location, 157 sizeof(sensor->location)); 158 159 if (OF_getprop(node, "reg", &sensor->reg, 160 sizeof(sensor->reg)) <= 0) 161 continue; 162 163 if ((sensor->reg < 0x30) || (sensor->reg > 0x37)) 164 continue; 165 sensor->reg -= 0x30; 166 167 if (OF_getprop(node, "zone", &sensor->zone, 168 sizeof(sensor->zone)) <= 0) 169 continue; 170 171 memset(type, 0, sizeof(type)); 172 OF_getprop(node, "device_type", type, sizeof(type)); 173 174 if (strcmp(type, "temp-sensor") == 0) { 175 sensor->type = SMUSAT_SENSOR_TEMP; 176 sensor->shift = 10; 177 } else if (strcmp(type, "current-sensor") == 0) { 178 sensor->type = SMUSAT_SENSOR_CURRENT; 179 sensor->shift = 8; 180 } else if (strcmp(type, "voltage-sensor") == 0) { 181 sensor->type = SMUSAT_SENSOR_VOLTAGE; 182 sensor->shift = 4; 183 } else if (strcmp(type, "power-sensor") == 0) { 184 sensor->type = SMUSAT_SENSOR_POWER; 185 sensor->shift = 0; 186 } 187 188 DPRINTF("sensor: location %s reg %x zone %d type %s\n", 189 sensor->location, sensor->reg, sensor->zone, type); 190 191 sc->sc_num_sensors++; 192 } 193 194 /* Create sysctl nodes for each sensor */ 195 196 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensors, 197 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 198 CTLTYPE_NODE, "sensors", NULL, 199 NULL, 0, NULL, 0, 200 CTL_MACHDEP, 201 sc->sc_sysctl_me->sysctl_num, 202 CTL_CREATE, CTL_EOL); 203 204 for (i = 0; i < sc->sc_num_sensors; i++) { 205 sensor = &sc->sc_sensors[i]; 206 207 for (j = 0; j < strlen(sensor->location); j++) { 208 sysctl_sensor_name[j] = tolower(sensor->location[j]); 209 if (sysctl_sensor_name[j] == ' ') 210 sysctl_sensor_name[j] = '_'; 211 } 212 sysctl_sensor_name[j] = '\0'; 213 214 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensor, 215 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 216 CTLTYPE_NODE, sysctl_sensor_name, "sensor information", 217 NULL, 0, NULL, 0, 218 CTL_MACHDEP, 219 sc->sc_sysctl_me->sysctl_num, 220 sysctl_sensors->sysctl_num, 221 CTL_CREATE, CTL_EOL); 222 223 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node, 224 CTLFLAG_READONLY | CTLFLAG_OWNDESC, 225 CTLTYPE_INT, "zone", "sensor zone", 226 NULL, 0, &sensor->zone, 0, 227 CTL_MACHDEP, 228 sc->sc_sysctl_me->sysctl_num, 229 sysctl_sensors->sysctl_num, 230 sysctl_sensor->sysctl_num, 231 CTL_CREATE, CTL_EOL); 232 233 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node, 234 CTLFLAG_READONLY | CTLFLAG_OWNDESC, 235 CTLTYPE_INT, "value", "sensor current value", 236 smusat_sysctl_sensor_value, 0, (void *) sensor, 0, 237 CTL_MACHDEP, 238 sc->sc_sysctl_me->sysctl_num, 239 sysctl_sensors->sysctl_num, 240 sysctl_sensor->sysctl_num, 241 CTL_CREATE, CTL_EOL); 242 } 243 244 smusat_setup_sme(sc); 245 246 printf("\n"); 247 } 248 249 static void 250 smusat_setup_sme(struct smusat_softc *sc) 251 { 252 struct smusat_sensor *sensor; 253 envsys_data_t *sme_sensor; 254 int i; 255 256 sc->sc_sme = sysmon_envsys_create(); 257 258 for (i = 0; i < sc->sc_num_sensors; i++) { 259 sme_sensor = &sc->sc_sme_sensors[i]; 260 sensor = &sc->sc_sensors[i]; 261 262 switch (sensor->type) { 263 case SMUSAT_SENSOR_TEMP: 264 sme_sensor->units = ENVSYS_STEMP; 265 break; 266 case SMUSAT_SENSOR_CURRENT: 267 sme_sensor->units = ENVSYS_SAMPS; 268 break; 269 case SMUSAT_SENSOR_VOLTAGE: 270 sme_sensor->units = ENVSYS_SVOLTS_DC; 271 break; 272 case SMUSAT_SENSOR_POWER: 273 sme_sensor->units = ENVSYS_SWATTS; 274 break; 275 default: 276 sme_sensor->units = ENVSYS_INTEGER; 277 } 278 279 sme_sensor->state = ENVSYS_SINVALID; 280 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc), 281 "%s", sensor->location); 282 283 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) { 284 sysmon_envsys_destroy(sc->sc_sme); 285 return; 286 } 287 } 288 289 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 290 sc->sc_sme->sme_cookie = sc; 291 sc->sc_sme->sme_refresh = smusat_sme_refresh; 292 293 if (sysmon_envsys_register(sc->sc_sme)) { 294 aprint_error_dev(sc->sc_dev, 295 "unable to register with sysmon\n"); 296 sysmon_envsys_destroy(sc->sc_sme); 297 } 298 } 299 300 static void 301 smusat_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 302 { 303 struct smusat_softc *sc = sme->sme_cookie; 304 struct smusat_sensor *sensor; 305 int which = edata->sensor; 306 int ret; 307 308 edata->state = ENVSYS_SINVALID; 309 310 if (which < sc->sc_num_sensors) { 311 sensor = &sc->sc_sensors[which]; 312 313 ret = smusat_sensor_read(sensor, NULL); 314 if (ret == 0) { 315 switch (sensor->type) { 316 case SMUSAT_SENSOR_TEMP: 317 edata->value_cur = sensor->current_value * 318 1000000 + 273150000; 319 break; 320 case SMUSAT_SENSOR_CURRENT: 321 edata->value_cur = sensor->current_value * 1000000; 322 break; 323 case SMUSAT_SENSOR_VOLTAGE: 324 edata->value_cur = sensor->current_value * 1000000; 325 break; 326 case SMUSAT_SENSOR_POWER: 327 edata->value_cur = sensor->current_value * 1000000; 328 break; 329 default: 330 edata->value_cur = sensor->current_value; 331 } 332 333 edata->state = ENVSYS_SVALID; 334 } 335 } 336 } 337 338 static int 339 smusat_sensors_update(struct smusat_softc *sc) 340 { 341 u_char reg = 0x3f; 342 int ret; 343 344 iic_acquire_bus(sc->sc_i2c, 0); 345 ret = iic_exec(sc->sc_i2c, I2C_OP_READ, sc->sc_addr, ®, 1, sc->sc_cache, 16, 0); 346 iic_release_bus(sc->sc_i2c, 0); 347 348 if (ret != 0) 349 return (ret); 350 351 sc->sc_last_update = time_uptime; 352 353 return 0; 354 } 355 356 static int 357 smusat_sensor_read(struct smusat_sensor *sensor, int *value) 358 { 359 struct smusat_softc *sc = sensor->sc; 360 int ret, reg; 361 362 if (time_uptime - sc->sc_last_update > 1) { 363 ret = smusat_sensors_update(sc); 364 if (ret != 0) 365 return ret; 366 } 367 368 reg = sensor->reg << 1; 369 sensor->current_value = (sc->sc_cache[reg] << 8) + sc->sc_cache[reg + 1]; 370 sensor->current_value <<= sensor->shift; 371 /* Discard the .16 */ 372 sensor->current_value >>= 16; 373 374 if (value != NULL) 375 *value = sensor->current_value; 376 377 return 0; 378 } 379 380 static int 381 smusat_sysctl_sensor_value(SYSCTLFN_ARGS) 382 { 383 struct sysctlnode node = *rnode; 384 struct smusat_sensor *sensor = node.sysctl_data; 385 int value = 0; 386 int ret; 387 388 node.sysctl_data = &value; 389 390 ret = smusat_sensor_read(sensor, &value); 391 if (ret != 0) 392 return (ret); 393 394 return sysctl_lookup(SYSCTLFN_CALL(&node)); 395 } 396 397 SYSCTL_SETUP(smusat_sysctl_setup, "SMU-SAT sysctl subtree setup") 398 { 399 sysctl_createv(NULL, 0, NULL, NULL, 400 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 401 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 402 } 403