Home | History | Annotate | Line # | Download | only in dev
smu.c revision 1.13.2.1
      1 /*	$NetBSD: smu.c,v 1.13.2.1 2021/05/09 21:37:04 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2013 Phileas Fogg
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/systm.h>
     31 #include <sys/kernel.h>
     32 #include <sys/malloc.h>
     33 #include <sys/device.h>
     34 #include <sys/proc.h>
     35 #include <sys/kmem.h>
     36 #include <sys/mutex.h>
     37 #include <sys/time.h>
     38 #include <sys/reboot.h>
     39 #include <sys/sysctl.h>
     40 #include <sys/kthread.h>
     41 
     42 #include <machine/autoconf.h>
     43 
     44 #include <dev/ofw/openfirm.h>
     45 #include <dev/i2c/i2cvar.h>
     46 #include <dev/clock_subr.h>
     47 #include <dev/sysmon/sysmonvar.h>
     48 #include <dev/sysmon/sysmon_taskq.h>
     49 
     50 #include <macppc/dev/obiovar.h>
     51 #include <macppc/dev/smuvar.h>
     52 
     53 #include "opt_smu.h"
     54 
     55 struct smu_softc;
     56 
     57 struct smu_cmd {
     58 	u_char cmd;
     59 	u_char len;
     60 	u_char data[254];
     61 };
     62 
     63 struct smu_fan {
     64 	struct smu_softc* sc;
     65 
     66 	char location[32];
     67 	int reg;
     68 	int zone;
     69 	int rpm_ctl;
     70 	int min_rpm;
     71 	int max_rpm;
     72 	int default_rpm;
     73 	int current_rpm;
     74 	time_t last_update;
     75 };
     76 
     77 struct smu_iicbus {
     78 	struct smu_softc *sc;
     79 
     80 	int reg;
     81 	struct i2c_controller i2c;
     82 
     83 	LIST_ENTRY(smu_iicbus) buslist;
     84 };
     85 
     86 #define SMU_MAX_FANS		8
     87 #define SMU_MAX_SME_SENSORS	(SMU_MAX_FANS + 8)
     88 
     89 struct smu_zone {
     90 	bool (*filter)(const envsys_data_t *);
     91 	int nfans;
     92 	int fans[SMU_MAX_FANS];
     93 	int threshold, step;
     94 	int duty;
     95 };
     96 
     97 
     98 #define SMU_ZONE_CPUS	0
     99 #define SMU_ZONE_DRIVES	1
    100 #define SMU_ZONE_SLOTS	2
    101 #define SMU_ZONES	3
    102 
    103 #define C_TO_uK(n) (n * 1000000 + 273150000)
    104 
    105 struct smu_softc {
    106 	device_t sc_dev;
    107 	int sc_node;
    108 	struct sysctlnode *sc_sysctl_me;
    109 
    110 	kmutex_t sc_cmd_lock;
    111 	kmutex_t sc_msg_lock;
    112 	struct smu_cmd *sc_cmd;
    113 	paddr_t sc_cmd_paddr;
    114 	int sc_dbell_mbox;
    115 	int sc_dbell_gpio;
    116 
    117 	int sc_num_fans;
    118 	struct smu_fan sc_fans[SMU_MAX_FANS];
    119 
    120 	/*
    121 	 * We provide our own i2c device enumeration method, so we
    122 	 * need to provide our own devhandle_impl.
    123 	 */
    124 	struct devhandle_impl sc_devhandle_impl;
    125 	LIST_HEAD(, smu_iicbus) sc_iic_busses;
    126 
    127 	struct todr_chip_handle sc_todr;
    128 
    129 	struct sysmon_envsys *sc_sme;
    130 	envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
    131 	uint32_t cpu_m;
    132 	int32_t  cpu_b;
    133 
    134 	struct smu_zone sc_zones[SMU_ZONES];
    135 	lwp_t *sc_thread;
    136 	bool sc_dying;
    137 };
    138 
    139 #define SMU_CMD_FAN	0x4a
    140 #define SMU_CMD_RTC	0x8e
    141 #define SMU_CMD_I2C	0x9a
    142 #define SMU_CMD_POWER	0xaa
    143 #define SMU_CMD_ADC	0xd8
    144 #define SMU_MISC	0xee
    145 #define  SMU_MISC_GET_DATA	0x02
    146 #define  SMU_MISC_LED_CTRL	0x04
    147 
    148 #define SMU_CPUTEMP_CAL 0x18
    149 #define SMU_CPUVOLT_CAL	0x21
    150 #define SMU_SLOTPW_CAL	0x78
    151 
    152 #define SMU_PARTITION		0x3e
    153 #define SMU_PARTITION_LATEST	0x01
    154 #define SMU_PARTITION_BASE	0x02
    155 #define SMU_PARTITION_UPDATE	0x03
    156 
    157 #ifdef SMU_DEBUG
    158 #define DPRINTF printf
    159 #else
    160 #define DPRINTF while (0) printf
    161 #endif
    162 
    163 static int smu_match(device_t, struct cfdata *, void *);
    164 static void smu_attach(device_t, device_t, void *);
    165 static int smu_setup_doorbell(struct smu_softc *);
    166 static void smu_setup_fans(struct smu_softc *);
    167 static void smu_setup_iicbus(struct smu_softc *);
    168 static void smu_setup_sme(struct smu_softc *);
    169 static void smu_sme_refresh(struct sysmon_envsys *, envsys_data_t *);
    170 static int smu_do_cmd(struct smu_softc *, struct smu_cmd *, int);
    171 static int smu_dbell_gpio_intr(void *);
    172 static int smu_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
    173 static int smu_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
    174 static int smu_fan_update_rpm(struct smu_fan *);
    175 static int smu_fan_get_rpm(struct smu_fan *, int *);
    176 static int smu_fan_set_rpm(struct smu_fan *, int);
    177 static int smu_read_adc(struct smu_softc *, int);
    178 
    179 static int smu_iicbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
    180     size_t, void *, size_t, int);
    181 static int smu_sysctl_fan_rpm(SYSCTLFN_ARGS);
    182 
    183 static void smu_setup_zones(struct smu_softc *);
    184 static void smu_adjust_zone(struct smu_softc *, int);
    185 static void smu_adjust(void *);
    186 static bool is_cpu_sensor(const envsys_data_t *);
    187 static bool is_drive_sensor(const envsys_data_t *);
    188 static bool is_slots_sensor(const envsys_data_t *);
    189 
    190 int smu_get_datablock(int, uint8_t *, size_t);
    191 
    192 CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
    193     smu_match, smu_attach, NULL, NULL);
    194 
    195 static struct smu_softc *smu0 = NULL;
    196 
    197 static int
    198 smu_match(device_t parent, struct cfdata *cf, void *aux)
    199 {
    200 	struct confargs *ca = aux;
    201 
    202 	if (strcmp(ca->ca_name, "smu") == 0)
    203 		return 5;
    204 
    205 	return 0;
    206 }
    207 
    208 static void
    209 smu_attach(device_t parent, device_t self, void *aux)
    210 {
    211 	struct confargs *ca = aux;
    212 	struct smu_softc *sc = device_private(self);
    213 	uint16_t data[4];
    214 
    215 	sc->sc_dev = self;
    216 	sc->sc_node = ca->ca_node;
    217 
    218 	if (smu0 == NULL)
    219 		smu0 = sc;
    220 
    221 	sysctl_createv(NULL, 0, NULL, (void *) &sc->sc_sysctl_me,
    222 	    CTLFLAG_READWRITE,
    223 	    CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
    224 	    NULL, 0, NULL, 0,
    225 	    CTL_MACHDEP, CTL_CREATE, CTL_EOL);
    226 
    227 	if (smu_setup_doorbell(sc) != 0) {
    228 		aprint_normal(": unable to set up doorbell\n");
    229 		return;
    230 	}
    231 
    232 	aprint_normal("\n");
    233 
    234 	smu_setup_fans(sc);
    235 	smu_setup_iicbus(sc);
    236 
    237 	sc->sc_todr.todr_gettime_ymdhms = smu_todr_gettime_ymdhms;
    238 	sc->sc_todr.todr_settime_ymdhms = smu_todr_settime_ymdhms;
    239 	sc->sc_todr.cookie = sc;
    240 	todr_attach(&sc->sc_todr);
    241 
    242 	/* calibration data */
    243 	memset(data, 0, 8);
    244 	smu_get_datablock(SMU_CPUTEMP_CAL, (void *)data, 8);
    245 	DPRINTF("data %04x %04x %04x %04x\n", data[0], data[1], data[2], data[3]);
    246 	sc->cpu_m = data[2];
    247 	sc->cpu_b = (int16_t)data[3];
    248 
    249 	smu_setup_sme(sc);
    250 
    251 	smu_setup_zones(sc);
    252 }
    253 
    254 static int
    255 smu_setup_doorbell(struct smu_softc *sc)
    256 {
    257 	int node, parent, reg[4], gpio_base, irq;
    258 
    259 	mutex_init(&sc->sc_cmd_lock, MUTEX_DEFAULT, IPL_NONE);
    260 	sc->sc_cmd = malloc(4096, M_DEVBUF, M_WAITOK);
    261 	sc->sc_cmd_paddr = vtophys((vaddr_t) sc->sc_cmd);
    262 
    263 	DPRINTF("%s: cmd vaddr 0x%x paddr 0x%x\n",
    264 	    __func__, (unsigned int) sc->sc_cmd,
    265 	    (unsigned int) sc->sc_cmd_paddr);
    266 
    267 	if (OF_getprop(sc->sc_node, "platform-doorbell-buff",
    268 	        &node, sizeof(node)) <= 0)
    269 		return -1;
    270 
    271 	if (OF_getprop(node, "platform-do-doorbell-buff",
    272 	        reg, sizeof(reg)) < sizeof(reg))
    273 		return -1;
    274 
    275 	sc->sc_dbell_mbox = reg[3];
    276 
    277 	if (OF_getprop(sc->sc_node, "platform-doorbell-ack",
    278 	        &node, sizeof(node)) <= 0)
    279 		return -1;
    280 
    281 	parent = OF_parent(node);
    282 	if (parent == 0)
    283 		return -1;
    284 
    285 	if (OF_getprop(parent, "reg", &gpio_base, sizeof(gpio_base)) <= 0)
    286 		return -1;
    287 
    288 	if (OF_getprop(node, "reg", reg, sizeof(reg)) <= 0)
    289 		return -1;
    290 
    291 	if (OF_getprop(node, "interrupts", &irq, sizeof(irq)) <= 0)
    292 		return -1;
    293 
    294 	sc->sc_dbell_gpio = gpio_base + reg[0];
    295 
    296 	aprint_normal(" mbox 0x%x gpio 0x%x irq %d",
    297 	    sc->sc_dbell_mbox, sc->sc_dbell_gpio, irq);
    298 
    299 	intr_establish_xname(irq, IST_EDGE_FALLING, IPL_TTY,
    300 	    smu_dbell_gpio_intr, sc, device_xname(sc->sc_dev));
    301 
    302 	return 0;
    303 }
    304 
    305 static void
    306 smu_setup_fans(struct smu_softc *sc)
    307 {
    308 	struct smu_fan *fan;
    309 	struct sysctlnode *sysctl_fans, *sysctl_fan, *sysctl_node;
    310 	char type[32], sysctl_fan_name[32];
    311 	int node, i, j;
    312 	const char *fans[] = { "fans", "rpm-fans", 0 };
    313 	int n = 0;
    314 
    315 	while (fans[n][0] != 0) {
    316 		node = of_getnode_byname(sc->sc_node, fans[n]);
    317 		for (node = OF_child(node);
    318 		    (node != 0) && (sc->sc_num_fans < SMU_MAX_FANS);
    319 		    node = OF_peer(node)) {
    320 			fan = &sc->sc_fans[sc->sc_num_fans];
    321 			fan->sc = sc;
    322 
    323 			memset(fan->location, 0, sizeof(fan->location));
    324 			OF_getprop(node, "location", fan->location,
    325 			    sizeof(fan->location));
    326 
    327 			if (OF_getprop(node, "reg", &fan->reg,
    328 			        sizeof(fan->reg)) <= 0)
    329 				continue;
    330 
    331 			if (OF_getprop(node, "zone", &fan->zone	,
    332 			        sizeof(fan->zone)) <= 0)
    333 				continue;
    334 
    335 			memset(type, 0, sizeof(type));
    336 			OF_getprop(node, "device_type", type, sizeof(type));
    337 			if (strcmp(type, "fan-rpm-control") == 0)
    338 				fan->rpm_ctl = 1;
    339 			else
    340 				fan->rpm_ctl = 0;
    341 
    342 			if (OF_getprop(node, "min-value", &fan->min_rpm,
    343 			    sizeof(fan->min_rpm)) <= 0)
    344 				fan->min_rpm = 0;
    345 
    346 			if (OF_getprop(node, "max-value", &fan->max_rpm,
    347 			    sizeof(fan->max_rpm)) <= 0)
    348 				fan->max_rpm = 0xffff;
    349 
    350 			if (OF_getprop(node, "unmanage-value", &fan->default_rpm,
    351 			    sizeof(fan->default_rpm)) <= 0)
    352 				fan->default_rpm = fan->max_rpm;
    353 
    354 			DPRINTF("fan: location %s reg %x zone %d rpm_ctl %d "
    355 			    "min_rpm %d max_rpm %d default_rpm %d\n",
    356 			    fan->location, fan->reg, fan->zone, fan->rpm_ctl,
    357 			    fan->min_rpm, fan->max_rpm, fan->default_rpm);
    358 
    359 			sc->sc_num_fans++;
    360 		}
    361 		n++;
    362 	}
    363 
    364 	for (i = 0; i < sc->sc_num_fans; i++) {
    365 		fan = &sc->sc_fans[i];
    366 		smu_fan_set_rpm(fan, fan->default_rpm);
    367 		smu_fan_get_rpm(fan, &fan->current_rpm);
    368 	}
    369 
    370 	/* Create sysctl nodes for each fan */
    371 
    372 	sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fans,
    373 	    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
    374 	    CTLTYPE_NODE, "fans", NULL,
    375 	    NULL, 0, NULL, 0,
    376 	    CTL_MACHDEP,
    377 	    sc->sc_sysctl_me->sysctl_num,
    378 	    CTL_CREATE, CTL_EOL);
    379 
    380 	for (i = 0; i < sc->sc_num_fans; i++) {
    381 		fan = &sc->sc_fans[i];
    382 
    383 		for (j = 0; j < strlen(fan->location); j++) {
    384 			sysctl_fan_name[j] = tolower(fan->location[j]);
    385 			if (sysctl_fan_name[j] == ' ')
    386 				sysctl_fan_name[j] = '_';
    387 		}
    388 		sysctl_fan_name[j] = '\0';
    389 
    390 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_fan,
    391 		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
    392 		    CTLTYPE_NODE, sysctl_fan_name, "fan information",
    393 		    NULL, 0, NULL, 0,
    394 		    CTL_MACHDEP,
    395 		    sc->sc_sysctl_me->sysctl_num,
    396 		    sysctl_fans->sysctl_num,
    397 		    CTL_CREATE, CTL_EOL);
    398 
    399 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
    400 		    CTLFLAG_READONLY | CTLFLAG_OWNDESC,
    401 		    CTLTYPE_INT, "zone", "fan zone",
    402 		    NULL, 0, &fan->zone, 0,
    403 		    CTL_MACHDEP,
    404 		    sc->sc_sysctl_me->sysctl_num,
    405 		    sysctl_fans->sysctl_num,
    406 		    sysctl_fan->sysctl_num,
    407 		    CTL_CREATE, CTL_EOL);
    408 
    409 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
    410 		    CTLFLAG_READONLY | CTLFLAG_OWNDESC,
    411 		    CTLTYPE_INT, "min_rpm", "fan minimum rpm",
    412 		    NULL, 0, &fan->min_rpm, 0,
    413 		    CTL_MACHDEP,
    414 		    sc->sc_sysctl_me->sysctl_num,
    415 		    sysctl_fans->sysctl_num,
    416 		    sysctl_fan->sysctl_num,
    417 		    CTL_CREATE, CTL_EOL);
    418 
    419 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
    420 		    CTLFLAG_READONLY | CTLFLAG_OWNDESC,
    421 		    CTLTYPE_INT, "max_rpm", "fan maximum rpm",
    422 		    NULL, 0, &fan->max_rpm, 0,
    423 		    CTL_MACHDEP,
    424 		    sc->sc_sysctl_me->sysctl_num,
    425 		    sysctl_fans->sysctl_num,
    426 		    sysctl_fan->sysctl_num,
    427 		    CTL_CREATE, CTL_EOL);
    428 
    429 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
    430 		    CTLFLAG_READONLY | CTLFLAG_OWNDESC,
    431 		    CTLTYPE_INT, "default_rpm", "fan default rpm",
    432 		    NULL, 0, &fan->default_rpm, 0,
    433 		    CTL_MACHDEP,
    434 		    sc->sc_sysctl_me->sysctl_num,
    435 		    sysctl_fans->sysctl_num,
    436 		    sysctl_fan->sysctl_num,
    437 		    CTL_CREATE, CTL_EOL);
    438 
    439 		sysctl_createv(NULL, 0, NULL, (void *) &sysctl_node,
    440 		    CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
    441 		    CTLTYPE_INT, "rpm", "fan current rpm",
    442 		    smu_sysctl_fan_rpm, 0, (void *) fan, 0,
    443 		    CTL_MACHDEP,
    444 		    sc->sc_sysctl_me->sysctl_num,
    445 		    sysctl_fans->sysctl_num,
    446 		    sysctl_fan->sysctl_num,
    447 		    CTL_CREATE, CTL_EOL);
    448 	}
    449 }
    450 
    451 static bool
    452 smu_i2c_get_address(int node, uint32_t *addrp)
    453 {
    454 	uint32_t reg;
    455 
    456 	if (of_getprop_uint32(node, "reg", &reg) == -1) {
    457 		return false;
    458 	}
    459 
    460 	*addrp = (reg & 0xff) >> 1;
    461 	return true;
    462 }
    463 
    464 static int
    465 smu_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v)
    466 {
    467 	/*
    468 	 * This follows the OpenFirmware I2C binding for the most
    469 	 * part, but has the address shifted left for the READ bit.
    470 	 */
    471 	return of_i2c_enumerate_devices_ext(dev, call_handle, v,
    472 	    smu_i2c_get_address);
    473 }
    474 
    475 static device_call_t
    476 smu_devhandle_lookup_device_call(devhandle_t handle, const char *name,
    477     devhandle_t *call_handlep)
    478 {
    479 	if (strcmp(name, "i2c-enumerate-devices") == 0) {
    480 		return smu_i2c_enumerate_devices;
    481 	}
    482 
    483 	/* Defer everything else to the "super". */
    484 	return NULL;
    485 }
    486 
    487 static void
    488 smu_setup_iicbus(struct smu_softc *sc)
    489 {
    490 	struct smu_iicbus *iicbus;
    491 	struct i2cbus_attach_args iba;
    492 	devhandle_t devhandle;
    493 	int node;
    494 	char name[32];
    495 
    496 	node = of_getnode_byname(sc->sc_node, "smu-i2c-control");
    497 	if (node == 0)
    498 		node = sc->sc_node;
    499 
    500 	/*
    501 	 * Set up our devhandle impl; we provide our own i2c device
    502 	 * enumeration method.
    503 	 */
    504 	devhandle = devhandle_from_of(node);
    505 	devhandle_impl_inherit(&sc->sc_devhandle_impl,
    506 	    devhandle.impl);
    507 	sc->sc_devhandle_impl.lookup_device_call =
    508 	    smu_devhandle_lookup_device_call;
    509 
    510 	for (node = OF_child(node); node != 0; node = OF_peer(node)) {
    511 
    512 		memset(name, 0, sizeof(name));
    513 		OF_getprop(node, "name", name, sizeof(name));
    514 		if (strcmp(name, "i2c-bus") != 0 && strcmp(name, "i2c") != 0)
    515 			continue;
    516 
    517 		iicbus = kmem_zalloc(sizeof(*iicbus), KM_SLEEP);
    518 		iicbus->sc = sc;
    519 
    520 		if (OF_getprop(node, "reg", &iicbus->reg,
    521 			       sizeof(iicbus->reg)) <= 0) {
    522 			kmem_free(iicbus, sizeof(*iicbus));
    523 			continue;
    524 		}
    525 		LIST_INSERT_HEAD(&sc->sc_iic_busses, iicbus, buslist);
    526 
    527 		DPRINTF("iicbus: reg %x\n", iicbus->reg);
    528 
    529 		iic_tag_init(&iicbus->i2c);
    530 		iicbus->i2c.ic_cookie = iicbus;
    531 		iicbus->i2c.ic_exec = smu_iicbus_exec;
    532 
    533 		devhandle = devhandle_from_of(node);
    534 		devhandle.impl = &sc->sc_devhandle_impl;
    535 
    536 		memset(&iba, 0, sizeof(iba));
    537 		iba.iba_tag = &iicbus->i2c;
    538 		iba.iba_bus = iicbus->reg;
    539 
    540 		config_found(sc->sc_dev, &iba, iicbus_print_multi,
    541 		    CFARG_DEVHANDLE, devhandle,
    542 		    CFARG_EOL);
    543 	}
    544 }
    545 
    546 static void
    547 smu_setup_sme(struct smu_softc *sc)
    548 {
    549 	struct smu_fan *fan;
    550 	envsys_data_t *sme_sensor;
    551 	int i, sensors, child, reg;
    552 	char loc[32], type[32];
    553 
    554 	sc->sc_sme = sysmon_envsys_create();
    555 
    556 	for (i = 0; i < sc->sc_num_fans; i++) {
    557 		sme_sensor = &sc->sc_sme_sensors[i];
    558 		fan = &sc->sc_fans[i];
    559 
    560 		sme_sensor->units = ENVSYS_SFANRPM;
    561 		sme_sensor->state = ENVSYS_SINVALID;
    562 		snprintf(sme_sensor->desc, sizeof(sme_sensor->desc),
    563 		    "%s", fan->location);
    564 
    565 		if (sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor)) {
    566 			sysmon_envsys_destroy(sc->sc_sme);
    567 			return;
    568 		}
    569 	}
    570 	sensors = OF_finddevice("/smu/sensors");
    571 	child = OF_child(sensors);
    572 	while (child != 0) {
    573 		sme_sensor = &sc->sc_sme_sensors[i];
    574 		if (OF_getprop(child, "location", loc, 32) == 0) goto next;
    575 		if (OF_getprop(child, "device_type", type, 32) == 0) goto next;
    576 		if (OF_getprop(child, "reg", &reg, 4) == 0) goto next;
    577 		if (strcmp(type, "temp-sensor") == 0) {
    578 			sme_sensor->units = ENVSYS_STEMP;
    579 			sme_sensor->state = ENVSYS_SINVALID;
    580 			strncpy(sme_sensor->desc, loc, sizeof(sme_sensor->desc));
    581 			sme_sensor->private = reg;
    582 			sysmon_envsys_sensor_attach(sc->sc_sme, sme_sensor);
    583 			i++;
    584 			printf("%s: %s@%x\n", loc, type, reg);
    585 		}
    586 next:
    587 		child = OF_peer(child);
    588 	}
    589 
    590 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
    591 	sc->sc_sme->sme_cookie = sc;
    592 	sc->sc_sme->sme_refresh = smu_sme_refresh;
    593 
    594 	if (sysmon_envsys_register(sc->sc_sme)) {
    595 		aprint_error_dev(sc->sc_dev,
    596 		    "unable to register with sysmon\n");
    597 		sysmon_envsys_destroy(sc->sc_sme);
    598 	}
    599 }
    600 
    601 static void
    602 smu_sme_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    603 {
    604 	struct smu_softc *sc = sme->sme_cookie;
    605 	struct smu_fan *fan;
    606 	int which = edata->sensor;
    607 	int ret;
    608 
    609 	edata->state = ENVSYS_SINVALID;
    610 
    611 	if (which < sc->sc_num_fans) {
    612 		fan = &sc->sc_fans[which];
    613 
    614 		ret = smu_fan_get_rpm(fan, &fan->current_rpm);
    615 		if (ret == 0) {
    616 			edata->value_cur = fan->current_rpm;
    617 			edata->state = ENVSYS_SVALID;
    618 		}
    619 	} else if (edata->private > 0) {
    620 		/* this works only for the CPU diode */
    621 		int64_t r = smu_read_adc(sc, edata->private);
    622 		if (r != -1) {
    623 			r = r * sc->cpu_m;
    624 			r >>= 3;
    625 			r += (int64_t)sc->cpu_b << 9;
    626 			r <<= 1;
    627 			r *= 15625;
    628 			r /= 1024;
    629 			edata->value_cur = r + 273150000;
    630 			edata->state = ENVSYS_SVALID;
    631 		}
    632 	}
    633 }
    634 
    635 static int
    636 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
    637 {
    638 	int gpio, ret, bail;
    639 	u_char ack;
    640 
    641 	mutex_enter(&sc->sc_cmd_lock);
    642 
    643 	DPRINTF("%s: cmd %02x len %02x\n", __func__, cmd->cmd, cmd->len);
    644 	DPRINTF("%s: data %02x %02x %02x %02x %02x %02x %02x %02x\n", __func__,
    645 	    cmd->data[0], cmd->data[1], cmd->data[2], cmd->data[3],
    646 	    cmd->data[4], cmd->data[5], cmd->data[6], cmd->data[7]);
    647 
    648 	sc->sc_cmd->cmd = cmd->cmd;
    649 	sc->sc_cmd->len = cmd->len;
    650 	memcpy(sc->sc_cmd->data, cmd->data, cmd->len);
    651 
    652 	__asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
    653 
    654 	obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
    655 	obio_write_1(sc->sc_dbell_gpio, 0x04);
    656 
    657 	bail = 0;
    658 
    659 	gpio = obio_read_1(sc->sc_dbell_gpio);
    660 
    661 	while (((gpio & 0x07) != 0x07) && (bail < timo)) {
    662 		ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
    663 		if (ret != 0) {
    664 			bail++;
    665 		}
    666 		gpio = obio_read_1(sc->sc_dbell_gpio);
    667 	}
    668 
    669 	if ((gpio & 0x07) != 0x07) {
    670 		mutex_exit(&sc->sc_cmd_lock);
    671 		return EWOULDBLOCK;
    672 	}
    673 
    674 	__asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
    675 
    676 	ack = (~cmd->cmd) & 0xff;
    677 	if (sc->sc_cmd->cmd != ack) {
    678 		DPRINTF("%s: invalid ack, got %x expected %x\n",
    679 		    __func__, sc->sc_cmd->cmd, ack);
    680 		mutex_exit(&sc->sc_cmd_lock);
    681 		return EIO;
    682 	}
    683 
    684 	cmd->cmd = sc->sc_cmd->cmd;
    685 	cmd->len = sc->sc_cmd->len;
    686 	memcpy(cmd->data, sc->sc_cmd->data, sc->sc_cmd->len);
    687 
    688 	mutex_exit(&sc->sc_cmd_lock);
    689 
    690 	return 0;
    691 }
    692 
    693 
    694 static int
    695 smu_dbell_gpio_intr(void *arg)
    696 {
    697 	struct smu_softc *sc = arg;
    698 
    699 	DPRINTF("%s\n", __func__);
    700 
    701 	wakeup(sc->sc_cmd);
    702 
    703 	return 1;
    704 }
    705 
    706 void
    707 smu_poweroff(void)
    708 {
    709 	struct smu_cmd cmd;
    710 
    711 	if (smu0 == NULL)
    712 		return;
    713 
    714 	cmd.cmd = SMU_CMD_POWER;
    715 	strcpy(cmd.data, "SHUTDOWN");
    716 	cmd.len = strlen(cmd.data) + 1;
    717 	smu_do_cmd(smu0, &cmd, 800);
    718 
    719 	for (;;);
    720 }
    721 
    722 void
    723 smu_restart(void)
    724 {
    725 	struct smu_cmd cmd;
    726 
    727 	if (smu0 == NULL)
    728 		return;
    729 
    730 	cmd.cmd = SMU_CMD_POWER;
    731 	strcpy(cmd.data, "RESTART");
    732 	cmd.len = strlen(cmd.data) + 1;
    733 	smu_do_cmd(smu0, &cmd, 800);
    734 
    735 	for (;;);
    736 }
    737 
    738 static int
    739 smu_todr_gettime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    740 {
    741 	struct smu_softc *sc = tch->cookie;
    742 	struct smu_cmd cmd;
    743 	int ret;
    744 
    745 	cmd.cmd = SMU_CMD_RTC;
    746 	cmd.len = 1;
    747 	cmd.data[0] = 0x81;
    748 
    749 	ret = smu_do_cmd(sc, &cmd, 800);
    750 	if (ret != 0)
    751 		return ret;
    752 
    753 	dt->dt_sec = bcdtobin(cmd.data[0]);
    754 	dt->dt_min = bcdtobin(cmd.data[1]);
    755 	dt->dt_hour = bcdtobin(cmd.data[2]);
    756 	dt->dt_wday = bcdtobin(cmd.data[3]);
    757 	dt->dt_day = bcdtobin(cmd.data[4]);
    758 	dt->dt_mon = bcdtobin(cmd.data[5]);
    759 	dt->dt_year = bcdtobin(cmd.data[6]) + 2000;
    760 
    761 	return 0;
    762 }
    763 
    764 static int
    765 smu_todr_settime_ymdhms(todr_chip_handle_t tch, struct clock_ymdhms *dt)
    766 {
    767 	struct smu_softc *sc = tch->cookie;
    768 	struct smu_cmd cmd;
    769 
    770 	cmd.cmd = SMU_CMD_RTC;
    771 	cmd.len = 8;
    772 	cmd.data[0] = 0x80;
    773 	cmd.data[1] = bintobcd(dt->dt_sec);
    774 	cmd.data[2] = bintobcd(dt->dt_min);
    775 	cmd.data[3] = bintobcd(dt->dt_hour);
    776 	cmd.data[4] = bintobcd(dt->dt_wday);
    777 	cmd.data[5] = bintobcd(dt->dt_day);
    778 	cmd.data[6] = bintobcd(dt->dt_mon);
    779 	cmd.data[7] = bintobcd(dt->dt_year - 2000);
    780 
    781 	return smu_do_cmd(sc, &cmd, 800);
    782 }
    783 
    784 static int
    785 smu_fan_update_rpm(struct smu_fan *fan)
    786 {
    787 	struct smu_softc *sc = fan->sc;
    788 	struct smu_cmd cmd;
    789 	int ret;
    790 
    791 	cmd.cmd = SMU_CMD_FAN;
    792 	cmd.len = 2;
    793 	cmd.data[0] = 0x31;
    794 	cmd.data[1] = fan->reg;
    795 
    796 	ret = smu_do_cmd(sc, &cmd, 800);
    797 	if (ret == 0) {
    798 		fan->last_update = time_uptime;
    799 		fan->current_rpm = (cmd.data[0] << 8) | cmd.data[1];
    800 	} else {
    801 		cmd.cmd = SMU_CMD_FAN;
    802 		cmd.len = 1;
    803 		cmd.data[0] = 0x01;
    804 
    805 		ret = smu_do_cmd(sc, &cmd, 800);
    806 		if (ret == 0) {
    807 			fan->last_update = time_uptime;
    808 			fan->current_rpm = (cmd.data[1 + fan->reg * 2] << 8) |
    809 			    cmd.data[2 + fan->reg * 2];
    810 		}
    811 	}
    812 
    813 	return ret;
    814 }
    815 
    816 static int
    817 smu_fan_get_rpm(struct smu_fan *fan, int *rpm)
    818 {
    819 	int ret;
    820 	ret = 0;
    821 
    822 	if (time_uptime - fan->last_update > 1) {
    823 		ret = smu_fan_update_rpm(fan);
    824 		if (ret != 0)
    825 			return ret;
    826 	}
    827 
    828 	*rpm = fan->current_rpm;
    829 
    830 	return ret;
    831 }
    832 
    833 static int
    834 smu_fan_set_rpm(struct smu_fan *fan, int rpm)
    835 {
    836 	struct smu_softc *sc = fan->sc;
    837 	struct smu_cmd cmd;
    838 	int ret;
    839 
    840 	DPRINTF("%s: fan %s rpm %d\n", __func__, fan->location, rpm);
    841 
    842 	rpm = uimax(fan->min_rpm, rpm);
    843 	rpm = uimin(fan->max_rpm, rpm);
    844 
    845 	cmd.cmd = SMU_CMD_FAN;
    846 	cmd.len = 4;
    847 	cmd.data[0] = 0x30;
    848 	cmd.data[1] = fan->reg;
    849 	cmd.data[2] = (rpm >> 8) & 0xff;
    850 	cmd.data[3] = rpm & 0xff;
    851 
    852 	ret = smu_do_cmd(sc, &cmd, 800);
    853 	if (ret != 0) {
    854 		cmd.cmd = SMU_CMD_FAN;
    855 		cmd.len = 14;
    856 		cmd.data[0] = fan->rpm_ctl ? 0x00 : 0x10;
    857 		cmd.data[1] = 1 << fan->reg;
    858 		cmd.data[2] = cmd.data[2 + fan->reg * 2] = (rpm >> 8) & 0xff;
    859 		cmd.data[3] = cmd.data[3 + fan->reg * 2] = rpm & 0xff;
    860 
    861 		ret = smu_do_cmd(sc, &cmd, 800);
    862 	}
    863 
    864 	return ret;
    865 }
    866 
    867 static int
    868 smu_read_adc(struct smu_softc *sc, int id)
    869 {
    870 	struct smu_cmd cmd;
    871 	int ret;
    872 
    873 	cmd.cmd = SMU_CMD_ADC;
    874 	cmd.len = 1;
    875 	cmd.data[0] = id;
    876 
    877 	ret = smu_do_cmd(sc, &cmd, 800);
    878 	if (ret == 0) {
    879 		return cmd.data[0] << 8 | cmd.data[1];
    880 	}
    881 	return -1;
    882 }
    883 
    884 static int
    885 smu_iicbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *send,
    886     size_t send_len, void *recv, size_t recv_len, int flags)
    887 {
    888 	struct smu_iicbus *iicbus = cookie;
    889 	struct smu_softc *sc = iicbus->sc;
    890 	struct smu_cmd cmd;
    891 	int retries, ret;
    892 
    893 	DPRINTF("%s: op %x addr %x send_len %d recv_len %d\n",
    894 	    __func__, op, addr, send_len, recv_len);
    895 
    896 	cmd.cmd = SMU_CMD_I2C;
    897 	cmd.len = 9 + recv_len;
    898 	cmd.data[0] = iicbus->reg;
    899 	cmd.data[1] = I2C_OP_READ_P(op) ? 0x02 : 0x00;
    900 	cmd.data[2] = addr << 1;
    901 	cmd.data[3] = send_len;
    902 	memcpy(&cmd.data[4], send, send_len);
    903 	cmd.data[7] = addr << 1;
    904 	if (I2C_OP_READ_P(op))
    905 		cmd.data[7] |= 0x01;
    906 	cmd.data[8] = recv_len;
    907 	memcpy(&cmd.data[9], recv, recv_len);
    908 
    909 	ret = smu_do_cmd(sc, &cmd, 800);
    910 	if (ret != 0)
    911 		return (ret);
    912 
    913 	for (retries = 0; retries < 10; retries++) {
    914 		cmd.cmd = SMU_CMD_I2C;
    915 		cmd.len = 1;
    916 		cmd.data[0] = 0x00;
    917 		memset(&cmd.data[1], 0xff, recv_len);
    918 
    919 		ret = smu_do_cmd(sc, &cmd, 800);
    920 
    921 		DPRINTF("%s: cmd data[0] %x\n", __func__, cmd.data[0]);
    922 
    923 		if (ret == 0 && (cmd.data[0] & 0x80) == 0)
    924 			break;
    925 
    926 		DELAY(10000);
    927 	}
    928 
    929 	if (cmd.data[0] & 0x80)
    930 		return EIO;
    931 
    932 	if (I2C_OP_READ_P(op))
    933 		memcpy(recv, &cmd.data[1], recv_len);
    934 
    935 	return 0;
    936 }
    937 
    938 static int
    939 smu_sysctl_fan_rpm(SYSCTLFN_ARGS)
    940 {
    941 	struct sysctlnode node = *rnode;
    942 	struct smu_fan *fan = node.sysctl_data;
    943 	int rpm = 0;
    944 	int ret;
    945 
    946 	node.sysctl_data = &rpm;
    947 
    948 	if (newp) {
    949 		if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
    950 			rpm = *(int *) node.sysctl_data;
    951 			return smu_fan_set_rpm(fan, rpm);
    952 		}
    953 		return EINVAL;
    954 	} else {
    955 		ret = smu_fan_get_rpm(fan, &rpm);
    956 		if (ret != 0)
    957 			return (ret);
    958 
    959 		return sysctl_lookup(SYSCTLFN_CALL(&node));
    960 	}
    961 
    962 	return 0;
    963 }
    964 
    965 SYSCTL_SETUP(smu_sysctl_setup, "SMU sysctl subtree setup")
    966 {
    967 	sysctl_createv(NULL, 0, NULL, NULL,
    968 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
    969 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
    970 }
    971 
    972 static void
    973 smu_setup_zones(struct smu_softc *sc)
    974 {
    975 	struct smu_zone *z;
    976 	struct smu_fan *f;
    977 	int i;
    978 
    979 	/* find CPU fans */
    980 	z = &sc->sc_zones[SMU_ZONE_CPUS];
    981 	z->nfans = 0;
    982 	for (i = 0; i < SMU_MAX_FANS; i++) {
    983 		f = &sc->sc_fans[i];
    984 		if ((strstr(f->location, "CPU") != NULL) ||
    985 		    (strstr(f->location, "System") != NULL)) {
    986 			z->fans[z->nfans] = i;
    987 			z->nfans++;
    988 		}
    989 	}
    990 	aprint_normal_dev(sc->sc_dev,
    991 	    "using %d fans for CPU zone\n", z->nfans);
    992 	z->threshold = C_TO_uK(45);
    993 	z->duty = 150;
    994 	z->step = 3;
    995 	z->filter = is_cpu_sensor;
    996 
    997 	z = &sc->sc_zones[SMU_ZONE_DRIVES];
    998 	z->nfans = 0;
    999 	for (i = 0; i < SMU_MAX_FANS; i++) {
   1000 		f = &sc->sc_fans[i];
   1001 		if ((strstr(f->location, "DRIVE") != NULL) ||
   1002 		    (strstr(f->location, "Drive") != NULL)) {
   1003 			z->fans[z->nfans] = i;
   1004 			z->nfans++;
   1005 		}
   1006 	}
   1007 	aprint_normal_dev(sc->sc_dev,
   1008 	    "using %d fans for drive bay zone\n", z->nfans);
   1009 	z->threshold = C_TO_uK(40);
   1010 	z->duty = 150;
   1011 	z->step = 2;
   1012 	z->filter = is_drive_sensor;
   1013 
   1014 	z = &sc->sc_zones[SMU_ZONE_SLOTS];
   1015 	z->nfans = 0;
   1016 	for (i = 0; i < SMU_MAX_FANS; i++) {
   1017 		f = &sc->sc_fans[i];
   1018 		if ((strstr(f->location, "BACKSIDE") != NULL) ||
   1019 		    (strstr(f->location, "SLOTS") != NULL)) {
   1020 			z->fans[z->nfans] = i;
   1021 			z->nfans++;
   1022 		}
   1023 	}
   1024 	aprint_normal_dev(sc->sc_dev,
   1025 	    "using %d fans for expansion slots zone\n", z->nfans);
   1026 	z->threshold = C_TO_uK(40);
   1027 	z->duty = 150;
   1028 	z->step = 2;
   1029 	z->filter = is_slots_sensor;
   1030 
   1031 	sc->sc_dying = false;
   1032 	kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
   1033 	    "fan control");
   1034 }
   1035 
   1036 static void
   1037 smu_adjust_zone(struct smu_softc *sc, int which)
   1038 {
   1039 	struct smu_zone *z = &sc->sc_zones[which];
   1040 	struct smu_fan *f;
   1041 	long temp, newduty, i, speed, diff;
   1042 
   1043 	DPRINTF("%s %d\n", __func__, which);
   1044 
   1045 	temp = sysmon_envsys_get_max_value(z->filter, true);
   1046 	if (temp == 0) {
   1047 		/* no sensor data - leave fan alone */
   1048 		DPRINTF("nodata\n");
   1049 		return;
   1050 	}
   1051 	DPRINTF("temp %ld ", (temp - 273150000) / 1000000);
   1052 	diff = ((temp - z->threshold) / 1000000) * z->step;
   1053 
   1054 	if (diff < 0) newduty = 0;
   1055 	else if (diff > 100) newduty = 100;
   1056 	else newduty = diff;
   1057 
   1058 	DPRINTF("newduty %ld diff %ld \n", newduty, diff);
   1059 	if (newduty == z->duty) {
   1060 		DPRINTF("no change\n");
   1061 		return;
   1062 	}
   1063 	z->duty = newduty;
   1064 	/* now adjust each fan to the new duty cycle */
   1065 	for (i = 0; i < z->nfans; i++) {
   1066 		f = &sc->sc_fans[z->fans[i]];
   1067 		speed = f->min_rpm + ((f->max_rpm - f->min_rpm) * newduty) / 100;
   1068 		DPRINTF("fan %d speed %ld ", z->fans[i], speed);
   1069 		smu_fan_set_rpm(f, speed);
   1070 	}
   1071 	DPRINTF("\n");
   1072 }
   1073 
   1074 static void
   1075 smu_adjust(void *cookie)
   1076 {
   1077 	struct smu_softc *sc = cookie;
   1078 	int i;
   1079 
   1080 	while (!sc->sc_dying) {
   1081 		for (i = 0; i < SMU_ZONES; i++)
   1082 			smu_adjust_zone(sc, i);
   1083 		kpause("fanctrl", true, mstohz(3000), NULL);
   1084 	}
   1085 	kthread_exit(0);
   1086 }
   1087 
   1088 static bool is_cpu_sensor(const envsys_data_t *edata)
   1089 {
   1090 	if (edata->units != ENVSYS_STEMP)
   1091 		return false;
   1092 	if (strstr(edata->desc, "CPU") != NULL)
   1093 		return TRUE;
   1094 	return false;
   1095 }
   1096 
   1097 static bool is_drive_sensor(const envsys_data_t *edata)
   1098 {
   1099 	if (edata->units != ENVSYS_STEMP)
   1100 		return false;
   1101 	if (strstr(edata->desc, "DRIVE") != NULL)
   1102 		return TRUE;
   1103 	if (strstr(edata->desc, "drive") != NULL)
   1104 		return TRUE;
   1105 	return false;
   1106 }
   1107 
   1108 static bool is_slots_sensor(const envsys_data_t *edata)
   1109 {
   1110 	if (edata->units != ENVSYS_STEMP)
   1111 		return false;
   1112 	if (strstr(edata->desc, "BACKSIDE") != NULL)
   1113 		return TRUE;
   1114 	if (strstr(edata->desc, "INLET") != NULL)
   1115 		return TRUE;
   1116 	if (strstr(edata->desc, "DIODE") != NULL)
   1117 		return TRUE;
   1118 	if (strstr(edata->desc, "TUNNEL") != NULL)
   1119 		return TRUE;
   1120 	return false;
   1121 }
   1122 
   1123 int
   1124 smu_get_datablock(int id, uint8_t *buf, size_t len)
   1125 {
   1126 	struct smu_cmd cmd;
   1127 
   1128 	cmd.cmd = SMU_PARTITION;
   1129 	cmd.len = 2;
   1130 	cmd.data[0] = SMU_PARTITION_LATEST;
   1131 	cmd.data[1] = id;
   1132 	smu_do_cmd(smu0, &cmd, 100);
   1133 
   1134 	cmd.data[4] = cmd.data[0];
   1135 	cmd.data[5] = cmd.data[1];
   1136 
   1137 	cmd.cmd = SMU_MISC;
   1138 	cmd.len = 7;
   1139 	cmd.data[0] = SMU_MISC_GET_DATA;
   1140 	cmd.data[1] = 4;
   1141 	cmd.data[2] = 0;
   1142 	cmd.data[3] = 0;
   1143 	cmd.data[6] = len;
   1144 	smu_do_cmd(smu0, &cmd, 100);
   1145 
   1146 	memcpy(buf, cmd.data, len);
   1147 	return 0;
   1148 }
   1149