xref: /netbsd-src/sys/arch/arm/nxp/imx_gpio.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
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