1 /* $NetBSD: gemini_icu.c,v 1.6 2020/09/26 10:06:25 skrll Exp $ */ 2 3 /* adapted from: 4 * NetBSD: omap2_icu.c,v 1.4 2008/08/27 11:03:10 matt Exp 5 */ 6 7 /* 8 * Define the SDP2430 specific information and then include the generic OMAP 9 * interrupt header. 10 */ 11 12 /* 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain this list of conditions 17 * and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce this list of conditions 19 * and the following disclaimer in the documentation and/or other materials 20 * provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY 25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include "opt_gemini.h" 34 #include "geminiicu.h" 35 36 #define _INTR_PRIVATE 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: gemini_icu.c,v 1.6 2020/09/26 10:06:25 skrll Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/evcnt.h> 43 44 #include <uvm/uvm_extern.h> 45 46 #include <machine/intr.h> 47 #include <sys/bus.h> 48 49 #include <arm/cpu.h> 50 #include <arm/armreg.h> 51 #include <arm/cpufunc.h> 52 53 #include <arm/pic/picvar.h> 54 55 #include <arm/gemini/gemini_reg.h> 56 #include <arm/gemini/gemini_obiovar.h> 57 58 59 #define INTC_READ(sc, o) \ 60 bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o)) 61 #define INTC_WRITE(sc, o, v) \ 62 bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), v) 63 64 static int geminiicu_match(device_t, cfdata_t, void *); 65 static void geminiicu_attach(device_t, device_t, void *); 66 67 static void geminiicu_unblock_irqs(struct pic_softc *, size_t, uint32_t); 68 static void geminiicu_block_irqs(struct pic_softc *, size_t, uint32_t); 69 static void geminiicu_establish_irq(struct pic_softc *, struct intrsource *); 70 static void geminiicu_source_name(struct pic_softc *, int, char *, size_t); 71 72 static const struct pic_ops geminiicu_picops = { 73 .pic_unblock_irqs = geminiicu_unblock_irqs, 74 .pic_block_irqs = geminiicu_block_irqs, 75 .pic_establish_irq = geminiicu_establish_irq, 76 .pic_source_name = geminiicu_source_name, 77 }; 78 79 #define PICTOSOFTC(pic) \ 80 ((void *)((uintptr_t)(pic) - offsetof(struct geminiicu_softc, sc_pic))) 81 82 static struct geminiicu_softc { 83 device_t sc_dev; 84 bus_space_tag_t sc_memt; 85 bus_space_handle_t sc_memh; 86 struct pic_softc sc_pic; 87 uint32_t sc_enabled_mask; 88 uint32_t sc_edge_mask; 89 uint32_t sc_edge_rising_mask; 90 uint32_t sc_edge_falling_mask; 91 uint32_t sc_level_mask; 92 uint32_t sc_level_hi_mask; 93 uint32_t sc_level_lo_mask; 94 } geminiicu_softc = { 95 .sc_pic = { 96 .pic_ops = &geminiicu_picops, 97 .pic_maxsources = 32, 98 .pic_name = "geminiicu", 99 }, 100 }; 101 102 static const char * const sources[32] = { 103 "ipi(0)", "gmac0(1)", "gmac1(2)", "wdt(3)", 104 "ide0(4)", "ide1(5)", "raid(6)", "crypto(7)", 105 "pci(8)", "dma(9)", "usb0(10)", "usb1(11)", 106 "flash(12)", "tve(13)", "timer0(14)", "timer1(15)", 107 "timer2(16)", "rtc(17)", "uart(18)", "lcd(19)", 108 "lpc(20)", "ssp(21)", "gpio0(22)", "gpio1(23)", 109 "gpio2(24)", "cir(25)", "power(26)", "irq 27", 110 "irq 28", "irq 29", "usbc0(30)", "usbc1(31)" 111 }; 112 113 static void geminiicu_source_name(struct pic_softc *pic, int irq, 114 char *buf, size_t len) 115 { 116 KASSERT((unsigned int)irq < 32); 117 strlcpy(buf, sources[irq], len); 118 } 119 120 static void 121 geminiicu_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) 122 { 123 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 124 KASSERT(irqbase == 0 && (irq_mask & sc->sc_enabled_mask) == 0); 125 sc->sc_enabled_mask |= irq_mask; 126 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 127 /* 128 * If this is a level source, ack it now. If it's still asserted 129 * it'll come back. 130 */ 131 if (irq_mask & sc->sc_level_mask) 132 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 133 irq_mask & sc->sc_level_mask); 134 } 135 136 static void 137 geminiicu_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) 138 { 139 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 140 KASSERT(irqbase == 0); 141 142 sc->sc_enabled_mask &= ~irq_mask; 143 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 144 /* 145 * If any of the source are edge triggered, ack them now so 146 * we won't lose them. 147 */ 148 if (irq_mask & sc->sc_edge_mask) 149 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 150 irq_mask & sc->sc_edge_mask); 151 } 152 153 /* 154 * Called with interrupts disabled 155 */ 156 static int 157 find_pending_irqs(struct geminiicu_softc *sc) 158 { 159 uint32_t pending = INTC_READ(sc, GEMINI_ICU_IRQ_STATUS); 160 161 KASSERT((sc->sc_enabled_mask & pending) == pending); 162 163 if (pending == 0) 164 return 0; 165 166 return pic_mark_pending_sources(&sc->sc_pic, 0, pending); 167 } 168 169 void 170 gemini_irq_handler(void *frame) 171 { 172 struct cpu_info * const ci = curcpu(); 173 struct geminiicu_softc * const sc = &geminiicu_softc; 174 const int oldipl = ci->ci_cpl; 175 const uint32_t oldipl_mask = __BIT(oldipl); 176 int ipl_mask = 0; 177 178 ci->ci_data.cpu_nintr++; 179 180 KASSERT(sc->sc_enabled_mask != 0); 181 182 ipl_mask = find_pending_irqs(sc); 183 184 /* 185 * Record the pending_ipls and deliver them if we can. 186 */ 187 if ((ipl_mask & ~oldipl_mask) > oldipl_mask) 188 pic_do_pending_ints(I32_bit, oldipl, frame); 189 } 190 191 void 192 geminiicu_establish_irq(struct pic_softc *pic, struct intrsource *is) 193 { 194 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 195 const uint32_t irq_mask = __BIT(is->is_irq); 196 197 KASSERT(is->is_irq < 32); 198 199 sc->sc_enabled_mask &= ~irq_mask; 200 /* Have to do with this interrupt disabled. */ 201 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 202 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, irq_mask); 203 204 sc->sc_edge_rising_mask &= ~irq_mask; 205 sc->sc_edge_falling_mask &= ~irq_mask; 206 sc->sc_level_lo_mask &= ~irq_mask; 207 sc->sc_level_hi_mask &= ~irq_mask; 208 209 switch (is->is_type) { 210 case IST_LEVEL_LOW: sc->sc_level_lo_mask |= irq_mask; break; 211 case IST_LEVEL_HIGH: sc->sc_level_hi_mask |= irq_mask; break; 212 case IST_EDGE_FALLING: sc->sc_edge_falling_mask |= irq_mask; break; 213 case IST_EDGE_RISING: sc->sc_edge_rising_mask |= irq_mask; break; 214 } 215 216 sc->sc_edge_mask = sc->sc_edge_rising_mask | sc->sc_edge_falling_mask; 217 sc->sc_level_mask = sc->sc_level_hi_mask|sc->sc_level_lo_mask; 218 219 /* 220 * Set the new interrupt mode. 221 */ 222 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, sc->sc_edge_mask); 223 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 224 sc->sc_level_lo_mask | sc->sc_edge_falling_mask); 225 } 226 227 int 228 geminiicu_match(device_t parent, cfdata_t cf, void *aux) 229 { 230 struct obio_attach_args * const oa = aux; 231 232 #if defined(SL3516) 233 if ((oa->obio_addr == GEMINI_IC0_BASE) 234 || (oa->obio_addr == GEMINI_IC1_BASE)) 235 return 1; 236 237 return 0; 238 #else 239 #error unsupported GEMINI variant 240 #endif 241 } 242 243 void 244 geminiicu_attach(device_t parent, device_t self, void *aux) 245 { 246 struct obio_attach_args * const oa = aux; 247 struct geminiicu_softc * const sc = &geminiicu_softc; 248 int error; 249 250 aprint_normal("\n"); 251 252 sc->sc_memt = oa->obio_iot; 253 254 error = bus_space_map(sc->sc_memt, oa->obio_addr, 0x1000, 0, 255 &sc->sc_memh); 256 if (error) 257 panic("failed to map interrupt registers: %d", error); 258 259 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, 0); 260 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 0xffffffff); 261 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, 0); 262 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 0xffffffff); 263 264 sc->sc_dev = self; 265 self->dv_private = sc; 266 267 pic_add(&sc->sc_pic, 0); 268 } 269 270 CFATTACH_DECL_NEW(geminiicu, 271 0, 272 geminiicu_match, geminiicu_attach, 273 NULL, NULL); 274