xref: /freebsd/sys/dev/gpio/gpioc.c (revision fdafd315)
15f958b85SOleksandr Tymoshenko /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
45f958b85SOleksandr Tymoshenko  * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
55f958b85SOleksandr Tymoshenko  * All rights reserved.
65f958b85SOleksandr Tymoshenko  *
75f958b85SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
85f958b85SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
95f958b85SOleksandr Tymoshenko  * are met:
105f958b85SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
115f958b85SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
125f958b85SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
135f958b85SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
145f958b85SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
155f958b85SOleksandr Tymoshenko  *
165f958b85SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
175f958b85SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
185f958b85SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
195f958b85SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
205f958b85SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
215f958b85SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
225f958b85SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
235f958b85SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
245f958b85SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255f958b85SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265f958b85SOleksandr Tymoshenko  * SUCH DAMAGE.
275f958b85SOleksandr Tymoshenko  */
285f958b85SOleksandr Tymoshenko 
296b34b16eSOleksandr Tymoshenko #include <sys/param.h>
306b34b16eSOleksandr Tymoshenko #include <sys/systm.h>
316b34b16eSOleksandr Tymoshenko #include <sys/bus.h>
326b34b16eSOleksandr Tymoshenko #include <sys/conf.h>
33667357dcSLuiz Otavio O Souza #include <sys/gpio.h>
346b34b16eSOleksandr Tymoshenko #include <sys/ioccom.h>
35ff3468acSIan Lepore #include <sys/filio.h>
36ff3468acSIan Lepore #include <sys/fcntl.h>
37ff3468acSIan Lepore #include <sys/sigio.h>
38ff3468acSIan Lepore #include <sys/signalvar.h>
396b34b16eSOleksandr Tymoshenko #include <sys/kernel.h>
406b34b16eSOleksandr Tymoshenko #include <sys/malloc.h>
41ff3468acSIan Lepore #include <sys/uio.h>
42ff3468acSIan Lepore #include <sys/poll.h>
43ff3468acSIan Lepore #include <sys/selinfo.h>
446b34b16eSOleksandr Tymoshenko #include <sys/module.h>
456b34b16eSOleksandr Tymoshenko 
46667357dcSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h>
47667357dcSLuiz Otavio O Souza 
486b34b16eSOleksandr Tymoshenko #include "gpio_if.h"
49d752f0f6SLuiz Otavio O Souza #include "gpiobus_if.h"
506b34b16eSOleksandr Tymoshenko 
516b34b16eSOleksandr Tymoshenko #undef GPIOC_DEBUG
526b34b16eSOleksandr Tymoshenko #ifdef GPIOC_DEBUG
536b34b16eSOleksandr Tymoshenko #define dprintf printf
54ff3468acSIan Lepore #define ddevice_printf device_printf
556b34b16eSOleksandr Tymoshenko #else
566b34b16eSOleksandr Tymoshenko #define dprintf(x, arg...)
57ff3468acSIan Lepore #define ddevice_printf(dev, x, arg...)
586b34b16eSOleksandr Tymoshenko #endif
596b34b16eSOleksandr Tymoshenko 
606b34b16eSOleksandr Tymoshenko struct gpioc_softc {
616b34b16eSOleksandr Tymoshenko 	device_t		sc_dev;		/* gpiocX dev */
626b34b16eSOleksandr Tymoshenko 	device_t		sc_pdev;	/* gpioX dev */
636b34b16eSOleksandr Tymoshenko 	struct cdev		*sc_ctl_dev;	/* controller device */
646b34b16eSOleksandr Tymoshenko 	int			sc_unit;
65ff3468acSIan Lepore 	int			sc_npins;
66ff3468acSIan Lepore 	struct gpioc_pin_intr	*sc_pin_intr;
676b34b16eSOleksandr Tymoshenko };
686b34b16eSOleksandr Tymoshenko 
69ff3468acSIan Lepore struct gpioc_pin_intr {
70ff3468acSIan Lepore 	struct gpioc_softc				*sc;
71ff3468acSIan Lepore 	gpio_pin_t					pin;
72ff3468acSIan Lepore 	bool						config_locked;
73ff3468acSIan Lepore 	int						intr_rid;
74ff3468acSIan Lepore 	struct resource					*intr_res;
75ff3468acSIan Lepore 	void						*intr_cookie;
76ff3468acSIan Lepore 	struct mtx					mtx;
77ff3468acSIan Lepore 	SLIST_HEAD(gpioc_privs_list, gpioc_privs)	privs;
78ff3468acSIan Lepore };
79ff3468acSIan Lepore 
80ff3468acSIan Lepore 
81ff3468acSIan Lepore struct gpioc_cdevpriv {
82ff3468acSIan Lepore 	struct gpioc_softc			*sc;
83ff3468acSIan Lepore 	struct selinfo				selinfo;
84ff3468acSIan Lepore 	bool					async;
85ff3468acSIan Lepore 	uint8_t					report_option;
86ff3468acSIan Lepore 	struct sigio				*sigio;
87ff3468acSIan Lepore 	struct mtx				mtx;
88ff3468acSIan Lepore 	struct gpioc_pin_event			*events;
89ff3468acSIan Lepore 	int					numevents;
90ff3468acSIan Lepore 	int					evidx_head;
91ff3468acSIan Lepore 	int					evidx_tail;
92ff3468acSIan Lepore 	SLIST_HEAD(gpioc_pins_list, gpioc_pins)	pins;
93ff3468acSIan Lepore };
94ff3468acSIan Lepore 
95ff3468acSIan Lepore struct gpioc_privs {
96ff3468acSIan Lepore 	struct gpioc_cdevpriv		*priv;
97ff3468acSIan Lepore 	SLIST_ENTRY(gpioc_privs)	next;
98ff3468acSIan Lepore };
99ff3468acSIan Lepore 
100ff3468acSIan Lepore struct gpioc_pins {
101ff3468acSIan Lepore 	struct gpioc_pin_intr	*pin;
102ff3468acSIan Lepore 	int			eventcount;
103ff3468acSIan Lepore 	int			firstevent;
104ff3468acSIan Lepore 	SLIST_ENTRY(gpioc_pins)	next;
105ff3468acSIan Lepore };
106ff3468acSIan Lepore 
107ff3468acSIan Lepore struct gpioc_pin_event {
108ff3468acSIan Lepore 	struct gpioc_pins	*privpin;
109ff3468acSIan Lepore 	sbintime_t		event_time;
110ff3468acSIan Lepore 	bool			event_pin_state;
111ff3468acSIan Lepore };
112ff3468acSIan Lepore 
113ff3468acSIan Lepore static MALLOC_DEFINE(M_GPIOC, "gpioc", "gpioc device data");
114ff3468acSIan Lepore 
115ff3468acSIan Lepore static int	gpioc_allocate_pin_intr(struct gpioc_pin_intr*, uint32_t);
116ff3468acSIan Lepore static int	gpioc_release_pin_intr(struct gpioc_pin_intr*);
117ff3468acSIan Lepore static int	gpioc_attach_priv_pin(struct gpioc_cdevpriv*,
118ff3468acSIan Lepore 		    struct gpioc_pin_intr*);
119ff3468acSIan Lepore static int	gpioc_detach_priv_pin(struct gpioc_cdevpriv*,
120ff3468acSIan Lepore 		    struct gpioc_pin_intr*);
121ff3468acSIan Lepore static bool	gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv*,
122ff3468acSIan Lepore 		    struct gpioc_pin_intr *intr_conf);
123ff3468acSIan Lepore static uint32_t	gpioc_get_intr_config(struct gpioc_softc*,
124ff3468acSIan Lepore 		    struct gpioc_cdevpriv*, uint32_t pin);
125ff3468acSIan Lepore static int	gpioc_set_intr_config(struct gpioc_softc*,
126ff3468acSIan Lepore 		    struct gpioc_cdevpriv*, uint32_t, uint32_t);
127ff3468acSIan Lepore static void	gpioc_interrupt_handler(void*);
128ff3468acSIan Lepore 
129ff3468acSIan Lepore static int	gpioc_kqread(struct knote*, long);
130ff3468acSIan Lepore static void	gpioc_kqdetach(struct knote*);
131ff3468acSIan Lepore 
132ff3468acSIan Lepore static int	gpioc_probe(device_t dev);
133ff3468acSIan Lepore static int	gpioc_attach(device_t dev);
134ff3468acSIan Lepore static int	gpioc_detach(device_t dev);
135ff3468acSIan Lepore 
136ff3468acSIan Lepore static void	gpioc_cdevpriv_dtor(void*);
137ff3468acSIan Lepore 
138ff3468acSIan Lepore static d_open_t		gpioc_open;
139ff3468acSIan Lepore static d_read_t		gpioc_read;
140ff3468acSIan Lepore static d_ioctl_t	gpioc_ioctl;
141ff3468acSIan Lepore static d_poll_t		gpioc_poll;
142ff3468acSIan Lepore static d_kqfilter_t	gpioc_kqfilter;
143ff3468acSIan Lepore 
144ff3468acSIan Lepore static struct cdevsw gpioc_cdevsw = {
145ff3468acSIan Lepore 	.d_version	= D_VERSION,
146ff3468acSIan Lepore 	.d_open		= gpioc_open,
147ff3468acSIan Lepore 	.d_read		= gpioc_read,
148ff3468acSIan Lepore 	.d_ioctl	= gpioc_ioctl,
149ff3468acSIan Lepore 	.d_poll		= gpioc_poll,
150ff3468acSIan Lepore 	.d_kqfilter	= gpioc_kqfilter,
151ff3468acSIan Lepore 	.d_name		= "gpioc",
152ff3468acSIan Lepore };
153ff3468acSIan Lepore 
154ff3468acSIan Lepore static struct filterops gpioc_read_filterops = {
155ff3468acSIan Lepore 	.f_isfd =	true,
156ff3468acSIan Lepore 	.f_attach =	NULL,
157ff3468acSIan Lepore 	.f_detach =	gpioc_kqdetach,
158ff3468acSIan Lepore 	.f_event =	gpioc_kqread,
159ff3468acSIan Lepore 	.f_touch =	NULL
160ff3468acSIan Lepore };
161ff3468acSIan Lepore 
162ff3468acSIan Lepore static struct gpioc_pin_event *
next_head_event(struct gpioc_cdevpriv * priv)163ff3468acSIan Lepore next_head_event(struct gpioc_cdevpriv *priv)
164ff3468acSIan Lepore {
165ff3468acSIan Lepore 	struct gpioc_pin_event *rv;
166ff3468acSIan Lepore 
167ff3468acSIan Lepore 	rv = &priv->events[priv->evidx_head++];
168ff3468acSIan Lepore 	if (priv->evidx_head == priv->numevents)
169ff3468acSIan Lepore 		priv->evidx_head = 0;
170ff3468acSIan Lepore 	return (rv);
171ff3468acSIan Lepore }
172ff3468acSIan Lepore 
173ff3468acSIan Lepore static struct gpioc_pin_event *
next_tail_event(struct gpioc_cdevpriv * priv)174ff3468acSIan Lepore next_tail_event(struct gpioc_cdevpriv *priv)
175ff3468acSIan Lepore {
176ff3468acSIan Lepore 	struct gpioc_pin_event *rv;
177ff3468acSIan Lepore 
178ff3468acSIan Lepore 	rv = &priv->events[priv->evidx_tail++];
179ff3468acSIan Lepore 	if (priv->evidx_tail == priv->numevents)
180ff3468acSIan Lepore 		priv->evidx_tail = 0;
181ff3468acSIan Lepore 	return (rv);
182ff3468acSIan Lepore }
183ff3468acSIan Lepore 
184ff3468acSIan Lepore static size_t
number_of_events(struct gpioc_cdevpriv * priv)185ff3468acSIan Lepore number_of_events(struct gpioc_cdevpriv *priv)
186ff3468acSIan Lepore {
187ff3468acSIan Lepore 	if (priv->evidx_head >= priv->evidx_tail)
188ff3468acSIan Lepore 		return (priv->evidx_head - priv->evidx_tail);
189ff3468acSIan Lepore 	else
190ff3468acSIan Lepore 		return (priv->numevents + priv->evidx_head - priv->evidx_tail);
191ff3468acSIan Lepore }
192ff3468acSIan Lepore 
193ff3468acSIan Lepore static int
gpioc_allocate_pin_intr(struct gpioc_pin_intr * intr_conf,uint32_t flags)194ff3468acSIan Lepore gpioc_allocate_pin_intr(struct gpioc_pin_intr *intr_conf, uint32_t flags)
195ff3468acSIan Lepore {
196ff3468acSIan Lepore 	int err;
197ff3468acSIan Lepore 
198ff3468acSIan Lepore 	intr_conf->config_locked = true;
199ff3468acSIan Lepore 	mtx_unlock(&intr_conf->mtx);
200ff3468acSIan Lepore 
201ff3468acSIan Lepore 	intr_conf->intr_res = gpio_alloc_intr_resource(intr_conf->pin->dev,
202ff3468acSIan Lepore 	    &intr_conf->intr_rid, RF_ACTIVE, intr_conf->pin, flags);
203ff3468acSIan Lepore 	if (intr_conf->intr_res == NULL) {
204ff3468acSIan Lepore 		err = ENXIO;
205ff3468acSIan Lepore 		goto error_exit;
206ff3468acSIan Lepore 	}
207ff3468acSIan Lepore 
208ff3468acSIan Lepore 	err = bus_setup_intr(intr_conf->pin->dev, intr_conf->intr_res,
209ff3468acSIan Lepore 	    INTR_TYPE_MISC | INTR_MPSAFE, NULL, gpioc_interrupt_handler,
210ff3468acSIan Lepore 	    intr_conf, &intr_conf->intr_cookie);
211ff3468acSIan Lepore 	if (err != 0)
212ff3468acSIan Lepore 		goto error_exit;
213ff3468acSIan Lepore 
214ff3468acSIan Lepore 	intr_conf->pin->flags = flags;
215ff3468acSIan Lepore 
216ff3468acSIan Lepore error_exit:
217ff3468acSIan Lepore 	mtx_lock(&intr_conf->mtx);
218ff3468acSIan Lepore 	intr_conf->config_locked = false;
219ff3468acSIan Lepore 	wakeup(&intr_conf->config_locked);
220ff3468acSIan Lepore 
221ff3468acSIan Lepore 	return (err);
222ff3468acSIan Lepore }
223ff3468acSIan Lepore 
224ff3468acSIan Lepore static int
gpioc_release_pin_intr(struct gpioc_pin_intr * intr_conf)225ff3468acSIan Lepore gpioc_release_pin_intr(struct gpioc_pin_intr *intr_conf)
226ff3468acSIan Lepore {
227ff3468acSIan Lepore 	int err;
228ff3468acSIan Lepore 
229ff3468acSIan Lepore 	intr_conf->config_locked = true;
230ff3468acSIan Lepore 	mtx_unlock(&intr_conf->mtx);
231ff3468acSIan Lepore 
232ff3468acSIan Lepore 	if (intr_conf->intr_cookie != NULL) {
233ff3468acSIan Lepore 		err = bus_teardown_intr(intr_conf->pin->dev,
234ff3468acSIan Lepore 		    intr_conf->intr_res, intr_conf->intr_cookie);
235ff3468acSIan Lepore 		if (err != 0)
236ff3468acSIan Lepore 			goto error_exit;
237ff3468acSIan Lepore 		else
238ff3468acSIan Lepore 			intr_conf->intr_cookie = NULL;
239ff3468acSIan Lepore 	}
240ff3468acSIan Lepore 
241ff3468acSIan Lepore 	if (intr_conf->intr_res != NULL) {
242ff3468acSIan Lepore 		err = bus_release_resource(intr_conf->pin->dev, SYS_RES_IRQ,
243ff3468acSIan Lepore 		    intr_conf->intr_rid, intr_conf->intr_res);
244ff3468acSIan Lepore 		if (err != 0)
245ff3468acSIan Lepore 			goto error_exit;
246ff3468acSIan Lepore 		else {
247ff3468acSIan Lepore 			intr_conf->intr_rid = 0;
248ff3468acSIan Lepore 			intr_conf->intr_res = NULL;
249ff3468acSIan Lepore 		}
250ff3468acSIan Lepore 	}
251ff3468acSIan Lepore 
252ff3468acSIan Lepore 	intr_conf->pin->flags = 0;
253ff3468acSIan Lepore 	err = 0;
254ff3468acSIan Lepore 
255ff3468acSIan Lepore error_exit:
256ff3468acSIan Lepore 	mtx_lock(&intr_conf->mtx);
257ff3468acSIan Lepore 	intr_conf->config_locked = false;
258ff3468acSIan Lepore 	wakeup(&intr_conf->config_locked);
259ff3468acSIan Lepore 
260ff3468acSIan Lepore 	return (err);
261ff3468acSIan Lepore }
262ff3468acSIan Lepore 
263ff3468acSIan Lepore static int
gpioc_attach_priv_pin(struct gpioc_cdevpriv * priv,struct gpioc_pin_intr * intr_conf)264ff3468acSIan Lepore gpioc_attach_priv_pin(struct gpioc_cdevpriv *priv,
265ff3468acSIan Lepore     struct gpioc_pin_intr *intr_conf)
266ff3468acSIan Lepore {
267ff3468acSIan Lepore 	struct gpioc_privs	*priv_link;
268ff3468acSIan Lepore 	struct gpioc_pins	*pin_link;
2697dc4d511SEd Maste 	unsigned int		consistency_a __diagused;
2707dc4d511SEd Maste 	unsigned int		consistency_b __diagused;
271ff3468acSIan Lepore 
272ff3468acSIan Lepore 	consistency_a = 0;
273ff3468acSIan Lepore 	consistency_b = 0;
274ff3468acSIan Lepore 	mtx_assert(&intr_conf->mtx, MA_OWNED);
275ff3468acSIan Lepore 	mtx_lock(&priv->mtx);
276ff3468acSIan Lepore 	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
277ff3468acSIan Lepore 		if (priv_link->priv == priv)
278ff3468acSIan Lepore 			consistency_a++;
279ff3468acSIan Lepore 	}
280ff3468acSIan Lepore 	KASSERT(consistency_a <= 1,
281ff3468acSIan Lepore 	    ("inconsistent links between pin config and cdevpriv"));
282ff3468acSIan Lepore 	SLIST_FOREACH(pin_link, &priv->pins, next) {
283ff3468acSIan Lepore 		if (pin_link->pin == intr_conf)
284ff3468acSIan Lepore 			consistency_b++;
285ff3468acSIan Lepore 	}
286ff3468acSIan Lepore 	KASSERT(consistency_a == consistency_b,
287ff3468acSIan Lepore 	    ("inconsistent links between pin config and cdevpriv"));
288ff3468acSIan Lepore 	if (consistency_a == 1 && consistency_b == 1) {
289ff3468acSIan Lepore 		mtx_unlock(&priv->mtx);
290ff3468acSIan Lepore 		return (EEXIST);
291ff3468acSIan Lepore 	}
292ff3468acSIan Lepore 	priv_link = malloc(sizeof(struct gpioc_privs), M_GPIOC,
293ff3468acSIan Lepore 	    M_NOWAIT | M_ZERO);
294ff3468acSIan Lepore 	if (priv_link == NULL)
295ff3468acSIan Lepore 	{
296ff3468acSIan Lepore 		mtx_unlock(&priv->mtx);
297ff3468acSIan Lepore 		return (ENOMEM);
298ff3468acSIan Lepore 	}
299ff3468acSIan Lepore 	pin_link = malloc(sizeof(struct gpioc_pins), M_GPIOC,
300ff3468acSIan Lepore 	    M_NOWAIT | M_ZERO);
301ff3468acSIan Lepore 	if (pin_link == NULL) {
302ff3468acSIan Lepore 		mtx_unlock(&priv->mtx);
303ff3468acSIan Lepore 		return (ENOMEM);
304ff3468acSIan Lepore 	}
305ff3468acSIan Lepore 	priv_link->priv = priv;
306ff3468acSIan Lepore 	pin_link->pin = intr_conf;
307ff3468acSIan Lepore 	SLIST_INSERT_HEAD(&intr_conf->privs, priv_link, next);
308ff3468acSIan Lepore 	SLIST_INSERT_HEAD(&priv->pins, pin_link, next);
309ff3468acSIan Lepore 	mtx_unlock(&priv->mtx);
310ff3468acSIan Lepore 
311ff3468acSIan Lepore 	return (0);
312ff3468acSIan Lepore }
313ff3468acSIan Lepore 
314ff3468acSIan Lepore static int
gpioc_detach_priv_pin(struct gpioc_cdevpriv * priv,struct gpioc_pin_intr * intr_conf)315ff3468acSIan Lepore gpioc_detach_priv_pin(struct gpioc_cdevpriv *priv,
316ff3468acSIan Lepore     struct gpioc_pin_intr *intr_conf)
317ff3468acSIan Lepore {
318ff3468acSIan Lepore 	struct gpioc_privs	*priv_link, *priv_link_temp;
319ff3468acSIan Lepore 	struct gpioc_pins	*pin_link, *pin_link_temp;
3206e62d9a5SDimitry Andric 	unsigned int		consistency_a __diagused;
3216e62d9a5SDimitry Andric 	unsigned int		consistency_b __diagused;
322ff3468acSIan Lepore 
323ff3468acSIan Lepore 	consistency_a = 0;
324ff3468acSIan Lepore 	consistency_b = 0;
325ff3468acSIan Lepore 	mtx_assert(&intr_conf->mtx, MA_OWNED);
326ff3468acSIan Lepore 	mtx_lock(&priv->mtx);
327ff3468acSIan Lepore 	SLIST_FOREACH_SAFE(priv_link, &intr_conf->privs, next, priv_link_temp) {
328ff3468acSIan Lepore 		if (priv_link->priv == priv) {
329ff3468acSIan Lepore 			SLIST_REMOVE(&intr_conf->privs, priv_link, gpioc_privs,
330ff3468acSIan Lepore 			    next);
331ff3468acSIan Lepore 			free(priv_link, M_GPIOC);
332ff3468acSIan Lepore 			consistency_a++;
333ff3468acSIan Lepore 		}
334ff3468acSIan Lepore 	}
335ff3468acSIan Lepore 	KASSERT(consistency_a <= 1,
336ff3468acSIan Lepore 	    ("inconsistent links between pin config and cdevpriv"));
337ff3468acSIan Lepore 	SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
338ff3468acSIan Lepore 		if (pin_link->pin == intr_conf) {
339ff3468acSIan Lepore 			/*
340ff3468acSIan Lepore 			 * If the pin we're removing has events in the priv's
341ff3468acSIan Lepore 			 * event fifo, we can't leave dangling pointers from
342ff3468acSIan Lepore 			 * those events to the gpioc_pins struct we're about to
343ff3468acSIan Lepore 			 * free.  We also can't remove random items and leave
344ff3468acSIan Lepore 			 * holes in the events fifo, so just empty it out.
345ff3468acSIan Lepore 			 */
346ff3468acSIan Lepore 			if (pin_link->eventcount > 0) {
347ff3468acSIan Lepore 				priv->evidx_head = priv->evidx_tail = 0;
348ff3468acSIan Lepore 			}
349ff3468acSIan Lepore 			SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
350ff3468acSIan Lepore 			free(pin_link, M_GPIOC);
351ff3468acSIan Lepore 			consistency_b++;
352ff3468acSIan Lepore 		}
353ff3468acSIan Lepore 	}
354ff3468acSIan Lepore 	KASSERT(consistency_a == consistency_b,
355ff3468acSIan Lepore 	    ("inconsistent links between pin config and cdevpriv"));
356ff3468acSIan Lepore 	mtx_unlock(&priv->mtx);
357ff3468acSIan Lepore 
358ff3468acSIan Lepore 	return (0);
359ff3468acSIan Lepore }
360ff3468acSIan Lepore 
361ff3468acSIan Lepore static bool
gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv * priv,struct gpioc_pin_intr * intr_conf)362ff3468acSIan Lepore gpioc_intr_reconfig_allowed(struct gpioc_cdevpriv *priv,
363ff3468acSIan Lepore     struct gpioc_pin_intr *intr_conf)
364ff3468acSIan Lepore {
365ff3468acSIan Lepore 	struct gpioc_privs	*priv_link;
366ff3468acSIan Lepore 
367ff3468acSIan Lepore 	mtx_assert(&intr_conf->mtx, MA_OWNED);
368ff3468acSIan Lepore 
369ff3468acSIan Lepore 	if (SLIST_EMPTY(&intr_conf->privs))
370ff3468acSIan Lepore 		return (true);
371ff3468acSIan Lepore 
372ff3468acSIan Lepore 	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
373ff3468acSIan Lepore 		if (priv_link->priv != priv)
374ff3468acSIan Lepore 			return (false);
375ff3468acSIan Lepore 	}
376ff3468acSIan Lepore 
377ff3468acSIan Lepore 	return (true);
378ff3468acSIan Lepore }
379ff3468acSIan Lepore 
380ff3468acSIan Lepore 
381ff3468acSIan Lepore static uint32_t
gpioc_get_intr_config(struct gpioc_softc * sc,struct gpioc_cdevpriv * priv,uint32_t pin)382ff3468acSIan Lepore gpioc_get_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
383ff3468acSIan Lepore     uint32_t pin)
384ff3468acSIan Lepore {
385ff3468acSIan Lepore 	struct gpioc_pin_intr	*intr_conf = &sc->sc_pin_intr[pin];
386ff3468acSIan Lepore 	struct gpioc_privs	*priv_link;
387ff3468acSIan Lepore 	uint32_t		flags;
388ff3468acSIan Lepore 
389ff3468acSIan Lepore 	flags = intr_conf->pin->flags;
390ff3468acSIan Lepore 
391ff3468acSIan Lepore 	if (flags == 0)
392ff3468acSIan Lepore 		return (0);
393ff3468acSIan Lepore 
394ff3468acSIan Lepore 	mtx_lock(&intr_conf->mtx);
395ff3468acSIan Lepore 	SLIST_FOREACH(priv_link, &intr_conf->privs, next) {
396ff3468acSIan Lepore 		if (priv_link->priv == priv) {
397ff3468acSIan Lepore 			flags |= GPIO_INTR_ATTACHED;
398ff3468acSIan Lepore 			break;
399ff3468acSIan Lepore 		}
400ff3468acSIan Lepore 	}
401ff3468acSIan Lepore 	mtx_unlock(&intr_conf->mtx);
402ff3468acSIan Lepore 
403ff3468acSIan Lepore 	return (flags);
404ff3468acSIan Lepore }
405ff3468acSIan Lepore 
406ff3468acSIan Lepore static int
gpioc_set_intr_config(struct gpioc_softc * sc,struct gpioc_cdevpriv * priv,uint32_t pin,uint32_t flags)407ff3468acSIan Lepore gpioc_set_intr_config(struct gpioc_softc *sc, struct gpioc_cdevpriv *priv,
408ff3468acSIan Lepore     uint32_t pin, uint32_t flags)
409ff3468acSIan Lepore {
410ff3468acSIan Lepore 	struct gpioc_pin_intr *intr_conf = &sc->sc_pin_intr[pin];
411ff3468acSIan Lepore 	int res;
412ff3468acSIan Lepore 
413ff3468acSIan Lepore 	res = 0;
414ff3468acSIan Lepore 	if (intr_conf->pin->flags == 0 && flags == 0) {
415ff3468acSIan Lepore 		/* No interrupt configured and none requested: Do nothing. */
416ff3468acSIan Lepore 		return (0);
417ff3468acSIan Lepore 	}
418ff3468acSIan Lepore 	mtx_lock(&intr_conf->mtx);
419ff3468acSIan Lepore 	while (intr_conf->config_locked == true)
420ff3468acSIan Lepore 		mtx_sleep(&intr_conf->config_locked, &intr_conf->mtx, 0,
421ff3468acSIan Lepore 		    "gpicfg", 0);
422ff3468acSIan Lepore 	if (intr_conf->pin->flags == 0 && flags != 0) {
423ff3468acSIan Lepore 		/*
424ff3468acSIan Lepore 		 * No interrupt is configured, but one is requested: Allocate
425ff3468acSIan Lepore 		 * and setup interrupt on the according pin.
426ff3468acSIan Lepore 		 */
427ff3468acSIan Lepore 		res = gpioc_allocate_pin_intr(intr_conf, flags);
428ff3468acSIan Lepore 		if (res == 0)
429ff3468acSIan Lepore 			res = gpioc_attach_priv_pin(priv, intr_conf);
430ff3468acSIan Lepore 		if (res == EEXIST)
431ff3468acSIan Lepore 			res = 0;
432ff3468acSIan Lepore 	} else if (intr_conf->pin->flags == flags) {
433ff3468acSIan Lepore 		/*
434ff3468acSIan Lepore 		 * Same interrupt requested as already configured: Attach the
435ff3468acSIan Lepore 		 * cdevpriv to the corresponding pin.
436ff3468acSIan Lepore 		 */
437ff3468acSIan Lepore 		res = gpioc_attach_priv_pin(priv, intr_conf);
438ff3468acSIan Lepore 		if (res == EEXIST)
439ff3468acSIan Lepore 			res = 0;
440ff3468acSIan Lepore 	} else if (intr_conf->pin->flags != 0 && flags == 0) {
441ff3468acSIan Lepore 		/*
442ff3468acSIan Lepore 		 * Interrupt configured, but none requested: Teardown and
443ff3468acSIan Lepore 		 * release the pin when no other cdevpriv is attached. Otherwise
444ff3468acSIan Lepore 		 * just detach pin and cdevpriv from each other.
445ff3468acSIan Lepore 		 */
446ff3468acSIan Lepore 		if (gpioc_intr_reconfig_allowed(priv, intr_conf)) {
447ff3468acSIan Lepore 			res = gpioc_release_pin_intr(intr_conf);
448ff3468acSIan Lepore 		}
449ff3468acSIan Lepore 		if (res == 0)
450ff3468acSIan Lepore 			res = gpioc_detach_priv_pin(priv, intr_conf);
451ff3468acSIan Lepore 	} else {
452ff3468acSIan Lepore 		/*
453ff3468acSIan Lepore 		 * Other flag requested than configured: Reconfigure when no
454ff3468acSIan Lepore 		 * other cdevpriv is are attached to the pin.
455ff3468acSIan Lepore 		 */
456ff3468acSIan Lepore 		if (!gpioc_intr_reconfig_allowed(priv, intr_conf))
457ff3468acSIan Lepore 			res = EBUSY;
458ff3468acSIan Lepore 		else {
459ff3468acSIan Lepore 			res = gpioc_release_pin_intr(intr_conf);
460ff3468acSIan Lepore 			if (res == 0)
461ff3468acSIan Lepore 				res = gpioc_allocate_pin_intr(intr_conf, flags);
462ff3468acSIan Lepore 			if (res == 0)
463ff3468acSIan Lepore 				res = gpioc_attach_priv_pin(priv, intr_conf);
464ff3468acSIan Lepore 			if (res == EEXIST)
465ff3468acSIan Lepore 				res = 0;
466ff3468acSIan Lepore 		}
467ff3468acSIan Lepore 	}
468ff3468acSIan Lepore 	mtx_unlock(&intr_conf->mtx);
469ff3468acSIan Lepore 
470ff3468acSIan Lepore 	return (res);
471ff3468acSIan Lepore }
472ff3468acSIan Lepore 
473ff3468acSIan Lepore static void
gpioc_interrupt_handler(void * arg)474ff3468acSIan Lepore gpioc_interrupt_handler(void *arg)
475ff3468acSIan Lepore {
476ff3468acSIan Lepore 	struct gpioc_pin_intr *intr_conf;
477ff3468acSIan Lepore 	struct gpioc_privs *privs;
478ff3468acSIan Lepore 	struct gpioc_softc *sc;
479ff3468acSIan Lepore 	sbintime_t evtime;
480ff3468acSIan Lepore 	uint32_t pin_state;
481ff3468acSIan Lepore 
482ff3468acSIan Lepore 	intr_conf = arg;
483ff3468acSIan Lepore 	sc = intr_conf->sc;
484ff3468acSIan Lepore 
485ff3468acSIan Lepore 	/* Capture time and pin state first. */
486ff3468acSIan Lepore 	evtime = sbinuptime();
487ff3468acSIan Lepore 	if (intr_conf->pin->flags & GPIO_INTR_EDGE_BOTH)
488ff3468acSIan Lepore 		GPIO_PIN_GET(sc->sc_pdev, intr_conf->pin->pin, &pin_state);
489ff3468acSIan Lepore 	else if (intr_conf->pin->flags & GPIO_INTR_EDGE_RISING)
490ff3468acSIan Lepore 		pin_state = true;
491ff3468acSIan Lepore 	else
492ff3468acSIan Lepore 		pin_state = false;
493ff3468acSIan Lepore 
494ff3468acSIan Lepore 	mtx_lock(&intr_conf->mtx);
495ff3468acSIan Lepore 
496ff3468acSIan Lepore 	if (intr_conf->config_locked == true) {
497ff3468acSIan Lepore 		ddevice_printf(sc->sc_dev, "Interrupt configuration in "
498ff3468acSIan Lepore 		    "progress. Discarding interrupt on pin %d.\n",
499ff3468acSIan Lepore 		    intr_conf->pin->pin);
500ff3468acSIan Lepore 		mtx_unlock(&intr_conf->mtx);
501ff3468acSIan Lepore 		return;
502ff3468acSIan Lepore 	}
503ff3468acSIan Lepore 
504ff3468acSIan Lepore 	if (SLIST_EMPTY(&intr_conf->privs)) {
505ff3468acSIan Lepore 		ddevice_printf(sc->sc_dev, "No file descriptor associated with "
506ff3468acSIan Lepore 		    "occurred interrupt on pin %d.\n", intr_conf->pin->pin);
507ff3468acSIan Lepore 		mtx_unlock(&intr_conf->mtx);
508ff3468acSIan Lepore 		return;
509ff3468acSIan Lepore 	}
510ff3468acSIan Lepore 
511ff3468acSIan Lepore 	SLIST_FOREACH(privs, &intr_conf->privs, next) {
512ff3468acSIan Lepore 		struct gpioc_cdevpriv *priv = privs->priv;
513ff3468acSIan Lepore 		struct gpioc_pins *privpin;
514ff3468acSIan Lepore 		struct gpioc_pin_event *event;
515ff3468acSIan Lepore 		mtx_lock(&priv->mtx);
516ff3468acSIan Lepore 		SLIST_FOREACH(privpin, &priv->pins, next) {
517ff3468acSIan Lepore 			if (privpin->pin == intr_conf)
518ff3468acSIan Lepore 				break;
519ff3468acSIan Lepore 		}
520ff3468acSIan Lepore 		if (privpin == NULL) {
521ff3468acSIan Lepore 			/* Should be impossible. */
522ff3468acSIan Lepore 			ddevice_printf(sc->sc_dev, "Cannot find privpin\n");
523ff3468acSIan Lepore 			mtx_unlock(&priv->mtx);
524ff3468acSIan Lepore 			continue;
525ff3468acSIan Lepore 		}
526ff3468acSIan Lepore 
527ff3468acSIan Lepore 		if (priv->report_option == GPIO_EVENT_REPORT_DETAIL) {
528ff3468acSIan Lepore 			event = next_head_event(priv);
529ff3468acSIan Lepore 			/* If head is overtaking tail, advance tail. */
530ff3468acSIan Lepore 			if (priv->evidx_head == priv->evidx_tail)
531ff3468acSIan Lepore 				next_tail_event(priv);
532ff3468acSIan Lepore 		} else {
533ff3468acSIan Lepore 			if (privpin->eventcount > 0)
534ff3468acSIan Lepore 				event = &priv->events[privpin->firstevent + 1];
535ff3468acSIan Lepore 			else {
536ff3468acSIan Lepore 				privpin->firstevent = priv->evidx_head;
537ff3468acSIan Lepore 				event = next_head_event(priv);
538ff3468acSIan Lepore 				event->privpin = privpin;
539ff3468acSIan Lepore 				event->event_time = evtime;
540ff3468acSIan Lepore 				event->event_pin_state = pin_state;
541ff3468acSIan Lepore 				event = next_head_event(priv);
542ff3468acSIan Lepore 			}
543ff3468acSIan Lepore 			++privpin->eventcount;
544ff3468acSIan Lepore 		}
545ff3468acSIan Lepore 		event->privpin = privpin;
546ff3468acSIan Lepore 		event->event_time = evtime;
547ff3468acSIan Lepore 		event->event_pin_state = pin_state;
548ff3468acSIan Lepore 		wakeup(priv);
549ff3468acSIan Lepore 		selwakeup(&priv->selinfo);
550ff3468acSIan Lepore 		KNOTE_LOCKED(&priv->selinfo.si_note, 0);
551ff3468acSIan Lepore 		if (priv->async == true && priv->sigio != NULL)
552ff3468acSIan Lepore 			pgsigio(&priv->sigio, SIGIO, 0);
553ff3468acSIan Lepore 		mtx_unlock(&priv->mtx);
554ff3468acSIan Lepore 	}
555ff3468acSIan Lepore 
556ff3468acSIan Lepore 	mtx_unlock(&intr_conf->mtx);
557ff3468acSIan Lepore }
558ff3468acSIan Lepore 
5596b34b16eSOleksandr Tymoshenko static int
gpioc_probe(device_t dev)5606b34b16eSOleksandr Tymoshenko gpioc_probe(device_t dev)
5616b34b16eSOleksandr Tymoshenko {
5626b34b16eSOleksandr Tymoshenko 	device_set_desc(dev, "GPIO controller");
5636b34b16eSOleksandr Tymoshenko 	return (0);
5646b34b16eSOleksandr Tymoshenko }
5656b34b16eSOleksandr Tymoshenko 
5666b34b16eSOleksandr Tymoshenko static int
gpioc_attach(device_t dev)5676b34b16eSOleksandr Tymoshenko gpioc_attach(device_t dev)
5686b34b16eSOleksandr Tymoshenko {
5694723c14fSLuiz Otavio O Souza 	int err;
5704723c14fSLuiz Otavio O Souza 	struct gpioc_softc *sc;
5714723c14fSLuiz Otavio O Souza 	struct make_dev_args devargs;
5726b34b16eSOleksandr Tymoshenko 
5734723c14fSLuiz Otavio O Souza 	sc = device_get_softc(dev);
5746b34b16eSOleksandr Tymoshenko 	sc->sc_dev = dev;
5756b34b16eSOleksandr Tymoshenko 	sc->sc_pdev = device_get_parent(dev);
5766b34b16eSOleksandr Tymoshenko 	sc->sc_unit = device_get_unit(dev);
577ff3468acSIan Lepore 
578ff3468acSIan Lepore 	err = GPIO_PIN_MAX(sc->sc_pdev, &sc->sc_npins);
579ff3468acSIan Lepore 	sc->sc_npins++; /* Number of pins is one more than max pin number. */
580ff3468acSIan Lepore 	if (err != 0)
581ff3468acSIan Lepore 		return (err);
582ff3468acSIan Lepore 	sc->sc_pin_intr = malloc(sizeof(struct gpioc_pin_intr) * sc->sc_npins,
583ff3468acSIan Lepore 	    M_GPIOC, M_WAITOK | M_ZERO);
584f2a7b434SHans Petter Selasky 	for (int i = 0; i < sc->sc_npins; i++) {
585ff3468acSIan Lepore 		sc->sc_pin_intr[i].pin = malloc(sizeof(struct gpiobus_pin),
586ff3468acSIan Lepore 		    M_GPIOC, M_WAITOK | M_ZERO);
587ff3468acSIan Lepore 		sc->sc_pin_intr[i].sc = sc;
588ff3468acSIan Lepore 		sc->sc_pin_intr[i].pin->pin = i;
589ff3468acSIan Lepore 		sc->sc_pin_intr[i].pin->dev = sc->sc_pdev;
590ff3468acSIan Lepore 		mtx_init(&sc->sc_pin_intr[i].mtx, "gpioc pin", NULL, MTX_DEF);
591ff3468acSIan Lepore 		SLIST_INIT(&sc->sc_pin_intr[i].privs);
592ff3468acSIan Lepore 	}
593ff3468acSIan Lepore 
5944723c14fSLuiz Otavio O Souza 	make_dev_args_init(&devargs);
5954723c14fSLuiz Otavio O Souza 	devargs.mda_devsw = &gpioc_cdevsw;
5964723c14fSLuiz Otavio O Souza 	devargs.mda_uid = UID_ROOT;
5974723c14fSLuiz Otavio O Souza 	devargs.mda_gid = GID_WHEEL;
5984723c14fSLuiz Otavio O Souza 	devargs.mda_mode = 0600;
5994723c14fSLuiz Otavio O Souza 	devargs.mda_si_drv1 = sc;
6004723c14fSLuiz Otavio O Souza 	err = make_dev_s(&devargs, &sc->sc_ctl_dev, "gpioc%d", sc->sc_unit);
6014723c14fSLuiz Otavio O Souza 	if (err != 0) {
602ff3468acSIan Lepore 		device_printf(dev, "Failed to create gpioc%d", sc->sc_unit);
6036b34b16eSOleksandr Tymoshenko 		return (ENXIO);
6046b34b16eSOleksandr Tymoshenko 	}
6056b34b16eSOleksandr Tymoshenko 
6066b34b16eSOleksandr Tymoshenko 	return (0);
6076b34b16eSOleksandr Tymoshenko }
6086b34b16eSOleksandr Tymoshenko 
6096b34b16eSOleksandr Tymoshenko static int
gpioc_detach(device_t dev)6106b34b16eSOleksandr Tymoshenko gpioc_detach(device_t dev)
6116b34b16eSOleksandr Tymoshenko {
6126b34b16eSOleksandr Tymoshenko 	struct gpioc_softc *sc = device_get_softc(dev);
6136b34b16eSOleksandr Tymoshenko 	int err;
6146b34b16eSOleksandr Tymoshenko 
6156de0a4faSOleksandr Tymoshenko 	if (sc->sc_ctl_dev)
6166b34b16eSOleksandr Tymoshenko 		destroy_dev(sc->sc_ctl_dev);
6176b34b16eSOleksandr Tymoshenko 
618f2a7b434SHans Petter Selasky 	for (int i = 0; i < sc->sc_npins; i++) {
619ff3468acSIan Lepore 		mtx_destroy(&sc->sc_pin_intr[i].mtx);
6203c6b5956SAndriy Gapon 		free(sc->sc_pin_intr[i].pin, M_GPIOC);
621ff3468acSIan Lepore 	}
622ff3468acSIan Lepore 	free(sc->sc_pin_intr, M_GPIOC);
623ff3468acSIan Lepore 
6246b34b16eSOleksandr Tymoshenko 	if ((err = bus_generic_detach(dev)) != 0)
6256b34b16eSOleksandr Tymoshenko 		return (err);
6266b34b16eSOleksandr Tymoshenko 
6276b34b16eSOleksandr Tymoshenko 	return (0);
6286b34b16eSOleksandr Tymoshenko }
6296b34b16eSOleksandr Tymoshenko 
630ff3468acSIan Lepore static void
gpioc_cdevpriv_dtor(void * data)631ff3468acSIan Lepore gpioc_cdevpriv_dtor(void *data)
632ff3468acSIan Lepore {
633ff3468acSIan Lepore 	struct gpioc_cdevpriv	*priv;
634ff3468acSIan Lepore 	struct gpioc_privs	*priv_link, *priv_link_temp;
635ff3468acSIan Lepore 	struct gpioc_pins	*pin_link, *pin_link_temp;
6367dc4d511SEd Maste 	unsigned int		consistency __diagused;
637ff3468acSIan Lepore 
638ff3468acSIan Lepore 	priv = data;
639ff3468acSIan Lepore 
640ff3468acSIan Lepore 	SLIST_FOREACH_SAFE(pin_link, &priv->pins, next, pin_link_temp) {
641ff3468acSIan Lepore 		consistency = 0;
642ff3468acSIan Lepore 		mtx_lock(&pin_link->pin->mtx);
643ff3468acSIan Lepore 		while (pin_link->pin->config_locked == true)
644ff3468acSIan Lepore 			mtx_sleep(&pin_link->pin->config_locked,
645ff3468acSIan Lepore 			    &pin_link->pin->mtx, 0, "gpicfg", 0);
646ff3468acSIan Lepore 		SLIST_FOREACH_SAFE(priv_link, &pin_link->pin->privs, next,
647ff3468acSIan Lepore 		    priv_link_temp) {
648ff3468acSIan Lepore 			if (priv_link->priv == priv) {
649ff3468acSIan Lepore 				SLIST_REMOVE(&pin_link->pin->privs, priv_link,
650ff3468acSIan Lepore 				    gpioc_privs, next);
651ff3468acSIan Lepore 				free(priv_link, M_GPIOC);
652ff3468acSIan Lepore 				consistency++;
653ff3468acSIan Lepore 			}
654ff3468acSIan Lepore 		}
655ff3468acSIan Lepore 		KASSERT(consistency == 1,
656ff3468acSIan Lepore 		    ("inconsistent links between pin config and cdevpriv"));
657ff3468acSIan Lepore 		if (gpioc_intr_reconfig_allowed(priv, pin_link->pin)) {
658ff3468acSIan Lepore 			gpioc_release_pin_intr(pin_link->pin);
659ff3468acSIan Lepore 		}
660ff3468acSIan Lepore 		mtx_unlock(&pin_link->pin->mtx);
661ff3468acSIan Lepore 		SLIST_REMOVE(&priv->pins, pin_link, gpioc_pins, next);
662ff3468acSIan Lepore 		free(pin_link, M_GPIOC);
663ff3468acSIan Lepore 	}
664ff3468acSIan Lepore 
665ff3468acSIan Lepore 	wakeup(&priv);
666ff3468acSIan Lepore 	knlist_clear(&priv->selinfo.si_note, 0);
667ff3468acSIan Lepore 	seldrain(&priv->selinfo);
668ff3468acSIan Lepore 	knlist_destroy(&priv->selinfo.si_note);
669ff3468acSIan Lepore 	funsetown(&priv->sigio);
670ff3468acSIan Lepore 
671ff3468acSIan Lepore 	mtx_destroy(&priv->mtx);
672ff3468acSIan Lepore 	free(priv->events, M_GPIOC);
673ff3468acSIan Lepore 	free(data, M_GPIOC);
674ff3468acSIan Lepore }
675ff3468acSIan Lepore 
676ff3468acSIan Lepore static int
gpioc_open(struct cdev * dev,int oflags,int devtype,struct thread * td)677ff3468acSIan Lepore gpioc_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
678ff3468acSIan Lepore {
679ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv;
680ff3468acSIan Lepore 	int err;
681ff3468acSIan Lepore 
682ff3468acSIan Lepore 	priv = malloc(sizeof(*priv), M_GPIOC, M_WAITOK | M_ZERO);
683ff3468acSIan Lepore 	priv->sc = dev->si_drv1;
684ff3468acSIan Lepore 	priv->report_option = GPIO_EVENT_REPORT_DETAIL;
685ff3468acSIan Lepore 	err = devfs_set_cdevpriv(priv, gpioc_cdevpriv_dtor);
686ff3468acSIan Lepore 	if (err != 0) {
687ff3468acSIan Lepore 		gpioc_cdevpriv_dtor(priv);
688ff3468acSIan Lepore 		return (err);
689ff3468acSIan Lepore 	}
690ff3468acSIan Lepore 	mtx_init(&priv->mtx, "gpioc priv", NULL, MTX_DEF);
691ff3468acSIan Lepore 	knlist_init_mtx(&priv->selinfo.si_note, &priv->mtx);
692ff3468acSIan Lepore 
693ff3468acSIan Lepore 	/*
694ff3468acSIan Lepore 	 * Allocate a circular buffer for events.  The scheme we use for summary
695ff3468acSIan Lepore 	 * reporting assumes there will always be a pair of events available to
696ff3468acSIan Lepore 	 * record the first/last events on any pin, so we allocate 2 * npins.
697ff3468acSIan Lepore 	 * Even though we actually default to detailed event reporting, 2 *
698ff3468acSIan Lepore 	 * npins isn't a horrible fifo size for that either.
699ff3468acSIan Lepore 	 */
700ff3468acSIan Lepore 	priv->numevents = priv->sc->sc_npins * 2;
701ff3468acSIan Lepore 	priv->events = malloc(priv->numevents * sizeof(struct gpio_event_detail),
702ff3468acSIan Lepore 	    M_GPIOC, M_WAITOK | M_ZERO);
703ff3468acSIan Lepore 
704ff3468acSIan Lepore 	return (0);
705ff3468acSIan Lepore }
706ff3468acSIan Lepore 
707ff3468acSIan Lepore static int
gpioc_read(struct cdev * dev,struct uio * uio,int ioflag)708ff3468acSIan Lepore gpioc_read(struct cdev *dev, struct uio *uio, int ioflag)
709ff3468acSIan Lepore {
710ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv;
711ff3468acSIan Lepore 	struct gpioc_pin_event *event;
712ff3468acSIan Lepore 	union {
713ff3468acSIan Lepore 		struct gpio_event_summary sum;
714ff3468acSIan Lepore 		struct gpio_event_detail  evt;
715ff3468acSIan Lepore 		uint8_t 		  data[1];
716ff3468acSIan Lepore 	} recbuf;
717ff3468acSIan Lepore 	size_t recsize;
718ff3468acSIan Lepore 	int err;
719ff3468acSIan Lepore 
720ff3468acSIan Lepore 	if ((err = devfs_get_cdevpriv((void **)&priv)) != 0)
721ff3468acSIan Lepore 		return (err);
722ff3468acSIan Lepore 
723ff3468acSIan Lepore 	if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY)
724ff3468acSIan Lepore 		recsize = sizeof(struct gpio_event_summary);
725ff3468acSIan Lepore 	else
726ff3468acSIan Lepore 		recsize = sizeof(struct gpio_event_detail);
727ff3468acSIan Lepore 
728ff3468acSIan Lepore 	if (uio->uio_resid < recsize)
729ff3468acSIan Lepore 		return (EINVAL);
730ff3468acSIan Lepore 
731ff3468acSIan Lepore 	mtx_lock(&priv->mtx);
732ff3468acSIan Lepore 	while (priv->evidx_head == priv->evidx_tail) {
733ff3468acSIan Lepore 		if (SLIST_EMPTY(&priv->pins)) {
734ff3468acSIan Lepore 			err = ENXIO;
735ff3468acSIan Lepore 			break;
736ff3468acSIan Lepore 		} else if (ioflag & O_NONBLOCK) {
737ff3468acSIan Lepore 			err = EWOULDBLOCK;
738ff3468acSIan Lepore 			break;
739ff3468acSIan Lepore 		} else {
740ff3468acSIan Lepore 			err = mtx_sleep(priv, &priv->mtx, PCATCH, "gpintr", 0);
741ff3468acSIan Lepore 			if (err != 0)
742ff3468acSIan Lepore 				break;
743ff3468acSIan Lepore 		}
744ff3468acSIan Lepore 	}
745ff3468acSIan Lepore 
746ff3468acSIan Lepore 	while (err == 0 && uio->uio_resid >= recsize &&
747ff3468acSIan Lepore            priv->evidx_tail != priv->evidx_head) {
748ff3468acSIan Lepore 		event = next_tail_event(priv);
749ff3468acSIan Lepore 		if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY) {
750ff3468acSIan Lepore 			recbuf.sum.gp_first_time = event->event_time;
751ff3468acSIan Lepore 			recbuf.sum.gp_pin = event->privpin->pin->pin->pin;
752ff3468acSIan Lepore 			recbuf.sum.gp_count = event->privpin->eventcount;
753ff3468acSIan Lepore 			recbuf.sum.gp_first_state = event->event_pin_state;
754ff3468acSIan Lepore 			event = next_tail_event(priv);
755ff3468acSIan Lepore 			recbuf.sum.gp_last_time = event->event_time;
756ff3468acSIan Lepore 			recbuf.sum.gp_last_state = event->event_pin_state;
757ff3468acSIan Lepore 			event->privpin->eventcount = 0;
758ff3468acSIan Lepore 			event->privpin->firstevent = 0;
759ff3468acSIan Lepore 		} else {
760ff3468acSIan Lepore 			recbuf.evt.gp_time = event->event_time;
761ff3468acSIan Lepore 			recbuf.evt.gp_pin = event->privpin->pin->pin->pin;
762ff3468acSIan Lepore 			recbuf.evt.gp_pinstate = event->event_pin_state;
763ff3468acSIan Lepore 		}
764ff3468acSIan Lepore 		mtx_unlock(&priv->mtx);
765ff3468acSIan Lepore 		err = uiomove(recbuf.data, recsize, uio);
766ff3468acSIan Lepore 		mtx_lock(&priv->mtx);
767ff3468acSIan Lepore 	}
768ff3468acSIan Lepore 	mtx_unlock(&priv->mtx);
769ff3468acSIan Lepore 	return (err);
770ff3468acSIan Lepore }
771ff3468acSIan Lepore 
7726b34b16eSOleksandr Tymoshenko static int
gpioc_ioctl(struct cdev * cdev,u_long cmd,caddr_t arg,int fflag,struct thread * td)7736b34b16eSOleksandr Tymoshenko gpioc_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
7746b34b16eSOleksandr Tymoshenko     struct thread *td)
7756b34b16eSOleksandr Tymoshenko {
776d752f0f6SLuiz Otavio O Souza 	device_t bus;
7776b34b16eSOleksandr Tymoshenko 	int max_pin, res;
7786b34b16eSOleksandr Tymoshenko 	struct gpioc_softc *sc = cdev->si_drv1;
779ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv;
7806b34b16eSOleksandr Tymoshenko 	struct gpio_pin pin;
7816b34b16eSOleksandr Tymoshenko 	struct gpio_req req;
782e1275c68SIan Lepore 	struct gpio_access_32 *a32;
783e1275c68SIan Lepore 	struct gpio_config_32 *c32;
784ff3468acSIan Lepore 	struct gpio_event_config *evcfg;
785ff3468acSIan Lepore 	uint32_t caps, intrflags;
7866b34b16eSOleksandr Tymoshenko 
787d752f0f6SLuiz Otavio O Souza 	bus = GPIO_GET_BUS(sc->sc_pdev);
788d752f0f6SLuiz Otavio O Souza 	if (bus == NULL)
789d752f0f6SLuiz Otavio O Souza 		return (EINVAL);
7906b34b16eSOleksandr Tymoshenko 	switch (cmd) {
7916b34b16eSOleksandr Tymoshenko 	case GPIOMAXPIN:
7926b34b16eSOleksandr Tymoshenko 		max_pin = -1;
7936b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_MAX(sc->sc_pdev, &max_pin);
7946b34b16eSOleksandr Tymoshenko 		bcopy(&max_pin, arg, sizeof(max_pin));
7956b34b16eSOleksandr Tymoshenko 		break;
7966b34b16eSOleksandr Tymoshenko 	case GPIOGETCONFIG:
7976b34b16eSOleksandr Tymoshenko 		bcopy(arg, &pin, sizeof(pin));
7986b34b16eSOleksandr Tymoshenko 		dprintf("get config pin %d\n", pin.gp_pin);
7996b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_GETFLAGS(sc->sc_pdev, pin.gp_pin,
8006b34b16eSOleksandr Tymoshenko 		    &pin.gp_flags);
8016b34b16eSOleksandr Tymoshenko 		/* Fail early */
8026b34b16eSOleksandr Tymoshenko 		if (res)
8036b34b16eSOleksandr Tymoshenko 			break;
804ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
805ff3468acSIan Lepore 		if (res)
806ff3468acSIan Lepore 			break;
807ff3468acSIan Lepore 		pin.gp_flags |= gpioc_get_intr_config(sc, priv,
808ff3468acSIan Lepore 		    pin.gp_pin);
8096b34b16eSOleksandr Tymoshenko 		GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &pin.gp_caps);
810d752f0f6SLuiz Otavio O Souza 		GPIOBUS_PIN_GETNAME(bus, pin.gp_pin, pin.gp_name);
8116b34b16eSOleksandr Tymoshenko 		bcopy(&pin, arg, sizeof(pin));
8126b34b16eSOleksandr Tymoshenko 		break;
8136b34b16eSOleksandr Tymoshenko 	case GPIOSETCONFIG:
8146b34b16eSOleksandr Tymoshenko 		bcopy(arg, &pin, sizeof(pin));
8156b34b16eSOleksandr Tymoshenko 		dprintf("set config pin %d\n", pin.gp_pin);
816ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
817ff3468acSIan Lepore 		if (res != 0)
818ff3468acSIan Lepore 			break;
819667357dcSLuiz Otavio O Souza 		res = GPIO_PIN_GETCAPS(sc->sc_pdev, pin.gp_pin, &caps);
820ff3468acSIan Lepore 		if (res != 0)
821ff3468acSIan Lepore 			break;
822667357dcSLuiz Otavio O Souza 		res = gpio_check_flags(caps, pin.gp_flags);
823ff3468acSIan Lepore 		if (res != 0)
824ff3468acSIan Lepore 			break;
825ff3468acSIan Lepore 		intrflags = pin.gp_flags & GPIO_INTR_MASK;
826ff3468acSIan Lepore 		/*
827ff3468acSIan Lepore 		 * We can do only edge interrupts, and only if the
828ff3468acSIan Lepore 		 * hardware supports that interrupt type on that pin.
829ff3468acSIan Lepore 		 */
830ff3468acSIan Lepore 		switch (intrflags) {
831ff3468acSIan Lepore 		case GPIO_INTR_NONE:
832ff3468acSIan Lepore 			break;
833ff3468acSIan Lepore 		case GPIO_INTR_EDGE_RISING:
834ff3468acSIan Lepore 		case GPIO_INTR_EDGE_FALLING:
835ff3468acSIan Lepore 		case GPIO_INTR_EDGE_BOTH:
836ff3468acSIan Lepore 			if ((intrflags & caps) == 0)
837ff3468acSIan Lepore 				res = EOPNOTSUPP;
838ff3468acSIan Lepore 			break;
839ff3468acSIan Lepore 		default:
840ff3468acSIan Lepore 			res = EINVAL;
841ff3468acSIan Lepore 			break;
842ff3468acSIan Lepore 		}
843ff3468acSIan Lepore 		if (res != 0)
844ff3468acSIan Lepore 			break;
8456b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_SETFLAGS(sc->sc_pdev, pin.gp_pin,
846ff3468acSIan Lepore 		    (pin.gp_flags & ~GPIO_INTR_MASK));
847ff3468acSIan Lepore 		if (res != 0)
848ff3468acSIan Lepore 			break;
849ff3468acSIan Lepore 		res = gpioc_set_intr_config(sc, priv, pin.gp_pin,
850ff3468acSIan Lepore 		    intrflags);
8516b34b16eSOleksandr Tymoshenko 		break;
8526b34b16eSOleksandr Tymoshenko 	case GPIOGET:
8536b34b16eSOleksandr Tymoshenko 		bcopy(arg, &req, sizeof(req));
8546b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_GET(sc->sc_pdev, req.gp_pin,
8556b34b16eSOleksandr Tymoshenko 		    &req.gp_value);
8566b34b16eSOleksandr Tymoshenko 		dprintf("read pin %d -> %d\n",
8576b34b16eSOleksandr Tymoshenko 		    req.gp_pin, req.gp_value);
8586b34b16eSOleksandr Tymoshenko 		bcopy(&req, arg, sizeof(req));
8596b34b16eSOleksandr Tymoshenko 		break;
8606b34b16eSOleksandr Tymoshenko 	case GPIOSET:
8616b34b16eSOleksandr Tymoshenko 		bcopy(arg, &req, sizeof(req));
8626b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_SET(sc->sc_pdev, req.gp_pin,
8636b34b16eSOleksandr Tymoshenko 		    req.gp_value);
8646b34b16eSOleksandr Tymoshenko 		dprintf("write pin %d -> %d\n",
8656b34b16eSOleksandr Tymoshenko 		    req.gp_pin, req.gp_value);
8666b34b16eSOleksandr Tymoshenko 		break;
8676b34b16eSOleksandr Tymoshenko 	case GPIOTOGGLE:
8686b34b16eSOleksandr Tymoshenko 		bcopy(arg, &req, sizeof(req));
8696b34b16eSOleksandr Tymoshenko 		dprintf("toggle pin %d\n",
8706b34b16eSOleksandr Tymoshenko 		    req.gp_pin);
8716b34b16eSOleksandr Tymoshenko 		res = GPIO_PIN_TOGGLE(sc->sc_pdev, req.gp_pin);
8726b34b16eSOleksandr Tymoshenko 		break;
873d752f0f6SLuiz Otavio O Souza 	case GPIOSETNAME:
874d752f0f6SLuiz Otavio O Souza 		bcopy(arg, &pin, sizeof(pin));
875d752f0f6SLuiz Otavio O Souza 		dprintf("set name on pin %d\n", pin.gp_pin);
876d752f0f6SLuiz Otavio O Souza 		res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin,
877d752f0f6SLuiz Otavio O Souza 		    pin.gp_name);
878d752f0f6SLuiz Otavio O Souza 		break;
879e1275c68SIan Lepore 	case GPIOACCESS32:
880e1275c68SIan Lepore 		a32 = (struct gpio_access_32 *)arg;
881e1275c68SIan Lepore 		res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin,
88220105d31SIan Lepore 		    a32->clear_pins, a32->change_pins, &a32->orig_pins);
883e1275c68SIan Lepore 		break;
884e1275c68SIan Lepore 	case GPIOCONFIG32:
885e1275c68SIan Lepore 		c32 = (struct gpio_config_32 *)arg;
886e1275c68SIan Lepore 		res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin,
887e1275c68SIan Lepore 		    c32->num_pins, c32->pin_flags);
888e1275c68SIan Lepore 		break;
889ff3468acSIan Lepore 	case GPIOCONFIGEVENTS:
890ff3468acSIan Lepore 		evcfg = (struct gpio_event_config *)arg;
891ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
892ff3468acSIan Lepore 		if (res != 0)
893ff3468acSIan Lepore 			break;
894ff3468acSIan Lepore 		/* If any pins have been configured, changes aren't allowed. */
895ff3468acSIan Lepore 		if (!SLIST_EMPTY(&priv->pins)) {
896ff3468acSIan Lepore 			res = EINVAL;
897ff3468acSIan Lepore 			break;
898ff3468acSIan Lepore 		}
899ff3468acSIan Lepore 		if (evcfg->gp_report_type != GPIO_EVENT_REPORT_DETAIL &&
900ff3468acSIan Lepore 		    evcfg->gp_report_type != GPIO_EVENT_REPORT_SUMMARY) {
901ff3468acSIan Lepore 			res = EINVAL;
902ff3468acSIan Lepore 			break;
903ff3468acSIan Lepore 		}
904ff3468acSIan Lepore 		priv->report_option = evcfg->gp_report_type;
905ff3468acSIan Lepore 		/* Reallocate the events buffer if the user wants it bigger. */
906ff3468acSIan Lepore 		if (priv->report_option == GPIO_EVENT_REPORT_DETAIL &&
907ff3468acSIan Lepore 		    priv->numevents < evcfg->gp_fifo_size) {
908ff3468acSIan Lepore 			free(priv->events, M_GPIOC);
909ff3468acSIan Lepore 			priv->numevents = evcfg->gp_fifo_size;
910ff3468acSIan Lepore 			priv->events = malloc(priv->numevents *
911ff3468acSIan Lepore 			    sizeof(struct gpio_event_detail), M_GPIOC,
912ff3468acSIan Lepore 			    M_WAITOK | M_ZERO);
913ff3468acSIan Lepore 			priv->evidx_head = priv->evidx_tail = 0;
914ff3468acSIan Lepore 		}
915ff3468acSIan Lepore 		break;
916ff3468acSIan Lepore 	case FIONBIO:
917ff3468acSIan Lepore 		/*
918ff3468acSIan Lepore 		 * This dummy handler is necessary to prevent fcntl()
919ff3468acSIan Lepore 		 * from failing. The actual handling of non-blocking IO
920ff3468acSIan Lepore 		 * is done using the O_NONBLOCK ioflag passed to the
921ff3468acSIan Lepore 		 * read() syscall.
922ff3468acSIan Lepore 		 */
923ff3468acSIan Lepore 		res = 0;
924ff3468acSIan Lepore 		break;
925ff3468acSIan Lepore 	case FIOASYNC:
926ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
927ff3468acSIan Lepore 		if (res == 0) {
928ff3468acSIan Lepore 			if (*(int *)arg == FASYNC)
929ff3468acSIan Lepore 				priv->async = true;
930ff3468acSIan Lepore 			else
931ff3468acSIan Lepore 				priv->async = false;
932ff3468acSIan Lepore 		}
933ff3468acSIan Lepore 		break;
934ff3468acSIan Lepore 	case FIOGETOWN:
935ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
936ff3468acSIan Lepore 		if (res == 0)
937ff3468acSIan Lepore 			*(int *)arg = fgetown(&priv->sigio);
938ff3468acSIan Lepore 		break;
939ff3468acSIan Lepore 	case FIOSETOWN:
940ff3468acSIan Lepore 		res = devfs_get_cdevpriv((void **)&priv);
941ff3468acSIan Lepore 		if (res == 0)
942ff3468acSIan Lepore 			res = fsetown(*(int *)arg, &priv->sigio);
943ff3468acSIan Lepore 		break;
9446b34b16eSOleksandr Tymoshenko 	default:
9456b34b16eSOleksandr Tymoshenko 		return (ENOTTY);
9466b34b16eSOleksandr Tymoshenko 		break;
9476b34b16eSOleksandr Tymoshenko 	}
9486b34b16eSOleksandr Tymoshenko 
9496b34b16eSOleksandr Tymoshenko 	return (res);
9506b34b16eSOleksandr Tymoshenko }
9516b34b16eSOleksandr Tymoshenko 
952ff3468acSIan Lepore static int
gpioc_poll(struct cdev * dev,int events,struct thread * td)953ff3468acSIan Lepore gpioc_poll(struct cdev *dev, int events, struct thread *td)
954ff3468acSIan Lepore {
955ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv;
956ff3468acSIan Lepore 	int err;
957ff3468acSIan Lepore 	int revents;
958ff3468acSIan Lepore 
959ff3468acSIan Lepore 	revents = 0;
960ff3468acSIan Lepore 
961ff3468acSIan Lepore 	err = devfs_get_cdevpriv((void **)&priv);
962ff3468acSIan Lepore 	if (err != 0) {
963ff3468acSIan Lepore 		revents = POLLERR;
964ff3468acSIan Lepore 		return (revents);
965ff3468acSIan Lepore 	}
966ff3468acSIan Lepore 
967ff3468acSIan Lepore 	if (SLIST_EMPTY(&priv->pins)) {
968ff3468acSIan Lepore 		revents = POLLHUP;
969ff3468acSIan Lepore 		return (revents);
970ff3468acSIan Lepore 	}
971ff3468acSIan Lepore 
972ff3468acSIan Lepore 	if (events & (POLLIN | POLLRDNORM)) {
973ff3468acSIan Lepore 		if (priv->evidx_head != priv->evidx_tail)
974ff3468acSIan Lepore 			revents |= events & (POLLIN | POLLRDNORM);
975ff3468acSIan Lepore 		else
976ff3468acSIan Lepore 			selrecord(td, &priv->selinfo);
977ff3468acSIan Lepore 	}
978ff3468acSIan Lepore 
979ff3468acSIan Lepore 	return (revents);
980ff3468acSIan Lepore }
981ff3468acSIan Lepore 
982ff3468acSIan Lepore static int
gpioc_kqfilter(struct cdev * dev,struct knote * kn)983ff3468acSIan Lepore gpioc_kqfilter(struct cdev *dev, struct knote *kn)
984ff3468acSIan Lepore {
985ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv;
986ff3468acSIan Lepore 	struct knlist *knlist;
987ff3468acSIan Lepore 	int err;
988ff3468acSIan Lepore 
989ff3468acSIan Lepore 	err = devfs_get_cdevpriv((void **)&priv);
990ff3468acSIan Lepore 	if (err != 0)
991ff3468acSIan Lepore 		return err;
992ff3468acSIan Lepore 
993ff3468acSIan Lepore 	if (SLIST_EMPTY(&priv->pins))
994ff3468acSIan Lepore 		return (ENXIO);
995ff3468acSIan Lepore 
996ff3468acSIan Lepore 	switch(kn->kn_filter) {
997ff3468acSIan Lepore 	case EVFILT_READ:
998ff3468acSIan Lepore 		kn->kn_fop = &gpioc_read_filterops;
999ff3468acSIan Lepore 		kn->kn_hook = (void *)priv;
1000ff3468acSIan Lepore 		break;
1001ff3468acSIan Lepore 	default:
1002ff3468acSIan Lepore 		return (EOPNOTSUPP);
1003ff3468acSIan Lepore 	}
1004ff3468acSIan Lepore 
1005ff3468acSIan Lepore 	knlist = &priv->selinfo.si_note;
1006ff3468acSIan Lepore 	knlist_add(knlist, kn, 0);
1007ff3468acSIan Lepore 
1008ff3468acSIan Lepore 	return (0);
1009ff3468acSIan Lepore }
1010ff3468acSIan Lepore 
1011ff3468acSIan Lepore static int
gpioc_kqread(struct knote * kn,long hint)1012ff3468acSIan Lepore gpioc_kqread(struct knote *kn, long hint)
1013ff3468acSIan Lepore {
1014ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv = kn->kn_hook;
1015ff3468acSIan Lepore 	size_t recsize;
1016ff3468acSIan Lepore 
1017ff3468acSIan Lepore 
1018ff3468acSIan Lepore 	if (SLIST_EMPTY(&priv->pins)) {
1019ff3468acSIan Lepore 		kn->kn_flags |= EV_EOF;
1020ff3468acSIan Lepore 		return (1);
1021ff3468acSIan Lepore 	} else {
1022ff3468acSIan Lepore 		if (priv->evidx_head != priv->evidx_tail) {
1023ff3468acSIan Lepore 			if (priv->report_option == GPIO_EVENT_REPORT_SUMMARY)
1024ff3468acSIan Lepore 				recsize = sizeof(struct gpio_event_summary);
1025ff3468acSIan Lepore 			else
1026ff3468acSIan Lepore 				recsize = sizeof(struct gpio_event_detail);
1027ff3468acSIan Lepore 			kn->kn_data = recsize * number_of_events(priv);
1028ff3468acSIan Lepore 			return (1);
1029ff3468acSIan Lepore 		}
1030ff3468acSIan Lepore 	}
1031ff3468acSIan Lepore 	return (0);
1032ff3468acSIan Lepore }
1033ff3468acSIan Lepore 
1034ff3468acSIan Lepore static void
gpioc_kqdetach(struct knote * kn)1035ff3468acSIan Lepore gpioc_kqdetach(struct knote *kn)
1036ff3468acSIan Lepore {
1037ff3468acSIan Lepore 	struct gpioc_cdevpriv *priv = kn->kn_hook;
1038ff3468acSIan Lepore 	struct knlist *knlist = &priv->selinfo.si_note;
1039ff3468acSIan Lepore 
1040ff3468acSIan Lepore 	knlist_remove(knlist, kn, 0);
1041ff3468acSIan Lepore }
1042ff3468acSIan Lepore 
10436b34b16eSOleksandr Tymoshenko static device_method_t gpioc_methods[] = {
10446b34b16eSOleksandr Tymoshenko 	/* Device interface */
10456b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_probe,		gpioc_probe),
10466b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_attach,	gpioc_attach),
10476b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_detach,	gpioc_detach),
10486b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
10496b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_suspend,	bus_generic_suspend),
10506b34b16eSOleksandr Tymoshenko 	DEVMETHOD(device_resume,	bus_generic_resume),
10516b34b16eSOleksandr Tymoshenko 
1052e2a1919dSOleksandr Tymoshenko 	DEVMETHOD_END
10536b34b16eSOleksandr Tymoshenko };
10546b34b16eSOleksandr Tymoshenko 
105554873b4cSAndrew Thompson driver_t gpioc_driver = {
10566b34b16eSOleksandr Tymoshenko 	"gpioc",
10576b34b16eSOleksandr Tymoshenko 	gpioc_methods,
10586b34b16eSOleksandr Tymoshenko 	sizeof(struct gpioc_softc)
10596b34b16eSOleksandr Tymoshenko };
10606b34b16eSOleksandr Tymoshenko 
1061d885615aSJohn Baldwin DRIVER_MODULE(gpioc, gpio, gpioc_driver, 0, 0);
10626b34b16eSOleksandr Tymoshenko MODULE_VERSION(gpioc, 1);
1063