2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2018-2019, Rubicon Communications, LLC (Netgate)
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
41 #include <machine/bus.h>
42 #include <machine/resource.h>
44 #include <dev/gpio/gpiobusvar.h>
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
50 static struct resource_spec a37x0_gpio_res_spec[] = {
51 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Pinctl / GPIO */
52 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* Interrupts control */
56 struct a37x0_gpio_softc {
57 bus_space_tag_t sc_bst;
58 bus_space_handle_t sc_bsh;
63 struct resource *sc_mem_res[nitems(a37x0_gpio_res_spec) - 1];
70 /* North Bridge / South Bridge. */
71 #define A37X0_NB_GPIO 1
72 #define A37X0_SB_GPIO 2
74 #define A37X0_GPIO_WRITE(_sc, _off, _val) \
75 bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
76 #define A37X0_GPIO_READ(_sc, _off) \
77 bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
79 #define A37X0_GPIO_BIT(_p) (1U << ((_p) % 32))
80 #define A37X0_GPIO_OUT_EN(_p) (0x0 + ((_p) / 32) * 4)
81 #define A37X0_GPIO_LATCH(_p) (0x8 + ((_p) / 32) * 4)
82 #define A37X0_GPIO_INPUT(_p) (0x10 + ((_p) / 32) * 4)
83 #define A37X0_GPIO_OUTPUT(_p) (0x18 + ((_p) / 32) * 4)
84 #define A37X0_GPIO_SEL 0x30
87 static struct ofw_compat_data compat_data[] = {
88 { "marvell,armada3710-nb-pinctrl", A37X0_NB_GPIO },
89 { "marvell,armada3710-sb-pinctrl", A37X0_SB_GPIO },
94 a37x0_gpio_get_node(device_t bus, device_t dev)
97 return (ofw_bus_get_node(bus));
101 a37x0_gpio_get_bus(device_t dev)
103 struct a37x0_gpio_softc *sc;
105 sc = device_get_softc(dev);
107 return (sc->sc_busdev);
111 a37x0_gpio_pin_max(device_t dev, int *maxpin)
113 struct a37x0_gpio_softc *sc;
115 sc = device_get_softc(dev);
116 *maxpin = sc->sc_npins - 1;
122 a37x0_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
124 struct a37x0_gpio_softc *sc;
126 sc = device_get_softc(dev);
127 if (pin >= sc->sc_npins)
129 snprintf(name, GPIOMAXNAME, "pin %d", pin);
135 a37x0_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
137 struct a37x0_gpio_softc *sc;
139 sc = device_get_softc(dev);
140 if (pin >= sc->sc_npins)
142 *caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
148 a37x0_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
150 struct a37x0_gpio_softc *sc;
153 sc = device_get_softc(dev);
154 if (pin >= sc->sc_npins)
156 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
157 if ((reg & A37X0_GPIO_BIT(pin)) != 0)
158 *flags = GPIO_PIN_OUTPUT;
160 *flags = GPIO_PIN_INPUT;
166 a37x0_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
168 struct a37x0_gpio_softc *sc;
171 sc = device_get_softc(dev);
172 if (pin >= sc->sc_npins)
175 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
176 if (flags & GPIO_PIN_OUTPUT)
177 reg |= A37X0_GPIO_BIT(pin);
179 reg &= ~A37X0_GPIO_BIT(pin);
180 A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUT_EN(pin), reg);
186 a37x0_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
188 struct a37x0_gpio_softc *sc;
191 sc = device_get_softc(dev);
192 if (pin >= sc->sc_npins)
195 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
196 if ((reg & A37X0_GPIO_BIT(pin)) != 0)
197 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
199 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_INPUT(pin));
200 *val = ((reg & A37X0_GPIO_BIT(pin)) != 0) ? 1 : 0;
206 a37x0_gpio_pin_set(device_t dev, uint32_t pin, unsigned int val)
208 struct a37x0_gpio_softc *sc;
211 sc = device_get_softc(dev);
212 if (pin >= sc->sc_npins)
215 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
217 reg |= A37X0_GPIO_BIT(pin);
219 reg &= ~A37X0_GPIO_BIT(pin);
220 A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
226 a37x0_gpio_pin_toggle(device_t dev, uint32_t pin)
228 struct a37x0_gpio_softc *sc;
231 sc = device_get_softc(dev);
232 if (pin >= sc->sc_npins)
235 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUT_EN(pin));
236 if ((reg & A37X0_GPIO_BIT(pin)) == 0)
238 reg = A37X0_GPIO_READ(sc, A37X0_GPIO_OUTPUT(pin));
239 reg ^= A37X0_GPIO_BIT(pin);
240 A37X0_GPIO_WRITE(sc, A37X0_GPIO_OUTPUT(pin), reg);
246 a37x0_gpio_probe(device_t dev)
249 struct a37x0_gpio_softc *sc;
251 if (!OF_hasprop(ofw_bus_get_node(dev), "gpio-controller"))
254 sc = device_get_softc(dev);
255 sc->sc_type = ofw_bus_search_compatible(
256 device_get_parent(dev), compat_data)->ocd_data;
257 switch (sc->sc_type) {
259 sc->sc_max_pins = 36;
260 desc = "Armada 37x0 North Bridge GPIO Controller";
263 sc->sc_max_pins = 30;
264 desc = "Armada 37x0 South Bridge GPIO Controller";
269 device_set_desc(dev, desc);
271 return (BUS_PROBE_DEFAULT);
275 a37x0_gpio_attach(device_t dev)
279 struct a37x0_gpio_softc *sc;
281 sc = device_get_softc(dev);
283 /* Read and verify the "gpio-ranges" property. */
284 ncells = OF_getencprop_alloc(ofw_bus_get_node(dev), "gpio-ranges",
288 if (ncells != sizeof(*ranges) * 4 || ranges[1] != 0 || ranges[2] != 0) {
289 OF_prop_free(ranges);
292 sc->sc_npins = ranges[3];
293 OF_prop_free(ranges);
295 /* Check the number of pins in the DTS vs HW capabilities. */
296 if (sc->sc_npins > sc->sc_max_pins)
299 err = bus_alloc_resources(dev, a37x0_gpio_res_spec, sc->sc_mem_res);
301 device_printf(dev, "cannot allocate memory window\n");
304 sc->sc_bst = rman_get_bustag(sc->sc_mem_res[A37X0_GPIO]);
305 sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res[A37X0_GPIO]);
307 sc->sc_busdev = gpiobus_attach_bus(dev);
308 if (sc->sc_busdev == NULL)
315 a37x0_gpio_detach(device_t dev)
321 static device_method_t a37x0_gpio_methods[] = {
322 /* Device interface */
323 DEVMETHOD(device_probe, a37x0_gpio_probe),
324 DEVMETHOD(device_attach, a37x0_gpio_attach),
325 DEVMETHOD(device_detach, a37x0_gpio_detach),
328 DEVMETHOD(gpio_get_bus, a37x0_gpio_get_bus),
329 DEVMETHOD(gpio_pin_max, a37x0_gpio_pin_max),
330 DEVMETHOD(gpio_pin_getname, a37x0_gpio_pin_getname),
331 DEVMETHOD(gpio_pin_getcaps, a37x0_gpio_pin_getcaps),
332 DEVMETHOD(gpio_pin_getflags, a37x0_gpio_pin_getflags),
333 DEVMETHOD(gpio_pin_setflags, a37x0_gpio_pin_setflags),
334 DEVMETHOD(gpio_pin_get, a37x0_gpio_pin_get),
335 DEVMETHOD(gpio_pin_set, a37x0_gpio_pin_set),
336 DEVMETHOD(gpio_pin_toggle, a37x0_gpio_pin_toggle),
338 /* ofw_bus interface */
339 DEVMETHOD(ofw_bus_get_node, a37x0_gpio_get_node),
344 static devclass_t a37x0_gpio_devclass;
345 static driver_t a37x0_gpio_driver = {
348 sizeof(struct a37x0_gpio_softc),
351 EARLY_DRIVER_MODULE(a37x0_gpio, simple_mfd, a37x0_gpio_driver,
352 a37x0_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);