intr_machdep.c revision 302408
1/*- 2 * Copyright 2003-2011 Netlogic Microsystems (Netlogic). All rights 3 * reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY Netlogic Microsystems ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE 20 * 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 26 * THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * NETLOGIC_BSD */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/mips/nlm/intr_machdep.c 279387 2015-02-28 00:17:29Z jchandra $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/interrupt.h> 37#include <sys/kernel.h> 38#include <sys/module.h> 39 40#include <dev/ofw/ofw_bus.h> 41#include <dev/ofw/ofw_bus_subr.h> 42 43#include <machine/cpu.h> 44#include <machine/cpufunc.h> 45#include <machine/cpuinfo.h> 46#include <machine/cpuregs.h> 47#include <machine/frame.h> 48#include <machine/intr_machdep.h> 49#include <machine/md_var.h> 50#include <machine/trap.h> 51#include <machine/hwfunc.h> 52 53#include <mips/nlm/hal/haldefs.h> 54#include <mips/nlm/hal/iomap.h> 55#include <mips/nlm/hal/mips-extns.h> 56#include <mips/nlm/interrupt.h> 57#include <mips/nlm/hal/pic.h> 58#include <mips/nlm/xlp.h> 59 60struct xlp_intrsrc { 61 void (*bus_ack)(int, void *); /* Additional ack */ 62 void *bus_ack_arg; /* arg for additional ack */ 63 struct intr_event *ie; /* event corresponding to intr */ 64 int irq; 65 int irt; 66}; 67 68static struct xlp_intrsrc xlp_interrupts[XLR_MAX_INTR]; 69static mips_intrcnt_t mips_intr_counters[XLR_MAX_INTR]; 70static int intrcnt_index; 71 72int 73xlp_irq_to_irt(int irq) 74{ 75 uint32_t offset; 76 77 switch (irq) { 78 case PIC_UART_0_IRQ: 79 case PIC_UART_1_IRQ: 80 offset = XLP_IO_UART_OFFSET(0, irq - PIC_UART_0_IRQ); 81 return (xlp_socdev_irt(offset)); 82 case PIC_PCIE_0_IRQ: 83 case PIC_PCIE_1_IRQ: 84 case PIC_PCIE_2_IRQ: 85 case PIC_PCIE_3_IRQ: 86 offset = XLP_IO_PCIE_OFFSET(0, irq - PIC_PCIE_0_IRQ); 87 return (xlp_socdev_irt(offset)); 88 case PIC_USB_0_IRQ: 89 case PIC_USB_1_IRQ: 90 case PIC_USB_2_IRQ: 91 case PIC_USB_3_IRQ: 92 case PIC_USB_4_IRQ: 93 offset = XLP_IO_USB_OFFSET(0, irq - PIC_USB_0_IRQ); 94 return (xlp_socdev_irt(offset)); 95 case PIC_I2C_0_IRQ: 96 case PIC_I2C_1_IRQ: 97 offset = XLP_IO_I2C0_OFFSET(0); 98 return (xlp_socdev_irt(offset) + irq - PIC_I2C_0_IRQ); 99 default: 100 printf("ERROR: %s: unknown irq %d\n", __func__, irq); 101 return (-1); 102 } 103} 104 105void 106xlp_enable_irq(int irq) 107{ 108 uint64_t eimr; 109 110 eimr = nlm_read_c0_eimr(); 111 nlm_write_c0_eimr(eimr | (1ULL << irq)); 112} 113 114void 115cpu_establish_softintr(const char *name, driver_filter_t * filt, 116 void (*handler) (void *), void *arg, int irq, int flags, 117 void **cookiep) 118{ 119 120 panic("Soft interrupts unsupported!\n"); 121} 122 123static void 124xlp_post_filter(void *source) 125{ 126 struct xlp_intrsrc *src = source; 127 128 if (src->bus_ack) 129 src->bus_ack(src->irq, src->bus_ack_arg); 130 nlm_pic_ack(xlp_pic_base, src->irt); 131} 132 133static void 134xlp_pre_ithread(void *source) 135{ 136 struct xlp_intrsrc *src = source; 137 138 if (src->bus_ack) 139 src->bus_ack(src->irq, src->bus_ack_arg); 140} 141 142static void 143xlp_post_ithread(void *source) 144{ 145 struct xlp_intrsrc *src = source; 146 147 nlm_pic_ack(xlp_pic_base, src->irt); 148} 149 150void 151xlp_set_bus_ack(int irq, void (*ack)(int, void *), void *arg) 152{ 153 struct xlp_intrsrc *src; 154 155 KASSERT(irq > 0 && irq <= XLR_MAX_INTR, 156 ("%s called for bad hard intr %d", __func__, irq)); 157 158 /* no locking needed - this will called early in boot */ 159 src = &xlp_interrupts[irq]; 160 KASSERT(src->ie != NULL, 161 ("%s called after IRQ enable for %d.", __func__, irq)); 162 src->bus_ack_arg = arg; 163 src->bus_ack = ack; 164} 165 166void 167cpu_establish_hardintr(const char *name, driver_filter_t * filt, 168 void (*handler) (void *), void *arg, int irq, int flags, 169 void **cookiep) 170{ 171 struct intr_event *ie; /* descriptor for the IRQ */ 172 struct xlp_intrsrc *src = NULL; 173 int errcode; 174 175 KASSERT(irq > 0 && irq <= XLR_MAX_INTR , 176 ("%s called for bad hard intr %d", __func__, irq)); 177 178 /* 179 * Locking - not needed now, because we do this only on 180 * startup from CPU0 181 */ 182 src = &xlp_interrupts[irq]; 183 ie = src->ie; 184 if (ie == NULL) { 185 /* 186 * PIC based interrupts need ack in PIC, and some SoC 187 * components need additional acks (e.g. PCI) 188 */ 189 if (XLP_IRQ_IS_PICINTR(irq)) 190 errcode = intr_event_create(&ie, src, 0, irq, 191 xlp_pre_ithread, xlp_post_ithread, xlp_post_filter, 192 NULL, "hard intr%d:", irq); 193 else { 194 if (filt == NULL) 195 panic("Unsupported non filter percpu intr %d", irq); 196 errcode = intr_event_create(&ie, src, 0, irq, 197 NULL, NULL, NULL, NULL, "hard intr%d:", irq); 198 } 199 if (errcode) { 200 printf("Could not create event for intr %d\n", irq); 201 return; 202 } 203 src->irq = irq; 204 src->ie = ie; 205 } 206 if (XLP_IRQ_IS_PICINTR(irq)) { 207 /* Set all irqs to CPU 0 for now */ 208 src->irt = xlp_irq_to_irt(irq); 209 nlm_pic_write_irt_direct(xlp_pic_base, src->irt, 1, 0, 210 PIC_LOCAL_SCHEDULING, irq, 0); 211 } 212 213 intr_event_add_handler(ie, name, filt, handler, arg, 214 intr_priority(flags), flags, cookiep); 215 xlp_enable_irq(irq); 216} 217 218void 219cpu_intr(struct trapframe *tf) 220{ 221 struct intr_event *ie; 222 uint64_t eirr, eimr; 223 int i; 224 225 critical_enter(); 226 227 /* find a list of enabled interrupts */ 228 eirr = nlm_read_c0_eirr(); 229 eimr = nlm_read_c0_eimr(); 230 eirr &= eimr; 231 232 if (eirr == 0) { 233 critical_exit(); 234 return; 235 } 236 /* 237 * No need to clear the EIRR here as the handler writes to 238 * compare which ACKs the interrupt. 239 */ 240 if (eirr & (1 << IRQ_TIMER)) { 241 intr_event_handle(xlp_interrupts[IRQ_TIMER].ie, tf); 242 critical_exit(); 243 return; 244 } 245 246 /* FIXME sched pin >? LOCK>? */ 247 for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) { 248 if ((eirr & (1ULL << i)) == 0) 249 continue; 250 251 ie = xlp_interrupts[i].ie; 252 /* Don't account special IRQs */ 253 switch (i) { 254 case IRQ_IPI: 255 case IRQ_MSGRING: 256 break; 257 default: 258 mips_intrcnt_inc(mips_intr_counters[i]); 259 } 260 261 /* Ack the IRQ on the CPU */ 262 nlm_write_c0_eirr(1ULL << i); 263 if (intr_event_handle(ie, tf) != 0) { 264 printf("stray interrupt %d\n", i); 265 } 266 } 267 critical_exit(); 268} 269 270void 271mips_intrcnt_setname(mips_intrcnt_t counter, const char *name) 272{ 273 int idx = counter - intrcnt; 274 275 KASSERT(counter != NULL, ("mips_intrcnt_setname: NULL counter")); 276 277 snprintf(intrnames + (MAXCOMLEN + 1) * idx, 278 MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name); 279} 280 281mips_intrcnt_t 282mips_intrcnt_create(const char* name) 283{ 284 mips_intrcnt_t counter = &intrcnt[intrcnt_index++]; 285 286 mips_intrcnt_setname(counter, name); 287 return counter; 288} 289 290void 291cpu_init_interrupts() 292{ 293 int i; 294 char name[MAXCOMLEN + 1]; 295 296 /* 297 * Initialize all available vectors so spare IRQ 298 * would show up in systat output 299 */ 300 for (i = 0; i < XLR_MAX_INTR; i++) { 301 snprintf(name, MAXCOMLEN + 1, "int%d:", i); 302 mips_intr_counters[i] = mips_intrcnt_create(name); 303 } 304} 305 306static int xlp_pic_probe(device_t); 307static int xlp_pic_attach(device_t); 308 309static int 310xlp_pic_probe(device_t dev) 311{ 312 313 if (!ofw_bus_is_compatible(dev, "netlogic,xlp-pic")) 314 return (ENXIO); 315 device_set_desc(dev, "XLP PIC"); 316 return (0); 317} 318 319static int 320xlp_pic_attach(device_t dev) 321{ 322 323 return (0); 324} 325 326static device_method_t xlp_pic_methods[] = { 327 DEVMETHOD(device_probe, xlp_pic_probe), 328 DEVMETHOD(device_attach, xlp_pic_attach), 329 330 DEVMETHOD_END 331}; 332 333static driver_t xlp_pic_driver = { 334 "xlp_pic", 335 xlp_pic_methods, 336 1, /* no softc */ 337}; 338 339static devclass_t xlp_pic_devclass; 340DRIVER_MODULE(xlp_pic, simplebus, xlp_pic_driver, xlp_pic_devclass, 0, 0); 341