FreeBSD/Linux Kernel Cross Reference
sys/dev/gpio/gpiodcf.c
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 ? "" : "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 }
Cache object: 71068db3de59f2363852272640a1c2d9
|