Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: envmon_ebus.c,v 1.1 2026/09/07 07:35:13 jdc Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Julian Coleman.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: envmon_ebus.c,v 1.1 2026/09/07 07:35:13 jdc Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/kernel.h>
     37 #include <sys/device.h>
     38 #include <sys/proc.h>
     39 #include <sys/types.h>
     40 
     41 #include <sys/bus.h>
     42 #include <machine/autoconf.h>
     43 
     44 #include <dev/ebus/ebusreg.h>
     45 #include <dev/ebus/ebusvar.h>
     46 
     47 #include <dev/led.h>
     48 #include <dev/sysmon/sysmonvar.h>
     49 
     50 #include <arch/sparc64/dev/envmon_ebusreg.h>
     51 
     52 #define ENVMON_MACHINE_U25	0x19
     53 #define ENVMON_MACHINE_U45	0x2d
     54 #define ENVMON_MACHINE_V215	0xd7
     55 #define ENVMON_MACHINE_V245	0xf5
     56 
     57 #define ENVMON_PIC_MAX_TEMPS	1
     58 #define ENVMON_PIC_WMIN_TEMP		273150000	/* 0C from Solaris */
     59 #define ENVMON_PIC_WMAX_TEMP		318150000	/* 45C from Solaris */
     60 #define ENVMON_PIC_CRIT_TEMP		338150000	/* 65C from Solaris */
     61 
     62 struct envmon_epic_fans {
     63 	const uint8_t	reg;
     64 	const char	*desc;
     65 };
     66 static struct envmon_epic_fans envmon_epic_fans_table[] = {
     67 	{ ENVMON_EPC_FAN_FT0_F0, "FT0.F0" },
     68 	{ ENVMON_EPC_FAN_FT1_F0, "FT1.F0" },
     69 	{ ENVMON_EPC_FAN_FT2_F0, "FT2.F0" },
     70 	{ ENVMON_EPC_FAN_FT3_F0, "FT3.F0" },
     71 	{ ENVMON_EPC_FAN_FT4_F0, "FT4.F0" },
     72 	{ ENVMON_EPC_FAN_FT5_F0, "FT5.F0" },
     73 	{ ENVMON_EPC_FAN_FT0_F1, "FT0.F1" },
     74 	{ ENVMON_EPC_FAN_FT1_F1, "FT1.F1" },
     75 	{ ENVMON_EPC_FAN_FT2_F1, "FT2.F1" },
     76 	{ ENVMON_EPC_FAN_FT3_F1, "FT3.F1" },
     77 	{ ENVMON_EPC_FAN_FT4_F1, "FT4.F1" },
     78 	{ ENVMON_EPC_FAN_FT5_F1, "FT5.F1" },
     79 };
     80 #define ENVMON_EPC_MAX_FANS	(sizeof(envmon_epic_fans_table) / \
     81 				    sizeof(envmon_epic_fans_table[0]))
     82 #define ENVMON_EPC_DIV_V245		5625
     83 #define ENVMON_EPC_DIV_V215		11250
     84 #define ENVMON_EPC_CRIT_SPEED_V245	2022		/* From ALOM */
     85 #define ENVMON_EPC_CRIT_SPEED_V215	5012		/* From ALOM */
     86 #define ENVMON_EPC_WMIN_TEMP		273150000	/* 0C from ALOM */
     87 #define ENVMON_EPC_WMAX_TEMP		318150000	/* 45C from ALOM */
     88 #define ENVMON_EPC_CRIT_TEMP		323150000	/* 50C from ALOM */
     89 #define ENVMON_EPC_VAL_TO_SPEED(div, val)	((div * 60) / val)
     90 
     91 #define ENVMON_EPC_MAX_FAULTS	1
     92 
     93 #define ENVMON_EPC_MAX_TEMPS	1
     94 
     95 struct envmon_epic_ind {
     96 	uint8_t		reg, set;
     97 	const char	*desc;
     98 };
     99 static struct envmon_epic_ind envmon_epic_inds_table[] = {
    100 	{ ENVMON_EPC_KEYSW, ENVMON_EPC_KEYSW_NORM, "keyswitch normal/diag" },
    101 	{ ENVMON_EPC_KEYSW, ENVMON_EPC_KEYSW_LOCK, "keyswitch locked" },
    102 };
    103 
    104 #define ENVMON_EPC_MAX_INDS	(sizeof(envmon_epic_inds_table) / \
    105 				    sizeof(envmon_epic_inds_table[0]))
    106 
    107 struct envmon_epic_led {
    108 	uint8_t		reg, v_on;
    109 	const char	*desc;
    110 };
    111 static struct envmon_epic_led envmon_epic_led_table[] = {
    112 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_LOC, "locator" },
    113 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_SRV, "service" },
    114 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_SRV_FSH, "service_flash" },
    115 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_PWR, "power" },
    116 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_TF_FLT, "top_fan_fault" },
    117 	{ ENVMON_EPC_LED6, ENVMON_EPC_LED6_TF_FLT_FSH, "top_fan_fault_flash" },
    118 	{ ENVMON_EPC_LED7, ENVMON_EPC_LED7_PSU, "psu_fault" },
    119 	{ ENVMON_EPC_LED7, ENVMON_EPC_LED7_PSU_FSH, "psu_fault_flash" },
    120 	{ ENVMON_EPC_LED7, ENVMON_EPC_LED7_CTEMP, "cpu_overtemp" },
    121 	{ ENVMON_EPC_LED7, ENVMON_EPC_LED7_CTEMP_FSH, "cpu_overtemp_flash" },
    122 };
    123 #define ENVMON_EPC_MAX_LEDS	(sizeof(envmon_epic_led_table) / \
    124 				    sizeof(envmon_epic_led_table[0]))
    125 
    126 #define ENVMON_EPC_MAX_SENSORS	\
    127     (ENVMON_EPC_MAX_FANS + ENVMON_EPC_MAX_FAULTS + \
    128     ENVMON_EPC_MAX_TEMPS + ENVMON_EPC_MAX_INDS)
    129 
    130 #define ENVMON_MAX_TEMPS \
    131      MAX(ENVMON_PIC_MAX_TEMPS, ENVMON_EPC_MAX_TEMPS)
    132 #define ENVMON_MAX_SENSORS \
    133     MAX(ENVMON_PIC_MAX_TEMPS, ENVMON_EPC_MAX_SENSORS)
    134 
    135 struct envmon_sc_fan {
    136 	uint8_t		reg;
    137 	int		cmin;
    138 };
    139 
    140 struct envmon_sc_temp {
    141 	uint8_t		reg;
    142 	int		wmin, wmax, cmax;
    143 };
    144 
    145 struct envmon_sc_ind {
    146 	uint8_t		reg, set;
    147 };
    148 struct envmon_sc_led {
    149 	void			*cookie;
    150 	struct led_device	*led;
    151 	uint8_t			reg, v_on;
    152 };
    153 
    154 struct envmon_softc {
    155 	device_t		sc_dev;
    156 
    157 	bus_space_tag_t		sc_bst;
    158 	bus_space_handle_t	sc_bsh;
    159 
    160 	int			sc_machine_type, sc_nfans;
    161 	struct envmon_sc_fan	sc_fan[ENVMON_EPC_MAX_FANS];
    162 	struct envmon_sc_temp	sc_temp[ENVMON_MAX_TEMPS];
    163 	struct envmon_sc_ind	sc_ind[ENVMON_EPC_MAX_INDS];
    164 	struct envmon_sc_led	sc_led[ENVMON_EPC_MAX_LEDS];
    165 	struct sysmon_envsys	*sc_sme;
    166 	envsys_data_t		sc_sensor[ENVMON_MAX_SENSORS];
    167 
    168 #ifdef ENVMON_DEBUG
    169 	#define ENVMON_MAX_REG	0x3d
    170 	uint8_t			sc_reg[ENVMON_MAX_REG];
    171 	#define ENVMON_TIMER	60
    172 	callout_t		sc_timer;
    173 #endif
    174 };
    175 
    176 static int envmon_ebus_match(device_t, cfdata_t, void *);
    177 static void envmon_ebus_attach(device_t, device_t, void *);
    178 static void envmon_pic_attach(struct envmon_softc *, int);
    179 static void envmon_epic_attach(struct envmon_softc *, int);
    180 void envmon_pic_refresh(struct sysmon_envsys *, envsys_data_t *);
    181 void envmon_epic_refresh(struct sysmon_envsys *, envsys_data_t *);
    182 void envmon_epic_refresh_fan(struct envmon_softc *, envsys_data_t *);
    183 void envmon_epic_refresh_fault(struct envmon_softc *, envsys_data_t *);
    184 void envmon_refresh_temp(struct envmon_softc *, envsys_data_t *);
    185 void envmon_epic_refresh_ind(struct envmon_softc *, envsys_data_t *);
    186 void envmon_pic_get_limits(struct sysmon_envsys *, envsys_data_t *,
    187     sysmon_envsys_lim_t *, uint32_t *);
    188 void envmon_pic_set_limits(struct sysmon_envsys *, envsys_data_t *,
    189     sysmon_envsys_lim_t *, uint32_t *);
    190 void envmon_epic_get_limits(struct sysmon_envsys *, envsys_data_t *,
    191     sysmon_envsys_lim_t *, uint32_t *);
    192 void envmon_epic_set_limits(struct sysmon_envsys *, envsys_data_t *,
    193     sysmon_envsys_lim_t *, uint32_t *);
    194 static void envmon_epic_get_fan_limits(struct envmon_softc *,
    195     envsys_data_t *, sysmon_envsys_lim_t *, uint32_t *);
    196 static void envmon_get_temp_limits(struct envmon_softc *,
    197     envsys_data_t *, sysmon_envsys_lim_t *, uint32_t *);
    198 static void envmon_epic_set_fan_limits(struct envmon_softc *,
    199     envsys_data_t *, sysmon_envsys_lim_t *, uint32_t *);
    200 static void envmon_set_temp_limits(struct envmon_softc *,
    201     envsys_data_t *, sysmon_envsys_lim_t *, uint32_t *);
    202 int envmon_epic_get_led(void *);
    203 void envmon_epic_set_led(void *, int);
    204 #ifdef ENVMON_DEBUG
    205 static void envmon_timeout(void *);
    206 #endif
    207 
    208 CFATTACH_DECL_NEW(envmon_ebus, sizeof(struct envmon_softc),
    209     envmon_ebus_match, envmon_ebus_attach, NULL, NULL);
    210 
    211 #define	envmon_read(sc, reg) \
    212     bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg)
    213 #define envmon_write(sc, reg, val) \
    214     bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val)
    215 
    216 static int
    217 envmon_ebus_match(device_t parent, cfdata_t cf, void *aux)
    218 {
    219 	struct ebus_attach_args *ea = aux;
    220 	char *compat;
    221 
    222 	if (strcmp("env-monitor", ea->ea_name) != 0)
    223 		return 0;
    224 
    225 	compat = prom_getpropstring(ea->ea_node, "compatible");
    226 	if (compat != NULL && (!strcmp(compat, "SUNW,ebus-pic16f747-env") ||
    227 	    !strcmp(compat, "epic")))
    228 		return 1;
    229 
    230 	return 0;
    231 }
    232 
    233 static void
    234 envmon_ebus_attach(device_t parent, device_t self, void *aux)
    235 {
    236 	struct envmon_softc *sc = device_private(self);
    237 	struct ebus_attach_args *ea = aux;
    238 	char compat[32];
    239 	int sz;
    240 #ifdef ENVMON_DEBUG
    241 	uint8_t	reg;
    242 #endif
    243 
    244 	sc->sc_dev = self;
    245 	sc->sc_bst = ea->ea_bustag;
    246 
    247 	sz = ea->ea_reg[0].size;
    248 
    249 	if (bus_space_map(sc->sc_bst,
    250 			 EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
    251 			 sz, 0,
    252 			 &sc->sc_bsh) != 0) {
    253 		aprint_error(": can't map register\n");
    254 		return;
    255 	}
    256 
    257 	sc->sc_sme = sysmon_envsys_create();
    258 	sc->sc_sme->sme_name = device_xname(self);
    259 	sc->sc_sme->sme_cookie = sc;
    260 
    261 	OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat));
    262 
    263 	if (!strcmp(compat, "SUNW,ebus-pic16f747-env")) {
    264 		envmon_pic_attach(sc, ea->ea_node);
    265 	}
    266 	if (!strcmp(compat, "epic")) {
    267 		envmon_epic_attach(sc, ea->ea_node);
    268 	}
    269 
    270 #ifdef ENVMON_DEBUG
    271 	for (reg = 0; reg < ENVMON_MAX_REG; reg++) {
    272 		envmon_write(sc, ENVMON_ADDR, reg);
    273 		sc->sc_reg[reg] = envmon_read(sc, ENVMON_DATA);
    274 	}
    275 	callout_init(&sc->sc_timer, CALLOUT_MPSAFE);
    276 	callout_reset(&sc->sc_timer, hz * ENVMON_TIMER, envmon_timeout, sc);
    277 #endif
    278 }
    279 
    280 /*
    281  * We only add the front panel temperature here.
    282  * Other U45 sensors are handled by the ADT7462 driver.
    283  */
    284 static void
    285 envmon_pic_attach(struct envmon_softc *sc, int node)
    286 {
    287 	int32_t vers;
    288 
    289 	vers = prom_getpropint(node, "version", 0);
    290 	aprint_normal(": pic16f747 (ver. %d)\n", vers);
    291 
    292 	if (!strcmp(machine_model, "SUNW,A70")) {
    293 		sc->sc_machine_type = ENVMON_MACHINE_U45;
    294 	} else if (!strcmp(machine_model, "SUNW,Ultra-25")) {
    295 		sc->sc_machine_type = ENVMON_MACHINE_U25;
    296 	} else {
    297 		aprint_error_dev(sc->sc_dev, "unsupported machine model\n");
    298 		return;
    299 	}
    300 
    301 	sc->sc_sme->sme_refresh = envmon_pic_refresh;
    302 	sc->sc_sme->sme_get_limits = envmon_pic_get_limits;
    303 	sc->sc_sme->sme_set_limits = envmon_pic_set_limits;
    304 
    305 	strlcpy(sc->sc_sensor[0].desc, "Front_panel",
    306 	    sizeof(sc->sc_sensor[0].desc));
    307 	sc->sc_sensor[0].units = ENVSYS_STEMP;
    308 	sc->sc_sensor[0].state = ENVSYS_SINVALID;
    309 	sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY | ENVSYS_FMONLIMITS |
    310 	    ENVSYS_FMONCRITICAL;
    311 	sc->sc_temp[0].reg = ENVMON_PIC_TEMP_FP;
    312 	sc->sc_temp[0].wmin = ENVMON_PIC_WMIN_TEMP;
    313 	sc->sc_temp[0].wmax = ENVMON_PIC_WMAX_TEMP;
    314 	sc->sc_temp[0].cmax = ENVMON_PIC_CRIT_TEMP;
    315 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[0])) {
    316 		sysmon_envsys_destroy(sc->sc_sme);
    317 		sc->sc_sme = NULL;
    318 		aprint_error_dev(sc->sc_dev,
    319 		    "unable to attach sensor to sysmon\n");
    320 		return;
    321 	}
    322 
    323 	if (sysmon_envsys_register(sc->sc_sme)) {
    324 		aprint_error_dev(sc->sc_dev,
    325 		    "unable to register with sysmon\n");
    326 		sysmon_envsys_destroy(sc->sc_sme);
    327 		sc->sc_sme = NULL;
    328 		return;
    329 	}
    330 
    331 	return;
    332 }
    333 
    334 static void
    335 envmon_epic_attach(struct envmon_softc *sc, int node)
    336 {
    337 	const char *vers;
    338 	int cmin, i, j;
    339 
    340 	vers = prom_getpropstring(node, "version");
    341 	aprint_normal(": epic (ver. %s)\n", vers);
    342 
    343 	/* Fans */
    344 	if (!strcmp(machine_model, "SUNW,Sun-Fire-V245")) {
    345 		sc->sc_machine_type = ENVMON_MACHINE_V245;
    346 		sc->sc_nfans = 6;
    347 		cmin = ENVMON_EPC_CRIT_SPEED_V245;
    348 	} else if (!strcmp(machine_model, "SUNW,Sun-Fire-V215")) {
    349 		sc->sc_machine_type = ENVMON_MACHINE_V215;
    350 		sc->sc_nfans = 12;
    351 		cmin = ENVMON_EPC_CRIT_SPEED_V215;
    352 	} else {
    353 		aprint_error_dev(sc->sc_dev, "unsupported machine model\n");
    354 		return;
    355 	}
    356 
    357 	sc->sc_sme->sme_refresh = envmon_epic_refresh;
    358 	sc->sc_sme->sme_get_limits = envmon_epic_get_limits;
    359 	sc->sc_sme->sme_set_limits = envmon_epic_set_limits;
    360 
    361 	for (i = 0; i < sc->sc_nfans; i++) {
    362 		strlcpy(sc->sc_sensor[i].desc, envmon_epic_fans_table[i].desc,
    363 		    sizeof(sc->sc_sensor[i].desc));
    364 		sc->sc_sensor[i].units = ENVSYS_SFANRPM;
    365 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
    366 		sc->sc_sensor[i].flags = ENVSYS_FMONLIMITS |
    367 		    ENVSYS_FMONCRITICAL;
    368 		sc->sc_fan[i].reg = envmon_epic_fans_table[i].reg;
    369 		sc->sc_fan[i].cmin = cmin;
    370 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
    371 		    &sc->sc_sensor[i])) {
    372 			sysmon_envsys_destroy(sc->sc_sme);
    373 			sc->sc_sme = NULL;
    374 			aprint_error_dev(sc->sc_dev,
    375 			    "unable to attach sensor to sysmon\n");
    376 			goto leds;
    377 		}
    378 	}
    379 
    380 	/* Fan fault */
    381 	i = sc->sc_nfans;
    382 	strlcpy(sc->sc_sensor[i].desc, "fan fault",
    383 	    sizeof(sc->sc_sensor[i].desc));
    384 	sc->sc_sensor[i].units = ENVSYS_INTEGER;
    385 	sc->sc_sensor[i].state = ENVSYS_SINVALID;
    386 	sc->sc_sensor[i].flags = ENVSYS_FMONCRITICAL;
    387 	if (sysmon_envsys_sensor_attach(
    388 	    sc->sc_sme, &sc->sc_sensor[i])) {
    389 		aprint_error_dev(sc->sc_dev,
    390 		    "unable to attach fan fault at sysmon\n");
    391 		goto leds;
    392 	}
    393 	i++;
    394 
    395 	/* Temperature */
    396 	strlcpy(sc->sc_sensor[i].desc, "FIOB.T_AMB",
    397 	    sizeof(sc->sc_sensor[i].desc));
    398 	sc->sc_sensor[i].units = ENVSYS_STEMP;
    399 	sc->sc_sensor[i].state = ENVSYS_SINVALID;
    400 	sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY | ENVSYS_FMONLIMITS |
    401 	    ENVSYS_FMONCRITICAL;
    402 	sc->sc_temp[0].reg = ENVMON_EPC_TEMP_FP;
    403 	sc->sc_temp[0].wmin = ENVMON_EPC_WMIN_TEMP;
    404 	sc->sc_temp[0].wmax = ENVMON_EPC_WMAX_TEMP;
    405 	sc->sc_temp[0].cmax = ENVMON_EPC_CRIT_TEMP;
    406 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) {
    407 		sysmon_envsys_destroy(sc->sc_sme);
    408 		sc->sc_sme = NULL;
    409 		aprint_error_dev(sc->sc_dev,
    410 			    "unable to attach sensor to sysmon\n");
    411 		goto leds;
    412 	}
    413 	i++;
    414 
    415 	/* Indicators */
    416 	for (j = 0; j < ENVMON_EPC_MAX_INDS; j++, i++) {
    417 		strlcpy(sc->sc_sensor[i].desc, envmon_epic_inds_table[j].desc,
    418 		    sizeof(sc->sc_sensor[i].desc));
    419 		sc->sc_sensor[i].units = ENVSYS_INDICATOR;
    420 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
    421 		sc->sc_ind[j].reg = envmon_epic_inds_table[j].reg;
    422 		sc->sc_ind[j].set = envmon_epic_inds_table[j].set;
    423 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
    424 		    &sc->sc_sensor[i])) {
    425 			sysmon_envsys_destroy(sc->sc_sme);
    426 			sc->sc_sme = NULL;
    427 			aprint_error_dev(sc->sc_dev,
    428 			    "unable to attach sensor to sysmon\n");
    429 			goto leds;
    430 		}
    431 	}
    432 
    433 	if (sysmon_envsys_register(sc->sc_sme)) {
    434 		aprint_error_dev(sc->sc_dev,
    435 		    "unable to register with sysmon\n");
    436 		sysmon_envsys_destroy(sc->sc_sme);
    437 		sc->sc_sme = NULL;
    438 	}
    439 
    440 leds:
    441 	/* LED's */
    442 	for (i = 0; i < ENVMON_EPC_MAX_LEDS; i++) {
    443 		sc->sc_led[i].cookie = sc;
    444 		sc->sc_led[i].reg = envmon_epic_led_table[i].reg;
    445 		sc->sc_led[i].v_on = envmon_epic_led_table[i].v_on;
    446 		led_attach(envmon_epic_led_table[i].desc,
    447 		    &sc->sc_led[i], envmon_epic_get_led, envmon_epic_set_led);
    448 	}
    449 
    450 	return;
    451 }
    452 
    453 void
    454 envmon_pic_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    455 {
    456 	struct envmon_softc *sc = sme->sme_cookie;
    457 
    458 	envmon_refresh_temp(sc, edata);
    459 }
    460 
    461 void
    462 envmon_epic_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    463 {
    464 	struct envmon_softc *sc = sme->sme_cookie;
    465 
    466 	if (edata->sensor < sc->sc_nfans)
    467 		envmon_epic_refresh_fan(sc, edata);
    468 	else if (edata->sensor < sc->sc_nfans + ENVMON_EPC_MAX_FAULTS)
    469 		envmon_epic_refresh_fault(sc, edata);
    470 	else if (edata->sensor <
    471 	    sc->sc_nfans + ENVMON_EPC_MAX_FAULTS + ENVMON_EPC_MAX_TEMPS)
    472 		envmon_refresh_temp(sc, edata);
    473 	else
    474 		envmon_epic_refresh_ind(sc, edata);
    475 
    476 	return;
    477 }
    478 
    479 void
    480 envmon_epic_refresh_fan(struct envmon_softc *sc, envsys_data_t *edata)
    481 {
    482 	uint8_t val;
    483 	int div;
    484 
    485 	if (sc->sc_machine_type == ENVMON_MACHINE_V245)
    486 		div = ENVMON_EPC_DIV_V245;
    487 	else
    488 		div = ENVMON_EPC_DIV_V215;
    489 
    490 	envmon_write(sc, ENVMON_ADDR, sc->sc_fan[edata->sensor].reg);
    491 	val = envmon_read(sc, ENVMON_DATA);
    492 	if (val == 0xff || val == 0x0)
    493 		edata->value_cur = 0;
    494 	else {
    495 		edata->value_cur = ENVMON_EPC_VAL_TO_SPEED(div, val);
    496 	}
    497 	if (edata->value_cur < sc->sc_fan[edata->sensor].cmin)
    498 		edata->state = ENVSYS_SCRITICAL;
    499 	else
    500 		edata->state = ENVSYS_SVALID;
    501 }
    502 
    503 void
    504 envmon_epic_refresh_fault(struct envmon_softc *sc, envsys_data_t *edata)
    505 {
    506 	uint8_t val;
    507 	int32_t which, total;
    508 	int i, j;
    509 
    510 	envmon_write(sc, ENVMON_ADDR, ENVMON_EPC_FAN_FLT);
    511 	val = envmon_read(sc, ENVMON_DATA);
    512 
    513 	total = 0;
    514 	if (val == ENVMON_EPC_FAN_FLT_NONE) {
    515 		edata->state = ENVSYS_SVALID;
    516 	} else {
    517 		edata->state = ENVSYS_SCRITICAL;
    518 		for (i = 0; i < ENVMON_EPC_NUM_FAN_FLTS; i++) {
    519 			if (!(val & __BIT(i))) {
    520 				which = 1;
    521 				for (j = 0; j < i; j++)
    522 					which *= 10;
    523 				total += which;
    524 			}
    525 		}
    526 	}
    527 	edata->value_cur = total;
    528 }
    529 
    530 void
    531 envmon_refresh_temp(struct envmon_softc *sc, envsys_data_t *edata)
    532 {
    533 	uint8_t val;
    534 
    535 	/* We currently only have 1 temperature sensor */
    536 	envmon_write(sc, ENVMON_ADDR, sc->sc_temp[0].reg);
    537 	val = envmon_read(sc, ENVMON_DATA);
    538 	edata->value_cur = ENVMON_TEMP_BASE + val * 1000000;
    539 	if (edata->value_cur > sc->sc_temp[0].cmax)
    540 		edata->state = ENVSYS_SCRITICAL;
    541 	else
    542 		edata->state = ENVSYS_SVALID;
    543 }
    544 
    545 void
    546 envmon_epic_refresh_ind(struct envmon_softc *sc, envsys_data_t *edata)
    547 {
    548 	int ind = edata->sensor -
    549 	    sc->sc_nfans - ENVMON_EPC_MAX_TEMPS - ENVMON_EPC_MAX_FAULTS;
    550 	uint8_t val;
    551 
    552 	envmon_write(sc, ENVMON_ADDR, sc->sc_ind[ind].reg);
    553 	val = envmon_read(sc, ENVMON_DATA);
    554 
    555 	if (val == sc->sc_ind[ind].set)
    556 		edata->value_cur = TRUE;
    557 	else
    558 		edata->value_cur = FALSE;
    559 	edata->state = ENVSYS_SVALID;
    560 }
    561 
    562 void
    563 envmon_pic_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    564     sysmon_envsys_lim_t *limits, uint32_t *props)
    565 {
    566 	struct envmon_softc *sc = sme->sme_cookie;
    567 
    568 	envmon_get_temp_limits(sc, edata, limits, props);
    569 }
    570 
    571 void
    572 envmon_pic_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    573     sysmon_envsys_lim_t *limits, uint32_t *props)
    574 {
    575 	struct envmon_softc *sc = sme->sme_cookie;
    576 
    577 	envmon_set_temp_limits(sc, edata, limits, props);
    578 }
    579 
    580 void
    581 envmon_epic_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    582     sysmon_envsys_lim_t *limits, uint32_t *props)
    583 {
    584 	struct envmon_softc *sc = sme->sme_cookie;
    585 
    586 	if (edata->sensor < sc->sc_nfans)
    587 		envmon_epic_get_fan_limits(sc, edata, limits, props);
    588 	else if (edata->sensor < sc->sc_nfans + ENVMON_EPC_MAX_TEMPS)
    589 		envmon_get_temp_limits(sc, edata, limits, props);
    590 }
    591 
    592 void
    593 envmon_epic_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata,
    594     sysmon_envsys_lim_t *limits, uint32_t *props)
    595 {
    596 	struct envmon_softc *sc = sme->sme_cookie;
    597 
    598 	if (edata->sensor < sc->sc_nfans)
    599 		envmon_epic_set_fan_limits(sc, edata, limits, props);
    600 	else if (edata->sensor < sc->sc_nfans + ENVMON_EPC_MAX_TEMPS)
    601 		envmon_set_temp_limits(sc, edata, limits, props);
    602 }
    603 
    604 static void
    605 envmon_epic_get_fan_limits(struct envmon_softc *sc,
    606     envsys_data_t *edata, sysmon_envsys_lim_t *limits, uint32_t *props)
    607 {
    608 
    609 	*props = PROP_CRITMIN;
    610 	limits->sel_critmin = sc->sc_fan[edata->sensor].cmin;
    611 }
    612 
    613 static void
    614 envmon_get_temp_limits(struct envmon_softc *sc,
    615     envsys_data_t *edata, sysmon_envsys_lim_t *limits, uint32_t *props)
    616 {
    617 
    618 	*props = PROP_WARNMIN | PROP_WARNMAX | PROP_CRITMAX;
    619 	limits->sel_warnmin = sc->sc_temp[0].wmin;
    620 	limits->sel_warnmax = sc->sc_temp[0].wmax;
    621 	limits->sel_critmax = sc->sc_temp[0].cmax;
    622 }
    623 
    624 static void
    625 envmon_epic_set_fan_limits(struct envmon_softc *sc,
    626     envsys_data_t *edata, sysmon_envsys_lim_t *limits, uint32_t *props)
    627 {
    628 
    629 	if (limits == NULL || *props & PROP_CRITMIN) {
    630 		if (limits == NULL)	/* Restore defaults */
    631 			switch (sc->sc_machine_type) {
    632 				case ENVMON_MACHINE_V215:
    633 					sc->sc_fan[edata->sensor].cmin =
    634 					    ENVMON_EPC_CRIT_SPEED_V215;
    635 					break;
    636 				case ENVMON_MACHINE_V245:
    637 					sc->sc_fan[edata->sensor].cmin =
    638 					    ENVMON_EPC_CRIT_SPEED_V245;
    639 					break;
    640 			}
    641 		else
    642 			sc->sc_fan[edata->sensor].cmin = limits->sel_critmin;
    643 	}
    644 }
    645 
    646 static void
    647 envmon_set_temp_limits(struct envmon_softc *sc,
    648     envsys_data_t *edata, sysmon_envsys_lim_t *limits, uint32_t *props)
    649 {
    650 
    651 	if (limits == NULL || *props & PROP_CRITMAX) {
    652 		if (limits == NULL)	/* Restore defaults */
    653 			switch (sc->sc_machine_type) {
    654 				case ENVMON_MACHINE_U25:
    655 				case ENVMON_MACHINE_U45:
    656 					sc->sc_temp[0].cmax =
    657 					    ENVMON_PIC_CRIT_TEMP;
    658 					break;
    659 				case ENVMON_MACHINE_V215:
    660 				case ENVMON_MACHINE_V245:
    661 					sc->sc_temp[0].cmax =
    662 					    ENVMON_EPC_CRIT_TEMP;
    663 					break;
    664 			}
    665 		else
    666 			sc->sc_temp[edata->sensor].cmax = limits->sel_critmax;
    667 	}
    668 
    669 	if (limits == NULL || *props & PROP_WARNMAX) {
    670 		if (limits == NULL)	/* Restore defaults */
    671 			switch (sc->sc_machine_type) {
    672 				case ENVMON_MACHINE_U25:
    673 				case ENVMON_MACHINE_U45:
    674 					sc->sc_temp[0].wmax =
    675 					    ENVMON_PIC_WMAX_TEMP;
    676 					break;
    677 				case ENVMON_MACHINE_V215:
    678 				case ENVMON_MACHINE_V245:
    679 					sc->sc_temp[0].wmax =
    680 					    ENVMON_EPC_WMAX_TEMP;
    681 					break;
    682 			}
    683 		else
    684 			sc->sc_temp[edata->sensor].wmax = limits->sel_warnmax;
    685 	}
    686 
    687 	if (limits == NULL || *props & PROP_WARNMIN) {
    688 		if (limits == NULL)	/* Restore defaults */
    689 			switch (sc->sc_machine_type) {
    690 				case ENVMON_MACHINE_U25:
    691 				case ENVMON_MACHINE_U45:
    692 					sc->sc_temp[0].wmin =
    693 					    ENVMON_PIC_WMIN_TEMP;
    694 					break;
    695 				case ENVMON_MACHINE_V215:
    696 				case ENVMON_MACHINE_V245:
    697 					sc->sc_temp[0].wmin =
    698 					    ENVMON_EPC_WMIN_TEMP;
    699 					break;
    700 			}
    701 		else
    702 			sc->sc_temp[edata->sensor].wmin = limits->sel_warnmin;
    703 	}
    704 
    705 }
    706 
    707 int
    708 envmon_epic_get_led(void *cookie)
    709 {
    710 	struct envmon_sc_led *l = cookie;
    711 	struct envmon_softc *sc = l->cookie;
    712 	uint8_t val;
    713 
    714 	envmon_write(sc, ENVMON_ADDR, l->reg);
    715 	val = envmon_read(sc, ENVMON_DATA);
    716 	return ((val & l->v_on) == l->v_on);
    717 }
    718 
    719 void
    720 envmon_epic_set_led(void *cookie, int val)
    721 {
    722 	struct envmon_sc_led *l = cookie;
    723 	struct envmon_softc *sc = l->cookie;
    724 	uint8_t oldval, newval;
    725 
    726 	envmon_write(sc, ENVMON_ADDR, l->reg);
    727 	oldval = envmon_read(sc, ENVMON_DATA);
    728 	newval = oldval & ~(l->v_on);
    729 	newval |= val ? l->v_on : 0x0;
    730 	if (newval != oldval) {
    731 		envmon_write(sc, ENVMON_EPC_LED_MASK, l->v_on);
    732 		envmon_write(sc, ENVMON_ADDR, l->reg);
    733 		delay(10000);
    734 		envmon_write(sc, ENVMON_DATA, newval);
    735 	}
    736 }
    737 
    738 #ifdef ENVMON_DEBUG
    739 static void
    740 envmon_timeout(void *v)
    741 {
    742 	struct envmon_softc *sc = v;
    743 	uint8_t reg, val;
    744 
    745 	for (reg = 0; reg < ENVMON_MAX_REG; reg++) {
    746 		envmon_write(sc, ENVMON_ADDR, reg);
    747 		val = envmon_read(sc, ENVMON_DATA);
    748 		if (val != sc->sc_reg[reg]) {
    749 			printf("%s: reg 0x%02x: 0x%02x > 0x%02x\n",
    750 			    device_xname(sc->sc_dev), reg,
    751 			    sc->sc_reg[reg], val);
    752 			sc->sc_reg[reg] = val;
    753 		}
    754 	}
    755 	callout_reset(&sc->sc_timer, hz * ENVMON_TIMER, envmon_timeout, sc);
    756 }
    757 #endif
    758