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