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