smusat.c revision 208842
138889Sjdp/*-
2218822Sdim * Copyright (c) 2010 Nathan Whitehorn
385815Sobrien * All rights reserved.
438889Sjdp *
538889Sjdp * Redistribution and use in source and binary forms, with or without
685815Sobrien * modification, are permitted provided that the following conditions
785815Sobrien * are met:
885815Sobrien * 1. Redistributions of source code must retain the above copyright
985815Sobrien *    notice, this list of conditions and the following disclaimer.
1038889Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1185815Sobrien *    notice, this list of conditions and the following disclaimer in the
1285815Sobrien *    documentation and/or other materials provided with the distribution.
1385815Sobrien *
1485815Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1538889Sjdp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1685815Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1785815Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18218822Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19218822Sdim * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2038889Sjdp * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21104834Sobrien * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22104834Sobrien * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338889Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438889Sjdp * SUCH DAMAGE.
2538889Sjdp *
2638889Sjdp */
2785815Sobrien
2860484Sobrien#include <sys/cdefs.h>
2938889Sjdp__FBSDID("$FreeBSD: head/sys/powerpc/powermac/smusat.c 208842 2010-06-05 17:51:37Z nwhitehorn $");
3085815Sobrien
3185815Sobrien#include <sys/param.h>
3285815Sobrien#include <sys/systm.h>
3338889Sjdp#include <sys/module.h>
3485815Sobrien#include <sys/bus.h>
3585815Sobrien#include <sys/conf.h>
3685815Sobrien#include <sys/cpu.h>
3738889Sjdp#include <sys/ctype.h>
38218822Sdim#include <sys/kernel.h>
39218822Sdim#include <sys/sysctl.h>
40218822Sdim
41218822Sdim#include <dev/iicbus/iicbus.h>
42218822Sdim#include <dev/iicbus/iiconf.h>
43218822Sdim#include <dev/ofw/ofw_bus.h>
44218822Sdim#include <dev/ofw/openfirm.h>
45218822Sdim
46218822Sdimstruct smu_sensor {
47218822Sdim	cell_t	reg;
48218822Sdim	char	location[32];
49218822Sdim	enum {
50218822Sdim		SMU_CURRENT_SENSOR,
51218822Sdim		SMU_VOLTAGE_SENSOR,
52218822Sdim		SMU_POWER_SENSOR,
53218822Sdim		SMU_TEMP_SENSOR
54218822Sdim	} type;
55218822Sdim};
56218822Sdim
57218822Sdimstatic int	smusat_probe(device_t);
58218822Sdimstatic int	smusat_attach(device_t);
59218822Sdimstatic int	smusat_sensor_sysctl(SYSCTL_HANDLER_ARGS);
6089857Sobrien
6189857SobrienMALLOC_DEFINE(M_SMUSAT, "smusat", "SMU Sattelite Sensors");
6289857Sobrien
6389857Sobrienstatic device_method_t  smusat_methods[] = {
6489857Sobrien	/* Device interface */
6589857Sobrien	DEVMETHOD(device_probe,		smusat_probe),
6638889Sjdp	DEVMETHOD(device_attach,	smusat_attach),
6785815Sobrien	{ 0, 0 },
6885815Sobrien};
6989857Sobrien
7085815Sobrienstruct smusat_softc {
7189857Sobrien	struct smu_sensor *sc_sensors;
7289857Sobrien	int	sc_nsensors;
7389857Sobrien
7489857Sobrien	uint8_t	sc_cache[16];
7589857Sobrien	time_t	sc_last_update;
7689857Sobrien};
7789857Sobrien
7885815Sobrienstatic driver_t smusat_driver = {
7985815Sobrien	"smusat",
8085815Sobrien	smusat_methods,
8185815Sobrien	sizeof(struct smusat_softc)
8285815Sobrien};
8385815Sobrien
8485815Sobrienstatic devclass_t smusat_devclass;
8585815Sobrien
8685815SobrienDRIVER_MODULE(smusat, iicbus, smusat_driver, smusat_devclass, 0, 0);
8785815Sobrien
8885815Sobrienstatic int
8985815Sobriensmusat_probe(device_t dev)
9085815Sobrien{
9185815Sobrien	const char *compat = ofw_bus_get_compat(dev);
9285815Sobrien
9385815Sobrien	if (compat == NULL || strcmp(compat, "smu-sat") != 0)
9485815Sobrien		return (ENXIO);
9585815Sobrien
9685815Sobrien	device_set_desc(dev, "SMU Satellite Sensors");
9785815Sobrien	return (0);
9885815Sobrien}
9985815Sobrien
10085815Sobrienstatic int
10185815Sobriensmusat_attach(device_t dev)
10285815Sobrien{
10385815Sobrien	phandle_t child;
10485815Sobrien	struct smu_sensor *sens;
10585815Sobrien	struct smusat_softc *sc;
10685815Sobrien	struct sysctl_oid *sensroot_oid;
10785815Sobrien	struct sysctl_ctx_list *ctx;
10885815Sobrien	char type[32];
10985815Sobrien	int i;
11085815Sobrien
11185815Sobrien	sc = device_get_softc(dev);
11285815Sobrien	sc->sc_nsensors = 0;
11385815Sobrien	sc->sc_last_update = 0;
11485815Sobrien
11585815Sobrien	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
11685815Sobrien	    child = OF_peer(child))
11785815Sobrien		sc->sc_nsensors++;
11885815Sobrien
11985815Sobrien	if (sc->sc_nsensors == 0) {
12085815Sobrien		device_printf(dev, "WARNING: No sensors detected!\n");
12185815Sobrien		return (-1);
12285815Sobrien	}
12389857Sobrien
12485815Sobrien	sc->sc_sensors = malloc(sc->sc_nsensors * sizeof(struct smu_sensor),
12585815Sobrien	    M_SMUSAT, M_WAITOK | M_ZERO);
12685815Sobrien
12785815Sobrien	sens = sc->sc_sensors;
12885815Sobrien	sc->sc_nsensors = 0;
12985815Sobrien
13085815Sobrien	ctx = device_get_sysctl_ctx(dev);
13185815Sobrien	sensroot_oid = device_get_sysctl_tree(dev);
13285815Sobrien
13385815Sobrien	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
13485815Sobrien	    child = OF_peer(child)) {
13585815Sobrien		char sysctl_name[40], sysctl_desc[40];
13685815Sobrien		const char *units;
13785815Sobrien
13885815Sobrien		sens->reg = 0;
13985815Sobrien		OF_getprop(child, "reg", &sens->reg, sizeof(sens->reg));
14085815Sobrien		if (sens->reg < 0x30)
14185815Sobrien			continue;
14285815Sobrien
14385815Sobrien		sens->reg -= 0x30;
14485815Sobrien		OF_getprop(child, "location", sens->location,
14585815Sobrien		    sizeof(sens->location));
14685815Sobrien
14785815Sobrien		OF_getprop(child, "device_type", type, sizeof(type));
148218822Sdim
149218822Sdim		if (strcmp(type, "current-sensor") == 0) {
150218822Sdim			sens->type = SMU_CURRENT_SENSOR;
151218822Sdim			units = "mA";
152218822Sdim		} else if (strcmp(type, "temp-sensor") == 0) {
153218822Sdim			sens->type = SMU_TEMP_SENSOR;
154218822Sdim			units = "C";
155218822Sdim		} else if (strcmp(type, "voltage-sensor") == 0) {
156218822Sdim			sens->type = SMU_VOLTAGE_SENSOR;
15785815Sobrien			units = "mV";
15885815Sobrien		} else if (strcmp(type, "power-sensor") == 0) {
15985815Sobrien			sens->type = SMU_POWER_SENSOR;
16085815Sobrien			units = "mW";
16189857Sobrien		} else {
16285815Sobrien			continue;
16389857Sobrien		}
16485815Sobrien
16589857Sobrien		for (i = 0; i < strlen(sens->location); i++) {
16685815Sobrien			sysctl_name[i] = tolower(sens->location[i]);
16789857Sobrien			if (isspace(sysctl_name[i]))
16885815Sobrien				sysctl_name[i] = '_';
16985815Sobrien		}
17085815Sobrien		sysctl_name[i] = 0;
17185815Sobrien
172218822Sdim		sprintf(sysctl_desc,"%s (%s)", sens->location, units);
17338889Sjdp		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensroot_oid), OID_AUTO,
17485815Sobrien		    sysctl_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev,
17585815Sobrien		    sc->sc_nsensors, smusat_sensor_sysctl, "I", sysctl_desc);
17685815Sobrien
17785815Sobrien		sens++;
17838889Sjdp		sc->sc_nsensors++;
17985815Sobrien	}
180218822Sdim
18185815Sobrien	return (0);
18285815Sobrien}
18385815Sobrien
18485815Sobrienstatic int
18585815Sobriensmusat_updatecache(device_t dev)
18685815Sobrien{
18785815Sobrien	uint8_t reg = 0x3f;
188218822Sdim	struct smusat_softc *sc = device_get_softc(dev);
18985815Sobrien	struct iic_msg msgs[2] = {
19085815Sobrien	    {0, IIC_M_WR | IIC_M_NOSTOP, 1, &reg},
19185815Sobrien	    {0, IIC_M_RD, 16, sc->sc_cache},
19285815Sobrien	};
19385815Sobrien
19485815Sobrien	msgs[0].slave = msgs[1].slave = iicbus_get_addr(dev);
19585815Sobrien	sc->sc_last_update = time_uptime;
196218822Sdim
197218822Sdim	return (iicbus_transfer(dev, msgs, 2));
198218822Sdim}
199218822Sdim
20085815Sobrienstatic int
20185815Sobriensmusat_sensor_read(device_t dev, struct smu_sensor *sens, int *val)
20285815Sobrien{
20385815Sobrien	int value;
20485815Sobrien	struct smusat_softc *sc;
20585815Sobrien
20685815Sobrien	sc = device_get_softc(dev);
207218822Sdim
20885815Sobrien	if (time_uptime - sc->sc_last_update > 1)
20985815Sobrien		smusat_updatecache(dev);
21085815Sobrien
21185815Sobrien	value = (sc->sc_cache[sens->reg*2] << 8) +
21285815Sobrien	    sc->sc_cache[sens->reg*2 + 1];
21385815Sobrien
214218822Sdim	switch (sens->type) {
21585815Sobrien	case SMU_TEMP_SENSOR:
21685815Sobrien		/* 16.16 */
21785815Sobrien		value <<= 10;
21885815Sobrien		/* Kill the .16 */
21985815Sobrien		value >>= 16;
22038889Sjdp		break;
22185815Sobrien	case SMU_VOLTAGE_SENSOR:
22285815Sobrien		/* 16.16 */
22385815Sobrien		value <<= 4;
22485815Sobrien		/* Kill the .16 */
22585815Sobrien		value >>= 16;
22685815Sobrien		break;
22789857Sobrien	case SMU_CURRENT_SENSOR:
22885815Sobrien		/* 16.16 */
22938889Sjdp		value <<= 8;
23085815Sobrien		/* Kill the .16 */
23185815Sobrien		value >>= 16;
23238889Sjdp		break;
23389857Sobrien	case SMU_POWER_SENSOR:
234218822Sdim		/* Doesn't exist */
23585815Sobrien		break;
23689857Sobrien	}
23785815Sobrien
23885815Sobrien	*val = value;
23985815Sobrien	return (0);
240218822Sdim}
24189857Sobrien
242218822Sdimstatic int
24389857Sobriensmusat_sensor_sysctl(SYSCTL_HANDLER_ARGS)
24489857Sobrien{
24585815Sobrien	device_t dev;
24685815Sobrien	struct smusat_softc *sc;
24785815Sobrien	struct smu_sensor *sens;
24885815Sobrien	int value, error;
24985815Sobrien
25089857Sobrien	dev = arg1;
25189857Sobrien	sc = device_get_softc(dev);
25285815Sobrien	sens = &sc->sc_sensors[arg2];
25385815Sobrien
25489857Sobrien	error = smusat_sensor_read(dev, sens, &value);
25589857Sobrien	if (error != 0)
25689857Sobrien		return (error);
25789857Sobrien
25885815Sobrien	error = sysctl_handle_int(oidp, &value, 0, req);
25985815Sobrien
26085815Sobrien	return (error);
26185815Sobrien}
26285815Sobrien
26389857Sobrien