1#include "opt_gemini.h" 2#if !defined(GEMINI_MASTER) && !defined(GEMINI_SLAVE) 3# error IPI needs GEMINI_MASTER or GEMINI_SLAVE 4#endif 5#include "locators.h" 6#include "geminiipm.h" 7 8#include <sys/cdefs.h> 9 10__KERNEL_RCSID(0, "$NetBSD: gemini_ipi.c,v 1.11 2021/08/07 16:18:44 thorpej Exp $"); 11 12#include <sys/param.h> 13#include <sys/systm.h> 14#include <sys/device.h> 15#include <sys/intr.h> 16#include <sys/kmem.h> 17#include <arch/arm/gemini/gemini_obiovar.h> 18#include <arch/arm/gemini/gemini_ipivar.h> 19#include <arch/arm/gemini/gemini_reg.h> 20 21static int gemini_ipi_match(device_t, cfdata_t, void *); 22static void gemini_ipi_attach(device_t, device_t, void *); 23static int gemini_ipiintr(void *); 24 25CFATTACH_DECL_NEW(geminiipi, sizeof(struct gemini_ipi_softc), 26 gemini_ipi_match, gemini_ipi_attach, NULL, NULL); 27 28static gemini_ipi_softc_t *gemini_ipi_sc; 29 30 31static int 32gemini_ipi_match(device_t parent, cfdata_t cf, void *aux) 33{ 34 struct obio_attach_args *obio = aux; 35 36 if (obio->obio_intr == LPCCF_INTR_DEFAULT) 37 panic("ipi must specify intr in config."); 38 39 return 1; 40} 41 42static void 43gemini_ipi_attach(device_t parent, device_t self, void *aux) 44{ 45 gemini_ipi_softc_t *sc = device_private(self); 46 struct obio_attach_args *obio = aux; 47 bus_space_tag_t iot; 48 bus_space_handle_t ioh; 49 bus_size_t size; 50 bus_addr_t addr; 51 void *ih; 52 53 iot = obio->obio_iot; 54 addr = GEMINI_GLOBAL_BASE; 55 size = 4096; /* XXX */ 56 57 if (bus_space_map(iot, addr, size, 0, &ioh)) 58 panic("%s: Cannot map registers", device_xname(self)); 59 60 /* 61 * NOTE: we are using IPL_NET, not IPL_HIGH use of IPI on this system 62 * is (mainly) networking keep simple (for now) and force all IPIs 63 * to same level so splnet() can block them as any other NIC. 64 */ 65#if 0 66 ih = intr_establish(obio->obio_intr, IPL_NET, IST_LEVEL_HIGH, 67 gemini_ipiintr, sc); 68#else 69 ih = intr_establish(obio->obio_intr, IPL_NET, IST_EDGE_RISING, 70 gemini_ipiintr, sc); 71#endif 72 if (ih == NULL) 73 panic("%s: Cannot establish interrupt %d\n", 74 device_xname(self), obio->obio_intr); 75 76 SIMPLEQ_INIT(&sc->sc_intrq); 77 78 sc->sc_iot = iot; 79 sc->sc_ioh = ioh; 80 sc->sc_addr = addr; 81 sc->sc_size = size; 82 sc->sc_intr = obio->obio_intr; 83 sc->sc_ih = ih; 84 85 gemini_ipi_sc = sc; 86 87 aprint_normal("\n"); 88 aprint_naive("\n"); 89 90#if NGEMINIIPM > 0 91 config_found(self, __UNCONST("geminiipm"), NULL, CFARGS_NONE); 92#endif 93} 94 95static inline int 96gemini_ipi_intrq_empty(gemini_ipi_softc_t *sc) 97{ 98 return SIMPLEQ_EMPTY(&sc->sc_intrq); 99} 100 101static inline void * 102gemini_ipi_intrq_insert(gemini_ipi_softc_t *sc, int (*func)(void *), void *arg) 103{ 104 gemini_ipi_intrq_t *iqp; 105 106 iqp = kmem_zalloc(sizeof(*iqp), KM_SLEEP); 107 iqp->iq_func = func; 108 iqp->iq_arg = arg; 109 SIMPLEQ_INSERT_TAIL(&sc->sc_intrq, iqp, iq_q); 110 111 return (void *)iqp; 112} 113 114static inline void 115gemini_ipi_intrq_remove(gemini_ipi_softc_t *sc, void *cookie) 116{ 117 gemini_ipi_intrq_t *iqp; 118 119 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) { 120 if ((void *)iqp == cookie) { 121 SIMPLEQ_REMOVE(&sc->sc_intrq, 122 iqp, gemini_ipi_intrq, iq_q); 123 kmem_free(iqp, sizeof(*iqp)); 124 return; 125 } 126 } 127} 128 129static inline int 130gemini_ipi_intrq_dispatch(gemini_ipi_softc_t *sc) 131{ 132 gemini_ipi_intrq_t *iqp; 133 int rv = 0; 134 135 SIMPLEQ_FOREACH(iqp, &sc->sc_intrq, iq_q) 136 rv |= (*iqp->iq_func)(iqp->iq_arg); 137 138 return (rv != 0); 139} 140 141 142void * 143ipi_intr_establish(int (*func)(void *), void *arg) 144{ 145 gemini_ipi_softc_t *sc = gemini_ipi_sc; 146 void *ih; 147 148 if (sc == NULL) 149 return NULL; 150 151 ih = gemini_ipi_intrq_insert(sc, func, arg); 152 return ih; 153} 154 155void 156ipi_intr_disestablish(void *ih) 157{ 158 gemini_ipi_softc_t *sc = gemini_ipi_sc; 159 160 if (sc == NULL) 161 panic("NULL gemini_ipi_sc"); 162 163 gemini_ipi_intrq_remove(sc, ih); 164} 165 166int 167ipi_send(void) 168{ 169 gemini_ipi_softc_t *sc = gemini_ipi_sc; 170 uint32_t r; 171 uint32_t bit; 172 bus_addr_t off; 173 174 if (sc == NULL) 175 return -1; 176 177#if defined(GEMINI_MASTER) 178 off = GEMINI_GLOBAL_CPU0; 179 bit = GLOBAL_CPU0_IPICPU1; 180#elif defined(GEMINI_SLAVE) 181 off = GEMINI_GLOBAL_CPU1; 182 bit = GLOBAL_CPU1_IPICPU0; 183#endif 184 185 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off); 186 r |= bit; 187 bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r); 188 189 return 0; 190} 191 192static inline void 193ipi_ack(gemini_ipi_softc_t *sc) 194{ 195 uint32_t r; 196 uint32_t bit; 197 bus_addr_t off; 198 199#if defined(GEMINI_MASTER) 200 off = GEMINI_GLOBAL_CPU1; 201 bit = GLOBAL_CPU1_IPICPU0; 202#elif defined(GEMINI_SLAVE) 203 off = GEMINI_GLOBAL_CPU0; 204 bit = GLOBAL_CPU0_IPICPU1; 205#endif 206 207 r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, off); 208 r &= ~bit; 209 bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, r); 210} 211 212static int 213gemini_ipiintr(void *arg) 214{ 215 gemini_ipi_softc_t *sc = arg; 216 int rv; 217 218 if (sc == NULL) 219 return -1; 220 221 ipi_ack(sc); 222 223 rv = gemini_ipi_intrq_dispatch(sc); 224 225 return rv; 226} 227