1 /* $OpenBSD: mvicu.c,v 1.2 2018/03/29 18:11:55 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 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 22 #include <machine/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/fdt.h> 28 29 /* Registers. */ 30 #define ICU_SETSPI_NSR_AL 0x10 31 #define ICU_SETSPI_NSR_AH 0x14 32 #define ICU_CLRSPI_NSR_AL 0x18 33 #define ICU_CLRSPI_NSR_AH 0x1c 34 #define ICU_INT_CFG(x) (0x100 + (x) * 4) 35 #define ICU_INT_ENABLE (1 << 24) 36 #define ICU_INT_EDGE (1 << 28) 37 #define ICU_INT_GROUP_SHIFT 29 38 #define ICU_INT_MASK 0x3ff 39 40 #define HREAD4(sc, reg) \ 41 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 42 #define HWRITE4(sc, reg, val) \ 43 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 44 45 struct mvicu_softc { 46 struct device sc_dev; 47 bus_space_tag_t sc_iot; 48 bus_space_handle_t sc_ioh; 49 50 uint32_t sc_spi_ranges[4]; 51 52 struct interrupt_controller sc_ic; 53 struct interrupt_controller *sc_parent_ic; 54 }; 55 56 int mvicu_match(struct device *, void *, void *); 57 void mvicu_attach(struct device *, struct device *, void *); 58 59 struct cfattach mvicu_ca = { 60 sizeof (struct mvicu_softc), mvicu_match, mvicu_attach 61 }; 62 63 struct cfdriver mvicu_cd = { 64 NULL, "mvicu", DV_DULL 65 }; 66 67 void *mvicu_intr_establish(void *, int *, int, int (*)(void *), 68 void *, char *); 69 void mvicu_intr_disestablish(void *); 70 71 int 72 mvicu_match(struct device *parent, void *match, void *aux) 73 { 74 struct fdt_attach_args *faa = aux; 75 76 return OF_is_compatible(faa->fa_node, "marvell,cp110-icu"); 77 } 78 79 void 80 mvicu_attach(struct device *parent, struct device *self, void *aux) 81 { 82 struct mvicu_softc *sc = (struct mvicu_softc *)self; 83 struct fdt_attach_args *faa = aux; 84 struct interrupt_controller *ic; 85 bus_addr_t low, high, setspi_addr, clrspi_addr; 86 uint32_t phandle; 87 int node; 88 89 if (faa->fa_nreg < 1) { 90 printf(": no registers\n"); 91 return; 92 } 93 94 phandle = OF_getpropint(faa->fa_node, "msi-parent", 0); 95 node = OF_getnodebyphandle(phandle); 96 if (node == 0) { 97 printf(": GICP not found\n"); 98 return; 99 } 100 OF_getpropintarray(node, "marvell,spi-ranges", sc->sc_spi_ranges, 101 sizeof(sc->sc_spi_ranges)); 102 103 sc->sc_iot = faa->fa_iot; 104 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 105 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 106 printf(": can't map registers\n"); 107 return; 108 } 109 110 /* 111 * This driver assumes that the ICU has been configured by the 112 * firmware. Do some (minimal) checks to verify that has 113 * indeed been done. 114 */ 115 low = HREAD4(sc, ICU_SETSPI_NSR_AL); 116 high = HREAD4(sc, ICU_SETSPI_NSR_AH); 117 setspi_addr = (high << 32) | low; 118 low = HREAD4(sc, ICU_CLRSPI_NSR_AL); 119 high = HREAD4(sc, ICU_CLRSPI_NSR_AH); 120 clrspi_addr = (high << 32) | low; 121 if (setspi_addr == 0 || clrspi_addr == 0) { 122 printf(": not configured by firmware\n"); 123 return; 124 } 125 126 printf("\n"); 127 128 extern uint32_t arm_intr_get_parent(int); 129 phandle = arm_intr_get_parent(node); 130 extern LIST_HEAD(, interrupt_controller) interrupt_controllers; 131 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 132 if (ic->ic_phandle == phandle) 133 break; 134 } 135 sc->sc_parent_ic = ic; 136 137 sc->sc_ic.ic_node = faa->fa_node; 138 sc->sc_ic.ic_cookie = sc; 139 sc->sc_ic.ic_establish = mvicu_intr_establish; 140 sc->sc_ic.ic_disestablish = mvicu_intr_disestablish; 141 arm_intr_register_fdt(&sc->sc_ic); 142 } 143 144 void * 145 mvicu_intr_establish(void *cookie, int *cell, int level, 146 int (*func)(void *), void *arg, char *name) 147 { 148 struct mvicu_softc *sc = cookie; 149 struct interrupt_controller *ic = sc->sc_parent_ic; 150 uint32_t group = cell[0]; 151 uint32_t idx = cell[1]; 152 uint32_t interrupt[3]; 153 uint32_t reg; 154 int i; 155 156 if (ic == NULL) 157 return NULL; 158 159 reg = HREAD4(sc, ICU_INT_CFG(idx)); 160 if ((reg & ICU_INT_ENABLE) == 0 || 161 (reg >> ICU_INT_GROUP_SHIFT) != group) 162 return NULL; 163 164 /* Convert to GIC interrupt source. */ 165 idx = reg & ICU_INT_MASK; 166 for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) { 167 if (idx < sc->sc_spi_ranges[i + 1]) { 168 idx += sc->sc_spi_ranges[i]; 169 break; 170 } 171 idx -= sc->sc_spi_ranges[i]; 172 } 173 if (i == nitems(sc->sc_spi_ranges)) 174 return NULL; 175 interrupt[0] = 0; 176 interrupt[1] = idx - 32; 177 interrupt[2] = cell[2]; 178 return ic->ic_establish(ic->ic_cookie, interrupt, level, 179 func, arg, name); 180 } 181 182 void 183 mvicu_intr_disestablish(void *cookie) 184 { 185 panic("%s", __func__); 186 } 187