xref: /openbsd-src/sys/dev/gpio/gpiodcf.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1*0f9e9ec2Sjsg /*	$OpenBSD: gpiodcf.c,v 1.11 2024/05/13 01:15:50 jsg Exp $ */
2c9851b67Smbalmer 
3c9851b67Smbalmer /*
4c9851b67Smbalmer  * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
5c9851b67Smbalmer  *
6c9851b67Smbalmer  * Permission to use, copy, modify, and distribute this software for any
7c9851b67Smbalmer  * purpose with or without fee is hereby granted, provided that the above
8c9851b67Smbalmer  * copyright notice and this permission notice appear in all copies.
9c9851b67Smbalmer  *
10c9851b67Smbalmer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11c9851b67Smbalmer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c9851b67Smbalmer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c9851b67Smbalmer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c9851b67Smbalmer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c9851b67Smbalmer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c9851b67Smbalmer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c9851b67Smbalmer  */
18c9851b67Smbalmer 
19c9851b67Smbalmer #include <sys/param.h>
20c9851b67Smbalmer #include <sys/systm.h>
21c9851b67Smbalmer #include <sys/kernel.h>
22c9851b67Smbalmer #include <sys/conf.h>
23c9851b67Smbalmer #include <sys/proc.h>
24c9851b67Smbalmer #include <sys/vnode.h>
25c9851b67Smbalmer #include <sys/device.h>
26c9851b67Smbalmer #include <sys/time.h>
27c9851b67Smbalmer #include <sys/sensors.h>
28c9851b67Smbalmer #include <sys/gpio.h>
29c9851b67Smbalmer 
30c9851b67Smbalmer #include <dev/gpio/gpiovar.h>
31c9851b67Smbalmer 
32c9851b67Smbalmer #ifdef GPIODCF_DEBUG
33c9851b67Smbalmer #define DPRINTFN(n, x)	do { if (gpiodcfdebug > (n)) printf x; } while (0)
34c9851b67Smbalmer int gpiodcfdebug = 0;
35c9851b67Smbalmer #else
36c9851b67Smbalmer #define DPRINTFN(n, x)
37c9851b67Smbalmer #endif
38c9851b67Smbalmer #define DPRINTF(x)	DPRINTFN(0, x)
39c9851b67Smbalmer 
40c9851b67Smbalmer /* max. skew of received time diff vs. measured time diff in percent. */
41c9851b67Smbalmer #define MAX_SKEW	5
42c9851b67Smbalmer 
43c9851b67Smbalmer #define GPIODCF_NPINS		1
44c9851b67Smbalmer #define	GPIODCF_PIN_DATA	0
45c9851b67Smbalmer 
46c9851b67Smbalmer struct gpiodcf_softc {
47c9851b67Smbalmer 	struct device		sc_dev;		/* base device */
48c9851b67Smbalmer 	void			*sc_gpio;
49c9851b67Smbalmer 	struct gpio_pinmap	sc_map;
50c9851b67Smbalmer 	int			__map[GPIODCF_NPINS];
51c9851b67Smbalmer 	u_char			sc_dying;	/* disconnecting */
52c9851b67Smbalmer 	int			sc_data;
53c9851b67Smbalmer 
54c9851b67Smbalmer 	struct timeout		sc_to;
55c9851b67Smbalmer 
56c9851b67Smbalmer 	struct timeout		sc_bv_to;	/* bit-value detect */
57c9851b67Smbalmer 	struct timeout		sc_db_to;	/* debounce */
58c9851b67Smbalmer 	struct timeout		sc_mg_to;	/* minute-gap detect */
59c9851b67Smbalmer 	struct timeout		sc_sl_to;	/* signal-loss detect */
60c9851b67Smbalmer 	struct timeout		sc_it_to;	/* invalidate time */
61c9851b67Smbalmer 
62c9851b67Smbalmer 	int			sc_sync;	/* 1 during sync */
63c9851b67Smbalmer 	u_int64_t		sc_mask;	/* 64 bit mask */
64c9851b67Smbalmer 	u_int64_t		sc_tbits;	/* Time bits */
65c9851b67Smbalmer 	int			sc_minute;
66c9851b67Smbalmer 	int			sc_level;
67c9851b67Smbalmer 	time_t			sc_last_mg;
68c9851b67Smbalmer 	time_t			sc_current;	/* current time */
69c9851b67Smbalmer 	time_t			sc_next;	/* time to become valid next */
70c9851b67Smbalmer 	time_t			sc_last;
71c9851b67Smbalmer 	int			sc_nrecv;	/* consecutive valid times */
72c9851b67Smbalmer 	struct timeval		sc_last_tv;	/* uptime of last valid time */
73c9851b67Smbalmer 	struct ksensor		sc_sensor;
74c9851b67Smbalmer #ifdef GPIODCF_DEBUG
75c9851b67Smbalmer 	struct ksensor		sc_skew;	/* recv vs local skew */
76c9851b67Smbalmer #endif
77c9851b67Smbalmer 	struct ksensordev	sc_sensordev;
78c9851b67Smbalmer };
79c9851b67Smbalmer 
80c9851b67Smbalmer /*
8194536692Scheloha  * timeouts used:
82c9851b67Smbalmer  */
8394536692Scheloha #define	T_BV		150	/* bit value detection (150ms) */
8494536692Scheloha #define	T_SYNC		950	/* sync (950ms) */
8594536692Scheloha #define	T_MG		1500	/* minute gap detection (1500ms) */
8694536692Scheloha #define	T_MGSYNC	450	/* resync after a minute gap (450ms) */
8794536692Scheloha #define	T_SL		3000	/* detect signal loss (3sec) */
8894536692Scheloha #define	T_WAIT		5000	/* wait (5sec) */
892f640e26Scheloha #define	T_WARN		300000	/* degrade sensor status to warning (5min) */
9094536692Scheloha #define	T_CRIT		900000	/* degrade sensor status to critical (15min) */
91c9851b67Smbalmer 
92c9851b67Smbalmer void	gpiodcf_probe(void *);
93c9851b67Smbalmer void	gpiodcf_bv_probe(void *);
94c9851b67Smbalmer void	gpiodcf_mg_probe(void *);
95c9851b67Smbalmer void	gpiodcf_sl_probe(void *);
96c9851b67Smbalmer void	gpiodcf_invalidate(void *);
97c9851b67Smbalmer 
98c9851b67Smbalmer int gpiodcf_match(struct device *, void *, void *);
99c9851b67Smbalmer void gpiodcf_attach(struct device *, struct device *, void *);
100c9851b67Smbalmer int gpiodcf_detach(struct device *, int);
101e78728c7Spirofti int gpiodcf_activate(struct device *, int);
102c9851b67Smbalmer 
103c9851b67Smbalmer int gpiodcf_signal(struct gpiodcf_softc *);
104c9851b67Smbalmer 
105c9851b67Smbalmer struct cfdriver gpiodcf_cd = {
106c9851b67Smbalmer 	NULL, "gpiodcf", DV_DULL
107c9851b67Smbalmer };
108c9851b67Smbalmer 
109c9851b67Smbalmer const struct cfattach gpiodcf_ca = {
110c9851b67Smbalmer 	sizeof(struct gpiodcf_softc),
111c9851b67Smbalmer 	gpiodcf_match,
112c9851b67Smbalmer 	gpiodcf_attach,
113c9851b67Smbalmer 	gpiodcf_detach,
114c9851b67Smbalmer 	gpiodcf_activate
115c9851b67Smbalmer };
116c9851b67Smbalmer 
117c9851b67Smbalmer int
gpiodcf_match(struct device * parent,void * match,void * aux)118c9851b67Smbalmer gpiodcf_match(struct device *parent, void *match, void *aux)
119c9851b67Smbalmer {
120c9851b67Smbalmer 	struct cfdata *cf = match;
121c9851b67Smbalmer 	struct gpio_attach_args *ga = aux;
122c9851b67Smbalmer 
123c9851b67Smbalmer 	if (ga->ga_offset == -1)
124c9851b67Smbalmer 		return 0;
125c9851b67Smbalmer 
126c9851b67Smbalmer 	return (strcmp(cf->cf_driver->cd_name, "gpiodcf") == 0);
127c9851b67Smbalmer }
128c9851b67Smbalmer 
129c9851b67Smbalmer void
gpiodcf_attach(struct device * parent,struct device * self,void * aux)130c9851b67Smbalmer gpiodcf_attach(struct device *parent, struct device *self, void *aux)
131c9851b67Smbalmer {
132c9851b67Smbalmer 	struct gpiodcf_softc		*sc = (struct gpiodcf_softc *)self;
133c9851b67Smbalmer 	struct gpio_attach_args		*ga = aux;
134c9851b67Smbalmer 	int				 caps;
135c9851b67Smbalmer 
136c9851b67Smbalmer 	if (gpio_npins(ga->ga_mask) != GPIODCF_NPINS) {
137c9851b67Smbalmer 		printf(": invalid pin mask\n");
138c9851b67Smbalmer 		return;
139c9851b67Smbalmer 	}
140c9851b67Smbalmer 	sc->sc_gpio = ga->ga_gpio;
141c9851b67Smbalmer 	sc->sc_map.pm_map = sc->__map;
142c9851b67Smbalmer 	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
143c9851b67Smbalmer 	    &sc->sc_map)) {
144c9851b67Smbalmer 		printf(": can't map pins\n");
145c9851b67Smbalmer 		return;
146c9851b67Smbalmer 	}
147c9851b67Smbalmer 
148c9851b67Smbalmer 	caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA);
149c9851b67Smbalmer 	if (!(caps & GPIO_PIN_INPUT)) {
150c9851b67Smbalmer 		printf(": data pin is unable to receive input\n");
151c9851b67Smbalmer 		goto fishy;
152c9851b67Smbalmer 	}
153c9851b67Smbalmer 	printf(": DATA[%d]", sc->sc_map.pm_map[GPIODCF_PIN_DATA]);
154c9851b67Smbalmer 	sc->sc_data = GPIO_PIN_INPUT;
155c9851b67Smbalmer 	gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA, sc->sc_data);
156c9851b67Smbalmer 	printf("\n");
157c9851b67Smbalmer 
158947eb244Sclaudio 	strlcpy(sc->sc_sensor.desc, "DCF77", sizeof(sc->sc_sensor.desc));
159c9851b67Smbalmer 
160c9851b67Smbalmer 	timeout_set(&sc->sc_to, gpiodcf_probe, sc);
161c9851b67Smbalmer 	timeout_set(&sc->sc_bv_to, gpiodcf_bv_probe, sc);
162c9851b67Smbalmer 	timeout_set(&sc->sc_mg_to, gpiodcf_mg_probe, sc);
163c9851b67Smbalmer 	timeout_set(&sc->sc_sl_to, gpiodcf_sl_probe, sc);
164c9851b67Smbalmer 	timeout_set(&sc->sc_it_to, gpiodcf_invalidate, sc);
165c9851b67Smbalmer 
166c9851b67Smbalmer 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
167c9851b67Smbalmer 	    sizeof(sc->sc_sensordev.xname));
168c9851b67Smbalmer 
169c9851b67Smbalmer 	sc->sc_sensor.type = SENSOR_TIMEDELTA;
170c9851b67Smbalmer 	sc->sc_sensor.status = SENSOR_S_UNKNOWN;
171c9851b67Smbalmer 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor);
172c9851b67Smbalmer 
173c9851b67Smbalmer #ifdef GPIODCF_DEBUG
174c9851b67Smbalmer 	sc->sc_skew.type = SENSOR_TIMEDELTA;
175c9851b67Smbalmer 	sc->sc_skew.status = SENSOR_S_UNKNOWN;
176c9851b67Smbalmer 	strlcpy(sc->sc_skew.desc, "local clock skew",
177c9851b67Smbalmer 	    sizeof(sc->sc_skew.desc));
178c9851b67Smbalmer 	sensor_attach(&sc->sc_sensordev, &sc->sc_skew);
179c9851b67Smbalmer #endif
180c9851b67Smbalmer 	sensordev_install(&sc->sc_sensordev);
181c9851b67Smbalmer 
182c9851b67Smbalmer 	sc->sc_level = 0;
183c9851b67Smbalmer 	sc->sc_minute = 0;
184c9851b67Smbalmer 	sc->sc_last_mg = 0L;
185c9851b67Smbalmer 
186c9851b67Smbalmer 	sc->sc_sync = 1;
187c9851b67Smbalmer 
188c9851b67Smbalmer 	sc->sc_current = 0L;
189c9851b67Smbalmer 	sc->sc_next = 0L;
190c9851b67Smbalmer 	sc->sc_nrecv = 0;
191c9851b67Smbalmer 	sc->sc_last = 0L;
192c9851b67Smbalmer 	sc->sc_last_tv.tv_sec = 0L;
193c9851b67Smbalmer 
194c9851b67Smbalmer 	/* Give the receiver some slack to stabilize */
19594536692Scheloha 	timeout_add_msec(&sc->sc_to, T_WAIT);
196c9851b67Smbalmer 
197c9851b67Smbalmer 	/* Detect signal loss */
19894536692Scheloha 	timeout_add_msec(&sc->sc_sl_to, T_WAIT + T_SL);
199c9851b67Smbalmer 
200c9851b67Smbalmer 	DPRINTF(("synchronizing\n"));
201c9851b67Smbalmer 	return;
202c9851b67Smbalmer 
203c9851b67Smbalmer fishy:
204c9851b67Smbalmer 	DPRINTF(("gpiodcf_attach failed\n"));
205c9851b67Smbalmer 	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
206c9851b67Smbalmer 	sc->sc_dying = 1;
207c9851b67Smbalmer }
208c9851b67Smbalmer 
209c9851b67Smbalmer int
gpiodcf_detach(struct device * self,int flags)210c9851b67Smbalmer gpiodcf_detach(struct device *self, int flags)
211c9851b67Smbalmer {
212c9851b67Smbalmer 	struct gpiodcf_softc	*sc = (struct gpiodcf_softc *)self;
213c9851b67Smbalmer 
214c9851b67Smbalmer 	sc->sc_dying = 1;
215c9851b67Smbalmer 
216c9851b67Smbalmer 	timeout_del(&sc->sc_to);
217c9851b67Smbalmer 	timeout_del(&sc->sc_bv_to);
218c9851b67Smbalmer 	timeout_del(&sc->sc_mg_to);
219c9851b67Smbalmer 	timeout_del(&sc->sc_sl_to);
220c9851b67Smbalmer 	timeout_del(&sc->sc_it_to);
221c9851b67Smbalmer 
222c9851b67Smbalmer 	/* Unregister the clock with the kernel */
223c9851b67Smbalmer 	sensordev_deinstall(&sc->sc_sensordev);
224c9851b67Smbalmer 
225c9851b67Smbalmer 	/* Finally unmap the GPIO pin */
226c9851b67Smbalmer 	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
227c9851b67Smbalmer 
228c9851b67Smbalmer 	return 0;
229c9851b67Smbalmer }
230c9851b67Smbalmer 
231c9851b67Smbalmer /*
232c9851b67Smbalmer  * return 1 during high-power-, 0 during low-power-emission
233c9851b67Smbalmer  * If bit 0 is set, the transmitter emits at full power.
234c9851b67Smbalmer  * During the low-power emission we decode a zero bit.
235c9851b67Smbalmer  */
236c9851b67Smbalmer int
gpiodcf_signal(struct gpiodcf_softc * sc)237c9851b67Smbalmer gpiodcf_signal(struct gpiodcf_softc *sc)
238c9851b67Smbalmer {
239c9851b67Smbalmer 	return (gpio_pin_read(sc->sc_gpio, &sc->sc_map, GPIODCF_PIN_DATA) ==
240c9851b67Smbalmer 	    GPIO_PIN_HIGH ? 1 : 0);
241c9851b67Smbalmer }
242c9851b67Smbalmer 
243c9851b67Smbalmer /* gpiodcf_probe runs in a process context. */
244c9851b67Smbalmer void
gpiodcf_probe(void * xsc)245c9851b67Smbalmer gpiodcf_probe(void *xsc)
246c9851b67Smbalmer {
247c9851b67Smbalmer 	struct gpiodcf_softc	*sc = xsc;
248c9851b67Smbalmer 	struct timespec		 now;
249c9851b67Smbalmer 	int			 data;
250c9851b67Smbalmer 
251c9851b67Smbalmer 	if (sc->sc_dying)
252c9851b67Smbalmer 		return;
253c9851b67Smbalmer 
254c9851b67Smbalmer 	data = gpiodcf_signal(sc);
255c9851b67Smbalmer 	if (data == -1)
256c9851b67Smbalmer 		return;
257c9851b67Smbalmer 
258c9851b67Smbalmer 	if (data) {
259c9851b67Smbalmer 		sc->sc_level = 1;
260c9851b67Smbalmer 		timeout_add(&sc->sc_to, 1);
261c9851b67Smbalmer 		return;
262c9851b67Smbalmer 	}
263c9851b67Smbalmer 
264c9851b67Smbalmer 	if (sc->sc_level == 0)
265c9851b67Smbalmer 		return;
266c9851b67Smbalmer 
267c9851b67Smbalmer 	/* the beginning of a second */
268c9851b67Smbalmer 	sc->sc_level = 0;
269c9851b67Smbalmer 	if (sc->sc_minute == 1) {
270c9851b67Smbalmer 		if (sc->sc_sync) {
271c9851b67Smbalmer 			DPRINTF(("start collecting bits\n"));
272c9851b67Smbalmer 			sc->sc_sync = 0;
273c9851b67Smbalmer 		} else {
274c9851b67Smbalmer 			/* provide the timedelta */
275c9851b67Smbalmer 			microtime(&sc->sc_sensor.tv);
276c9851b67Smbalmer 			nanotime(&now);
277c9851b67Smbalmer 			sc->sc_current = sc->sc_next;
278c9851b67Smbalmer 			sc->sc_sensor.value = (int64_t)(now.tv_sec -
279c9851b67Smbalmer 			    sc->sc_current) * 1000000000LL + now.tv_nsec;
280c9851b67Smbalmer 
281c9851b67Smbalmer 			sc->sc_sensor.status = SENSOR_S_OK;
282c9851b67Smbalmer 
283c9851b67Smbalmer 			/*
284c9851b67Smbalmer 			 * if no valid time information is received
285c9851b67Smbalmer 			 * during the next 5 minutes, the sensor state
286c9851b67Smbalmer 			 * will be degraded to SENSOR_S_WARN
287c9851b67Smbalmer 			 */
28894536692Scheloha 			timeout_add_msec(&sc->sc_it_to, T_WARN);
289c9851b67Smbalmer 		}
290c9851b67Smbalmer 		sc->sc_minute = 0;
291c9851b67Smbalmer 	}
292c9851b67Smbalmer 
29394536692Scheloha 	timeout_add_msec(&sc->sc_to, T_SYNC);	/* resync in 950 ms */
294c9851b67Smbalmer 
295c9851b67Smbalmer 	/* no clock and bit detection during sync */
296c9851b67Smbalmer 	if (!sc->sc_sync) {
297c9851b67Smbalmer 		/* detect bit value */
29894536692Scheloha 		timeout_add_msec(&sc->sc_bv_to, T_BV);
299c9851b67Smbalmer 	}
30094536692Scheloha 	timeout_add_msec(&sc->sc_mg_to, T_MG);	/* detect minute gap */
30194536692Scheloha 	timeout_add_msec(&sc->sc_sl_to, T_SL);	/* detect signal loss */
302c9851b67Smbalmer }
303c9851b67Smbalmer 
304c9851b67Smbalmer /* detect the bit value */
305c9851b67Smbalmer void
gpiodcf_bv_probe(void * xsc)306c9851b67Smbalmer gpiodcf_bv_probe(void *xsc)
307c9851b67Smbalmer {
308c9851b67Smbalmer 	struct gpiodcf_softc	*sc = xsc;
309c9851b67Smbalmer 	int			 data;
310c9851b67Smbalmer 
311c9851b67Smbalmer 	if (sc->sc_dying)
312c9851b67Smbalmer 		return;
313c9851b67Smbalmer 
314c9851b67Smbalmer 	data = gpiodcf_signal(sc);
315c9851b67Smbalmer 	if (data == -1) {
316c9851b67Smbalmer 		DPRINTF(("bit detection failed\n"));
317c9851b67Smbalmer 		return;
318c9851b67Smbalmer 	}
319c9851b67Smbalmer 
320c9851b67Smbalmer 	DPRINTFN(1, (data ? "0" : "1"));
321c9851b67Smbalmer 	if (!(data))
322c9851b67Smbalmer 		sc->sc_tbits |= sc->sc_mask;
323c9851b67Smbalmer 	sc->sc_mask <<= 1;
324c9851b67Smbalmer }
325c9851b67Smbalmer 
326c9851b67Smbalmer /* detect the minute gap */
327c9851b67Smbalmer void
gpiodcf_mg_probe(void * xsc)328c9851b67Smbalmer gpiodcf_mg_probe(void *xsc)
329c9851b67Smbalmer {
330c9851b67Smbalmer 	struct gpiodcf_softc	*sc = xsc;
331c9851b67Smbalmer 	struct clock_ymdhms	 ymdhm;
332c9851b67Smbalmer 	struct timeval		 monotime;
333c9851b67Smbalmer 	int			 tdiff_recv, tdiff_local;
334c9851b67Smbalmer 	int			 skew;
335c9851b67Smbalmer 	int			 minute_bits, hour_bits, day_bits;
336c9851b67Smbalmer 	int			 month_bits, year_bits, wday;
337c9851b67Smbalmer 	int			 p1, p2, p3;
338c9851b67Smbalmer 	int			 p1_bit, p2_bit, p3_bit;
339c9851b67Smbalmer 	int			 r_bit, a1_bit, a2_bit, z1_bit, z2_bit;
340c9851b67Smbalmer 	int			 s_bit, m_bit;
341c9851b67Smbalmer 	u_int32_t		 parity = 0x6996;
342c9851b67Smbalmer 
343c9851b67Smbalmer 	if (sc->sc_sync) {
344c9851b67Smbalmer 		sc->sc_minute = 1;
345c9851b67Smbalmer 		goto cleanbits;
346c9851b67Smbalmer 	}
347c9851b67Smbalmer 
3483209772dScheloha 	if (gettime() - sc->sc_last_mg < 57) {
349c9851b67Smbalmer 		DPRINTF(("\nunexpected gap, resync\n"));
350c9851b67Smbalmer 		sc->sc_sync = sc->sc_minute = 1;
351c9851b67Smbalmer 		goto cleanbits;
352c9851b67Smbalmer 	}
353c9851b67Smbalmer 
354c9851b67Smbalmer 	/* extract bits w/o parity */
355c9851b67Smbalmer 	m_bit = sc->sc_tbits & 1;
356c9851b67Smbalmer 	r_bit = sc->sc_tbits >> 15 & 1;
357c9851b67Smbalmer 	a1_bit = sc->sc_tbits >> 16 & 1;
358c9851b67Smbalmer 	z1_bit = sc->sc_tbits >> 17 & 1;
359c9851b67Smbalmer 	z2_bit = sc->sc_tbits >> 18 & 1;
360c9851b67Smbalmer 	a2_bit = sc->sc_tbits >> 19 & 1;
361c9851b67Smbalmer 	s_bit = sc->sc_tbits >> 20 & 1;
362c9851b67Smbalmer 	p1_bit = sc->sc_tbits >> 28 & 1;
363c9851b67Smbalmer 	p2_bit = sc->sc_tbits >> 35 & 1;
364c9851b67Smbalmer 	p3_bit = sc->sc_tbits >> 58 & 1;
365c9851b67Smbalmer 
366c9851b67Smbalmer 	minute_bits = sc->sc_tbits >> 21 & 0x7f;
367c9851b67Smbalmer 	hour_bits = sc->sc_tbits >> 29 & 0x3f;
368c9851b67Smbalmer 	day_bits = sc->sc_tbits >> 36 & 0x3f;
369c9851b67Smbalmer 	wday = (sc->sc_tbits >> 42) & 0x07;
370c9851b67Smbalmer 	month_bits = sc->sc_tbits >> 45 & 0x1f;
371c9851b67Smbalmer 	year_bits = sc->sc_tbits >> 50 & 0xff;
372c9851b67Smbalmer 
373c9851b67Smbalmer 	/* validate time information */
374c9851b67Smbalmer 	p1 = (parity >> (minute_bits & 0x0f) & 1) ^
375c9851b67Smbalmer 	    (parity >> (minute_bits >> 4) & 1);
376c9851b67Smbalmer 
377c9851b67Smbalmer 	p2 = (parity >> (hour_bits & 0x0f) & 1) ^
378c9851b67Smbalmer 	    (parity >> (hour_bits >> 4) & 1);
379c9851b67Smbalmer 
380c9851b67Smbalmer 	p3 = (parity >> (day_bits & 0x0f) & 1) ^
381c9851b67Smbalmer 	    (parity >> (day_bits >> 4) & 1) ^
382c9851b67Smbalmer 	    ((parity >> wday) & 1) ^ (parity >> (month_bits & 0x0f) & 1) ^
383c9851b67Smbalmer 	    (parity >> (month_bits >> 4) & 1) ^
384c9851b67Smbalmer 	    (parity >> (year_bits & 0x0f) & 1) ^
385c9851b67Smbalmer 	    (parity >> (year_bits >> 4) & 1);
386c9851b67Smbalmer 
387c9851b67Smbalmer 	if (m_bit == 0 && s_bit == 1 && p1 == p1_bit && p2 == p2_bit &&
388c9851b67Smbalmer 	    p3 == p3_bit && (z1_bit ^ z2_bit)) {
389c9851b67Smbalmer 
390c9851b67Smbalmer 		/* Decode time */
391c9851b67Smbalmer 		if ((ymdhm.dt_year = 2000 + FROMBCD(year_bits)) > 2037) {
392c9851b67Smbalmer 			DPRINTF(("year out of range, resync\n"));
393c9851b67Smbalmer 			sc->sc_sync = 1;
394c9851b67Smbalmer 			goto cleanbits;
395c9851b67Smbalmer 		}
396c9851b67Smbalmer 		ymdhm.dt_min = FROMBCD(minute_bits);
397c9851b67Smbalmer 		ymdhm.dt_hour = FROMBCD(hour_bits);
398c9851b67Smbalmer 		ymdhm.dt_day = FROMBCD(day_bits);
399c9851b67Smbalmer 		ymdhm.dt_mon = FROMBCD(month_bits);
400c9851b67Smbalmer 		ymdhm.dt_sec = 0;
401c9851b67Smbalmer 
402c9851b67Smbalmer 		sc->sc_next = clock_ymdhms_to_secs(&ymdhm);
403c9851b67Smbalmer 		getmicrouptime(&monotime);
404c9851b67Smbalmer 
405c9851b67Smbalmer 		/* convert to coordinated universal time */
406c9851b67Smbalmer 		sc->sc_next -= z1_bit ? 7200 : 3600;
407c9851b67Smbalmer 
408c9851b67Smbalmer 		DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s",
409c9851b67Smbalmer 		    ymdhm.dt_day, ymdhm.dt_mon, ymdhm.dt_year,
410c9851b67Smbalmer 		    ymdhm.dt_hour, ymdhm.dt_min, z1_bit ? "CEST" : "CET"));
411c9851b67Smbalmer 		DPRINTF((r_bit ? ", call bit" : ""));
412c9851b67Smbalmer 		DPRINTF((a1_bit ? ", dst chg ann." : ""));
413c9851b67Smbalmer 		DPRINTF((a2_bit ? ", leap sec ann." : ""));
414c9851b67Smbalmer 		DPRINTF(("\n"));
415c9851b67Smbalmer 
416c9851b67Smbalmer 		if (sc->sc_last) {
417c9851b67Smbalmer 			tdiff_recv = sc->sc_next - sc->sc_last;
418c9851b67Smbalmer 			tdiff_local = monotime.tv_sec - sc->sc_last_tv.tv_sec;
419c9851b67Smbalmer 			skew = abs(tdiff_local - tdiff_recv);
420c9851b67Smbalmer #ifdef GPIODCF_DEBUG
421c9851b67Smbalmer 			if (sc->sc_skew.status == SENSOR_S_UNKNOWN)
422c9851b67Smbalmer 				sc->sc_skew.status = SENSOR_S_CRIT;
423c9851b67Smbalmer 			sc->sc_skew.value = skew * 1000000000LL;
424c9851b67Smbalmer 			getmicrotime(&sc->sc_skew.tv);
425c9851b67Smbalmer #endif
426c9851b67Smbalmer 			DPRINTF(("local = %d, recv = %d, skew = %d\n",
427c9851b67Smbalmer 			    tdiff_local, tdiff_recv, skew));
428c9851b67Smbalmer 
429c9851b67Smbalmer 			if (skew && skew * 100LL / tdiff_local > MAX_SKEW) {
430c9851b67Smbalmer 				DPRINTF(("skew out of tolerated range\n"));
431c9851b67Smbalmer 				goto cleanbits;
432c9851b67Smbalmer 			} else {
433c9851b67Smbalmer 				if (sc->sc_nrecv < 2) {
434c9851b67Smbalmer 					sc->sc_nrecv++;
435c9851b67Smbalmer 					DPRINTF(("got frame %d\n",
436c9851b67Smbalmer 					    sc->sc_nrecv));
437c9851b67Smbalmer 				} else {
438c9851b67Smbalmer 					DPRINTF(("data is valid\n"));
439c9851b67Smbalmer 					sc->sc_minute = 1;
440c9851b67Smbalmer 				}
441c9851b67Smbalmer 			}
442c9851b67Smbalmer 		} else {
443c9851b67Smbalmer 			DPRINTF(("received the first frame\n"));
444c9851b67Smbalmer 			sc->sc_nrecv = 1;
445c9851b67Smbalmer 		}
446c9851b67Smbalmer 
447c9851b67Smbalmer 		/* record the time received and when it was received */
448c9851b67Smbalmer 		sc->sc_last = sc->sc_next;
449c9851b67Smbalmer 		sc->sc_last_tv.tv_sec = monotime.tv_sec;
450c9851b67Smbalmer 	} else {
451c9851b67Smbalmer 		DPRINTF(("\nparity error, resync\n"));
452c9851b67Smbalmer 		sc->sc_sync = sc->sc_minute = 1;
453c9851b67Smbalmer 	}
454c9851b67Smbalmer 
455c9851b67Smbalmer cleanbits:
45694536692Scheloha 	timeout_add_msec(&sc->sc_to, T_MGSYNC);	/* re-sync in 450 ms */
4573209772dScheloha 	sc->sc_last_mg = gettime();
458c9851b67Smbalmer 	sc->sc_tbits = 0LL;
459c9851b67Smbalmer 	sc->sc_mask = 1LL;
460c9851b67Smbalmer }
461c9851b67Smbalmer 
462c9851b67Smbalmer /* detect signal loss */
463c9851b67Smbalmer void
gpiodcf_sl_probe(void * xsc)464c9851b67Smbalmer gpiodcf_sl_probe(void *xsc)
465c9851b67Smbalmer {
466c9851b67Smbalmer 	struct gpiodcf_softc *sc = xsc;
467c9851b67Smbalmer 
468c9851b67Smbalmer 	if (sc->sc_dying)
469c9851b67Smbalmer 		return;
470c9851b67Smbalmer 
471c9851b67Smbalmer 	DPRINTF(("no signal\n"));
472c9851b67Smbalmer 	sc->sc_sync = 1;
47394536692Scheloha 	timeout_add_msec(&sc->sc_to, T_WAIT);
47494536692Scheloha 	timeout_add_msec(&sc->sc_sl_to, T_WAIT + T_SL);
475c9851b67Smbalmer }
476c9851b67Smbalmer 
477c9851b67Smbalmer /* invalidate timedelta (called in an interrupt context) */
478c9851b67Smbalmer void
gpiodcf_invalidate(void * xsc)479c9851b67Smbalmer gpiodcf_invalidate(void *xsc)
480c9851b67Smbalmer {
481c9851b67Smbalmer 	struct gpiodcf_softc *sc = xsc;
482c9851b67Smbalmer 
483c9851b67Smbalmer 	if (sc->sc_dying)
484c9851b67Smbalmer 		return;
485c9851b67Smbalmer 
486c9851b67Smbalmer 	if (sc->sc_sensor.status == SENSOR_S_OK) {
487c9851b67Smbalmer 		sc->sc_sensor.status = SENSOR_S_WARN;
488c9851b67Smbalmer 		/*
489c9851b67Smbalmer 		 * further degrade in 15 minutes if we dont receive any new
490c9851b67Smbalmer 		 * time information
491c9851b67Smbalmer 		 */
49294536692Scheloha 		timeout_add_msec(&sc->sc_it_to, T_CRIT);
493c9851b67Smbalmer 	} else {
494c9851b67Smbalmer 		sc->sc_sensor.status = SENSOR_S_CRIT;
495c9851b67Smbalmer 		sc->sc_nrecv = 0;
496c9851b67Smbalmer 	}
497c9851b67Smbalmer }
498c9851b67Smbalmer 
499c9851b67Smbalmer int
gpiodcf_activate(struct device * self,int act)500e78728c7Spirofti gpiodcf_activate(struct device *self, int act)
501c9851b67Smbalmer {
502c9851b67Smbalmer 	struct gpiodcf_softc *sc = (struct gpiodcf_softc *)self;
503c9851b67Smbalmer 
504c9851b67Smbalmer 	switch (act) {
505c9851b67Smbalmer 	case DVACT_DEACTIVATE:
506c9851b67Smbalmer 		sc->sc_dying = 1;
507c9851b67Smbalmer 		break;
508c9851b67Smbalmer 	}
509c9851b67Smbalmer 	return 0;
510c9851b67Smbalmer }
511