1*6e54367aSthorpej /* $NetBSD: imx_gpio.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $ */
28644267aSskrll /*-
38644267aSskrll * Copyright (c) 2019 Genetec Corporation. All rights reserved.
48644267aSskrll * Written by Hashimoto Kenichi for Genetec Corporation.
58644267aSskrll *
68644267aSskrll * Redistribution and use in source and binary forms, with or without
78644267aSskrll * modification, are permitted provided that the following conditions
88644267aSskrll * are met:
98644267aSskrll * 1. Redistributions of source code must retain the above copyright
108644267aSskrll * notice, this list of conditions and the following disclaimer.
118644267aSskrll * 2. Redistributions in binary form must reproduce the above copyright
128644267aSskrll * notice, this list of conditions and the following disclaimer in the
138644267aSskrll * documentation and/or other materials provided with the distribution.
148644267aSskrll *
158644267aSskrll * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168644267aSskrll * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178644267aSskrll * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188644267aSskrll * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198644267aSskrll * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
208644267aSskrll * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
218644267aSskrll * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
228644267aSskrll * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
238644267aSskrll * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248644267aSskrll * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258644267aSskrll * SUCH DAMAGE.
268644267aSskrll */
278644267aSskrll
288644267aSskrll #include <sys/cdefs.h>
29*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: imx_gpio.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $");
308644267aSskrll
318644267aSskrll #include "opt_fdt.h"
328644267aSskrll #include "gpio.h"
338644267aSskrll
348644267aSskrll #define _INTR_PRIVATE
358644267aSskrll
368644267aSskrll #include <sys/param.h>
378644267aSskrll #include <sys/bus.h>
388644267aSskrll #include <sys/device.h>
398644267aSskrll #include <sys/intr.h>
408644267aSskrll #include <sys/systm.h>
418644267aSskrll #include <sys/kernel.h>
428644267aSskrll #include <sys/kmem.h>
438644267aSskrll #include <sys/gpio.h>
448644267aSskrll
458644267aSskrll #include <dev/gpio/gpiovar.h>
468644267aSskrll
478644267aSskrll #include <arm/pic/picvar.h>
488644267aSskrll #include <arm/nxp/imx6_reg.h>
498644267aSskrll
508644267aSskrll #include <arm/imx/imxgpioreg.h>
518644267aSskrll #include <arm/imx/imxgpiovar.h>
528644267aSskrll
538644267aSskrll #include <dev/fdt/fdtvar.h>
548644267aSskrll
558644267aSskrll static void *imx6_gpio_fdt_acquire(device_t, const void *, size_t, int);
568644267aSskrll static void imx6_gpio_fdt_release(device_t, void *);
578644267aSskrll static int imx6_gpio_fdt_read(device_t, void *, bool);
588644267aSskrll static void imx6_gpio_fdt_write(device_t, void *, int, bool);
598644267aSskrll
608644267aSskrll static void *imxgpio_establish(device_t, u_int *, int, int,
6159ad346dSjmcneill int (*)(void *), void *, const char *);
628644267aSskrll static void imxgpio_disestablish(device_t, void *);
638644267aSskrll static bool imxgpio_intrstr(device_t, u_int *, char *, size_t);
648644267aSskrll
658644267aSskrll static struct fdtbus_interrupt_controller_func imxgpio_funcs = {
668644267aSskrll .establish = imxgpio_establish,
678644267aSskrll .disestablish = imxgpio_disestablish,
688644267aSskrll .intrstr = imxgpio_intrstr
698644267aSskrll };
708644267aSskrll
718644267aSskrll static struct fdtbus_gpio_controller_func imx6_gpio_funcs = {
728644267aSskrll .acquire = imx6_gpio_fdt_acquire,
738644267aSskrll .release = imx6_gpio_fdt_release,
748644267aSskrll .read = imx6_gpio_fdt_read,
758644267aSskrll .write = imx6_gpio_fdt_write
768644267aSskrll };
778644267aSskrll
788644267aSskrll const int imxgpio_ngroups = GPIO_NGROUPS;
798644267aSskrll
80*6e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
81*6e54367aSthorpej { .compat = "fsl,imx35-gpio" },
82*6e54367aSthorpej DEVICE_COMPAT_EOL
83*6e54367aSthorpej };
84*6e54367aSthorpej
858644267aSskrll int
imxgpio_match(device_t parent,cfdata_t cf,void * aux)868644267aSskrll imxgpio_match(device_t parent, cfdata_t cf, void *aux)
878644267aSskrll {
888644267aSskrll struct fdt_attach_args * const faa = aux;
898644267aSskrll
90*6e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
918644267aSskrll }
928644267aSskrll
938644267aSskrll void
imxgpio_attach(device_t parent,device_t self,void * aux)948644267aSskrll imxgpio_attach(device_t parent, device_t self, void *aux)
958644267aSskrll {
968644267aSskrll struct imxgpio_softc * const sc = device_private(self);
978644267aSskrll struct fdt_attach_args * const faa = aux;
988644267aSskrll char intrstr[128];
998644267aSskrll const int phandle = faa->faa_phandle;
1008644267aSskrll bus_space_handle_t ioh;
1018644267aSskrll bus_addr_t addr;
1028644267aSskrll bus_size_t size;
1038644267aSskrll int error;
1048644267aSskrll
1058644267aSskrll if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1068644267aSskrll aprint_error(": couldn't get registers\n");
1078644267aSskrll return;
1088644267aSskrll }
1098644267aSskrll
1108644267aSskrll error = bus_space_map(faa->faa_bst, addr, size, 0, &ioh);
1118644267aSskrll if (error) {
1128644267aSskrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
1138644267aSskrll return;
1148644267aSskrll }
1158644267aSskrll
1168644267aSskrll aprint_naive("\n");
1178644267aSskrll aprint_normal(": GPIO (%s)\n", fdtbus_get_string(phandle, "name"));
1188644267aSskrll
1198644267aSskrll sc->gpio_memt = faa->faa_bst;
1208644267aSskrll sc->gpio_memh = ioh;
1218644267aSskrll sc->gpio_unit = -1;
1228644267aSskrll sc->gpio_irqbase = PIC_IRQBASE_ALLOC;
1238644267aSskrll
1248644267aSskrll if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1258644267aSskrll aprint_error_dev(self, "failed to decode interrupt\n");
1268644267aSskrll return;
1278644267aSskrll }
12882b8374aSjmcneill sc->gpio_is = fdtbus_intr_establish_xname(phandle, 0, IPL_HIGH, 0,
12982b8374aSjmcneill pic_handle_intr, &sc->gpio_pic, device_xname(self));
1308644267aSskrll if (sc->gpio_is == NULL) {
1318644267aSskrll aprint_error_dev(self, "couldn't establish interrupt on %s\n",
1328644267aSskrll intrstr);
1338644267aSskrll return;
1348644267aSskrll }
1358644267aSskrll aprint_normal_dev(self, "interrupting on %s\n", intrstr);
1368644267aSskrll
1378644267aSskrll if (!fdtbus_intr_str(phandle, 1, intrstr, sizeof(intrstr))) {
1388644267aSskrll aprint_error_dev(self, "failed to decode interrupt\n");
1398644267aSskrll return;
1408644267aSskrll }
14182b8374aSjmcneill sc->gpio_is_high = fdtbus_intr_establish_xname(phandle, 1, IPL_HIGH, 0,
14282b8374aSjmcneill pic_handle_intr, &sc->gpio_pic, device_xname(self));
1438644267aSskrll if (sc->gpio_is_high == NULL) {
1448644267aSskrll aprint_error_dev(self, "couldn't establish interrupt on %s\n",
1458644267aSskrll intrstr);
1468644267aSskrll return;
1478644267aSskrll }
1488644267aSskrll aprint_normal_dev(self, "interrupting on %s\n", intrstr);
1498644267aSskrll
1508644267aSskrll fdtbus_register_gpio_controller(self, phandle, &imx6_gpio_funcs);
1518644267aSskrll
1528644267aSskrll error = fdtbus_register_interrupt_controller(self, phandle,
1538644267aSskrll &imxgpio_funcs);
1548644267aSskrll if (error) {
1558644267aSskrll aprint_error(": couldn't register with fdtbus: %d\n", error);
1568644267aSskrll return;
1578644267aSskrll }
1588644267aSskrll
1598644267aSskrll imxgpio_attach_common(self);
1608644267aSskrll }
1618644267aSskrll
1628644267aSskrll static void *
imx6_gpio_fdt_acquire(device_t dev,const void * data,size_t len,int flags)1638644267aSskrll imx6_gpio_fdt_acquire(device_t dev, const void *data, size_t len, int flags)
1648644267aSskrll {
1658644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
1668644267aSskrll struct imxgpio_pin *gpin;
1678644267aSskrll const u_int *gpio = data;
1688644267aSskrll
1698644267aSskrll if (len != 12)
1708644267aSskrll return NULL;
1718644267aSskrll
1728644267aSskrll const u_int pin = be32toh(gpio[1]);
1738644267aSskrll const bool actlo = be32toh(gpio[2]) & 1;
1748644267aSskrll
1758644267aSskrll gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
1768644267aSskrll gpin->pin_no = pin;
1778644267aSskrll gpin->pin_flags = flags;
1788644267aSskrll gpin->pin_actlo = actlo;
1798644267aSskrll
1808644267aSskrll imxgpio_pin_ctl(sc, gpin->pin_no, gpin->pin_flags);
1818644267aSskrll
1828644267aSskrll return gpin;
1838644267aSskrll }
1848644267aSskrll
1858644267aSskrll static void
imx6_gpio_fdt_release(device_t dev,void * priv)1868644267aSskrll imx6_gpio_fdt_release(device_t dev, void *priv)
1878644267aSskrll {
1888644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
1898644267aSskrll struct imxgpio_pin *gpin = priv;
1908644267aSskrll
1918644267aSskrll imxgpio_pin_ctl(sc, gpin->pin_no, GPIO_PIN_INPUT);
1928644267aSskrll kmem_free(gpin, sizeof(*gpin));
1938644267aSskrll }
1948644267aSskrll
1958644267aSskrll static int
imx6_gpio_fdt_read(device_t dev,void * priv,bool raw)1968644267aSskrll imx6_gpio_fdt_read(device_t dev, void *priv, bool raw)
1978644267aSskrll {
1988644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
1998644267aSskrll struct imxgpio_pin *gpin = priv;
2008644267aSskrll int val;
2018644267aSskrll
2028644267aSskrll val = imxgpio_pin_read(sc, gpin->pin_no);
2038644267aSskrll
2048644267aSskrll if (!raw && gpin->pin_actlo)
2058644267aSskrll val = !val;
2068644267aSskrll
2078644267aSskrll return val;
2088644267aSskrll }
2098644267aSskrll
2108644267aSskrll static void
imx6_gpio_fdt_write(device_t dev,void * priv,int val,bool raw)2118644267aSskrll imx6_gpio_fdt_write(device_t dev, void *priv, int val, bool raw)
2128644267aSskrll {
2138644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
2148644267aSskrll struct imxgpio_pin *gpin = priv;
2158644267aSskrll
2168644267aSskrll if (!raw && gpin->pin_actlo)
2178644267aSskrll val = !val;
2188644267aSskrll
2198644267aSskrll imxgpio_pin_write(sc, gpin->pin_no, val);
2208644267aSskrll }
2218644267aSskrll
2228644267aSskrll static void *
imxgpio_establish(device_t dev,u_int * specifier,int ipl,int flags,int (* func)(void *),void * arg,const char * xname)2238644267aSskrll imxgpio_establish(device_t dev, u_int *specifier, int ipl, int flags,
22459ad346dSjmcneill int (*func)(void *), void *arg, const char *xname)
2258644267aSskrll {
2268644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
2278644267aSskrll
2288644267aSskrll /* 1st cell is the interrupt number */
2298644267aSskrll /* 2nd cell is flags */
2308644267aSskrll
2318644267aSskrll const u_int intr = be32toh(specifier[0]);
2328644267aSskrll const u_int trig = be32toh(specifier[1]) & 0xf;
2338644267aSskrll u_int level;
2348644267aSskrll
2358644267aSskrll if ((trig & 0x1) && (trig & 0x2))
2368644267aSskrll level = IST_EDGE_BOTH;
2378644267aSskrll else if (trig & 0x1)
2388644267aSskrll level = IST_EDGE_RISING;
2398644267aSskrll else if (trig & 0x2)
2408644267aSskrll level = IST_EDGE_FALLING;
2418644267aSskrll else if (trig & 0x4)
2428644267aSskrll level = IST_LEVEL_HIGH;
2438644267aSskrll else
2448644267aSskrll level = IST_LEVEL_LOW;
2458644267aSskrll
2468644267aSskrll const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
2478644267aSskrll
2488644267aSskrll aprint_debug_dev(dev, "intr establish irq %d, level %d\n",
2498644267aSskrll sc->gpio_irqbase + intr, level);
25059ad346dSjmcneill return intr_establish_xname(sc->gpio_irqbase + intr, ipl,
25159ad346dSjmcneill level | mpsafe, func, arg, xname);
2528644267aSskrll }
2538644267aSskrll
2548644267aSskrll static void
imxgpio_disestablish(device_t dev,void * ih)2558644267aSskrll imxgpio_disestablish(device_t dev, void *ih)
2568644267aSskrll {
2578644267aSskrll intr_disestablish(ih);
2588644267aSskrll }
2598644267aSskrll
2608644267aSskrll static bool
imxgpio_intrstr(device_t dev,u_int * specifier,char * buf,size_t buflen)2618644267aSskrll imxgpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
2628644267aSskrll {
2638644267aSskrll struct imxgpio_softc * const sc = device_private(dev);
2648644267aSskrll
2658644267aSskrll /* 1st cell is the interrupt number */
2668644267aSskrll /* 2nd cell is flags */
2678644267aSskrll
2688644267aSskrll if (!specifier)
2698644267aSskrll return false;
2708644267aSskrll
2718644267aSskrll const u_int intr = be32toh(specifier[0]);
2728644267aSskrll
2738644267aSskrll snprintf(buf, buflen, "irq %d (gpio%d %d)",
2748644267aSskrll sc->gpio_irqbase + intr, sc->gpio_unit, intr);
2758644267aSskrll
2768644267aSskrll return true;
2778644267aSskrll }
278