qcpdc.c revision 1.2
1/* $OpenBSD: qcpdc.c,v 1.2 2022/12/16 18:08:08 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 *); 87 88int 89qcpdc_match(struct device *parent, void *match, void *aux) 90{ 91 struct fdt_attach_args *faa = aux; 92 93 return OF_is_compatible(faa->fa_node, "qcom,pdc"); 94} 95 96void 97qcpdc_attach(struct device *parent, struct device *self, void *aux) 98{ 99 struct qcpdc_softc *sc = (struct qcpdc_softc *)self; 100 struct fdt_attach_args *faa = aux; 101 int i, j, len; 102 103 if (faa->fa_nreg < 1) { 104 printf(": no registers\n"); 105 return; 106 } 107 108 len = OF_getproplen(faa->fa_node, "qcom,pdc-ranges"); 109 if (len <= 0 || len % (3 * sizeof(uint32_t)) != 0) { 110 printf(": invalid ranges property\n"); 111 return; 112 } 113 114 sc->sc_npr = len / (3 * sizeof(uint32_t)); 115 sc->sc_pr = mallocarray(sc->sc_npr, sizeof(*sc->sc_pr), 116 M_DEVBUF, M_WAITOK); 117 OF_getpropintarray(faa->fa_node, "qcom,pdc-ranges", 118 (uint32_t *)sc->sc_pr, len); 119 120 sc->sc_node = faa->fa_node; 121 sc->sc_iot = faa->fa_iot; 122 123 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 124 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 125 printf(": can't map registers\n"); 126 return; 127 } 128 129 for (i = 0; i < sc->sc_npr; i++) { 130 for (j = 0; j < sc->sc_pr[i].count; j++) { 131 HCLR4(sc, PDC_INTR_ENABLE(sc->sc_pr[i].pin_base + j), 132 PDC_INTR_ENABLE_BIT(sc->sc_pr[i].pin_base + j)); 133 } 134 } 135 136 sc->sc_ic.ic_node = faa->fa_node; 137 sc->sc_ic.ic_cookie = sc; 138 sc->sc_ic.ic_establish = qcpdc_intr_establish; 139 sc->sc_ic.ic_disestablish = qcpdc_intr_disestablish; 140 sc->sc_ic.ic_enable = qcpdc_intr_enable; 141 sc->sc_ic.ic_disable = qcpdc_intr_disable; 142 sc->sc_ic.ic_barrier = qcpdc_intr_barrier; 143 fdt_intr_register(&sc->sc_ic); 144 145 printf("\n"); 146} 147 148void * 149qcpdc_intr_establish(void *aux, int *cells, int ipl, 150 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 151{ 152 struct qcpdc_softc *sc = aux; 153 struct intrhand *ih; 154 void *cookie; 155 uint32_t pcells[3]; 156 int pin = cells[0]; 157 int level = cells[1]; 158 int i, s; 159 160 for (i = 0; i < sc->sc_npr; i++) { 161 if (pin >= sc->sc_pr[i].pin_base && 162 pin < sc->sc_pr[i].pin_base + sc->sc_pr[i].count) 163 break; 164 } 165 if (i == sc->sc_npr) 166 return NULL; 167 168 switch (level) { 169 case 1: 170 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_RISING); 171 break; 172 case 2: 173 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_FALLING); 174 break; 175 case 3: 176 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_EDGE_BOTH); 177 break; 178 case 4: 179 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_LEVEL_HIGH); 180 break; 181 case 8: 182 HWRITE4(sc, PDC_INTR_CONFIG(pin), PDC_INTR_CONFIG_LEVEL_LOW); 183 break; 184 default: 185 printf("%s: unsupported interrupt mode/polarity\n", 186 sc->sc_dev.dv_xname); 187 return NULL; 188 } 189 190 pcells[0] = 0; /* GIC-SPI */ 191 pcells[1] = pin - sc->sc_pr[i].pin_base + sc->sc_pr[i].gic_base; 192 pcells[2] = level; 193 194 cookie = fdt_intr_parent_establish(&sc->sc_ic, &pcells[0], ipl, ci, 195 func, arg, name); 196 if (cookie == NULL) 197 return NULL; 198 199 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 200 ih->ih_cookie = cookie; 201 ih->ih_sc = sc; 202 ih->ih_pin = pin; 203 204 s = splhigh(); 205 HSET4(sc, PDC_INTR_ENABLE(pin), PDC_INTR_ENABLE_BIT(pin)); 206 splx(s); 207 208 return ih; 209} 210 211void 212qcpdc_intr_disestablish(void *cookie) 213{ 214 struct intrhand *ih = cookie; 215 struct qcpdc_softc *sc = ih->ih_sc; 216 int s; 217 218 s = splhigh(); 219 HCLR4(sc, PDC_INTR_ENABLE(ih->ih_pin), PDC_INTR_ENABLE_BIT(ih->ih_pin)); 220 splx(s); 221 222 fdt_intr_parent_disestablish(ih->ih_cookie); 223 224 free(ih, M_DEVBUF, sizeof(*ih)); 225} 226 227void 228qcpdc_intr_enable(void *cookie) 229{ 230 struct intrhand *ih = cookie; 231 232 fdt_intr_enable(ih->ih_cookie); 233} 234 235void 236qcpdc_intr_disable(void *cookie) 237{ 238 struct intrhand *ih = cookie; 239 240 fdt_intr_disable(ih->ih_cookie); 241} 242 243void 244qcpdc_intr_barrier(void *cookie) 245{ 246 struct intrhand *ih = cookie; 247 248 intr_barrier(ih->ih_cookie); 249} 250