1 /* $OpenBSD: mvmpic.c,v 1.5 2021/10/24 17:52:27 mpi Exp $ */ 2 /* 3 * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se> 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/systm.h> 21 #include <sys/malloc.h> 22 #include <sys/device.h> 23 #include <sys/evcount.h> 24 25 #include <arm/cpufunc.h> 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/fdt.h> 31 32 #define MPIC_CTRL 0x000 /* control register */ 33 #define MPIC_CTRL_PRIO_EN 0x1 34 #define MPIC_SOFTINT 0x004 /* software triggered interrupt register */ 35 #define MPIC_INTERR 0x020 /* SOC main interrupt error cause register */ 36 #define MPIC_ISE 0x030 /* interrupt set enable */ 37 #define MPIC_ICE 0x034 /* interrupt clear enable */ 38 #define MPIC_ISCR(x) (0x100 + (4 * x)) /* interrupt x source control register */ 39 #define MPIC_ISCR_PRIO_SHIFT 24 40 #define MPIC_ISCR_INTEN (1 << 28) 41 42 #define MPIC_DOORBELL_CAUSE 0x008 43 #define MPIC_CTP 0x040 /* current task priority */ 44 #define MPIC_CTP_SHIFT 28 45 #define MPIC_IACK 0x044 /* interrupt acknowledge */ 46 #define MPIC_ISM 0x048 /* set mask */ 47 #define MPIC_ICM 0x04c /* clear mask */ 48 49 struct mpic_softc { 50 struct device sc_dev; 51 bus_space_tag_t sc_iot; 52 bus_space_handle_t sc_m_ioh, sc_c_ioh; 53 int sc_node; 54 55 struct intrhand **sc_handlers; 56 int sc_ipl; 57 int sc_nintr; 58 struct evcount sc_spur; 59 struct interrupt_controller sc_intc; 60 void *sc_ih; 61 }; 62 63 struct intrhand { 64 int (*ih_func)(void *); /* handler */ 65 void *ih_arg; /* arg for handler */ 66 int ih_ipl; /* IPL_* */ 67 int ih_irq; /* IRQ number */ 68 struct evcount ih_count; 69 char *ih_name; 70 void *ih_sc; 71 }; 72 73 int mpic_match(struct device *, void *, void *); 74 void mpic_attach(struct device *, struct device *, void *); 75 void mpic_calc_mask(struct mpic_softc *); 76 void *mpic_intr_establish(void *, int *, int, struct cpu_info *, 77 int (*)(void *), void *, char *); 78 void mpic_intr_disestablish(void *); 79 int mpic_intr(void *); 80 void mpic_set_priority(struct mpic_softc *, int, int); 81 void mpic_intr_enable(struct mpic_softc *, int); 82 void mpic_intr_disable(struct mpic_softc *, int); 83 84 const struct cfattach mvmpic_ca = { 85 sizeof (struct mpic_softc), mpic_match, mpic_attach 86 }; 87 88 struct cfdriver mvmpic_cd = { 89 NULL, "mvmpic", DV_DULL 90 }; 91 92 int 93 mpic_match(struct device *parent, void *cfdata, void *aux) 94 { 95 struct fdt_attach_args *faa = aux; 96 97 return OF_is_compatible(faa->fa_node, "marvell,mpic"); 98 } 99 100 void 101 mpic_attach(struct device *parent, struct device *self, void *args) 102 { 103 struct mpic_softc *sc = (struct mpic_softc *)self; 104 struct fdt_attach_args *faa = args; 105 int i; 106 107 sc->sc_node = faa->fa_node; 108 sc->sc_iot = faa->fa_iot; 109 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 110 faa->fa_reg[0].size, 0, &sc->sc_m_ioh)) 111 panic("%s: main bus_space_map failed!", __func__); 112 113 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, 114 faa->fa_reg[1].size, 0, &sc->sc_c_ioh)) 115 panic("%s: cpu bus_space_map failed!", __func__); 116 117 evcount_attach(&sc->sc_spur, "irq1023/spur", NULL); 118 119 sc->sc_nintr = (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh, 120 MPIC_CTRL) >> 2) & 0x3ff; 121 printf(" nirq %d\n", sc->sc_nintr); 122 123 /* Disable all interrupts */ 124 for (i = 0; i < sc->sc_nintr; i++) { 125 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, i); 126 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, i); 127 } 128 129 /* Clear pending IPIs */ 130 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_DOORBELL_CAUSE, 0); 131 132 /* Enable hardware priorization selection */ 133 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_CTRL, 134 MPIC_CTRL_PRIO_EN); 135 136 /* Always allow everything. */ 137 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP, 138 (bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_CTP) & 139 ~(0xf << MPIC_CTP_SHIFT)) | (IPL_NONE << MPIC_CTP_SHIFT)); 140 141 sc->sc_handlers = mallocarray(sc->sc_nintr, 142 sizeof(*sc->sc_handlers), M_DEVBUF, M_ZERO | M_NOWAIT); 143 144 sc->sc_ipl = IPL_NONE; 145 mpic_calc_mask(sc); 146 147 sc->sc_intc.ic_node = faa->fa_node; 148 sc->sc_intc.ic_cookie = sc; 149 sc->sc_intc.ic_establish = mpic_intr_establish; 150 arm_intr_register_fdt(&sc->sc_intc); 151 } 152 153 void 154 mpic_set_priority(struct mpic_softc *sc, int irq, int pri) 155 { 156 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq), 157 (bus_space_read_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISCR(irq)) & 158 ~(0xf << MPIC_ISCR_PRIO_SHIFT)) | (pri << MPIC_ISCR_PRIO_SHIFT)); 159 } 160 161 void 162 mpic_intr_enable(struct mpic_softc *sc, int irq) 163 { 164 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ISE, irq); 165 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ICM, irq); 166 } 167 168 void 169 mpic_intr_disable(struct mpic_softc *sc, int irq) 170 { 171 bus_space_write_4(sc->sc_iot, sc->sc_m_ioh, MPIC_ICE, irq); 172 bus_space_write_4(sc->sc_iot, sc->sc_c_ioh, MPIC_ISM, irq); 173 } 174 175 void 176 mpic_calc_mask(struct mpic_softc *sc) 177 { 178 struct intrhand *ih; 179 int irq; 180 int max = IPL_NONE; 181 int min = IPL_HIGH; 182 183 for (irq = 0; irq < sc->sc_nintr; irq++) { 184 ih = sc->sc_handlers[irq]; 185 if (ih == NULL) 186 continue; 187 188 if (ih->ih_ipl > max) 189 max = ih->ih_ipl; 190 191 if (ih->ih_ipl < min) 192 min = ih->ih_ipl; 193 } 194 195 if (max == IPL_NONE) 196 min = IPL_NONE; 197 198 if (sc->sc_ipl != min) { 199 sc->sc_ipl = min; 200 201 if (sc->sc_ih != NULL) 202 arm_intr_disestablish_fdt(sc->sc_ih); 203 204 if (sc->sc_ipl != IPL_NONE) 205 sc->sc_ih = arm_intr_establish_fdt(sc->sc_node, 206 sc->sc_ipl, mpic_intr, sc, sc->sc_dev.dv_xname); 207 } 208 } 209 210 int 211 mpic_intr(void *cookie) 212 { 213 struct mpic_softc *sc = cookie; 214 struct intrhand *ih; 215 int irq, s; 216 217 irq = bus_space_read_4(sc->sc_iot, sc->sc_c_ioh, MPIC_IACK) & 0x3ff; 218 219 if (irq == 1023) { 220 sc->sc_spur.ec_count++; 221 return 1; 222 } 223 224 if (irq >= sc->sc_nintr) 225 return 1; 226 227 if ((ih = sc->sc_handlers[irq]) != NULL) { 228 s = splraise(ih->ih_ipl); 229 if (ih->ih_func(ih->ih_arg)) 230 ih->ih_count.ec_count++; 231 splx(s); 232 } 233 234 return 1; 235 } 236 237 void * 238 mpic_intr_establish(void *cookie, int *cells, int level, 239 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 240 { 241 struct mpic_softc *sc = cookie; 242 struct intrhand *ih; 243 int psw; 244 int irqno = cells[0]; 245 246 if (irqno < 0 || irqno >= sc->sc_nintr) 247 panic("%s: bogus irqnumber %d: %s", __func__, 248 irqno, name); 249 250 if (sc->sc_handlers[irqno] != NULL) 251 panic("%s: irq %d already registered" , __func__, 252 irqno); 253 254 if (ci != NULL && !CPU_IS_PRIMARY(ci)) 255 return NULL; 256 257 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 258 ih->ih_func = func; 259 ih->ih_arg = arg; 260 ih->ih_ipl = level; 261 ih->ih_irq = irqno; 262 ih->ih_name = name; 263 ih->ih_sc = sc; 264 265 psw = disable_interrupts(PSR_I); 266 267 sc->sc_handlers[irqno] = ih; 268 269 if (name != NULL) 270 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 271 272 #ifdef DEBUG_INTC 273 printf("%s: irq %d level %d [%s]\n", __func__, irqno, level, name); 274 #endif 275 276 mpic_calc_mask(sc); 277 mpic_set_priority(sc, irqno, level); 278 mpic_intr_enable(sc, irqno); 279 280 restore_interrupts(psw); 281 return (ih); 282 } 283 284 void 285 mpic_intr_disestablish(void *cookie) 286 { 287 struct intrhand *ih = cookie; 288 struct mpic_softc *sc = ih->ih_sc; 289 int psw; 290 291 psw = disable_interrupts(PSR_I); 292 293 #ifdef DEBUG_INTC 294 printf("%s: irq %d ipl %d [%s]\n", __func__, ih->ih_irq, ih->ih_ipl, 295 ih->ih_name); 296 #endif 297 298 mpic_intr_disable(sc, ih->ih_irq); 299 300 sc->sc_handlers[ih->ih_irq] = NULL; 301 if (ih->ih_name != NULL) 302 evcount_detach(&ih->ih_count); 303 free(ih, M_DEVBUF, sizeof(*ih)); 304 305 mpic_calc_mask(sc); 306 307 restore_interrupts(psw); 308 } 309