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