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