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