gpiobus.c revision 274670
1213237Sgonzo/*-
2213237Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
3213237Sgonzo * All rights reserved.
4213237Sgonzo *
5213237Sgonzo * Redistribution and use in source and binary forms, with or without
6213237Sgonzo * modification, are permitted provided that the following conditions
7213237Sgonzo * are met:
8213237Sgonzo * 1. Redistributions of source code must retain the above copyright
9213237Sgonzo *    notice, this list of conditions and the following disclaimer.
10213237Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11213237Sgonzo *    notice, this list of conditions and the following disclaimer in the
12213237Sgonzo *    documentation and/or other materials provided with the distribution.
13213237Sgonzo *
14213277Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15213277Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16213277Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17213277Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18213277Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19213277Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20213277Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21213277Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22213277Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23213277Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24213277Sgonzo * SUCH DAMAGE.
25213237Sgonzo */
26213237Sgonzo
27213237Sgonzo#include <sys/cdefs.h>
28213237Sgonzo__FBSDID("$FreeBSD: head/sys/dev/gpio/gpiobus.c 274670 2014-11-18 17:22:08Z loos $");
29213237Sgonzo
30213237Sgonzo#include <sys/param.h>
31265289Sloos#include <sys/systm.h>
32265191Sloos#include <sys/bus.h>
33274670Sloos#include <sys/gpio.h>
34265191Sloos#include <sys/kernel.h>
35213237Sgonzo#include <sys/malloc.h>
36213237Sgonzo#include <sys/module.h>
37213237Sgonzo
38265191Sloos#include <dev/gpio/gpiobusvar.h>
39213237Sgonzo
40213237Sgonzo#include "gpiobus_if.h"
41213237Sgonzo
42274638Sloos#undef GPIOBUS_DEBUG
43274638Sloos#ifdef GPIOBUS_DEBUG
44274638Sloos#define	dprintf printf
45274638Sloos#else
46274638Sloos#define	dprintf(x, arg...)
47274638Sloos#endif
48274638Sloos
49274643Sloosstatic void gpiobus_print_pins(struct gpiobus_ivar *, char *, size_t);
50213237Sgonzostatic int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
51213237Sgonzostatic int gpiobus_probe(device_t);
52213237Sgonzostatic int gpiobus_attach(device_t);
53213237Sgonzostatic int gpiobus_detach(device_t);
54213237Sgonzostatic int gpiobus_suspend(device_t);
55213237Sgonzostatic int gpiobus_resume(device_t);
56213237Sgonzostatic int gpiobus_print_child(device_t, device_t);
57213237Sgonzostatic int gpiobus_child_location_str(device_t, device_t, char *, size_t);
58213237Sgonzostatic int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t);
59213237Sgonzostatic device_t gpiobus_add_child(device_t, u_int, const char *, int);
60213237Sgonzostatic void gpiobus_hinted_child(device_t, const char *, int);
61213237Sgonzo
62213237Sgonzo/*
63213237Sgonzo * GPIOBUS interface
64213237Sgonzo */
65273917Sloosstatic int gpiobus_acquire_bus(device_t, device_t, int);
66213237Sgonzostatic void gpiobus_release_bus(device_t, device_t);
67213237Sgonzostatic int gpiobus_pin_setflags(device_t, device_t, uint32_t, uint32_t);
68213237Sgonzostatic int gpiobus_pin_getflags(device_t, device_t, uint32_t, uint32_t*);
69213237Sgonzostatic int gpiobus_pin_getcaps(device_t, device_t, uint32_t, uint32_t*);
70213237Sgonzostatic int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned int);
71213237Sgonzostatic int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*);
72213237Sgonzostatic int gpiobus_pin_toggle(device_t, device_t, uint32_t);
73213237Sgonzo
74274670Sloosint
75274670Sloosgpio_check_flags(uint32_t caps, uint32_t flags)
76274670Sloos{
77274670Sloos
78274670Sloos	/* Check for unwanted flags. */
79274670Sloos	if ((flags & caps) == 0 || (flags & caps) != flags)
80274670Sloos		return (EINVAL);
81274670Sloos	/* Cannot mix input/output together. */
82274670Sloos	if (flags & GPIO_PIN_INPUT && flags & GPIO_PIN_OUTPUT)
83274670Sloos		return (EINVAL);
84274670Sloos	/* Cannot mix pull-up/pull-down together. */
85274670Sloos	if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN)
86274670Sloos		return (EINVAL);
87274670Sloos
88274670Sloos	return (0);
89274670Sloos}
90274670Sloos
91274643Sloosstatic void
92274643Sloosgpiobus_print_pins(struct gpiobus_ivar *devi, char *buf, size_t buflen)
93213237Sgonzo{
94274643Sloos	char tmp[128];
95274643Sloos	int i, range_start, range_stop, need_coma;
96213237Sgonzo
97213237Sgonzo	if (devi->npins == 0)
98213237Sgonzo		return;
99213237Sgonzo
100213237Sgonzo	need_coma = 0;
101213237Sgonzo	range_start = range_stop = devi->pins[0];
102213237Sgonzo	for (i = 1; i < devi->npins; i++) {
103213237Sgonzo		if (devi->pins[i] != (range_stop + 1)) {
104213237Sgonzo			if (need_coma)
105274643Sloos				strlcat(buf, ",", buflen);
106274643Sloos			memset(tmp, 0, sizeof(tmp));
107213237Sgonzo			if (range_start != range_stop)
108274643Sloos				snprintf(tmp, sizeof(tmp) - 1, "%d-%d",
109274643Sloos				    range_start, range_stop);
110213237Sgonzo			else
111274643Sloos				snprintf(tmp, sizeof(tmp) - 1, "%d",
112274643Sloos				    range_start);
113274643Sloos			strlcat(buf, tmp, buflen);
114213237Sgonzo
115213237Sgonzo			range_start = range_stop = devi->pins[i];
116213237Sgonzo			need_coma = 1;
117213237Sgonzo		}
118213237Sgonzo		else
119213237Sgonzo			range_stop++;
120213237Sgonzo	}
121213237Sgonzo
122213237Sgonzo	if (need_coma)
123274643Sloos		strlcat(buf, ",", buflen);
124274643Sloos	memset(tmp, 0, sizeof(tmp));
125213237Sgonzo	if (range_start != range_stop)
126274643Sloos		snprintf(tmp, sizeof(tmp) - 1, "%d-%d",
127274643Sloos		    range_start, range_stop);
128213237Sgonzo	else
129274643Sloos		snprintf(tmp, sizeof(tmp) - 1, "%d",
130274643Sloos		    range_start);
131274643Sloos	strlcat(buf, tmp, buflen);
132213237Sgonzo}
133213237Sgonzo
134273569Sloosint
135273569Sloosgpiobus_init_softc(device_t dev)
136273569Sloos{
137273569Sloos	struct gpiobus_softc *sc;
138273569Sloos
139273569Sloos	sc = GPIOBUS_SOFTC(dev);
140273569Sloos	sc->sc_busdev = dev;
141273569Sloos	sc->sc_dev = device_get_parent(dev);
142274638Sloos	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
143274638Sloos	sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
144274638Sloos	if (rman_init(&sc->sc_intr_rman) != 0 ||
145274638Sloos	    rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
146274638Sloos		panic("%s: failed to set up rman.", __func__);
147273569Sloos
148273569Sloos	if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
149273569Sloos		return (ENXIO);
150273569Sloos
151273569Sloos	KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
152273569Sloos
153273569Sloos	/* Pins = GPIO_PIN_MAX() + 1 */
154273569Sloos	sc->sc_npins++;
155273569Sloos
156273569Sloos	sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF,
157273569Sloos	    M_NOWAIT | M_ZERO);
158273569Sloos	if (sc->sc_pins_mapped == NULL)
159273569Sloos		return (ENOMEM);
160273569Sloos
161273569Sloos	/* Initialize the bus lock. */
162273569Sloos	GPIOBUS_LOCK_INIT(sc);
163273569Sloos
164273569Sloos	return (0);
165273569Sloos}
166273569Sloos
167213237Sgonzostatic int
168213237Sgonzogpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
169213237Sgonzo{
170213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
171213237Sgonzo	int i, npins;
172213237Sgonzo
173213237Sgonzo	npins = 0;
174213237Sgonzo	for (i = 0; i < 32; i++) {
175213237Sgonzo		if (mask & (1 << i))
176213237Sgonzo			npins++;
177213237Sgonzo	}
178213237Sgonzo
179213237Sgonzo	if (npins == 0) {
180255254Ssbruno		device_printf(child, "empty pin mask\n");
181213237Sgonzo		return (EINVAL);
182213237Sgonzo	}
183213237Sgonzo
184213237Sgonzo	devi->npins = npins;
185213237Sgonzo	devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF,
186213237Sgonzo	    M_NOWAIT | M_ZERO);
187213237Sgonzo
188213237Sgonzo	if (!devi->pins)
189213237Sgonzo		return (ENOMEM);
190213237Sgonzo
191213237Sgonzo	npins = 0;
192213237Sgonzo	for (i = 0; i < 32; i++) {
193213237Sgonzo
194213237Sgonzo		if ((mask & (1 << i)) == 0)
195213237Sgonzo			continue;
196213237Sgonzo
197213237Sgonzo		if (i >= sc->sc_npins) {
198213237Sgonzo			device_printf(child,
199213237Sgonzo			    "invalid pin %d, max: %d\n", i, sc->sc_npins - 1);
200254988Sloos			free(devi->pins, M_DEVBUF);
201213237Sgonzo			return (EINVAL);
202213237Sgonzo		}
203213237Sgonzo
204213237Sgonzo		devi->pins[npins++] = i;
205213237Sgonzo		/*
206213237Sgonzo		 * Mark pin as mapped and give warning if it's already mapped
207213237Sgonzo		 */
208213237Sgonzo		if (sc->sc_pins_mapped[i]) {
209213237Sgonzo			device_printf(child,
210213237Sgonzo			    "warning: pin %d is already mapped\n", i);
211254988Sloos			free(devi->pins, M_DEVBUF);
212213237Sgonzo			return (EINVAL);
213213237Sgonzo		}
214213237Sgonzo		sc->sc_pins_mapped[i] = 1;
215213237Sgonzo	}
216213237Sgonzo
217213237Sgonzo	return (0);
218213237Sgonzo}
219213237Sgonzo
220213237Sgonzostatic int
221213237Sgonzogpiobus_probe(device_t dev)
222213237Sgonzo{
223213237Sgonzo	device_set_desc(dev, "GPIO bus");
224258050Sloos
225258050Sloos	return (BUS_PROBE_GENERIC);
226213237Sgonzo}
227213237Sgonzo
228213237Sgonzostatic int
229213237Sgonzogpiobus_attach(device_t dev)
230213237Sgonzo{
231273569Sloos	int err;
232213237Sgonzo
233273569Sloos	err = gpiobus_init_softc(dev);
234273569Sloos	if (err != 0)
235273569Sloos		return (err);
236213237Sgonzo
237213237Sgonzo	/*
238213237Sgonzo	 * Get parent's pins and mark them as unmapped
239213237Sgonzo	 */
240258050Sloos	bus_generic_probe(dev);
241213237Sgonzo	bus_enumerate_hinted_children(dev);
242258050Sloos
243213237Sgonzo	return (bus_generic_attach(dev));
244213237Sgonzo}
245213237Sgonzo
246213237Sgonzo/*
247213237Sgonzo * Since this is not a self-enumerating bus, and since we always add
248213237Sgonzo * children in attach, we have to always delete children here.
249213237Sgonzo */
250213237Sgonzostatic int
251213237Sgonzogpiobus_detach(device_t dev)
252213237Sgonzo{
253254988Sloos	struct gpiobus_softc *sc;
254254988Sloos	struct gpiobus_ivar *devi;
255254988Sloos	device_t *devlist;
256254988Sloos	int i, err, ndevs;
257213237Sgonzo
258254988Sloos	sc = GPIOBUS_SOFTC(dev);
259213237Sgonzo	KASSERT(mtx_initialized(&sc->sc_mtx),
260213237Sgonzo	    ("gpiobus mutex not initialized"));
261213237Sgonzo	GPIOBUS_LOCK_DESTROY(sc);
262213237Sgonzo
263213237Sgonzo	if ((err = bus_generic_detach(dev)) != 0)
264213237Sgonzo		return (err);
265213237Sgonzo
266254988Sloos	if ((err = device_get_children(dev, &devlist, &ndevs)) != 0)
267254988Sloos		return (err);
268254988Sloos	for (i = 0; i < ndevs; i++) {
269254988Sloos		device_delete_child(dev, devlist[i]);
270254988Sloos		devi = GPIOBUS_IVAR(devlist[i]);
271254988Sloos		if (devi->pins) {
272254988Sloos			free(devi->pins, M_DEVBUF);
273254988Sloos			devi->pins = NULL;
274254988Sloos		}
275254988Sloos	}
276254988Sloos	free(devlist, M_TEMP);
277227701Shselasky
278213237Sgonzo	if (sc->sc_pins_mapped) {
279213237Sgonzo		free(sc->sc_pins_mapped, M_DEVBUF);
280213237Sgonzo		sc->sc_pins_mapped = NULL;
281213237Sgonzo	}
282213237Sgonzo
283213237Sgonzo	return (0);
284213237Sgonzo}
285213237Sgonzo
286213237Sgonzostatic int
287213237Sgonzogpiobus_suspend(device_t dev)
288213237Sgonzo{
289213237Sgonzo
290213237Sgonzo	return (bus_generic_suspend(dev));
291213237Sgonzo}
292213237Sgonzo
293213237Sgonzostatic int
294213237Sgonzogpiobus_resume(device_t dev)
295213237Sgonzo{
296213237Sgonzo
297213237Sgonzo	return (bus_generic_resume(dev));
298213237Sgonzo}
299213237Sgonzo
300213237Sgonzostatic int
301213237Sgonzogpiobus_print_child(device_t dev, device_t child)
302213237Sgonzo{
303274643Sloos	char pins[128];
304213237Sgonzo	int retval = 0;
305274643Sloos	struct gpiobus_ivar *devi;
306213237Sgonzo
307274643Sloos	devi = GPIOBUS_IVAR(child);
308274643Sloos	memset(pins, 0, sizeof(pins));
309213237Sgonzo	retval += bus_print_child_header(dev, child);
310213237Sgonzo	retval += printf(" at pin(s) ");
311274643Sloos	gpiobus_print_pins(devi, pins, sizeof(pins));
312274643Sloos	retval += printf("%s", pins);
313274638Sloos	resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld");
314213237Sgonzo	retval += bus_print_child_footer(dev, child);
315213237Sgonzo
316213237Sgonzo	return (retval);
317213237Sgonzo}
318213237Sgonzo
319213237Sgonzostatic int
320213237Sgonzogpiobus_child_location_str(device_t bus, device_t child, char *buf,
321213237Sgonzo    size_t buflen)
322213237Sgonzo{
323274643Sloos	struct gpiobus_ivar *devi;
324213237Sgonzo
325274643Sloos	devi = GPIOBUS_IVAR(child);
326274643Sloos	strlcpy(buf, "pin(s)=", buflen);
327274643Sloos	gpiobus_print_pins(devi, buf, buflen);
328274643Sloos
329213237Sgonzo	return (0);
330213237Sgonzo}
331213237Sgonzo
332213237Sgonzostatic int
333213237Sgonzogpiobus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
334213237Sgonzo    size_t buflen)
335213237Sgonzo{
336213237Sgonzo
337213237Sgonzo	*buf = '\0';
338213237Sgonzo	return (0);
339213237Sgonzo}
340213237Sgonzo
341213237Sgonzostatic device_t
342213237Sgonzogpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
343213237Sgonzo{
344213237Sgonzo	device_t child;
345213237Sgonzo	struct gpiobus_ivar *devi;
346213237Sgonzo
347213237Sgonzo	child = device_add_child_ordered(dev, order, name, unit);
348213237Sgonzo	if (child == NULL)
349213237Sgonzo		return (child);
350213237Sgonzo	devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
351213237Sgonzo	if (devi == NULL) {
352213237Sgonzo		device_delete_child(dev, child);
353213237Sgonzo		return (0);
354213237Sgonzo	}
355274638Sloos	resource_list_init(&devi->rl);
356213237Sgonzo	device_set_ivars(child, devi);
357274638Sloos
358213237Sgonzo	return (child);
359213237Sgonzo}
360213237Sgonzo
361213237Sgonzostatic void
362213237Sgonzogpiobus_hinted_child(device_t bus, const char *dname, int dunit)
363213237Sgonzo{
364213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
365213237Sgonzo	struct gpiobus_ivar *devi;
366213237Sgonzo	device_t child;
367274638Sloos	int irq, pins;
368213237Sgonzo
369213237Sgonzo	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
370213237Sgonzo	devi = GPIOBUS_IVAR(child);
371213237Sgonzo	resource_int_value(dname, dunit, "pins", &pins);
372213237Sgonzo	if (gpiobus_parse_pins(sc, child, pins))
373213237Sgonzo		device_delete_child(bus, child);
374274638Sloos	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
375274638Sloos		if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0)
376274638Sloos			device_printf(bus,
377274638Sloos			    "warning: bus_set_resource() failed\n");
378274638Sloos	}
379213237Sgonzo}
380213237Sgonzo
381273917Sloosstatic int
382274638Sloosgpiobus_set_resource(device_t dev, device_t child, int type, int rid,
383274638Sloos    u_long start, u_long count)
384274638Sloos{
385274638Sloos	struct gpiobus_ivar *devi;
386274638Sloos	struct resource_list_entry *rle;
387274638Sloos
388274638Sloos	dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
389274638Sloos	    __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
390274638Sloos	devi = GPIOBUS_IVAR(child);
391274638Sloos	rle = resource_list_add(&devi->rl, type, rid, start,
392274638Sloos	    start + count - 1, count);
393274638Sloos	if (rle == NULL)
394274638Sloos		return (ENXIO);
395274638Sloos
396274638Sloos	return (0);
397274638Sloos}
398274638Sloos
399274638Sloosstatic struct resource *
400274638Sloosgpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
401274638Sloos    u_long start, u_long end, u_long count, u_int flags)
402274638Sloos{
403274638Sloos	struct gpiobus_softc *sc;
404274638Sloos	struct resource *rv;
405274638Sloos	struct resource_list *rl;
406274638Sloos	struct resource_list_entry *rle;
407274638Sloos	int isdefault;
408274638Sloos
409274638Sloos	if (type != SYS_RES_IRQ)
410274638Sloos		return (NULL);
411274638Sloos	isdefault = (start == 0UL && end == ~0UL && count == 1);
412274638Sloos	rle = NULL;
413274638Sloos	if (isdefault) {
414274638Sloos		rl = BUS_GET_RESOURCE_LIST(bus, child);
415274638Sloos		if (rl == NULL)
416274638Sloos			return (NULL);
417274638Sloos		rle = resource_list_find(rl, type, *rid);
418274638Sloos		if (rle == NULL)
419274638Sloos			return (NULL);
420274638Sloos		if (rle->res != NULL)
421274638Sloos			panic("%s: resource entry is busy", __func__);
422274638Sloos		start = rle->start;
423274638Sloos		count = rle->count;
424274638Sloos		end = rle->end;
425274638Sloos	}
426274638Sloos	sc = device_get_softc(bus);
427274638Sloos	rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags,
428274638Sloos	    child);
429274638Sloos	if (rv == NULL)
430274638Sloos		return (NULL);
431274638Sloos	rman_set_rid(rv, *rid);
432274638Sloos	if ((flags & RF_ACTIVE) != 0 &&
433274638Sloos	    bus_activate_resource(child, type, *rid, rv) != 0) {
434274638Sloos		rman_release_resource(rv);
435274638Sloos		return (NULL);
436274638Sloos	}
437274638Sloos
438274638Sloos	return (rv);
439274638Sloos}
440274638Sloos
441274638Sloosstatic int
442274638Sloosgpiobus_release_resource(device_t bus __unused, device_t child, int type,
443274638Sloos    int rid, struct resource *r)
444274638Sloos{
445274638Sloos	int error;
446274638Sloos
447274638Sloos	if (rman_get_flags(r) & RF_ACTIVE) {
448274638Sloos		error = bus_deactivate_resource(child, type, rid, r);
449274638Sloos		if (error)
450274638Sloos			return (error);
451274638Sloos	}
452274638Sloos
453274638Sloos	return (rman_release_resource(r));
454274638Sloos}
455274638Sloos
456274638Sloosstatic struct resource_list *
457274638Sloosgpiobus_get_resource_list(device_t bus __unused, device_t child)
458274638Sloos{
459274638Sloos	struct gpiobus_ivar *ivar;
460274638Sloos
461274638Sloos	ivar = GPIOBUS_IVAR(child);
462274638Sloos
463274638Sloos	return (&ivar->rl);
464274638Sloos}
465274638Sloos
466274638Sloosstatic int
467273917Sloosgpiobus_acquire_bus(device_t busdev, device_t child, int how)
468213237Sgonzo{
469213237Sgonzo	struct gpiobus_softc *sc;
470213237Sgonzo
471213237Sgonzo	sc = device_get_softc(busdev);
472213237Sgonzo	GPIOBUS_ASSERT_UNLOCKED(sc);
473213237Sgonzo	GPIOBUS_LOCK(sc);
474273917Sloos	if (sc->sc_owner != NULL) {
475273917Sloos		if (how == GPIOBUS_DONTWAIT) {
476273917Sloos			GPIOBUS_UNLOCK(sc);
477273917Sloos			return (EWOULDBLOCK);
478273917Sloos		}
479273917Sloos		while (sc->sc_owner != NULL)
480273917Sloos			mtx_sleep(sc, &sc->sc_mtx, 0, "gpiobuswait", 0);
481273917Sloos	}
482273917Sloos	sc->sc_owner = child;
483213237Sgonzo	GPIOBUS_UNLOCK(sc);
484213237Sgonzo
485273917Sloos	return (0);
486213237Sgonzo}
487213237Sgonzo
488213237Sgonzostatic void
489213237Sgonzogpiobus_release_bus(device_t busdev, device_t child)
490213237Sgonzo{
491213237Sgonzo	struct gpiobus_softc *sc;
492213237Sgonzo
493213237Sgonzo	sc = device_get_softc(busdev);
494273917Sloos	GPIOBUS_ASSERT_UNLOCKED(sc);
495273917Sloos	GPIOBUS_LOCK(sc);
496273917Sloos	if (sc->sc_owner == NULL)
497243464Sgonzo		panic("gpiobus: releasing unowned bus.");
498213237Sgonzo	if (sc->sc_owner != child)
499243464Sgonzo		panic("gpiobus: you don't own the bus. game over.");
500213237Sgonzo	sc->sc_owner = NULL;
501273917Sloos	wakeup(sc);
502273917Sloos	GPIOBUS_UNLOCK(sc);
503213237Sgonzo}
504213237Sgonzo
505213237Sgonzostatic int
506213237Sgonzogpiobus_pin_setflags(device_t dev, device_t child, uint32_t pin,
507213237Sgonzo    uint32_t flags)
508213237Sgonzo{
509213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
510213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
511274670Sloos	uint32_t caps;
512213237Sgonzo
513213237Sgonzo	if (pin >= devi->npins)
514213237Sgonzo		return (EINVAL);
515274670Sloos	if (GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], &caps) != 0)
516274670Sloos		return (EINVAL);
517274670Sloos	if (gpio_check_flags(caps, flags) != 0)
518274670Sloos		return (EINVAL);
519213237Sgonzo
520274670Sloos	return (GPIO_PIN_SETFLAGS(sc->sc_dev, devi->pins[pin], flags));
521213237Sgonzo}
522213237Sgonzo
523213237Sgonzostatic int
524213237Sgonzogpiobus_pin_getflags(device_t dev, device_t child, uint32_t pin,
525213237Sgonzo    uint32_t *flags)
526213237Sgonzo{
527213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
528213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
529213237Sgonzo
530213237Sgonzo	if (pin >= devi->npins)
531213237Sgonzo		return (EINVAL);
532213237Sgonzo
533213237Sgonzo	return GPIO_PIN_GETFLAGS(sc->sc_dev, devi->pins[pin], flags);
534213237Sgonzo}
535213237Sgonzo
536213237Sgonzostatic int
537213237Sgonzogpiobus_pin_getcaps(device_t dev, device_t child, uint32_t pin,
538213237Sgonzo    uint32_t *caps)
539213237Sgonzo{
540213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
541213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
542213237Sgonzo
543213237Sgonzo	if (pin >= devi->npins)
544213237Sgonzo		return (EINVAL);
545213237Sgonzo
546213237Sgonzo	return GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], caps);
547213237Sgonzo}
548213237Sgonzo
549213237Sgonzostatic int
550213237Sgonzogpiobus_pin_set(device_t dev, device_t child, uint32_t pin,
551213237Sgonzo    unsigned int value)
552213237Sgonzo{
553213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
554213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
555213237Sgonzo
556213237Sgonzo	if (pin >= devi->npins)
557213237Sgonzo		return (EINVAL);
558213237Sgonzo
559213237Sgonzo	return GPIO_PIN_SET(sc->sc_dev, devi->pins[pin], value);
560213237Sgonzo}
561213237Sgonzo
562213237Sgonzostatic int
563213237Sgonzogpiobus_pin_get(device_t dev, device_t child, uint32_t pin,
564213237Sgonzo    unsigned int *value)
565213237Sgonzo{
566213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
567213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
568213237Sgonzo
569213237Sgonzo	if (pin >= devi->npins)
570213237Sgonzo		return (EINVAL);
571213237Sgonzo
572213237Sgonzo	return GPIO_PIN_GET(sc->sc_dev, devi->pins[pin], value);
573213237Sgonzo}
574213237Sgonzo
575213237Sgonzostatic int
576213237Sgonzogpiobus_pin_toggle(device_t dev, device_t child, uint32_t pin)
577213237Sgonzo{
578213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
579213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
580213237Sgonzo
581213237Sgonzo	if (pin >= devi->npins)
582213237Sgonzo		return (EINVAL);
583213237Sgonzo
584213237Sgonzo	return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]);
585213237Sgonzo}
586213237Sgonzo
587213237Sgonzostatic device_method_t gpiobus_methods[] = {
588213237Sgonzo	/* Device interface */
589213237Sgonzo	DEVMETHOD(device_probe,		gpiobus_probe),
590213237Sgonzo	DEVMETHOD(device_attach,	gpiobus_attach),
591213237Sgonzo	DEVMETHOD(device_detach,	gpiobus_detach),
592213237Sgonzo	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
593213237Sgonzo	DEVMETHOD(device_suspend,	gpiobus_suspend),
594213237Sgonzo	DEVMETHOD(device_resume,	gpiobus_resume),
595213237Sgonzo
596213237Sgonzo	/* Bus interface */
597274638Sloos	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
598274638Sloos	DEVMETHOD(bus_config_intr,	bus_generic_config_intr),
599274638Sloos	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
600274638Sloos	DEVMETHOD(bus_set_resource,	gpiobus_set_resource),
601274638Sloos	DEVMETHOD(bus_alloc_resource,	gpiobus_alloc_resource),
602274638Sloos	DEVMETHOD(bus_release_resource,	gpiobus_release_resource),
603274638Sloos	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
604274638Sloos	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
605274638Sloos	DEVMETHOD(bus_get_resource_list,	gpiobus_get_resource_list),
606213237Sgonzo	DEVMETHOD(bus_add_child,	gpiobus_add_child),
607213237Sgonzo	DEVMETHOD(bus_print_child,	gpiobus_print_child),
608213237Sgonzo	DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
609213237Sgonzo	DEVMETHOD(bus_child_location_str, gpiobus_child_location_str),
610213237Sgonzo	DEVMETHOD(bus_hinted_child,	gpiobus_hinted_child),
611213237Sgonzo
612213237Sgonzo	/* GPIO protocol */
613213237Sgonzo	DEVMETHOD(gpiobus_acquire_bus,	gpiobus_acquire_bus),
614213237Sgonzo	DEVMETHOD(gpiobus_release_bus,	gpiobus_release_bus),
615213237Sgonzo	DEVMETHOD(gpiobus_pin_getflags,	gpiobus_pin_getflags),
616213237Sgonzo	DEVMETHOD(gpiobus_pin_getcaps,	gpiobus_pin_getcaps),
617213237Sgonzo	DEVMETHOD(gpiobus_pin_setflags,	gpiobus_pin_setflags),
618213237Sgonzo	DEVMETHOD(gpiobus_pin_get,	gpiobus_pin_get),
619213237Sgonzo	DEVMETHOD(gpiobus_pin_set,	gpiobus_pin_set),
620213237Sgonzo	DEVMETHOD(gpiobus_pin_toggle,	gpiobus_pin_toggle),
621213237Sgonzo
622227843Smarius	DEVMETHOD_END
623213237Sgonzo};
624213237Sgonzo
625215142Sthompsadriver_t gpiobus_driver = {
626213237Sgonzo	"gpiobus",
627213237Sgonzo	gpiobus_methods,
628213237Sgonzo	sizeof(struct gpiobus_softc)
629213237Sgonzo};
630213237Sgonzo
631213237Sgonzodevclass_t	gpiobus_devclass;
632213237Sgonzo
633213237SgonzoDRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0);
634213237SgonzoMODULE_VERSION(gpiobus, 1);
635