1 /* $NetBSD: pic_ohare.c,v 1.17 2021/03/05 07:15:53 rin 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.17 2021/03/05 07:15:53 rin Exp $"); 31 32 #include "opt_interrupt.h" 33 34 #include <sys/param.h> 35 #include <sys/kmem.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 static 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 72 int init_ohare(void) 73 { 74 uint32_t reg[5]; 75 uint32_t obio_base; 76 uint32_t irq; 77 int ohare, ohare2, is_gc = 0; 78 79 ohare = OF_finddevice("/bandit/ohare"); 80 if (ohare == -1) { 81 ohare = OF_finddevice("/bandit/gc"); 82 is_gc = 1; 83 } 84 85 86 if (OF_getprop(ohare, "assigned-addresses", reg, sizeof(reg)) != 20) 87 return FALSE; 88 89 obio_base = reg[2]; 90 aprint_normal("found %s PIC at %08x\n", 91 is_gc ? "Grand Central" : "ohare", obio_base); 92 setup_ohare(obio_base, is_gc); 93 94 /* look for 2nd ohare */ 95 ohare2 = OF_finddevice("/bandit/pci106b,7"); 96 if (ohare2 == -1) 97 goto done; 98 99 if (OF_getprop(ohare2, "assigned-addresses", reg, sizeof(reg)) < 20) 100 goto done; 101 102 if (OF_getprop(ohare2, "AAPL,interrupts", &irq, sizeof(irq)) < 4) 103 goto done; 104 105 obio_base = reg[2]; 106 aprint_normal("found ohare2 PIC at %08x, irq %d\n", obio_base, irq); 107 setup_ohare2(obio_base, irq); 108 done: 109 return TRUE; 110 } 111 112 static struct ohare_ops * 113 setup_ohare(uint32_t addr, int is_gc) 114 { 115 struct ohare_ops *ohare; 116 struct pic_ops *pic; 117 int i; 118 119 ohare = kmem_zalloc(sizeof(struct ohare_ops), KM_SLEEP); 120 pic = &ohare->pic; 121 122 pic->pic_numintrs = OHARE_NIRQ; 123 pic->pic_cookie = (void *)addr; 124 pic->pic_enable_irq = ohare_enable_irq; 125 pic->pic_reenable_irq = ohare_reenable_irq; 126 pic->pic_disable_irq = ohare_disable_irq; 127 pic->pic_get_irq = ohare_get_irq; 128 pic->pic_ack_irq = ohare_ack_irq; 129 pic->pic_establish_irq = ohare_establish_irq; 130 pic->pic_finish_setup = NULL; 131 132 if (is_gc) { 133 134 strcpy(pic->pic_name, "gc"); 135 } else { 136 137 strcpy(pic->pic_name, "ohare"); 138 } 139 ohare->level_mask = 0; 140 141 for (i = 0; i < OHARE_NIRQ; i++) 142 ohare->priority_masks[i] = 0; 143 for (i = 0; i < NIPL; i++) 144 ohare->irqs[i] = 0; 145 pic_add(pic); 146 ohare->pending_events = 0; 147 ohare->enable_mask = 0; 148 out32rb(INT_ENABLE_REG, 0); 149 out32rb(INT_CLEAR_REG, 0xffffffff); 150 return ohare; 151 } 152 153 static void 154 setup_ohare2(uint32_t addr, int irq) 155 { 156 struct ohare_ops *pic; 157 158 pic = setup_ohare(addr, 0); 159 strcpy(pic->pic.pic_name, "ohare2"); 160 intr_establish_xname(irq, IST_LEVEL, IPL_HIGH, pic_handle_intr, pic, 161 "ohara2"); 162 } 163 164 static void 165 ohare_enable_irq(struct pic_ops *pic, int irq, int type) 166 { 167 struct ohare_ops *ohare = (struct ohare_ops *)pic; 168 uint32_t mask = 1 << irq; 169 170 ohare->enable_mask |= mask; 171 out32rb(INT_ENABLE_REG, ohare->enable_mask); 172 } 173 174 static void 175 ohare_reenable_irq(struct pic_ops *pic, int irq, int type) 176 { 177 struct ohare_ops *ohare = (struct ohare_ops *)pic; 178 uint32_t levels; 179 uint32_t mask = 1 << irq; 180 181 ohare->enable_mask |= mask; 182 out32rb(INT_ENABLE_REG, ohare->enable_mask); 183 levels = in32rb(INT_STATE_REG); 184 if (levels & mask) { 185 pic_mark_pending(pic->pic_intrbase + irq); 186 out32rb(INT_CLEAR_REG, mask); 187 } 188 } 189 190 static void 191 ohare_disable_irq(struct pic_ops *pic, int irq) 192 { 193 struct ohare_ops *ohare = (struct ohare_ops *)pic; 194 uint32_t mask = 1 << irq; 195 196 ohare->enable_mask &= ~mask; 197 out32rb(INT_ENABLE_REG, ohare->enable_mask); 198 } 199 200 static inline void 201 ohare_read_events(struct ohare_ops *ohare) 202 { 203 struct pic_ops *pic = &ohare->pic; 204 uint32_t irqs, events, levels; 205 206 irqs = in32rb(INT_STATE_REG); 207 events = irqs & ~ohare->level_mask; 208 209 levels = in32rb(INT_LEVEL_REG) & ohare->enable_mask; 210 events |= levels & ohare->level_mask; 211 out32rb(INT_CLEAR_REG, events | irqs); 212 ohare->pending_events |= events; 213 214 #if 0 215 if (events != 0) 216 aprint_error("%s: ev %08x\n", __func__, events); 217 #endif 218 } 219 220 static int 221 ohare_get_irq(struct pic_ops *pic, int mode) 222 { 223 struct ohare_ops *ohare = (struct ohare_ops *)pic; 224 uint32_t evt; 225 uint16_t prio; 226 int bit, mask, lvl; 227 #ifdef OHARE_DEBUG 228 int bail = 0; 229 #endif 230 231 if (ohare->pending_events == 0) 232 ohare_read_events(ohare); 233 234 if (ohare->pending_events == 0) 235 return 255; 236 237 bit = 31 - __builtin_clz(ohare->pending_events); 238 mask = 1 << bit; 239 240 if ((ohare->pending_events & ~mask) == 0) { 241 242 ohare->pending_events = 0; 243 return bit; 244 } 245 246 /* 247 * if we get here we have more than one irq pending so return them 248 * according to priority 249 */ 250 251 evt = ohare->pending_events & ~mask; 252 prio = ohare->priority_masks[bit]; 253 while (evt != 0) { 254 bit = 31 - __builtin_clz(evt); 255 prio |= ohare->priority_masks[bit]; 256 evt &= ~(1 << bit); 257 #ifdef OHARE_DEBUG 258 bail++; 259 if (bail > 31) 260 panic("hanging in ohare_get_irq"); 261 #endif 262 } 263 lvl = 31 - __builtin_clz(prio); 264 evt = ohare->pending_events & ohare->irqs[lvl]; 265 266 if (evt == 0) { 267 #ifdef OHARE_DEBUG 268 aprint_verbose("%s: spurious interrupt\n", 269 ohare->pic.pic_name); 270 printf("levels: %08x\n", in32rb(INT_LEVEL_REG)); 271 printf("states: %08x\n", in32rb(INT_STATE_REG)); 272 printf("enable: %08x\n", in32rb(INT_ENABLE_REG)); 273 printf("events: %08x\n", ohare->pending_events); 274 #endif 275 evt = ohare->pending_events; 276 } 277 278 bit = 31 - __builtin_clz(evt); 279 mask = 1 << bit; 280 ohare->pending_events &= ~mask; 281 return bit; 282 } 283 284 static void 285 ohare_ack_irq(struct pic_ops *pic, int irq) 286 { 287 } 288 289 static void 290 ohare_establish_irq(struct pic_ops *pic, int irq, int type, int pri) 291 { 292 struct ohare_ops *ohare = (struct ohare_ops *)pic; 293 uint32_t mask = (1 << irq); 294 int realpri = uimin(NIPL, uimax(0, pri)), i; 295 uint32_t level = 1 << realpri; 296 297 KASSERT((irq >= 0) && (irq < OHARE_NIRQ)); 298 299 if (type == IST_LEVEL) { 300 301 ohare->level_mask |= mask; 302 } else { 303 304 ohare->level_mask &= ~mask; 305 } 306 aprint_debug("mask: %08x\n", ohare->level_mask); 307 ohare->priority_masks[irq] = level; 308 for (i = 0; i < NIPL; i++) 309 ohare->irqs[i] = 0; 310 311 for (i = 0; i < OHARE_NIRQ; i++) { 312 if (ohare->priority_masks[i] == 0) 313 continue; 314 level = 31 - __builtin_clz(ohare->priority_masks[i]); 315 ohare->irqs[level] |= (1 << i); 316 } 317 } 318