1 /* $NetBSD: pic_ohare.c,v 1.8 2010/12/20 00:25:37 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: pic_ohare.c,v 1.8 2010/12/20 00:25:37 matt Exp $"); 31 32 #include "opt_interrupt.h" 33 34 #include <sys/param.h> 35 #include <sys/malloc.h> 36 #include <sys/kernel.h> 37 38 #include <machine/pio.h> 39 40 #include <dev/ofw/openfirm.h> 41 42 #include <machine/autoconf.h> 43 #include <arch/powerpc/pic/picvar.h> 44 45 static void ohare_enable_irq(struct pic_ops *, int, int); 46 static void ohare_reenable_irq(struct pic_ops *, int, int); 47 static void ohare_disable_irq(struct pic_ops *, int); 48 static int ohare_get_irq(struct pic_ops *, int); 49 static void ohare_ack_irq(struct pic_ops *, int); 50 static void ohare_establish_irq(struct pic_ops *, int, int, int); 51 52 #define OHARE_NIRQ 32 53 54 struct ohare_ops { 55 struct pic_ops pic; 56 uint32_t pending_events; 57 uint32_t enable_mask; 58 uint32_t level_mask; 59 uint32_t irqs[NIPL]; /* per priority level */ 60 uint32_t priority_masks[OHARE_NIRQ]; /* per IRQ */ 61 }; 62 63 static struct ohare_ops *setup_ohare(uint32_t, int); 64 static void setup_ohare2(uint32_t, int); 65 inline void ohare_read_events(struct ohare_ops *); 66 67 #define INT_STATE_REG ((uint32_t)pic->pic_cookie + 0x20) 68 #define INT_ENABLE_REG ((uint32_t)pic->pic_cookie + 0x24) 69 #define INT_CLEAR_REG ((uint32_t)pic->pic_cookie + 0x28) 70 #define INT_LEVEL_REG ((uint32_t)pic->pic_cookie + 0x2c) 71 #define INT_LEVEL_MASK_OHARE 0x1ff00000 72 #define INT_LEVEL_MASK_GC 0x3ff00000 73 74 int init_ohare(void) 75 { 76 uint32_t reg[5]; 77 uint32_t obio_base; 78 uint32_t irq; 79 int ohare, ohare2, is_gc = 0; 80 81 ohare = OF_finddevice("/bandit/ohare"); 82 if (ohare == -1) { 83 ohare = OF_finddevice("/bandit/gc"); 84 is_gc = 1; 85 } 86 87 88 if (OF_getprop(ohare, "assigned-addresses", reg, sizeof(reg)) != 20) 89 return FALSE; 90 91 obio_base = reg[2]; 92 aprint_normal("found %s PIC at %08x\n", 93 is_gc ? "Grand Central" : "ohare", obio_base); 94 setup_ohare(obio_base, is_gc); 95 96 /* look for 2nd ohare */ 97 ohare2 = OF_finddevice("/bandit/pci106b,7"); 98 if (ohare2 == -1) 99 goto done; 100 101 if (OF_getprop(ohare2, "assigned-addresses", reg, sizeof(reg)) < 20) 102 goto done; 103 104 if (OF_getprop(ohare2, "AAPL,interrupts", &irq, sizeof(irq)) < 4) 105 goto done; 106 107 obio_base = reg[2]; 108 aprint_normal("found ohare2 PIC at %08x, irq %d\n", obio_base, irq); 109 setup_ohare2(obio_base, irq); 110 done: 111 return TRUE; 112 } 113 114 static struct ohare_ops * 115 setup_ohare(uint32_t addr, int is_gc) 116 { 117 struct ohare_ops *ohare; 118 struct pic_ops *pic; 119 int i; 120 121 ohare = malloc(sizeof(struct ohare_ops), M_DEVBUF, M_NOWAIT); 122 KASSERT(ohare != NULL); 123 pic = &ohare->pic; 124 125 pic->pic_numintrs = OHARE_NIRQ; 126 pic->pic_cookie = (void *)addr; 127 pic->pic_enable_irq = ohare_enable_irq; 128 pic->pic_reenable_irq = ohare_reenable_irq; 129 pic->pic_disable_irq = ohare_disable_irq; 130 pic->pic_get_irq = ohare_get_irq; 131 pic->pic_ack_irq = ohare_ack_irq; 132 pic->pic_establish_irq = ohare_establish_irq; 133 pic->pic_finish_setup = NULL; 134 135 if (is_gc) { 136 137 strcpy(pic->pic_name, "gc"); 138 ohare->level_mask = 0; 139 } else { 140 141 strcpy(pic->pic_name, "ohare"); 142 ohare->level_mask = 0; 143 } 144 for (i = 0; i < OHARE_NIRQ; i++) 145 ohare->priority_masks[i] = 0; 146 for (i = 0; i < NIPL; i++) 147 ohare->irqs[i] = 0; 148 pic_add(pic); 149 ohare->pending_events = 0; 150 ohare->enable_mask = 0; 151 out32rb(INT_ENABLE_REG, 0); 152 out32rb(INT_CLEAR_REG, 0xffffffff); 153 return ohare; 154 } 155 156 static void 157 setup_ohare2(uint32_t addr, int irq) 158 { 159 struct ohare_ops *pic; 160 161 pic = setup_ohare(addr, 0); 162 strcpy(pic->pic.pic_name, "ohare2"); 163 intr_establish(irq, IST_LEVEL, IPL_NONE, pic_handle_intr, pic); 164 } 165 166 static void 167 ohare_enable_irq(struct pic_ops *pic, int irq, int type) 168 { 169 struct ohare_ops *ohare = (struct ohare_ops *)pic; 170 uint32_t mask = 1 << irq; 171 172 ohare->enable_mask |= mask; 173 out32rb(INT_ENABLE_REG, ohare->enable_mask); 174 } 175 176 static void 177 ohare_reenable_irq(struct pic_ops *pic, int irq, int type) 178 { 179 struct ohare_ops *ohare = (struct ohare_ops *)pic; 180 uint32_t levels; 181 uint32_t mask = 1 << irq; 182 183 ohare->enable_mask |= mask; 184 out32rb(INT_ENABLE_REG, ohare->enable_mask); 185 levels = in32rb(INT_LEVEL_REG); 186 if (levels & mask) { 187 pic_mark_pending(pic->pic_intrbase + irq); 188 out32rb(INT_CLEAR_REG, mask); 189 } 190 } 191 192 static void 193 ohare_disable_irq(struct pic_ops *pic, int irq) 194 { 195 struct ohare_ops *ohare = (struct ohare_ops *)pic; 196 uint32_t mask = 1 << irq; 197 198 ohare->enable_mask &= ~mask; 199 out32rb(INT_ENABLE_REG, ohare->enable_mask); 200 } 201 202 inline void 203 ohare_read_events(struct ohare_ops *ohare) 204 { 205 struct pic_ops *pic = &ohare->pic; 206 uint32_t irqs, events, levels; 207 208 irqs = in32rb(INT_STATE_REG); 209 events = irqs & ~ohare->level_mask; 210 211 levels = in32rb(INT_LEVEL_REG) & ohare->enable_mask; 212 events |= levels & ohare->level_mask; 213 out32rb(INT_CLEAR_REG, events | irqs); 214 ohare->pending_events |= events; 215 216 #if 0 217 if (events != 0) 218 aprint_error("%s: ev %08x\n", __func__, events); 219 #endif 220 } 221 222 static int 223 ohare_get_irq(struct pic_ops *pic, int mode) 224 { 225 struct ohare_ops *ohare = (struct ohare_ops *)pic; 226 uint32_t evt; 227 uint16_t prio; 228 int bit, mask, lvl; 229 #ifdef OHARE_DEBUG 230 int bail = 0; 231 #endif 232 233 if (ohare->pending_events == 0) 234 ohare_read_events(ohare); 235 236 if (ohare->pending_events == 0) 237 return 255; 238 239 bit = 31 - cntlzw(ohare->pending_events); 240 mask = 1 << bit; 241 if ((ohare->pending_events & ~mask) == 0) { 242 243 ohare->pending_events = 0; 244 return bit; 245 } 246 247 /* 248 * if we get here we have more than one irq pending so return them 249 * according to priority 250 */ 251 252 evt = ohare->pending_events & ~mask; 253 prio = ohare->priority_masks[bit]; 254 while (evt != 0) { 255 bit = 31 - cntlzw(evt); 256 prio |= ohare->priority_masks[bit]; 257 evt &= ~(1 << bit); 258 #ifdef OHARE_DEBUG 259 bail++; 260 if (bail > 31) 261 panic("hanging in ohare_get_irq"); 262 #endif 263 } 264 lvl = 31 - cntlzw(prio); 265 evt = ohare->pending_events & ohare->irqs[lvl]; 266 267 if (evt == 0) { 268 aprint_verbose("%s: spurious interrupt\n", 269 ohare->pic.pic_name); 270 evt = ohare->pending_events; 271 } 272 273 bit = 31 - cntlzw(evt); 274 mask = 1 << bit; 275 ohare->pending_events &= ~mask; 276 return bit; 277 } 278 279 static void 280 ohare_ack_irq(struct pic_ops *pic, int irq) 281 { 282 } 283 284 static void 285 ohare_establish_irq(struct pic_ops *pic, int irq, int type, int pri) 286 { 287 struct ohare_ops *ohare = (struct ohare_ops *)pic; 288 uint32_t mask = (1 << irq); 289 int realpri = min(NIPL, max(0, pri)), i; 290 uint32_t level = 1 << realpri; 291 292 KASSERT((irq >= 0) && (irq < OHARE_NIRQ)); 293 294 if (type == IST_LEVEL) { 295 296 ohare->level_mask |= mask; 297 } else { 298 299 ohare->level_mask &= ~mask; 300 } 301 aprint_debug("mask: %08x\n", ohare->level_mask); 302 ohare->priority_masks[irq] = level; 303 for (i = 0; i < NIPL; i++) 304 ohare->irqs[i] = 0; 305 306 for (i = 0; i < OHARE_NIRQ; i++) { 307 if (ohare->priority_masks[i] == 0) 308 continue; 309 level = 31 - cntlzw(ohare->priority_masks[i]); 310 ohare->irqs[level] |= (1 << i); 311 } 312 } 313