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