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