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