gpiobus.c revision 213237
1/*-
2 * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/dev/gpio/gpiobus.c 213237 2010-09-28 03:24:53Z gonzo $");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/malloc.h>
32#include <sys/module.h>
33#include <sys/kernel.h>
34#include <sys/queue.h>
35#include <sys/sysctl.h>
36#include <sys/types.h>
37
38#include <sys/bus.h>
39#include <machine/bus.h>
40#include <sys/rman.h>
41#include <machine/resource.h>
42
43#include <sys/gpio.h>
44#include <dev/gpio/gpiobusvar.h>
45#include "gpio_if.h"
46#include "gpiobus_if.h"
47
48static void gpiobus_print_pins(struct gpiobus_ivar *);
49static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
50static int gpiobus_probe(device_t);
51static int gpiobus_attach(device_t);
52static int gpiobus_detach(device_t);
53static int gpiobus_suspend(device_t);
54static int gpiobus_resume(device_t);
55static int gpiobus_print_child(device_t, device_t);
56static int gpiobus_child_location_str(device_t, device_t, char *, size_t);
57static int gpiobus_child_pnpinfo_str(device_t, device_t, char *, size_t);
58static device_t gpiobus_add_child(device_t, u_int, const char *, int);
59static void gpiobus_hinted_child(device_t, const char *, int);
60
61/*
62 * GPIOBUS interface
63 */
64static void gpiobus_lock_bus(device_t);
65static void gpiobus_unlock_bus(device_t);
66static void gpiobus_acquire_bus(device_t, device_t);
67static void gpiobus_release_bus(device_t, device_t);
68static int gpiobus_pin_setflags(device_t, device_t, uint32_t, uint32_t);
69static int gpiobus_pin_getflags(device_t, device_t, uint32_t, uint32_t*);
70static int gpiobus_pin_getcaps(device_t, device_t, uint32_t, uint32_t*);
71static int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned int);
72static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*);
73static int gpiobus_pin_toggle(device_t, device_t, uint32_t);
74
75#define	GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
76#define	GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
77#define	GPIOBUS_LOCK_INIT(_sc) \
78	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
79	    "gpiobus", MTX_DEF)
80#define	GPIOBUS_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
81#define	GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
82#define	GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
83
84
85static void
86gpiobus_print_pins(struct gpiobus_ivar *devi)
87{
88	int range_start, range_stop, need_coma;
89	int i;
90
91	if (devi->npins == 0)
92		return;
93
94	need_coma = 0;
95	range_start = range_stop = devi->pins[0];
96	for (i = 1; i < devi->npins; i++) {
97		if (devi->pins[i] != (range_stop + 1)) {
98			if (need_coma)
99				printf(",");
100			if (range_start != range_stop)
101				printf("%d-%d", range_start, range_stop);
102			else
103				printf("%d", range_start);
104
105			range_start = range_stop = devi->pins[i];
106			need_coma = 1;
107		}
108		else
109			range_stop++;
110	}
111
112	if (need_coma)
113		printf(",");
114	if (range_start != range_stop)
115		printf("%d-%d", range_start, range_stop);
116	else
117		printf("%d", range_start);
118}
119
120static int
121gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
122{
123	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
124	int i, npins;
125
126	npins = 0;
127	for (i = 0; i < 32; i++) {
128		if (mask & (1 << i))
129			npins++;
130	}
131
132	if (npins == 0) {
133		device_printf(child, "empty pin mask");
134		return (EINVAL);
135	}
136
137	devi->npins = npins;
138	devi->pins = malloc(sizeof(uint32_t) * devi->npins, M_DEVBUF,
139	    M_NOWAIT | M_ZERO);
140
141	if (!devi->pins)
142		return (ENOMEM);
143
144	npins = 0;
145	for (i = 0; i < 32; i++) {
146
147		if ((mask & (1 << i)) == 0)
148			continue;
149
150		if (i >= sc->sc_npins) {
151			device_printf(child,
152			    "invalid pin %d, max: %d\n", i, sc->sc_npins - 1);
153			return (EINVAL);
154		}
155
156		devi->pins[npins++] = i;
157		/*
158		 * Mark pin as mapped and give warning if it's already mapped
159		 */
160		if (sc->sc_pins_mapped[i]) {
161			device_printf(child,
162			    "warning: pin %d is already mapped\n", i);
163			return (EINVAL);
164		}
165		sc->sc_pins_mapped[i] = 1;
166	}
167
168	return (0);
169}
170
171static int
172gpiobus_probe(device_t dev)
173{
174	device_set_desc(dev, "GPIO bus");
175	return (0);
176}
177
178static int
179gpiobus_attach(device_t dev)
180{
181	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
182	int res;
183
184	sc->sc_busdev = dev;
185	sc->sc_dev = device_get_parent(dev);
186	res = GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins);
187	if (res)
188		return (ENXIO);
189
190	/*
191	 * Increase to get number of pins
192	 */
193	sc->sc_npins++;
194
195	KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
196
197	sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF,
198	    M_NOWAIT | M_ZERO);
199
200	if (!sc->sc_pins_mapped)
201		return (ENOMEM);
202
203	/* init bus lock */
204	GPIOBUS_LOCK_INIT(sc);
205
206	/*
207	 * Get parent's pins and mark them as unmapped
208	 */
209	bus_enumerate_hinted_children(dev);
210	return (bus_generic_attach(dev));
211}
212
213/*
214 * Since this is not a self-enumerating bus, and since we always add
215 * children in attach, we have to always delete children here.
216 */
217static int
218gpiobus_detach(device_t dev)
219{
220	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
221	int err, ndevs, i;
222	device_t *devlist;
223
224	KASSERT(mtx_initialized(&sc->sc_mtx),
225	    ("gpiobus mutex not initialized"));
226	GPIOBUS_LOCK_DESTROY(sc);
227
228	if ((err = bus_generic_detach(dev)) != 0)
229		return (err);
230	if ((err = device_get_children(dev, &devlist, &ndevs)) != 0)
231		return (err);
232	for (i = 0; i < ndevs; i++)
233		device_delete_child(dev, devlist[i]);
234
235	if (sc->sc_pins_mapped) {
236		free(sc->sc_pins_mapped, M_DEVBUF);
237		sc->sc_pins_mapped = NULL;
238	}
239	free(devlist, M_TEMP);
240
241	return (0);
242}
243
244static int
245gpiobus_suspend(device_t dev)
246{
247
248	return (bus_generic_suspend(dev));
249}
250
251static int
252gpiobus_resume(device_t dev)
253{
254
255	return (bus_generic_resume(dev));
256}
257
258static int
259gpiobus_print_child(device_t dev, device_t child)
260{
261	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
262	int retval = 0;
263
264	retval += bus_print_child_header(dev, child);
265	retval += printf(" at pin(s) ");
266	gpiobus_print_pins(devi);
267	retval += bus_print_child_footer(dev, child);
268
269	return (retval);
270}
271
272static int
273gpiobus_child_location_str(device_t bus, device_t child, char *buf,
274    size_t buflen)
275{
276	// struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
277
278	snprintf(buf, buflen, "pins=?");
279	return (0);
280}
281
282static int
283gpiobus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
284    size_t buflen)
285{
286
287	*buf = '\0';
288	return (0);
289}
290
291static device_t
292gpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
293{
294	device_t child;
295	struct gpiobus_ivar *devi;
296
297	child = device_add_child_ordered(dev, order, name, unit);
298	if (child == NULL)
299		return (child);
300	devi = malloc(sizeof(struct gpiobus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO);
301	if (devi == NULL) {
302		device_delete_child(dev, child);
303		return (0);
304	}
305	device_set_ivars(child, devi);
306	return (child);
307}
308
309static void
310gpiobus_hinted_child(device_t bus, const char *dname, int dunit)
311{
312	struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
313	struct gpiobus_ivar *devi;
314	device_t child;
315	int pins;
316
317
318	child = BUS_ADD_CHILD(bus, 0, dname, dunit);
319	devi = GPIOBUS_IVAR(child);
320	resource_int_value(dname, dunit, "pins", &pins);
321	if (gpiobus_parse_pins(sc, child, pins))
322		device_delete_child(bus, child);
323}
324
325static void
326gpiobus_lock_bus(device_t busdev)
327{
328	struct gpiobus_softc *sc;
329
330	sc = device_get_softc(busdev);
331	GPIOBUS_ASSERT_UNLOCKED(sc);
332	GPIOBUS_LOCK(sc);
333}
334
335static void
336gpiobus_unlock_bus(device_t busdev)
337{
338	struct gpiobus_softc *sc;
339
340	sc = device_get_softc(busdev);
341	GPIOBUS_ASSERT_LOCKED(sc);
342	GPIOBUS_UNLOCK(sc);
343}
344
345static void
346gpiobus_acquire_bus(device_t busdev, device_t child)
347{
348	struct gpiobus_softc *sc;
349
350	sc = device_get_softc(busdev);
351	GPIOBUS_ASSERT_LOCKED(sc);
352
353	if (sc->sc_owner)
354		panic("rb_cpldbus: cannot serialize the access to device.");
355	sc->sc_owner = child;
356}
357
358static void
359gpiobus_release_bus(device_t busdev, device_t child)
360{
361	struct gpiobus_softc *sc;
362
363	sc = device_get_softc(busdev);
364	GPIOBUS_ASSERT_LOCKED(sc);
365
366	if (!sc->sc_owner)
367		panic("rb_cpldbus: releasing unowned bus.");
368	if (sc->sc_owner != child)
369		panic("rb_cpldbus: you don't own the bus. game over.");
370
371	sc->sc_owner = NULL;
372}
373
374static int
375gpiobus_pin_setflags(device_t dev, device_t child, uint32_t pin,
376    uint32_t flags)
377{
378	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
379	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
380
381	if (pin >= devi->npins)
382		return (EINVAL);
383
384	return GPIO_PIN_SETFLAGS(sc->sc_dev, devi->pins[pin], flags);
385}
386
387static int
388gpiobus_pin_getflags(device_t dev, device_t child, uint32_t pin,
389    uint32_t *flags)
390{
391	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
392	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
393
394	if (pin >= devi->npins)
395		return (EINVAL);
396
397	return GPIO_PIN_GETFLAGS(sc->sc_dev, devi->pins[pin], flags);
398}
399
400static int
401gpiobus_pin_getcaps(device_t dev, device_t child, uint32_t pin,
402    uint32_t *caps)
403{
404	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
405	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
406
407	if (pin >= devi->npins)
408		return (EINVAL);
409
410	return GPIO_PIN_GETCAPS(sc->sc_dev, devi->pins[pin], caps);
411}
412
413static int
414gpiobus_pin_set(device_t dev, device_t child, uint32_t pin,
415    unsigned int value)
416{
417	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
418	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
419
420	if (pin >= devi->npins)
421		return (EINVAL);
422
423	return GPIO_PIN_SET(sc->sc_dev, devi->pins[pin], value);
424}
425
426static int
427gpiobus_pin_get(device_t dev, device_t child, uint32_t pin,
428    unsigned int *value)
429{
430	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
431	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
432
433	if (pin >= devi->npins)
434		return (EINVAL);
435
436	return GPIO_PIN_GET(sc->sc_dev, devi->pins[pin], value);
437}
438
439static int
440gpiobus_pin_toggle(device_t dev, device_t child, uint32_t pin)
441{
442	struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
443	struct gpiobus_ivar *devi = GPIOBUS_IVAR(child);
444
445	if (pin >= devi->npins)
446		return (EINVAL);
447
448	return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]);
449}
450
451static device_method_t gpiobus_methods[] = {
452	/* Device interface */
453	DEVMETHOD(device_probe,		gpiobus_probe),
454	DEVMETHOD(device_attach,	gpiobus_attach),
455	DEVMETHOD(device_detach,	gpiobus_detach),
456	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
457	DEVMETHOD(device_suspend,	gpiobus_suspend),
458	DEVMETHOD(device_resume,	gpiobus_resume),
459
460	/* Bus interface */
461	DEVMETHOD(bus_add_child,	gpiobus_add_child),
462	DEVMETHOD(bus_print_child,	gpiobus_print_child),
463	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
464	DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
465	DEVMETHOD(bus_child_location_str, gpiobus_child_location_str),
466	DEVMETHOD(bus_hinted_child,	gpiobus_hinted_child),
467
468	/* GPIO protocol */
469	DEVMETHOD(gpiobus_lock_bus,	gpiobus_lock_bus),
470	DEVMETHOD(gpiobus_unlock_bus,	gpiobus_unlock_bus),
471	DEVMETHOD(gpiobus_acquire_bus,	gpiobus_acquire_bus),
472	DEVMETHOD(gpiobus_release_bus,	gpiobus_release_bus),
473	DEVMETHOD(gpiobus_pin_getflags,	gpiobus_pin_getflags),
474	DEVMETHOD(gpiobus_pin_getcaps,	gpiobus_pin_getcaps),
475	DEVMETHOD(gpiobus_pin_setflags,	gpiobus_pin_setflags),
476	DEVMETHOD(gpiobus_pin_get,	gpiobus_pin_get),
477	DEVMETHOD(gpiobus_pin_set,	gpiobus_pin_set),
478	DEVMETHOD(gpiobus_pin_toggle,	gpiobus_pin_toggle),
479
480	{ 0, 0 }
481};
482
483static driver_t gpiobus_driver = {
484	"gpiobus",
485	gpiobus_methods,
486	sizeof(struct gpiobus_softc)
487};
488
489devclass_t	gpiobus_devclass;
490
491DRIVER_MODULE(gpiobus, gpio, gpiobus_driver, gpiobus_devclass, 0, 0);
492MODULE_VERSION(gpiobus, 1);
493