1/* $OpenBSD: qcpdc.c,v 1.3 2022/12/21 23:26:54 patrick Exp $ */ 2/* 3 * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> 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/systm.h> 20#include <sys/device.h> 21#include <sys/malloc.h> 22 23#include <machine/fdt.h> 24 25#include <dev/ofw/openfirm.h> 26#include <dev/ofw/fdt.h> 27 28#define PDC_INTR_ENABLE(x) (0x10 + (sizeof(uint32_t) * ((x) / 32))) 29#define PDC_INTR_ENABLE_BIT(x) (1U << ((x) % 32)) 30#define PDC_INTR_CONFIG(x) (0x110 + (sizeof(uint32_t) * (x))) 31#define PDC_INTR_CONFIG_LEVEL_LOW 0x0 32#define PDC_INTR_CONFIG_EDGE_FALLING 0x2 33#define PDC_INTR_CONFIG_LEVEL_HIGH 0x4 34#define PDC_INTR_CONFIG_EDGE_RISING 0x6 35#define PDC_INTR_CONFIG_EDGE_BOTH 0x7 36 37#define HREAD4(sc, reg) \ 38 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 39#define HWRITE4(sc, reg, val) \ 40 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 41#define HSET4(sc, reg, bits) \ 42 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 43#define HCLR4(sc, reg, bits) \ 44 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 45 46struct intrhand { 47 void *ih_cookie; 48 void *ih_sc; 49 int ih_pin; 50}; 51 52struct qcpdc_pin_region { 53 uint32_t pin_base; 54 uint32_t gic_base; 55 uint32_t count; 56}; 57 58struct qcpdc_softc { 59 struct device sc_dev; 60 bus_space_tag_t sc_iot; 61 bus_space_handle_t sc_ioh; 62 int sc_node; 63 64 struct qcpdc_pin_region *sc_pr; 65 int sc_npr; 66 67 struct interrupt_controller sc_ic; 68}; 69 70int qcpdc_match(struct device *, void *, void *); 71void qcpdc_attach(struct device *, struct device *, void *); 72 73const struct cfattach qcpdc_ca = { 74 sizeof(struct qcpdc_softc), qcpdc_match, qcpdc_attach 75}; 76 77struct cfdriver qcpdc_cd = { 78 NULL, "qcpdc", DV_DULL 79}; 80 81void *qcpdc_intr_establish(void *, int *, int, struct cpu_info *, 82 int (*)(void *), void *, char *); 83void qcpdc_intr_disestablish(void *); 84void qcpdc_intr_enable(void *); 85void qcpdc_intr_disable(void *); 86void qcpdc_intr_barrier(void *); 87void qcpdc_intr_set_wakeup(void *); 88 89int 90qcpdc_match(struct device *parent, void *match, void *aux) 91{ 92 struct fdt_attach_args *faa = aux; 93 94 return OF_is_compatible(faa->fa_node, "qcom,pdc"); 95} 96 97void 98qcpdc_attach(struct device *parent, struct device *self, void *aux) 99{ 100 struct qcpdc_softc *sc = (struct qcpdc_softc *)self; 101 struct fdt_attach_args *faa = aux; 102 int i, j, len; 103 104 if (faa->fa_nreg < 1) { 105 printf(": no registers\n"); 106 return; 107 } 108 109 len = OF_getproplen(faa->fa_node, "qcom,pdc-ranges"); 110 if (len <= 0 || len % (3 * sizeof(uint32_t)) != 0) { 111 printf(": invalid ranges property\n"); 112 return; 113 } 114 115 sc->sc_npr = len / (3 * sizeof(uint32_t)); 116 sc->sc_pr = mallocarray(sc->sc_npr, sizeof(*sc->sc_pr), 117 M_DEVBUF, M_WAITOK); 118 OF_getpropintarray(faa->fa_node, "qcom,pdc-ranges", 119 (uint32_t *)sc->sc_pr, len); 120 121 sc->sc_node = faa->fa_node; 122 sc->sc_iot = faa->fa_iot; 123 124 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 125 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 126 printf(": can't map registers\n"); 127 return; 128 } 129 130 for (i = 0; i < sc->sc_npr; i++) { 131 for (j = 0; j < sc->sc_pr[i].count; j++) { 132 HCLR4(sc, PDC_INTR_ENABLE(sc->sc_pr[i].pin_base + j), 133 PDC_INTR_ENABLE_BIT(sc->sc_pr[i].pin_base + j)); 134 } 135 } 136 137 sc->sc_ic.ic_node = faa->fa_node; 138 sc->sc_ic.ic_cookie = sc; 139 sc->sc_ic.ic_establish = qcpdc_intr_establish; 140 sc->sc_ic.ic_disestablish = qcpdc_intr_disestablish; 141 sc->sc_ic.ic_enable = qcpdc_intr_enable; 142 sc->sc_ic.ic_disable = qcpdc_intr_disable; 143 sc->sc_ic.ic_barrier = qcpdc_intr_barrier; 144 sc->sc_ic.ic_set_wakeup = qcpdc_intr_set_wakeup; 145 fdt_intr_register(&sc->sc_ic); 146 147 printf("\n"); 148} 149 150void * 151qcpdc_intr_establish(void *aux, int *cells, int ipl, 152 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 153{ 154 struct qcpdc_softc *sc = aux; 155 struct intrhand *ih; 156 void *cookie; 157 uint32_t pcells[3]; 158 int pin = cells[0]; 159 int level = cells[1]; 160 int i, s; 161 162 for (i = 0; i < sc->sc_npr; i++) { 163 if (pin >= sc->sc_pr[i].pin_base && 164 pin < sc->sc_pr[i].pin_base + sc->sc_pr[i].count) 165 break; 166 } 167 if (i == sc->sc_npr) 168 return NULL; 169 170 switch (level) { 171 case 1: 172 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_RISING); 173 break; 174 case 2: 175 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_FALLING); 176 break; 177 case 3: 178 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_BOTH); 179 break; 180 case 4: 181 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_LEVEL_HIGH); 182 break; 183 case 8: 184 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_LEVEL_LOW); 185 break; 186 default: 187 printf("%s: unsupported interrupt mode/polarity\n", 188 sc->sc_dev.dv_xname); 189 return NULL; 190 } 191 192 pcells[0] = 0; /* GIC-SPI */ 193 pcells[1] = pin - sc->sc_pr[i].pin_base + sc->sc_pr[i].gic_base; 194 pcells[2] = level; 195 196 cookie = fdt_intr_parent_establish(&sc->sc_ic, &pcells[0], ipl, ci, 197 func, arg, name); 198 if (cookie == NULL) 199 return NULL; 200 201 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 202 ih->ih_cookie = cookie; 203 ih->ih_sc = sc; 204 ih->ih_pin = pin; 205 206 s = splhigh(); 207 HSET4(sc, PDC_INTR_ENABLE(pin), PDC_INTR_ENABLE_BIT(pin)); 208 splx(s); 209 210 return ih; 211} 212 213void 214qcpdc_intr_disestablish(void *cookie) 215{ 216 struct intrhand *ih = cookie; 217 struct qcpdc_softc *sc = ih->ih_sc; 218 int s; 219 220 s = splhigh(); 221 HCLR4(sc, PDC_INTR_ENABLE(ih->ih_pin), PDC_INTR_ENABLE_BIT(ih->ih_pin)); 222 splx(s); 223 224 fdt_intr_parent_disestablish(ih->ih_cookie); 225 226 free(ih, M_DEVBUF, sizeof(*ih)); 227} 228 229void 230qcpdc_intr_enable(void *cookie) 231{ 232 struct intrhand *ih = cookie; 233 234 fdt_intr_enable(ih->ih_cookie); 235} 236 237void 238qcpdc_intr_disable(void *cookie) 239{ 240 struct intrhand *ih = cookie; 241 242 fdt_intr_disable(ih->ih_cookie); 243} 244 245void 246qcpdc_intr_barrier(void *cookie) 247{ 248 struct intrhand *ih = cookie; 249 250 intr_barrier(ih->ih_cookie); 251} 252 253void 254qcpdc_intr_set_wakeup(void *cookie) 255{ 256 struct intrhand *ih = cookie; 257 258 intr_set_wakeup(ih->ih_cookie); 259} 260