xref: /openbsd/sys/arch/sparc64/dev/lom.c (revision a4f1f624)
1*a4f1f624Skettenis /*	$OpenBSD: lom.c,v 1.17 2009/10/31 19:13:37 kettenis Exp $	*/
22613757eSkettenis /*
35f2dcfb1Skettenis  * Copyright (c) 2009 Mark Kettenis
42613757eSkettenis  *
52613757eSkettenis  * Permission to use, copy, modify, and distribute this software for any
62613757eSkettenis  * purpose with or without fee is hereby granted, provided that the above
72613757eSkettenis  * copyright notice and this permission notice appear in all copies.
82613757eSkettenis  *
92613757eSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102613757eSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112613757eSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122613757eSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132613757eSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142613757eSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152613757eSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162613757eSkettenis  */
172613757eSkettenis 
182613757eSkettenis #include <sys/param.h>
192613757eSkettenis #include <sys/device.h>
206801a748Skettenis #include <sys/kernel.h>
21fc69c4e1Skettenis #include <sys/proc.h>
222613757eSkettenis #include <sys/sensors.h>
232613757eSkettenis #include <sys/systm.h>
24877407b4Skettenis #include <sys/timeout.h>
252613757eSkettenis 
262613757eSkettenis #include <machine/autoconf.h>
272613757eSkettenis #include <machine/openfirm.h>
282613757eSkettenis 
292613757eSkettenis #include <sparc64/dev/ebusreg.h>
302613757eSkettenis #include <sparc64/dev/ebusvar.h>
312613757eSkettenis 
322613757eSkettenis /*
3313d051d6Skettenis  * LOMlite is a so far unidentified microcontroller.
3413d051d6Skettenis  */
3513d051d6Skettenis #define LOM1_STATUS		0x00	/* R */
3613d051d6Skettenis #define  LOM1_STATUS_BUSY	0x80
3713d051d6Skettenis #define LOM1_CMD		0x00	/* W */
3813d051d6Skettenis #define LOM1_DATA		0x01	/* R/W */
3913d051d6Skettenis 
4013d051d6Skettenis /*
4113d051d6Skettenis  * LOMlite2 is implemented as a H8/3437 microcontroller which has its
422613757eSkettenis  * on-chip host interface hooked up to EBus.
432613757eSkettenis  */
4413d051d6Skettenis #define LOM2_DATA		0x00	/* R/W */
4513d051d6Skettenis #define LOM2_CMD		0x01	/* W */
4613d051d6Skettenis #define LOM2_STATUS		0x01	/* R */
4713d051d6Skettenis #define  LOM2_STATUS_OBF	0x01	/* Output Buffer Full */
4813d051d6Skettenis #define  LOM2_STATUS_IBF	0x02	/* Input Buffer Full  */
492613757eSkettenis 
505f2dcfb1Skettenis #define LOM_IDX_CMD		0x00
515f2dcfb1Skettenis #define  LOM_IDX_CMD_GENERIC	0x00
525f2dcfb1Skettenis #define  LOM_IDX_CMD_TEMP	0x04
5302222f5cSkettenis #define  LOM_IDX_CMD_FAN	0x05
545f2dcfb1Skettenis 
552613757eSkettenis #define LOM_IDX_FW_REV		0x01	/* Firmware revision  */
562613757eSkettenis 
5702222f5cSkettenis #define LOM_IDX_FAN1		0x04	/* Fan speed */
5802222f5cSkettenis #define LOM_IDX_FAN2		0x05
5902222f5cSkettenis #define LOM_IDX_FAN3		0x06
6002222f5cSkettenis #define LOM_IDX_FAN4		0x07
6178eeb547Skettenis #define LOM_IDX_PSU1		0x08	/* PSU status */
6278eeb547Skettenis #define LOM_IDX_PSU2		0x09
6378eeb547Skettenis #define LOM_IDX_PSU3		0x0a
6478eeb547Skettenis #define  LOM_PSU_INPUTA		0x01
6578eeb547Skettenis #define  LOM_PSU_INPUTB		0x02
6678eeb547Skettenis #define  LOM_PSU_OUTPUT		0x04
6778eeb547Skettenis #define  LOM_PSU_PRESENT	0x08
6878eeb547Skettenis #define  LOM_PSU_STANDBY	0x10
6902222f5cSkettenis 
702613757eSkettenis #define LOM_IDX_TEMP1		0x18	/* Temperature */
7102222f5cSkettenis #define LOM_IDX_TEMP2		0x19
7202222f5cSkettenis #define LOM_IDX_TEMP3		0x1a
7302222f5cSkettenis #define LOM_IDX_TEMP4		0x1b
7402222f5cSkettenis #define LOM_IDX_TEMP5		0x1c
7502222f5cSkettenis #define LOM_IDX_TEMP6		0x1d
7602222f5cSkettenis #define LOM_IDX_TEMP7		0x1e
7702222f5cSkettenis #define LOM_IDX_TEMP8		0x1f
782613757eSkettenis 
795f2dcfb1Skettenis #define LOM_IDX_LED1		0x25
805f2dcfb1Skettenis 
815f2dcfb1Skettenis #define LOM_IDX_ALARM		0x30
82877407b4Skettenis #define LOM_IDX_WDOG_CTL	0x31
83877407b4Skettenis #define  LOM_WDOG_ENABLE	0x01
84877407b4Skettenis #define  LOM_WDOG_RESET		0x02
85877407b4Skettenis #define  LOM_WDOG_AL3_WDOG	0x04
86877407b4Skettenis #define  LOM_WDOG_AL3_FANPSU	0x08
87877407b4Skettenis #define LOM_IDX_WDOG_TIME	0x32
8813d051d6Skettenis #define  LOM_WDOG_TIME_MAX	126
895f2dcfb1Skettenis 
9013d051d6Skettenis #define LOM1_IDX_HOSTNAME1	0x33
9113d051d6Skettenis #define LOM1_IDX_HOSTNAME2	0x34
9213d051d6Skettenis #define LOM1_IDX_HOSTNAME3	0x35
9313d051d6Skettenis #define LOM1_IDX_HOSTNAME4	0x36
9413d051d6Skettenis #define LOM1_IDX_HOSTNAME5	0x37
9513d051d6Skettenis #define LOM1_IDX_HOSTNAME6	0x38
9613d051d6Skettenis #define LOM1_IDX_HOSTNAME7	0x39
9713d051d6Skettenis #define LOM1_IDX_HOSTNAME8	0x3a
9813d051d6Skettenis #define LOM1_IDX_HOSTNAME9	0x3b
9913d051d6Skettenis #define LOM1_IDX_HOSTNAME10	0x3c
10013d051d6Skettenis #define LOM1_IDX_HOSTNAME11	0x3d
10113d051d6Skettenis #define LOM1_IDX_HOSTNAME12	0x3e
10213d051d6Skettenis 
10313d051d6Skettenis #define LOM2_IDX_HOSTNAMELEN	0x38
10413d051d6Skettenis #define LOM2_IDX_HOSTNAME	0x39
1056801a748Skettenis 
10602222f5cSkettenis #define LOM_IDX_CONFIG		0x5d
10702222f5cSkettenis #define LOM_IDX_FAN1_CAL	0x5e
10802222f5cSkettenis #define LOM_IDX_FAN2_CAL	0x5f
10902222f5cSkettenis #define LOM_IDX_FAN3_CAL	0x60
11002222f5cSkettenis #define LOM_IDX_FAN4_CAL	0x61
11102222f5cSkettenis #define LOM_IDX_FAN1_LOW	0x62
11202222f5cSkettenis #define LOM_IDX_FAN2_LOW	0x63
11302222f5cSkettenis #define LOM_IDX_FAN3_LOW	0x64
11402222f5cSkettenis #define LOM_IDX_FAN4_LOW	0x65
11502222f5cSkettenis 
11602222f5cSkettenis #define LOM_IDX_CONFIG2		0x66
11702222f5cSkettenis #define LOM_IDX_CONFIG3		0x67
11802222f5cSkettenis 
1192613757eSkettenis #define LOM_IDX_PROBE55		0x7e	/* Always returns 0x55 */
1202613757eSkettenis #define LOM_IDX_PROBEAA		0x7f	/* Always returns 0xaa */
1212613757eSkettenis 
1225e80de5eSkettenis #define LOM_IDX_WRITE		0x80
1235e80de5eSkettenis 
1245f2dcfb1Skettenis #define LOM_IDX4_TEMP_NAME_START	0x40
1255f2dcfb1Skettenis #define LOM_IDX4_TEMP_NAME_END		0xff
1265f2dcfb1Skettenis 
12702222f5cSkettenis #define LOM_IDX5_FAN_NAME_START		0x40
12802222f5cSkettenis #define LOM_IDX5_FAN_NAME_END		0xff
12902222f5cSkettenis 
13002222f5cSkettenis #define LOM_MAX_FAN	4
13102222f5cSkettenis #define LOM_MAX_PSU	3
13202222f5cSkettenis #define LOM_MAX_TEMP	8
13302222f5cSkettenis 
1345e80de5eSkettenis struct lom_cmd {
1355e80de5eSkettenis 	uint8_t			lc_cmd;
1365e80de5eSkettenis 	uint8_t			lc_data;
1375e80de5eSkettenis 
1385e80de5eSkettenis 	TAILQ_ENTRY(lom_cmd)	lc_next;
1395e80de5eSkettenis };
1405e80de5eSkettenis 
1412613757eSkettenis struct lom_softc {
1422613757eSkettenis 	struct device		sc_dev;
1432613757eSkettenis 	bus_space_tag_t		sc_iot;
1442613757eSkettenis 	bus_space_handle_t	sc_ioh;
1452613757eSkettenis 
14613d051d6Skettenis 	int			sc_type;
14713d051d6Skettenis #define LOM_LOMLITE		0
14813d051d6Skettenis #define LOM_LOMLITE2		2
1495f2dcfb1Skettenis 	int			sc_space;
1505f2dcfb1Skettenis 
15102222f5cSkettenis 	struct ksensor		sc_fan[LOM_MAX_FAN];
15278eeb547Skettenis 	struct ksensor		sc_psu[LOM_MAX_PSU];
15302222f5cSkettenis 	struct ksensor		sc_temp[LOM_MAX_TEMP];
1542613757eSkettenis 	struct ksensordev	sc_sensordev;
15502222f5cSkettenis 
15602222f5cSkettenis 	int			sc_num_fan;
15702222f5cSkettenis 	int			sc_num_psu;
15802222f5cSkettenis 	int			sc_num_temp;
15902222f5cSkettenis 
16002222f5cSkettenis 	uint8_t			sc_fan_cal[LOM_MAX_FAN];
16102222f5cSkettenis 	uint8_t			sc_fan_low[LOM_MAX_FAN];
1626801a748Skettenis 
1636801a748Skettenis 	char			sc_hostname[MAXHOSTNAMELEN];
164877407b4Skettenis 
165877407b4Skettenis 	struct timeout		sc_wdog_to;
166877407b4Skettenis 	int			sc_wdog_period;
1675223c73eSkettenis 	uint8_t			sc_wdog_ctl;
1685e80de5eSkettenis 	struct lom_cmd		sc_wdog_pat;
169fc69c4e1Skettenis 
1705e80de5eSkettenis 	TAILQ_HEAD(, lom_cmd)	sc_queue;
1715e80de5eSkettenis 	struct mutex		sc_queue_mtx;
172fc69c4e1Skettenis 	struct timeout		sc_state_to;
173fc69c4e1Skettenis 	int			sc_state;
174fc69c4e1Skettenis #define LOM_STATE_IDLE		0
1755e80de5eSkettenis #define LOM_STATE_CMD		1
1765e80de5eSkettenis #define LOM_STATE_DATA		2
1775e80de5eSkettenis 	int			sc_retry;
1782613757eSkettenis };
1792613757eSkettenis 
1802613757eSkettenis int	lom_match(struct device *, void *, void *);
1812613757eSkettenis void	lom_attach(struct device *, struct device *, void *);
1822613757eSkettenis 
1832613757eSkettenis struct cfattach lom_ca = {
1842613757eSkettenis 	sizeof(struct lom_softc), lom_match, lom_attach
1852613757eSkettenis };
1862613757eSkettenis 
1872613757eSkettenis struct cfdriver lom_cd = {
1882613757eSkettenis 	NULL, "lom", DV_DULL
1892613757eSkettenis };
1902613757eSkettenis 
1912613757eSkettenis int	lom_read(struct lom_softc *, uint8_t, uint8_t *);
1922613757eSkettenis int	lom_write(struct lom_softc *, uint8_t, uint8_t);
1935e80de5eSkettenis void	lom_queue_cmd(struct lom_softc *, struct lom_cmd *);
194*a4f1f624Skettenis void	lom_dequeue_cmd(struct lom_softc *, struct lom_cmd *);
19513d051d6Skettenis int	lom1_read(struct lom_softc *, uint8_t, uint8_t *);
19613d051d6Skettenis int	lom1_write(struct lom_softc *, uint8_t, uint8_t);
1975e80de5eSkettenis int	lom1_read_polled(struct lom_softc *, uint8_t, uint8_t *);
1985e80de5eSkettenis int	lom1_write_polled(struct lom_softc *, uint8_t, uint8_t);
1995e80de5eSkettenis void	lom1_queue_cmd(struct lom_softc *, struct lom_cmd *);
2005e80de5eSkettenis void	lom1_dequeue_cmd(struct lom_softc *, struct lom_cmd *);
2015e80de5eSkettenis void	lom1_process_queue(void *);
2025e80de5eSkettenis void	lom1_process_queue_locked(struct lom_softc *);
20313d051d6Skettenis int	lom2_read(struct lom_softc *, uint8_t, uint8_t *);
20413d051d6Skettenis int	lom2_write(struct lom_softc *, uint8_t, uint8_t);
2055e80de5eSkettenis void	lom2_queue_cmd(struct lom_softc *, struct lom_cmd *);
2062613757eSkettenis 
2075f2dcfb1Skettenis int	lom_init_desc(struct lom_softc *sc);
2082613757eSkettenis void	lom_refresh(void *);
20913d051d6Skettenis void	lom1_write_hostname(struct lom_softc *);
21013d051d6Skettenis void	lom2_write_hostname(struct lom_softc *);
2112613757eSkettenis 
212877407b4Skettenis void	lom_wdog_pat(void *);
213877407b4Skettenis int	lom_wdog_cb(void *, int);
214877407b4Skettenis 
2152613757eSkettenis int
2162613757eSkettenis lom_match(struct device *parent, void *match, void *aux)
2172613757eSkettenis {
2182613757eSkettenis 	struct ebus_attach_args *ea = aux;
2192613757eSkettenis 
220fc69c4e1Skettenis 	if (strcmp(ea->ea_name, "SUNW,lom") == 0 ||
221fc69c4e1Skettenis 	    strcmp(ea->ea_name, "SUNW,lomh") == 0)
2222613757eSkettenis 		return (1);
2232613757eSkettenis 
2242613757eSkettenis 	return (0);
2252613757eSkettenis }
2262613757eSkettenis 
2272613757eSkettenis void
2282613757eSkettenis lom_attach(struct device *parent, struct device *self, void *aux)
2292613757eSkettenis {
2302613757eSkettenis 	struct lom_softc *sc = (void *)self;
2312613757eSkettenis 	struct ebus_attach_args *ea = aux;
23202222f5cSkettenis 	uint8_t reg, fw_rev, config, config2, config3;
23313d051d6Skettenis 	uint8_t cal, low;
23402222f5cSkettenis 	int i;
2352613757eSkettenis 
23613d051d6Skettenis 	if (strcmp(ea->ea_name, "SUNW,lomh") == 0)
23713d051d6Skettenis 		sc->sc_type = LOM_LOMLITE2;
23813d051d6Skettenis 
239d151cb6cSkettenis 	if (ebus_bus_map(ea->ea_iotag, 0,
2402613757eSkettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
241d151cb6cSkettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
242d151cb6cSkettenis 		sc->sc_iot = ea->ea_iotag;
243d151cb6cSkettenis 	} else if (ebus_bus_map(ea->ea_memtag, 0,
244d151cb6cSkettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
245d151cb6cSkettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
246d151cb6cSkettenis 		sc->sc_iot = ea->ea_memtag;
247d151cb6cSkettenis 	} else {
2482613757eSkettenis 		printf(": can't map register space\n");
2492613757eSkettenis                 return;
2502613757eSkettenis 	}
2512613757eSkettenis 
25213d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2) {
25313d051d6Skettenis 		/* XXX Magic */
25413d051d6Skettenis 		bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);
25513d051d6Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, 3, 0xca);
256fc69c4e1Skettenis 
2575e80de5eSkettenis 		TAILQ_INIT(&sc->sc_queue);
2585e80de5eSkettenis 		mtx_init(&sc->sc_queue_mtx, IPL_BIO);
2595e80de5eSkettenis 		timeout_set(&sc->sc_state_to, lom1_process_queue, sc);
26013d051d6Skettenis 	}
26113d051d6Skettenis 
2622613757eSkettenis 	if (lom_read(sc, LOM_IDX_PROBE55, &reg) || reg != 0x55 ||
2632613757eSkettenis 	    lom_read(sc, LOM_IDX_PROBEAA, &reg) || reg != 0xaa ||
26402222f5cSkettenis 	    lom_read(sc, LOM_IDX_FW_REV, &fw_rev) ||
26513d051d6Skettenis 	    lom_read(sc, LOM_IDX_CONFIG, &config))
26602222f5cSkettenis 	{
2672613757eSkettenis 		printf(": not responding\n");
2682613757eSkettenis 		return;
2692613757eSkettenis 	}
2702613757eSkettenis 
27113d051d6Skettenis 	config2 = config3 = 0;
27213d051d6Skettenis 	if (sc->sc_type >= LOM_LOMLITE2) {
27313d051d6Skettenis 		lom_read(sc, LOM_IDX_CONFIG2, &config2);
27413d051d6Skettenis 		lom_read(sc, LOM_IDX_CONFIG3, &config3);
27513d051d6Skettenis 	}
27613d051d6Skettenis 
27702222f5cSkettenis 	sc->sc_num_fan = min((config >> 5) & 0x7, LOM_MAX_FAN);
27802222f5cSkettenis 	sc->sc_num_psu = min((config >> 3) & 0x3, LOM_MAX_PSU);
27902222f5cSkettenis 	sc->sc_num_temp = min((config2 >> 4) & 0xf, LOM_MAX_TEMP);
28002222f5cSkettenis 
28102222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
28202222f5cSkettenis 		if (lom_read(sc, LOM_IDX_FAN1_CAL + i, &cal) ||
28302222f5cSkettenis 		    lom_read(sc, LOM_IDX_FAN1_LOW + i, &low)) {
28402222f5cSkettenis 			printf(": can't read fan information\n");
28502222f5cSkettenis 			return;
28602222f5cSkettenis 		}
28702222f5cSkettenis 		sc->sc_fan_cal[i] = cal;
28802222f5cSkettenis 		sc->sc_fan_low[i] = low;
28902222f5cSkettenis 	}
29002222f5cSkettenis 
2912613757eSkettenis 	/* Initialize sensor data. */
2922613757eSkettenis 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
2932613757eSkettenis 	    sizeof(sc->sc_sensordev.xname));
29402222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
29502222f5cSkettenis 		sc->sc_fan[i].type = SENSOR_FANRPM;
29602222f5cSkettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_fan[i]);
29713d051d6Skettenis 		snprintf(sc->sc_fan[i].desc, sizeof(sc->sc_fan[i].desc),
29813d051d6Skettenis 		    "fan%d", i + 1);
29902222f5cSkettenis 	}
30078eeb547Skettenis 	for (i = 0; i < sc->sc_num_psu; i++) {
30178eeb547Skettenis 		sc->sc_psu[i].type = SENSOR_INDICATOR;
30278eeb547Skettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_psu[i]);
30378eeb547Skettenis 		snprintf(sc->sc_psu[i].desc, sizeof(sc->sc_psu[i].desc),
30478eeb547Skettenis 		    "PSU%d", i + 1);
30578eeb547Skettenis 	}
30602222f5cSkettenis 	for (i = 0; i < sc->sc_num_temp; i++) {
30702222f5cSkettenis 		sc->sc_temp[i].type = SENSOR_TEMP;
30802222f5cSkettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_temp[i]);
30902222f5cSkettenis 	}
3105f2dcfb1Skettenis 	if (lom_init_desc(sc)) {
3115f2dcfb1Skettenis 		printf(": can't read sensor names\n");
3125f2dcfb1Skettenis 		return;
3135f2dcfb1Skettenis 	}
3142613757eSkettenis 
3152613757eSkettenis 	if (sensor_task_register(sc, lom_refresh, 5) == NULL) {
3162613757eSkettenis 		printf(": unable to register update task\n");
3172613757eSkettenis 		return;
3182613757eSkettenis 	}
3192613757eSkettenis 
3202613757eSkettenis 	sensordev_install(&sc->sc_sensordev);
3212613757eSkettenis 
322877407b4Skettenis 	/*
323877407b4Skettenis 	 * We configure the watchdog to turn on the fault LED when the
324877407b4Skettenis 	 * watchdog timer expires.  We run our own timeout to pat it
325877407b4Skettenis 	 * such that this won't happen unless the kernel hangs.  When
326877407b4Skettenis 	 * the watchdog is explicitly configured using sysctl(8), we
327877407b4Skettenis 	 * reconfigure it to reset the machine and let the standard
328877407b4Skettenis 	 * watchdog(4) machinery take over.
329877407b4Skettenis 	 */
330877407b4Skettenis 	lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
3315223c73eSkettenis 	lom_read(sc, LOM_IDX_WDOG_CTL, &sc->sc_wdog_ctl);
3325223c73eSkettenis 	sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
3335223c73eSkettenis 	sc->sc_wdog_ctl |= LOM_WDOG_ENABLE;
3345223c73eSkettenis 	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
335877407b4Skettenis 	timeout_set(&sc->sc_wdog_to, lom_wdog_pat, sc);
336877407b4Skettenis 	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
337877407b4Skettenis 
338877407b4Skettenis 	wdog_register(sc, lom_wdog_cb);
339877407b4Skettenis 
340e6d8f4d1Skettenis 	printf(": %s rev %d.%d\n",
341e6d8f4d1Skettenis 	    sc->sc_type < LOM_LOMLITE2 ? "LOMlite" : "LOMlite2",
342e6d8f4d1Skettenis 	    fw_rev >> 4, fw_rev & 0x0f);
3432613757eSkettenis }
3442613757eSkettenis 
3452613757eSkettenis int
3462613757eSkettenis lom_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
3472613757eSkettenis {
34813d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
34913d051d6Skettenis 		return lom1_read(sc, reg, val);
35013d051d6Skettenis 	else
35113d051d6Skettenis 		return lom2_read(sc, reg, val);
3522613757eSkettenis }
3532613757eSkettenis 
3545f2dcfb1Skettenis int
3555f2dcfb1Skettenis lom_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
3565f2dcfb1Skettenis {
35713d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
35813d051d6Skettenis 		return lom1_write(sc, reg, val);
35913d051d6Skettenis 	else
36013d051d6Skettenis 		return lom2_write(sc, reg, val);
36113d051d6Skettenis }
36213d051d6Skettenis 
3635e80de5eSkettenis void
3645e80de5eSkettenis lom_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
3655e80de5eSkettenis {
3665e80de5eSkettenis 	if (sc->sc_type < LOM_LOMLITE2)
3675e80de5eSkettenis 		return lom1_queue_cmd(sc, lc);
3685e80de5eSkettenis 	else
3695e80de5eSkettenis 		return lom2_queue_cmd(sc, lc);
3705e80de5eSkettenis }
3715e80de5eSkettenis 
372*a4f1f624Skettenis void
373*a4f1f624Skettenis lom_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
374*a4f1f624Skettenis {
375*a4f1f624Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
376*a4f1f624Skettenis 		return lom1_dequeue_cmd(sc, lc);
377*a4f1f624Skettenis }
378*a4f1f624Skettenis 
37913d051d6Skettenis int
38013d051d6Skettenis lom1_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
38113d051d6Skettenis {
3825e80de5eSkettenis 	struct lom_cmd lc;
383fc69c4e1Skettenis 	int error;
384fc69c4e1Skettenis 
385fc69c4e1Skettenis 	if (cold)
386fc69c4e1Skettenis 		return lom1_read_polled(sc, reg, val);
387fc69c4e1Skettenis 
3885e80de5eSkettenis 	lc.lc_cmd = reg;
3895e80de5eSkettenis 	lc.lc_data = 0xff;
3905e80de5eSkettenis 	lom1_queue_cmd(sc, &lc);
391fc69c4e1Skettenis 
3925e80de5eSkettenis 	error = tsleep(&lc, PZERO, "lomrd", hz);
3935e80de5eSkettenis 	if (error)
3945e80de5eSkettenis 		lom1_dequeue_cmd(sc, &lc);
395fc69c4e1Skettenis 
3965e80de5eSkettenis 	*val = lc.lc_data;
3975e80de5eSkettenis 
3985e80de5eSkettenis 	return (error);
3995e80de5eSkettenis }
4005e80de5eSkettenis 
4015e80de5eSkettenis int
4025e80de5eSkettenis lom1_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
4035e80de5eSkettenis {
4045e80de5eSkettenis 	struct lom_cmd lc;
4055e80de5eSkettenis 	int error;
4065e80de5eSkettenis 
4075e80de5eSkettenis 	if (cold)
4085e80de5eSkettenis 		return lom1_write_polled(sc, reg, val);
4095e80de5eSkettenis 
4105e80de5eSkettenis 	lc.lc_cmd = reg | LOM_IDX_WRITE;
4115e80de5eSkettenis 	lc.lc_data = val;
4125e80de5eSkettenis 	lom1_queue_cmd(sc, &lc);
4135e80de5eSkettenis 
4148fe2a92fSkettenis 	error = tsleep(&lc, PZERO, "lomwr", 2 * hz);
4155e80de5eSkettenis 	if (error)
4165e80de5eSkettenis 		lom1_dequeue_cmd(sc, &lc);
417fc69c4e1Skettenis 
418fc69c4e1Skettenis 	return (error);
419fc69c4e1Skettenis }
420fc69c4e1Skettenis 
421fc69c4e1Skettenis int
422fc69c4e1Skettenis lom1_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
423fc69c4e1Skettenis {
42413d051d6Skettenis 	uint8_t str;
42513d051d6Skettenis 	int i;
42613d051d6Skettenis 
42713d051d6Skettenis 	/* Wait for input buffer to become available. */
4283c40421dSkettenis 	for (i = 30; i > 0; i--) {
42913d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4303c40421dSkettenis 		delay(1000);
43113d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
43213d051d6Skettenis 			break;
43313d051d6Skettenis 	}
43413d051d6Skettenis 	if (i == 0)
43513d051d6Skettenis 		return (ETIMEDOUT);
43613d051d6Skettenis 
43713d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
43813d051d6Skettenis 
43913d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
4403c40421dSkettenis 	for (i = 30; i > 0; i--) {
44113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4423c40421dSkettenis 		delay(1000);
44313d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
44413d051d6Skettenis 			break;
44513d051d6Skettenis 	}
44613d051d6Skettenis 	if (i == 0)
44713d051d6Skettenis 		return (ETIMEDOUT);
44813d051d6Skettenis 
44913d051d6Skettenis 	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
45013d051d6Skettenis 	return (0);
45113d051d6Skettenis }
45213d051d6Skettenis 
45313d051d6Skettenis int
4545e80de5eSkettenis lom1_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
45513d051d6Skettenis {
45613d051d6Skettenis 	uint8_t str;
45713d051d6Skettenis 	int i;
45813d051d6Skettenis 
45913d051d6Skettenis 	/* Wait for input buffer to become available. */
4603c40421dSkettenis 	for (i = 30; i > 0; i--) {
46113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4623c40421dSkettenis 		delay(1000);
46313d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
46413d051d6Skettenis 			break;
46513d051d6Skettenis 	}
46613d051d6Skettenis 	if (i == 0)
46713d051d6Skettenis 		return (ETIMEDOUT);
46813d051d6Skettenis 
4695e80de5eSkettenis 	reg |= LOM_IDX_WRITE;
4705e80de5eSkettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
47113d051d6Skettenis 
47213d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
4733c40421dSkettenis 	for (i = 30; i > 0; i--) {
47413d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4753c40421dSkettenis 		delay(1000);
47613d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
47713d051d6Skettenis 			break;
47813d051d6Skettenis 	}
47913d051d6Skettenis 	if (i == 0)
48013d051d6Skettenis 		return (ETIMEDOUT);
48113d051d6Skettenis 
48213d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, val);
48313d051d6Skettenis 
48413d051d6Skettenis 	return (0);
48513d051d6Skettenis }
48613d051d6Skettenis 
487fc69c4e1Skettenis void
4885e80de5eSkettenis lom1_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
4895e80de5eSkettenis {
4905e80de5eSkettenis 	mtx_enter(&sc->sc_queue_mtx);
4915e80de5eSkettenis 	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
4925e80de5eSkettenis 	if (sc->sc_state == LOM_STATE_IDLE) {
4935e80de5eSkettenis 		sc->sc_state = LOM_STATE_CMD;
4945e80de5eSkettenis 		lom1_process_queue_locked(sc);
4955e80de5eSkettenis 	}
4965e80de5eSkettenis 	mtx_leave(&sc->sc_queue_mtx);
4975e80de5eSkettenis }
4985e80de5eSkettenis 
4995e80de5eSkettenis void
5005e80de5eSkettenis lom1_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
5015e80de5eSkettenis {
5025e80de5eSkettenis 	struct lom_cmd *lcp;
5035e80de5eSkettenis 
5045e80de5eSkettenis 	mtx_enter(&sc->sc_queue_mtx);
5055e80de5eSkettenis 	TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
5065e80de5eSkettenis 		if (lcp == lc) {
5075e80de5eSkettenis 			TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
5085e80de5eSkettenis 			break;
5095e80de5eSkettenis 		}
5105e80de5eSkettenis 	}
5115e80de5eSkettenis 	mtx_leave(&sc->sc_queue_mtx);
5125e80de5eSkettenis }
5135e80de5eSkettenis 
5145e80de5eSkettenis void
5155e80de5eSkettenis lom1_process_queue(void *arg)
516fc69c4e1Skettenis {
517fc69c4e1Skettenis 	struct lom_softc *sc = arg;
5185e80de5eSkettenis 
5195e80de5eSkettenis 	mtx_enter(&sc->sc_queue_mtx);
5205e80de5eSkettenis 	lom1_process_queue_locked(sc);
5215e80de5eSkettenis 	mtx_leave(&sc->sc_queue_mtx);
5225e80de5eSkettenis }
5235e80de5eSkettenis 
5245e80de5eSkettenis void
5255e80de5eSkettenis lom1_process_queue_locked(struct lom_softc *sc)
5265e80de5eSkettenis {
5275e80de5eSkettenis 	struct lom_cmd *lc;
528fc69c4e1Skettenis 	uint8_t str;
529fc69c4e1Skettenis 
5305e80de5eSkettenis 	lc = TAILQ_FIRST(&sc->sc_queue);
5318fe2a92fSkettenis 	if (lc == NULL) {
5328fe2a92fSkettenis 		sc->sc_state = LOM_STATE_IDLE;
5338fe2a92fSkettenis 		return;
5348fe2a92fSkettenis 	}
5355e80de5eSkettenis 
536fc69c4e1Skettenis 	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
537fc69c4e1Skettenis 	if (str & LOM1_STATUS_BUSY) {
5388fe2a92fSkettenis 		if (sc->sc_retry++ < 30) {
5395e80de5eSkettenis 			timeout_add_msec(&sc->sc_state_to, 1);
540fc69c4e1Skettenis 			return;
541fc69c4e1Skettenis 		}
542fc69c4e1Skettenis 
5438fe2a92fSkettenis 		/*
5448fe2a92fSkettenis 		 * Looks like the microcontroller got wedged.  Unwedge
5458fe2a92fSkettenis 		 * it by writing this magic value.  Give it some time
5468fe2a92fSkettenis 		 * to recover.
5478fe2a92fSkettenis 		 */
5488fe2a92fSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, 0xac);
5498fe2a92fSkettenis 		timeout_add_msec(&sc->sc_state_to, 1000);
5508fe2a92fSkettenis 		sc->sc_state = LOM_STATE_CMD;
5518fe2a92fSkettenis 		return;
5528fe2a92fSkettenis 	}
5538fe2a92fSkettenis 
5545e80de5eSkettenis 	sc->sc_retry = 0;
5555e80de5eSkettenis 
5565e80de5eSkettenis 	if (sc->sc_state == LOM_STATE_CMD) {
5575e80de5eSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, lc->lc_cmd);
5585e80de5eSkettenis 		sc->sc_state = LOM_STATE_DATA;
5595e80de5eSkettenis 		timeout_add_msec(&sc->sc_state_to, 250);
560fc69c4e1Skettenis 		return;
561fc69c4e1Skettenis 	}
562fc69c4e1Skettenis 
5635e80de5eSkettenis 	KASSERT(sc->sc_state == LOM_STATE_DATA);
5645e80de5eSkettenis 	if ((lc->lc_cmd & LOM_IDX_WRITE) == 0)
5655e80de5eSkettenis 		lc->lc_data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
5665e80de5eSkettenis 	else
5675e80de5eSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, lc->lc_data);
5685e80de5eSkettenis 
5695e80de5eSkettenis 	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
5705e80de5eSkettenis 
5715e80de5eSkettenis 	wakeup(lc);
5725e80de5eSkettenis 
5735e80de5eSkettenis 	if (!TAILQ_EMPTY(&sc->sc_queue)) {
5745e80de5eSkettenis 		sc->sc_state = LOM_STATE_CMD;
5755e80de5eSkettenis 		timeout_add_msec(&sc->sc_state_to, 1);
5765e80de5eSkettenis 		return;
5775e80de5eSkettenis 	}
5785e80de5eSkettenis 
5795e80de5eSkettenis 	sc->sc_state = LOM_STATE_IDLE;
580fc69c4e1Skettenis }
581fc69c4e1Skettenis 
58213d051d6Skettenis int
58313d051d6Skettenis lom2_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
58413d051d6Skettenis {
5855f2dcfb1Skettenis 	uint8_t str;
5865f2dcfb1Skettenis 	int i;
5875f2dcfb1Skettenis 
5885f2dcfb1Skettenis 	/* Wait for input buffer to become available. */
5895f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
59013d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
5915f2dcfb1Skettenis 		delay(10);
59213d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
59313d051d6Skettenis 			break;
59413d051d6Skettenis 	}
59513d051d6Skettenis 	if (i == 0)
59613d051d6Skettenis 		return (ETIMEDOUT);
59713d051d6Skettenis 
59813d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
59913d051d6Skettenis 
60013d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
60113d051d6Skettenis 	for (i = 1000; i > 0; i--) {
60213d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
60313d051d6Skettenis 		delay(10);
60413d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
60513d051d6Skettenis 			break;
60613d051d6Skettenis 	}
60713d051d6Skettenis 	if (i == 0)
60813d051d6Skettenis 		return (ETIMEDOUT);
60913d051d6Skettenis 
61013d051d6Skettenis 	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
61113d051d6Skettenis 	return (0);
61213d051d6Skettenis }
61313d051d6Skettenis 
61413d051d6Skettenis int
61513d051d6Skettenis lom2_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
61613d051d6Skettenis {
61713d051d6Skettenis 	uint8_t str;
61813d051d6Skettenis 	int i;
61913d051d6Skettenis 
62013d051d6Skettenis 	/* Wait for input buffer to become available. */
62113d051d6Skettenis 	for (i = 1000; i > 0; i--) {
62213d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
62313d051d6Skettenis 		delay(10);
62413d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
6255f2dcfb1Skettenis 			break;
6265f2dcfb1Skettenis 	}
6275f2dcfb1Skettenis 	if (i == 0)
6285f2dcfb1Skettenis 		return (ETIMEDOUT);
6295f2dcfb1Skettenis 
6305f2dcfb1Skettenis 	if (sc->sc_space == LOM_IDX_CMD_GENERIC && reg != LOM_IDX_CMD)
6315f2dcfb1Skettenis 		reg |= 0x80;
6325f2dcfb1Skettenis 
63313d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
6345f2dcfb1Skettenis 
6355f2dcfb1Skettenis 	/* Wait until the microcontroller fills output buffer. */
6365f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
63713d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
6385f2dcfb1Skettenis 		delay(10);
63913d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
6405f2dcfb1Skettenis 			break;
6415f2dcfb1Skettenis 	}
6425f2dcfb1Skettenis 	if (i == 0)
6435f2dcfb1Skettenis 		return (ETIMEDOUT);
6445f2dcfb1Skettenis 
64513d051d6Skettenis 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
6465f2dcfb1Skettenis 
6475f2dcfb1Skettenis 	/* Wait for input buffer to become available. */
6485f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
64913d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
6505f2dcfb1Skettenis 		delay(10);
65113d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
6525f2dcfb1Skettenis 			break;
6535f2dcfb1Skettenis 	}
6545f2dcfb1Skettenis 	if (i == 0)
6555f2dcfb1Skettenis 		return (ETIMEDOUT);
6565f2dcfb1Skettenis 
65713d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA, val);
6585f2dcfb1Skettenis 
6595f2dcfb1Skettenis 	/* Wait until the microcontroller fills output buffer. */
6605f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
66113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
6625f2dcfb1Skettenis 		delay(10);
66313d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
6645f2dcfb1Skettenis 			break;
6655f2dcfb1Skettenis 	}
6665f2dcfb1Skettenis 	if (i == 0)
6675f2dcfb1Skettenis 		return (ETIMEDOUT);
6685f2dcfb1Skettenis 
66913d051d6Skettenis 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
6705f2dcfb1Skettenis 
6715f2dcfb1Skettenis 	/* If we switched spaces, remember the one we're in now. */
6725f2dcfb1Skettenis 	if (reg == LOM_IDX_CMD)
6735f2dcfb1Skettenis 		sc->sc_space = val;
6745f2dcfb1Skettenis 
6755f2dcfb1Skettenis 	return (0);
6765f2dcfb1Skettenis }
6775f2dcfb1Skettenis 
6785e80de5eSkettenis void
6795e80de5eSkettenis lom2_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
6805e80de5eSkettenis {
6815e80de5eSkettenis 	KASSERT(lc->lc_cmd & LOM_IDX_WRITE);
6825e80de5eSkettenis 	lom2_write(sc, lc->lc_cmd, lc->lc_data);
6835e80de5eSkettenis }
6845e80de5eSkettenis 
6855f2dcfb1Skettenis int
6865f2dcfb1Skettenis lom_init_desc(struct lom_softc *sc)
6875f2dcfb1Skettenis {
6885f2dcfb1Skettenis 	uint8_t val;
68902222f5cSkettenis 	int i, j, k;
69002222f5cSkettenis 	int error;
6915f2dcfb1Skettenis 
69213d051d6Skettenis 	/* LOMlite doesn't provide sensor descriptions. */
69313d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
69413d051d6Skettenis 		return (0);
69513d051d6Skettenis 
69602222f5cSkettenis 	/*
69702222f5cSkettenis 	 * Read temperature sensor names.
69802222f5cSkettenis 	 */
6995f2dcfb1Skettenis 	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_TEMP);
7005f2dcfb1Skettenis 	if (error)
7015f2dcfb1Skettenis 		return (error);
7025f2dcfb1Skettenis 
70302222f5cSkettenis 	i = 0;
70402222f5cSkettenis 	j = 0;
70502222f5cSkettenis 	k = LOM_IDX4_TEMP_NAME_START;
70602222f5cSkettenis 	while (k <= LOM_IDX4_TEMP_NAME_END) {
70702222f5cSkettenis 		error = lom_read(sc, k++, &val);
7085f2dcfb1Skettenis 		if (error)
7095f2dcfb1Skettenis 			goto fail;
7105f2dcfb1Skettenis 
7115f2dcfb1Skettenis 		if (val == 0xff)
7125f2dcfb1Skettenis 			break;
7135f2dcfb1Skettenis 
71413d051d6Skettenis 		if (j < sizeof (sc->sc_temp[i].desc) - 1)
71513d051d6Skettenis 			sc->sc_temp[i].desc[j++] = val;
71613d051d6Skettenis 
71702222f5cSkettenis 		if (val == '\0') {
7185f2dcfb1Skettenis 			i++;
71902222f5cSkettenis 			j = 0;
72002222f5cSkettenis 			if (i < sc->sc_num_temp)
72102222f5cSkettenis 				continue;
72202222f5cSkettenis 
72302222f5cSkettenis 			break;
72402222f5cSkettenis 		}
72502222f5cSkettenis 	}
72602222f5cSkettenis 
72702222f5cSkettenis 	/*
72802222f5cSkettenis 	 * Read fan names.
72902222f5cSkettenis 	 */
73002222f5cSkettenis 	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_FAN);
73102222f5cSkettenis 	if (error)
73202222f5cSkettenis 		return (error);
73302222f5cSkettenis 
73402222f5cSkettenis 	i = 0;
73502222f5cSkettenis 	j = 0;
73602222f5cSkettenis 	k = LOM_IDX5_FAN_NAME_START;
73702222f5cSkettenis 	while (k <= LOM_IDX5_FAN_NAME_END) {
73802222f5cSkettenis 		error = lom_read(sc, k++, &val);
73902222f5cSkettenis 		if (error)
74002222f5cSkettenis 			goto fail;
74102222f5cSkettenis 
74202222f5cSkettenis 		if (val == 0xff)
74302222f5cSkettenis 			break;
74402222f5cSkettenis 
74513d051d6Skettenis 		if (j < sizeof (sc->sc_fan[i].desc) - 1)
74613d051d6Skettenis 			sc->sc_fan[i].desc[j++] = val;
74713d051d6Skettenis 
74802222f5cSkettenis 		if (val == '\0') {
74902222f5cSkettenis 			i++;
75002222f5cSkettenis 			j = 0;
75102222f5cSkettenis 			if (i < sc->sc_num_fan)
75202222f5cSkettenis 				continue;
75302222f5cSkettenis 
75402222f5cSkettenis 			break;
75502222f5cSkettenis 		}
7565f2dcfb1Skettenis 	}
7575f2dcfb1Skettenis 
7585f2dcfb1Skettenis fail:
7595f2dcfb1Skettenis 	lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_GENERIC);
7605f2dcfb1Skettenis 	return (error);
7615f2dcfb1Skettenis }
7625f2dcfb1Skettenis 
7632613757eSkettenis void
7642613757eSkettenis lom_refresh(void *arg)
7652613757eSkettenis {
7662613757eSkettenis 	struct lom_softc *sc = arg;
7672613757eSkettenis 	uint8_t val;
76802222f5cSkettenis 	int i;
7692613757eSkettenis 
77002222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
77102222f5cSkettenis 		if (lom_read(sc, LOM_IDX_FAN1 + i, &val)) {
77202222f5cSkettenis 			sc->sc_fan[i].flags |= SENSOR_FINVALID;
77302222f5cSkettenis 			continue;
77402222f5cSkettenis 		}
77502222f5cSkettenis 
77602222f5cSkettenis 		sc->sc_fan[i].value = (60 * sc->sc_fan_cal[i] * val) / 100;
777df54a7c0Skettenis 		if (val < sc->sc_fan_low[i])
778df54a7c0Skettenis 			sc->sc_fan[i].status = SENSOR_S_CRIT;
779df54a7c0Skettenis 		else
780df54a7c0Skettenis 			sc->sc_fan[i].status = SENSOR_S_OK;
78102222f5cSkettenis 		sc->sc_fan[i].flags &= ~SENSOR_FINVALID;
78202222f5cSkettenis 	}
7836801a748Skettenis 
78478eeb547Skettenis 	for (i = 0; i < sc->sc_num_psu; i++) {
78578eeb547Skettenis 		if (lom_read(sc, LOM_IDX_PSU1 + i, &val) ||
78678eeb547Skettenis 		    !ISSET(val, LOM_PSU_PRESENT)) {
78778eeb547Skettenis 			sc->sc_psu[i].flags |= SENSOR_FINVALID;
78878eeb547Skettenis 			continue;
78978eeb547Skettenis 		}
79078eeb547Skettenis 
79178eeb547Skettenis 		if (val & LOM_PSU_STANDBY) {
79278eeb547Skettenis 			sc->sc_psu[i].value = 0;
79378eeb547Skettenis 			sc->sc_psu[i].status = SENSOR_S_UNSPEC;
79478eeb547Skettenis 		} else {
79578eeb547Skettenis 			sc->sc_psu[i].value = 1;
79678eeb547Skettenis 			if (ISSET(val, LOM_PSU_INPUTA) &&
79778eeb547Skettenis 			    ISSET(val, LOM_PSU_INPUTB) &&
79878eeb547Skettenis 			    ISSET(val, LOM_PSU_OUTPUT))
79978eeb547Skettenis 				sc->sc_psu[i].status = SENSOR_S_OK;
80078eeb547Skettenis 			else
80178eeb547Skettenis 				sc->sc_psu[i].status = SENSOR_S_CRIT;
80278eeb547Skettenis 		}
80378eeb547Skettenis 		sc->sc_psu[i].flags &= ~SENSOR_FINVALID;
80478eeb547Skettenis 	}
80578eeb547Skettenis 
80678eeb547Skettenis 	for (i = 0; i < sc->sc_num_temp; i++) {
80778eeb547Skettenis 		if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) {
80878eeb547Skettenis 			sc->sc_temp[i].flags |= SENSOR_FINVALID;
80978eeb547Skettenis 			continue;
81078eeb547Skettenis 		}
81178eeb547Skettenis 
81278eeb547Skettenis 		sc->sc_temp[i].value = val * 1000000 + 273150000;
81378eeb547Skettenis 		sc->sc_temp[i].flags &= ~SENSOR_FINVALID;
81478eeb547Skettenis 	}
81578eeb547Skettenis 
8166801a748Skettenis 	/*
8176801a748Skettenis 	 * If our hostname is set and differs from what's stored in
8186801a748Skettenis 	 * the LOM, write the new hostname back to the LOM.  Note that
8196801a748Skettenis 	 * we include the terminating NUL when writing the hostname
820fc69c4e1Skettenis 	 * back to the LOM, otherwise the LOM will print any trailing
8216801a748Skettenis 	 * garbage.
8226801a748Skettenis 	 */
8236801a748Skettenis 	if (hostnamelen > 0 &&
8246801a748Skettenis 	    strncmp(sc->sc_hostname, hostname, sizeof(hostname)) != 0) {
82513d051d6Skettenis 		if (sc->sc_type < LOM_LOMLITE2)
82613d051d6Skettenis 			lom1_write_hostname(sc);
82713d051d6Skettenis 		else
82813d051d6Skettenis 			lom2_write_hostname(sc);
8296801a748Skettenis 		strlcpy(sc->sc_hostname, hostname, sizeof(hostname));
8306801a748Skettenis 	}
8312613757eSkettenis }
832877407b4Skettenis 
833877407b4Skettenis void
83413d051d6Skettenis lom1_write_hostname(struct lom_softc *sc)
83513d051d6Skettenis {
83613d051d6Skettenis 	char name[LOM1_IDX_HOSTNAME12 - LOM1_IDX_HOSTNAME1 + 1];
83713d051d6Skettenis 	char *p;
83813d051d6Skettenis 	int i;
83913d051d6Skettenis 
84013d051d6Skettenis 	/*
84113d051d6Skettenis 	 * LOMlite generally doesn't have enough space to store the
84213d051d6Skettenis 	 * fully qualified hostname.  If the hostname is too long,
84313d051d6Skettenis 	 * strip off the domain name.
84413d051d6Skettenis 	 */
84513d051d6Skettenis 	strlcpy(name, hostname, sizeof(name));
84613d051d6Skettenis 	if (hostnamelen > sizeof(name)) {
84713d051d6Skettenis 		p = strchr(name, '.');
84813d051d6Skettenis 		if (p)
84913d051d6Skettenis 			*p = '\0';
85013d051d6Skettenis 	}
85113d051d6Skettenis 
85213d051d6Skettenis 	for (i = 0; i < strlen(name) + 1; i++)
8535e80de5eSkettenis 		if (lom_write(sc, LOM1_IDX_HOSTNAME1 + i, name[i]))
8545e80de5eSkettenis 			break;
85513d051d6Skettenis }
85613d051d6Skettenis 
85713d051d6Skettenis void
85813d051d6Skettenis lom2_write_hostname(struct lom_softc *sc)
85913d051d6Skettenis {
86013d051d6Skettenis 	int i;
86113d051d6Skettenis 
86213d051d6Skettenis 	lom_write(sc, LOM2_IDX_HOSTNAMELEN, hostnamelen + 1);
86313d051d6Skettenis 	for (i = 0; i < hostnamelen + 1; i++)
86413d051d6Skettenis 		lom_write(sc, LOM2_IDX_HOSTNAME, hostname[i]);
86513d051d6Skettenis }
86613d051d6Skettenis 
86713d051d6Skettenis void
868877407b4Skettenis lom_wdog_pat(void *arg)
869877407b4Skettenis {
870877407b4Skettenis 	struct lom_softc *sc;
871877407b4Skettenis 
872877407b4Skettenis 	/* Pat the dog. */
8735e80de5eSkettenis 	sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
8745e80de5eSkettenis 	sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
8755e80de5eSkettenis 	lom_queue_cmd(sc, &sc->sc_wdog_pat);
876877407b4Skettenis 
877877407b4Skettenis 	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
878877407b4Skettenis }
879877407b4Skettenis 
880877407b4Skettenis int
881877407b4Skettenis lom_wdog_cb(void *arg, int period)
882877407b4Skettenis {
883877407b4Skettenis 	struct lom_softc *sc = arg;
884877407b4Skettenis 
885fc69c4e1Skettenis 	if (period > LOM_WDOG_TIME_MAX)
886fc69c4e1Skettenis 		period = LOM_WDOG_TIME_MAX;
887877407b4Skettenis 	else if (period < 0)
888877407b4Skettenis 		period = 0;
889877407b4Skettenis 
890877407b4Skettenis 	if (period == 0) {
891877407b4Skettenis 		if (sc->sc_wdog_period != 0) {
892877407b4Skettenis 			/* Stop watchdog from resetting the machine. */
8935223c73eSkettenis 			sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
8945223c73eSkettenis 			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
895877407b4Skettenis 
896877407b4Skettenis 			lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
897877407b4Skettenis 			timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
898877407b4Skettenis 		}
899877407b4Skettenis 	} else {
900877407b4Skettenis 		if (sc->sc_wdog_period != period) {
901877407b4Skettenis 			/* Set new timeout. */
902877407b4Skettenis 			lom_write(sc, LOM_IDX_WDOG_TIME, period);
903877407b4Skettenis 		}
904877407b4Skettenis 		if (sc->sc_wdog_period == 0) {
905877407b4Skettenis 			/* Make watchdog reset the machine. */
9065223c73eSkettenis 			sc->sc_wdog_ctl |= LOM_WDOG_RESET;
9075223c73eSkettenis 			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
908877407b4Skettenis 
909877407b4Skettenis 			timeout_del(&sc->sc_wdog_to);
910877407b4Skettenis 		} else {
911877407b4Skettenis 			/* Pat the dog. */
912*a4f1f624Skettenis 			lom_dequeue_cmd(sc, &sc->sc_wdog_pat);
9135e80de5eSkettenis 			sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
9145e80de5eSkettenis 			sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
9155e80de5eSkettenis 			lom_queue_cmd(sc, &sc->sc_wdog_pat);
916877407b4Skettenis 		}
917877407b4Skettenis 	}
918877407b4Skettenis 	sc->sc_wdog_period = period;
919877407b4Skettenis 
920877407b4Skettenis 	return (period);
921877407b4Skettenis }
922