1 /* $NetBSD: gemini_icu.c,v 1.3 2009/06/14 23:20:35 rjs 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.3 2009/06/14 23:20:35 rjs 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 <machine/bus.h> 48 49 #include <arm/cpu.h> 50 #include <arm/armreg.h> 51 #include <arm/cpufunc.h> 52 #include <arm/atomic.h> 53 54 #include <arm/pic/picvar.h> 55 56 #include <arm/gemini/gemini_reg.h> 57 #include <arm/gemini/gemini_obiovar.h> 58 59 60 #define INTC_READ(sc, o) \ 61 bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o)) 62 #define INTC_WRITE(sc, o, v) \ 63 bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), v) 64 65 static int geminiicu_match(device_t, cfdata_t, void *); 66 static void geminiicu_attach(device_t, device_t, void *); 67 68 static void geminiicu_unblock_irqs(struct pic_softc *, size_t, uint32_t); 69 static void geminiicu_block_irqs(struct pic_softc *, size_t, uint32_t); 70 static void geminiicu_establish_irq(struct pic_softc *, struct intrsource *); 71 static void geminiicu_source_name(struct pic_softc *, int, char *, size_t); 72 73 static const struct pic_ops geminiicu_picops = { 74 .pic_unblock_irqs = geminiicu_unblock_irqs, 75 .pic_block_irqs = geminiicu_block_irqs, 76 .pic_establish_irq = geminiicu_establish_irq, 77 .pic_source_name = geminiicu_source_name, 78 }; 79 80 #define PICTOSOFTC(pic) \ 81 ((void *)((uintptr_t)(pic) - offsetof(struct geminiicu_softc, sc_pic))) 82 83 static struct geminiicu_softc { 84 device_t sc_dev; 85 bus_space_tag_t sc_memt; 86 bus_space_handle_t sc_memh; 87 struct pic_softc sc_pic; 88 uint32_t sc_enabled_mask; 89 uint32_t sc_edge_mask; 90 uint32_t sc_edge_rising_mask; 91 uint32_t sc_edge_falling_mask; 92 uint32_t sc_level_mask; 93 uint32_t sc_level_hi_mask; 94 uint32_t sc_level_lo_mask; 95 } geminiicu_softc = { 96 .sc_pic = { 97 .pic_ops = &geminiicu_picops, 98 .pic_maxsources = 32, 99 .pic_name = "geminiicu", 100 }, 101 }; 102 103 static const char * const sources[32] = { 104 "ipi(0)", "gmac0(1)", "gmac1(2)", "wdt(3)", 105 "ide0(4)", "ide1(5)", "raid(6)", "crypto(7)", 106 "pci(8)", "dma(9)", "usb0(10)", "usb1(11)", 107 "flash(12)", "tve(13)", "timer0(14)", "timer1(15)", 108 "timer2(16)", "rtc(17)", "uart(18)", "lcd(19)", 109 "lpc(20)", "ssp(21)", "gpio0(22)", "gpio1(23)", 110 "gpio2(24)", "cir(25)", "power(26)", "irq 27", 111 "irq 28", "irq 29", "usbc0(30)", "usbc1(31)" 112 }; 113 114 static void geminiicu_source_name(struct pic_softc *pic, int irq, 115 char *buf, size_t len) 116 { 117 KASSERT((unsigned int)irq < 32); 118 strlcpy(buf, sources[irq], len); 119 } 120 121 static void 122 geminiicu_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) 123 { 124 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 125 KASSERT(irqbase == 0 && (irq_mask & sc->sc_enabled_mask) == 0); 126 sc->sc_enabled_mask |= irq_mask; 127 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 128 /* 129 * If this is a level source, ack it now. If it's still asserted 130 * it'll come back. 131 */ 132 if (irq_mask & sc->sc_level_mask) 133 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 134 irq_mask & sc->sc_level_mask); 135 } 136 137 static void 138 geminiicu_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask) 139 { 140 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 141 KASSERT(irqbase == 0); 142 143 sc->sc_enabled_mask &= ~irq_mask; 144 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 145 /* 146 * If any of the source are edge triggered, ack them now so 147 * we won't lose them. 148 */ 149 if (irq_mask & sc->sc_edge_mask) 150 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 151 irq_mask & sc->sc_edge_mask); 152 } 153 154 /* 155 * Called with interrupts disabled 156 */ 157 static int 158 find_pending_irqs(struct geminiicu_softc *sc) 159 { 160 uint32_t pending = INTC_READ(sc, GEMINI_ICU_IRQ_STATUS); 161 162 KASSERT((sc->sc_enabled_mask & pending) == pending); 163 164 if (pending == 0) 165 return 0; 166 167 return pic_mark_pending_sources(&sc->sc_pic, 0, pending); 168 } 169 170 void 171 gemini_irq_handler(void *frame) 172 { 173 struct cpu_info * const ci = curcpu(); 174 struct geminiicu_softc * const sc = &geminiicu_softc; 175 const int oldipl = ci->ci_cpl; 176 const uint32_t oldipl_mask = __BIT(oldipl); 177 int ipl_mask = 0; 178 179 uvmexp.intrs++; 180 181 KASSERT(sc->sc_enabled_mask != 0); 182 183 ipl_mask = find_pending_irqs(sc); 184 185 /* 186 * Record the pending_ipls and deliver them if we can. 187 */ 188 if ((ipl_mask & ~oldipl_mask) > oldipl_mask) 189 pic_do_pending_ints(I32_bit, oldipl, frame); 190 } 191 192 void 193 geminiicu_establish_irq(struct pic_softc *pic, struct intrsource *is) 194 { 195 struct geminiicu_softc * const sc = PICTOSOFTC(pic); 196 const uint32_t irq_mask = __BIT(is->is_irq); 197 198 KASSERT(is->is_irq < 32); 199 200 sc->sc_enabled_mask &= ~irq_mask; 201 /* Have to do with this interrupt disabled. */ 202 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask); 203 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, irq_mask); 204 205 sc->sc_edge_rising_mask &= ~irq_mask; 206 sc->sc_edge_falling_mask &= ~irq_mask; 207 sc->sc_level_lo_mask &= ~irq_mask; 208 sc->sc_level_hi_mask &= ~irq_mask; 209 210 switch (is->is_type) { 211 case IST_LEVEL_LOW: sc->sc_level_lo_mask |= irq_mask; break; 212 case IST_LEVEL_HIGH: sc->sc_level_hi_mask |= irq_mask; break; 213 case IST_EDGE_FALLING: sc->sc_edge_falling_mask |= irq_mask; break; 214 case IST_EDGE_RISING: sc->sc_edge_rising_mask |= irq_mask; break; 215 } 216 217 sc->sc_edge_mask = sc->sc_edge_rising_mask | sc->sc_edge_falling_mask; 218 sc->sc_level_mask = sc->sc_level_hi_mask|sc->sc_level_lo_mask; 219 220 /* 221 * Set the new interrupt mode. 222 */ 223 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, sc->sc_edge_mask); 224 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 225 sc->sc_level_lo_mask | sc->sc_edge_falling_mask); 226 } 227 228 int 229 geminiicu_match(device_t parent, cfdata_t cf, void *aux) 230 { 231 struct obio_attach_args * const oa = aux; 232 233 #if defined(SL3516) 234 if ((oa->obio_addr == GEMINI_IC0_BASE) 235 || (oa->obio_addr == GEMINI_IC1_BASE)) 236 return 1; 237 238 return 0; 239 #else 240 #error unsupported GEMINI variant 241 #endif 242 } 243 244 void 245 geminiicu_attach(device_t parent, device_t self, void *aux) 246 { 247 struct obio_attach_args * const oa = aux; 248 struct geminiicu_softc * const sc = &geminiicu_softc; 249 int error; 250 251 aprint_normal("\n"); 252 253 sc->sc_memt = oa->obio_iot; 254 255 error = bus_space_map(sc->sc_memt, oa->obio_addr, 0x1000, 0, 256 &sc->sc_memh); 257 if (error) 258 panic("failed to map interrupt registers: %d", error); 259 260 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, 0); 261 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 0xffffffff); 262 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, 0); 263 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 0xffffffff); 264 265 sc->sc_dev = self; 266 self->dv_private = sc; 267 268 pic_add(&sc->sc_pic, 0); 269 } 270 271 CFATTACH_DECL_NEW(geminiicu, 272 0, 273 geminiicu_match, geminiicu_attach, 274 NULL, NULL); 275