16d866ed3SLuiz Otavio O Souza /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
46d866ed3SLuiz Otavio O Souza * Copyright (c) 2009, Nathan Whitehorn <nwhitehorn@FreeBSD.org>
56d866ed3SLuiz Otavio O Souza * Copyright (c) 2013, Luiz Otavio O Souza <loos@FreeBSD.org>
66d866ed3SLuiz Otavio O Souza * Copyright (c) 2013 The FreeBSD Foundation
76d866ed3SLuiz Otavio O Souza * All rights reserved.
86d866ed3SLuiz Otavio O Souza *
96d866ed3SLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without
106d866ed3SLuiz Otavio O Souza * modification, are permitted provided that the following conditions
116d866ed3SLuiz Otavio O Souza * are met:
126d866ed3SLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright
136d866ed3SLuiz Otavio O Souza * notice unmodified, this list of conditions, and the following
146d866ed3SLuiz Otavio O Souza * disclaimer.
156d866ed3SLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright
166d866ed3SLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the
176d866ed3SLuiz Otavio O Souza * documentation and/or other materials provided with the distribution.
186d866ed3SLuiz Otavio O Souza *
196d866ed3SLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
206d866ed3SLuiz Otavio O Souza * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
216d866ed3SLuiz Otavio O Souza * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
226d866ed3SLuiz Otavio O Souza * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
236d866ed3SLuiz Otavio O Souza * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
246d866ed3SLuiz Otavio O Souza * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
256d866ed3SLuiz Otavio O Souza * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
266d866ed3SLuiz Otavio O Souza * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
276d866ed3SLuiz Otavio O Souza * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
286d866ed3SLuiz Otavio O Souza * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
296d866ed3SLuiz Otavio O Souza */
306d866ed3SLuiz Otavio O Souza
316d866ed3SLuiz Otavio O Souza #include <sys/param.h>
32160ca719SLuiz Otavio O Souza #include <sys/systm.h>
33002381dfSLuiz Otavio O Souza #include <sys/bus.h>
34002381dfSLuiz Otavio O Souza #include <sys/kernel.h>
35002381dfSLuiz Otavio O Souza #include <sys/malloc.h>
36002381dfSLuiz Otavio O Souza #include <sys/module.h>
376d866ed3SLuiz Otavio O Souza
386d866ed3SLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h>
396d866ed3SLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h>
406d866ed3SLuiz Otavio O Souza
41d752f0f6SLuiz Otavio O Souza #include "gpiobus_if.h"
42d752f0f6SLuiz Otavio O Souza
436d866ed3SLuiz Otavio O Souza static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t,
44e8da3e8aSLuiz Otavio O Souza device_t, phandle_t);
456ad7f491SLuiz Otavio O Souza static void ofw_gpiobus_destroy_devinfo(device_t, struct ofw_gpiobus_devinfo *);
46e8da3e8aSLuiz Otavio O Souza static int ofw_gpiobus_parse_gpios_impl(device_t, phandle_t, char *,
47e8da3e8aSLuiz Otavio O Souza struct gpiobus_softc *, struct gpiobus_pin **);
486d866ed3SLuiz Otavio O Souza
498a4ba038SMichal Meloun /*
508a4ba038SMichal Meloun * Utility functions for easier handling of OFW GPIO pins.
518a4ba038SMichal Meloun *
528a4ba038SMichal Meloun * !!! BEWARE !!!
538a4ba038SMichal Meloun * GPIOBUS uses children's IVARs, so we cannot use this interface for cross
548a4ba038SMichal Meloun * tree consumers.
558a4ba038SMichal Meloun *
568a4ba038SMichal Meloun */
57dc027dc6SIan Lepore int
gpio_pin_get_by_ofw_propidx(device_t consumer,phandle_t cnode,char * prop_name,int idx,gpio_pin_t * out_pin)58dc027dc6SIan Lepore gpio_pin_get_by_ofw_propidx(device_t consumer, phandle_t cnode,
5951702162SOleksandr Tymoshenko char *prop_name, int idx, gpio_pin_t *out_pin)
608a4ba038SMichal Meloun {
6151702162SOleksandr Tymoshenko phandle_t xref;
628a4ba038SMichal Meloun pcell_t *cells;
638a4ba038SMichal Meloun device_t busdev;
648a4ba038SMichal Meloun struct gpiobus_pin pin;
658a4ba038SMichal Meloun int ncells, rv;
668a4ba038SMichal Meloun
6751702162SOleksandr Tymoshenko KASSERT(consumer != NULL && cnode > 0,
6851702162SOleksandr Tymoshenko ("both consumer and cnode required"));
698a4ba038SMichal Meloun
708a4ba038SMichal Meloun rv = ofw_bus_parse_xref_list_alloc(cnode, prop_name, "#gpio-cells",
718a4ba038SMichal Meloun idx, &xref, &ncells, &cells);
728a4ba038SMichal Meloun if (rv != 0)
738a4ba038SMichal Meloun return (rv);
748a4ba038SMichal Meloun
758a4ba038SMichal Meloun /* Translate provider to device. */
768a4ba038SMichal Meloun pin.dev = OF_device_from_xref(xref);
778a4ba038SMichal Meloun if (pin.dev == NULL) {
78bc90a48cSOleksandr Tymoshenko OF_prop_free(cells);
798a4ba038SMichal Meloun return (ENODEV);
808a4ba038SMichal Meloun }
818a4ba038SMichal Meloun
828a4ba038SMichal Meloun /* Test if GPIO bus already exist. */
838a4ba038SMichal Meloun busdev = GPIO_GET_BUS(pin.dev);
848a4ba038SMichal Meloun if (busdev == NULL) {
85bc90a48cSOleksandr Tymoshenko OF_prop_free(cells);
868a4ba038SMichal Meloun return (ENODEV);
878a4ba038SMichal Meloun }
888a4ba038SMichal Meloun
898a4ba038SMichal Meloun /* Map GPIO pin. */
908a4ba038SMichal Meloun rv = gpio_map_gpios(pin.dev, cnode, OF_node_from_xref(xref), ncells,
918a4ba038SMichal Meloun cells, &pin.pin, &pin.flags);
92bc90a48cSOleksandr Tymoshenko OF_prop_free(cells);
9351702162SOleksandr Tymoshenko if (rv != 0)
948a4ba038SMichal Meloun return (ENXIO);
958a4ba038SMichal Meloun
968a4ba038SMichal Meloun /* Reserve GPIO pin. */
97c45b8422SIan Lepore rv = gpiobus_acquire_pin(busdev, pin.pin);
9851702162SOleksandr Tymoshenko if (rv != 0)
998a4ba038SMichal Meloun return (EBUSY);
1008a4ba038SMichal Meloun
1018a4ba038SMichal Meloun *out_pin = malloc(sizeof(struct gpiobus_pin), M_DEVBUF,
1028a4ba038SMichal Meloun M_WAITOK | M_ZERO);
1038a4ba038SMichal Meloun **out_pin = pin;
1048a4ba038SMichal Meloun return (0);
1058a4ba038SMichal Meloun }
1068a4ba038SMichal Meloun
1078a4ba038SMichal Meloun int
gpio_pin_get_by_ofw_idx(device_t consumer,phandle_t node,int idx,gpio_pin_t * pin)10851702162SOleksandr Tymoshenko gpio_pin_get_by_ofw_idx(device_t consumer, phandle_t node,
10951702162SOleksandr Tymoshenko int idx, gpio_pin_t *pin)
1108a4ba038SMichal Meloun {
1118a4ba038SMichal Meloun
112dc027dc6SIan Lepore return (gpio_pin_get_by_ofw_propidx(consumer, node, "gpios", idx, pin));
1138a4ba038SMichal Meloun }
1148a4ba038SMichal Meloun
1158a4ba038SMichal Meloun int
gpio_pin_get_by_ofw_property(device_t consumer,phandle_t node,char * name,gpio_pin_t * pin)11651702162SOleksandr Tymoshenko gpio_pin_get_by_ofw_property(device_t consumer, phandle_t node,
11751702162SOleksandr Tymoshenko char *name, gpio_pin_t *pin)
1188a4ba038SMichal Meloun {
1198a4ba038SMichal Meloun
120dc027dc6SIan Lepore return (gpio_pin_get_by_ofw_propidx(consumer, node, name, 0, pin));
1218a4ba038SMichal Meloun }
1228a4ba038SMichal Meloun
1238a4ba038SMichal Meloun int
gpio_pin_get_by_ofw_name(device_t consumer,phandle_t node,char * name,gpio_pin_t * pin)12451702162SOleksandr Tymoshenko gpio_pin_get_by_ofw_name(device_t consumer, phandle_t node,
12551702162SOleksandr Tymoshenko char *name, gpio_pin_t *pin)
1268a4ba038SMichal Meloun {
1278a4ba038SMichal Meloun int rv, idx;
1288a4ba038SMichal Meloun
12951702162SOleksandr Tymoshenko KASSERT(consumer != NULL && node > 0,
13051702162SOleksandr Tymoshenko ("both consumer and node required"));
13151702162SOleksandr Tymoshenko
13251702162SOleksandr Tymoshenko rv = ofw_bus_find_string_index(node, "gpio-names", name, &idx);
1338a4ba038SMichal Meloun if (rv != 0)
1348a4ba038SMichal Meloun return (rv);
13551702162SOleksandr Tymoshenko return (gpio_pin_get_by_ofw_idx(consumer, node, idx, pin));
1368a4ba038SMichal Meloun }
1378a4ba038SMichal Meloun
1388a4ba038SMichal Meloun /*
1398a4ba038SMichal Meloun * OFW_GPIOBUS driver.
1408a4ba038SMichal Meloun */
1416d866ed3SLuiz Otavio O Souza device_t
ofw_gpiobus_add_fdt_child(device_t bus,const char * drvname,phandle_t child)142e8da3e8aSLuiz Otavio O Souza ofw_gpiobus_add_fdt_child(device_t bus, const char *drvname, phandle_t child)
1436d866ed3SLuiz Otavio O Souza {
1446d866ed3SLuiz Otavio O Souza device_t childdev;
145d752f0f6SLuiz Otavio O Souza int i;
146d752f0f6SLuiz Otavio O Souza struct gpiobus_ivar *devi;
147e8da3e8aSLuiz Otavio O Souza struct ofw_gpiobus_devinfo *dinfo;
1486d866ed3SLuiz Otavio O Souza
1496d866ed3SLuiz Otavio O Souza /*
1503dede9f3SWarner Losh * Check to see if we already have a child for @p child, and if so
1513dede9f3SWarner Losh * return it.
1523dede9f3SWarner Losh */
1533dede9f3SWarner Losh childdev = ofw_bus_find_child_device_by_phandle(bus, child);
1543dede9f3SWarner Losh if (childdev != NULL)
1553dede9f3SWarner Losh return (childdev);
1563dede9f3SWarner Losh
1573dede9f3SWarner Losh /*
1586d866ed3SLuiz Otavio O Souza * Set up the GPIO child and OFW bus layer devinfo and add it to bus.
1596d866ed3SLuiz Otavio O Souza */
160e8da3e8aSLuiz Otavio O Souza childdev = device_add_child(bus, drvname, -1);
161e8da3e8aSLuiz Otavio O Souza if (childdev == NULL)
1626d866ed3SLuiz Otavio O Souza return (NULL);
163e8da3e8aSLuiz Otavio O Souza dinfo = ofw_gpiobus_setup_devinfo(bus, childdev, child);
164e8da3e8aSLuiz Otavio O Souza if (dinfo == NULL) {
165e8da3e8aSLuiz Otavio O Souza device_delete_child(bus, childdev);
1666d866ed3SLuiz Otavio O Souza return (NULL);
1676d866ed3SLuiz Otavio O Souza }
168e8da3e8aSLuiz Otavio O Souza if (device_probe_and_attach(childdev) != 0) {
1696ad7f491SLuiz Otavio O Souza ofw_gpiobus_destroy_devinfo(bus, dinfo);
170e8da3e8aSLuiz Otavio O Souza device_delete_child(bus, childdev);
171e8da3e8aSLuiz Otavio O Souza return (NULL);
172e8da3e8aSLuiz Otavio O Souza }
173d752f0f6SLuiz Otavio O Souza /* Use the child name as pin name. */
174d752f0f6SLuiz Otavio O Souza devi = &dinfo->opd_dinfo;
175d752f0f6SLuiz Otavio O Souza for (i = 0; i < devi->npins; i++)
176d752f0f6SLuiz Otavio O Souza GPIOBUS_PIN_SETNAME(bus, devi->pins[i],
177d752f0f6SLuiz Otavio O Souza device_get_nameunit(childdev));
1786d866ed3SLuiz Otavio O Souza
1796d866ed3SLuiz Otavio O Souza return (childdev);
1806d866ed3SLuiz Otavio O Souza }
1816d866ed3SLuiz Otavio O Souza
182e8da3e8aSLuiz Otavio O Souza int
ofw_gpiobus_parse_gpios(device_t consumer,char * pname,struct gpiobus_pin ** pins)183e8da3e8aSLuiz Otavio O Souza ofw_gpiobus_parse_gpios(device_t consumer, char *pname,
184e8da3e8aSLuiz Otavio O Souza struct gpiobus_pin **pins)
1856d866ed3SLuiz Otavio O Souza {
1866d866ed3SLuiz Otavio O Souza
187e8da3e8aSLuiz Otavio O Souza return (ofw_gpiobus_parse_gpios_impl(consumer,
188e8da3e8aSLuiz Otavio O Souza ofw_bus_get_node(consumer), pname, NULL, pins));
1896d866ed3SLuiz Otavio O Souza }
1906d866ed3SLuiz Otavio O Souza
191dbdb1205SLuiz Otavio O Souza void
ofw_gpiobus_register_provider(device_t provider)192dbdb1205SLuiz Otavio O Souza ofw_gpiobus_register_provider(device_t provider)
193dbdb1205SLuiz Otavio O Souza {
194dbdb1205SLuiz Otavio O Souza phandle_t node;
195dbdb1205SLuiz Otavio O Souza
196dbdb1205SLuiz Otavio O Souza node = ofw_bus_get_node(provider);
197f5e4e915SAndrew Turner if (node != -1)
198dbdb1205SLuiz Otavio O Souza OF_device_register_xref(OF_xref_from_node(node), provider);
199dbdb1205SLuiz Otavio O Souza }
200dbdb1205SLuiz Otavio O Souza
201dbdb1205SLuiz Otavio O Souza void
ofw_gpiobus_unregister_provider(device_t provider)202dbdb1205SLuiz Otavio O Souza ofw_gpiobus_unregister_provider(device_t provider)
203dbdb1205SLuiz Otavio O Souza {
204dbdb1205SLuiz Otavio O Souza phandle_t node;
205dbdb1205SLuiz Otavio O Souza
206dbdb1205SLuiz Otavio O Souza node = ofw_bus_get_node(provider);
207f5e4e915SAndrew Turner if (node != -1)
208dbdb1205SLuiz Otavio O Souza OF_device_register_xref(OF_xref_from_node(node), NULL);
209dbdb1205SLuiz Otavio O Souza }
210dbdb1205SLuiz Otavio O Souza
2116d866ed3SLuiz Otavio O Souza static struct ofw_gpiobus_devinfo *
ofw_gpiobus_setup_devinfo(device_t bus,device_t child,phandle_t node)212e8da3e8aSLuiz Otavio O Souza ofw_gpiobus_setup_devinfo(device_t bus, device_t child, phandle_t node)
2136d866ed3SLuiz Otavio O Souza {
214e8da3e8aSLuiz Otavio O Souza int i, npins;
215e8da3e8aSLuiz Otavio O Souza struct gpiobus_ivar *devi;
216e8da3e8aSLuiz Otavio O Souza struct gpiobus_pin *pins;
2176d866ed3SLuiz Otavio O Souza struct gpiobus_softc *sc;
2186d866ed3SLuiz Otavio O Souza struct ofw_gpiobus_devinfo *dinfo;
2196d866ed3SLuiz Otavio O Souza
220e8da3e8aSLuiz Otavio O Souza sc = device_get_softc(bus);
2216d866ed3SLuiz Otavio O Souza dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
2226d866ed3SLuiz Otavio O Souza if (dinfo == NULL)
2236d866ed3SLuiz Otavio O Souza return (NULL);
2246d866ed3SLuiz Otavio O Souza if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, node) != 0) {
2256d866ed3SLuiz Otavio O Souza free(dinfo, M_DEVBUF);
2266d866ed3SLuiz Otavio O Souza return (NULL);
2276d866ed3SLuiz Otavio O Souza }
2286d866ed3SLuiz Otavio O Souza /* Parse the gpios property for the child. */
229e8da3e8aSLuiz Otavio O Souza npins = ofw_gpiobus_parse_gpios_impl(child, node, "gpios", sc, &pins);
2306ad7f491SLuiz Otavio O Souza if (npins <= 0) {
2316ad7f491SLuiz Otavio O Souza ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
2326ad7f491SLuiz Otavio O Souza free(dinfo, M_DEVBUF);
2336ad7f491SLuiz Otavio O Souza return (NULL);
2346ad7f491SLuiz Otavio O Souza }
2356ad7f491SLuiz Otavio O Souza /* Initialize the irq resource list. */
2366ad7f491SLuiz Otavio O Souza resource_list_init(&dinfo->opd_dinfo.rl);
2376ad7f491SLuiz Otavio O Souza /* Allocate the child ivars and copy the parsed pin data. */
238e8da3e8aSLuiz Otavio O Souza devi = &dinfo->opd_dinfo;
239e8da3e8aSLuiz Otavio O Souza devi->npins = (uint32_t)npins;
240e8da3e8aSLuiz Otavio O Souza if (gpiobus_alloc_ivars(devi) != 0) {
241e8da3e8aSLuiz Otavio O Souza free(pins, M_DEVBUF);
2426ad7f491SLuiz Otavio O Souza ofw_gpiobus_destroy_devinfo(bus, dinfo);
2436ad7f491SLuiz Otavio O Souza return (NULL);
2446d866ed3SLuiz Otavio O Souza }
245404e6469SLi-Wen Hsu for (i = 0; i < devi->npins; i++)
246e8da3e8aSLuiz Otavio O Souza devi->pins[i] = pins[i].pin;
247e8da3e8aSLuiz Otavio O Souza free(pins, M_DEVBUF);
2486ad7f491SLuiz Otavio O Souza /* Parse the interrupt resources. */
249a8c5ea04SRuslan Bukin if (ofw_bus_intr_to_rl(bus, node, &dinfo->opd_dinfo.rl, NULL) != 0) {
2506ad7f491SLuiz Otavio O Souza ofw_gpiobus_destroy_devinfo(bus, dinfo);
2516ad7f491SLuiz Otavio O Souza return (NULL);
252e8da3e8aSLuiz Otavio O Souza }
253e8da3e8aSLuiz Otavio O Souza device_set_ivars(child, dinfo);
254e8da3e8aSLuiz Otavio O Souza
255e8da3e8aSLuiz Otavio O Souza return (dinfo);
256138bf909SLuiz Otavio O Souza }
2576d866ed3SLuiz Otavio O Souza
2586d866ed3SLuiz Otavio O Souza static void
ofw_gpiobus_destroy_devinfo(device_t bus,struct ofw_gpiobus_devinfo * dinfo)2596ad7f491SLuiz Otavio O Souza ofw_gpiobus_destroy_devinfo(device_t bus, struct ofw_gpiobus_devinfo *dinfo)
2606d866ed3SLuiz Otavio O Souza {
2616ad7f491SLuiz Otavio O Souza int i;
262e8da3e8aSLuiz Otavio O Souza struct gpiobus_ivar *devi;
2636ad7f491SLuiz Otavio O Souza struct gpiobus_softc *sc;
2646d866ed3SLuiz Otavio O Souza
2656ad7f491SLuiz Otavio O Souza sc = device_get_softc(bus);
266e8da3e8aSLuiz Otavio O Souza devi = &dinfo->opd_dinfo;
2676ad7f491SLuiz Otavio O Souza for (i = 0; i < devi->npins; i++) {
2686ad7f491SLuiz Otavio O Souza if (devi->pins[i] > sc->sc_npins)
2696ad7f491SLuiz Otavio O Souza continue;
270d752f0f6SLuiz Otavio O Souza sc->sc_pins[devi->pins[i]].mapped = 0;
2716ad7f491SLuiz Otavio O Souza }
272e8da3e8aSLuiz Otavio O Souza gpiobus_free_ivars(devi);
273138bf909SLuiz Otavio O Souza resource_list_free(&dinfo->opd_dinfo.rl);
2746d866ed3SLuiz Otavio O Souza ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
2756d866ed3SLuiz Otavio O Souza free(dinfo, M_DEVBUF);
2766d866ed3SLuiz Otavio O Souza }
2776d866ed3SLuiz Otavio O Souza
2786d866ed3SLuiz Otavio O Souza static int
ofw_gpiobus_parse_gpios_impl(device_t consumer,phandle_t cnode,char * pname,struct gpiobus_softc * bussc,struct gpiobus_pin ** pins)279e8da3e8aSLuiz Otavio O Souza ofw_gpiobus_parse_gpios_impl(device_t consumer, phandle_t cnode, char *pname,
280e8da3e8aSLuiz Otavio O Souza struct gpiobus_softc *bussc, struct gpiobus_pin **pins)
281e8da3e8aSLuiz Otavio O Souza {
282e8da3e8aSLuiz Otavio O Souza int gpiocells, i, j, ncells, npins;
283e8da3e8aSLuiz Otavio O Souza pcell_t *gpios;
284e8da3e8aSLuiz Otavio O Souza phandle_t gpio;
285e8da3e8aSLuiz Otavio O Souza
286f7604b1bSOleksandr Tymoshenko ncells = OF_getencprop_alloc_multi(cnode, pname, sizeof(*gpios),
287e8da3e8aSLuiz Otavio O Souza (void **)&gpios);
288e8da3e8aSLuiz Otavio O Souza if (ncells == -1) {
289e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
290e8da3e8aSLuiz Otavio O Souza "Warning: No %s specified in fdt data; "
291e8da3e8aSLuiz Otavio O Souza "device may not function.\n", pname);
292e8da3e8aSLuiz Otavio O Souza return (-1);
293e8da3e8aSLuiz Otavio O Souza }
294e8da3e8aSLuiz Otavio O Souza /*
295e8da3e8aSLuiz Otavio O Souza * The gpio-specifier is controller independent, the first pcell has
296e8da3e8aSLuiz Otavio O Souza * the reference to the GPIO controller phandler.
297e8da3e8aSLuiz Otavio O Souza * Count the number of encoded gpio-specifiers on the first pass.
298e8da3e8aSLuiz Otavio O Souza */
299e8da3e8aSLuiz Otavio O Souza i = 0;
300e8da3e8aSLuiz Otavio O Souza npins = 0;
301e8da3e8aSLuiz Otavio O Souza while (i < ncells) {
302e8da3e8aSLuiz Otavio O Souza /* Allow NULL specifiers. */
303e8da3e8aSLuiz Otavio O Souza if (gpios[i] == 0) {
304e8da3e8aSLuiz Otavio O Souza npins++;
305e8da3e8aSLuiz Otavio O Souza i++;
306e8da3e8aSLuiz Otavio O Souza continue;
307e8da3e8aSLuiz Otavio O Souza }
308e8da3e8aSLuiz Otavio O Souza gpio = OF_node_from_xref(gpios[i]);
309e8da3e8aSLuiz Otavio O Souza /* If we have bussc, ignore devices from other gpios. */
310e8da3e8aSLuiz Otavio O Souza if (bussc != NULL)
311e8da3e8aSLuiz Otavio O Souza if (ofw_bus_get_node(bussc->sc_dev) != gpio)
312e8da3e8aSLuiz Otavio O Souza return (0);
313e8da3e8aSLuiz Otavio O Souza /*
314e8da3e8aSLuiz Otavio O Souza * Check for gpio-controller property and read the #gpio-cells
315e8da3e8aSLuiz Otavio O Souza * for this GPIO controller.
316e8da3e8aSLuiz Otavio O Souza */
317e8da3e8aSLuiz Otavio O Souza if (!OF_hasprop(gpio, "gpio-controller") ||
318e8da3e8aSLuiz Otavio O Souza OF_getencprop(gpio, "#gpio-cells", &gpiocells,
319e8da3e8aSLuiz Otavio O Souza sizeof(gpiocells)) < 0) {
320e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
321e8da3e8aSLuiz Otavio O Souza "gpio reference is not a gpio-controller.\n");
322bc90a48cSOleksandr Tymoshenko OF_prop_free(gpios);
323e8da3e8aSLuiz Otavio O Souza return (-1);
324e8da3e8aSLuiz Otavio O Souza }
325e8da3e8aSLuiz Otavio O Souza if (ncells - i < gpiocells + 1) {
326e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
327e8da3e8aSLuiz Otavio O Souza "%s cells doesn't match #gpio-cells.\n", pname);
328e8da3e8aSLuiz Otavio O Souza return (-1);
329e8da3e8aSLuiz Otavio O Souza }
330e8da3e8aSLuiz Otavio O Souza npins++;
331e8da3e8aSLuiz Otavio O Souza i += gpiocells + 1;
332e8da3e8aSLuiz Otavio O Souza }
333e8da3e8aSLuiz Otavio O Souza if (npins == 0 || pins == NULL) {
334e8da3e8aSLuiz Otavio O Souza if (npins == 0)
335e8da3e8aSLuiz Otavio O Souza device_printf(consumer, "no pin specified in %s.\n",
336e8da3e8aSLuiz Otavio O Souza pname);
337bc90a48cSOleksandr Tymoshenko OF_prop_free(gpios);
338e8da3e8aSLuiz Otavio O Souza return (npins);
339e8da3e8aSLuiz Otavio O Souza }
340e8da3e8aSLuiz Otavio O Souza *pins = malloc(sizeof(struct gpiobus_pin) * npins, M_DEVBUF,
341e8da3e8aSLuiz Otavio O Souza M_NOWAIT | M_ZERO);
342e8da3e8aSLuiz Otavio O Souza if (*pins == NULL) {
343bc90a48cSOleksandr Tymoshenko OF_prop_free(gpios);
344e8da3e8aSLuiz Otavio O Souza return (-1);
345e8da3e8aSLuiz Otavio O Souza }
346e8da3e8aSLuiz Otavio O Souza /* Decode the gpio specifier on the second pass. */
347e8da3e8aSLuiz Otavio O Souza i = 0;
348e8da3e8aSLuiz Otavio O Souza j = 0;
349e8da3e8aSLuiz Otavio O Souza while (i < ncells) {
350e8da3e8aSLuiz Otavio O Souza /* Allow NULL specifiers. */
351e8da3e8aSLuiz Otavio O Souza if (gpios[i] == 0) {
352e8da3e8aSLuiz Otavio O Souza j++;
353e8da3e8aSLuiz Otavio O Souza i++;
354e8da3e8aSLuiz Otavio O Souza continue;
355e8da3e8aSLuiz Otavio O Souza }
356e8da3e8aSLuiz Otavio O Souza gpio = OF_node_from_xref(gpios[i]);
357e8da3e8aSLuiz Otavio O Souza /* Read gpio-cells property for this GPIO controller. */
358e8da3e8aSLuiz Otavio O Souza if (OF_getencprop(gpio, "#gpio-cells", &gpiocells,
359e8da3e8aSLuiz Otavio O Souza sizeof(gpiocells)) < 0) {
360e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
361e8da3e8aSLuiz Otavio O Souza "gpio does not have the #gpio-cells property.\n");
362e8da3e8aSLuiz Otavio O Souza goto fail;
363e8da3e8aSLuiz Otavio O Souza }
364e8da3e8aSLuiz Otavio O Souza /* Return the device reference for the GPIO controller. */
365e8da3e8aSLuiz Otavio O Souza (*pins)[j].dev = OF_device_from_xref(gpios[i]);
366e8da3e8aSLuiz Otavio O Souza if ((*pins)[j].dev == NULL) {
367e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
368e8da3e8aSLuiz Otavio O Souza "no device registered for the gpio controller.\n");
369e8da3e8aSLuiz Otavio O Souza goto fail;
370e8da3e8aSLuiz Otavio O Souza }
371e8da3e8aSLuiz Otavio O Souza /*
372e8da3e8aSLuiz Otavio O Souza * If the gpiobus softc is NULL we use the GPIO_GET_BUS() to
373e8da3e8aSLuiz Otavio O Souza * retrieve it. The GPIO_GET_BUS() method is only valid after
374e8da3e8aSLuiz Otavio O Souza * the child is probed and attached.
375e8da3e8aSLuiz Otavio O Souza */
376e8da3e8aSLuiz Otavio O Souza if (bussc == NULL) {
377e8da3e8aSLuiz Otavio O Souza if (GPIO_GET_BUS((*pins)[j].dev) == NULL) {
378e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
379e8da3e8aSLuiz Otavio O Souza "no gpiobus reference for %s.\n",
380e8da3e8aSLuiz Otavio O Souza device_get_nameunit((*pins)[j].dev));
381e8da3e8aSLuiz Otavio O Souza goto fail;
382e8da3e8aSLuiz Otavio O Souza }
383e8da3e8aSLuiz Otavio O Souza bussc = device_get_softc(GPIO_GET_BUS((*pins)[j].dev));
384e8da3e8aSLuiz Otavio O Souza }
385e8da3e8aSLuiz Otavio O Souza /* Get the GPIO pin number and flags. */
386e8da3e8aSLuiz Otavio O Souza if (gpio_map_gpios((*pins)[j].dev, cnode, gpio, gpiocells,
387e8da3e8aSLuiz Otavio O Souza &gpios[i + 1], &(*pins)[j].pin, &(*pins)[j].flags) != 0) {
388e8da3e8aSLuiz Otavio O Souza device_printf(consumer,
389e8da3e8aSLuiz Otavio O Souza "cannot map the gpios specifier.\n");
390e8da3e8aSLuiz Otavio O Souza goto fail;
391e8da3e8aSLuiz Otavio O Souza }
392da3b488dSLuiz Otavio O Souza /* Reserve the GPIO pin. */
393c45b8422SIan Lepore if (gpiobus_acquire_pin(bussc->sc_busdev, (*pins)[j].pin) != 0)
394e8da3e8aSLuiz Otavio O Souza goto fail;
395e8da3e8aSLuiz Otavio O Souza j++;
396e8da3e8aSLuiz Otavio O Souza i += gpiocells + 1;
397e8da3e8aSLuiz Otavio O Souza }
398bc90a48cSOleksandr Tymoshenko OF_prop_free(gpios);
399e8da3e8aSLuiz Otavio O Souza
400e8da3e8aSLuiz Otavio O Souza return (npins);
401e8da3e8aSLuiz Otavio O Souza
402e8da3e8aSLuiz Otavio O Souza fail:
403bc90a48cSOleksandr Tymoshenko OF_prop_free(gpios);
404e8da3e8aSLuiz Otavio O Souza free(*pins, M_DEVBUF);
405e8da3e8aSLuiz Otavio O Souza return (-1);
406e8da3e8aSLuiz Otavio O Souza }
407e8da3e8aSLuiz Otavio O Souza
408e8da3e8aSLuiz Otavio O Souza static int
ofw_gpiobus_probe(device_t dev)4096d866ed3SLuiz Otavio O Souza ofw_gpiobus_probe(device_t dev)
4106d866ed3SLuiz Otavio O Souza {
4116d866ed3SLuiz Otavio O Souza
4126d866ed3SLuiz Otavio O Souza if (ofw_bus_get_node(dev) == -1)
4136d866ed3SLuiz Otavio O Souza return (ENXIO);
4146d866ed3SLuiz Otavio O Souza device_set_desc(dev, "OFW GPIO bus");
4156d866ed3SLuiz Otavio O Souza
416846419f8SIan Lepore return (BUS_PROBE_DEFAULT);
4176d866ed3SLuiz Otavio O Souza }
4186d866ed3SLuiz Otavio O Souza
4196d866ed3SLuiz Otavio O Souza static int
ofw_gpiobus_attach(device_t dev)4206d866ed3SLuiz Otavio O Souza ofw_gpiobus_attach(device_t dev)
4216d866ed3SLuiz Otavio O Souza {
42288516632SLuiz Otavio O Souza int err;
4236d866ed3SLuiz Otavio O Souza phandle_t child;
4246d866ed3SLuiz Otavio O Souza
42588516632SLuiz Otavio O Souza err = gpiobus_init_softc(dev);
42688516632SLuiz Otavio O Souza if (err != 0)
42788516632SLuiz Otavio O Souza return (err);
4286d866ed3SLuiz Otavio O Souza bus_generic_probe(dev);
4296d866ed3SLuiz Otavio O Souza bus_enumerate_hinted_children(dev);
4306d866ed3SLuiz Otavio O Souza /*
4316d866ed3SLuiz Otavio O Souza * Attach the children represented in the device tree.
4326d866ed3SLuiz Otavio O Souza */
4336d866ed3SLuiz Otavio O Souza for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
434e8da3e8aSLuiz Otavio O Souza child = OF_peer(child)) {
435846419f8SIan Lepore if (OF_hasprop(child, "gpio-hog"))
436846419f8SIan Lepore continue;
437e8da3e8aSLuiz Otavio O Souza if (!OF_hasprop(child, "gpios"))
4386d866ed3SLuiz Otavio O Souza continue;
439e8da3e8aSLuiz Otavio O Souza if (ofw_gpiobus_add_fdt_child(dev, NULL, child) == NULL)
440e8da3e8aSLuiz Otavio O Souza continue;
441e8da3e8aSLuiz Otavio O Souza }
4426d866ed3SLuiz Otavio O Souza
4436d866ed3SLuiz Otavio O Souza return (bus_generic_attach(dev));
4446d866ed3SLuiz Otavio O Souza }
4456d866ed3SLuiz Otavio O Souza
4466d866ed3SLuiz Otavio O Souza static device_t
ofw_gpiobus_add_child(device_t dev,u_int order,const char * name,int unit)4476d866ed3SLuiz Otavio O Souza ofw_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
4486d866ed3SLuiz Otavio O Souza {
4496d866ed3SLuiz Otavio O Souza device_t child;
4506d866ed3SLuiz Otavio O Souza struct ofw_gpiobus_devinfo *devi;
4516d866ed3SLuiz Otavio O Souza
4526d866ed3SLuiz Otavio O Souza child = device_add_child_ordered(dev, order, name, unit);
4536d866ed3SLuiz Otavio O Souza if (child == NULL)
4546d866ed3SLuiz Otavio O Souza return (child);
4556d866ed3SLuiz Otavio O Souza devi = malloc(sizeof(struct ofw_gpiobus_devinfo), M_DEVBUF,
4566d866ed3SLuiz Otavio O Souza M_NOWAIT | M_ZERO);
4576d866ed3SLuiz Otavio O Souza if (devi == NULL) {
4586d866ed3SLuiz Otavio O Souza device_delete_child(dev, child);
4596d866ed3SLuiz Otavio O Souza return (0);
4606d866ed3SLuiz Otavio O Souza }
4616d866ed3SLuiz Otavio O Souza
4626d866ed3SLuiz Otavio O Souza /*
4636d866ed3SLuiz Otavio O Souza * NULL all the OFW-related parts of the ivars for non-OFW
4646d866ed3SLuiz Otavio O Souza * children.
4656d866ed3SLuiz Otavio O Souza */
4666d866ed3SLuiz Otavio O Souza devi->opd_obdinfo.obd_node = -1;
4676d866ed3SLuiz Otavio O Souza devi->opd_obdinfo.obd_name = NULL;
4686d866ed3SLuiz Otavio O Souza devi->opd_obdinfo.obd_compat = NULL;
4696d866ed3SLuiz Otavio O Souza devi->opd_obdinfo.obd_type = NULL;
4706d866ed3SLuiz Otavio O Souza devi->opd_obdinfo.obd_model = NULL;
4716d866ed3SLuiz Otavio O Souza
4726d866ed3SLuiz Otavio O Souza device_set_ivars(child, devi);
4736d866ed3SLuiz Otavio O Souza
4746d866ed3SLuiz Otavio O Souza return (child);
4756d866ed3SLuiz Otavio O Souza }
4766d866ed3SLuiz Otavio O Souza
4776d866ed3SLuiz Otavio O Souza static const struct ofw_bus_devinfo *
ofw_gpiobus_get_devinfo(device_t bus,device_t dev)4786d866ed3SLuiz Otavio O Souza ofw_gpiobus_get_devinfo(device_t bus, device_t dev)
4796d866ed3SLuiz Otavio O Souza {
4806d866ed3SLuiz Otavio O Souza struct ofw_gpiobus_devinfo *dinfo;
4816d866ed3SLuiz Otavio O Souza
4826d866ed3SLuiz Otavio O Souza dinfo = device_get_ivars(dev);
4836d866ed3SLuiz Otavio O Souza
4846d866ed3SLuiz Otavio O Souza return (&dinfo->opd_obdinfo);
4856d866ed3SLuiz Otavio O Souza }
4866d866ed3SLuiz Otavio O Souza
4876d866ed3SLuiz Otavio O Souza static device_method_t ofw_gpiobus_methods[] = {
4886d866ed3SLuiz Otavio O Souza /* Device interface */
4896d866ed3SLuiz Otavio O Souza DEVMETHOD(device_probe, ofw_gpiobus_probe),
4906d866ed3SLuiz Otavio O Souza DEVMETHOD(device_attach, ofw_gpiobus_attach),
4916d866ed3SLuiz Otavio O Souza
4926d866ed3SLuiz Otavio O Souza /* Bus interface */
493ddfc9c4cSWarner Losh DEVMETHOD(bus_child_pnpinfo, ofw_bus_gen_child_pnpinfo),
4946d866ed3SLuiz Otavio O Souza DEVMETHOD(bus_add_child, ofw_gpiobus_add_child),
4956d866ed3SLuiz Otavio O Souza
4966d866ed3SLuiz Otavio O Souza /* ofw_bus interface */
4976d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_devinfo, ofw_gpiobus_get_devinfo),
4986d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
4996d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
5006d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
5016d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
5026d866ed3SLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
5036d866ed3SLuiz Otavio O Souza
5046d866ed3SLuiz Otavio O Souza DEVMETHOD_END
5056d866ed3SLuiz Otavio O Souza };
5066d866ed3SLuiz Otavio O Souza
5076d866ed3SLuiz Otavio O Souza DEFINE_CLASS_1(gpiobus, ofw_gpiobus_driver, ofw_gpiobus_methods,
5086d866ed3SLuiz Otavio O Souza sizeof(struct gpiobus_softc), gpiobus_driver);
509e8590c97SJohn Baldwin EARLY_DRIVER_MODULE(ofw_gpiobus, gpio, ofw_gpiobus_driver, 0, 0, BUS_PASS_BUS);
5106d866ed3SLuiz Otavio O Souza MODULE_VERSION(ofw_gpiobus, 1);
5116d866ed3SLuiz Otavio O Souza MODULE_DEPEND(ofw_gpiobus, gpiobus, 1, 1, 1);
512