xref: /netbsd-src/sys/dev/gpio/gpiopps.c (revision d1b93bab8c7801129a73a508d678c429e8f0fa6a)
1*d1b93babSmsaitoh /* $NetBSD: gpiopps.c,v 1.5 2023/06/24 05:34:59 msaitoh Exp $ */
2266bca89Sthorpej 
3266bca89Sthorpej /*
4266bca89Sthorpej  * Copyright (c) 2016 Brad Spencer <brad@anduin.eldar.org>
5266bca89Sthorpej  *
6266bca89Sthorpej  * Redistribution and use in source and binary forms, with or without
7266bca89Sthorpej  * modification, are permitted provided that the following conditions
8266bca89Sthorpej  * are met:
9266bca89Sthorpej  * 1. Redistributions of source code must retain the above copyright
10266bca89Sthorpej  *    notice, this list of conditions, and the following disclaimer.
11266bca89Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
12266bca89Sthorpej  *    notice, this list of conditions and the following disclaimer in the
13266bca89Sthorpej  *    documentation and/or other materials provided with the distribution.
14266bca89Sthorpej  *
15266bca89Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16266bca89Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17266bca89Sthorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18266bca89Sthorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19266bca89Sthorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20266bca89Sthorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21266bca89Sthorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22266bca89Sthorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23266bca89Sthorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24266bca89Sthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25266bca89Sthorpej  * SUCH DAMAGE.
26266bca89Sthorpej  */
27266bca89Sthorpej 
28266bca89Sthorpej #include <sys/cdefs.h>
29*d1b93babSmsaitoh __KERNEL_RCSID(0, "$NetBSD: gpiopps.c,v 1.5 2023/06/24 05:34:59 msaitoh Exp $");
30266bca89Sthorpej 
31266bca89Sthorpej /*
32266bca89Sthorpej  * GPIO interface to the pps subsystem for ntp support.
33266bca89Sthorpej  */
34266bca89Sthorpej 
35266bca89Sthorpej #include <sys/param.h>
36266bca89Sthorpej #include <sys/systm.h>
37266bca89Sthorpej #include <sys/bitops.h>
38266bca89Sthorpej #include <sys/device.h>
39266bca89Sthorpej #include <sys/module.h>
40266bca89Sthorpej #include <sys/conf.h>
41266bca89Sthorpej #include <sys/proc.h>
42266bca89Sthorpej #include <sys/ioctl.h>
43266bca89Sthorpej #include <sys/timepps.h>
44266bca89Sthorpej 
45266bca89Sthorpej #include <sys/gpio.h>
46266bca89Sthorpej #include <dev/gpio/gpiovar.h>
47266bca89Sthorpej 
48266bca89Sthorpej #define	GPIOPPS_NPINS		2
49266bca89Sthorpej 
50266bca89Sthorpej struct gpiopps_softc {
51266bca89Sthorpej 	device_t		sc_dev;
52266bca89Sthorpej 	void *			sc_gpio;
53266bca89Sthorpej 	struct gpio_pinmap	sc_map;
54266bca89Sthorpej 	int			_map[GPIOPPS_NPINS];
55266bca89Sthorpej 	struct {
56266bca89Sthorpej 		char		sc_intrstr[128];
57266bca89Sthorpej 		void *		sc_ih;
58266bca89Sthorpej 		int		sc_irqmode;
59266bca89Sthorpej 	} sc_intrs[GPIOPPS_NPINS];
60266bca89Sthorpej 	int			sc_assert_val;
61266bca89Sthorpej 	int			sc_npins;
62266bca89Sthorpej 	struct pps_state	sc_pps_state;
63266bca89Sthorpej 	bool			sc_functional;
64266bca89Sthorpej 	bool			sc_busy;
65266bca89Sthorpej };
66266bca89Sthorpej 
67266bca89Sthorpej #define	GPIOPPS_FLAGS_ASSERT_NEG_EDGE	0x01
68266bca89Sthorpej #define	GPIOPPS_FLAGS_NO_DOUBLE_EDGE	0x02
69266bca89Sthorpej 
70266bca89Sthorpej static int	gpiopps_match(device_t, cfdata_t, void *);
71266bca89Sthorpej static void	gpiopps_attach(device_t, device_t, void *);
72266bca89Sthorpej static int	gpiopps_detach(device_t, int);
73266bca89Sthorpej 
74266bca89Sthorpej CFATTACH_DECL_NEW(gpiopps, sizeof(struct gpiopps_softc),
75266bca89Sthorpej 		  gpiopps_match, gpiopps_attach,
76266bca89Sthorpej 		  gpiopps_detach, NULL /*activate*/);
77266bca89Sthorpej 
78266bca89Sthorpej extern struct cfdriver gpiopps_cd;
79266bca89Sthorpej 
80266bca89Sthorpej static dev_type_open(gpioppsopen);
81266bca89Sthorpej static dev_type_close(gpioppsclose);
82266bca89Sthorpej static dev_type_ioctl(gpioppsioctl);
83266bca89Sthorpej const struct cdevsw gpiopps_cdevsw = {
84266bca89Sthorpej 	.d_open = gpioppsopen,
85266bca89Sthorpej 	.d_close = gpioppsclose,
86266bca89Sthorpej 	.d_read = noread,
87266bca89Sthorpej 	.d_write = nowrite,
88266bca89Sthorpej 	.d_ioctl = gpioppsioctl,
89266bca89Sthorpej 	.d_stop = nostop,
90266bca89Sthorpej 	.d_tty = notty,
91266bca89Sthorpej 	.d_poll = nopoll,
92266bca89Sthorpej 	.d_mmap = nommap,
93266bca89Sthorpej 	.d_kqfilter = nokqfilter,
94266bca89Sthorpej 	.d_discard = nodiscard,
95266bca89Sthorpej 	.d_flag = D_OTHER
96266bca89Sthorpej };
97266bca89Sthorpej 
98266bca89Sthorpej static int
gpiopps_match(device_t parent,cfdata_t cf,void * aux)99266bca89Sthorpej gpiopps_match(device_t parent, cfdata_t cf, void *aux)
100266bca89Sthorpej {
101266bca89Sthorpej 	struct gpio_attach_args *ga = aux;
102266bca89Sthorpej 	int bits;
103266bca89Sthorpej 
104266bca89Sthorpej 	if (strcmp(ga->ga_dvname, cf->cf_name))
105266bca89Sthorpej 		return (0);
106266bca89Sthorpej 
107266bca89Sthorpej 	if (ga->ga_offset == -1)
108266bca89Sthorpej 		return (0);
109266bca89Sthorpej 
110266bca89Sthorpej 	/* One or 2 pins (unspecified, assume 1) */
111266bca89Sthorpej 	bits = gpio_npins(ga->ga_mask);
112266bca89Sthorpej 	if (bits > 2)
113266bca89Sthorpej 		return (0);
114266bca89Sthorpej 
115266bca89Sthorpej 	return (1);
116266bca89Sthorpej }
117266bca89Sthorpej 
118266bca89Sthorpej static void
gpiopps_attach(device_t parent,device_t self,void * aux)119266bca89Sthorpej gpiopps_attach(device_t parent, device_t self, void *aux)
120266bca89Sthorpej {
121266bca89Sthorpej 	struct gpiopps_softc *sc = device_private(self);
122266bca89Sthorpej 	struct gpio_attach_args *ga = aux;
123266bca89Sthorpej 	int flags, intrcaps, npins;
124266bca89Sthorpej 	int assert_edge = GPIO_INTR_POS_EDGE;
125266bca89Sthorpej 	int clear_edge  = GPIO_INTR_NEG_EDGE;
126266bca89Sthorpej 	int mask = ga->ga_mask;
127266bca89Sthorpej 
128266bca89Sthorpej 	sc->sc_dev = self;
129266bca89Sthorpej 	sc->sc_assert_val = GPIO_PIN_HIGH;
130266bca89Sthorpej 
131266bca89Sthorpej 	/* Map pins */
132266bca89Sthorpej 	sc->sc_gpio = ga->ga_gpio;
133266bca89Sthorpej 	sc->sc_map.pm_map = sc->_map;
134266bca89Sthorpej 
135*d1b93babSmsaitoh 	/* Determine our pin configuration. */
136266bca89Sthorpej 	npins = gpio_npins(mask);
137266bca89Sthorpej 	if (npins == 0) {
138266bca89Sthorpej 		npins = 1;
139266bca89Sthorpej 		mask = 0x1;
140266bca89Sthorpej 	}
141266bca89Sthorpej 
142266bca89Sthorpej 	/*
143266bca89Sthorpej 	 * Here's the different pin configurations we handle:
144266bca89Sthorpej 	 *
145266bca89Sthorpej 	 * 1 pin, single-edge capable pin -- interrupt on single-edge,
146266bca89Sthorpej 	 * only trigger ASSERT signal.
147266bca89Sthorpej 	 *
148266bca89Sthorpej 	 * 1 pin, double-edge capable pin -- interrupt on double-edge,
149266bca89Sthorpej 	 * trigger ASSERT and CLEAR signals, unless 0x2 is set in ga_flags,
150266bca89Sthorpej 	 * in which case we degrade to ASSERT only.
151266bca89Sthorpej 	 *
152266bca89Sthorpej 	 * 2 pins -- pin #0 is ASSERT signal, pin #1 is CLEAR signal.
153266bca89Sthorpej 	 *
154266bca89Sthorpej 	 * If 0x1 is set in ga_flags, ASSERT is negative edge, otherwise
155266bca89Sthorpej 	 * assert is positive edge.
156266bca89Sthorpej 	 */
157266bca89Sthorpej 	if (npins < 1 || npins > 2) {
158266bca89Sthorpej 		aprint_error(": invalid pin configuration\n");
159266bca89Sthorpej 		return;
160266bca89Sthorpej 	}
161266bca89Sthorpej 	if (ga->ga_flags & GPIOPPS_FLAGS_ASSERT_NEG_EDGE) {
162266bca89Sthorpej 		assert_edge = GPIO_INTR_NEG_EDGE;
163266bca89Sthorpej 		clear_edge  = GPIO_INTR_POS_EDGE;
164266bca89Sthorpej 		sc->sc_assert_val = GPIO_PIN_LOW;
165266bca89Sthorpej 	}
166266bca89Sthorpej 
167266bca89Sthorpej 	if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, mask,
168266bca89Sthorpej 			 &sc->sc_map)) {
169266bca89Sthorpej 		aprint_error(": can't map pins\n");
170266bca89Sthorpej 		return;
171266bca89Sthorpej 	}
172266bca89Sthorpej 	sc->sc_npins = npins;
173266bca89Sthorpej 
174266bca89Sthorpej 	aprint_normal("\n");
175266bca89Sthorpej 
176266bca89Sthorpej 	if (sc->sc_npins == 2) {
177266bca89Sthorpej 		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 0);
178266bca89Sthorpej 		if ((intrcaps & assert_edge) == 0) {
179266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
180266bca89Sthorpej 			    "%s edge interrupt not supported for ASSERT\n",
181266bca89Sthorpej 			    assert_edge == GPIO_INTR_POS_EDGE ? "positive"
182266bca89Sthorpej 			    				      : "negative");
183266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
184266bca89Sthorpej 			return;
185266bca89Sthorpej 		}
186266bca89Sthorpej 		sc->sc_intrs[0].sc_irqmode = assert_edge;
187266bca89Sthorpej 		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0,
188266bca89Sthorpej 				   sc->sc_intrs[0].sc_irqmode,
189266bca89Sthorpej 				   sc->sc_intrs[0].sc_intrstr,
190266bca89Sthorpej 				   sizeof(sc->sc_intrs[0].sc_intrstr))) {
191266bca89Sthorpej 			aprint_error_dev(self,
192266bca89Sthorpej 			    "failed to decode ASSERT interrupt\n");
193266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
194266bca89Sthorpej 			return;
195266bca89Sthorpej 		}
196266bca89Sthorpej 		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
197266bca89Sthorpej 		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
198266bca89Sthorpej 		    GPIO_PIN_INPUT;
199266bca89Sthorpej 		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
200266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
201266bca89Sthorpej 			    "ASSERT pin not capable of input\n");
202266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
203266bca89Sthorpej 			return;
204266bca89Sthorpej 		}
205266bca89Sthorpej 
206266bca89Sthorpej 		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 1);
207266bca89Sthorpej 		if ((intrcaps & clear_edge) == 0) {
208266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
209266bca89Sthorpej 			    "%s edge interrupt not supported for CLEAR\n",
210266bca89Sthorpej 			    clear_edge == GPIO_INTR_POS_EDGE ? "positive"
211266bca89Sthorpej 			    				     : "negative");
212266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
213266bca89Sthorpej 			return;
214266bca89Sthorpej 		}
215266bca89Sthorpej 		sc->sc_intrs[1].sc_irqmode = clear_edge;
216fe989590Sthorpej 		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 1,
217fe989590Sthorpej 				   sc->sc_intrs[1].sc_irqmode,
218fe989590Sthorpej 				   sc->sc_intrs[1].sc_intrstr,
219266bca89Sthorpej 				   sizeof(sc->sc_intrs[1].sc_intrstr))) {
220266bca89Sthorpej 			aprint_error_dev(self,
221266bca89Sthorpej 			    "failed to decode CLEAR interrupt\n");
222266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
223266bca89Sthorpej 			return;
224266bca89Sthorpej 		}
225266bca89Sthorpej 		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 1);
226266bca89Sthorpej 		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
227266bca89Sthorpej 		    GPIO_PIN_INPUT;
228266bca89Sthorpej 		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 1, flags)) {
229266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
230266bca89Sthorpej 			    "CLEAR pin not capable of input\n");
231266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
232266bca89Sthorpej 			return;
233266bca89Sthorpej 		}
234266bca89Sthorpej 
235266bca89Sthorpej 		aprint_normal_dev(self, "ASSERT interrupting on %s\n",
236266bca89Sthorpej 				  sc->sc_intrs[0].sc_intrstr);
237266bca89Sthorpej 		aprint_normal_dev(self, "CLEAR interrupting on %s\n",
238266bca89Sthorpej 				  sc->sc_intrs[1].sc_intrstr);
239266bca89Sthorpej 	} else {
240266bca89Sthorpej 		intrcaps = gpio_pin_intrcaps(sc->sc_gpio, &sc->sc_map, 0);
241266bca89Sthorpej 		bool double_edge = false;
242266bca89Sthorpej 		if ((intrcaps & GPIO_INTR_DOUBLE_EDGE) &&
243266bca89Sthorpej 		    (ga->ga_flags & GPIOPPS_FLAGS_NO_DOUBLE_EDGE) == 0) {
244266bca89Sthorpej 			sc->sc_intrs[0].sc_irqmode = GPIO_INTR_DOUBLE_EDGE;
245266bca89Sthorpej 			double_edge = true;
246266bca89Sthorpej 		} else if (intrcaps & assert_edge) {
247266bca89Sthorpej 			sc->sc_intrs[0].sc_irqmode = assert_edge;
248266bca89Sthorpej 		} else {
249266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
250266bca89Sthorpej 			    "%s edge interrupt not supported for ASSERT\n",
251266bca89Sthorpej 			    assert_edge == GPIO_INTR_POS_EDGE ? "positive"
252266bca89Sthorpej 			    				      : "negative");
253266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
254266bca89Sthorpej 			return;
255266bca89Sthorpej 		}
256266bca89Sthorpej 		if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0,
257266bca89Sthorpej 				   sc->sc_intrs[0].sc_irqmode,
258266bca89Sthorpej 				   sc->sc_intrs[0].sc_intrstr,
259266bca89Sthorpej 				   sizeof(sc->sc_intrs[0].sc_intrstr))) {
260266bca89Sthorpej 			aprint_error_dev(self,
261266bca89Sthorpej 			    "failed to decode interrupt\n");
262266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
263266bca89Sthorpej 			return;
264266bca89Sthorpej 		}
265266bca89Sthorpej 		flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
266266bca89Sthorpej 		flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
267266bca89Sthorpej 		    GPIO_PIN_INPUT;
268266bca89Sthorpej 		if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
269266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
270266bca89Sthorpej 			    "ASSERT%s pin not capable of input\n",
271266bca89Sthorpej 			    double_edge ? "+CLEAR" : "");
272266bca89Sthorpej 			gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
273266bca89Sthorpej 			return;
274266bca89Sthorpej 		}
275266bca89Sthorpej 
276266bca89Sthorpej 		aprint_normal_dev(self, "ASSERT%s interrupting on %s\n",
277266bca89Sthorpej 				  double_edge ? "+CLEAR" : "",
278266bca89Sthorpej 				  sc->sc_intrs[0].sc_intrstr);
279266bca89Sthorpej 	}
280266bca89Sthorpej 
281266bca89Sthorpej 	/* Interrupt will be registered when device is opened for use. */
282266bca89Sthorpej 
283266bca89Sthorpej 	sc->sc_functional = true;
284266bca89Sthorpej }
285266bca89Sthorpej 
286266bca89Sthorpej static int
gpiopps_assert_intr(void * arg)287266bca89Sthorpej gpiopps_assert_intr(void *arg)
288266bca89Sthorpej {
289266bca89Sthorpej 	struct gpiopps_softc *sc = arg;
290266bca89Sthorpej 
291266bca89Sthorpej 	mutex_spin_enter(&timecounter_lock);
292266bca89Sthorpej 	pps_capture(&sc->sc_pps_state);
293266bca89Sthorpej 	pps_event(&sc->sc_pps_state, PPS_CAPTUREASSERT);
294266bca89Sthorpej 	mutex_spin_exit(&timecounter_lock);
295266bca89Sthorpej 
296266bca89Sthorpej 	return (1);
297266bca89Sthorpej }
298266bca89Sthorpej 
299266bca89Sthorpej static int
gpiopps_clear_intr(void * arg)300266bca89Sthorpej gpiopps_clear_intr(void *arg)
301266bca89Sthorpej {
302266bca89Sthorpej 	struct gpiopps_softc *sc = arg;
303266bca89Sthorpej 
304266bca89Sthorpej 	mutex_spin_enter(&timecounter_lock);
305266bca89Sthorpej 	pps_capture(&sc->sc_pps_state);
306266bca89Sthorpej 	pps_event(&sc->sc_pps_state, PPS_CAPTURECLEAR);
307266bca89Sthorpej 	mutex_spin_exit(&timecounter_lock);
308266bca89Sthorpej 
309266bca89Sthorpej 	return (1);
310266bca89Sthorpej }
311266bca89Sthorpej 
312266bca89Sthorpej static int
gpiopps_double_intr(void * arg)313266bca89Sthorpej gpiopps_double_intr(void *arg)
314266bca89Sthorpej {
315266bca89Sthorpej 	struct gpiopps_softc *sc = arg;
316266bca89Sthorpej 	int val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0);
317266bca89Sthorpej 
318266bca89Sthorpej 	if (val == sc->sc_assert_val)
319266bca89Sthorpej 		return (gpiopps_assert_intr(arg));
320266bca89Sthorpej 	return (gpiopps_clear_intr(arg));
321266bca89Sthorpej }
322266bca89Sthorpej 
323266bca89Sthorpej static void
gpiopps_disable_interrupts(struct gpiopps_softc * sc)324266bca89Sthorpej gpiopps_disable_interrupts(struct gpiopps_softc *sc)
325266bca89Sthorpej {
326266bca89Sthorpej 	int i;
327266bca89Sthorpej 
328266bca89Sthorpej 	for (i = 0; i < GPIOPPS_NPINS; i++) {
329266bca89Sthorpej 		if (sc->sc_intrs[i].sc_ih != NULL) {
330266bca89Sthorpej 			gpio_intr_disestablish(sc->sc_gpio,
331266bca89Sthorpej 					       sc->sc_intrs[i].sc_ih);
332266bca89Sthorpej 			sc->sc_intrs[i].sc_ih = NULL;
333266bca89Sthorpej 		}
334266bca89Sthorpej 	}
335266bca89Sthorpej }
336266bca89Sthorpej 
337266bca89Sthorpej static void
gpiopps_reset(struct gpiopps_softc * sc)338266bca89Sthorpej gpiopps_reset(struct gpiopps_softc *sc)
339266bca89Sthorpej {
340266bca89Sthorpej 	mutex_spin_enter(&timecounter_lock);
341266bca89Sthorpej 	sc->sc_pps_state.ppsparam.mode = 0;
342266bca89Sthorpej 	sc->sc_busy = false;
343266bca89Sthorpej 	mutex_spin_exit(&timecounter_lock);
344266bca89Sthorpej }
345266bca89Sthorpej 
346266bca89Sthorpej static int
gpiopps_detach(device_t self,int flags)347266bca89Sthorpej gpiopps_detach(device_t self, int flags)
348266bca89Sthorpej {
349266bca89Sthorpej 	struct gpiopps_softc *sc = device_private(self);
350266bca89Sthorpej 
351266bca89Sthorpej 	if (!sc->sc_functional) {
352266bca89Sthorpej 		/* Attach failed, no work to do; resources already released. */
353266bca89Sthorpej 		return (0);
354266bca89Sthorpej 	}
355266bca89Sthorpej 
356266bca89Sthorpej 	if (sc->sc_busy)
357266bca89Sthorpej 		return (EBUSY);
358266bca89Sthorpej 
359266bca89Sthorpej 	/*
360266bca89Sthorpej 	 * Clear the handler and disable the interrupt.
361266bca89Sthorpej 	 * NOTE: This should never be true, because we
362266bca89Sthorpej 	 * register the interrupt handler at open, and
363266bca89Sthorpej 	 * remove it at close.  We keep this as a backstop.
364266bca89Sthorpej 	 */
365266bca89Sthorpej 	gpiopps_disable_interrupts(sc);
366266bca89Sthorpej 
367266bca89Sthorpej 	/* Release the pin. */
368266bca89Sthorpej 	gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
369266bca89Sthorpej 
370266bca89Sthorpej 	return (0);
371266bca89Sthorpej }
372266bca89Sthorpej 
373266bca89Sthorpej static int
gpioppsopen(dev_t dev,int flags,int fmt,struct lwp * l)374266bca89Sthorpej gpioppsopen(dev_t dev, int flags, int fmt, struct lwp *l)
375266bca89Sthorpej {
376266bca89Sthorpej 	struct gpiopps_softc *sc;
377266bca89Sthorpej 	int error = EIO;
378266bca89Sthorpej 
379266bca89Sthorpej 	sc = device_lookup_private(&gpiopps_cd, minor(dev));
380266bca89Sthorpej 	if (sc == NULL)
381266bca89Sthorpej 		return (ENXIO);
382266bca89Sthorpej 
383266bca89Sthorpej 	if (!sc->sc_functional)
384266bca89Sthorpej 		return (EIO);
385266bca89Sthorpej 
386266bca89Sthorpej 	mutex_spin_enter(&timecounter_lock);
387266bca89Sthorpej 
388266bca89Sthorpej 	if (sc->sc_busy) {
389266bca89Sthorpej 		mutex_spin_exit(&timecounter_lock);
390266bca89Sthorpej 		return (0);
391266bca89Sthorpej 	}
392266bca89Sthorpej 
393266bca89Sthorpej 	memset(&sc->sc_pps_state, 0, sizeof(sc->sc_pps_state));
394266bca89Sthorpej 	sc->sc_pps_state.ppscap = PPS_CAPTUREASSERT;
395266bca89Sthorpej 	if (sc->sc_npins == 2 ||
396266bca89Sthorpej 	    sc->sc_intrs[0].sc_irqmode == GPIO_INTR_DOUBLE_EDGE)
397266bca89Sthorpej 	    	sc->sc_pps_state.ppscap |= PPS_CAPTURECLEAR;
398266bca89Sthorpej 	pps_init(&sc->sc_pps_state);
399266bca89Sthorpej 	sc->sc_busy = true;
400266bca89Sthorpej 
401266bca89Sthorpej 	mutex_spin_exit(&timecounter_lock);
402266bca89Sthorpej 
403266bca89Sthorpej 	if (sc->sc_npins == 2) {
404266bca89Sthorpej 		sc->sc_intrs[0].sc_ih = gpio_intr_establish(sc->sc_gpio,
405266bca89Sthorpej 		    &sc->sc_map, 0, IPL_VM,
406266bca89Sthorpej 		    sc->sc_intrs[0].sc_irqmode | GPIO_INTR_MPSAFE,
407266bca89Sthorpej 		    gpiopps_assert_intr, sc);
408266bca89Sthorpej 		if (sc->sc_intrs[0].sc_ih == NULL) {
409266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
410266bca89Sthorpej 			    "unable to establish ASSERT interrupt on %s\n",
411266bca89Sthorpej 			    sc->sc_intrs[0].sc_intrstr);
412266bca89Sthorpej 			goto out;
413266bca89Sthorpej 		}
414266bca89Sthorpej 
415266bca89Sthorpej 		sc->sc_intrs[1].sc_ih = gpio_intr_establish(sc->sc_gpio,
416266bca89Sthorpej 		    &sc->sc_map, 1, IPL_VM,
417266bca89Sthorpej 		    sc->sc_intrs[1].sc_irqmode | GPIO_INTR_MPSAFE,
418266bca89Sthorpej 		    gpiopps_clear_intr, sc);
419266bca89Sthorpej 		if (sc->sc_intrs[1].sc_ih == NULL) {
420266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
421266bca89Sthorpej 			    "unable to establish CLEAR interrupt on %s\n",
422266bca89Sthorpej 			    sc->sc_intrs[0].sc_intrstr);
423266bca89Sthorpej 			gpio_intr_disestablish(sc->sc_gpio,
424266bca89Sthorpej 					       sc->sc_intrs[0].sc_ih);
425266bca89Sthorpej 			goto out;
426266bca89Sthorpej 		}
427266bca89Sthorpej 	} else {
428266bca89Sthorpej 		bool double_edge =
429266bca89Sthorpej 		    sc->sc_intrs[0].sc_irqmode == GPIO_INTR_DOUBLE_EDGE;
430266bca89Sthorpej 		sc->sc_intrs[0].sc_ih = gpio_intr_establish(sc->sc_gpio,
431266bca89Sthorpej 		    &sc->sc_map, 0, IPL_VM,
432266bca89Sthorpej 		    sc->sc_intrs[0].sc_irqmode | GPIO_INTR_MPSAFE,
433266bca89Sthorpej 		    double_edge ? gpiopps_double_intr
434266bca89Sthorpej 				: gpiopps_assert_intr, sc);
435266bca89Sthorpej 		if (sc->sc_intrs[0].sc_ih == NULL) {
436266bca89Sthorpej 			aprint_error_dev(sc->sc_dev,
437266bca89Sthorpej 			    "unable to establish ASSERT%s interrupt on %s\n",
438266bca89Sthorpej 			    double_edge ? "+CLEAR" : "",
439266bca89Sthorpej 			    sc->sc_intrs[0].sc_intrstr);
440266bca89Sthorpej 			goto out;
441266bca89Sthorpej 		}
442266bca89Sthorpej 	}
443266bca89Sthorpej 
444266bca89Sthorpej 	error = 0;
445266bca89Sthorpej 
446266bca89Sthorpej  out:
447266bca89Sthorpej 	if (error) {
448266bca89Sthorpej 		gpiopps_disable_interrupts(sc);
449266bca89Sthorpej 		gpiopps_reset(sc);
450266bca89Sthorpej 	}
451266bca89Sthorpej 	return (error);
452266bca89Sthorpej }
453266bca89Sthorpej 
454266bca89Sthorpej static int
gpioppsclose(dev_t dev,int flags,int fmt,struct lwp * l)455266bca89Sthorpej gpioppsclose(dev_t dev, int flags, int fmt, struct lwp *l)
456266bca89Sthorpej {
457266bca89Sthorpej 	struct gpiopps_softc *sc;
458266bca89Sthorpej 
459266bca89Sthorpej 	sc = device_lookup_private(&gpiopps_cd, minor(dev));
460266bca89Sthorpej 
461266bca89Sthorpej 	gpiopps_disable_interrupts(sc);
462266bca89Sthorpej 	gpiopps_reset(sc);
463266bca89Sthorpej 
464266bca89Sthorpej 	return (0);
465266bca89Sthorpej }
466266bca89Sthorpej 
467266bca89Sthorpej static int
gpioppsioctl(dev_t dev,u_long cmd,void * data,int flags,struct lwp * l)468266bca89Sthorpej gpioppsioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
469266bca89Sthorpej {
470266bca89Sthorpej 	struct gpiopps_softc *sc;
471266bca89Sthorpej 	int error = 0;
472266bca89Sthorpej 
473266bca89Sthorpej 	sc = device_lookup_private(&gpiopps_cd, minor(dev));
474266bca89Sthorpej 
475266bca89Sthorpej 	switch (cmd) {
476266bca89Sthorpej 	case PPS_IOC_CREATE:
477266bca89Sthorpej 	case PPS_IOC_DESTROY:
478266bca89Sthorpej 	case PPS_IOC_GETPARAMS:
479266bca89Sthorpej 	case PPS_IOC_SETPARAMS:
480266bca89Sthorpej 	case PPS_IOC_GETCAP:
481266bca89Sthorpej 	case PPS_IOC_FETCH:
482266bca89Sthorpej 	case PPS_IOC_KCBIND:
483266bca89Sthorpej 		mutex_spin_enter(&timecounter_lock);
484266bca89Sthorpej 		error = pps_ioctl(cmd, data, &sc->sc_pps_state);
485266bca89Sthorpej 		mutex_spin_exit(&timecounter_lock);
486266bca89Sthorpej 		break;
487266bca89Sthorpej 
488266bca89Sthorpej 	default:
489266bca89Sthorpej 		error = EPASSTHROUGH;
490266bca89Sthorpej 	}
491266bca89Sthorpej 
492266bca89Sthorpej 	return (error);
493266bca89Sthorpej }
494266bca89Sthorpej 
4956acf5ca3Spgoyette MODULE(MODULE_CLASS_DRIVER, gpiopps, "gpio");
496266bca89Sthorpej 
497266bca89Sthorpej #ifdef _MODULE
498266bca89Sthorpej #include "ioconf.c"
499266bca89Sthorpej #endif
500266bca89Sthorpej 
501266bca89Sthorpej static int
gpiopps_modcmd(modcmd_t cmd,void * opaque)502266bca89Sthorpej gpiopps_modcmd(modcmd_t cmd, void *opaque)
503266bca89Sthorpej {
504266bca89Sthorpej 	int error = 0;
505266bca89Sthorpej #ifdef _MODULE
506266bca89Sthorpej 	int bmaj = -1, cmaj = -1;
507266bca89Sthorpej #endif
508266bca89Sthorpej 
509266bca89Sthorpej 	switch (cmd) {
510266bca89Sthorpej 	case MODULE_CMD_INIT:
511266bca89Sthorpej #ifdef _MODULE
512266bca89Sthorpej 		error = devsw_attach("gpiopps", NULL, &bmaj,
513266bca89Sthorpej 		    &gpiopps_cdevsw, &cmaj);
514266bca89Sthorpej 		if (error) {
515266bca89Sthorpej 			aprint_error("%s: unable to attach devsw\n",
516266bca89Sthorpej 			    gpiopps_cd.cd_name);
51797f8debdSpgoyette 			return error;
51897f8debdSpgoyette 		}
51997f8debdSpgoyette 		error = config_init_component(cfdriver_ioconf_gpiopps,
520266bca89Sthorpej 		    cfattach_ioconf_gpiopps, cfdata_ioconf_gpiopps);
52197f8debdSpgoyette 		if (error) {
52297f8debdSpgoyette 			aprint_error("%s: unable to init component\n",
52397f8debdSpgoyette 			    gpiopps_cd.cd_name);
52497f8debdSpgoyette 			devsw_detach(NULL, &gpiopps_cdevsw);
52597f8debdSpgoyette 			return (error);
526266bca89Sthorpej 		}
527266bca89Sthorpej #endif
528266bca89Sthorpej 		return (error);
529266bca89Sthorpej 	case MODULE_CMD_FINI:
530266bca89Sthorpej #ifdef _MODULE
531266bca89Sthorpej 		config_fini_component(cfdriver_ioconf_gpiopps,
532266bca89Sthorpej 		    cfattach_ioconf_gpiopps, cfdata_ioconf_gpiopps);
53397f8debdSpgoyette 		devsw_detach(NULL, &gpiopps_cdevsw);
534266bca89Sthorpej #endif
535266bca89Sthorpej 		return (0);
536266bca89Sthorpej 	default:
537266bca89Sthorpej 		return (ENOTTY);
538266bca89Sthorpej 	}
539266bca89Sthorpej }
540