1 /* $NetBSD: imx31_gpio.c,v 1.10 2021/08/07 16:18:44 thorpej Exp $ */ 2 /*- 3 * Copyright (c) 2007 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Matt Thomas 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: imx31_gpio.c,v 1.10 2021/08/07 16:18:44 thorpej Exp $"); 32 33 #define _INTR_PRIVATE 34 35 #include "locators.h" 36 #include "gpio.h" 37 38 #include <sys/param.h> 39 #include <sys/evcnt.h> 40 #include <sys/atomic.h> 41 42 #include <uvm/uvm_extern.h> 43 44 #include <machine/intr.h> 45 46 #include <arm/cpu.h> 47 #include <arm/armreg.h> 48 #include <arm/cpufunc.h> 49 50 #include <sys/bus.h> 51 52 #include <arm/imx/imx31reg.h> 53 #include <arm/imx/imx31var.h> 54 #include <arm/imx/imxgpioreg.h> 55 #include <arm/pic/picvar.h> 56 57 #if NGPIO > 0 58 #include <sys/gpio.h> 59 #include <dev/gpio/gpiovar.h> 60 #endif 61 62 static void gpio_pic_block_irqs(struct pic_softc *, size_t, uint32_t); 63 static void gpio_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t); 64 static int gpio_pic_find_pending_irqs(struct pic_softc *); 65 static void gpio_pic_establish_irq(struct pic_softc *, struct intrsource *); 66 67 const struct pic_ops gpio_pic_ops = { 68 .pic_block_irqs = gpio_pic_block_irqs, 69 .pic_unblock_irqs = gpio_pic_unblock_irqs, 70 .pic_find_pending_irqs = gpio_pic_find_pending_irqs, 71 .pic_establish_irq = gpio_pic_establish_irq, 72 }; 73 74 struct gpio_softc { 75 struct pic_softc gpio_pic; 76 bus_space_tag_t gpio_memt; 77 bus_space_handle_t gpio_memh; 78 uint32_t gpio_enable_mask; 79 uint32_t gpio_edge_mask; 80 uint32_t gpio_level_mask; 81 #if NGPIO > 0 82 struct gpio_chipset_tag gpio_chipset; 83 gpio_pin_t gpio_pins[32]; 84 #endif 85 }; 86 87 #define PIC_TO_SOFTC(pic) \ 88 ((struct gpio_softc *)((char *)(pic) - \ 89 offsetof(struct gpio_softc, gpio_pic))) 90 91 #define GPIO_READ(gpio, reg) \ 92 bus_space_read_4((gpio)->gpio_memt, (gpio)->gpio_memh, (reg)) 93 #define GPIO_WRITE(gpio, reg, val) \ 94 bus_space_write_4((gpio)->gpio_memt, (gpio)->gpio_memh, (reg), (val)) 95 96 void 97 gpio_pic_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask) 98 { 99 struct gpio_softc * const gpio = PIC_TO_SOFTC(pic); 100 KASSERT(irq_base == 0); 101 102 gpio->gpio_enable_mask |= irq_mask; 103 /* 104 * If this a level source, ack it now. If it's still asserted 105 * it'll come back. 106 */ 107 if (irq_mask & gpio->gpio_level_mask) 108 GPIO_WRITE(gpio, GPIO_ISR, irq_mask); 109 GPIO_WRITE(gpio, GPIO_IMR, gpio->gpio_enable_mask); 110 } 111 112 void 113 gpio_pic_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask) 114 { 115 struct gpio_softc * const gpio = PIC_TO_SOFTC(pic); 116 KASSERT(irq_base == 0); 117 118 gpio->gpio_enable_mask &= ~irq_mask; 119 GPIO_WRITE(gpio, GPIO_IMR, gpio->gpio_enable_mask); 120 } 121 122 int 123 gpio_pic_find_pending_irqs(struct pic_softc *pic) 124 { 125 struct gpio_softc * const gpio = PIC_TO_SOFTC(pic); 126 uint32_t v; 127 uint32_t pending; 128 129 v = GPIO_READ(gpio, GPIO_ISR); 130 pending = (v & gpio->gpio_enable_mask); 131 if (pending == 0) 132 return 0; 133 134 /* 135 * Disable the pending interrupts. 136 */ 137 gpio->gpio_enable_mask &= ~pending; 138 GPIO_WRITE(gpio, GPIO_IMR, gpio->gpio_enable_mask); 139 140 /* 141 * If any of the sources are edge triggered, ack them now so 142 * we won't lose them. 143 */ 144 if (v & gpio->gpio_edge_mask) 145 GPIO_WRITE(gpio, GPIO_ISR, v & gpio->gpio_edge_mask); 146 147 /* 148 * Now find all the pending bits and mark them as pending. 149 */ 150 do { 151 int irq; 152 KASSERT(pending != 0); 153 irq = 31 - __builtin_clz(pending); 154 pending &= ~__BIT(irq); 155 pic_mark_pending(&gpio->gpio_pic, irq); 156 } while (pending != 0); 157 158 return 1; 159 } 160 161 #define GPIO_TYPEMAP \ 162 ((GPIO_ICR_LEVEL_LOW << (2*IST_LEVEL_LOW)) | \ 163 (GPIO_ICR_LEVEL_HIGH << (2*IST_LEVEL_HIGH)) | \ 164 (GPIO_ICR_EDGE_RISING << (2*IST_EDGE_RISING)) | \ 165 (GPIO_ICR_EDGE_FALLING << (2*IST_EDGE_FALLING))) 166 167 void 168 gpio_pic_establish_irq(struct pic_softc *pic, struct intrsource *is) 169 { 170 struct gpio_softc * const gpio = PIC_TO_SOFTC(pic); 171 KASSERT(is->is_irq < 32); 172 uint32_t irq_mask = __BIT(is->is_irq); 173 uint32_t v; 174 unsigned int icr_shift, icr_reg; 175 unsigned int gtype; 176 177 /* 178 * Make sure the irq isn't enabled and not asserting. 179 */ 180 gpio->gpio_enable_mask &= ~irq_mask; 181 GPIO_WRITE(gpio, GPIO_IMR, gpio->gpio_enable_mask); 182 GPIO_WRITE(gpio, GPIO_ISR, irq_mask); 183 184 /* 185 * Convert the type to a gpio type and figure out which bits in what 186 * register we have to tweak. 187 */ 188 gtype = (GPIO_TYPEMAP >> (2 * is->is_type)) & 3; 189 icr_shift = (is->is_irq & 0x0f) << 1; 190 icr_reg = GPIO_ICR1 + ((is->is_irq & 0x10) >> 2); 191 192 /* 193 * Set the interrupt type. 194 */ 195 v = GPIO_READ(gpio, icr_reg); 196 v &= ~(3 << icr_shift); 197 v |= gtype << icr_shift; 198 GPIO_WRITE(gpio, icr_reg, v); 199 200 /* 201 * Mark it as input. 202 */ 203 v = GPIO_READ(gpio, GPIO_DIR); 204 v &= ~irq_mask; 205 GPIO_WRITE(gpio, GPIO_DIR, v); 206 207 /* 208 * Now record the type of interrupt. 209 */ 210 if (gtype == GPIO_ICR_EDGE_RISING || gtype == GPIO_ICR_EDGE_FALLING) { 211 gpio->gpio_edge_mask |= irq_mask; 212 gpio->gpio_level_mask &= ~irq_mask; 213 } else { 214 gpio->gpio_edge_mask &= ~irq_mask; 215 gpio->gpio_level_mask |= irq_mask; 216 } 217 } 218 219 static int gpio_match(device_t, cfdata_t, void *); 220 static void gpio_attach(device_t, device_t, void *); 221 222 CFATTACH_DECL_NEW(imxgpio, 223 sizeof(struct gpio_softc), 224 gpio_match, gpio_attach, 225 NULL, NULL); 226 227 #if NGPIO > 0 228 229 static int 230 imxgpio_pin_read(void *arg, int pin) 231 { 232 struct gpio_softc * const gpio = arg; 233 234 return (GPIO_READ(gpio, GPIO_DR) >> pin) & 1; 235 } 236 237 static void 238 imxgpio_pin_write(void *arg, int pin, int value) 239 { 240 struct gpio_softc * const gpio = arg; 241 uint32_t mask = 1 << pin; 242 uint32_t old, new; 243 244 old = GPIO_READ(gpio, GPIO_DR); 245 if (value) 246 new = old | mask; 247 else 248 new = old & ~mask; 249 250 if (old != new) 251 GPIO_WRITE(gpio, GPIO_DR, new); 252 } 253 254 static void 255 imxgpio_pin_ctl(void *arg, int pin, int flags) 256 { 257 struct gpio_softc * const gpio = arg; 258 uint32_t mask = 1 << pin; 259 uint32_t old, new; 260 261 old = GPIO_READ(gpio, GPIO_DIR); 262 new = old; 263 switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 264 case GPIO_PIN_INPUT: new |= mask; break; 265 case GPIO_PIN_OUTPUT: new &= ~mask; break; 266 default: return; 267 } 268 if (old != new) 269 GPIO_WRITE(gpio, GPIO_DIR, new); 270 } 271 272 static void 273 gpio_defer(device_t self) 274 { 275 struct gpio_softc * const gpio = device_private(self); 276 struct gpio_chipset_tag * const gp = &gpio->gpio_chipset; 277 struct gpiobus_attach_args gba; 278 gpio_pin_t *pins; 279 uint32_t mask, dir, value; 280 int pin; 281 282 gp->gp_cookie = gpio; 283 gp->gp_pin_read = imxgpio_pin_read; 284 gp->gp_pin_write = imxgpio_pin_write; 285 gp->gp_pin_ctl = imxgpio_pin_ctl; 286 287 gba.gba_gc = gp; 288 gba.gba_pins = gpio->gpio_pins; 289 gba.gba_npins = __arraycount(gpio->gpio_pins); 290 291 dir = GPIO_READ(gpio, GPIO_DIR); 292 value = GPIO_READ(gpio, GPIO_DR); 293 for (pin = 0, mask = 1, pins = gpio->gpio_pins; 294 pin < 32; pin++, mask <<= 1, pins++) { 295 pins->pin_num = pin; 296 if ((gpio->gpio_edge_mask|gpio->gpio_level_mask) & mask) 297 pins->pin_caps = GPIO_PIN_INPUT; 298 else 299 pins->pin_caps = GPIO_PIN_INPUT|GPIO_PIN_OUTPUT; 300 pins->pin_flags = 301 (dir & mask) ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; 302 pins->pin_state = 303 (value & mask) ? GPIO_PIN_HIGH : GPIO_PIN_LOW; 304 } 305 306 config_found(self, &gba, gpiobus_print, CFARGS_NONE); 307 } 308 #endif /* NGPIO > 0 */ 309 310 int 311 gpio_match(device_t parent, cfdata_t cfdata, void *aux) 312 { 313 struct ahb_attach_args *ahba = aux; 314 bus_space_handle_t memh; 315 bus_size_t size; 316 int error; 317 318 if (ahba->ahba_addr != GPIO1_BASE 319 && ahba->ahba_addr != GPIO2_BASE 320 && ahba->ahba_addr != GPIO3_BASE) 321 return 0; 322 323 size = (ahba->ahba_size == AHBCF_SIZE_DEFAULT) ? GPIO_SIZE : ahba->ahba_size; 324 325 error = bus_space_map(ahba->ahba_memt, ahba->ahba_addr, size, 0, &memh); 326 if (error) 327 return 0; 328 329 bus_space_unmap(ahba->ahba_memt, memh, size); 330 return 1; 331 } 332 333 void 334 gpio_attach(device_t parent, device_t self, void *aux) 335 { 336 struct ahb_attach_args * const ahba = aux; 337 struct gpio_softc * const gpio = device_private(self); 338 int error; 339 340 if (ahba->ahba_size == AHBCF_SIZE_DEFAULT) 341 ahba->ahba_size = GPIO_SIZE; 342 343 gpio->gpio_memt = ahba->ahba_memt; 344 error = bus_space_map(ahba->ahba_memt, ahba->ahba_addr, ahba->ahba_size, 345 0, &gpio->gpio_memh); 346 347 if (error) { 348 aprint_error(": failed to map register %#lx@%#lx: %d\n", 349 ahba->ahba_size, ahba->ahba_addr, error); 350 return; 351 } 352 353 if (ahba->ahba_irqbase != AHBCF_IRQBASE_DEFAULT) { 354 gpio->gpio_pic.pic_ops = &gpio_pic_ops; 355 strlcpy(gpio->gpio_pic.pic_name, device_xname(self), 356 sizeof(gpio->gpio_pic.pic_name)); 357 gpio->gpio_pic.pic_maxsources = 32; 358 pic_add(&gpio->gpio_pic, ahba->ahba_irqbase); 359 aprint_normal(": interrupts %d..%d", 360 ahba->ahba_irqbase, ahba->ahba_irqbase + 31); 361 } 362 aprint_normal("\n"); 363 #if NGPIO > 0 364 config_interrupts(self, gpio_defer); 365 #endif 366 } 367