1 /* $OpenBSD: aplgpio.c,v 1.1 2019/06/17 18:28:18 patrick Exp $ */ 2 /* 3 * Copyright (c) 2016 Mark Kettenis 4 * Copyright (c) 2019 James Hastings 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/malloc.h> 21 #include <sys/systm.h> 22 23 #include <dev/acpi/acpireg.h> 24 #include <dev/acpi/acpivar.h> 25 #include <dev/acpi/acpidev.h> 26 #include <dev/acpi/amltypes.h> 27 #include <dev/acpi/dsdt.h> 28 29 #define APLGPIO_CONF_TXSTATE 0x00000001 30 #define APLGPIO_CONF_RXSTATE 0x00000002 31 #define APLGPIO_CONF_RXINV 0x00800000 32 #define APLGPIO_CONF_RXEV_EDGE 0x02000000 33 #define APLGPIO_CONF_RXEV_ZERO 0x04000000 34 #define APLGPIO_CONF_RXEV_MASK 0x06000000 35 36 #define APLGPIO_IRQ_STS 0x100 37 #define APLGPIO_IRQ_EN 0x110 38 #define APLGPIO_PAD_CFG0 0x500 39 40 struct aplgpio_intrhand { 41 int (*ih_func)(void *); 42 void *ih_arg; 43 }; 44 45 struct aplgpio_softc { 46 struct device sc_dev; 47 struct acpi_softc *sc_acpi; 48 struct aml_node *sc_node; 49 50 bus_space_tag_t sc_memt; 51 bus_space_handle_t sc_memh; 52 bus_addr_t sc_addr; 53 bus_size_t sc_size; 54 55 int sc_irq; 56 int sc_irq_flags; 57 void *sc_ih; 58 59 int sc_npins; 60 struct aplgpio_intrhand *sc_pin_ih; 61 62 struct acpi_gpio sc_gpio; 63 }; 64 65 int aplgpio_match(struct device *, void *, void *); 66 void aplgpio_attach(struct device *, struct device *, void *); 67 68 struct cfattach aplgpio_ca = { 69 sizeof(struct aplgpio_softc), aplgpio_match, aplgpio_attach 70 }; 71 72 struct cfdriver aplgpio_cd = { 73 NULL, "aplgpio", DV_DULL 74 }; 75 76 const char *aplgpio_hids[] = { 77 "INT3452", 78 NULL 79 }; 80 81 int aplgpio_parse_resources(int, union acpi_resource *, void *); 82 int aplgpio_read_pin(void *, int); 83 void aplgpio_write_pin(void *, int, int); 84 void aplgpio_intr_establish(void *, int, int, int (*)(), void *); 85 int aplgpio_intr(void *); 86 87 int 88 aplgpio_match(struct device *parent, void *match, void *aux) 89 { 90 struct acpi_attach_args *aaa = aux; 91 struct cfdata *cf = match; 92 93 return acpi_matchhids(aaa, aplgpio_hids, cf->cf_driver->cd_name); 94 } 95 96 void 97 aplgpio_attach(struct device *parent, struct device *self, void *aux) 98 { 99 struct acpi_attach_args *aaa = aux; 100 struct aplgpio_softc *sc = (struct aplgpio_softc *)self; 101 struct aml_value res; 102 int64_t uid; 103 int i; 104 105 sc->sc_acpi = (struct acpi_softc *)parent; 106 sc->sc_node = aaa->aaa_node; 107 printf(": %s", sc->sc_node->name); 108 109 if (aml_evalinteger(sc->sc_acpi, sc->sc_node, "_UID", 0, NULL, &uid)) { 110 printf(", can't find uid\n"); 111 return; 112 } 113 114 printf(" uid %lld", uid); 115 116 switch (uid) { 117 case 1: 118 sc->sc_npins = 78; 119 break; 120 case 2: 121 sc->sc_npins = 77; 122 break; 123 case 3: 124 sc->sc_npins = 47; 125 break; 126 case 4: 127 sc->sc_npins = 43; 128 break; 129 default: 130 printf("\n"); 131 return; 132 } 133 134 if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) { 135 printf(", can't find registers\n"); 136 return; 137 } 138 139 aml_parse_resource(&res, aplgpio_parse_resources, sc); 140 printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size); 141 if (sc->sc_addr == 0 || sc->sc_size == 0) { 142 printf("\n"); 143 return; 144 } 145 aml_freevalue(&res); 146 147 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih), 148 M_DEVBUF, M_NOWAIT | M_ZERO); 149 if (sc->sc_pin_ih == NULL) { 150 printf("\n"); 151 return; 152 } 153 154 printf(" irq %d", sc->sc_irq); 155 156 sc->sc_memt = aaa->aaa_memt; 157 if (bus_space_map(sc->sc_memt, sc->sc_addr, sc->sc_size, 0, 158 &sc->sc_memh)) { 159 printf(", can't map registers\n"); 160 goto free; 161 } 162 163 sc->sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags, IPL_BIO, 164 aplgpio_intr, sc, sc->sc_dev.dv_xname); 165 if (sc->sc_ih == NULL) { 166 printf(", can't establish interrupt\n"); 167 goto unmap; 168 } 169 170 sc->sc_gpio.cookie = sc; 171 sc->sc_gpio.read_pin = aplgpio_read_pin; 172 sc->sc_gpio.write_pin = aplgpio_write_pin; 173 sc->sc_gpio.intr_establish = aplgpio_intr_establish; 174 sc->sc_node->gpio = &sc->sc_gpio; 175 176 /* Mask and clear all interrupts. */ 177 for (i = 0; i < sc->sc_npins; i++) { 178 if (i % 32 == 0) { 179 bus_space_write_4(sc->sc_memt, sc->sc_memh, 180 APLGPIO_IRQ_EN + (i / 32) * 4, 0x00000000); 181 bus_space_write_4(sc->sc_memt, sc->sc_memh, 182 APLGPIO_IRQ_STS + (i / 32) * 4, 0xffffffff); 183 } 184 } 185 186 printf(", %d pins\n", sc->sc_npins); 187 188 acpi_register_gpio(sc->sc_acpi, sc->sc_node); 189 return; 190 191 unmap: 192 bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_size); 193 free: 194 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih)); 195 } 196 197 int 198 aplgpio_parse_resources(int crsidx, union acpi_resource *crs, void *arg) 199 { 200 struct aplgpio_softc *sc = arg; 201 int type = AML_CRSTYPE(crs); 202 203 switch (type) { 204 case LR_MEM32FIXED: 205 sc->sc_addr = crs->lr_m32fixed._bas; 206 sc->sc_size = crs->lr_m32fixed._len; 207 break; 208 case LR_EXTIRQ: 209 sc->sc_irq = crs->lr_extirq.irq[0]; 210 sc->sc_irq_flags = crs->lr_extirq.flags; 211 break; 212 default: 213 printf(" type 0x%x\n", type); 214 break; 215 } 216 217 return 0; 218 } 219 220 int 221 aplgpio_read_pin(void *cookie, int pin) 222 { 223 struct aplgpio_softc *sc = cookie; 224 uint32_t reg; 225 226 reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, 227 APLGPIO_PAD_CFG0 + pin * 8); 228 229 return !!(reg & APLGPIO_CONF_RXSTATE); 230 } 231 232 void 233 aplgpio_write_pin(void *cookie, int pin, int value) 234 { 235 struct aplgpio_softc *sc = cookie; 236 uint32_t reg; 237 238 reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, 239 APLGPIO_PAD_CFG0 + pin * 8); 240 if (value) 241 reg |= APLGPIO_CONF_TXSTATE; 242 else 243 reg &= ~APLGPIO_CONF_TXSTATE; 244 bus_space_write_4(sc->sc_memt, sc->sc_memh, 245 APLGPIO_PAD_CFG0 + pin * 8, reg); 246 } 247 248 void 249 aplgpio_intr_establish(void *cookie, int pin, int flags, 250 int (*func)(void *), void *arg) 251 { 252 struct aplgpio_softc *sc = cookie; 253 uint32_t reg; 254 255 KASSERT(pin >= 0 && pin < sc->sc_npins); 256 257 sc->sc_pin_ih[pin].ih_func = func; 258 sc->sc_pin_ih[pin].ih_arg = arg; 259 260 reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, 261 APLGPIO_PAD_CFG0 + pin * 8); 262 reg &= ~(APLGPIO_CONF_RXEV_MASK | APLGPIO_CONF_RXINV); 263 if ((flags & LR_GPIO_MODE) == 1) 264 reg |= APLGPIO_CONF_RXEV_EDGE; 265 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTLO) 266 reg |= APLGPIO_CONF_RXINV; 267 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTBOTH) 268 reg |= APLGPIO_CONF_RXEV_EDGE | APLGPIO_CONF_RXEV_ZERO; 269 bus_space_write_4(sc->sc_memt, sc->sc_memh, 270 APLGPIO_PAD_CFG0 + pin * 8, reg); 271 272 reg = bus_space_read_4(sc->sc_memt, sc->sc_memh, 273 APLGPIO_IRQ_EN + (pin / 32) * 4); 274 reg |= (1 << (pin % 32)); 275 bus_space_write_4(sc->sc_memt, sc->sc_memh, 276 APLGPIO_IRQ_EN + (pin / 32) * 4, reg); 277 } 278 279 int 280 aplgpio_intr(void *arg) 281 { 282 struct aplgpio_softc *sc = arg; 283 uint32_t status, enable; 284 int rc = 0; 285 int pin; 286 287 for (pin = 0; pin < sc->sc_npins; pin++) { 288 if (pin % 32 == 0) { 289 status = bus_space_read_4(sc->sc_memt, sc->sc_memh, 290 APLGPIO_IRQ_STS + (pin / 32) * 4); 291 bus_space_write_4(sc->sc_memt, sc->sc_memh, 292 APLGPIO_IRQ_STS + (pin / 32) * 4, status); 293 enable = bus_space_read_4(sc->sc_memt, sc->sc_memh, 294 APLGPIO_IRQ_EN + (pin / 32) * 4); 295 status &= enable; 296 } 297 if (status & (1 << (pin % 32))) { 298 if (sc->sc_pin_ih[pin].ih_func) 299 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg); 300 rc = 1; 301 } 302 } 303 return rc; 304 } 305