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