smusat.c revision 1.4
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/malloc.h> 31#include <sys/device.h> 32#include <sys/proc.h> 33#include <sys/mutex.h> 34#include <sys/time.h> 35#include <sys/sysctl.h> 36 37#include <machine/autoconf.h> 38 39#include <dev/ofw/openfirm.h> 40#include <dev/i2c/i2cvar.h> 41#include <dev/sysmon/sysmonvar.h> 42#include <dev/sysmon/sysmon_taskq.h> 43 44#include <macppc/dev/smuiicvar.h> 45 46#include "opt_smusat.h" 47 48extern int smu_get_datablock(int, uint8_t *, size_t); 49 50enum { 51 SMUSAT_SENSOR_TEMP, 52 SMUSAT_SENSOR_CURRENT, 53 SMUSAT_SENSOR_VOLTAGE, 54 SMUSAT_SENSOR_POWER, 55}; 56 57struct smusat_softc; 58 59struct smusat_sensor { 60 struct smusat_softc *sc; 61 62 char location[32]; 63 int type; 64 int reg; 65 int zone; 66 int shift; 67 int offset; 68 int scale; 69 int current_value; 70}; 71 72#define SMUSAT_MAX_SENSORS 16 73#define SMUSAT_MAX_SME_SENSORS SMUSAT_MAX_SENSORS 74 75struct smusat_softc { 76 device_t sc_dev; 77 int sc_node; 78 i2c_addr_t sc_addr; 79 uint8_t sc_cache[16]; 80 time_t sc_last_update; 81 struct i2c_controller *sc_i2c; 82 struct sysctlnode *sc_sysctl_me; 83 84 int sc_num_sensors; 85 struct smusat_sensor sc_sensors[SMUSAT_MAX_SENSORS]; 86 87 struct sysmon_envsys *sc_sme; 88 envsys_data_t sc_sme_sensors[SMUSAT_MAX_SME_SENSORS]; 89}; 90 91#ifdef SMUSAT_DEBUG 92#define DPRINTF printf 93#else 94#define DPRINTF while (0) printf 95#endif 96 97static int smusat_match(device_t, struct cfdata *, void *); 98static void smusat_attach(device_t, device_t, void *); 99static void smusat_setup_sme(struct smusat_softc *); 100static void smusat_sme_refresh(struct sysmon_envsys *, envsys_data_t *); 101static int smusat_sensors_update(struct smusat_softc *); 102static int smusat_sensor_read(struct smusat_sensor *, int *); 103static int smusat_sysctl_sensor_value(SYSCTLFN_ARGS); 104 105CFATTACH_DECL_NEW(smusat, sizeof(struct smusat_softc), 106 smusat_match, smusat_attach, NULL, NULL); 107 108static const char * smusat_compats[] = { 109 "sat", 110 "smu-sat", 111 NULL 112}; 113 114static int 115smusat_match(device_t parent, struct cfdata *cf, void *aux) 116{ 117 struct i2c_attach_args *ia = aux; 118 int match_result; 119 120 if (iic_use_direct_match(ia, cf, smusat_compats, &match_result)) 121 return match_result; 122 123 if (ia->ia_addr == 0x58) 124 return I2C_MATCH_ADDRESS_ONLY; 125 126 return 0; 127} 128 129static void 130smusat_attach(device_t parent, device_t self, void *aux) 131{ 132 struct i2c_attach_args *ia = aux; 133 struct smusat_softc *sc = device_private(self); 134 struct smusat_sensor *sensor; 135 struct sysctlnode *sysctl_sensors, *sysctl_sensor, *sysctl_node; 136 char type[32], sysctl_sensor_name[32]; 137 int node, i, j; 138 139 sc->sc_dev = self; 140 sc->sc_node = ia->ia_cookie; 141 sc->sc_addr = ia->ia_addr; 142 sc->sc_i2c = ia->ia_tag; 143 144 sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me, 145 CTLFLAG_READWRITE, 146 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 147 NULL, 0, NULL, 0, 148 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 149 150 for (node = OF_child(sc->sc_node); 151 (node != 0) && (sc->sc_num_sensors < SMUSAT_MAX_SENSORS); 152 node = OF_peer(node)) { 153 sensor = &sc->sc_sensors[sc->sc_num_sensors]; 154 sensor->sc = sc; 155 156 memset(sensor->location, 0, sizeof(sensor->location)); 157 OF_getprop(node, "location", sensor->location, 158 sizeof(sensor->location)); 159 160 if (OF_getprop(node, "reg", &sensor->reg, 161 sizeof(sensor->reg)) <= 0) 162 continue; 163 164 if ((sensor->reg < 0x30) || (sensor->reg > 0x37)) 165 continue; 166 sensor->reg -= 0x30; 167 168 if (OF_getprop(node, "zone", &sensor->zone, 169 sizeof(sensor->zone)) <= 0) 170 continue; 171 172 memset(type, 0, sizeof(type)); 173 OF_getprop(node, "device_type", type, sizeof(type)); 174 175 if (strcmp(type, "temp-sensor") == 0) { 176 sensor->type = SMUSAT_SENSOR_TEMP; 177 sensor->shift = 10; 178 } else if (strcmp(type, "current-sensor") == 0) { 179 sensor->type = SMUSAT_SENSOR_CURRENT; 180 sensor->shift = 8; 181 } else if (strcmp(type, "voltage-sensor") == 0) { 182 sensor->type = SMUSAT_SENSOR_VOLTAGE; 183 sensor->shift = 4; 184 } else if (strcmp(type, "power-sensor") == 0) { 185 sensor->type = SMUSAT_SENSOR_POWER; 186 sensor->shift = 0; 187 } 188 189 DPRINTF("sensor: location %s reg %x zone %d type %s\n", 190 sensor->location, sensor->reg, sensor->zone, type); 191 192 sc->sc_num_sensors++; 193 } 194 195 /* Create sysctl nodes for each sensor */ 196 197 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensors, 198 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 199 CTLTYPE_NODE, "sensors", NULL, 200 NULL, 0, NULL, 0, 201 CTL_MACHDEP, 202 sc->sc_sysctl_me->sysctl_num, 203 CTL_CREATE, CTL_EOL); 204 205 for (i = 0; i < sc->sc_num_sensors; i++) { 206 sensor = &sc->sc_sensors[i]; 207 208 for (j = 0; j < strlen(sensor->location); j++) { 209 sysctl_sensor_name[j] = tolower(sensor->location[j]); 210 if (sysctl_sensor_name[j] == ' ') 211 sysctl_sensor_name[j] = '_'; 212 } 213 sysctl_sensor_name[j] = '\0'; 214 215 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_sensor, 216 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 217 CTLTYPE_NODE, sysctl_sensor_name, "sensor information", 218 NULL, 0, NULL, 0, 219 CTL_MACHDEP, 220 sc->sc_sysctl_me->sysctl_num, 221 sysctl_sensors->sysctl_num, 222 CTL_CREATE, CTL_EOL); 223 224 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node, 225 CTLFLAG_READONLY | CTLFLAG_OWNDESC, 226 CTLTYPE_INT, "zone", "sensor zone", 227 NULL, 0, &sensor->zone, 0, 228 CTL_MACHDEP, 229 sc->sc_sysctl_me->sysctl_num, 230 sysctl_sensors->sysctl_num, 231 sysctl_sensor->sysctl_num, 232 CTL_CREATE, CTL_EOL); 233 234 sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node, 235 CTLFLAG_READONLY | CTLFLAG_OWNDESC, 236 CTLTYPE_INT, "value", "sensor current value", 237 smusat_sysctl_sensor_value, 0, (void *) sensor, 0, 238 CTL_MACHDEP, 239 sc->sc_sysctl_me->sysctl_num, 240 sysctl_sensors->sysctl_num, 241 sysctl_sensor->sysctl_num, 242 CTL_CREATE, CTL_EOL); 243 } 244 245 smusat_setup_sme(sc); 246 247 printf("\n"); 248} 249 250static void 251smusat_setup_sme(struct smusat_softc *sc) 252{ 253 struct smusat_sensor *sensor; 254 envsys_data_t *sme_sensor; 255 int i; 256 257 sc->sc_sme = sysmon_envsys_create(); 258 259 for (i = 0; i < sc->sc_num_sensors; i++) { 260 sme_sensor = &sc->sc_sme_sensors[i]; 261 sensor = &sc->sc_sensors[i]; 262 263 switch (sensor->type) { 264 case SMUSAT_SENSOR_TEMP: 265 sme_sensor->units = ENVSYS_STEMP; 266 break; 267 case SMUSAT_SENSOR_CURRENT: 268 sme_sensor->units = ENVSYS_SAMPS; 269 break; 270 case SMUSAT_SENSOR_VOLTAGE: 271 sme_sensor->units = ENVSYS_SVOLTS_DC; 272 break; 273 case SMUSAT_SENSOR_POWER: 274 sme_sensor->units = ENVSYS_SWATTS; 275 break; 276 default: 277 sme_sensor->units = ENVSYS_INTEGER; 278 } 279 280 sme_sensor->state = ENVSYS_SINVALID; 281 snprintf(sme_sensor->desc, sizeof(sme_sensor->desc), 282 "%s", sensor->location); 283 284 if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) { 285 sysmon_envsys_destroy(sc->sc_sme); 286 return; 287 } 288 } 289 290 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 291 sc->sc_sme->sme_cookie = sc; 292 sc->sc_sme->sme_refresh = smusat_sme_refresh; 293 294 if (sysmon_envsys_register(sc->sc_sme)) { 295 aprint_error_dev(sc->sc_dev, 296 "unable to register with sysmon\n"); 297 sysmon_envsys_destroy(sc->sc_sme); 298 } 299} 300 301static void 302smusat_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 303{ 304 struct smusat_softc *sc = sme->sme_cookie; 305 struct smusat_sensor *sensor; 306 int which = edata->sensor; 307 int ret; 308 309 edata->state = ENVSYS_SINVALID; 310 311 if (which < sc->sc_num_sensors) { 312 sensor = &sc->sc_sensors[which]; 313 314 ret = smusat_sensor_read(sensor, NULL); 315 if (ret == 0) { 316 switch (sensor->type) { 317 case SMUSAT_SENSOR_TEMP: 318 edata->value_cur = sensor->current_value * 319 1000000 + 273150000; 320 break; 321 case SMUSAT_SENSOR_CURRENT: 322 edata->value_cur = sensor->current_value * 1000000; 323 break; 324 case SMUSAT_SENSOR_VOLTAGE: 325 edata->value_cur = sensor->current_value * 1000000; 326 break; 327 case SMUSAT_SENSOR_POWER: 328 edata->value_cur = sensor->current_value * 1000000; 329 break; 330 default: 331 edata->value_cur = sensor->current_value; 332 } 333 334 edata->state = ENVSYS_SVALID; 335 } 336 } 337} 338 339static int 340smusat_sensors_update(struct smusat_softc *sc) 341{ 342 u_char reg = 0x3f; 343 int ret; 344 345 iic_acquire_bus(sc->sc_i2c, 0); 346 ret = iic_exec(sc->sc_i2c, I2C_OP_READ, sc->sc_addr, ®, 1, sc->sc_cache, 16, 0); 347 iic_release_bus(sc->sc_i2c, 0); 348 349 if (ret != 0) 350 return (ret); 351 352 sc->sc_last_update = time_uptime; 353 354 return 0; 355} 356 357static int 358smusat_sensor_read(struct smusat_sensor *sensor, int *value) 359{ 360 struct smusat_softc *sc = sensor->sc; 361 int ret, reg; 362 363 if (time_uptime - sc->sc_last_update > 1) { 364 ret = smusat_sensors_update(sc); 365 if (ret != 0) 366 return ret; 367 } 368 369 reg = sensor->reg << 1; 370 sensor->current_value = (sc->sc_cache[reg] << 8) + sc->sc_cache[reg + 1]; 371 sensor->current_value <<= sensor->shift; 372 /* Discard the .16 */ 373 sensor->current_value >>= 16; 374 375 if (value != NULL) 376 *value = sensor->current_value; 377 378 return 0; 379} 380 381static int 382smusat_sysctl_sensor_value(SYSCTLFN_ARGS) 383{ 384 struct sysctlnode node = *rnode; 385 struct smusat_sensor *sensor = node.sysctl_data; 386 int value = 0; 387 int ret; 388 389 node.sysctl_data = &value; 390 391 ret = smusat_sensor_read(sensor, &value); 392 if (ret != 0) 393 return (ret); 394 395 return sysctl_lookup(SYSCTLFN_CALL(&node)); 396} 397 398SYSCTL_SETUP(smusat_sysctl_setup, "SMU-SAT sysctl subtree setup") 399{ 400 sysctl_createv(NULL, 0, NULL, NULL, 401 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, 402 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 403} 404