Home | History | Annotate | Line # | Download | only in nxp
      1 /*	$NetBSD: imx_gpio.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $	*/
      2 /*-
      3  * Copyright (c) 2019 Genetec Corporation.  All rights reserved.
      4  * Written by Hashimoto Kenichi for Genetec Corporation.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     20  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     22  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: imx_gpio.c,v 1.4 2021/01/27 03:10:20 thorpej Exp $");
     30 
     31 #include "opt_fdt.h"
     32 #include "gpio.h"
     33 
     34 #define	_INTR_PRIVATE
     35 
     36 #include <sys/param.h>
     37 #include <sys/bus.h>
     38 #include <sys/device.h>
     39 #include <sys/intr.h>
     40 #include <sys/systm.h>
     41 #include <sys/kernel.h>
     42 #include <sys/kmem.h>
     43 #include <sys/gpio.h>
     44 
     45 #include <dev/gpio/gpiovar.h>
     46 
     47 #include <arm/pic/picvar.h>
     48 #include <arm/nxp/imx6_reg.h>
     49 
     50 #include <arm/imx/imxgpioreg.h>
     51 #include <arm/imx/imxgpiovar.h>
     52 
     53 #include <dev/fdt/fdtvar.h>
     54 
     55 static void *imx6_gpio_fdt_acquire(device_t, const void *, size_t, int);
     56 static void imx6_gpio_fdt_release(device_t, void *);
     57 static int imx6_gpio_fdt_read(device_t, void *, bool);
     58 static void imx6_gpio_fdt_write(device_t, void *, int, bool);
     59 
     60 static void *imxgpio_establish(device_t, u_int *, int, int,
     61     int (*)(void *), void *, const char *);
     62 static void imxgpio_disestablish(device_t, void *);
     63 static bool imxgpio_intrstr(device_t, u_int *, char *, size_t);
     64 
     65 static struct fdtbus_interrupt_controller_func imxgpio_funcs = {
     66 	.establish = imxgpio_establish,
     67 	.disestablish = imxgpio_disestablish,
     68 	.intrstr = imxgpio_intrstr
     69 };
     70 
     71 static struct fdtbus_gpio_controller_func imx6_gpio_funcs = {
     72 	.acquire = imx6_gpio_fdt_acquire,
     73 	.release = imx6_gpio_fdt_release,
     74 	.read = imx6_gpio_fdt_read,
     75 	.write = imx6_gpio_fdt_write
     76 };
     77 
     78 const int imxgpio_ngroups = GPIO_NGROUPS;
     79 
     80 static const struct device_compatible_entry compat_data[] = {
     81 	{ .compat = "fsl,imx35-gpio" },
     82 	DEVICE_COMPAT_EOL
     83 };
     84 
     85 int
     86 imxgpio_match(device_t parent, cfdata_t cf, void *aux)
     87 {
     88 	struct fdt_attach_args * const faa = aux;
     89 
     90 	return of_compatible_match(faa->faa_phandle, compat_data);
     91 }
     92 
     93 void
     94 imxgpio_attach(device_t parent, device_t self, void *aux)
     95 {
     96 	struct imxgpio_softc * const sc = device_private(self);
     97 	struct fdt_attach_args * const faa = aux;
     98 	char intrstr[128];
     99 	const int phandle = faa->faa_phandle;
    100 	bus_space_handle_t ioh;
    101 	bus_addr_t addr;
    102 	bus_size_t size;
    103 	int error;
    104 
    105 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    106 		aprint_error(": couldn't get registers\n");
    107 		return;
    108 	}
    109 
    110 	error = bus_space_map(faa->faa_bst, addr, size, 0, &ioh);
    111 	if (error) {
    112 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
    113 		return;
    114 	}
    115 
    116 	aprint_naive("\n");
    117 	aprint_normal(": GPIO (%s)\n", fdtbus_get_string(phandle, "name"));
    118 
    119 	sc->gpio_memt = faa->faa_bst;
    120 	sc->gpio_memh = ioh;
    121 	sc->gpio_unit = -1;
    122 	sc->gpio_irqbase = PIC_IRQBASE_ALLOC;
    123 
    124 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
    125 		aprint_error_dev(self, "failed to decode interrupt\n");
    126 		return;
    127 	}
    128 	sc->gpio_is = fdtbus_intr_establish_xname(phandle, 0, IPL_HIGH, 0,
    129 	    pic_handle_intr, &sc->gpio_pic, device_xname(self));
    130 	if (sc->gpio_is == NULL) {
    131 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
    132 		    intrstr);
    133 		return;
    134 	}
    135 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
    136 
    137 	if (!fdtbus_intr_str(phandle, 1, intrstr, sizeof(intrstr))) {
    138 		aprint_error_dev(self, "failed to decode interrupt\n");
    139 		return;
    140 	}
    141 	sc->gpio_is_high = fdtbus_intr_establish_xname(phandle, 1, IPL_HIGH, 0,
    142 	    pic_handle_intr, &sc->gpio_pic, device_xname(self));
    143 	if (sc->gpio_is_high == NULL) {
    144 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
    145 		    intrstr);
    146 		return;
    147 	}
    148 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
    149 
    150 	fdtbus_register_gpio_controller(self, phandle, &imx6_gpio_funcs);
    151 
    152 	error = fdtbus_register_interrupt_controller(self, phandle,
    153 	    &imxgpio_funcs);
    154 	if (error) {
    155 		aprint_error(": couldn't register with fdtbus: %d\n", error);
    156 		return;
    157 	}
    158 
    159 	imxgpio_attach_common(self);
    160 }
    161 
    162 static void *
    163 imx6_gpio_fdt_acquire(device_t dev, const void *data, size_t len, int flags)
    164 {
    165 	struct imxgpio_softc * const sc = device_private(dev);
    166 	struct imxgpio_pin *gpin;
    167 	const u_int *gpio = data;
    168 
    169 	if (len != 12)
    170 		return NULL;
    171 
    172 	const u_int pin = be32toh(gpio[1]);
    173 	const bool actlo = be32toh(gpio[2]) & 1;
    174 
    175 	gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
    176 	gpin->pin_no = pin;
    177 	gpin->pin_flags = flags;
    178 	gpin->pin_actlo = actlo;
    179 
    180 	imxgpio_pin_ctl(sc, gpin->pin_no, gpin->pin_flags);
    181 
    182 	return gpin;
    183 }
    184 
    185 static void
    186 imx6_gpio_fdt_release(device_t dev, void *priv)
    187 {
    188 	struct imxgpio_softc * const sc = device_private(dev);
    189 	struct imxgpio_pin *gpin = priv;
    190 
    191 	imxgpio_pin_ctl(sc, gpin->pin_no, GPIO_PIN_INPUT);
    192 	kmem_free(gpin, sizeof(*gpin));
    193 }
    194 
    195 static int
    196 imx6_gpio_fdt_read(device_t dev, void *priv, bool raw)
    197 {
    198 	struct imxgpio_softc * const sc = device_private(dev);
    199 	struct imxgpio_pin *gpin = priv;
    200 	int val;
    201 
    202 	val = imxgpio_pin_read(sc, gpin->pin_no);
    203 
    204 	if (!raw && gpin->pin_actlo)
    205 		val = !val;
    206 
    207 	return val;
    208 }
    209 
    210 static void
    211 imx6_gpio_fdt_write(device_t dev, void *priv, int val, bool raw)
    212 {
    213 	struct imxgpio_softc * const sc = device_private(dev);
    214 	struct imxgpio_pin *gpin = priv;
    215 
    216 	if (!raw && gpin->pin_actlo)
    217 		val = !val;
    218 
    219 	imxgpio_pin_write(sc, gpin->pin_no, val);
    220 }
    221 
    222 static void *
    223 imxgpio_establish(device_t dev, u_int *specifier, int ipl, int flags,
    224     int (*func)(void *), void *arg, const char *xname)
    225 {
    226 	struct imxgpio_softc * const sc = device_private(dev);
    227 
    228 	/* 1st cell is the interrupt number */
    229 	/* 2nd cell is flags */
    230 
    231 	const u_int intr = be32toh(specifier[0]);
    232 	const u_int trig = be32toh(specifier[1]) & 0xf;
    233 	u_int level;
    234 
    235 	if ((trig & 0x1) && (trig & 0x2))
    236 		level = IST_EDGE_BOTH;
    237 	else if (trig & 0x1)
    238 		level = IST_EDGE_RISING;
    239 	else if (trig & 0x2)
    240 		level = IST_EDGE_FALLING;
    241 	else if (trig & 0x4)
    242 		level = IST_LEVEL_HIGH;
    243 	else
    244 		level = IST_LEVEL_LOW;
    245 
    246 	const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
    247 
    248 	aprint_debug_dev(dev, "intr establish irq %d, level %d\n",
    249 	    sc->gpio_irqbase + intr, level);
    250 	return intr_establish_xname(sc->gpio_irqbase + intr, ipl,
    251 	    level | mpsafe, func, arg, xname);
    252 }
    253 
    254 static void
    255 imxgpio_disestablish(device_t dev, void *ih)
    256 {
    257 	intr_disestablish(ih);
    258 }
    259 
    260 static bool
    261 imxgpio_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
    262 {
    263 	struct imxgpio_softc * const sc = device_private(dev);
    264 
    265 	/* 1st cell is the interrupt number */
    266 	/* 2nd cell is flags */
    267 
    268 	if (!specifier)
    269 		return false;
    270 
    271 	const u_int intr = be32toh(specifier[0]);
    272 
    273 	snprintf(buf, buflen, "irq %d (gpio%d %d)",
    274 	    sc->gpio_irqbase + intr, sc->gpio_unit, intr);
    275 
    276 	return true;
    277 }
    278