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