xref: /openbsd/sys/arch/sparc64/dev/lom.c (revision 50dbebda)
1*50dbebdaSmiod /*	$OpenBSD: lom.c,v 1.29 2022/07/04 19:06:10 miod 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 *);
182c06fda6dSderaadt int	lom_activate(struct device *, int);
1832613757eSkettenis 
184eb7eaf8dSmpi const struct cfattach lom_ca = {
185c06fda6dSderaadt 	sizeof(struct lom_softc), lom_match, lom_attach,
186c06fda6dSderaadt 	NULL, lom_activate
1872613757eSkettenis };
1882613757eSkettenis 
1892613757eSkettenis struct cfdriver lom_cd = {
1902613757eSkettenis 	NULL, "lom", DV_DULL
1912613757eSkettenis };
1922613757eSkettenis 
1932613757eSkettenis int	lom_read(struct lom_softc *, uint8_t, uint8_t *);
1942613757eSkettenis int	lom_write(struct lom_softc *, uint8_t, uint8_t);
1955e80de5eSkettenis void	lom_queue_cmd(struct lom_softc *, struct lom_cmd *);
196a4f1f624Skettenis void	lom_dequeue_cmd(struct lom_softc *, struct lom_cmd *);
19713d051d6Skettenis int	lom1_read(struct lom_softc *, uint8_t, uint8_t *);
19813d051d6Skettenis int	lom1_write(struct lom_softc *, uint8_t, uint8_t);
1995e80de5eSkettenis int	lom1_read_polled(struct lom_softc *, uint8_t, uint8_t *);
2005e80de5eSkettenis int	lom1_write_polled(struct lom_softc *, uint8_t, uint8_t);
2015e80de5eSkettenis void	lom1_queue_cmd(struct lom_softc *, struct lom_cmd *);
2025e80de5eSkettenis void	lom1_process_queue(void *);
2035e80de5eSkettenis void	lom1_process_queue_locked(struct lom_softc *);
20413d051d6Skettenis int	lom2_read(struct lom_softc *, uint8_t, uint8_t *);
20513d051d6Skettenis int	lom2_write(struct lom_softc *, uint8_t, uint8_t);
206d95b06c1Skettenis int	lom2_read_polled(struct lom_softc *, uint8_t, uint8_t *);
207d95b06c1Skettenis int	lom2_write_polled(struct lom_softc *, uint8_t, uint8_t);
2085e80de5eSkettenis void	lom2_queue_cmd(struct lom_softc *, struct lom_cmd *);
209d95b06c1Skettenis int	lom2_intr(void *);
2102613757eSkettenis 
2115f2dcfb1Skettenis int	lom_init_desc(struct lom_softc *sc);
2122613757eSkettenis void	lom_refresh(void *);
21313d051d6Skettenis void	lom1_write_hostname(struct lom_softc *);
21413d051d6Skettenis void	lom2_write_hostname(struct lom_softc *);
2152613757eSkettenis 
216877407b4Skettenis void	lom_wdog_pat(void *);
217877407b4Skettenis int	lom_wdog_cb(void *, int);
218877407b4Skettenis 
219edd433c7Skettenis void	lom_shutdown(void *);
220edd433c7Skettenis 
2212613757eSkettenis int
lom_match(struct device * parent,void * match,void * aux)2222613757eSkettenis lom_match(struct device *parent, void *match, void *aux)
2232613757eSkettenis {
2242613757eSkettenis 	struct ebus_attach_args *ea = aux;
2252613757eSkettenis 
226fc69c4e1Skettenis 	if (strcmp(ea->ea_name, "SUNW,lom") == 0 ||
227fc69c4e1Skettenis 	    strcmp(ea->ea_name, "SUNW,lomh") == 0)
2282613757eSkettenis 		return (1);
2292613757eSkettenis 
2302613757eSkettenis 	return (0);
2312613757eSkettenis }
2322613757eSkettenis 
2332613757eSkettenis void
lom_attach(struct device * parent,struct device * self,void * aux)2342613757eSkettenis lom_attach(struct device *parent, struct device *self, void *aux)
2352613757eSkettenis {
2362613757eSkettenis 	struct lom_softc *sc = (void *)self;
2372613757eSkettenis 	struct ebus_attach_args *ea = aux;
23802222f5cSkettenis 	uint8_t reg, fw_rev, config, config2, config3;
23913d051d6Skettenis 	uint8_t cal, low;
24002222f5cSkettenis 	int i;
2412613757eSkettenis 
242d95b06c1Skettenis 	if (strcmp(ea->ea_name, "SUNW,lomh") == 0) {
243d95b06c1Skettenis 		if (ea->ea_nintrs < 1) {
244d95b06c1Skettenis 			printf(": no interrupt\n");
245d95b06c1Skettenis 			return;
246d95b06c1Skettenis 		}
24713d051d6Skettenis 		sc->sc_type = LOM_LOMLITE2;
248d95b06c1Skettenis 	}
24913d051d6Skettenis 
250d151cb6cSkettenis 	if (ebus_bus_map(ea->ea_iotag, 0,
2512613757eSkettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
252d151cb6cSkettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
253d151cb6cSkettenis 		sc->sc_iot = ea->ea_iotag;
254d151cb6cSkettenis 	} else if (ebus_bus_map(ea->ea_memtag, 0,
255d151cb6cSkettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
256d151cb6cSkettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
257d151cb6cSkettenis 		sc->sc_iot = ea->ea_memtag;
258d151cb6cSkettenis 	} else {
2592613757eSkettenis 		printf(": can't map register space\n");
2602613757eSkettenis                 return;
2612613757eSkettenis 	}
2622613757eSkettenis 
26313d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2) {
26413d051d6Skettenis 		/* XXX Magic */
26513d051d6Skettenis 		bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0);
26613d051d6Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, 3, 0xca);
26713d051d6Skettenis 	}
26813d051d6Skettenis 
2692613757eSkettenis 	if (lom_read(sc, LOM_IDX_PROBE55, &reg) || reg != 0x55 ||
2702613757eSkettenis 	    lom_read(sc, LOM_IDX_PROBEAA, &reg) || reg != 0xaa ||
27102222f5cSkettenis 	    lom_read(sc, LOM_IDX_FW_REV, &fw_rev) ||
27213d051d6Skettenis 	    lom_read(sc, LOM_IDX_CONFIG, &config))
27302222f5cSkettenis 	{
2742613757eSkettenis 		printf(": not responding\n");
2752613757eSkettenis 		return;
2762613757eSkettenis 	}
2772613757eSkettenis 
278d95b06c1Skettenis 	TAILQ_INIT(&sc->sc_queue);
279d95b06c1Skettenis 	mtx_init(&sc->sc_queue_mtx, IPL_BIO);
280d95b06c1Skettenis 
28113d051d6Skettenis 	config2 = config3 = 0;
282d95b06c1Skettenis 	if (sc->sc_type < LOM_LOMLITE2) {
283d95b06c1Skettenis 		/*
284d95b06c1Skettenis 		 * LOMlite doesn't do interrupts so we limp along on
285d95b06c1Skettenis 		 * timeouts.
286d95b06c1Skettenis 		 */
287d95b06c1Skettenis 		timeout_set(&sc->sc_state_to, lom1_process_queue, sc);
288d95b06c1Skettenis 	} else {
28913d051d6Skettenis 		lom_read(sc, LOM_IDX_CONFIG2, &config2);
29013d051d6Skettenis 		lom_read(sc, LOM_IDX_CONFIG3, &config3);
291d95b06c1Skettenis 
292d95b06c1Skettenis 		bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
293d95b06c1Skettenis 		    IPL_BIO, 0, lom2_intr, sc, self->dv_xname);
29413d051d6Skettenis 	}
29513d051d6Skettenis 
29602222f5cSkettenis 	sc->sc_num_fan = min((config >> 5) & 0x7, LOM_MAX_FAN);
29702222f5cSkettenis 	sc->sc_num_psu = min((config >> 3) & 0x3, LOM_MAX_PSU);
29802222f5cSkettenis 	sc->sc_num_temp = min((config2 >> 4) & 0xf, LOM_MAX_TEMP);
29902222f5cSkettenis 
30002222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
30102222f5cSkettenis 		if (lom_read(sc, LOM_IDX_FAN1_CAL + i, &cal) ||
30202222f5cSkettenis 		    lom_read(sc, LOM_IDX_FAN1_LOW + i, &low)) {
30302222f5cSkettenis 			printf(": can't read fan information\n");
30402222f5cSkettenis 			return;
30502222f5cSkettenis 		}
30602222f5cSkettenis 		sc->sc_fan_cal[i] = cal;
30702222f5cSkettenis 		sc->sc_fan_low[i] = low;
30802222f5cSkettenis 	}
30902222f5cSkettenis 
3102613757eSkettenis 	/* Initialize sensor data. */
3112613757eSkettenis 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
3122613757eSkettenis 	    sizeof(sc->sc_sensordev.xname));
31302222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
31402222f5cSkettenis 		sc->sc_fan[i].type = SENSOR_FANRPM;
31502222f5cSkettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_fan[i]);
31613d051d6Skettenis 		snprintf(sc->sc_fan[i].desc, sizeof(sc->sc_fan[i].desc),
31713d051d6Skettenis 		    "fan%d", i + 1);
31802222f5cSkettenis 	}
31978eeb547Skettenis 	for (i = 0; i < sc->sc_num_psu; i++) {
32078eeb547Skettenis 		sc->sc_psu[i].type = SENSOR_INDICATOR;
32178eeb547Skettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_psu[i]);
32278eeb547Skettenis 		snprintf(sc->sc_psu[i].desc, sizeof(sc->sc_psu[i].desc),
32378eeb547Skettenis 		    "PSU%d", i + 1);
32478eeb547Skettenis 	}
32502222f5cSkettenis 	for (i = 0; i < sc->sc_num_temp; i++) {
32602222f5cSkettenis 		sc->sc_temp[i].type = SENSOR_TEMP;
32702222f5cSkettenis 		sensor_attach(&sc->sc_sensordev, &sc->sc_temp[i]);
32802222f5cSkettenis 	}
3295f2dcfb1Skettenis 	if (lom_init_desc(sc)) {
3305f2dcfb1Skettenis 		printf(": can't read sensor names\n");
3315f2dcfb1Skettenis 		return;
3325f2dcfb1Skettenis 	}
3332613757eSkettenis 
3342613757eSkettenis 	if (sensor_task_register(sc, lom_refresh, 5) == NULL) {
3352613757eSkettenis 		printf(": unable to register update task\n");
3362613757eSkettenis 		return;
3372613757eSkettenis 	}
3382613757eSkettenis 
3392613757eSkettenis 	sensordev_install(&sc->sc_sensordev);
3402613757eSkettenis 
341877407b4Skettenis 	/*
342877407b4Skettenis 	 * We configure the watchdog to turn on the fault LED when the
343877407b4Skettenis 	 * watchdog timer expires.  We run our own timeout to pat it
344877407b4Skettenis 	 * such that this won't happen unless the kernel hangs.  When
345877407b4Skettenis 	 * the watchdog is explicitly configured using sysctl(8), we
346877407b4Skettenis 	 * reconfigure it to reset the machine and let the standard
347877407b4Skettenis 	 * watchdog(4) machinery take over.
348877407b4Skettenis 	 */
349877407b4Skettenis 	lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
3505223c73eSkettenis 	lom_read(sc, LOM_IDX_WDOG_CTL, &sc->sc_wdog_ctl);
3515223c73eSkettenis 	sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
3525223c73eSkettenis 	sc->sc_wdog_ctl |= LOM_WDOG_ENABLE;
3535223c73eSkettenis 	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
354877407b4Skettenis 	timeout_set(&sc->sc_wdog_to, lom_wdog_pat, sc);
355877407b4Skettenis 	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
356877407b4Skettenis 
3572bc62decSderaadt 	wdog_register(lom_wdog_cb, sc);
358877407b4Skettenis 
359e6d8f4d1Skettenis 	printf(": %s rev %d.%d\n",
360e6d8f4d1Skettenis 	    sc->sc_type < LOM_LOMLITE2 ? "LOMlite" : "LOMlite2",
361e6d8f4d1Skettenis 	    fw_rev >> 4, fw_rev & 0x0f);
362c06fda6dSderaadt }
363edd433c7Skettenis 
364c06fda6dSderaadt int
lom_activate(struct device * self,int act)365c06fda6dSderaadt lom_activate(struct device *self, int act)
366c06fda6dSderaadt {
367c06fda6dSderaadt 	int ret = 0;
368c06fda6dSderaadt 
369c06fda6dSderaadt 	switch (act) {
370c06fda6dSderaadt 	case DVACT_POWERDOWN:
3713b06f262Smikeb 		wdog_shutdown(self);
372c06fda6dSderaadt 		lom_shutdown(self);
373c06fda6dSderaadt 		break;
374c06fda6dSderaadt 	}
375c06fda6dSderaadt 
376c06fda6dSderaadt 	return (ret);
3772613757eSkettenis }
3782613757eSkettenis 
3792613757eSkettenis int
lom_read(struct lom_softc * sc,uint8_t reg,uint8_t * val)3802613757eSkettenis lom_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
3812613757eSkettenis {
38213d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
38313d051d6Skettenis 		return lom1_read(sc, reg, val);
38413d051d6Skettenis 	else
38513d051d6Skettenis 		return lom2_read(sc, reg, val);
3862613757eSkettenis }
3872613757eSkettenis 
3885f2dcfb1Skettenis int
lom_write(struct lom_softc * sc,uint8_t reg,uint8_t val)3895f2dcfb1Skettenis lom_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
3905f2dcfb1Skettenis {
39113d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
39213d051d6Skettenis 		return lom1_write(sc, reg, val);
39313d051d6Skettenis 	else
39413d051d6Skettenis 		return lom2_write(sc, reg, val);
39513d051d6Skettenis }
39613d051d6Skettenis 
3975e80de5eSkettenis void
lom_queue_cmd(struct lom_softc * sc,struct lom_cmd * lc)3985e80de5eSkettenis lom_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
3995e80de5eSkettenis {
4005e80de5eSkettenis 	if (sc->sc_type < LOM_LOMLITE2)
4015e80de5eSkettenis 		return lom1_queue_cmd(sc, lc);
4025e80de5eSkettenis 	else
4035e80de5eSkettenis 		return lom2_queue_cmd(sc, lc);
4045e80de5eSkettenis }
4055e80de5eSkettenis 
406a4f1f624Skettenis void
lom_dequeue_cmd(struct lom_softc * sc,struct lom_cmd * lc)407a4f1f624Skettenis lom_dequeue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
408a4f1f624Skettenis {
409d95b06c1Skettenis 	struct lom_cmd *lcp;
410d95b06c1Skettenis 
411d95b06c1Skettenis 	mtx_enter(&sc->sc_queue_mtx);
412d95b06c1Skettenis 	TAILQ_FOREACH(lcp, &sc->sc_queue, lc_next) {
413d95b06c1Skettenis 		if (lcp == lc) {
414d95b06c1Skettenis 			TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
415d95b06c1Skettenis 			break;
416d95b06c1Skettenis 		}
417d95b06c1Skettenis 	}
418d95b06c1Skettenis 	mtx_leave(&sc->sc_queue_mtx);
419a4f1f624Skettenis }
420a4f1f624Skettenis 
42113d051d6Skettenis int
lom1_read(struct lom_softc * sc,uint8_t reg,uint8_t * val)42213d051d6Skettenis lom1_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
42313d051d6Skettenis {
4245e80de5eSkettenis 	struct lom_cmd lc;
425fc69c4e1Skettenis 	int error;
426fc69c4e1Skettenis 
427fc69c4e1Skettenis 	if (cold)
428fc69c4e1Skettenis 		return lom1_read_polled(sc, reg, val);
429fc69c4e1Skettenis 
4305e80de5eSkettenis 	lc.lc_cmd = reg;
4315e80de5eSkettenis 	lc.lc_data = 0xff;
4325e80de5eSkettenis 	lom1_queue_cmd(sc, &lc);
433fc69c4e1Skettenis 
434d6fc7890Scheloha 	error = tsleep_nsec(&lc, PZERO, "lomrd", SEC_TO_NSEC(1));
4355e80de5eSkettenis 	if (error)
436d95b06c1Skettenis 		lom_dequeue_cmd(sc, &lc);
437fc69c4e1Skettenis 
4385e80de5eSkettenis 	*val = lc.lc_data;
4395e80de5eSkettenis 
4405e80de5eSkettenis 	return (error);
4415e80de5eSkettenis }
4425e80de5eSkettenis 
4435e80de5eSkettenis int
lom1_write(struct lom_softc * sc,uint8_t reg,uint8_t val)4445e80de5eSkettenis lom1_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
4455e80de5eSkettenis {
4465e80de5eSkettenis 	struct lom_cmd lc;
4475e80de5eSkettenis 	int error;
4485e80de5eSkettenis 
4495e80de5eSkettenis 	if (cold)
4505e80de5eSkettenis 		return lom1_write_polled(sc, reg, val);
4515e80de5eSkettenis 
4525e80de5eSkettenis 	lc.lc_cmd = reg | LOM_IDX_WRITE;
4535e80de5eSkettenis 	lc.lc_data = val;
4545e80de5eSkettenis 	lom1_queue_cmd(sc, &lc);
4555e80de5eSkettenis 
456d6fc7890Scheloha 	error = tsleep_nsec(&lc, PZERO, "lomwr", SEC_TO_NSEC(2));
4575e80de5eSkettenis 	if (error)
458d95b06c1Skettenis 		lom_dequeue_cmd(sc, &lc);
459fc69c4e1Skettenis 
460fc69c4e1Skettenis 	return (error);
461fc69c4e1Skettenis }
462fc69c4e1Skettenis 
463fc69c4e1Skettenis int
lom1_read_polled(struct lom_softc * sc,uint8_t reg,uint8_t * val)464fc69c4e1Skettenis lom1_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
465fc69c4e1Skettenis {
46613d051d6Skettenis 	uint8_t str;
46713d051d6Skettenis 	int i;
46813d051d6Skettenis 
46913d051d6Skettenis 	/* Wait for input buffer to become available. */
4703c40421dSkettenis 	for (i = 30; i > 0; i--) {
47113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4723c40421dSkettenis 		delay(1000);
47313d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
47413d051d6Skettenis 			break;
47513d051d6Skettenis 	}
47613d051d6Skettenis 	if (i == 0)
47713d051d6Skettenis 		return (ETIMEDOUT);
47813d051d6Skettenis 
47913d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
48013d051d6Skettenis 
48113d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
4823c40421dSkettenis 	for (i = 30; i > 0; i--) {
48313d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
4843c40421dSkettenis 		delay(1000);
48513d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
48613d051d6Skettenis 			break;
48713d051d6Skettenis 	}
48813d051d6Skettenis 	if (i == 0)
48913d051d6Skettenis 		return (ETIMEDOUT);
49013d051d6Skettenis 
49113d051d6Skettenis 	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
49213d051d6Skettenis 	return (0);
49313d051d6Skettenis }
49413d051d6Skettenis 
49513d051d6Skettenis int
lom1_write_polled(struct lom_softc * sc,uint8_t reg,uint8_t val)4965e80de5eSkettenis lom1_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
49713d051d6Skettenis {
49813d051d6Skettenis 	uint8_t str;
49913d051d6Skettenis 	int i;
50013d051d6Skettenis 
50113d051d6Skettenis 	/* Wait for input buffer to become available. */
5023c40421dSkettenis 	for (i = 30; i > 0; i--) {
50313d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
5043c40421dSkettenis 		delay(1000);
50513d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
50613d051d6Skettenis 			break;
50713d051d6Skettenis 	}
50813d051d6Skettenis 	if (i == 0)
50913d051d6Skettenis 		return (ETIMEDOUT);
51013d051d6Skettenis 
5115e80de5eSkettenis 	reg |= LOM_IDX_WRITE;
5125e80de5eSkettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, reg);
51313d051d6Skettenis 
51413d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
5153c40421dSkettenis 	for (i = 30; i > 0; i--) {
51613d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
5173c40421dSkettenis 		delay(1000);
51813d051d6Skettenis 		if ((str & LOM1_STATUS_BUSY) == 0)
51913d051d6Skettenis 			break;
52013d051d6Skettenis 	}
52113d051d6Skettenis 	if (i == 0)
52213d051d6Skettenis 		return (ETIMEDOUT);
52313d051d6Skettenis 
52413d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, val);
52513d051d6Skettenis 
52613d051d6Skettenis 	return (0);
52713d051d6Skettenis }
52813d051d6Skettenis 
529fc69c4e1Skettenis void
lom1_queue_cmd(struct lom_softc * sc,struct lom_cmd * lc)5305e80de5eSkettenis lom1_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
5315e80de5eSkettenis {
5325e80de5eSkettenis 	mtx_enter(&sc->sc_queue_mtx);
5335e80de5eSkettenis 	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
5345e80de5eSkettenis 	if (sc->sc_state == LOM_STATE_IDLE) {
5355e80de5eSkettenis 		sc->sc_state = LOM_STATE_CMD;
5365e80de5eSkettenis 		lom1_process_queue_locked(sc);
5375e80de5eSkettenis 	}
5385e80de5eSkettenis 	mtx_leave(&sc->sc_queue_mtx);
5395e80de5eSkettenis }
5405e80de5eSkettenis 
5415e80de5eSkettenis void
lom1_process_queue(void * arg)5425e80de5eSkettenis lom1_process_queue(void *arg)
543fc69c4e1Skettenis {
544fc69c4e1Skettenis 	struct lom_softc *sc = arg;
5455e80de5eSkettenis 
5465e80de5eSkettenis 	mtx_enter(&sc->sc_queue_mtx);
5475e80de5eSkettenis 	lom1_process_queue_locked(sc);
5485e80de5eSkettenis 	mtx_leave(&sc->sc_queue_mtx);
5495e80de5eSkettenis }
5505e80de5eSkettenis 
5515e80de5eSkettenis void
lom1_process_queue_locked(struct lom_softc * sc)5525e80de5eSkettenis lom1_process_queue_locked(struct lom_softc *sc)
5535e80de5eSkettenis {
5545e80de5eSkettenis 	struct lom_cmd *lc;
555fc69c4e1Skettenis 	uint8_t str;
556fc69c4e1Skettenis 
5575e80de5eSkettenis 	lc = TAILQ_FIRST(&sc->sc_queue);
5588fe2a92fSkettenis 	if (lc == NULL) {
5598fe2a92fSkettenis 		sc->sc_state = LOM_STATE_IDLE;
5608fe2a92fSkettenis 		return;
5618fe2a92fSkettenis 	}
5625e80de5eSkettenis 
563fc69c4e1Skettenis 	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_STATUS);
564fc69c4e1Skettenis 	if (str & LOM1_STATUS_BUSY) {
5658fe2a92fSkettenis 		if (sc->sc_retry++ < 30) {
5665e80de5eSkettenis 			timeout_add_msec(&sc->sc_state_to, 1);
567fc69c4e1Skettenis 			return;
568fc69c4e1Skettenis 		}
569fc69c4e1Skettenis 
5708fe2a92fSkettenis 		/*
5718fe2a92fSkettenis 		 * Looks like the microcontroller got wedged.  Unwedge
5728fe2a92fSkettenis 		 * it by writing this magic value.  Give it some time
5738fe2a92fSkettenis 		 * to recover.
5748fe2a92fSkettenis 		 */
5758fe2a92fSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, 0xac);
5768fe2a92fSkettenis 		timeout_add_msec(&sc->sc_state_to, 1000);
5778fe2a92fSkettenis 		sc->sc_state = LOM_STATE_CMD;
5788fe2a92fSkettenis 		return;
5798fe2a92fSkettenis 	}
5808fe2a92fSkettenis 
5815e80de5eSkettenis 	sc->sc_retry = 0;
5825e80de5eSkettenis 
5835e80de5eSkettenis 	if (sc->sc_state == LOM_STATE_CMD) {
5845e80de5eSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_CMD, lc->lc_cmd);
5855e80de5eSkettenis 		sc->sc_state = LOM_STATE_DATA;
5865e80de5eSkettenis 		timeout_add_msec(&sc->sc_state_to, 250);
587fc69c4e1Skettenis 		return;
588fc69c4e1Skettenis 	}
589fc69c4e1Skettenis 
5905e80de5eSkettenis 	KASSERT(sc->sc_state == LOM_STATE_DATA);
5915e80de5eSkettenis 	if ((lc->lc_cmd & LOM_IDX_WRITE) == 0)
5925e80de5eSkettenis 		lc->lc_data = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA);
5935e80de5eSkettenis 	else
5945e80de5eSkettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM1_DATA, lc->lc_data);
5955e80de5eSkettenis 
5965e80de5eSkettenis 	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
5975e80de5eSkettenis 
5985e80de5eSkettenis 	wakeup(lc);
5995e80de5eSkettenis 
6005e80de5eSkettenis 	if (!TAILQ_EMPTY(&sc->sc_queue)) {
6015e80de5eSkettenis 		sc->sc_state = LOM_STATE_CMD;
6025e80de5eSkettenis 		timeout_add_msec(&sc->sc_state_to, 1);
6035e80de5eSkettenis 		return;
6045e80de5eSkettenis 	}
6055e80de5eSkettenis 
6065e80de5eSkettenis 	sc->sc_state = LOM_STATE_IDLE;
607fc69c4e1Skettenis }
608fc69c4e1Skettenis 
60913d051d6Skettenis int
lom2_read(struct lom_softc * sc,uint8_t reg,uint8_t * val)61013d051d6Skettenis lom2_read(struct lom_softc *sc, uint8_t reg, uint8_t *val)
61113d051d6Skettenis {
612d95b06c1Skettenis 	struct lom_cmd lc;
613d95b06c1Skettenis 	int error;
614d95b06c1Skettenis 
615d95b06c1Skettenis 	if (cold)
616d95b06c1Skettenis 		return lom2_read_polled(sc, reg, val);
617d95b06c1Skettenis 
618d95b06c1Skettenis 	lc.lc_cmd = reg;
619d95b06c1Skettenis 	lc.lc_data = 0xff;
620d95b06c1Skettenis 	lom2_queue_cmd(sc, &lc);
621d95b06c1Skettenis 
622d6fc7890Scheloha 	error = tsleep_nsec(&lc, PZERO, "lom2rd", SEC_TO_NSEC(1));
623d95b06c1Skettenis 	if (error)
624d7e63099Skettenis 		lom_dequeue_cmd(sc, &lc);
625d95b06c1Skettenis 
626d95b06c1Skettenis 	*val = lc.lc_data;
627d95b06c1Skettenis 
628d95b06c1Skettenis 	return (error);
629d95b06c1Skettenis }
630d95b06c1Skettenis 
631d95b06c1Skettenis int
lom2_read_polled(struct lom_softc * sc,uint8_t reg,uint8_t * val)632d95b06c1Skettenis lom2_read_polled(struct lom_softc *sc, uint8_t reg, uint8_t *val)
633d95b06c1Skettenis {
6345f2dcfb1Skettenis 	uint8_t str;
6355f2dcfb1Skettenis 	int i;
6365f2dcfb1Skettenis 
6375f2dcfb1Skettenis 	/* Wait for input buffer to become available. */
6385f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
63913d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
6405f2dcfb1Skettenis 		delay(10);
64113d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
64213d051d6Skettenis 			break;
64313d051d6Skettenis 	}
64413d051d6Skettenis 	if (i == 0)
64513d051d6Skettenis 		return (ETIMEDOUT);
64613d051d6Skettenis 
64713d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
64813d051d6Skettenis 
64913d051d6Skettenis 	/* Wait until the microcontroller fills output buffer. */
65013d051d6Skettenis 	for (i = 1000; i > 0; i--) {
65113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
65213d051d6Skettenis 		delay(10);
65313d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
65413d051d6Skettenis 			break;
65513d051d6Skettenis 	}
65613d051d6Skettenis 	if (i == 0)
65713d051d6Skettenis 		return (ETIMEDOUT);
65813d051d6Skettenis 
65913d051d6Skettenis 	*val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
66013d051d6Skettenis 	return (0);
66113d051d6Skettenis }
66213d051d6Skettenis 
66313d051d6Skettenis int
lom2_write(struct lom_softc * sc,uint8_t reg,uint8_t val)66413d051d6Skettenis lom2_write(struct lom_softc *sc, uint8_t reg, uint8_t val)
66513d051d6Skettenis {
666d95b06c1Skettenis 	struct lom_cmd lc;
667d95b06c1Skettenis 	int error;
668d95b06c1Skettenis 
669d95b06c1Skettenis 	if (cold)
670d95b06c1Skettenis 		return lom2_write_polled(sc, reg, val);
671d95b06c1Skettenis 
672d95b06c1Skettenis 	lc.lc_cmd = reg | LOM_IDX_WRITE;
673d95b06c1Skettenis 	lc.lc_data = val;
674d95b06c1Skettenis 	lom2_queue_cmd(sc, &lc);
675d95b06c1Skettenis 
676d6fc7890Scheloha 	error = tsleep_nsec(&lc, PZERO, "lom2wr", SEC_TO_NSEC(1));
677d95b06c1Skettenis 	if (error)
678d95b06c1Skettenis 		lom_dequeue_cmd(sc, &lc);
679d95b06c1Skettenis 
680d95b06c1Skettenis 	return (error);
681d95b06c1Skettenis }
682d95b06c1Skettenis 
683d95b06c1Skettenis int
lom2_write_polled(struct lom_softc * sc,uint8_t reg,uint8_t val)684d95b06c1Skettenis lom2_write_polled(struct lom_softc *sc, uint8_t reg, uint8_t val)
685d95b06c1Skettenis {
68613d051d6Skettenis 	uint8_t str;
68713d051d6Skettenis 	int i;
68813d051d6Skettenis 
68913d051d6Skettenis 	/* Wait for input buffer to become available. */
69013d051d6Skettenis 	for (i = 1000; i > 0; i--) {
69113d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
69213d051d6Skettenis 		delay(10);
69313d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
6945f2dcfb1Skettenis 			break;
6955f2dcfb1Skettenis 	}
6965f2dcfb1Skettenis 	if (i == 0)
6975f2dcfb1Skettenis 		return (ETIMEDOUT);
6985f2dcfb1Skettenis 
6995f2dcfb1Skettenis 	if (sc->sc_space == LOM_IDX_CMD_GENERIC && reg != LOM_IDX_CMD)
700d95b06c1Skettenis 		reg |= LOM_IDX_WRITE;
7015f2dcfb1Skettenis 
70213d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_CMD, reg);
7035f2dcfb1Skettenis 
7045f2dcfb1Skettenis 	/* Wait until the microcontroller fills output buffer. */
7055f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
70613d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
7075f2dcfb1Skettenis 		delay(10);
70813d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
7095f2dcfb1Skettenis 			break;
7105f2dcfb1Skettenis 	}
7115f2dcfb1Skettenis 	if (i == 0)
7125f2dcfb1Skettenis 		return (ETIMEDOUT);
7135f2dcfb1Skettenis 
71413d051d6Skettenis 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
7155f2dcfb1Skettenis 
7165f2dcfb1Skettenis 	/* Wait for input buffer to become available. */
7175f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
71813d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
7195f2dcfb1Skettenis 		delay(10);
72013d051d6Skettenis 		if ((str & LOM2_STATUS_IBF) == 0)
7215f2dcfb1Skettenis 			break;
7225f2dcfb1Skettenis 	}
7235f2dcfb1Skettenis 	if (i == 0)
7245f2dcfb1Skettenis 		return (ETIMEDOUT);
7255f2dcfb1Skettenis 
72613d051d6Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA, val);
7275f2dcfb1Skettenis 
7285f2dcfb1Skettenis 	/* Wait until the microcontroller fills output buffer. */
7295f2dcfb1Skettenis 	for (i = 1000; i > 0; i--) {
73013d051d6Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
7315f2dcfb1Skettenis 		delay(10);
73213d051d6Skettenis 		if (str & LOM2_STATUS_OBF)
7335f2dcfb1Skettenis 			break;
7345f2dcfb1Skettenis 	}
7355f2dcfb1Skettenis 	if (i == 0)
7365f2dcfb1Skettenis 		return (ETIMEDOUT);
7375f2dcfb1Skettenis 
73813d051d6Skettenis 	bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
7395f2dcfb1Skettenis 
7405f2dcfb1Skettenis 	/* If we switched spaces, remember the one we're in now. */
7415f2dcfb1Skettenis 	if (reg == LOM_IDX_CMD)
7425f2dcfb1Skettenis 		sc->sc_space = val;
7435f2dcfb1Skettenis 
7445f2dcfb1Skettenis 	return (0);
7455f2dcfb1Skettenis }
7465f2dcfb1Skettenis 
7475e80de5eSkettenis void
lom2_queue_cmd(struct lom_softc * sc,struct lom_cmd * lc)7485e80de5eSkettenis lom2_queue_cmd(struct lom_softc *sc, struct lom_cmd *lc)
7495e80de5eSkettenis {
750d95b06c1Skettenis 	uint8_t str;
751d95b06c1Skettenis 
752d95b06c1Skettenis 	mtx_enter(&sc->sc_queue_mtx);
753d95b06c1Skettenis 	TAILQ_INSERT_TAIL(&sc->sc_queue, lc, lc_next);
754d95b06c1Skettenis 	if (sc->sc_state == LOM_STATE_IDLE) {
755d95b06c1Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
756d95b06c1Skettenis 		if ((str & LOM2_STATUS_IBF) == 0) {
757d95b06c1Skettenis 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
758d95b06c1Skettenis 			    LOM2_CMD, lc->lc_cmd);
759d95b06c1Skettenis 			sc->sc_state = LOM_STATE_DATA;
760d95b06c1Skettenis 		}
761d95b06c1Skettenis 	}
762d95b06c1Skettenis 	mtx_leave(&sc->sc_queue_mtx);
763d95b06c1Skettenis }
764d95b06c1Skettenis 
765d95b06c1Skettenis int
lom2_intr(void * arg)766d95b06c1Skettenis lom2_intr(void *arg)
767d95b06c1Skettenis {
768d95b06c1Skettenis 	struct lom_softc *sc = arg;
769d95b06c1Skettenis 	struct lom_cmd *lc;
770d95b06c1Skettenis 	uint8_t str, obr;
771d95b06c1Skettenis 
772d95b06c1Skettenis 	mtx_enter(&sc->sc_queue_mtx);
773d95b06c1Skettenis 
774d95b06c1Skettenis 	str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
775d95b06c1Skettenis 	obr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_DATA);
776d95b06c1Skettenis 
777d95b06c1Skettenis 	lc = TAILQ_FIRST(&sc->sc_queue);
778d95b06c1Skettenis 	if (lc == NULL) {
779d95b06c1Skettenis 		mtx_leave(&sc->sc_queue_mtx);
780d95b06c1Skettenis 		return (0);
781d95b06c1Skettenis 	}
782d95b06c1Skettenis 
783d95b06c1Skettenis 	if (lc->lc_cmd & LOM_IDX_WRITE) {
784d95b06c1Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh,
785d95b06c1Skettenis 		    LOM2_DATA, lc->lc_data);
786d95b06c1Skettenis 		lc->lc_cmd &= ~LOM_IDX_WRITE;
787d95b06c1Skettenis 		mtx_leave(&sc->sc_queue_mtx);
788d95b06c1Skettenis 		return (1);
789d95b06c1Skettenis 	}
790d95b06c1Skettenis 
791604a7870Sbluhm 	KASSERT(sc->sc_state == LOM_STATE_DATA);
792d95b06c1Skettenis 	lc->lc_data = obr;
793d95b06c1Skettenis 
794d95b06c1Skettenis 	TAILQ_REMOVE(&sc->sc_queue, lc, lc_next);
795d95b06c1Skettenis 
796d95b06c1Skettenis 	wakeup(lc);
797d95b06c1Skettenis 
798d95b06c1Skettenis 	sc->sc_state = LOM_STATE_IDLE;
799d95b06c1Skettenis 
800d95b06c1Skettenis 	if (!TAILQ_EMPTY(&sc->sc_queue)) {
801d95b06c1Skettenis 		str = bus_space_read_1(sc->sc_iot, sc->sc_ioh, LOM2_STATUS);
802d95b06c1Skettenis 		if ((str & LOM2_STATUS_IBF) == 0) {
803d95b06c1Skettenis 			bus_space_write_1(sc->sc_iot, sc->sc_ioh,
804d95b06c1Skettenis 			    LOM2_CMD, lc->lc_cmd);
805d95b06c1Skettenis 			sc->sc_state = LOM_STATE_DATA;
806d95b06c1Skettenis 		}
807d95b06c1Skettenis 	}
808d95b06c1Skettenis 
809d95b06c1Skettenis 	mtx_leave(&sc->sc_queue_mtx);
810d95b06c1Skettenis 
811d95b06c1Skettenis 	return (1);
8125e80de5eSkettenis }
8135e80de5eSkettenis 
8145f2dcfb1Skettenis int
lom_init_desc(struct lom_softc * sc)8155f2dcfb1Skettenis lom_init_desc(struct lom_softc *sc)
8165f2dcfb1Skettenis {
8175f2dcfb1Skettenis 	uint8_t val;
81802222f5cSkettenis 	int i, j, k;
81902222f5cSkettenis 	int error;
8205f2dcfb1Skettenis 
82113d051d6Skettenis 	/* LOMlite doesn't provide sensor descriptions. */
82213d051d6Skettenis 	if (sc->sc_type < LOM_LOMLITE2)
82313d051d6Skettenis 		return (0);
82413d051d6Skettenis 
82502222f5cSkettenis 	/*
82602222f5cSkettenis 	 * Read temperature sensor names.
82702222f5cSkettenis 	 */
8285f2dcfb1Skettenis 	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_TEMP);
8295f2dcfb1Skettenis 	if (error)
8305f2dcfb1Skettenis 		return (error);
8315f2dcfb1Skettenis 
83202222f5cSkettenis 	i = 0;
83302222f5cSkettenis 	j = 0;
83402222f5cSkettenis 	k = LOM_IDX4_TEMP_NAME_START;
83502222f5cSkettenis 	while (k <= LOM_IDX4_TEMP_NAME_END) {
83602222f5cSkettenis 		error = lom_read(sc, k++, &val);
8375f2dcfb1Skettenis 		if (error)
8385f2dcfb1Skettenis 			goto fail;
8395f2dcfb1Skettenis 
8405f2dcfb1Skettenis 		if (val == 0xff)
8415f2dcfb1Skettenis 			break;
8425f2dcfb1Skettenis 
84313d051d6Skettenis 		if (j < sizeof (sc->sc_temp[i].desc) - 1)
84413d051d6Skettenis 			sc->sc_temp[i].desc[j++] = val;
84513d051d6Skettenis 
84602222f5cSkettenis 		if (val == '\0') {
8475f2dcfb1Skettenis 			i++;
84802222f5cSkettenis 			j = 0;
84902222f5cSkettenis 			if (i < sc->sc_num_temp)
85002222f5cSkettenis 				continue;
85102222f5cSkettenis 
85202222f5cSkettenis 			break;
85302222f5cSkettenis 		}
85402222f5cSkettenis 	}
85502222f5cSkettenis 
85602222f5cSkettenis 	/*
85702222f5cSkettenis 	 * Read fan names.
85802222f5cSkettenis 	 */
85902222f5cSkettenis 	error = lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_FAN);
86002222f5cSkettenis 	if (error)
86102222f5cSkettenis 		return (error);
86202222f5cSkettenis 
86302222f5cSkettenis 	i = 0;
86402222f5cSkettenis 	j = 0;
86502222f5cSkettenis 	k = LOM_IDX5_FAN_NAME_START;
86602222f5cSkettenis 	while (k <= LOM_IDX5_FAN_NAME_END) {
86702222f5cSkettenis 		error = lom_read(sc, k++, &val);
86802222f5cSkettenis 		if (error)
86902222f5cSkettenis 			goto fail;
87002222f5cSkettenis 
87102222f5cSkettenis 		if (val == 0xff)
87202222f5cSkettenis 			break;
87302222f5cSkettenis 
87413d051d6Skettenis 		if (j < sizeof (sc->sc_fan[i].desc) - 1)
87513d051d6Skettenis 			sc->sc_fan[i].desc[j++] = val;
87613d051d6Skettenis 
87702222f5cSkettenis 		if (val == '\0') {
87802222f5cSkettenis 			i++;
87902222f5cSkettenis 			j = 0;
88002222f5cSkettenis 			if (i < sc->sc_num_fan)
88102222f5cSkettenis 				continue;
88202222f5cSkettenis 
88302222f5cSkettenis 			break;
88402222f5cSkettenis 		}
8855f2dcfb1Skettenis 	}
8865f2dcfb1Skettenis 
8875f2dcfb1Skettenis fail:
8885f2dcfb1Skettenis 	lom_write(sc, LOM_IDX_CMD, LOM_IDX_CMD_GENERIC);
8895f2dcfb1Skettenis 	return (error);
8905f2dcfb1Skettenis }
8915f2dcfb1Skettenis 
8922613757eSkettenis void
lom_refresh(void * arg)8932613757eSkettenis lom_refresh(void *arg)
8942613757eSkettenis {
8952613757eSkettenis 	struct lom_softc *sc = arg;
8962613757eSkettenis 	uint8_t val;
89702222f5cSkettenis 	int i;
8982613757eSkettenis 
89902222f5cSkettenis 	for (i = 0; i < sc->sc_num_fan; i++) {
90002222f5cSkettenis 		if (lom_read(sc, LOM_IDX_FAN1 + i, &val)) {
90102222f5cSkettenis 			sc->sc_fan[i].flags |= SENSOR_FINVALID;
90202222f5cSkettenis 			continue;
90302222f5cSkettenis 		}
90402222f5cSkettenis 
90502222f5cSkettenis 		sc->sc_fan[i].value = (60 * sc->sc_fan_cal[i] * val) / 100;
906df54a7c0Skettenis 		if (val < sc->sc_fan_low[i])
907df54a7c0Skettenis 			sc->sc_fan[i].status = SENSOR_S_CRIT;
908df54a7c0Skettenis 		else
909df54a7c0Skettenis 			sc->sc_fan[i].status = SENSOR_S_OK;
91002222f5cSkettenis 		sc->sc_fan[i].flags &= ~SENSOR_FINVALID;
91102222f5cSkettenis 	}
9126801a748Skettenis 
91378eeb547Skettenis 	for (i = 0; i < sc->sc_num_psu; i++) {
91478eeb547Skettenis 		if (lom_read(sc, LOM_IDX_PSU1 + i, &val) ||
91578eeb547Skettenis 		    !ISSET(val, LOM_PSU_PRESENT)) {
91678eeb547Skettenis 			sc->sc_psu[i].flags |= SENSOR_FINVALID;
91778eeb547Skettenis 			continue;
91878eeb547Skettenis 		}
91978eeb547Skettenis 
92078eeb547Skettenis 		if (val & LOM_PSU_STANDBY) {
92178eeb547Skettenis 			sc->sc_psu[i].value = 0;
92278eeb547Skettenis 			sc->sc_psu[i].status = SENSOR_S_UNSPEC;
92378eeb547Skettenis 		} else {
92478eeb547Skettenis 			sc->sc_psu[i].value = 1;
92578eeb547Skettenis 			if (ISSET(val, LOM_PSU_INPUTA) &&
92678eeb547Skettenis 			    ISSET(val, LOM_PSU_INPUTB) &&
92778eeb547Skettenis 			    ISSET(val, LOM_PSU_OUTPUT))
92878eeb547Skettenis 				sc->sc_psu[i].status = SENSOR_S_OK;
92978eeb547Skettenis 			else
93078eeb547Skettenis 				sc->sc_psu[i].status = SENSOR_S_CRIT;
93178eeb547Skettenis 		}
93278eeb547Skettenis 		sc->sc_psu[i].flags &= ~SENSOR_FINVALID;
93378eeb547Skettenis 	}
93478eeb547Skettenis 
93578eeb547Skettenis 	for (i = 0; i < sc->sc_num_temp; i++) {
93678eeb547Skettenis 		if (lom_read(sc, LOM_IDX_TEMP1 + i, &val)) {
93778eeb547Skettenis 			sc->sc_temp[i].flags |= SENSOR_FINVALID;
93878eeb547Skettenis 			continue;
93978eeb547Skettenis 		}
94078eeb547Skettenis 
94178eeb547Skettenis 		sc->sc_temp[i].value = val * 1000000 + 273150000;
94278eeb547Skettenis 		sc->sc_temp[i].flags &= ~SENSOR_FINVALID;
94378eeb547Skettenis 	}
94478eeb547Skettenis 
9456801a748Skettenis 	/*
9466801a748Skettenis 	 * If our hostname is set and differs from what's stored in
9476801a748Skettenis 	 * the LOM, write the new hostname back to the LOM.  Note that
9486801a748Skettenis 	 * we include the terminating NUL when writing the hostname
949fc69c4e1Skettenis 	 * back to the LOM, otherwise the LOM will print any trailing
9506801a748Skettenis 	 * garbage.
9516801a748Skettenis 	 */
9526801a748Skettenis 	if (hostnamelen > 0 &&
9536801a748Skettenis 	    strncmp(sc->sc_hostname, hostname, sizeof(hostname)) != 0) {
95413d051d6Skettenis 		if (sc->sc_type < LOM_LOMLITE2)
95513d051d6Skettenis 			lom1_write_hostname(sc);
95613d051d6Skettenis 		else
95713d051d6Skettenis 			lom2_write_hostname(sc);
958*50dbebdaSmiod 		strlcpy(sc->sc_hostname, hostname, sizeof(sc->sc_hostname));
9596801a748Skettenis 	}
9602613757eSkettenis }
961877407b4Skettenis 
962877407b4Skettenis void
lom1_write_hostname(struct lom_softc * sc)96313d051d6Skettenis lom1_write_hostname(struct lom_softc *sc)
96413d051d6Skettenis {
96549d60162Skettenis 	char name[(LOM1_IDX_HOSTNAME12 - LOM1_IDX_HOSTNAME1 + 1) + 1];
96613d051d6Skettenis 	char *p;
96713d051d6Skettenis 	int i;
96813d051d6Skettenis 
96913d051d6Skettenis 	/*
97013d051d6Skettenis 	 * LOMlite generally doesn't have enough space to store the
97113d051d6Skettenis 	 * fully qualified hostname.  If the hostname is too long,
97213d051d6Skettenis 	 * strip off the domain name.
97313d051d6Skettenis 	 */
97413d051d6Skettenis 	strlcpy(name, hostname, sizeof(name));
97549d60162Skettenis 	if (hostnamelen >= sizeof(name)) {
97613d051d6Skettenis 		p = strchr(name, '.');
97713d051d6Skettenis 		if (p)
97813d051d6Skettenis 			*p = '\0';
97913d051d6Skettenis 	}
98013d051d6Skettenis 
98113d051d6Skettenis 	for (i = 0; i < strlen(name) + 1; i++)
9825e80de5eSkettenis 		if (lom_write(sc, LOM1_IDX_HOSTNAME1 + i, name[i]))
9835e80de5eSkettenis 			break;
98413d051d6Skettenis }
98513d051d6Skettenis 
98613d051d6Skettenis void
lom2_write_hostname(struct lom_softc * sc)98713d051d6Skettenis lom2_write_hostname(struct lom_softc *sc)
98813d051d6Skettenis {
98913d051d6Skettenis 	int i;
99013d051d6Skettenis 
99113d051d6Skettenis 	lom_write(sc, LOM2_IDX_HOSTNAMELEN, hostnamelen + 1);
99213d051d6Skettenis 	for (i = 0; i < hostnamelen + 1; i++)
99313d051d6Skettenis 		lom_write(sc, LOM2_IDX_HOSTNAME, hostname[i]);
99413d051d6Skettenis }
99513d051d6Skettenis 
99613d051d6Skettenis void
lom_wdog_pat(void * arg)997877407b4Skettenis lom_wdog_pat(void *arg)
998877407b4Skettenis {
99956bd4c6bSkettenis 	struct lom_softc *sc = arg;
1000877407b4Skettenis 
1001877407b4Skettenis 	/* Pat the dog. */
10025e80de5eSkettenis 	sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
10035e80de5eSkettenis 	sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
10045e80de5eSkettenis 	lom_queue_cmd(sc, &sc->sc_wdog_pat);
1005877407b4Skettenis 
1006877407b4Skettenis 	timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
1007877407b4Skettenis }
1008877407b4Skettenis 
1009877407b4Skettenis int
lom_wdog_cb(void * arg,int period)1010877407b4Skettenis lom_wdog_cb(void *arg, int period)
1011877407b4Skettenis {
1012877407b4Skettenis 	struct lom_softc *sc = arg;
1013877407b4Skettenis 
1014fc69c4e1Skettenis 	if (period > LOM_WDOG_TIME_MAX)
1015fc69c4e1Skettenis 		period = LOM_WDOG_TIME_MAX;
1016877407b4Skettenis 	else if (period < 0)
1017877407b4Skettenis 		period = 0;
1018877407b4Skettenis 
1019877407b4Skettenis 	if (period == 0) {
1020877407b4Skettenis 		if (sc->sc_wdog_period != 0) {
1021877407b4Skettenis 			/* Stop watchdog from resetting the machine. */
10225223c73eSkettenis 			sc->sc_wdog_ctl &= ~LOM_WDOG_RESET;
10235223c73eSkettenis 			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
1024877407b4Skettenis 
1025877407b4Skettenis 			lom_write(sc, LOM_IDX_WDOG_TIME, LOM_WDOG_TIME_MAX);
1026877407b4Skettenis 			timeout_add_sec(&sc->sc_wdog_to, LOM_WDOG_TIME_MAX / 2);
1027877407b4Skettenis 		}
1028877407b4Skettenis 	} else {
1029877407b4Skettenis 		if (sc->sc_wdog_period != period) {
1030877407b4Skettenis 			/* Set new timeout. */
1031877407b4Skettenis 			lom_write(sc, LOM_IDX_WDOG_TIME, period);
1032877407b4Skettenis 		}
1033877407b4Skettenis 		if (sc->sc_wdog_period == 0) {
1034877407b4Skettenis 			/* Make watchdog reset the machine. */
10355223c73eSkettenis 			sc->sc_wdog_ctl |= LOM_WDOG_RESET;
10365223c73eSkettenis 			lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
1037877407b4Skettenis 
1038877407b4Skettenis 			timeout_del(&sc->sc_wdog_to);
1039877407b4Skettenis 		} else {
1040877407b4Skettenis 			/* Pat the dog. */
1041a4f1f624Skettenis 			lom_dequeue_cmd(sc, &sc->sc_wdog_pat);
10425e80de5eSkettenis 			sc->sc_wdog_pat.lc_cmd = LOM_IDX_WDOG_CTL | LOM_IDX_WRITE;
10435e80de5eSkettenis 			sc->sc_wdog_pat.lc_data = sc->sc_wdog_ctl;
10445e80de5eSkettenis 			lom_queue_cmd(sc, &sc->sc_wdog_pat);
1045877407b4Skettenis 		}
1046877407b4Skettenis 	}
1047877407b4Skettenis 	sc->sc_wdog_period = period;
1048877407b4Skettenis 
1049877407b4Skettenis 	return (period);
1050877407b4Skettenis }
1051edd433c7Skettenis 
1052edd433c7Skettenis void
lom_shutdown(void * arg)1053edd433c7Skettenis lom_shutdown(void *arg)
1054edd433c7Skettenis {
1055edd433c7Skettenis 	struct lom_softc *sc = arg;
1056edd433c7Skettenis 
1057edd433c7Skettenis 	sc->sc_wdog_ctl &= ~LOM_WDOG_ENABLE;
1058edd433c7Skettenis 	lom_write(sc, LOM_IDX_WDOG_CTL, sc->sc_wdog_ctl);
1059edd433c7Skettenis }
1060