gpiobus.c revision 279761
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 279761 2015-03-08 00:47:50Z 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);
56279620Sloosstatic void gpiobus_probe_nomatch(device_t, device_t);
57213237Sgonzostatic int gpiobus_print_child(device_t, device_t);
58213237Sgonzostatic int gpiobus_child_location_str(device_t, device_t, char *, size_t);
59213237Sgonzostatic int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t);
60213237Sgonzostatic device_t gpiobus_add_child(device_t, u_int, const char *, int);
61213237Sgonzostatic void gpiobus_hinted_child(device_t, const char *, int);
62213237Sgonzo
63213237Sgonzo/*
64213237Sgonzo * GPIOBUS interface
65213237Sgonzo */
66273917Sloosstatic int gpiobus_acquire_bus(device_t, device_t, int);
67213237Sgonzostatic void gpiobus_release_bus(device_t, device_t);
68213237Sgonzostatic int gpiobus_pin_setflags(device_t, device_t, uint32_t, uint32_t);
69213237Sgonzostatic int gpiobus_pin_getflags(device_t, device_t, uint32_t, uint32_t*);
70213237Sgonzostatic int gpiobus_pin_getcaps(device_t, device_t, uint32_t, uint32_t*);
71213237Sgonzostatic int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned int);
72213237Sgonzostatic int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*);
73213237Sgonzostatic int gpiobus_pin_toggle(device_t, device_t, uint32_t);
74213237Sgonzo
75274670Sloosint
76274670Sloosgpio_check_flags(uint32_t caps, uint32_t flags)
77274670Sloos{
78274670Sloos
79274670Sloos	/* Check for unwanted flags. */
80274670Sloos	if ((flags & caps) == 0 || (flags & caps) != flags)
81274670Sloos		return (EINVAL);
82274670Sloos	/* Cannot mix input/output together. */
83274670Sloos	if (flags & GPIO_PIN_INPUT && flags & GPIO_PIN_OUTPUT)
84274670Sloos		return (EINVAL);
85274670Sloos	/* Cannot mix pull-up/pull-down together. */
86274670Sloos	if (flags & GPIO_PIN_PULLUP && flags & GPIO_PIN_PULLDOWN)
87274670Sloos		return (EINVAL);
88274670Sloos
89274670Sloos	return (0);
90274670Sloos}
91274670Sloos
92274643Sloosstatic void
93274643Sloosgpiobus_print_pins(struct gpiobus_ivar *devi, char *buf, size_t buflen)
94213237Sgonzo{
95274643Sloos	char tmp[128];
96274643Sloos	int i, range_start, range_stop, need_coma;
97213237Sgonzo
98213237Sgonzo	if (devi->npins == 0)
99213237Sgonzo		return;
100213237Sgonzo
101213237Sgonzo	need_coma = 0;
102213237Sgonzo	range_start = range_stop = devi->pins[0];
103213237Sgonzo	for (i = 1; i < devi->npins; i++) {
104213237Sgonzo		if (devi->pins[i] != (range_stop + 1)) {
105213237Sgonzo			if (need_coma)
106274643Sloos				strlcat(buf, ",", buflen);
107274643Sloos			memset(tmp, 0, sizeof(tmp));
108213237Sgonzo			if (range_start != range_stop)
109274643Sloos				snprintf(tmp, sizeof(tmp) - 1, "%d-%d",
110274643Sloos				    range_start, range_stop);
111213237Sgonzo			else
112274643Sloos				snprintf(tmp, sizeof(tmp) - 1, "%d",
113274643Sloos				    range_start);
114274643Sloos			strlcat(buf, tmp, buflen);
115213237Sgonzo
116213237Sgonzo			range_start = range_stop = devi->pins[i];
117213237Sgonzo			need_coma = 1;
118213237Sgonzo		}
119213237Sgonzo		else
120213237Sgonzo			range_stop++;
121213237Sgonzo	}
122213237Sgonzo
123213237Sgonzo	if (need_coma)
124274643Sloos		strlcat(buf, ",", buflen);
125274643Sloos	memset(tmp, 0, sizeof(tmp));
126213237Sgonzo	if (range_start != range_stop)
127274643Sloos		snprintf(tmp, sizeof(tmp) - 1, "%d-%d",
128274643Sloos		    range_start, range_stop);
129213237Sgonzo	else
130274643Sloos		snprintf(tmp, sizeof(tmp) - 1, "%d",
131274643Sloos		    range_start);
132274643Sloos	strlcat(buf, tmp, buflen);
133213237Sgonzo}
134213237Sgonzo
135277996Sloosdevice_t
136277996Sloosgpiobus_attach_bus(device_t dev)
137277996Sloos{
138277996Sloos	device_t busdev;
139277996Sloos
140277996Sloos	busdev = device_add_child(dev, "gpiobus", -1);
141277996Sloos	if (busdev == NULL)
142277996Sloos		return (NULL);
143277996Sloos	if (device_add_child(dev, "gpioc", -1) == NULL) {
144277996Sloos		device_delete_child(dev, busdev);
145277996Sloos		return (NULL);
146277996Sloos	}
147278108Sloos#ifdef FDT
148278108Sloos	ofw_gpiobus_register_provider(dev);
149278108Sloos#endif
150277996Sloos	bus_generic_attach(dev);
151277996Sloos
152277996Sloos	return (busdev);
153277996Sloos}
154277996Sloos
155273569Sloosint
156277996Sloosgpiobus_detach_bus(device_t dev)
157277996Sloos{
158277996Sloos
159278108Sloos#ifdef FDT
160278108Sloos	ofw_gpiobus_unregister_provider(dev);
161278108Sloos#endif
162278108Sloos
163277996Sloos	return (bus_generic_detach(dev));
164277996Sloos}
165277996Sloos
166277996Sloosint
167273569Sloosgpiobus_init_softc(device_t dev)
168273569Sloos{
169273569Sloos	struct gpiobus_softc *sc;
170273569Sloos
171273569Sloos	sc = GPIOBUS_SOFTC(dev);
172273569Sloos	sc->sc_busdev = dev;
173273569Sloos	sc->sc_dev = device_get_parent(dev);
174274638Sloos	sc->sc_intr_rman.rm_type = RMAN_ARRAY;
175274638Sloos	sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
176274638Sloos	if (rman_init(&sc->sc_intr_rman) != 0 ||
177274638Sloos	    rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
178274638Sloos		panic("%s: failed to set up rman.", __func__);
179273569Sloos
180273569Sloos	if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
181273569Sloos		return (ENXIO);
182273569Sloos
183273569Sloos	KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
184273569Sloos
185273569Sloos	/* Pins = GPIO_PIN_MAX() + 1 */
186273569Sloos	sc->sc_npins++;
187273569Sloos
188279761Sloos	sc->sc_pins = malloc(sizeof(*sc->sc_pins) * sc->sc_npins, M_DEVBUF,
189273569Sloos	    M_NOWAIT | M_ZERO);
190279761Sloos	if (sc->sc_pins == NULL)
191273569Sloos		return (ENOMEM);
192273569Sloos
193273569Sloos	/* Initialize the bus lock. */
194273569Sloos	GPIOBUS_LOCK_INIT(sc);
195273569Sloos
196273569Sloos	return (0);
197273569Sloos}
198273569Sloos
199279402Sloosint
200279402Sloosgpiobus_alloc_ivars(struct gpiobus_ivar *devi)
201279402Sloos{
202279402Sloos
203279402Sloos	/* Allocate pins and flags memory. */
204279402Sloos	devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF,
205279402Sloos	    M_NOWAIT | M_ZERO);
206279402Sloos	if (devi->pins == NULL)
207279402Sloos		return (ENOMEM);
208279402Sloos	devi->flags = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF,
209279402Sloos	    M_NOWAIT | M_ZERO);
210279402Sloos	if (devi->flags == NULL) {
211279402Sloos		free(devi->pins, M_DEVBUF);
212279402Sloos		return (ENOMEM);
213279402Sloos	}
214279402Sloos
215279402Sloos	return (0);
216279402Sloos}
217279402Sloos
218279402Sloosvoid
219279402Sloosgpiobus_free_ivars(struct gpiobus_ivar *devi)
220279402Sloos{
221279402Sloos
222279402Sloos	if (devi->flags) {
223279402Sloos		free(devi->flags, M_DEVBUF);
224279402Sloos		devi->flags = NULL;
225279402Sloos	}
226279402Sloos	if (devi->pins) {
227279402Sloos		free(devi->pins, M_DEVBUF);
228279402Sloos		devi->pins = NULL;
229279402Sloos	}
230279402Sloos}
231279402Sloos
232279553Sloosint
233279622Sloosgpiobus_map_pin(device_t bus, uint32_t pin)
234279553Sloos{
235279553Sloos	struct gpiobus_softc *sc;
236279553Sloos
237279553Sloos	sc = device_get_softc(bus);
238279553Sloos	/* Consistency check. */
239279553Sloos	if (pin >= sc->sc_npins) {
240279622Sloos		device_printf(bus,
241279553Sloos		    "invalid pin %d, max: %d\n", pin, sc->sc_npins - 1);
242279553Sloos		return (-1);
243279553Sloos	}
244279553Sloos	/* Mark pin as mapped and give warning if it's already mapped. */
245279761Sloos	if (sc->sc_pins[pin].mapped) {
246279622Sloos		device_printf(bus, "warning: pin %d is already mapped\n", pin);
247279553Sloos		return (-1);
248279553Sloos	}
249279761Sloos	sc->sc_pins[pin].mapped = 1;
250279553Sloos
251279553Sloos	return (0);
252279553Sloos}
253279553Sloos
254213237Sgonzostatic int
255213237Sgonzogpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
256213237Sgonzo{
257213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
258213237Sgonzo	int i, npins;
259213237Sgonzo
260213237Sgonzo	npins = 0;
261213237Sgonzo	for (i = 0; i < 32; i++) {
262213237Sgonzo		if (mask & (1 << i))
263213237Sgonzo			npins++;
264213237Sgonzo	}
265213237Sgonzo	if (npins == 0) {
266255254Ssbruno		device_printf(child, "empty pin mask\n");
267213237Sgonzo		return (EINVAL);
268213237Sgonzo	}
269213237Sgonzo	devi->npins = npins;
270279402Sloos	if (gpiobus_alloc_ivars(devi) != 0) {
271279402Sloos		device_printf(child, "cannot allocate device ivars\n");
272279402Sloos		return (EINVAL);
273279402Sloos	}
274213237Sgonzo	npins = 0;
275213237Sgonzo	for (i = 0; i < 32; i++) {
276213237Sgonzo		if ((mask & (1 << i)) == 0)
277213237Sgonzo			continue;
278279553Sloos		/* Reserve the GPIO pin. */
279279622Sloos		if (gpiobus_map_pin(sc->sc_busdev, i) != 0) {
280279402Sloos			gpiobus_free_ivars(devi);
281213237Sgonzo			return (EINVAL);
282213237Sgonzo		}
283213237Sgonzo		devi->pins[npins++] = i;
284279761Sloos		/* Use the child name as pin name. */
285279761Sloos		GPIOBUS_PIN_SETNAME(sc->sc_busdev, i,
286279761Sloos		    device_get_nameunit(child));
287213237Sgonzo	}
288213237Sgonzo
289213237Sgonzo	return (0);
290213237Sgonzo}
291213237Sgonzo
292213237Sgonzostatic int
293213237Sgonzogpiobus_probe(device_t dev)
294213237Sgonzo{
295213237Sgonzo	device_set_desc(dev, "GPIO bus");
296258050Sloos
297258050Sloos	return (BUS_PROBE_GENERIC);
298213237Sgonzo}
299213237Sgonzo
300213237Sgonzostatic int
301213237Sgonzogpiobus_attach(device_t dev)
302213237Sgonzo{
303273569Sloos	int err;
304213237Sgonzo
305273569Sloos	err = gpiobus_init_softc(dev);
306273569Sloos	if (err != 0)
307273569Sloos		return (err);
308213237Sgonzo
309213237Sgonzo	/*
310213237Sgonzo	 * Get parent's pins and mark them as unmapped
311213237Sgonzo	 */
312258050Sloos	bus_generic_probe(dev);
313213237Sgonzo	bus_enumerate_hinted_children(dev);
314258050Sloos
315213237Sgonzo	return (bus_generic_attach(dev));
316213237Sgonzo}
317213237Sgonzo
318213237Sgonzo/*
319213237Sgonzo * Since this is not a self-enumerating bus, and since we always add
320213237Sgonzo * children in attach, we have to always delete children here.
321213237Sgonzo */
322213237Sgonzostatic int
323213237Sgonzogpiobus_detach(device_t dev)
324213237Sgonzo{
325254988Sloos	struct gpiobus_softc *sc;
326254988Sloos	struct gpiobus_ivar *devi;
327254988Sloos	device_t *devlist;
328254988Sloos	int i, err, ndevs;
329213237Sgonzo
330254988Sloos	sc = GPIOBUS_SOFTC(dev);
331213237Sgonzo	KASSERT(mtx_initialized(&sc->sc_mtx),
332213237Sgonzo	    ("gpiobus mutex not initialized"));
333213237Sgonzo	GPIOBUS_LOCK_DESTROY(sc);
334213237Sgonzo
335213237Sgonzo	if ((err = bus_generic_detach(dev)) != 0)
336213237Sgonzo		return (err);
337213237Sgonzo
338254988Sloos	if ((err = device_get_children(dev, &devlist, &ndevs)) != 0)
339254988Sloos		return (err);
340254988Sloos	for (i = 0; i < ndevs; i++) {
341254988Sloos		device_delete_child(dev, devlist[i]);
342254988Sloos		devi = GPIOBUS_IVAR(devlist[i]);
343279402Sloos		gpiobus_free_ivars(devi);
344254988Sloos	}
345254988Sloos	free(devlist, M_TEMP);
346279761Sloos	if (sc->sc_pins) {
347279761Sloos		for (i = 0; i < sc->sc_npins; i++) {
348279761Sloos			if (sc->sc_pins[i].name != NULL)
349279761Sloos				free(sc->sc_pins[i].name, M_DEVBUF);
350279761Sloos			sc->sc_pins[i].name = NULL;
351279761Sloos		}
352279761Sloos		free(sc->sc_pins, M_DEVBUF);
353279761Sloos		sc->sc_pins = NULL;
354213237Sgonzo	}
355213237Sgonzo
356213237Sgonzo	return (0);
357213237Sgonzo}
358213237Sgonzo
359213237Sgonzostatic int
360213237Sgonzogpiobus_suspend(device_t dev)
361213237Sgonzo{
362213237Sgonzo
363213237Sgonzo	return (bus_generic_suspend(dev));
364213237Sgonzo}
365213237Sgonzo
366213237Sgonzostatic int
367213237Sgonzogpiobus_resume(device_t dev)
368213237Sgonzo{
369213237Sgonzo
370213237Sgonzo	return (bus_generic_resume(dev));
371213237Sgonzo}
372213237Sgonzo
373279620Sloosstatic void
374279620Sloosgpiobus_probe_nomatch(device_t dev, device_t child)
375279620Sloos{
376279620Sloos	char pins[128];
377279620Sloos	struct gpiobus_ivar *devi;
378279620Sloos
379279620Sloos	devi = GPIOBUS_IVAR(child);
380279620Sloos	memset(pins, 0, sizeof(pins));
381279620Sloos	gpiobus_print_pins(devi, pins, sizeof(pins));
382279620Sloos	device_printf(dev, "<unknown device> at pin(s) %s", pins);
383279620Sloos	resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld");
384279620Sloos	printf("\n");
385279620Sloos}
386279620Sloos
387213237Sgonzostatic int
388213237Sgonzogpiobus_print_child(device_t dev, device_t child)
389213237Sgonzo{
390274643Sloos	char pins[128];
391213237Sgonzo	int retval = 0;
392274643Sloos	struct gpiobus_ivar *devi;
393213237Sgonzo
394274643Sloos	devi = GPIOBUS_IVAR(child);
395274643Sloos	memset(pins, 0, sizeof(pins));
396213237Sgonzo	retval += bus_print_child_header(dev, child);
397213237Sgonzo	retval += printf(" at pin(s) ");
398274643Sloos	gpiobus_print_pins(devi, pins, sizeof(pins));
399274643Sloos	retval += printf("%s", pins);
400274638Sloos	resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld");
401213237Sgonzo	retval += bus_print_child_footer(dev, child);
402213237Sgonzo
403213237Sgonzo	return (retval);
404213237Sgonzo}
405213237Sgonzo
406213237Sgonzostatic int
407213237Sgonzogpiobus_child_location_str(device_t bus, device_t child, char *buf,
408213237Sgonzo    size_t buflen)
409213237Sgonzo{
410274643Sloos	struct gpiobus_ivar *devi;
411213237Sgonzo
412274643Sloos	devi = GPIOBUS_IVAR(child);
413274643Sloos	strlcpy(buf, "pin(s)=", buflen);
414274643Sloos	gpiobus_print_pins(devi, buf, buflen);
415274643Sloos
416213237Sgonzo	return (0);
417213237Sgonzo}
418213237Sgonzo
419213237Sgonzostatic int
420213237Sgonzogpiobus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
421213237Sgonzo    size_t buflen)
422213237Sgonzo{
423213237Sgonzo
424213237Sgonzo	*buf = '\0';
425213237Sgonzo	return (0);
426213237Sgonzo}
427213237Sgonzo
428213237Sgonzostatic device_t
429213237Sgonzogpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
430213237Sgonzo{
431213237Sgonzo	device_t child;
432213237Sgonzo	struct gpiobus_ivar *devi;
433213237Sgonzo
434213237Sgonzo	child = device_add_child_ordered(dev, order, name, unit);
435213237Sgonzo	if (child == NULL)
436213237Sgonzo		return (child);
437213237Sgonzo	devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
438213237Sgonzo	if (devi == NULL) {
439213237Sgonzo		device_delete_child(dev, child);
440213237Sgonzo		return (0);
441213237Sgonzo	}
442274638Sloos	resource_list_init(&devi->rl);
443213237Sgonzo	device_set_ivars(child, devi);
444274638Sloos
445213237Sgonzo	return (child);
446213237Sgonzo}
447213237Sgonzo
448213237Sgonzostatic void
449213237Sgonzogpiobus_hinted_child(device_t bus, const char *dname, int dunit)
450213237Sgonzo{
451213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
452213237Sgonzo	struct gpiobus_ivar *devi;
453213237Sgonzo	device_t child;
454274638Sloos	int irq, pins;
455213237Sgonzo
456213237Sgonzo	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
457213237Sgonzo	devi = GPIOBUS_IVAR(child);
458213237Sgonzo	resource_int_value(dname, dunit, "pins", &pins);
459213237Sgonzo	if (gpiobus_parse_pins(sc, child, pins))
460213237Sgonzo		device_delete_child(bus, child);
461274638Sloos	if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
462274638Sloos		if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0)
463274638Sloos			device_printf(bus,
464274638Sloos			    "warning: bus_set_resource() failed\n");
465274638Sloos	}
466213237Sgonzo}
467213237Sgonzo
468273917Sloosstatic int
469274638Sloosgpiobus_set_resource(device_t dev, device_t child, int type, int rid,
470274638Sloos    u_long start, u_long count)
471274638Sloos{
472274638Sloos	struct gpiobus_ivar *devi;
473274638Sloos	struct resource_list_entry *rle;
474274638Sloos
475274638Sloos	dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
476274638Sloos	    __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
477274638Sloos	devi = GPIOBUS_IVAR(child);
478274638Sloos	rle = resource_list_add(&devi->rl, type, rid, start,
479274638Sloos	    start + count - 1, count);
480274638Sloos	if (rle == NULL)
481274638Sloos		return (ENXIO);
482274638Sloos
483274638Sloos	return (0);
484274638Sloos}
485274638Sloos
486274638Sloosstatic struct resource *
487274638Sloosgpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
488274638Sloos    u_long start, u_long end, u_long count, u_int flags)
489274638Sloos{
490274638Sloos	struct gpiobus_softc *sc;
491274638Sloos	struct resource *rv;
492274638Sloos	struct resource_list *rl;
493274638Sloos	struct resource_list_entry *rle;
494274638Sloos	int isdefault;
495274638Sloos
496274638Sloos	if (type != SYS_RES_IRQ)
497274638Sloos		return (NULL);
498274638Sloos	isdefault = (start == 0UL && end == ~0UL && count == 1);
499274638Sloos	rle = NULL;
500274638Sloos	if (isdefault) {
501274638Sloos		rl = BUS_GET_RESOURCE_LIST(bus, child);
502274638Sloos		if (rl == NULL)
503274638Sloos			return (NULL);
504274638Sloos		rle = resource_list_find(rl, type, *rid);
505274638Sloos		if (rle == NULL)
506274638Sloos			return (NULL);
507274638Sloos		if (rle->res != NULL)
508274638Sloos			panic("%s: resource entry is busy", __func__);
509274638Sloos		start = rle->start;
510274638Sloos		count = rle->count;
511274638Sloos		end = rle->end;
512274638Sloos	}
513274638Sloos	sc = device_get_softc(bus);
514274638Sloos	rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags,
515274638Sloos	    child);
516274638Sloos	if (rv == NULL)
517274638Sloos		return (NULL);
518274638Sloos	rman_set_rid(rv, *rid);
519274638Sloos	if ((flags & RF_ACTIVE) != 0 &&
520274638Sloos	    bus_activate_resource(child, type, *rid, rv) != 0) {
521274638Sloos		rman_release_resource(rv);
522274638Sloos		return (NULL);
523274638Sloos	}
524274638Sloos
525274638Sloos	return (rv);
526274638Sloos}
527274638Sloos
528274638Sloosstatic int
529274638Sloosgpiobus_release_resource(device_t bus __unused, device_t child, int type,
530274638Sloos    int rid, struct resource *r)
531274638Sloos{
532274638Sloos	int error;
533274638Sloos
534274638Sloos	if (rman_get_flags(r) & RF_ACTIVE) {
535274638Sloos		error = bus_deactivate_resource(child, type, rid, r);
536274638Sloos		if (error)
537274638Sloos			return (error);
538274638Sloos	}
539274638Sloos
540274638Sloos	return (rman_release_resource(r));
541274638Sloos}
542274638Sloos
543274638Sloosstatic struct resource_list *
544274638Sloosgpiobus_get_resource_list(device_t bus __unused, device_t child)
545274638Sloos{
546274638Sloos	struct gpiobus_ivar *ivar;
547274638Sloos
548274638Sloos	ivar = GPIOBUS_IVAR(child);
549274638Sloos
550274638Sloos	return (&ivar->rl);
551274638Sloos}
552274638Sloos
553274638Sloosstatic int
554273917Sloosgpiobus_acquire_bus(device_t busdev, device_t child, int how)
555213237Sgonzo{
556213237Sgonzo	struct gpiobus_softc *sc;
557213237Sgonzo
558213237Sgonzo	sc = device_get_softc(busdev);
559213237Sgonzo	GPIOBUS_ASSERT_UNLOCKED(sc);
560213237Sgonzo	GPIOBUS_LOCK(sc);
561273917Sloos	if (sc->sc_owner != NULL) {
562273917Sloos		if (how == GPIOBUS_DONTWAIT) {
563273917Sloos			GPIOBUS_UNLOCK(sc);
564273917Sloos			return (EWOULDBLOCK);
565273917Sloos		}
566273917Sloos		while (sc->sc_owner != NULL)
567273917Sloos			mtx_sleep(sc, &sc->sc_mtx, 0, "gpiobuswait", 0);
568273917Sloos	}
569273917Sloos	sc->sc_owner = child;
570213237Sgonzo	GPIOBUS_UNLOCK(sc);
571213237Sgonzo
572273917Sloos	return (0);
573213237Sgonzo}
574213237Sgonzo
575213237Sgonzostatic void
576213237Sgonzogpiobus_release_bus(device_t busdev, device_t child)
577213237Sgonzo{
578213237Sgonzo	struct gpiobus_softc *sc;
579213237Sgonzo
580213237Sgonzo	sc = device_get_softc(busdev);
581273917Sloos	GPIOBUS_ASSERT_UNLOCKED(sc);
582273917Sloos	GPIOBUS_LOCK(sc);
583273917Sloos	if (sc->sc_owner == NULL)
584243464Sgonzo		panic("gpiobus: releasing unowned bus.");
585213237Sgonzo	if (sc->sc_owner != child)
586243464Sgonzo		panic("gpiobus: you don't own the bus. game over.");
587213237Sgonzo	sc->sc_owner = NULL;
588273917Sloos	wakeup(sc);
589273917Sloos	GPIOBUS_UNLOCK(sc);
590213237Sgonzo}
591213237Sgonzo
592213237Sgonzostatic int
593213237Sgonzogpiobus_pin_setflags(device_t dev, device_t child, uint32_t pin,
594213237Sgonzo    uint32_t flags)
595213237Sgonzo{
596213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
597213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
598274670Sloos	uint32_t caps;
599213237Sgonzo
600213237Sgonzo	if (pin >= devi->npins)
601213237Sgonzo		return (EINVAL);
602274670Sloos	if (GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], &caps) != 0)
603274670Sloos		return (EINVAL);
604274670Sloos	if (gpio_check_flags(caps, flags) != 0)
605274670Sloos		return (EINVAL);
606213237Sgonzo
607274670Sloos	return (GPIO_PIN_SETFLAGS(sc->sc_dev, devi->pins[pin], flags));
608213237Sgonzo}
609213237Sgonzo
610213237Sgonzostatic int
611213237Sgonzogpiobus_pin_getflags(device_t dev, device_t child, uint32_t pin,
612213237Sgonzo    uint32_t *flags)
613213237Sgonzo{
614213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
615213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
616213237Sgonzo
617213237Sgonzo	if (pin >= devi->npins)
618213237Sgonzo		return (EINVAL);
619213237Sgonzo
620213237Sgonzo	return GPIO_PIN_GETFLAGS(sc->sc_dev, devi->pins[pin], flags);
621213237Sgonzo}
622213237Sgonzo
623213237Sgonzostatic int
624213237Sgonzogpiobus_pin_getcaps(device_t dev, device_t child, uint32_t pin,
625213237Sgonzo    uint32_t *caps)
626213237Sgonzo{
627213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
628213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
629213237Sgonzo
630213237Sgonzo	if (pin >= devi->npins)
631213237Sgonzo		return (EINVAL);
632213237Sgonzo
633213237Sgonzo	return GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], caps);
634213237Sgonzo}
635213237Sgonzo
636213237Sgonzostatic int
637213237Sgonzogpiobus_pin_set(device_t dev, device_t child, uint32_t pin,
638213237Sgonzo    unsigned int value)
639213237Sgonzo{
640213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
641213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
642213237Sgonzo
643213237Sgonzo	if (pin >= devi->npins)
644213237Sgonzo		return (EINVAL);
645213237Sgonzo
646213237Sgonzo	return GPIO_PIN_SET(sc->sc_dev, devi->pins[pin], value);
647213237Sgonzo}
648213237Sgonzo
649213237Sgonzostatic int
650213237Sgonzogpiobus_pin_get(device_t dev, device_t child, uint32_t pin,
651213237Sgonzo    unsigned int *value)
652213237Sgonzo{
653213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
654213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
655213237Sgonzo
656213237Sgonzo	if (pin >= devi->npins)
657213237Sgonzo		return (EINVAL);
658213237Sgonzo
659213237Sgonzo	return GPIO_PIN_GET(sc->sc_dev, devi->pins[pin], value);
660213237Sgonzo}
661213237Sgonzo
662213237Sgonzostatic int
663213237Sgonzogpiobus_pin_toggle(device_t dev, device_t child, uint32_t pin)
664213237Sgonzo{
665213237Sgonzo	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
666213237Sgonzo	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
667213237Sgonzo
668213237Sgonzo	if (pin >= devi->npins)
669213237Sgonzo		return (EINVAL);
670213237Sgonzo
671213237Sgonzo	return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]);
672213237Sgonzo}
673213237Sgonzo
674279761Sloosstatic int
675279761Sloosgpiobus_pin_getname(device_t dev, uint32_t pin, char *name)
676279761Sloos{
677279761Sloos	struct gpiobus_softc *sc;
678279761Sloos
679279761Sloos	sc = GPIOBUS_SOFTC(dev);
680279761Sloos	if (pin > sc->sc_npins)
681279761Sloos		return (EINVAL);
682279761Sloos	/* Did we have a name for this pin ? */
683279761Sloos	if (sc->sc_pins[pin].name != NULL) {
684279761Sloos		memcpy(name, sc->sc_pins[pin].name, GPIOMAXNAME);
685279761Sloos		return (0);
686279761Sloos	}
687279761Sloos
688279761Sloos	/* Return the default pin name. */
689279761Sloos	return (GPIO_PIN_GETNAME(device_get_parent(dev), pin, name));
690279761Sloos}
691279761Sloos
692279761Sloosstatic int
693279761Sloosgpiobus_pin_setname(device_t dev, uint32_t pin, const char *name)
694279761Sloos{
695279761Sloos	struct gpiobus_softc *sc;
696279761Sloos
697279761Sloos	sc = GPIOBUS_SOFTC(dev);
698279761Sloos	if (pin > sc->sc_npins)
699279761Sloos		return (EINVAL);
700279761Sloos	if (name == NULL)
701279761Sloos		return (EINVAL);
702279761Sloos	/* Save the pin name. */
703279761Sloos	if (sc->sc_pins[pin].name == NULL)
704279761Sloos		sc->sc_pins[pin].name = malloc(GPIOMAXNAME, M_DEVBUF,
705279761Sloos		    M_WAITOK | M_ZERO);
706279761Sloos	strlcpy(sc->sc_pins[pin].name, name, GPIOMAXNAME);
707279761Sloos
708279761Sloos	return (0);
709279761Sloos}
710279761Sloos
711213237Sgonzostatic device_method_t gpiobus_methods[] = {
712213237Sgonzo	/* Device interface */
713213237Sgonzo	DEVMETHOD(device_probe,		gpiobus_probe),
714213237Sgonzo	DEVMETHOD(device_attach,	gpiobus_attach),
715213237Sgonzo	DEVMETHOD(device_detach,	gpiobus_detach),
716213237Sgonzo	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
717213237Sgonzo	DEVMETHOD(device_suspend,	gpiobus_suspend),
718213237Sgonzo	DEVMETHOD(device_resume,	gpiobus_resume),
719213237Sgonzo
720213237Sgonzo	/* Bus interface */
721274638Sloos	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
722274638Sloos	DEVMETHOD(bus_config_intr,	bus_generic_config_intr),
723274638Sloos	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
724274638Sloos	DEVMETHOD(bus_set_resource,	gpiobus_set_resource),
725274638Sloos	DEVMETHOD(bus_alloc_resource,	gpiobus_alloc_resource),
726274638Sloos	DEVMETHOD(bus_release_resource,	gpiobus_release_resource),
727274638Sloos	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
728274638Sloos	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
729274638Sloos	DEVMETHOD(bus_get_resource_list,	gpiobus_get_resource_list),
730213237Sgonzo	DEVMETHOD(bus_add_child,	gpiobus_add_child),
731279620Sloos	DEVMETHOD(bus_probe_nomatch,	gpiobus_probe_nomatch),
732213237Sgonzo	DEVMETHOD(bus_print_child,	gpiobus_print_child),
733213237Sgonzo	DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
734213237Sgonzo	DEVMETHOD(bus_child_location_str, gpiobus_child_location_str),
735213237Sgonzo	DEVMETHOD(bus_hinted_child,	gpiobus_hinted_child),
736213237Sgonzo
737213237Sgonzo	/* GPIO protocol */
738213237Sgonzo	DEVMETHOD(gpiobus_acquire_bus,	gpiobus_acquire_bus),
739213237Sgonzo	DEVMETHOD(gpiobus_release_bus,	gpiobus_release_bus),
740213237Sgonzo	DEVMETHOD(gpiobus_pin_getflags,	gpiobus_pin_getflags),
741213237Sgonzo	DEVMETHOD(gpiobus_pin_getcaps,	gpiobus_pin_getcaps),
742213237Sgonzo	DEVMETHOD(gpiobus_pin_setflags,	gpiobus_pin_setflags),
743213237Sgonzo	DEVMETHOD(gpiobus_pin_get,	gpiobus_pin_get),
744213237Sgonzo	DEVMETHOD(gpiobus_pin_set,	gpiobus_pin_set),
745213237Sgonzo	DEVMETHOD(gpiobus_pin_toggle,	gpiobus_pin_toggle),
746279761Sloos	DEVMETHOD(gpiobus_pin_getname,	gpiobus_pin_getname),
747279761Sloos	DEVMETHOD(gpiobus_pin_setname,	gpiobus_pin_setname),
748213237Sgonzo
749227843Smarius	DEVMETHOD_END
750213237Sgonzo};
751213237Sgonzo
752215142Sthompsadriver_t gpiobus_driver = {
753213237Sgonzo	"gpiobus",
754213237Sgonzo	gpiobus_methods,
755213237Sgonzo	sizeof(struct gpiobus_softc)
756213237Sgonzo};
757213237Sgonzo
758213237Sgonzodevclass_t	gpiobus_devclass;
759213237Sgonzo
760213237SgonzoDRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0);
761213237SgonzoMODULE_VERSION(gpiobus, 1);
762