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