Index: projects/arm_intrng/sys/arm/arm/intr.c =================================================================== --- projects/arm_intrng/sys/arm/arm/intr.c (revision 277020) +++ projects/arm_intrng/sys/arm/arm/intr.c (revision 277021) @@ -1,266 +1,266 @@ /* $NetBSD: intr.c,v 1.12 2003/07/15 00:24:41 lukem Exp $ */ /*- * Copyright (c) 2004 Olivier Houchard. * Copyright (c) 1994-1998 Mark Brinicombe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Mark Brinicombe * for the NetBSD Project. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Soft interrupt and other generic interrupt functions. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #endif #define INTRNAME_LEN (MAXCOMLEN + 1) typedef void (*mask_fn)(void *); static struct intr_event *intr_events[NIRQ]; void arm_irq_handler(struct trapframe *); void (*arm_post_filter)(void *) = NULL; int (*arm_config_irq)(int irq, enum intr_trigger trig, enum intr_polarity pol) = NULL; /* Data for statistics reporting. */ u_long intrcnt[NIRQ]; char intrnames[NIRQ * INTRNAME_LEN]; size_t sintrcnt = sizeof(intrcnt); size_t sintrnames = sizeof(intrnames); /* * Pre-format intrnames into an array of fixed-size strings containing spaces. * This allows us to avoid the need for an intermediate table of indices into * the names and counts arrays, while still meeting the requirements and * assumptions of vmstat(8) and the kdb "show intrcnt" command, the two * consumers of this data. */ static void intr_init(void *unused) { int i; for (i = 0; i < NIRQ; ++i) { snprintf(&intrnames[i * INTRNAME_LEN], INTRNAME_LEN, "%-*s", INTRNAME_LEN - 1, ""); } } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); #ifdef FDT int arm_fdt_map_irq(phandle_t iparent, pcell_t *intr, int icells) { fdt_pic_decode_t intr_decode; phandle_t intr_parent; int i, rv, interrupt, trig, pol; intr_parent = OF_node_from_xref(iparent); for (i = 0; i < icells; i++) intr[i] = cpu_to_fdt32(intr[i]); for (i = 0; fdt_pic_table[i] != NULL; i++) { intr_decode = fdt_pic_table[i]; rv = intr_decode(intr_parent, intr, &interrupt, &trig, &pol); if (rv == 0) { /* This was recognized as our PIC and decoded. */ interrupt = FDT_MAP_IRQ(intr_parent, interrupt); return (interrupt); } } /* Not in table, so guess */ interrupt = FDT_MAP_IRQ(intr_parent, fdt32_to_cpu(intr[0])); return (interrupt); } -#endif -const char * -arm_describe_irq(int irq) +int +fdt_describe_irq(char *buf, u_int len, u_int irq) { - static char buffer[8]; + int rv; - sprintf(buffer, "%d", irq); - return (buffer); + rv = snprintf(buf, len, "%u", irq); + return (rv); } +#endif void arm_setup_irqhandler(const char *name, driver_filter_t *filt, void (*hand)(void*), void *arg, int irq, int flags, void **cookiep) { struct intr_event *event; int error; if (irq < 0 || irq >= NIRQ) return; event = intr_events[irq]; if (event == NULL) { error = intr_event_create(&event, (void *)irq, 0, irq, (mask_fn)arm_mask_irq, (mask_fn)arm_unmask_irq, arm_post_filter, NULL, "intr%d:", irq); if (error) return; intr_events[irq] = event; snprintf(&intrnames[irq * INTRNAME_LEN], INTRNAME_LEN, "irq%d: %-*s", irq, INTRNAME_LEN - 1, name); } intr_event_add_handler(event, name, filt, hand, arg, intr_priority(flags), flags, cookiep); } int arm_remove_irqhandler(int irq, void *cookie) { struct intr_event *event; int error; event = intr_events[irq]; arm_mask_irq(irq); error = intr_event_remove_handler(cookie); if (!TAILQ_EMPTY(&event->ie_handlers)) arm_unmask_irq(irq); return (error); } void dosoftints(void); void dosoftints(void) { } void arm_irq_handler(struct trapframe *frame) { struct intr_event *event; int i; PCPU_INC(cnt.v_intr); i = -1; while ((i = arm_get_next_irq(i)) != -1) { intrcnt[i]++; event = intr_events[i]; if (intr_event_handle(event, frame) != 0) { /* XXX: Log stray IRQs */ arm_mask_irq(i); } } } /* * arm_irq_memory_barrier() * * Ensure all writes to device memory have reached devices before proceeding. * * This is intended to be called from the post-filter and post-thread routines * of an interrupt controller implementation. A peripheral device driver should * use bus_space_barrier() if it needs to ensure a write has reached the * hardware for some reason other than clearing interrupt conditions. * * The need for this function arises from the ARM weak memory ordering model. * Writes to locations mapped with the Device attribute bypass any caches, but * are buffered. Multiple writes to the same device will be observed by that * device in the order issued by the cpu. Writes to different devices may * appear at those devices in a different order than issued by the cpu. That * is, if the cpu writes to device A then device B, the write to device B could * complete before the write to device A. * * Consider a typical device interrupt handler which services the interrupt and * writes to a device status-acknowledge register to clear the interrupt before * returning. That write is posted to the L2 controller which "immediately" * places it in a store buffer and automatically drains that buffer. This can * be less immediate than you'd think... There may be no free slots in the store * buffers, so an existing buffer has to be drained first to make room. The * target bus may be busy with other traffic (such as DMA for various devices), * delaying the drain of the store buffer for some indeterminate time. While * all this delay is happening, execution proceeds on the CPU, unwinding its way * out of the interrupt call stack to the point where the interrupt driver code * is ready to EOI and unmask the interrupt. The interrupt controller may be * accessed via a faster bus than the hardware whose handler just ran; the write * to unmask and EOI the interrupt may complete quickly while the device write * to ack and clear the interrupt source is still lingering in a store buffer * waiting for access to a slower bus. With the interrupt unmasked at the * interrupt controller but still active at the device, as soon as interrupts * are enabled on the core the device re-interrupts immediately: now you've got * a spurious interrupt on your hands. * * The right way to fix this problem is for every device driver to use the * proper bus_space_barrier() calls in its interrupt handler. For ARM a single * barrier call at the end of the handler would work. This would have to be * done to every driver in the system, not just arm-specific drivers. * * Another potential fix is to map all device memory as Strongly-Ordered rather * than Device memory, which takes the store buffers out of the picture. This * has a pretty big impact on overall system performance, because each strongly * ordered memory access causes all L2 store buffers to be drained. * * A compromise solution is to have the interrupt controller implementation call * this function to establish a barrier between writes to the interrupt-source * device and writes to the interrupt controller device. * * This takes the interrupt number as an argument, and currently doesn't use it. * The plan is that maybe some day there is a way to flag certain interrupts as * "memory barrier safe" and we can avoid this overhead with them. */ void arm_irq_memory_barrier(uintptr_t irq) { dsb(); cpu_l2cache_drain_writebuf(); } Index: projects/arm_intrng/sys/arm/arm/intrng.c =================================================================== --- projects/arm_intrng/sys/arm/arm/intrng.c (revision 277020) +++ projects/arm_intrng/sys/arm/arm/intrng.c (revision 277021) @@ -1,675 +1,674 @@ /*- * Copyright (c) 2012-2014 Jakub Wojciech Klama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Mark Brinicombe * for the NetBSD Project. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include + #include "pic_if.h" #define INTRNAME_LEN (MAXCOMLEN + 1) #define MAXINTRS 1024 // XXX Need this passed in to pic registration //#define DEBUG #ifdef DEBUG #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define debugf(fmt, args...) #endif typedef void (*mask_fn)(void *); struct arm_intr_controller { device_t ic_dev; phandle_t ic_node; u_int ic_idx; u_int ic_maxintrs; struct arm_intr_handler **ic_ih_by_hwirq; u_int ic_ih_count; SLIST_HEAD(, arm_intr_handler) ic_ih_list; }; struct arm_intr_handler { SLIST_ENTRY(arm_intr_handler) ih_next_entry; u_int ih_intrcnt_idx; u_int ih_resirq; u_int ih_hwirq; u_int ih_ncells; enum intr_trigger ih_trig; enum intr_polarity ih_pol; struct intr_event * ih_event; struct arm_intr_controller *ih_ic; pcell_t ih_cells[]; }; static u_int resirq_encode(u_int picidx, u_int irqidx); static void arm_mask_irq(void *); static void arm_unmask_irq(void *); static void arm_eoi(void *); static struct arm_intr_controller arm_pics[NPIC]; static struct arm_intr_controller *arm_ipi_pic; static int intrcnt_index; static int intrcnt_last_printed; MALLOC_DECLARE(M_INTRNG); MALLOC_DEFINE(M_INTRNG, "intrng", "ARM interrupt handling"); static const char *ipi_names[] = { "IPI:AST", "IPI:PREEMPT", "IPI:RENDEZVOUS", "IPI:STOP", "IPI:HARDCLOCK", "IPI:TLB" }; CTASSERT(ARM_IPI_COUNT == nitems(ipi_names)); static struct arm_intr_handler * ipi_handlers[ARM_IPI_COUNT]; /* Data for statistics reporting. */ u_long intrcnt[NIRQ]; char intrnames[NIRQ * INTRNAME_LEN]; size_t sintrcnt = sizeof(intrcnt); size_t sintrnames = sizeof(intrnames); int (*arm_config_irq)(int irq, enum intr_trigger trig, enum intr_polarity pol) = NULL; static inline struct arm_intr_controller * ic_from_dev(device_t dev) { struct arm_intr_controller *ic; u_int i; for (i = 0, ic = arm_pics; i < nitems(arm_pics); i++, ic++) { if (dev == ic->ic_dev) return (ic); } return (NULL); } static inline struct arm_intr_controller * ic_from_node(phandle_t node) { struct arm_intr_controller *ic; u_int i; for (i = 0, ic = arm_pics; i < nitems(arm_pics); i++, ic++) { if (node == ic->ic_node) return (ic); } return (NULL); } static struct arm_intr_controller * ic_create(phandle_t node) { struct arm_intr_controller *ic; u_int i; for (i = 0, ic = arm_pics; i < nitems(arm_pics); i++, ic++) { if (ic->ic_node == 0) break; } if (i == nitems(arm_pics)) panic("no room to add interrupt controller"); bzero(ic, sizeof(ic)); ic->ic_idx = i; ic->ic_node = node; SLIST_INIT(&ic->ic_ih_list); debugf("allocated new interrupt controller at index %d ptr %p for node %d\n", i, ic, node); return (ic); } static void ic_setup_dev(struct arm_intr_controller *ic, device_t dev, u_int maxintrs) { struct arm_intr_handler *ih; ic->ic_dev = dev; ic->ic_maxintrs = maxintrs; ic->ic_ih_by_hwirq = malloc(maxintrs * sizeof(struct arm_intr_handler *), M_INTRNG, M_WAITOK | M_ZERO); SLIST_FOREACH(ih, &ic->ic_ih_list, ih_next_entry) { PIC_TRANSLATE(ic->ic_dev, ih->ih_cells, &ih->ih_hwirq, &ih->ih_trig, &ih->ih_pol); ic->ic_ih_by_hwirq[ih->ih_hwirq] = ih; } } static struct arm_intr_handler * ic_add_ih(struct arm_intr_controller *ic, pcell_t *cells, u_int ncells) { struct arm_intr_handler *ih; u_int cellsize; cellsize = ncells * sizeof(*cells); ih = malloc(sizeof(*ih) + cellsize, M_INTRNG, M_WAITOK | M_ZERO); memcpy(ih->ih_cells, cells, cellsize); ih->ih_ncells = ncells; ih->ih_ic = ic; ih->ih_resirq = resirq_encode(ic->ic_idx, ic->ic_ih_count++); SLIST_INSERT_HEAD(&ic->ic_ih_list, ih, ih_next_entry); return (ih); } static void ic_index_ih_by_hwirq(struct arm_intr_controller *ic, struct arm_intr_handler *ih) { KASSERT(ih->ih_hwirq < ic->ic_maxintrs, ("%s irq %u too large", device_get_nameunit(ic->ic_dev), ih->ih_hwirq)); KASSERT(ic->ic_ih_by_hwirq[ih->ih_hwirq] == NULL, ("%s irq %u already registered", device_get_nameunit(ic->ic_dev), ih->ih_hwirq)); ic->ic_ih_by_hwirq[ih->ih_hwirq] = ih; } static struct arm_intr_handler * ih_from_fdtcells(struct arm_intr_controller *ic, pcell_t *cells, u_int ncells) { struct arm_intr_handler *ih; SLIST_FOREACH(ih, &ic->ic_ih_list, ih_next_entry) { if (ncells == ih->ih_ncells && memcmp(cells, ih->ih_cells, ncells * sizeof(*cells)) == 0) return (ih); } return (NULL); } static struct arm_intr_handler * ih_from_resirq(struct arm_intr_controller *ic, u_int resirq) { struct arm_intr_handler *ih; SLIST_FOREACH(ih, &ic->ic_ih_list, ih_next_entry) { if (resirq == ih->ih_resirq) return (ih); } return (NULL); } static struct arm_intr_handler * ih_from_hwirq(struct arm_intr_controller *ic, u_int hwirq) { return (ic->ic_ih_by_hwirq[hwirq]); } static u_int resirq_encode(u_int picidx, u_int irqidx) { return((picidx << 16) | (irqidx & 0xffff)); } static u_int -resirq_decode(int resirq, struct arm_intr_controller **pic, +resirq_decode(u_int resirq, struct arm_intr_controller **pic, struct arm_intr_handler **pih) { struct arm_intr_controller *ic; u_int irqidx, picidx; picidx = resirq >> 16; KASSERT(picidx < nitems(arm_pics), ("bad pic index %u", picidx)); ic = &arm_pics[picidx]; irqidx = resirq & 0xffff; KASSERT(irqidx < ic->ic_ih_count, ("bad irq index %u for pic %u", irqidx, picidx)); if (pic != NULL) *pic = ic; if (pih != NULL && ic != NULL) *pih = ih_from_resirq(ic, resirq); return (irqidx); } void arm_intrnames_init(void) { /* nothing... */ } void arm_dispatch_irq(device_t dev, struct trapframe *tf, int irq) { struct arm_intr_controller *ic; struct arm_intr_handler *ih = NULL; // debugf("pic %s, tf %p, irq %d\n", device_get_nameunit(dev), tf, irq); /* * If we got null trapframe argument, that probably means * a call from non-root interrupt controller. In that case, * we'll just use the saved one. */ if (tf == NULL) tf = PCPU_GET(curthread)->td_intr_frame; ic = ic_from_dev(dev); KASSERT(ic != NULL, ("%s: interrupt controller for %s not found", __FUNCTION__, device_get_nameunit(dev))); ih = ih_from_hwirq(ic, irq); KASSERT(ih != NULL, ("%s: interrupt handler for %s irq %d not found", __FUNCTION__, device_get_nameunit(dev), irq)); intrcnt[ih->ih_intrcnt_idx]++; if (intr_event_handle(ih->ih_event, tf) != 0) { device_printf(dev, "stray irq %d; disabled", irq); arm_mask_irq(ih); } // debugf("done\n"); } int arm_fdt_map_irq(phandle_t icnode, pcell_t *cells, int ncells) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; debugf("map icnode %08x cells <%*D>\n", icnode, ncells * sizeof(pcell_t), (char *)cells, ","); icnode = OF_node_from_xref(icnode); ic = ic_from_node(icnode); if (ic == NULL) ic = ic_create(icnode); ih = ih_from_fdtcells(ic, cells, ncells); if (ih == NULL) { ih = ic_add_ih(ic, cells, ncells); if (ic->ic_dev != NULL) { PIC_TRANSLATE(ic->ic_dev, ih->ih_cells, &ih->ih_hwirq, &ih->ih_trig, &ih->ih_pol); ic_index_ih_by_hwirq(ic, ih); } } return (ih->ih_resirq); } -const char * -arm_describe_irq(int resirq) +int +fdt_describe_irq(char *buf, u_int len, u_int resirq) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; - int irqidx; - static char buffer[INTRNAME_LEN]; + int irqidx, rv; - /* XXX static buffer, can this be called after APs released? */ - irqidx = resirq_decode(resirq, &ic, &ih); KASSERT(ic != NULL, ("%s: bad resirq 0x%08x", resirq)); if (ic->ic_dev == NULL) { /* * Interrupt controller not attached yet. We don't know the * IC device name nor interrupt number. All we can do is to * use its index (fdt names are unbounded length). */ - snprintf(buffer, sizeof(buffer), "ic%d.%d", ic->ic_idx, irqidx); + rv = snprintf(buf, len, "ic%d.%d", ic->ic_idx, irqidx); } else { KASSERT(ih != NULL, ("%s: no handler for resirq 0x%08x\n", resirq)); - snprintf(buffer, sizeof(buffer), "%s.%d", + rv = snprintf(buf, len, "%s.%d", device_get_nameunit(ih->ih_ic->ic_dev), ih->ih_hwirq); } - return (buffer); + return (rv); } void arm_register_pic(device_t dev, int flags) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; phandle_t node; node = ofw_bus_get_node(dev); ic = ic_from_node(node); if (ic == NULL) ic = ic_create(node); ic_setup_dev(ic, dev, MAXINTRS); /* * The nexus root usually isn't described by fdt data. If the node is * -1 and the number of interrupts added is zero and the device's * name is "nexus", allocate a single entry for irq 0. */ if (node == -1 && ic->ic_ih_count == 0 && strcmp(device_get_name(dev), "nexus") == 0) { ih = ic_add_ih(ic, NULL, 0); ih->ih_hwirq = 0; ic_index_ih_by_hwirq(ic, ih); } debugf("device %s node %08x slot %d\n", device_get_nameunit(dev), ic->ic_node, ic->ic_idx); if (flags & PIC_FEATURE_IPI) { KASSERT(arm_ipi_pic == NULL, ("controller for IPIs is already registered")); arm_ipi_pic = ic; } /* - * arm_describe_irq() has to print fake names earlier when the device + * fdt_describe_irq() has to print fake names earlier when the device * issn't registered yet, emit a string that has the same fake name in * it, so that earlier output links to this device. */ device_printf(dev, "registered as interrupt controller ic%d\n", ic->ic_idx); } void arm_setup_irqhandler(device_t dev, driver_filter_t *filt, void (*hand)(void*), void *arg, int resirq, int flags, void **cookiep) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; const char *name; int error; int irqidx; if (flags & INTR_IPI) { ic = arm_ipi_pic; irqidx = resirq; /* resirq is the same as hwirq for IPIs */ KASSERT(ic != NULL, ("%s: no interrupt controller for IPIs", __FUNCTION__)); ih = ipi_handlers[irqidx]; KASSERT(ih != NULL, ("%s: interrupt handler for %s IPI %u not found", __FUNCTION__, device_get_nameunit(ic->ic_dev), irqidx)); KASSERT(irqidx < ARM_IPI_COUNT, ("IPI number too big: %u", irqidx)); name = ipi_names[irqidx]; debugf("setup ipi %u (%s)\n", irqidx, name); } else { irqidx = resirq_decode(resirq, &ic, &ih); name = device_get_nameunit(dev); debugf("setup irq %s.%d on %s\n", device_get_nameunit(ic->ic_dev), ih->ih_hwirq, name); } if (ih->ih_event == NULL) { error = intr_event_create(&ih->ih_event, ih, 0, resirq, (mask_fn)arm_mask_irq, (mask_fn)arm_unmask_irq, arm_eoi, NULL, "ic%d.%d:", ic->ic_idx, ih->ih_hwirq); if (error) { device_printf(dev, "intr_event_create() failed " "for irq %s.%u\n", device_get_nameunit(ic->ic_dev), ih->ih_hwirq); return; } intrcnt_last_printed += 1 + snprintf(intrnames + intrcnt_last_printed, INTRNAME_LEN, "%s:%d: %s", device_get_nameunit(ic->ic_dev), irqidx, name); ih->ih_intrcnt_idx = intrcnt_index++; } if (!TAILQ_EMPTY(&ih->ih_event->ie_handlers)) arm_mask_irq(ih); intr_event_add_handler(ih->ih_event, name, filt, hand, arg, intr_priority(flags), flags, cookiep); arm_unmask_irq(ih); } int arm_remove_irqhandler(int resirq, void *cookie) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; int error; resirq_decode(resirq, &ic, &ih); arm_mask_irq(ih); error = intr_event_remove_handler(cookie); if (!TAILQ_EMPTY(&ih->ih_event->ie_handlers)) arm_unmask_irq(ih); return (error); } static void arm_mask_irq(void *arg) { struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; PIC_MASK(ih->ih_ic->ic_dev, ih->ih_hwirq); } static void arm_unmask_irq(void *arg) { struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; PIC_UNMASK(ih->ih_ic->ic_dev, ih->ih_hwirq); } static void arm_eoi(void *arg) { struct arm_intr_handler *ih = (struct arm_intr_handler *)arg; PIC_EOI(ih->ih_ic->ic_dev, ih->ih_hwirq); } int arm_intrng_config_irq(int resirq, enum intr_trigger trig, enum intr_polarity pol) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; resirq_decode(resirq, &ic, &ih); return PIC_CONFIG(ic->ic_dev, ih->ih_hwirq, trig, pol); } #ifdef SMP void arm_ipi_map_irq(device_t dev, u_int ipi, u_int hwirq) { struct arm_intr_controller *ic; struct arm_intr_handler *ih; ic = ic_from_dev(dev); KASSERT(ic != NULL, ("ipi controller not registered")); ih = ih_from_hwirq(ic, hwirq); KASSERT(ih == NULL, ("handler already registered for IPI %u", ipi)); ih = ic_add_ih(ic, NULL, 0); ih->ih_hwirq = hwirq; ic_index_ih_by_hwirq(ic, ih); ipi_handlers[ipi] = ih; debugf("ipi %u mapped to %s.%u\n", ipi, device_get_nameunit(dev), hwirq); } void arm_init_secondary_ic(void) { KASSERT(arm_ipi_pic != NULL, ("%s: no IPI PIC attached", __FUNCTION__)); PIC_INIT_SECONDARY(arm_ipi_pic->ic_dev); } void pic_ipi_send(cpuset_t cpus, u_int ipi) { KASSERT(ipi < ARM_IPI_COUNT, ("invalid IPI %u", ipi)); KASSERT(ipi_handlers[ipi] != NULL, ("no handler for IPI %u", ipi)); PIC_IPI_SEND(ipi_handlers[ipi]->ih_ic->ic_dev, cpus, ipi); } void pic_ipi_clear(int ipi) { KASSERT(ipi < ARM_IPI_COUNT, ("invalid IPI %u", ipi)); KASSERT(ipi_handlers[ipi] != NULL, ("no handler for IPI %u", ipi)); PIC_IPI_CLEAR(ipi_handlers[ipi]->ih_ic->ic_dev, ipi); } int pic_ipi_read(int ipi) { KASSERT(arm_ipi_pic != NULL, ("no IPI interrupt controller")); return (PIC_IPI_READ(arm_ipi_pic->ic_dev, ipi)); } void arm_unmask_ipi(u_int ipi) { KASSERT(ipi < ARM_IPI_COUNT, ("invalid IPI %u", ipi)); KASSERT(ipi_handlers[ipi] != NULL, ("no handler for IPI %u", ipi)); PIC_UNMASK(ipi_handlers[ipi]->ih_ic->ic_dev, ipi); } void arm_mask_ipi(u_int ipi) { KASSERT(ipi < ARM_IPI_COUNT, ("invalid IPI %u", ipi)); KASSERT(ipi_handlers[ipi] != NULL, ("no handler for IPI %u", ipi)); PIC_MASK(ipi_handlers[ipi]->ih_ic->ic_dev, ipi); } #endif void dosoftints(void); void dosoftints(void) { } /* * arm_irq_memory_barrier() * * Ensure all writes to device memory have reached devices before proceeding. * * This is intended to be called from the post-filter and post-thread routines * of an interrupt controller implementation. A peripheral device driver should * use bus_space_barrier() if it needs to ensure a write has reached the * hardware for some reason other than clearing interrupt conditions. * * The need for this function arises from the ARM weak memory ordering model. * Writes to locations mapped with the Device attribute bypass any caches, but * are buffered. Multiple writes to the same device will be observed by that * device in the order issued by the cpu. Writes to different devices may * appear at those devices in a different order than issued by the cpu. That * is, if the cpu writes to device A then device B, the write to device B could * complete before the write to device A. * * Consider a typical device interrupt handler which services the interrupt and * writes to a device status-acknowledge register to clear the interrupt before * returning. That write is posted to the L2 controller which "immediately" * places it in a store buffer and automatically drains that buffer. This can * be less immediate than you'd think... There may be no free slots in the store * buffers, so an existing buffer has to be drained first to make room. The * target bus may be busy with other traffic (such as DMA for various devices), * delaying the drain of the store buffer for some indeterminate time. While * all this delay is happening, execution proceeds on the CPU, unwinding its way * out of the interrupt call stack to the point where the interrupt driver code * is ready to EOI and unmask the interrupt. The interrupt controller may be * accessed via a faster bus than the hardware whose handler just ran; the write * to unmask and EOI the interrupt may complete quickly while the device write * to ack and clear the interrupt source is still lingering in a store buffer * waiting for access to a slower bus. With the interrupt unmasked at the * interrupt controller but still active at the device, as soon as interrupts * are enabled on the core the device re-interrupts immediately: now you've got * a spurious interrupt on your hands. * * The right way to fix this problem is for every device driver to use the * proper bus_space_barrier() calls in its interrupt handler. For ARM a single * barrier call at the end of the handler would work. This would have to be * done to every driver in the system, not just arm-specific drivers. * * Another potential fix is to map all device memory as Strongly-Ordered rather * than Device memory, which takes the store buffers out of the picture. This * has a pretty big impact on overall system performance, because each strongly * ordered memory access causes all L2 store buffers to be drained. * * A compromise solution is to have the interrupt controller implementation call * this function to establish a barrier between writes to the interrupt-source * device and writes to the interrupt controller device. * * This takes the interrupt number as an argument, and currently doesn't use it. * The plan is that maybe some day there is a way to flag certain interrupts as * "memory barrier safe" and we can avoid this overhead with them. */ void arm_irq_memory_barrier(uintptr_t irq) { dsb(); cpu_l2cache_drain_writebuf(); } Index: projects/arm_intrng/sys/arm/include/fdt.h =================================================================== --- projects/arm_intrng/sys/arm/include/fdt.h (revision 277020) +++ projects/arm_intrng/sys/arm/include/fdt.h (revision 277021) @@ -1,73 +1,71 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_FDT_H_ #define _MACHINE_FDT_H_ #include #include #include #include #include #if defined(ARM_INTRNG) /* Max interrupt number */ #define FDT_INTR_MAX (0xffff) /* Map phandle/intpin pair to global IRQ number */ #define FDT_MAP_IRQ(node, pin) (arm_fdt_map_irq(node, pin)) -#define FDT_DESCRIBE_IRQ(irq) (arm_describe_irq(irq)) #else /* Max interrupt number */ #define FDT_INTR_MAX NIRQ /* Map phandle/intpin pair to global IRQ number */ #define FDT_MAP_IRQ(node, pin) (pin) -#define FDT_DESCRIBE_IRQ(irq) (arm_describe_irq(irq)) #endif /* ARM_INTRNG */ /* * Bus space tag. XXX endianess info needs to be derived from the blob. */ extern bus_space_tag_t fdtbus_bs_tag; struct arm_devmap_entry; int fdt_localbus_devmap(phandle_t, struct arm_devmap_entry *, int, int *); #endif /* _MACHINE_FDT_H_ */ Index: projects/arm_intrng/sys/arm/include/intr.h =================================================================== --- projects/arm_intrng/sys/arm/include/intr.h (revision 277020) +++ projects/arm_intrng/sys/arm/include/intr.h (revision 277021) @@ -1,130 +1,129 @@ /* $NetBSD: intr.h,v 1.7 2003/06/16 20:01:00 thorpej Exp $ */ /*- * Copyright (c) 1997 Mark Brinicombe. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Mark Brinicombe * for the NetBSD Project. * 4. The name of the company nor the name of the author may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #ifndef _MACHINE_INTR_H_ #define _MACHINE_INTR_H_ #include #include #include #include "opt_global.h" #if defined(ARM_INTRNG) #define NIRQ 255 #define NPIC 16 #define INTR_CONTROLLER INTR_MD1 #define INTR_IPI INTR_MD2 #define CORE_PIC_IDX (0) #define CORE_PIC_NODE (0xffffffff) /* Interrupt controller features used in arm_register_pic(): */ #define PIC_FEATURE_IPI 0x1 void arm_register_pic(device_t dev, int features); void arm_unregister_pic(device_t dev); void arm_dispatch_irq(device_t dev, struct trapframe *tf, int irq); void arm_setup_irqhandler(device_t dev, int (*)(void*), void (*)(void*), void *, int, int, void **); int arm_remove_irqhandler(int, void *); int arm_intrng_config_irq(int, enum intr_trigger, enum intr_polarity); #ifdef SMP void arm_init_secondary_ic(void); void arm_ipi_map_irq(device_t, u_int, u_int); void arm_unmask_ipi(u_int); void arm_mask_ipi(u_int); #endif #else /* XXX move to std.* files? */ #ifdef CPU_XSCALE_81342 #define NIRQ 128 #elif defined(CPU_XSCALE_PXA2X0) #include #define NIRQ IRQ_GPIO_MAX #elif defined(SOC_MV_DISCOVERY) #define NIRQ 96 #elif defined(CPU_ARM9) || defined(SOC_MV_KIRKWOOD) || \ defined(CPU_XSCALE_IXP435) #define NIRQ 64 #elif defined(CPU_CORTEXA) #define NIRQ 1020 #elif defined(CPU_KRAIT) #define NIRQ 288 #elif defined(CPU_ARM1136) || defined(CPU_ARM1176) #define NIRQ 128 #elif defined(SOC_MV_ARMADAXP) #define MAIN_IRQ_NUM 116 #define ERR_IRQ_NUM 32 #define ERR_IRQ (MAIN_IRQ_NUM) #define MSI_IRQ_NUM 32 #define MSI_IRQ (ERR_IRQ + ERR_IRQ_NUM) #define NIRQ (MAIN_IRQ_NUM + ERR_IRQ_NUM + MSI_IRQ_NUM) #else #define NIRQ 32 #endif int arm_get_next_irq(int); void arm_mask_irq(uintptr_t); void arm_unmask_irq(uintptr_t); void arm_setup_irqhandler(const char *, int (*)(void*), void (*)(void*), void *, int, int, void **); int arm_remove_irqhandler(int, void *); extern void (*arm_post_filter)(void *); extern int (*arm_config_irq)(int irq, enum intr_trigger trig, enum intr_polarity pol); #endif /* !ARM_INTRNG */ -const char *arm_describe_irq(int irq); void arm_intrnames_init(void); void arm_irq_memory_barrier(uintptr_t); void arm_init_secondary_ic(void); int gic_decode_fdt(uint32_t iparentnode, uint32_t *intrcells, int *interrupt, int *trig, int *pol); #ifdef FDT int arm_fdt_map_irq(phandle_t, pcell_t *, int); #endif #endif /* _MACHINE_INTR_H */ Index: projects/arm_intrng/sys/dev/fdt/fdt_common.h =================================================================== --- projects/arm_intrng/sys/dev/fdt/fdt_common.h (revision 277020) +++ projects/arm_intrng/sys/dev/fdt/fdt_common.h (revision 277021) @@ -1,100 +1,101 @@ /*- * Copyright (c) 2009-2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _FDT_COMMON_H_ #define _FDT_COMMON_H_ #include #include #include #define FDT_MEM_REGIONS 8 #define DI_MAX_INTR_NUM 32 struct fdt_sense_level { enum intr_trigger trig; enum intr_polarity pol; }; typedef int (*fdt_pic_decode_t)(phandle_t, pcell_t *, int *, int *, int *); extern fdt_pic_decode_t fdt_pic_table[]; typedef void (*fdt_fixup_t)(phandle_t); struct fdt_fixup_entry { char *model; fdt_fixup_t handler; }; extern struct fdt_fixup_entry fdt_fixup_table[]; extern SLIST_HEAD(fdt_ic_list, fdt_ic) fdt_ic_list_head; struct fdt_ic { SLIST_ENTRY(fdt_ic) fdt_ics; ihandle_t iph; device_t dev; }; extern vm_paddr_t fdt_immr_pa; extern vm_offset_t fdt_immr_va; extern vm_offset_t fdt_immr_size; struct fdt_pm_mask_entry { char *compat; uint32_t mask; }; extern struct fdt_pm_mask_entry fdt_pm_mask_table[]; #if defined(FDT_DTB_STATIC) extern u_char fdt_static_dtb; #endif int fdt_addrsize_cells(phandle_t, int *, int *); u_long fdt_data_get(void *, int); int fdt_data_to_res(pcell_t *, int, int, u_long *, u_long *); +int fdt_describe_irq(char *, u_int, u_int); phandle_t fdt_find_compatible(phandle_t, const char *, int); phandle_t fdt_depth_search_compatible(phandle_t, const char *, int); int fdt_get_mem_regions(struct mem_region *, int *, uint32_t *); int fdt_get_reserved_regions(struct mem_region *, int *); int fdt_get_phyaddr(phandle_t, device_t, int *, void **); int fdt_get_range(phandle_t, int, u_long *, u_long *); int fdt_immr_addr(vm_offset_t); int fdt_regsize(phandle_t, u_long *, u_long *); int fdt_is_compatible(phandle_t, const char *); int fdt_is_compatible_strict(phandle_t, const char *); int fdt_is_enabled(phandle_t); int fdt_pm_is_enabled(phandle_t); int fdt_is_type(phandle_t, const char *); int fdt_parent_addr_cells(phandle_t); int fdt_reg_to_rl(phandle_t, struct resource_list *); int fdt_pm(phandle_t); int fdt_get_unit(device_t); #endif /* _FDT_COMMON_H_ */ Index: projects/arm_intrng/sys/dev/fdt/simplebus.c =================================================================== --- projects/arm_intrng/sys/dev/fdt/simplebus.c (revision 277020) +++ projects/arm_intrng/sys/dev/fdt/simplebus.c (revision 277021) @@ -1,439 +1,443 @@ /*- * Copyright (c) 2013 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include -#include +#include struct simplebus_range { uint64_t bus; uint64_t host; uint64_t size; }; struct simplebus_softc { device_t dev; phandle_t node; struct simplebus_range *ranges; int nranges; pcell_t acells, scells; }; struct simplebus_devinfo { struct ofw_bus_devinfo obdinfo; struct resource_list rl; }; /* * Bus interface. */ static int simplebus_probe(device_t dev); static int simplebus_attach(device_t dev); static struct resource *simplebus_alloc_resource(device_t, device_t, int, int *, u_long, u_long, u_long, u_int); static void simplebus_probe_nomatch(device_t bus, device_t child); static int simplebus_print_child(device_t bus, device_t child); static int simplebus_print_irqs(struct resource_list *rl); /* * ofw_bus interface */ static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, device_t child); /* * local methods */ static int simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc); static struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, phandle_t node); /* * Driver methods. */ static device_method_t simplebus_methods[] = { /* Device interface */ DEVMETHOD(device_probe, simplebus_probe), DEVMETHOD(device_attach, simplebus_attach), /* Bus interface */ DEVMETHOD(bus_print_child, simplebus_print_child), DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; static driver_t simplebus_driver = { "simplebus", simplebus_methods, sizeof(struct simplebus_softc) }; static devclass_t simplebus_devclass; EARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass, 0, 0, BUS_PASS_BUS); EARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); static int simplebus_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); /* * FDT data puts a "simple-bus" compatible string on many things that * have children but aren't really busses in our world. Without a * ranges property we will fail to attach, so just fail to probe too. */ if (!(ofw_bus_is_compatible(dev, "simple-bus") && ofw_bus_has_prop(dev, "ranges")) && (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), "soc") != 0)) return (ENXIO); device_set_desc(dev, "Flattened device tree simple bus"); return (BUS_PROBE_GENERIC); } static int simplebus_attach(device_t dev) { struct simplebus_softc *sc; struct simplebus_devinfo *di; phandle_t node; device_t cdev; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); sc->dev = dev; sc->node = node; /* * Some important numbers */ sc->acells = 2; OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); sc->scells = 1; OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); if (simplebus_fill_ranges(node, sc) < 0) { device_printf(dev, "could not get ranges\n"); return (ENXIO); } /* * In principle, simplebus could have an interrupt map, but ignore that * for now */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { if ((di = simplebus_setup_dinfo(dev, node)) == NULL) continue; cdev = device_add_child(dev, NULL, -1); if (cdev == NULL) { device_printf(dev, "<%s>: device_add_child failed\n", di->obdinfo.obd_name); resource_list_free(&di->rl); ofw_bus_gen_destroy_devinfo(&di->obdinfo); free(di, M_DEVBUF); continue; } device_set_ivars(cdev, di); } return (bus_generic_attach(dev)); } static int simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) { int host_address_cells; cell_t *base_ranges; ssize_t nbase_ranges; int err; int i, j, k; err = OF_searchencprop(OF_parent(node), "#address-cells", &host_address_cells, sizeof(host_address_cells)); if (err <= 0) return (-1); nbase_ranges = OF_getproplen(node, "ranges"); if (nbase_ranges < 0) return (-1); sc->nranges = nbase_ranges / sizeof(cell_t) / (sc->acells + host_address_cells + sc->scells); if (sc->nranges == 0) return (0); sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), M_DEVBUF, M_WAITOK); base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); OF_getencprop(node, "ranges", base_ranges, nbase_ranges); for (i = 0, j = 0; i < sc->nranges; i++) { sc->ranges[i].bus = 0; for (k = 0; k < sc->acells; k++) { sc->ranges[i].bus <<= 32; sc->ranges[i].bus |= base_ranges[j++]; } sc->ranges[i].host = 0; for (k = 0; k < host_address_cells; k++) { sc->ranges[i].host <<= 32; sc->ranges[i].host |= base_ranges[j++]; } sc->ranges[i].size = 0; for (k = 0; k < sc->scells; k++) { sc->ranges[i].size <<= 32; sc->ranges[i].size |= base_ranges[j++]; } } free(base_ranges, M_DEVBUF); return (sc->nranges); } static struct simplebus_devinfo * simplebus_setup_dinfo(device_t dev, phandle_t node) { struct simplebus_softc *sc; struct simplebus_devinfo *ndi; uint32_t *reg; uint64_t phys, size; int i, j, k; int nreg; sc = device_get_softc(dev); ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { free(ndi, M_DEVBUF); return (NULL); } resource_list_init(&ndi->rl); nreg = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); if (nreg == -1) nreg = 0; if (nreg % (sc->acells + sc->scells) != 0) { if (bootverbose) device_printf(dev, "Malformed reg property on <%s>\n", ndi->obdinfo.obd_name); nreg = 0; } for (i = 0, k = 0; i < nreg; i += sc->acells + sc->scells, k++) { phys = size = 0; for (j = 0; j < sc->acells; j++) { phys <<= 32; phys |= reg[i + j]; } for (j = 0; j < sc->scells; j++) { size <<= 32; size |= reg[i + sc->acells + j]; } resource_list_add(&ndi->rl, SYS_RES_MEMORY, k, phys, phys + size - 1, size); } free(reg, M_OFWPROP); ofw_bus_intr_to_rl(dev, node, &ndi->rl); return (ndi); } static const struct ofw_bus_devinfo * simplebus_get_devinfo(device_t bus __unused, device_t child) { struct simplebus_devinfo *ndi; ndi = device_get_ivars(child); return (&ndi->obdinfo); } static struct resource * simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct simplebus_softc *sc; struct simplebus_devinfo *di; struct resource_list_entry *rle; int j; sc = device_get_softc(bus); /* * Request for the default allocation with a given rid: use resource * list stored in the local device info. */ if ((start == 0UL) && (end == ~0UL)) { if ((di = device_get_ivars(child)) == NULL) return (NULL); if (type == SYS_RES_IOPORT) type = SYS_RES_MEMORY; rle = resource_list_find(&di->rl, type, *rid); if (rle == NULL) { if (bootverbose) device_printf(bus, "no default resources for " "rid = %d, type = %d\n", *rid, type); return (NULL); } start = rle->start; end = rle->end; count = rle->count; } if (type == SYS_RES_MEMORY) { /* Remap through ranges property */ for (j = 0; j < sc->nranges; j++) { if (start >= sc->ranges[j].bus && end < sc->ranges[j].bus + sc->ranges[j].size) { start -= sc->ranges[j].bus; start += sc->ranges[j].host; end -= sc->ranges[j].bus; end += sc->ranges[j].host; break; } } if (j == sc->nranges && sc->nranges != 0) { if (bootverbose) device_printf(bus, "Could not map resource " "%#lx-%#lx\n", start, end); return (NULL); } } return (bus_generic_alloc_resource(bus, child, type, rid, start, end, count, flags)); } static int simplebus_print_res(struct simplebus_devinfo *di) { int rv; rv = 0; rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); rv += simplebus_print_irqs(&di->rl); return (rv); } static void simplebus_probe_nomatch(device_t bus, device_t child) { const char *name, *type, *compat; if (!bootverbose) return; name = ofw_bus_get_name(child); type = ofw_bus_get_type(child); compat = ofw_bus_get_compat(child); device_printf(bus, "<%s>", name != NULL ? name : "unknown"); simplebus_print_res(device_get_ivars(child)); if (!ofw_bus_status_okay(child)) printf(" disabled"); if (type) printf(" type %s", type); if (compat) printf(" compat %s", compat); printf(" (no driver attached)\n"); } static int simplebus_print_irqs(struct resource_list *rl) { struct resource_list_entry *rle; - int printed, retval; + int err, printed, retval; + char buf[16]; printed = 0; retval = 0; STAILQ_FOREACH(rle, rl, link) { if (rle->type != SYS_RES_IRQ) continue; - retval += printf("%s", printed ? "," : " irq "); - retval += printf("%s", FDT_DESCRIBE_IRQ(rle->start)); + err = fdt_describe_irq(buf, sizeof(buf), rle->start); + if (err < 0) + snprintf(buf, sizeof(buf), "???"); + + retval += printf("%s%s", printed ? "," : " irq ", buf); printed++; } return (retval); } static int simplebus_print_child(device_t bus, device_t child) { int rv; rv = bus_print_child_header(bus, child); rv += simplebus_print_res(device_get_ivars(child)); if (!ofw_bus_status_okay(child)) rv += printf(" disabled"); rv += bus_print_child_footer(bus, child); return (rv); } Index: projects/arm_intrng/sys/powerpc/powerpc/intr_machdep.c =================================================================== --- projects/arm_intrng/sys/powerpc/powerpc/intr_machdep.c (revision 277020) +++ projects/arm_intrng/sys/powerpc/powerpc/intr_machdep.c (revision 277021) @@ -1,606 +1,620 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2002 Benno Rice. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20 * * $FreeBSD$ */ #include "opt_isa.h" +#include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#ifdef FDT +#include +#endif + #include "pic_if.h" #define MAX_STRAY_LOG 5 static MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data"); struct powerpc_intr { struct intr_event *event; long *cntp; u_int irq; device_t pic; u_int intline; u_int vector; u_int cntindex; cpuset_t cpu; enum intr_trigger trig; enum intr_polarity pol; int fwcode; int ipi; }; struct pic { device_t dev; uint32_t node; u_int irqs; u_int ipis; int base; }; static u_int intrcnt_index = 0; static struct mtx intr_table_lock; static struct powerpc_intr *powerpc_intrs[INTR_VECTORS]; static struct pic piclist[MAX_PICS]; static u_int nvectors; /* Allocated vectors */ static u_int npics; /* PICs registered */ #ifdef DEV_ISA static u_int nirqs = 16; /* Allocated IRQS (ISA pre-allocated). */ #else static u_int nirqs = 0; /* Allocated IRQs. */ #endif static u_int stray_count; device_t root_pic; #ifdef SMP static void *ipi_cookie; #endif static void intr_init(void *dummy __unused) { mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); #ifdef SMP static void smp_intr_init(void *dummy __unused) { struct powerpc_intr *i; int vector; for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i != NULL && i->pic == root_pic) PIC_BIND(i->pic, i->intline, i->cpu); } } SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL); #endif static void intrcnt_setname(const char *name, int index) { snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name); } void intrcnt_add(const char *name, u_long **countp) { int idx; idx = atomic_fetchadd_int(&intrcnt_index, 1); *countp = &intrcnt[idx]; intrcnt_setname(name, idx); } static struct powerpc_intr * intr_lookup(u_int irq) { char intrname[16]; struct powerpc_intr *i, *iscan; int vector; mtx_lock(&intr_table_lock); for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i != NULL && i->irq == irq) { mtx_unlock(&intr_table_lock); return (i); } } i = malloc(sizeof(*i), M_INTR, M_NOWAIT); if (i == NULL) { mtx_unlock(&intr_table_lock); return (NULL); } i->event = NULL; i->cntp = NULL; i->trig = INTR_TRIGGER_CONFORM; i->pol = INTR_POLARITY_CONFORM; i->irq = irq; i->pic = NULL; i->vector = -1; i->fwcode = 0; i->ipi = 0; #ifdef SMP i->cpu = all_cpus; #else CPU_SETOF(0, &i->cpu); #endif for (vector = 0; vector < INTR_VECTORS && vector <= nvectors; vector++) { iscan = powerpc_intrs[vector]; if (iscan != NULL && iscan->irq == irq) break; if (iscan == NULL && i->vector == -1) i->vector = vector; iscan = NULL; } if (iscan == NULL && i->vector != -1) { powerpc_intrs[i->vector] = i; i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1); i->cntp = &intrcnt[i->cntindex]; sprintf(intrname, "irq%u:", i->irq); intrcnt_setname(intrname, i->cntindex); nvectors++; } mtx_unlock(&intr_table_lock); if (iscan != NULL || i->vector == -1) { free(i, M_INTR); i = iscan; } return (i); } static int powerpc_map_irq(struct powerpc_intr *i) { struct pic *p; u_int cnt; int idx; for (idx = 0; idx < npics; idx++) { p = &piclist[idx]; cnt = p->irqs + p->ipis; if (i->irq >= p->base && i->irq < p->base + cnt) break; } if (idx == npics) return (EINVAL); i->intline = i->irq - p->base; i->pic = p->dev; /* Try a best guess if that failed */ if (i->pic == NULL) i->pic = root_pic; return (0); } static void powerpc_intr_eoi(void *arg) { struct powerpc_intr *i = arg; PIC_EOI(i->pic, i->intline); } static void powerpc_intr_pre_ithread(void *arg) { struct powerpc_intr *i = arg; PIC_MASK(i->pic, i->intline); PIC_EOI(i->pic, i->intline); } static void powerpc_intr_post_ithread(void *arg) { struct powerpc_intr *i = arg; PIC_UNMASK(i->pic, i->intline); } static int powerpc_assign_intr_cpu(void *arg, int cpu) { #ifdef SMP struct powerpc_intr *i = arg; if (cpu == NOCPU) i->cpu = all_cpus; else CPU_SETOF(cpu, &i->cpu); if (!cold && i->pic != NULL && i->pic == root_pic) PIC_BIND(i->pic, i->intline, i->cpu); return (0); #else return (EOPNOTSUPP); #endif } void powerpc_register_pic(device_t dev, uint32_t node, u_int irqs, u_int ipis, u_int atpic) { struct pic *p; u_int irq; int idx; mtx_lock(&intr_table_lock); /* XXX see powerpc_get_irq(). */ for (idx = 0; idx < npics; idx++) { p = &piclist[idx]; if (p->node != node) continue; if (node != 0 || p->dev == dev) break; } p = &piclist[idx]; p->dev = dev; p->node = node; p->irqs = irqs; p->ipis = ipis; if (idx == npics) { #ifdef DEV_ISA p->base = (atpic) ? 0 : nirqs; #else p->base = nirqs; #endif irq = p->base + irqs + ipis; nirqs = MAX(nirqs, irq); npics++; } mtx_unlock(&intr_table_lock); } u_int powerpc_get_irq(uint32_t node, u_int pin) { int idx; if (node == 0) return (pin); mtx_lock(&intr_table_lock); for (idx = 0; idx < npics; idx++) { if (piclist[idx].node == node) { mtx_unlock(&intr_table_lock); return (piclist[idx].base + pin); } } /* * XXX we should never encounter an unregistered PIC, but that * can only be done when we properly support bus enumeration * using multiple passes. Until then, fake an entry and give it * some adhoc maximum number of IRQs and IPIs. */ piclist[idx].dev = NULL; piclist[idx].node = node; piclist[idx].irqs = 124; piclist[idx].ipis = 4; piclist[idx].base = nirqs; nirqs += 128; npics++; mtx_unlock(&intr_table_lock); return (piclist[idx].base + pin); } int powerpc_enable_intr(void) { struct powerpc_intr *i; int error, vector; #ifdef SMP int n; #endif if (npics == 0) panic("no PIC detected\n"); if (root_pic == NULL) root_pic = piclist[0].dev; #ifdef SMP /* Install an IPI handler. */ if (mp_ncpus > 1) { for (n = 0; n < npics; n++) { if (piclist[n].dev != root_pic) continue; KASSERT(piclist[n].ipis != 0, ("%s: SMP root PIC does not supply any IPIs", __func__)); error = powerpc_setup_intr("IPI", MAP_IRQ(piclist[n].node, piclist[n].irqs), powerpc_ipi_handler, NULL, NULL, INTR_TYPE_MISC | INTR_EXCL, &ipi_cookie); if (error) { printf("unable to setup IPI handler\n"); return (error); } /* * Some subterfuge: disable late EOI and mark this * as an IPI to the dispatch layer. */ i = intr_lookup(MAP_IRQ(piclist[n].node, piclist[n].irqs)); i->event->ie_post_filter = NULL; i->ipi = 1; } } #endif for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i == NULL) continue; error = powerpc_map_irq(i); if (error) continue; if (i->trig == -1) PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); if (i->trig != INTR_TRIGGER_CONFORM || i->pol != INTR_POLARITY_CONFORM) PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); if (i->event != NULL) PIC_ENABLE(i->pic, i->intline, vector); } return (0); } int powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) { struct powerpc_intr *i; int error, enable = 0; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); if (i->event == NULL) { error = intr_event_create(&i->event, (void *)i, 0, irq, powerpc_intr_pre_ithread, powerpc_intr_post_ithread, powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq); if (error) return (error); enable = 1; } error = intr_event_add_handler(i->event, name, filter, handler, arg, intr_priority(flags), flags, cookiep); mtx_lock(&intr_table_lock); intrcnt_setname(i->event->ie_fullname, i->cntindex); mtx_unlock(&intr_table_lock); if (!cold) { error = powerpc_map_irq(i); if (!error) { if (i->trig == -1) PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); if (i->trig != INTR_TRIGGER_CONFORM || i->pol != INTR_POLARITY_CONFORM) PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); if (i->pic == root_pic) PIC_BIND(i->pic, i->intline, i->cpu); if (enable) PIC_ENABLE(i->pic, i->intline, i->vector); } } return (error); } int powerpc_teardown_intr(void *cookie) { return (intr_event_remove_handler(cookie)); } #ifdef SMP int powerpc_bind_intr(u_int irq, u_char cpu) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); return (intr_event_bind(i->event, cpu)); } #endif int powerpc_fw_config_intr(int irq, int sense_code) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); i->trig = -1; i->pol = INTR_POLARITY_CONFORM; i->fwcode = sense_code; if (!cold && i->pic != NULL) { PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); } return (0); } int powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); i->trig = trig; i->pol = pol; if (!cold && i->pic != NULL) PIC_CONFIG(i->pic, i->intline, trig, pol); return (0); } void powerpc_dispatch_intr(u_int vector, struct trapframe *tf) { struct powerpc_intr *i; struct intr_event *ie; i = powerpc_intrs[vector]; if (i == NULL) goto stray; (*i->cntp)++; ie = i->event; KASSERT(ie != NULL, ("%s: interrupt without an event", __func__)); /* * IPIs are magical and need to be EOI'ed before filtering. * This prevents races in IPI handling. */ if (i->ipi) PIC_EOI(i->pic, i->intline); if (intr_event_handle(ie, tf) != 0) { goto stray; } return; stray: stray_count++; if (stray_count <= MAX_STRAY_LOG) { printf("stray irq %d\n", i ? i->irq : -1); if (stray_count >= MAX_STRAY_LOG) { printf("got %d stray interrupts, not logging anymore\n", MAX_STRAY_LOG); } } if (i != NULL) PIC_MASK(i->pic, i->intline); } + +#ifdef FDT +int +fdt_describe_irq(char *buf, u_int len, u_int irq) +{ + + return (snprintf(buf, len, "%u", irq)); +} +#endif