intr.c revision 269646
1129198Scognet/* $NetBSD: intr.c,v 1.12 2003/07/15 00:24:41 lukem Exp $ */ 2129198Scognet 3139735Simp/*- 4129198Scognet * Copyright (c) 2004 Olivier Houchard. 5129198Scognet * Copyright (c) 1994-1998 Mark Brinicombe. 6129198Scognet * All rights reserved. 7129198Scognet * 8129198Scognet * Redistribution and use in source and binary forms, with or without 9129198Scognet * modification, are permitted provided that the following conditions 10129198Scognet * are met: 11129198Scognet * 1. Redistributions of source code must retain the above copyright 12129198Scognet * notice, this list of conditions and the following disclaimer. 13129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 14129198Scognet * notice, this list of conditions and the following disclaimer in the 15129198Scognet * documentation and/or other materials provided with the distribution. 16129198Scognet * 3. All advertising materials mentioning features or use of this software 17129198Scognet * must display the following acknowledgement: 18129198Scognet * This product includes software developed by Mark Brinicombe 19129198Scognet * for the NetBSD Project. 20129198Scognet * 4. The name of the company nor the name of the author may be used to 21129198Scognet * endorse or promote products derived from this software without specific 22129198Scognet * prior written permission. 23129198Scognet * 24129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27129198Scognet * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28129198Scognet * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29129198Scognet * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30129198Scognet * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34129198Scognet * SUCH DAMAGE. 35129198Scognet * 36129198Scognet * Soft interrupt and other generic interrupt functions. 37129198Scognet */ 38129198Scognet 39129198Scognet#include <sys/cdefs.h> 40129198Scognet__FBSDID("$FreeBSD: head/sys/arm/arm/intr.c 269646 2014-08-06 21:27:15Z ian $"); 41129198Scognet#include <sys/param.h> 42129198Scognet#include <sys/systm.h> 43236991Simp#include <sys/syslog.h> 44269646Sian#include <sys/kernel.h> 45129198Scognet#include <sys/malloc.h> 46135650Scognet#include <sys/proc.h> 47129198Scognet#include <sys/bus.h> 48129198Scognet#include <sys/interrupt.h> 49129198Scognet#include <sys/conf.h> 50129198Scognet#include <machine/atomic.h> 51129198Scognet#include <machine/intr.h> 52129198Scognet#include <machine/cpu.h> 53129198Scognet 54245637Sian#define INTRNAME_LEN (MAXCOMLEN + 1) 55245637Sian 56177325Sjhbtypedef void (*mask_fn)(void *); 57177325Sjhb 58151658Sjhbstatic struct intr_event *intr_events[NIRQ]; 59129198Scognet 60262979Sianvoid arm_irq_handler(struct trapframe *); 61135650Scognet 62178366Scognetvoid (*arm_post_filter)(void *) = NULL; 63260161Szbbint (*arm_config_irq)(int irq, enum intr_trigger trig, 64260161Szbb enum intr_polarity pol) = NULL; 65178366Scognet 66262979Sian/* Data for statistics reporting. */ 67262979Sianu_long intrcnt[NIRQ]; 68262979Sianchar intrnames[NIRQ * INTRNAME_LEN]; 69262979Siansize_t sintrcnt = sizeof(intrcnt); 70262979Siansize_t sintrnames = sizeof(intrnames); 71262979Sian 72245637Sian/* 73245637Sian * Pre-format intrnames into an array of fixed-size strings containing spaces. 74245637Sian * This allows us to avoid the need for an intermediate table of indices into 75245637Sian * the names and counts arrays, while still meeting the requirements and 76245637Sian * assumptions of vmstat(8) and the kdb "show intrcnt" command, the two 77245637Sian * consumers of this data. 78245637Sian */ 79269646Sianstatic void 80269646Sianintr_init(void *unused) 81245637Sian{ 82245637Sian int i; 83245637Sian 84246000Sian for (i = 0; i < NIRQ; ++i) { 85245948Sian snprintf(&intrnames[i * INTRNAME_LEN], INTRNAME_LEN, "%-*s", 86245948Sian INTRNAME_LEN - 1, ""); 87246000Sian } 88245637Sian} 89245637Sian 90269646SianSYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); 91269646Sian 92245637Sianvoid 93236991Simparm_setup_irqhandler(const char *name, driver_filter_t *filt, 94166901Spiso void (*hand)(void*), void *arg, int irq, int flags, void **cookiep) 95129198Scognet{ 96151658Sjhb struct intr_event *event; 97129198Scognet int error; 98129198Scognet 99129198Scognet if (irq < 0 || irq >= NIRQ) 100129198Scognet return; 101151658Sjhb event = intr_events[irq]; 102151658Sjhb if (event == NULL) { 103178092Sjeff error = intr_event_create(&event, (void *)irq, 0, irq, 104177325Sjhb (mask_fn)arm_mask_irq, (mask_fn)arm_unmask_irq, 105178366Scognet arm_post_filter, NULL, "intr%d:", irq); 106129198Scognet if (error) 107129198Scognet return; 108151658Sjhb intr_events[irq] = event; 109246000Sian snprintf(&intrnames[irq * INTRNAME_LEN], INTRNAME_LEN, 110246000Sian "irq%d: %-*s", irq, INTRNAME_LEN - 1, name); 111129198Scognet } 112166901Spiso intr_event_add_handler(event, name, filt, hand, arg, 113151658Sjhb intr_priority(flags), flags, cookiep); 114129198Scognet} 115129198Scognet 116147166Scognetint 117182933Srajarm_remove_irqhandler(int irq, void *cookie) 118147166Scognet{ 119182933Sraj struct intr_event *event; 120182933Sraj int error; 121182933Sraj 122182933Sraj event = intr_events[irq]; 123182933Sraj arm_mask_irq(irq); 124182933Sraj 125182933Sraj error = intr_event_remove_handler(cookie); 126182933Sraj 127182933Sraj if (!TAILQ_EMPTY(&event->ie_handlers)) 128182933Sraj arm_unmask_irq(irq); 129182933Sraj return (error); 130147166Scognet} 131147166Scognet 132129198Scognetvoid dosoftints(void); 133129198Scognetvoid 134129198Scognetdosoftints(void) 135129198Scognet{ 136129198Scognet} 137129198Scognet 138129198Scognetvoid 139262979Sianarm_irq_handler(struct trapframe *frame) 140129198Scognet{ 141151658Sjhb struct intr_event *event; 142171616Scognet int i; 143129198Scognet 144170291Sattilio PCPU_INC(cnt.v_intr); 145193847Smarcel i = -1; 146193847Smarcel while ((i = arm_get_next_irq(i)) != -1) { 147245637Sian intrcnt[i]++; 148151658Sjhb event = intr_events[i]; 149177940Sjhb if (intr_event_handle(event, frame) != 0) { 150177940Sjhb /* XXX: Log stray IRQs */ 151171616Scognet arm_mask_irq(i); 152171616Scognet } 153135650Scognet } 154129198Scognet} 155266621Sian 156266621Sian/* 157266621Sian * arm_irq_memory_barrier() 158266621Sian * 159266621Sian * Ensure all writes to device memory have reached devices before proceeding. 160266621Sian * 161266621Sian * This is intended to be called from the post-filter and post-thread routines 162266621Sian * of an interrupt controller implementation. A peripheral device driver should 163266621Sian * use bus_space_barrier() if it needs to ensure a write has reached the 164266621Sian * hardware for some reason other than clearing interrupt conditions. 165266621Sian * 166266621Sian * The need for this function arises from the ARM weak memory ordering model. 167266621Sian * Writes to locations mapped with the Device attribute bypass any caches, but 168266621Sian * are buffered. Multiple writes to the same device will be observed by that 169266621Sian * device in the order issued by the cpu. Writes to different devices may 170266621Sian * appear at those devices in a different order than issued by the cpu. That 171266621Sian * is, if the cpu writes to device A then device B, the write to device B could 172266621Sian * complete before the write to device A. 173266621Sian * 174266621Sian * Consider a typical device interrupt handler which services the interrupt and 175266621Sian * writes to a device status-acknowledge register to clear the interrupt before 176266621Sian * returning. That write is posted to the L2 controller which "immediately" 177266621Sian * places it in a store buffer and automatically drains that buffer. This can 178266621Sian * be less immediate than you'd think... There may be no free slots in the store 179266621Sian * buffers, so an existing buffer has to be drained first to make room. The 180266621Sian * target bus may be busy with other traffic (such as DMA for various devices), 181266621Sian * delaying the drain of the store buffer for some indeterminate time. While 182266621Sian * all this delay is happening, execution proceeds on the CPU, unwinding its way 183266621Sian * out of the interrupt call stack to the point where the interrupt driver code 184266621Sian * is ready to EOI and unmask the interrupt. The interrupt controller may be 185266621Sian * accessed via a faster bus than the hardware whose handler just ran; the write 186266621Sian * to unmask and EOI the interrupt may complete quickly while the device write 187266621Sian * to ack and clear the interrupt source is still lingering in a store buffer 188266621Sian * waiting for access to a slower bus. With the interrupt unmasked at the 189266621Sian * interrupt controller but still active at the device, as soon as interrupts 190266621Sian * are enabled on the core the device re-interrupts immediately: now you've got 191266621Sian * a spurious interrupt on your hands. 192266621Sian * 193266621Sian * The right way to fix this problem is for every device driver to use the 194266621Sian * proper bus_space_barrier() calls in its interrupt handler. For ARM a single 195266621Sian * barrier call at the end of the handler would work. This would have to be 196266621Sian * done to every driver in the system, not just arm-specific drivers. 197266621Sian * 198266621Sian * Another potential fix is to map all device memory as Strongly-Ordered rather 199266621Sian * than Device memory, which takes the store buffers out of the picture. This 200266621Sian * has a pretty big impact on overall system performance, because each strongly 201266621Sian * ordered memory access causes all L2 store buffers to be drained. 202266621Sian * 203266621Sian * A compromise solution is to have the interrupt controller implementation call 204266621Sian * this function to establish a barrier between writes to the interrupt-source 205266621Sian * device and writes to the interrupt controller device. 206266621Sian * 207266621Sian * This takes the interrupt number as an argument, and currently doesn't use it. 208266621Sian * The plan is that maybe some day there is a way to flag certain interrupts as 209266621Sian * "memory barrier safe" and we can avoid this overhead with them. 210266621Sian */ 211266621Sianvoid 212266621Sianarm_irq_memory_barrier(uintptr_t irq) 213266621Sian{ 214266621Sian 215266621Sian dsb(); 216266621Sian cpu_l2cache_drain_writebuf(); 217266621Sian} 218266621Sian 219