xref: /netbsd-src/sys/dev/fdt/fdt_gpio.c (revision df0d9b14d34ebdc7b83587988ae69606324a6092)
1*df0d9b14Sthorpej /* $NetBSD: fdt_gpio.c,v 1.7 2020/12/23 04:07:34 thorpej Exp $ */
27d3ef0b6Sjmcneill 
37d3ef0b6Sjmcneill /*-
47d3ef0b6Sjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
57d3ef0b6Sjmcneill  * All rights reserved.
67d3ef0b6Sjmcneill  *
77d3ef0b6Sjmcneill  * Redistribution and use in source and binary forms, with or without
87d3ef0b6Sjmcneill  * modification, are permitted provided that the following conditions
97d3ef0b6Sjmcneill  * are met:
107d3ef0b6Sjmcneill  * 1. Redistributions of source code must retain the above copyright
117d3ef0b6Sjmcneill  *    notice, this list of conditions and the following disclaimer.
127d3ef0b6Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
137d3ef0b6Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
147d3ef0b6Sjmcneill  *    documentation and/or other materials provided with the distribution.
157d3ef0b6Sjmcneill  *
167d3ef0b6Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177d3ef0b6Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
187d3ef0b6Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
197d3ef0b6Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
207d3ef0b6Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
217d3ef0b6Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
227d3ef0b6Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
237d3ef0b6Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
247d3ef0b6Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257d3ef0b6Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267d3ef0b6Sjmcneill  * SUCH DAMAGE.
277d3ef0b6Sjmcneill  */
287d3ef0b6Sjmcneill 
297d3ef0b6Sjmcneill #include <sys/cdefs.h>
30*df0d9b14Sthorpej __KERNEL_RCSID(0, "$NetBSD: fdt_gpio.c,v 1.7 2020/12/23 04:07:34 thorpej Exp $");
317d3ef0b6Sjmcneill 
327d3ef0b6Sjmcneill #include <sys/param.h>
337d3ef0b6Sjmcneill #include <sys/bus.h>
347d3ef0b6Sjmcneill #include <sys/kmem.h>
35233c5d85Sjmcneill #include <sys/queue.h>
367d3ef0b6Sjmcneill 
377d3ef0b6Sjmcneill #include <libfdt.h>
387d3ef0b6Sjmcneill #include <dev/fdt/fdtvar.h>
397d3ef0b6Sjmcneill 
407d3ef0b6Sjmcneill struct fdtbus_gpio_controller {
417d3ef0b6Sjmcneill 	device_t gc_dev;
427d3ef0b6Sjmcneill 	int gc_phandle;
437d3ef0b6Sjmcneill 	const struct fdtbus_gpio_controller_func *gc_funcs;
447d3ef0b6Sjmcneill 
45233c5d85Sjmcneill 	LIST_ENTRY(fdtbus_gpio_controller) gc_next;
467d3ef0b6Sjmcneill };
477d3ef0b6Sjmcneill 
48233c5d85Sjmcneill static LIST_HEAD(, fdtbus_gpio_controller) fdtbus_gpio_controllers =
49233c5d85Sjmcneill     LIST_HEAD_INITIALIZER(fdtbus_gpio_controllers);
507d3ef0b6Sjmcneill 
517d3ef0b6Sjmcneill int
fdtbus_register_gpio_controller(device_t dev,int phandle,const struct fdtbus_gpio_controller_func * funcs)527d3ef0b6Sjmcneill fdtbus_register_gpio_controller(device_t dev, int phandle,
537d3ef0b6Sjmcneill     const struct fdtbus_gpio_controller_func *funcs)
547d3ef0b6Sjmcneill {
557d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc;
567d3ef0b6Sjmcneill 
577d3ef0b6Sjmcneill 	gc = kmem_alloc(sizeof(*gc), KM_SLEEP);
587d3ef0b6Sjmcneill 	gc->gc_dev = dev;
597d3ef0b6Sjmcneill 	gc->gc_phandle = phandle;
607d3ef0b6Sjmcneill 	gc->gc_funcs = funcs;
617d3ef0b6Sjmcneill 
62233c5d85Sjmcneill 	LIST_INSERT_HEAD(&fdtbus_gpio_controllers, gc, gc_next);
637d3ef0b6Sjmcneill 
647d3ef0b6Sjmcneill 	return 0;
657d3ef0b6Sjmcneill }
667d3ef0b6Sjmcneill 
677d3ef0b6Sjmcneill static struct fdtbus_gpio_controller *
fdtbus_get_gpio_controller(int phandle)687d3ef0b6Sjmcneill fdtbus_get_gpio_controller(int phandle)
697d3ef0b6Sjmcneill {
707d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc;
717d3ef0b6Sjmcneill 
72233c5d85Sjmcneill 	LIST_FOREACH(gc, &fdtbus_gpio_controllers, gc_next) {
73233c5d85Sjmcneill 		if (gc->gc_phandle == phandle)
747d3ef0b6Sjmcneill 			return gc;
757d3ef0b6Sjmcneill 	}
767d3ef0b6Sjmcneill 
777d3ef0b6Sjmcneill 	return NULL;
787d3ef0b6Sjmcneill }
797d3ef0b6Sjmcneill 
80*df0d9b14Sthorpej int
fdtbus_gpio_count(int phandle,const char * prop)81*df0d9b14Sthorpej fdtbus_gpio_count(int phandle, const char *prop)
82*df0d9b14Sthorpej {
83*df0d9b14Sthorpej 	const uint32_t *gpios, *p;
84*df0d9b14Sthorpej 	u_int n, gpio_cells;
85*df0d9b14Sthorpej 	int len, resid;
86*df0d9b14Sthorpej 
87*df0d9b14Sthorpej 	gpios = fdtbus_get_prop(phandle, prop, &len);
88*df0d9b14Sthorpej 	if (gpios == NULL)
89*df0d9b14Sthorpej 		return 0;
90*df0d9b14Sthorpej 
91*df0d9b14Sthorpej 	p = gpios;
92*df0d9b14Sthorpej 	for (n = 0, resid = len; resid > 0; n++) {
93*df0d9b14Sthorpej 		const int gc_phandle =
94*df0d9b14Sthorpej 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
95*df0d9b14Sthorpej 		if (of_getprop_uint32(gc_phandle, "#gpio-cells", &gpio_cells))
96*df0d9b14Sthorpej 			break;
97*df0d9b14Sthorpej 		resid -= (gpio_cells + 1) * 4;
98*df0d9b14Sthorpej 		p += gpio_cells + 1;
99*df0d9b14Sthorpej 	}
100*df0d9b14Sthorpej 
101*df0d9b14Sthorpej 	return n;
102*df0d9b14Sthorpej }
103*df0d9b14Sthorpej 
1047d3ef0b6Sjmcneill struct fdtbus_gpio_pin *
fdtbus_gpio_acquire(int phandle,const char * prop,int flags)1057d3ef0b6Sjmcneill fdtbus_gpio_acquire(int phandle, const char *prop, int flags)
1067d3ef0b6Sjmcneill {
10797d3260cSjmcneill 	return fdtbus_gpio_acquire_index(phandle, prop, 0, flags);
10897d3260cSjmcneill }
10997d3260cSjmcneill 
11097d3260cSjmcneill struct fdtbus_gpio_pin *
fdtbus_gpio_acquire_index(int phandle,const char * prop,int index,int flags)11197d3260cSjmcneill fdtbus_gpio_acquire_index(int phandle, const char *prop,
11297d3260cSjmcneill     int index, int flags)
11397d3260cSjmcneill {
1147d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc;
11597d3260cSjmcneill 	struct fdtbus_gpio_pin *gp = NULL;
11697d3260cSjmcneill 	const uint32_t *gpios, *p;
11797d3260cSjmcneill 	u_int n, gpio_cells;
11897d3260cSjmcneill 	int len, resid;
1197d3ef0b6Sjmcneill 
12097d3260cSjmcneill 	gpios = fdtbus_get_prop(phandle, prop, &len);
12197d3260cSjmcneill 	if (gpios == NULL)
1227d3ef0b6Sjmcneill 		return NULL;
1237d3ef0b6Sjmcneill 
12497d3260cSjmcneill 	p = gpios;
12597d3260cSjmcneill 	for (n = 0, resid = len; resid > 0; n++) {
12697d3260cSjmcneill 		const int gc_phandle =
12797d3260cSjmcneill 		    fdtbus_get_phandle_from_native(be32toh(p[0]));
12897d3260cSjmcneill 		if (of_getprop_uint32(gc_phandle, "#gpio-cells", &gpio_cells))
12997d3260cSjmcneill 			break;
13097d3260cSjmcneill 		if (n == index) {
13197d3260cSjmcneill 			gc = fdtbus_get_gpio_controller(gc_phandle);
13297d3260cSjmcneill 			if (gc == NULL)
1337d3ef0b6Sjmcneill 				return NULL;
1347d3ef0b6Sjmcneill 			gp = kmem_alloc(sizeof(*gp), KM_SLEEP);
1357d3ef0b6Sjmcneill 			gp->gp_gc = gc;
13697d3260cSjmcneill 			gp->gp_priv = gc->gc_funcs->acquire(gc->gc_dev,
13797d3260cSjmcneill 			    &p[0], (gpio_cells + 1) * 4, flags);
1387d3ef0b6Sjmcneill 			if (gp->gp_priv == NULL) {
139f9c11d46Smaxv 				kmem_free(gp, sizeof(*gp));
1407d3ef0b6Sjmcneill 				return NULL;
1417d3ef0b6Sjmcneill 			}
14297d3260cSjmcneill 			break;
14397d3260cSjmcneill 		}
14497d3260cSjmcneill 		resid -= (gpio_cells + 1) * 4;
14597d3260cSjmcneill 		p += gpio_cells + 1;
14697d3260cSjmcneill 	}
1477d3ef0b6Sjmcneill 
1487d3ef0b6Sjmcneill 	return gp;
1497d3ef0b6Sjmcneill }
1507d3ef0b6Sjmcneill 
1517d3ef0b6Sjmcneill void
fdtbus_gpio_release(struct fdtbus_gpio_pin * gp)1527d3ef0b6Sjmcneill fdtbus_gpio_release(struct fdtbus_gpio_pin *gp)
1537d3ef0b6Sjmcneill {
1547d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc = gp->gp_gc;
1557d3ef0b6Sjmcneill 
1567d3ef0b6Sjmcneill 	gc->gc_funcs->release(gc->gc_dev, gp->gp_priv);
1577d3ef0b6Sjmcneill 	kmem_free(gp, sizeof(*gp));
1587d3ef0b6Sjmcneill }
1597d3ef0b6Sjmcneill 
1607d3ef0b6Sjmcneill int
fdtbus_gpio_read(struct fdtbus_gpio_pin * gp)1617d3ef0b6Sjmcneill fdtbus_gpio_read(struct fdtbus_gpio_pin *gp)
1627d3ef0b6Sjmcneill {
1637d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc = gp->gp_gc;
1647d3ef0b6Sjmcneill 
1651a623fc2Sjmcneill 	return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, false);
1667d3ef0b6Sjmcneill }
1677d3ef0b6Sjmcneill 
1687d3ef0b6Sjmcneill void
fdtbus_gpio_write(struct fdtbus_gpio_pin * gp,int val)1697d3ef0b6Sjmcneill fdtbus_gpio_write(struct fdtbus_gpio_pin *gp, int val)
1707d3ef0b6Sjmcneill {
1717d3ef0b6Sjmcneill 	struct fdtbus_gpio_controller *gc = gp->gp_gc;
1727d3ef0b6Sjmcneill 
1731a623fc2Sjmcneill 	gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, false);
1741a623fc2Sjmcneill }
1751a623fc2Sjmcneill 
1761a623fc2Sjmcneill int
fdtbus_gpio_read_raw(struct fdtbus_gpio_pin * gp)1771a623fc2Sjmcneill fdtbus_gpio_read_raw(struct fdtbus_gpio_pin *gp)
1781a623fc2Sjmcneill {
1791a623fc2Sjmcneill 	struct fdtbus_gpio_controller *gc = gp->gp_gc;
1801a623fc2Sjmcneill 
1811a623fc2Sjmcneill 	return gc->gc_funcs->read(gc->gc_dev, gp->gp_priv, true);
1821a623fc2Sjmcneill }
1831a623fc2Sjmcneill 
1841a623fc2Sjmcneill void
fdtbus_gpio_write_raw(struct fdtbus_gpio_pin * gp,int val)1851a623fc2Sjmcneill fdtbus_gpio_write_raw(struct fdtbus_gpio_pin *gp, int val)
1861a623fc2Sjmcneill {
1871a623fc2Sjmcneill 	struct fdtbus_gpio_controller *gc = gp->gp_gc;
1881a623fc2Sjmcneill 
1891a623fc2Sjmcneill 	gc->gc_funcs->write(gc->gc_dev, gp->gp_priv, val, true);
1907d3ef0b6Sjmcneill }
191