1 /* $OpenBSD: dwgpio.c,v 1.8 2024/05/13 01:15:50 jsg 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_intr(void *); 95 96 int 97 dwgpio_match(struct device *parent, void *match, void *aux) 98 { 99 struct acpi_attach_args *aaa = aux; 100 struct cfdata *cf = match; 101 102 if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1) 103 return 0; 104 return acpi_matchhids(aaa, dwgpio_hids, cf->cf_driver->cd_name); 105 } 106 107 void 108 dwgpio_attach(struct device *parent, struct device *self, void *aux) 109 { 110 struct acpi_attach_args *aaa = aux; 111 struct dwgpio_softc *sc = (struct dwgpio_softc *)self; 112 int i; 113 114 sc->sc_acpi = (struct acpi_softc *)parent; 115 sc->sc_node = aaa->aaa_node; 116 printf(" %s", sc->sc_node->name); 117 118 printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]); 119 120 sc->sc_memt = aaa->aaa_bst[0]; 121 if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0], 122 0, &sc->sc_memh)) { 123 printf(": can't map registers\n"); 124 return; 125 } 126 127 aml_find_node(sc->sc_node, "_HID", dwgpio_found_hid, sc); 128 129 if (sc->sc_npins == 0) { 130 printf(": no pins\n"); 131 return; 132 } 133 if (sc->sc_npins >= GPIO_NUM_PINS) { 134 printf(": too many pins\n"); 135 return; 136 } 137 138 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 139 M_DEVBUF, M_WAITOK | M_ZERO); 140 141 printf(" irq"); 142 143 sc->sc_nirq = aaa->aaa_nirq; 144 sc->sc_ih = mallocarray(sc->sc_nirq, sizeof(*sc->sc_ih), 145 M_DEVBUF, M_WAITOK | M_ZERO); 146 for (i = 0; i < sc->sc_nirq; i++) { 147 printf(" %d", aaa->aaa_irq[i]); 148 149 sc->sc_ih[i] = acpi_intr_establish(aaa->aaa_irq[i], 150 aaa->aaa_irq_flags[i], IPL_BIO, dwgpio_intr, 151 sc, sc->sc_dev.dv_xname); 152 if (sc->sc_ih[i] == NULL) { 153 printf(": can't establish interrupt\n"); 154 goto unmap; 155 } 156 } 157 158 sc->sc_gpio.cookie = sc; 159 sc->sc_gpio.read_pin = dwgpio_read_pin; 160 sc->sc_gpio.write_pin = dwgpio_write_pin; 161 sc->sc_gpio.intr_establish = dwgpio_intr_establish; 162 sc->sc_node->gpio = &sc->sc_gpio; 163 164 printf(", %d pins\n", sc->sc_npins); 165 166 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 167 return; 168 169 unmap: 170 for (i = 0; i < sc->sc_nirq; i++) { 171 if (sc->sc_ih[i]) 172 acpi_intr_disestablish(sc->sc_ih[i]); 173 } 174 free(sc->sc_ih, M_DEVBUF, sc->sc_nirq * sizeof(*sc->sc_ih)); 175 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 176 bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]); 177 } 178 179 180 int 181 dwgpio_found_hid(struct aml_node *child, void *arg) 182 { 183 struct dwgpio_softc *sc = (struct dwgpio_softc *)arg; 184 struct aml_node *node = child->parent; 185 int reg; 186 187 /* Skip our own _HID. */ 188 if (node == sc->sc_node) 189 return 0; 190 191 /* Only handle a single port for now. */ 192 reg = acpi_getpropint(node, "reg", -1); 193 if (reg != 0) 194 return 0; 195 196 sc->sc_npins = acpi_getpropint(node, "snps,nr-gpios", GPIO_NUM_PINS); 197 node->attached = 1; 198 return 0; 199 } 200 201 int 202 dwgpio_read_pin(void *cookie, int pin) 203 { 204 struct dwgpio_softc *sc = cookie; 205 uint32_t reg; 206 int val; 207 208 if (pin < 0 || pin >= sc->sc_npins) 209 return 0; 210 211 reg = HREAD4(sc, GPIO_EXT_PORTA); 212 val = (reg >> pin) & 1; 213 return val; 214 } 215 216 void 217 dwgpio_write_pin(void *cookie, int pin, int val) 218 { 219 struct dwgpio_softc *sc = cookie; 220 221 if (pin < 0 || pin >= sc->sc_npins) 222 return; 223 224 if (val) 225 HSET4(sc, GPIO_SWPORTA_DR, (1 << pin)); 226 else 227 HCLR4(sc, GPIO_SWPORTA_DR, (1 << pin)); 228 } 229 230 void 231 dwgpio_intr_establish(void *cookie, int pin, int flags, 232 int (*func)(void *), void *arg) 233 { 234 struct dwgpio_softc *sc = cookie; 235 236 if (pin < 0 || pin >= sc->sc_npins) 237 return; 238 239 sc->sc_pin_ih[pin].ih_func = func; 240 sc->sc_pin_ih[pin].ih_arg = arg; 241 242 if ((flags & LR_GPIO_MODE) == LR_GPIO_EDGE) 243 HSET4(sc, GPIO_INTTYPE_LEVEL, 1 << pin); 244 else 245 HCLR4(sc, GPIO_INTTYPE_LEVEL, 1 << pin); 246 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTHI) 247 HSET4(sc, GPIO_INT_POLARITY, 1 << pin); 248 else 249 HCLR4(sc, GPIO_INT_POLARITY, 1 << pin); 250 251 HCLR4(sc, GPIO_SWPORTA_DDR, 1 << pin); 252 HSET4(sc, GPIO_INTEN, 1 << pin); 253 HCLR4(sc, GPIO_INTMASK, 1 << pin); 254 } 255 256 int 257 dwgpio_intr(void *arg) 258 { 259 struct dwgpio_softc *sc = arg; 260 uint32_t status; 261 int pin, handled = 0; 262 263 status = HREAD4(sc, GPIO_INT_STATUS); 264 HWRITE4(sc, GPIO_PORTS_EOI, status); 265 266 for (pin = 0; pin < sc->sc_npins; pin++) { 267 if ((status & (1 << pin)) && sc->sc_pin_ih[pin].ih_func) { 268 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 269 handled = 1; 270 } 271 } 272 273 return handled; 274 } 275