1 /* $OpenBSD: dwgpio.c,v 1.7 2022/04/06 18:59:27 naddy Exp $ */ 2 /* 3 * Copyright (c) 2020 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/malloc.h> 20 #include <sys/systm.h> 21 22 #include <dev/acpi/acpireg.h> 23 #include <dev/acpi/acpivar.h> 24 #include <dev/acpi/acpidev.h> 25 #include <dev/acpi/amltypes.h> 26 #include <dev/acpi/dsdt.h> 27 28 /* Registers. */ 29 #define GPIO_SWPORTA_DR 0x0000 30 #define GPIO_SWPORTA_DDR 0x0004 31 #define GPIO_INTEN 0x0030 32 #define GPIO_INTTYPE_LEVEL 0x0034 33 #define GPIO_INT_POLARITY 0x0038 34 #define GPIO_INT_STATUS 0x003c 35 #define GPIO_PORTS_EOI 0x0040 36 #define GPIO_INTMASK 0x0044 37 #define GPIO_DEBOUNCE 0x0048 38 #define GPIO_EXT_PORTA 0x0050 39 40 #define GPIO_NUM_PORTS 4 41 #define GPIO_NUM_PINS 32 42 43 #define HREAD4(sc, reg) \ 44 (bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg))) 45 #define HWRITE4(sc, reg, val) \ 46 bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) 47 #define HSET4(sc, reg, bits) \ 48 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 49 #define HCLR4(sc, reg, bits) \ 50 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 51 52 struct dwgpio_intrhand { 53 int (*ih_func)(void *); 54 void *ih_arg; 55 }; 56 57 struct dwgpio_softc { 58 struct device sc_dev; 59 struct acpi_softc *sc_acpi; 60 struct aml_node *sc_node; 61 62 bus_space_tag_t sc_memt; 63 bus_space_handle_t sc_memh; 64 65 int sc_nirq; 66 void **sc_ih; 67 68 uint32_t sc_npins; 69 struct dwgpio_intrhand *sc_pin_ih; 70 71 struct acpi_gpio sc_gpio; 72 }; 73 74 int dwgpio_match(struct device *, void *, void *); 75 void dwgpio_attach(struct device *, struct device *, void *); 76 77 const struct cfattach dwgpio_ca = { 78 sizeof(struct dwgpio_softc), dwgpio_match, dwgpio_attach 79 }; 80 81 struct cfdriver dwgpio_cd = { 82 NULL, "dwgpio", DV_DULL 83 }; 84 85 const char *dwgpio_hids[] = { 86 "APMC0D81", 87 NULL 88 }; 89 90 int dwgpio_found_hid(struct aml_node *, void *); 91 int dwgpio_read_pin(void *, int); 92 void dwgpio_write_pin(void *, int, int); 93 void dwgpio_intr_establish(void *, int, int, int (*)(void *), void *); 94 int dwgpio_pin_intr(struct dwgpio_softc *, int); 95 int dwgpio_intr(void *); 96 97 int 98 dwgpio_match(struct device *parent, void *match, void *aux) 99 { 100 struct acpi_attach_args *aaa = aux; 101 struct cfdata *cf = match; 102 103 if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1) 104 return 0; 105 return acpi_matchhids(aaa, dwgpio_hids, cf->cf_driver->cd_name); 106 } 107 108 void 109 dwgpio_attach(struct device *parent, struct device *self, void *aux) 110 { 111 struct acpi_attach_args *aaa = aux; 112 struct dwgpio_softc *sc = (struct dwgpio_softc *)self; 113 int i; 114 115 sc->sc_acpi = (struct acpi_softc *)parent; 116 sc->sc_node = aaa->aaa_node; 117 printf(" %s", sc->sc_node->name); 118 119 printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); 120 121 sc->sc_memt = aaa->aaa_bst[0]; 122 if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0], 123 0, &sc->sc_memh)) { 124 printf(": can't map registers\n"); 125 return; 126 } 127 128 aml_find_node(sc->sc_node, "_HID", dwgpio_found_hid, sc); 129 130 if (sc->sc_npins == 0) { 131 printf(": no pins\n"); 132 return; 133 } 134 if (sc->sc_npins >= GPIO_NUM_PINS) { 135 printf(": too many pins\n"); 136 return; 137 } 138 139 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 140 M_DEVBUF, M_WAITOK | M_ZERO); 141 142 printf(" irq"); 143 144 sc->sc_nirq = aaa->aaa_nirq; 145 sc->sc_ih = mallocarray(sc->sc_nirq, sizeof(*sc->sc_ih), 146 M_DEVBUF, M_WAITOK | M_ZERO); 147 for (i = 0; i < sc->sc_nirq; i++) { 148 printf(" %d", aaa->aaa_irq[i]); 149 150 sc->sc_ih[i] = acpi_intr_establish(aaa->aaa_irq[i], 151 aaa->aaa_irq_flags[i], IPL_BIO, dwgpio_intr, 152 sc, sc->sc_dev.dv_xname); 153 if (sc->sc_ih[i] == NULL) { 154 printf(": can't establish interrupt\n"); 155 goto unmap; 156 } 157 } 158 159 sc->sc_gpio.cookie = sc; 160 sc->sc_gpio.read_pin = dwgpio_read_pin; 161 sc->sc_gpio.write_pin = dwgpio_write_pin; 162 sc->sc_gpio.intr_establish = dwgpio_intr_establish; 163 sc->sc_node->gpio = &sc->sc_gpio; 164 165 printf(", %d pins\n", sc->sc_npins); 166 167 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 168 return; 169 170 unmap: 171 for (i = 0; i < sc->sc_nirq; i++) { 172 if (sc->sc_ih[i]) 173 acpi_intr_disestablish(sc->sc_ih[i]); 174 } 175 free(sc->sc_ih, M_DEVBUF, sc->sc_nirq * sizeof(*sc->sc_ih)); 176 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 177 bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]); 178 } 179 180 181 int 182 dwgpio_found_hid(struct aml_node *child, void *arg) 183 { 184 struct dwgpio_softc *sc = (struct dwgpio_softc *)arg; 185 struct aml_node *node = child->parent; 186 int reg; 187 188 /* Skip our own _HID. */ 189 if (node == sc->sc_node) 190 return 0; 191 192 /* Only handle a single port for now. */ 193 reg = acpi_getpropint(node, "reg", -1); 194 if (reg != 0) 195 return 0; 196 197 sc->sc_npins = acpi_getpropint(node, "snps,nr-gpios", GPIO_NUM_PINS); 198 node->attached = 1; 199 return 0; 200 } 201 202 int 203 dwgpio_read_pin(void *cookie, int pin) 204 { 205 struct dwgpio_softc *sc = cookie; 206 uint32_t reg; 207 int val; 208 209 if (pin < 0 || pin >= sc->sc_npins) 210 return 0; 211 212 reg = HREAD4(sc, GPIO_EXT_PORTA); 213 val = (reg >> pin) & 1; 214 return val; 215 } 216 217 void 218 dwgpio_write_pin(void *cookie, int pin, int val) 219 { 220 struct dwgpio_softc *sc = cookie; 221 222 if (pin < 0 || pin >= sc->sc_npins) 223 return; 224 225 if (val) 226 HSET4(sc, GPIO_SWPORTA_DR, (1 << pin)); 227 else 228 HCLR4(sc, GPIO_SWPORTA_DR, (1 << pin)); 229 } 230 231 void 232 dwgpio_intr_establish(void *cookie, int pin, int flags, 233 int (*func)(void *), void *arg) 234 { 235 struct dwgpio_softc *sc = cookie; 236 237 if (pin < 0 || pin >= sc->sc_npins) 238 return; 239 240 sc->sc_pin_ih[pin].ih_func = func; 241 sc->sc_pin_ih[pin].ih_arg = arg; 242 243 if ((flags & LR_GPIO_MODE) == LR_GPIO_EDGE) 244 HSET4(sc, GPIO_INTTYPE_LEVEL, 1 << pin); 245 else 246 HCLR4(sc, GPIO_INTTYPE_LEVEL, 1 << pin); 247 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTHI) 248 HSET4(sc, GPIO_INT_POLARITY, 1 << pin); 249 else 250 HCLR4(sc, GPIO_INT_POLARITY, 1 << pin); 251 252 HCLR4(sc, GPIO_SWPORTA_DDR, 1 << pin); 253 HSET4(sc, GPIO_INTEN, 1 << pin); 254 HCLR4(sc, GPIO_INTMASK, 1 << pin); 255 } 256 257 int 258 dwgpio_intr(void *arg) 259 { 260 struct dwgpio_softc *sc = arg; 261 uint32_t status; 262 int pin, handled = 0; 263 264 status = HREAD4(sc, GPIO_INT_STATUS); 265 HWRITE4(sc, GPIO_PORTS_EOI, status); 266 267 for (pin = 0; pin < sc->sc_npins; pin++) { 268 if ((status & (1 << pin)) && sc->sc_pin_ih[pin].ih_func) { 269 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 270 handled = 1; 271 } 272 } 273 274 return handled; 275 } 276