diff options
Diffstat (limited to 'arch/arm/common')
-rw-r--r-- | arch/arm/common/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/common/gic.c | 172 | ||||
-rw-r--r-- | arch/arm/common/it8152.c | 16 | ||||
-rw-r--r-- | arch/arm/common/pl330.c | 11 | ||||
-rw-r--r-- | arch/arm/common/timer-sp.c | 7 | ||||
-rw-r--r-- | arch/arm/common/via82c505.c | 3 | ||||
-rw-r--r-- | arch/arm/common/vic.c | 148 |
7 files changed, 285 insertions, 78 deletions
diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 74df9ca2be3..81a933eb090 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -1,8 +1,14 @@ config ARM_GIC select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + bool + +config GIC_NON_BANKED bool config ARM_VIC + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER bool config ARM_VIC_NR diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 410a546060a..c47d6199b78 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -40,13 +40,37 @@ #include <linux/slab.h> #include <asm/irq.h> +#include <asm/exception.h> +#include <asm/smp_plat.h> #include <asm/mach/irq.h> #include <asm/hardware/gic.h> -static DEFINE_RAW_SPINLOCK(irq_controller_lock); +union gic_base { + void __iomem *common_base; + void __percpu __iomem **percpu_base; +}; + +struct gic_chip_data { + unsigned int irq_offset; + union gic_base dist_base; + union gic_base cpu_base; +#ifdef CONFIG_CPU_PM + u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)]; + u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)]; + u32 saved_spi_target[DIV_ROUND_UP(1020, 4)]; + u32 __percpu *saved_ppi_enable; + u32 __percpu *saved_ppi_conf; +#endif +#ifdef CONFIG_IRQ_DOMAIN + struct irq_domain domain; +#endif + unsigned int gic_irqs; +#ifdef CONFIG_GIC_NON_BANKED + void __iomem *(*get_base)(union gic_base *); +#endif +}; -/* Address of GIC 0 CPU interface */ -void __iomem *gic_cpu_base_addr __read_mostly; +static DEFINE_RAW_SPINLOCK(irq_controller_lock); /* * Supported arch specific GIC irq extension. @@ -67,16 +91,48 @@ struct irq_chip gic_arch_extn = { static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; +#ifdef CONFIG_GIC_NON_BANKED +static void __iomem *gic_get_percpu_base(union gic_base *base) +{ + return *__this_cpu_ptr(base->percpu_base); +} + +static void __iomem *gic_get_common_base(union gic_base *base) +{ + return base->common_base; +} + +static inline void __iomem *gic_data_dist_base(struct gic_chip_data *data) +{ + return data->get_base(&data->dist_base); +} + +static inline void __iomem *gic_data_cpu_base(struct gic_chip_data *data) +{ + return data->get_base(&data->cpu_base); +} + +static inline void gic_set_base_accessor(struct gic_chip_data *data, + void __iomem *(*f)(union gic_base *)) +{ + data->get_base = f; +} +#else +#define gic_data_dist_base(d) ((d)->dist_base.common_base) +#define gic_data_cpu_base(d) ((d)->cpu_base.common_base) +#define gic_set_base_accessor(d,f) +#endif + static inline void __iomem *gic_dist_base(struct irq_data *d) { struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); - return gic_data->dist_base; + return gic_data_dist_base(gic_data); } static inline void __iomem *gic_cpu_base(struct irq_data *d) { struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); - return gic_data->cpu_base; + return gic_data_cpu_base(gic_data); } static inline unsigned int gic_irq(struct irq_data *d) @@ -215,6 +271,32 @@ static int gic_set_wake(struct irq_data *d, unsigned int on) #define gic_set_wake NULL #endif +asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) +{ + u32 irqstat, irqnr; + struct gic_chip_data *gic = &gic_data[0]; + void __iomem *cpu_base = gic_data_cpu_base(gic); + + do { + irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); + irqnr = irqstat & ~0x1c00; + + if (likely(irqnr > 15 && irqnr < 1021)) { + irqnr = irq_domain_to_irq(&gic->domain, irqnr); + handle_IRQ(irqnr, regs); + continue; + } + if (irqnr < 16) { + writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); +#ifdef CONFIG_SMP + handle_IPI(irqnr, regs); +#endif + continue; + } + break; + } while (1); +} + static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) { struct gic_chip_data *chip_data = irq_get_handler_data(irq); @@ -225,7 +307,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) chained_irq_enter(chip, desc); raw_spin_lock(&irq_controller_lock); - status = readl_relaxed(chip_data->cpu_base + GIC_CPU_INTACK); + status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK); raw_spin_unlock(&irq_controller_lock); gic_irq = (status & 0x3ff); @@ -270,12 +352,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic) u32 cpumask; unsigned int gic_irqs = gic->gic_irqs; struct irq_domain *domain = &gic->domain; - void __iomem *base = gic->dist_base; - u32 cpu = 0; - -#ifdef CONFIG_SMP - cpu = cpu_logical_map(smp_processor_id()); -#endif + void __iomem *base = gic_data_dist_base(gic); + u32 cpu = cpu_logical_map(smp_processor_id()); cpumask = 1 << cpu; cpumask |= cpumask << 8; @@ -330,8 +408,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic) static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) { - void __iomem *dist_base = gic->dist_base; - void __iomem *base = gic->cpu_base; + void __iomem *dist_base = gic_data_dist_base(gic); + void __iomem *base = gic_data_cpu_base(gic); int i; /* @@ -368,7 +446,7 @@ static void gic_dist_save(unsigned int gic_nr) BUG(); gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data[gic_nr].dist_base; + dist_base = gic_data_dist_base(&gic_data[gic_nr]); if (!dist_base) return; @@ -403,7 +481,7 @@ static void gic_dist_restore(unsigned int gic_nr) BUG(); gic_irqs = gic_data[gic_nr].gic_irqs; - dist_base = gic_data[gic_nr].dist_base; + dist_base = gic_data_dist_base(&gic_data[gic_nr]); if (!dist_base) return; @@ -439,8 +517,8 @@ static void gic_cpu_save(unsigned int gic_nr) if (gic_nr >= MAX_GIC_NR) BUG(); - dist_base = gic_data[gic_nr].dist_base; - cpu_base = gic_data[gic_nr].cpu_base; + dist_base = gic_data_dist_base(&gic_data[gic_nr]); + cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); if (!dist_base || !cpu_base) return; @@ -465,8 +543,8 @@ static void gic_cpu_restore(unsigned int gic_nr) if (gic_nr >= MAX_GIC_NR) BUG(); - dist_base = gic_data[gic_nr].dist_base; - cpu_base = gic_data[gic_nr].cpu_base; + dist_base = gic_data_dist_base(&gic_data[gic_nr]); + cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); if (!dist_base || !cpu_base) return; @@ -491,6 +569,11 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) int i; for (i = 0; i < MAX_GIC_NR; i++) { +#ifdef CONFIG_GIC_NON_BANKED + /* Skip over unused GICs */ + if (!gic_data[i].get_base) + continue; +#endif switch (cmd) { case CPU_PM_ENTER: gic_cpu_save(i); @@ -564,8 +647,9 @@ const struct irq_domain_ops gic_irq_domain_ops = { #endif }; -void __init gic_init(unsigned int gic_nr, int irq_start, - void __iomem *dist_base, void __iomem *cpu_base) +void __init gic_init_bases(unsigned int gic_nr, int irq_start, + void __iomem *dist_base, void __iomem *cpu_base, + u32 percpu_offset) { struct gic_chip_data *gic; struct irq_domain *domain; @@ -575,8 +659,36 @@ void __init gic_init(unsigned int gic_nr, int irq_start, gic = &gic_data[gic_nr]; domain = &gic->domain; - gic->dist_base = dist_base; - gic->cpu_base = cpu_base; +#ifdef CONFIG_GIC_NON_BANKED + if (percpu_offset) { /* Frankein-GIC without banked registers... */ + unsigned int cpu; + + gic->dist_base.percpu_base = alloc_percpu(void __iomem *); + gic->cpu_base.percpu_base = alloc_percpu(void __iomem *); + if (WARN_ON(!gic->dist_base.percpu_base || + !gic->cpu_base.percpu_base)) { + free_percpu(gic->dist_base.percpu_base); + free_percpu(gic->cpu_base.percpu_base); + return; + } + + for_each_possible_cpu(cpu) { + unsigned long offset = percpu_offset * cpu_logical_map(cpu); + *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset; + *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset; + } + + gic_set_base_accessor(gic, gic_get_percpu_base); + } else +#endif + { /* Normal, sane GIC... */ + WARN(percpu_offset, + "GIC_NON_BANKED not enabled, ignoring %08x offset!", + percpu_offset); + gic->dist_base.common_base = dist_base; + gic->cpu_base.common_base = cpu_base; + gic_set_base_accessor(gic, gic_get_common_base); + } /* * For primary GICs, skip over SGIs. @@ -584,8 +696,6 @@ void __init gic_init(unsigned int gic_nr, int irq_start, */ domain->hwirq_base = 32; if (gic_nr == 0) { - gic_cpu_base_addr = cpu_base; - if ((irq_start & 31) > 0) { domain->hwirq_base = 16; if (irq_start != -1) @@ -597,7 +707,7 @@ void __init gic_init(unsigned int gic_nr, int irq_start, * Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources. */ - gic_irqs = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f; + gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f; gic_irqs = (gic_irqs + 1) * 32; if (gic_irqs > 1020) gic_irqs = 1020; @@ -645,7 +755,7 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) dsb(); /* this always happens on GIC0 */ - writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); + writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); } #endif @@ -656,6 +766,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *cpu_base; void __iomem *dist_base; + u32 percpu_offset; int irq; struct irq_domain *domain = &gic_data[gic_cnt].domain; @@ -668,9 +779,12 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) cpu_base = of_iomap(node, 1); WARN(!cpu_base, "unable to map gic cpu registers\n"); + if (of_property_read_u32(node, "cpu-offset", &percpu_offset)) + percpu_offset = 0; + domain->of_node = of_node_get(node); - gic_init(gic_cnt, -1, dist_base, cpu_base); + gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset); if (parent) { irq = irq_of_parse_and_map(node, 0); diff --git a/arch/arm/common/it8152.c b/arch/arm/common/it8152.c index b539ec855e1..fb1f1cfce60 100644 --- a/arch/arm/common/it8152.c +++ b/arch/arm/common/it8152.c @@ -299,8 +299,8 @@ int __init it8152_pci_setup(int nr, struct pci_sys_data *sys) goto err1; } - sys->resource[0] = &it8152_io; - sys->resource[1] = &it8152_mem; + pci_add_resource(&sys->resources, &it8152_io); + pci_add_resource(&sys->resources, &it8152_mem); if (platform_notify || platform_notify_remove) { printk(KERN_ERR "PCI: Can't use platform_notify\n"); @@ -320,13 +320,9 @@ err0: return -EBUSY; } -/* - * If we set up a device for bus mastering, we need to check the latency - * timer as we don't have even crappy BIOSes to set it properly. - * The implementation is from arch/i386/pci/i386.c - */ -unsigned int pcibios_max_latency = 255; - +/* ITE bridge requires setting latency timer to avoid early bus access + termination by PCI bus master devices +*/ void pcibios_set_master(struct pci_dev *dev) { u8 lat; @@ -352,7 +348,7 @@ void pcibios_set_master(struct pci_dev *dev) struct pci_bus * __init it8152_pci_scan_bus(int nr, struct pci_sys_data *sys) { - return pci_scan_bus(nr, &it8152_ops, sys); + return pci_scan_root_bus(NULL, nr, &it8152_ops, sys, &sys->resources); } EXPORT_SYMBOL(dma_set_coherent_mask); diff --git a/arch/arm/common/pl330.c b/arch/arm/common/pl330.c index 8d8df744f7a..ff3ad224482 100644 --- a/arch/arm/common/pl330.c +++ b/arch/arm/common/pl330.c @@ -1467,13 +1467,19 @@ int pl330_update(const struct pl330_info *pi) /* Now that we are in no hurry, do the callbacks */ while (!list_empty(&pl330->req_done)) { + struct pl330_req *r; + rqdone = container_of(pl330->req_done.next, struct _pl330_req, rqd); list_del_init(&rqdone->rqd); + /* Detach the req */ + r = rqdone->r; + rqdone->r = NULL; + spin_unlock_irqrestore(&pl330->lock, flags); - _callback(rqdone->r, PL330_ERR_NONE); + _callback(r, PL330_ERR_NONE); spin_lock_irqsave(&pl330->lock, flags); } @@ -1496,12 +1502,13 @@ int pl330_chan_ctrl(void *ch_id, enum pl330_chan_op op) struct pl330_thread *thrd = ch_id; struct pl330_dmac *pl330; unsigned long flags; - int ret = 0, active = thrd->req_running; + int ret = 0, active; if (!thrd || thrd->free || thrd->dmac->state == DYING) return -EINVAL; pl330 = thrd->dmac; + active = thrd->req_running; spin_lock_irqsave(&pl330->lock, flags); diff --git a/arch/arm/common/timer-sp.c b/arch/arm/common/timer-sp.c index 2393b5bc96f..8794a34eae6 100644 --- a/arch/arm/common/timer-sp.c +++ b/arch/arm/common/timer-sp.c @@ -143,7 +143,6 @@ static int sp804_set_next_event(unsigned long next, } static struct clock_event_device sp804_clockevent = { - .shift = 32, .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, .set_mode = sp804_set_mode, .set_next_event = sp804_set_next_event, @@ -169,13 +168,9 @@ void __init sp804_clockevents_init(void __iomem *base, unsigned int irq, clkevt_base = base; clkevt_reload = DIV_ROUND_CLOSEST(rate, HZ); - evt->name = name; evt->irq = irq; - evt->mult = div_sc(rate, NSEC_PER_SEC, evt->shift); - evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); - evt->min_delta_ns = clockevent_delta2ns(0xf, evt); setup_irq(irq, &sp804_timer_irq); - clockevents_register_device(evt); + clockevents_config_and_register(evt, rate, 0xf, 0xffffffff); } diff --git a/arch/arm/common/via82c505.c b/arch/arm/common/via82c505.c index 8421d39109b..67dd2affc57 100644 --- a/arch/arm/common/via82c505.c +++ b/arch/arm/common/via82c505.c @@ -86,7 +86,8 @@ int __init via82c505_setup(int nr, struct pci_sys_data *sys) struct pci_bus * __init via82c505_scan_bus(int nr, struct pci_sys_data *sysdata) { if (nr == 0) - return pci_scan_bus(0, &via82c505_ops, sysdata); + return pci_scan_root_bus(NULL, 0, &via82c505_ops, sysdata, + &sysdata->resources); return NULL; } diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index 01f18a421b1..dcb004a804c 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c @@ -19,17 +19,22 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/export.h> #include <linux/init.h> #include <linux/list.h> #include <linux/io.h> +#include <linux/irqdomain.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/syscore_ops.h> #include <linux/device.h> #include <linux/amba/bus.h> +#include <asm/exception.h> #include <asm/mach/irq.h> #include <asm/hardware/vic.h> -#ifdef CONFIG_PM /** * struct vic_device - VIC PM device * @irq: The IRQ number for the base of the VIC. @@ -40,6 +45,7 @@ * @int_enable: Save for VIC_INT_ENABLE. * @soft_int: Save for VIC_INT_SOFT. * @protect: Save for VIC_PROTECT. + * @domain: The IRQ domain for the VIC. */ struct vic_device { void __iomem *base; @@ -50,13 +56,13 @@ struct vic_device { u32 int_enable; u32 soft_int; u32 protect; + struct irq_domain domain; }; /* we cannot allocate memory when VICs are initially registered */ static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; static int vic_id; -#endif /* CONFIG_PM */ /** * vic_init2 - common initialisation code @@ -156,39 +162,50 @@ static int __init vic_pm_init(void) return 0; } late_initcall(vic_pm_init); +#endif /* CONFIG_PM */ /** - * vic_pm_register - Register a VIC for later power management control + * vic_register() - Register a VIC. * @base: The base address of the VIC. * @irq: The base IRQ for the VIC. * @resume_sources: bitmask of interrupts allowed for resume sources. + * @node: The device tree node associated with the VIC. * * Register the VIC with the system device tree so that it can be notified * of suspend and resume requests and ensure that the correct actions are * taken to re-instate the settings on resume. + * + * This also configures the IRQ domain for the VIC. */ -static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) +static void __init vic_register(void __iomem *base, unsigned int irq, + u32 resume_sources, struct device_node *node) { struct vic_device *v; - if (vic_id >= ARRAY_SIZE(vic_devices)) + if (vic_id >= ARRAY_SIZE(vic_devices)) { printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); - else { - v = &vic_devices[vic_id]; - v->base = base; - v->resume_sources = resume_sources; - v->irq = irq; - vic_id++; + return; } + + v = &vic_devices[vic_id]; + v->base = base; + v->resume_sources = resume_sources; + v->irq = irq; + vic_id++; + + v->domain.irq_base = irq; + v->domain.nr_irq = 32; +#ifdef CONFIG_OF_IRQ + v->domain.of_node = of_node_get(node); +#endif /* CONFIG_OF */ + v->domain.ops = &irq_domain_simple_ops; + irq_domain_add(&v->domain); } -#else -static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } -#endif /* CONFIG_PM */ static void vic_ack_irq(struct irq_data *d) { void __iomem *base = irq_data_get_irq_chip_data(d); - unsigned int irq = d->irq & 31; + unsigned int irq = d->hwirq; writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); /* moreover, clear the soft-triggered, in case it was the reason */ writel(1 << irq, base + VIC_INT_SOFT_CLEAR); @@ -197,14 +214,14 @@ static void vic_ack_irq(struct irq_data *d) static void vic_mask_irq(struct irq_data *d) { void __iomem *base = irq_data_get_irq_chip_data(d); - unsigned int irq = d->irq & 31; + unsigned int irq = d->hwirq; writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); } static void vic_unmask_irq(struct irq_data *d) { void __iomem *base = irq_data_get_irq_chip_data(d); - unsigned int irq = d->irq & 31; + unsigned int irq = d->hwirq; writel(1 << irq, base + VIC_INT_ENABLE); } @@ -226,7 +243,7 @@ static struct vic_device *vic_from_irq(unsigned int irq) static int vic_set_wake(struct irq_data *d, unsigned int on) { struct vic_device *v = vic_from_irq(d->irq); - unsigned int off = d->irq & 31; + unsigned int off = d->hwirq; u32 bit = 1 << off; if (!v) @@ -301,7 +318,7 @@ static void __init vic_set_irq_sources(void __iomem *base, * and 020 within the page. We call this "second block". */ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, - u32 vic_sources) + u32 vic_sources, struct device_node *node) { unsigned int i; int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; @@ -328,17 +345,12 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start, } vic_set_irq_sources(base, irq_start, vic_sources); + vic_register(base, irq_start, 0, node); } -/** - * vic_init - initialise a vectored interrupt controller - * @base: iomem base address - * @irq_start: starting interrupt number, must be muliple of 32 - * @vic_sources: bitmask of interrupt sources to allow - * @resume_sources: bitmask of interrupt sources to allow for resume - */ -void __init vic_init(void __iomem *base, unsigned int irq_start, - u32 vic_sources, u32 resume_sources) +static void __init __vic_init(void __iomem *base, unsigned int irq_start, + u32 vic_sources, u32 resume_sources, + struct device_node *node) { unsigned int i; u32 cellid = 0; @@ -356,7 +368,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, switch(vendor) { case AMBA_VENDOR_ST: - vic_init_st(base, irq_start, vic_sources); + vic_init_st(base, irq_start, vic_sources, node); return; default: printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); @@ -375,5 +387,81 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, vic_set_irq_sources(base, irq_start, vic_sources); - vic_pm_register(base, irq_start, resume_sources); + vic_register(base, irq_start, resume_sources, node); +} + +/** + * vic_init() - initialise a vectored interrupt controller + * @base: iomem base address + * @irq_start: starting interrupt number, must be muliple of 32 + * @vic_sources: bitmask of interrupt sources to allow + * @resume_sources: bitmask of interrupt sources to allow for resume + */ +void __init vic_init(void __iomem *base, unsigned int irq_start, + u32 vic_sources, u32 resume_sources) +{ + __vic_init(base, irq_start, vic_sources, resume_sources, NULL); +} + +#ifdef CONFIG_OF +int __init vic_of_init(struct device_node *node, struct device_node *parent) +{ + void __iomem *regs; + int irq_base; + + if (WARN(parent, "non-root VICs are not supported")) + return -EINVAL; + + regs = of_iomap(node, 0); + if (WARN_ON(!regs)) + return -EIO; + + irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); + if (WARN_ON(irq_base < 0)) + goto out_unmap; + + __vic_init(regs, irq_base, ~0, ~0, node); + + return 0; + + out_unmap: + iounmap(regs); + + return -EIO; +} +#endif /* CONFIG OF */ + +/* + * Handle each interrupt in a single VIC. Returns non-zero if we've + * handled at least one interrupt. This does a single read of the + * status register and handles all interrupts in order from LSB first. + */ +static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) +{ + u32 stat, irq; + int handled = 0; + + stat = readl_relaxed(vic->base + VIC_IRQ_STATUS); + while (stat) { + irq = ffs(stat) - 1; + handle_IRQ(irq_domain_to_irq(&vic->domain, irq), regs); + stat &= ~(1 << irq); + handled = 1; + } + + return handled; +} + +/* + * Keep iterating over all registered VIC's until there are no pending + * interrupts. + */ +asmlinkage void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) +{ + int i, handled; + + do { + for (i = 0, handled = 0; i < vic_id; ++i) + handled |= handle_one_vic(&vic_devices[i], regs); + } while (handled); } |