xref: /openbsd/sys/dev/pci/mbg.c (revision 9e6efb0a)
1 /*	$OpenBSD: mbg.c,v 1.37 2024/05/24 06:02:58 jsg Exp $ */
2 
3 /*
4  * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/timeout.h>
22 #include <sys/systm.h>
23 #include <sys/sensors.h>
24 #include <sys/time.h>
25 
26 #include <machine/bus.h>
27 
28 #include <dev/pci/pcivar.h>
29 #include <dev/pci/pcireg.h>
30 #include <dev/pci/pcidevs.h>
31 
32 struct mbg_softc {
33 	struct device		sc_dev;
34 	bus_space_tag_t		sc_iot;
35 	bus_space_handle_t	sc_ioh;
36 
37 	/*
38 	 * I/O region used by the AMCC S5920 found on the PCI509 card
39 	 * used to access the data.
40 	 */
41 	bus_space_tag_t		sc_iot_s5920;
42 	bus_space_handle_t	sc_ioh_s5920;
43 
44 	struct ksensor		sc_timedelta;
45 	struct ksensor		sc_signal;
46 	struct ksensordev	sc_sensordev;
47 	struct timeout		sc_timeout;	/* invalidate sensor */
48 	int			sc_trust;	/* trust time in seconds */
49 
50 	int			(*sc_read)(struct mbg_softc *, int cmd,
51 				    char *buf, size_t len,
52 				    struct timespec *tstamp);
53 };
54 
55 struct mbg_time {
56 	u_int8_t		hundreds;
57 	u_int8_t		sec;
58 	u_int8_t		min;
59 	u_int8_t		hour;
60 	u_int8_t		mday;
61 	u_int8_t		wday;	/* 1 (monday) - 7 (sunday) */
62 	u_int8_t		mon;
63 	u_int8_t		year;	/* 0 - 99 */
64 	u_int8_t		status;
65 	u_int8_t		signal;
66 	int8_t			utc_off;
67 };
68 
69 struct mbg_time_hr {
70 	u_int32_t		sec;		/* always UTC */
71 	u_int32_t		frac;		/* fractions of second */
72 	int32_t			utc_off;	/* informal only, in seconds */
73 	u_int16_t		status;
74 	u_int8_t		signal;
75 };
76 
77 /* mbg_time.status bits */
78 #define MBG_FREERUN		0x01	/* clock running on xtal */
79 #define MBG_DST_ENA		0x02	/* DST enabled */
80 #define MBG_SYNC		0x04	/* clock synced at least once */
81 #define MBG_DST_CHG		0x08	/* DST change announcement */
82 #define MBG_UTC			0x10	/* special UTC firmware is installed */
83 #define MBG_LEAP		0x20	/* announcement of a leap second */
84 #define MBG_IFTM		0x40	/* current time was set from host */
85 #define MBG_INVALID		0x80	/* time invalid, batt. was disconn. */
86 
87 /* AMCC S5920 registers */
88 #define AMCC_DATA		0x00	/* data register, on 2nd IO region */
89 #define AMCC_OMB		0x0c	/* outgoing mailbox */
90 #define AMCC_IMB		0x1c	/* incoming mailbox */
91 
92 /* AMCC S5933 registers */
93 #define AMCC_OMB1		0x00	/* outgoing mailbox 1 */
94 #define AMCC_IMB4		0x1c	/* incoming mailbox 4 */
95 #define AMCC_FIFO		0x20	/* FIFO register */
96 #define AMCC_INTCSR		0x38	/* interrupt control/status register */
97 #define AMCC_MCSR		0x3c	/* master control/status register */
98 
99 /* ASIC registers */
100 #define ASIC_CFG		0x00
101 #define ASIC_FEATURES		0x08	/* r/o */
102 #define ASIC_STATUS		0x10
103 #define ASIC_CTLSTATUS		0x14
104 #define ASIC_DATA		0x18
105 #define ASIC_RES1		0x1c
106 #define ASIC_ADDON		0x20
107 
108 /* commands */
109 #define MBG_GET_TIME		0x00
110 #define MBG_GET_SYNC_TIME	0x02
111 #define MBG_GET_TIME_HR		0x03
112 #define MBG_SET_TIME		0x10
113 #define MBG_GET_TZCODE		0x32
114 #define MBG_SET_TZCODE		0x33
115 #define MBG_GET_FW_ID_1		0x40
116 #define MBG_GET_FW_ID_2		0x41
117 #define MBG_GET_SERNUM		0x42
118 
119 /* timezone codes (for MBG_{GET|SET}_TZCODE) */
120 #define MBG_TZCODE_CET_CEST	0x00
121 #define MBG_TZCODE_CET		0x01
122 #define MBG_TZCODE_UTC		0x02
123 #define MBG_TZCODE_EET_EEST	0x03
124 
125 /* misc. constants */
126 #define MBG_FIFO_LEN		16
127 #define MBG_ID_LEN		(2 * MBG_FIFO_LEN + 1)
128 #define MBG_BUSY		0x01
129 #define MBG_SIG_BIAS		55
130 #define MBG_SIG_MAX		68
131 #define NSECPERSEC		1000000000LL	/* nanoseconds per second */
132 #define HRDIVISOR		0x100000000LL	/* for hi-res timestamp */
133 
134 int	mbg_probe(struct device *, void *, void *);
135 void	mbg_attach(struct device *, struct device *, void *);
136 void	mbg_task(void *);
137 void	mbg_task_hr(void *);
138 void	mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp,
139 	    int64_t timedelta, u_int8_t rsignal, u_int16_t status);
140 int	mbg_read_amcc_s5920(struct mbg_softc *, int cmd, char *buf, size_t len,
141 	    struct timespec *tstamp);
142 int	mbg_read_amcc_s5933(struct mbg_softc *, int cmd, char *buf, size_t len,
143 	    struct timespec *tstamp);
144 int	mbg_read_asic(struct mbg_softc *, int cmd, char *buf, size_t len,
145 	    struct timespec *tstamp);
146 void	mbg_timeout(void *);
147 
148 const struct cfattach mbg_ca = {
149 	sizeof(struct mbg_softc), mbg_probe, mbg_attach
150 };
151 
152 struct cfdriver mbg_cd = {
153 	NULL, "mbg", DV_DULL
154 };
155 
156 const struct pci_matchid mbg_devices[] = {
157 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_GPS170PCI },
158 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI32 },
159 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI509 },
160 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI510 },
161 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PCI511 },
162 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PEX511 },
163 	{ PCI_VENDOR_MEINBERG, PCI_PRODUCT_MEINBERG_PZF180PEX }
164 };
165 
166 int
167 mbg_probe(struct device *parent, void *match, void *aux)
168 {
169 	return pci_matchbyid((struct pci_attach_args *)aux, mbg_devices,
170 	    nitems(mbg_devices));
171 }
172 
173 void
174 mbg_attach(struct device *parent, struct device *self, void *aux)
175 {
176 	struct mbg_softc *sc = (struct mbg_softc *)self;
177 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
178 	struct mbg_time tframe;
179 	pcireg_t memtype;
180 	bus_size_t iosize, iosize2;
181 	int bar = PCI_MAPREG_START, signal, t_trust;
182 	const char *desc;
183 #ifdef MBG_DEBUG
184 	char fw_id[MBG_ID_LEN];
185 #endif
186 
187 	timeout_set(&sc->sc_timeout, mbg_timeout, sc);
188 
189 	/* for the PEX511 use BAR2 instead of BAR0*/
190 	if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_MEINBERG_PEX511)
191 		bar += 0x08;
192 
193 	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar);
194 	if (pci_mapreg_map(pa, bar, memtype, 0, &sc->sc_iot,
195 	    &sc->sc_ioh, NULL, &iosize, 0)) {
196 		printf(": PCI %s region not found\n",
197 		    memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory");
198 		return;
199 	}
200 
201 	if ((desc = pci_findproduct(pa->pa_id)) == NULL)
202 		desc = "Radio clock";
203 	strlcpy(sc->sc_timedelta.desc, desc, sizeof(sc->sc_timedelta.desc));
204 
205 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
206 	    sizeof(sc->sc_sensordev.xname));
207 
208 	sc->sc_timedelta.type = SENSOR_TIMEDELTA;
209 	sc->sc_timedelta.status = SENSOR_S_UNKNOWN;
210 	sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta);
211 
212 	sc->sc_signal.type = SENSOR_PERCENT;
213 	sc->sc_signal.status = SENSOR_S_UNKNOWN;
214 	strlcpy(sc->sc_signal.desc, "Signal", sizeof(sc->sc_signal.desc));
215 	sensor_attach(&sc->sc_sensordev, &sc->sc_signal);
216 
217 	t_trust = 12 * 60 * 60;		/* twelve hours */
218 
219 	switch (PCI_PRODUCT(pa->pa_id)) {
220 	case PCI_PRODUCT_MEINBERG_PCI32:
221 		sc->sc_read = mbg_read_amcc_s5933;
222 		sensor_task_register(sc, mbg_task, 10);
223 		break;
224 	case PCI_PRODUCT_MEINBERG_PCI509:
225 		/*
226 		 * map the second I/O region needed in addition to the first
227 		 * to get at the actual data.
228 		 */
229 		memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag,
230 		    PCI_MAPREG_START + 0x04);
231 		if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x04, memtype, 0,
232 		    &sc->sc_iot_s5920, &sc->sc_ioh_s5920, NULL, &iosize2, 0)) {
233 			printf(": PCI2 %s region not found\n",
234 			    memtype == PCI_MAPREG_TYPE_IO ? "I/O" : "memory");
235 
236 			/* unmap first mapped region as well if we fail */
237 			bus_space_unmap(sc->sc_iot, sc->sc_ioh, iosize);
238 			return;
239 		}
240 		sc->sc_read = mbg_read_amcc_s5920;
241 		sensor_task_register(sc, mbg_task, 10);
242 		break;
243 	case PCI_PRODUCT_MEINBERG_PCI510:
244 	case PCI_PRODUCT_MEINBERG_PCI511:
245 	case PCI_PRODUCT_MEINBERG_PEX511:
246 		sc->sc_read = mbg_read_asic;
247 		sensor_task_register(sc, mbg_task, 10);
248 		break;
249 	case PCI_PRODUCT_MEINBERG_GPS170PCI:
250 	case PCI_PRODUCT_MEINBERG_PZF180PEX:
251 		t_trust = 4 * 24 * 60 * 60;	/* four days */
252 		sc->sc_read = mbg_read_asic;
253 		sensor_task_register(sc, mbg_task_hr, 1);
254 		break;
255 	default:
256 		/* this can not normally happen, but then there is murphy */
257 		panic(": unsupported product 0x%04x", PCI_PRODUCT(pa->pa_id));
258 		break;
259 	}
260 
261 	sc->sc_trust = t_trust;
262 
263 	if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe,
264 	    sizeof(struct mbg_time), NULL)) {
265 		printf(": unknown status");
266 		sc->sc_signal.status = SENSOR_S_CRIT;
267 	} else {
268 		sc->sc_signal.status = SENSOR_S_OK;
269 		signal = tframe.signal - MBG_SIG_BIAS;
270 		if (signal < 0)
271 			signal = 0;
272 		else if (signal > MBG_SIG_MAX)
273 			signal = MBG_SIG_MAX;
274 		sc->sc_signal.value = signal;
275 
276 		if (tframe.status & MBG_SYNC)
277 			printf(": synchronized");
278 		else
279 			printf(": not synchronized");
280 		if (tframe.status & MBG_FREERUN) {
281 			sc->sc_signal.status = SENSOR_S_WARN;
282 			printf(", free running");
283 		}
284 		if (tframe.status & MBG_IFTM)
285 			printf(", time set from host");
286 	}
287 #ifdef MBG_DEBUG
288 	if (sc->sc_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) ||
289 	    sc->sc_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN,
290 	    NULL))
291 		printf(", firmware unknown");
292 	else {
293 		fw_id[MBG_ID_LEN - 1] = '\0';
294 		printf(", firmware %s", fw_id);
295 	}
296 #endif
297 	printf("\n");
298 	sensordev_install(&sc->sc_sensordev);
299 	timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
300 }
301 
302 /*
303  * mbg_task() reads a timestamp from cards that to not provide a high
304  * resolution timestamp.  The precision is limited to 1/100 sec.
305  */
306 void
307 mbg_task(void *arg)
308 {
309 	struct mbg_softc *sc = (struct mbg_softc *)arg;
310 	struct mbg_time tframe;
311 	struct clock_ymdhms ymdhms;
312 	struct timespec tstamp;
313 	int64_t timedelta;
314 	time_t trecv;
315 
316 	if (sc->sc_read(sc, MBG_GET_TIME, (char *)&tframe, sizeof(tframe),
317 	    &tstamp)) {
318 		sc->sc_signal.status = SENSOR_S_CRIT;
319 		return;
320 	}
321 	if (tframe.status & MBG_INVALID) {
322 		sc->sc_signal.status = SENSOR_S_CRIT;
323 		return;
324 	}
325 	ymdhms.dt_year = tframe.year + 2000;
326 	ymdhms.dt_mon = tframe.mon;
327 	ymdhms.dt_day = tframe.mday;
328 	ymdhms.dt_hour = tframe.hour;
329 	ymdhms.dt_min = tframe.min;
330 	ymdhms.dt_sec = tframe.sec;
331 	trecv = clock_ymdhms_to_secs(&ymdhms) - tframe.utc_off * 3600;
332 
333 	timedelta = (int64_t)((tstamp.tv_sec - trecv) * 100
334 	    - tframe.hundreds) * 10000000LL + tstamp.tv_nsec;
335 
336 	mbg_update_sensor(sc, &tstamp, timedelta, tframe.signal,
337 	    (u_int16_t)tframe.status);
338 }
339 
340 /*
341  * mbg_task_hr() reads a timestamp from cards that do provide a high
342  * resolution timestamp.
343  */
344 void
345 mbg_task_hr(void *arg)
346 {
347 	struct mbg_softc *sc = (struct mbg_softc *)arg;
348 	struct mbg_time_hr tframe;
349 	struct timespec tstamp;
350 	int64_t tlocal, trecv;
351 
352 	if (sc->sc_read(sc, MBG_GET_TIME_HR, (char *)&tframe, sizeof(tframe),
353 	    &tstamp)) {
354 		sc->sc_signal.status = SENSOR_S_CRIT;
355 		return;
356 	}
357 	if (tframe.status & MBG_INVALID) {
358 		sc->sc_signal.status = SENSOR_S_CRIT;
359 		return;
360 	}
361 
362 	tlocal = tstamp.tv_sec * NSECPERSEC + tstamp.tv_nsec;
363 	trecv = letoh32(tframe.sec) * NSECPERSEC +
364 	    (letoh32(tframe.frac) * NSECPERSEC >> 32);
365 
366 	mbg_update_sensor(sc, &tstamp, tlocal - trecv, tframe.signal,
367 	    letoh16(tframe.status));
368 }
369 
370 /* update the sensor value, common to all cards */
371 void
372 mbg_update_sensor(struct mbg_softc *sc, struct timespec *tstamp,
373     int64_t timedelta, u_int8_t rsignal, u_int16_t status)
374 {
375 	int signal;
376 
377 	sc->sc_timedelta.value = timedelta;
378 	sc->sc_timedelta.tv.tv_sec = tstamp->tv_sec;
379 	sc->sc_timedelta.tv.tv_usec = tstamp->tv_nsec / 1000;
380 
381 	signal = rsignal - MBG_SIG_BIAS;
382 	if (signal < 0)
383 		signal = 0;
384 	else if (signal > MBG_SIG_MAX)
385 		signal = MBG_SIG_MAX;
386 
387 	sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX;
388 	sc->sc_signal.status = status & MBG_FREERUN ?
389 	    SENSOR_S_WARN : SENSOR_S_OK;
390 	sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec;
391 	sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec;
392 	if (!(status & MBG_FREERUN)) {
393 		sc->sc_timedelta.status = SENSOR_S_OK;
394 		timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
395 	}
396 }
397 
398 /*
399  * send a command and read back results to an AMCC S5920 based card
400  * (e.g. the PCI509 DCF77 radio clock)
401  */
402 int
403 mbg_read_amcc_s5920(struct mbg_softc *sc, int cmd, char *buf, size_t len,
404     struct timespec *tstamp)
405 {
406 	long timer, tmax;
407 	size_t quot, rem;
408 	u_int32_t ul;
409 	int n;
410 	u_int8_t status;
411 
412 	quot = len / 4;
413 	rem = len % 4;
414 
415 	/* write the command, optionally taking a timestamp */
416 	if (tstamp)
417 		nanotime(tstamp);
418 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB, cmd);
419 
420 	/* wait for the BUSY flag to go low (approx 70 us on i386) */
421 	timer = 0;
422 	tmax = cold ? 50 : 10;
423 	do {
424 		if (cold)
425 			delay(20);
426 		else
427 			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
428 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
429 		    AMCC_IMB4 + 3);
430 	} while ((status & MBG_BUSY) && timer++ < tmax);
431 
432 	if (status & MBG_BUSY)
433 		return -1;
434 
435 	/* read data from the device */
436 	if (len) {
437 		for (n = 0; n < quot; n++) {
438 			*(u_int32_t *)buf = bus_space_read_4(sc->sc_iot_s5920,
439 			    sc->sc_ioh_s5920, AMCC_DATA);
440 			buf += sizeof(u_int32_t);
441 		}
442 		if (rem) {
443 			ul =  bus_space_read_4(sc->sc_iot_s5920,
444 			    sc->sc_ioh_s5920, AMCC_DATA);
445 			for (n = 0; n < rem; n++)
446 				*buf++ = *((char *)&ul + n);
447 		}
448 	} else
449 		bus_space_read_4(sc->sc_iot_s5920, sc->sc_ioh_s5920, AMCC_DATA);
450 	return 0;
451 }
452 
453 /*
454  * send a command and read back results to an AMCC S5933 based card
455  * (e.g. the PCI32 DCF77 radio clock)
456  */
457 int
458 mbg_read_amcc_s5933(struct mbg_softc *sc, int cmd, char *buf, size_t len,
459     struct timespec *tstamp)
460 {
461 	long timer, tmax;
462 	size_t n;
463 	u_int8_t status;
464 
465 	/* reset inbound mailbox and clear FIFO status */
466 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_MCSR + 3, 0x0c);
467 
468 	/* set FIFO */
469 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_INTCSR + 3, 0x3c);
470 
471 	/* write the command, optionally taking a timestamp */
472 	if (tstamp)
473 		nanotime(tstamp);
474 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMCC_OMB1, cmd);
475 
476 	/* wait for the BUSY flag to go low (approx 70 us on i386) */
477 	timer = 0;
478 	tmax = cold ? 50 : 10;
479 	do {
480 		if (cold)
481 			delay(20);
482 		else
483 			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
484 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
485 		    AMCC_IMB4 + 3);
486 	} while ((status & MBG_BUSY) && timer++ < tmax);
487 
488 	if (status & MBG_BUSY)
489 		return -1;
490 
491 	/* read data from the device FIFO */
492 	for (n = 0; n < len; n++) {
493 		if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, AMCC_MCSR)
494 		    & 0x20) {
495 			return -1;
496 		}
497 		buf[n] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
498 		    AMCC_FIFO + (n % 4));
499 	}
500 	return 0;
501 }
502 
503 /*
504  * send a command and read back results to an ASIC based card
505  * (e.g. the PCI511 DCF77 radio clock)
506  */
507 int
508 mbg_read_asic(struct mbg_softc *sc, int cmd, char *buf, size_t len,
509     struct timespec *tstamp)
510 {
511 	long timer, tmax;
512 	size_t n;
513 	u_int32_t data;
514 	u_int16_t port;
515 	char *p = buf;
516 	u_int8_t status;
517 	int s;
518 
519 	/* write the command, optionally taking a timestamp */
520 	if (tstamp) {
521 		s = splhigh();
522 		nanotime(tstamp);
523 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
524 		splx(s);
525 	} else
526 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, ASIC_DATA, cmd);
527 
528 	/* wait for the BUSY flag to go low */
529 	timer = 0;
530 	tmax = cold ? 50 : 10;
531 	do {
532 		if (cold)
533 			delay(20);
534 		else
535 			tsleep_nsec(tstamp, 0, "mbg", MSEC_TO_NSEC(1));
536 		status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ASIC_STATUS);
537 	} while ((status & MBG_BUSY) && timer++ < tmax);
538 
539 	if (status & MBG_BUSY)
540 		return -1;
541 
542 	/* read data from the device FIFO */
543 	port = ASIC_ADDON;
544 	for (n = 0; n < len / 4; n++) {
545 		data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
546 		*(u_int32_t *)p = data;
547 		p += sizeof(data);
548 		port += sizeof(data);
549 	}
550 
551 	if (len % 4) {
552 		data = bus_space_read_4(sc->sc_iot, sc->sc_ioh, port);
553 		for (n = 0; n < len % 4; n++) {
554 			*p++ = data & 0xff;
555 			data >>= 8;
556 		}
557 	}
558 	return 0;
559 }
560 
561 /*
562  * degrade the sensor state if we are freerunning for more than
563  * sc->sc_trust seconds.
564  */
565 void
566 mbg_timeout(void *xsc)
567 {
568 	struct mbg_softc *sc = xsc;
569 
570 	if (sc->sc_timedelta.status == SENSOR_S_OK) {
571 		sc->sc_timedelta.status = SENSOR_S_WARN;
572 		/*
573 		 * further degrade in sc->sc_trust seconds if no new valid
574 		 * time data can be read from the device.
575 		 */
576 		timeout_add_sec(&sc->sc_timeout, sc->sc_trust);
577 	} else
578 		sc->sc_timedelta.status = SENSOR_S_CRIT;
579 }
580