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