| /* |
| * Copyright 2008-2011 IBM Corporation. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version |
| * 2 of the License, or (at your option) any later version. |
| */ |
| |
| #include <linux/cpu.h> |
| #include <linux/init.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/kernel.h> |
| #include <linux/msi.h> |
| #include <linux/of.h> |
| #include <linux/slab.h> |
| #include <linux/smp.h> |
| #include <linux/spinlock.h> |
| #include <linux/types.h> |
| |
| #include <asm/io.h> |
| #include <asm/irq.h> |
| #include <asm/xics.h> |
| |
| #include "wsp.h" |
| #include "ics.h" |
| |
| |
| /* WSP ICS */ |
| |
| struct wsp_ics { |
| struct ics ics; |
| struct device_node *dn; |
| void __iomem *regs; |
| spinlock_t lock; |
| unsigned long *bitmap; |
| u32 chip_id; |
| u32 lsi_base; |
| u32 lsi_count; |
| u64 hwirq_start; |
| u64 count; |
| #ifdef CONFIG_SMP |
| int *hwirq_cpu_map; |
| #endif |
| }; |
| |
| #define to_wsp_ics(ics) container_of(ics, struct wsp_ics, ics) |
| |
| #define INT_SRC_LAYER_BUID_REG(base) ((base) + 0x00) |
| #define IODA_TBL_ADDR_REG(base) ((base) + 0x18) |
| #define IODA_TBL_DATA_REG(base) ((base) + 0x20) |
| #define XIVE_UPDATE_REG(base) ((base) + 0x28) |
| #define ICS_INT_CAPS_REG(base) ((base) + 0x30) |
| |
| #define TBL_AUTO_INCREMENT ((1UL << 63) | (1UL << 15)) |
| #define TBL_SELECT_XIST (1UL << 48) |
| #define TBL_SELECT_XIVT (1UL << 49) |
| |
| #define IODA_IRQ(irq) ((irq) & (0x7FFULL)) /* HRM 5.1.3.4 */ |
| |
| #define XIST_REQUIRED 0x8 |
| #define XIST_REJECTED 0x4 |
| #define XIST_PRESENTED 0x2 |
| #define XIST_PENDING 0x1 |
| |
| #define XIVE_SERVER_SHIFT 42 |
| #define XIVE_SERVER_MASK 0xFFFFULL |
| #define XIVE_PRIORITY_MASK 0xFFULL |
| #define XIVE_PRIORITY_SHIFT 32 |
| #define XIVE_WRITE_ENABLE (1ULL << 63) |
| |
| /* |
| * The docs refer to a 6 bit field called ChipID, which consists of a |
| * 3 bit NodeID and a 3 bit ChipID. On WSP the ChipID is always zero |
| * so we ignore it, and every where we use "chip id" in this code we |
| * mean the NodeID. |
| */ |
| #define WSP_ICS_CHIP_SHIFT 17 |
| |
| |
| static struct wsp_ics *ics_list; |
| static int num_ics; |
| |
| /* ICS Source controller accessors */ |
| |
| static u64 wsp_ics_get_xive(struct wsp_ics *ics, unsigned int irq) |
| { |
| unsigned long flags; |
| u64 xive; |
| |
| spin_lock_irqsave(&ics->lock, flags); |
| out_be64(IODA_TBL_ADDR_REG(ics->regs), TBL_SELECT_XIVT | IODA_IRQ(irq)); |
| xive = in_be64(IODA_TBL_DATA_REG(ics->regs)); |
| spin_unlock_irqrestore(&ics->lock, flags); |
| |
| return xive; |
| } |
| |
| static void wsp_ics_set_xive(struct wsp_ics *ics, unsigned int irq, u64 xive) |
| { |
| xive &= ~XIVE_ADDR_MASK; |
| xive |= (irq & XIVE_ADDR_MASK); |
| xive |= XIVE_WRITE_ENABLE; |
| |
| out_be64(XIVE_UPDATE_REG(ics->regs), xive); |
| } |
| |
| static u64 xive_set_server(u64 xive, unsigned int server) |
| { |
| u64 mask = ~(XIVE_SERVER_MASK << XIVE_SERVER_SHIFT); |
| |
| xive &= mask; |
| xive |= (server & XIVE_SERVER_MASK) << XIVE_SERVER_SHIFT; |
| |
| return xive; |
| } |
| |
| static u64 xive_set_priority(u64 xive, unsigned int priority) |
| { |
| u64 mask = ~(XIVE_PRIORITY_MASK << XIVE_PRIORITY_SHIFT); |
| |
| xive &= mask; |
| xive |= (priority & XIVE_PRIORITY_MASK) << XIVE_PRIORITY_SHIFT; |
| |
| return xive; |
| } |
| |
| |
| #ifdef CONFIG_SMP |
| /* Find logical CPUs within mask on a given chip and store result in ret */ |
| void cpus_on_chip(int chip_id, cpumask_t *mask, cpumask_t *ret) |
| { |
| int cpu, chip; |
| struct device_node *cpu_dn, *dn; |
| const u32 *prop; |
| |
| cpumask_clear(ret); |
| for_each_cpu(cpu, mask) { |
| cpu_dn = of_get_cpu_node(cpu, NULL); |
| if (!cpu_dn) |
| continue; |
| |
| prop = of_get_property(cpu_dn, "at-node", NULL); |
| if (!prop) { |
| of_node_put(cpu_dn); |
| continue; |
| } |
| |
| dn = of_find_node_by_phandle(*prop); |
| of_node_put(cpu_dn); |
| |
| chip = wsp_get_chip_id(dn); |
| if (chip == chip_id) |
| cpumask_set_cpu(cpu, ret); |
| |
| of_node_put(dn); |
| } |
| } |
| |
| /* Store a suitable CPU to handle a hwirq in the ics->hwirq_cpu_map cache */ |
| static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq, |
| const cpumask_t *affinity) |
| { |
| cpumask_var_t avail, newmask; |
| int ret = -ENOMEM, cpu, cpu_rover = 0, target; |
| int index = hwirq - ics->hwirq_start; |
| unsigned int nodeid; |
| |
| BUG_ON(index < 0 || index >= ics->count); |
| |
| if (!ics->hwirq_cpu_map) |
| return -ENOMEM; |
| |
| if (!distribute_irqs) { |
| ics->hwirq_cpu_map[hwirq - ics->hwirq_start] = xics_default_server; |
| return 0; |
| } |
| |
| /* Allocate needed CPU masks */ |
| if (!alloc_cpumask_var(&avail, GFP_KERNEL)) |
| goto ret; |
| if (!alloc_cpumask_var(&newmask, GFP_KERNEL)) |
| goto freeavail; |
| |
| /* Find PBus attached to the source of this IRQ */ |
| nodeid = (hwirq >> WSP_ICS_CHIP_SHIFT) & 0x3; /* 12:14 */ |
| |
| /* Find CPUs that could handle this IRQ */ |
| if (affinity) |
| cpumask_and(avail, cpu_online_mask, affinity); |
| else |
| cpumask_copy(avail, cpu_online_mask); |
| |
| /* Narrow selection down to logical CPUs on the same chip */ |
| cpus_on_chip(nodeid, avail, newmask); |
| |
| /* Ensure we haven't narrowed it down to 0 */ |
| if (unlikely(cpumask_empty(newmask))) { |
| if (unlikely(cpumask_empty(avail))) { |
| ret = -1; |
| goto out; |
| } |
| cpumask_copy(newmask, avail); |
| } |
| |
| /* Choose a CPU out of those we narrowed it down to in round robin */ |
| target = hwirq % cpumask_weight(newmask); |
| for_each_cpu(cpu, newmask) { |
| if (cpu_rover++ >= target) { |
| ics->hwirq_cpu_map[index] = get_hard_smp_processor_id(cpu); |
| ret = 0; |
| goto out; |
| } |
| } |
| |
| /* Shouldn't happen */ |
| WARN_ON(1); |
| |
| out: |
| free_cpumask_var(newmask); |
| freeavail: |
| free_cpumask_var(avail); |
| ret: |
| if (ret < 0) { |
| ics->hwirq_cpu_map[index] = cpumask_first(cpu_online_mask); |
| pr_warning("Error, falling hwirq 0x%x routing back to CPU %i\n", |
| hwirq, ics->hwirq_cpu_map[index]); |
| } |
| return ret; |
| } |
| |
| static void alloc_irq_map(struct wsp_ics *ics) |
| { |
| int i; |
| |
| ics->hwirq_cpu_map = kmalloc(sizeof(int) * ics->count, GFP_KERNEL); |
| if (!ics->hwirq_cpu_map) { |
| pr_warning("Allocate hwirq_cpu_map failed, " |
| "IRQ balancing disabled\n"); |
| return; |
| } |
| |
| for (i=0; i < ics->count; i++) |
| ics->hwirq_cpu_map[i] = xics_default_server; |
| } |
| |
| static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq) |
| { |
| int index = hwirq - ics->hwirq_start; |
| |
| BUG_ON(index < 0 || index >= ics->count); |
| |
| if (!ics->hwirq_cpu_map) |
| return xics_default_server; |
| |
| return ics->hwirq_cpu_map[index]; |
| } |
| #else /* !CONFIG_SMP */ |
| static int cache_hwirq_map(struct wsp_ics *ics, unsigned int hwirq, |
| const cpumask_t *affinity) |
| { |
| return 0; |
| } |
| |
| static int get_irq_server(struct wsp_ics *ics, unsigned int hwirq) |
| { |
| return xics_default_server; |
| } |
| |
| static void alloc_irq_map(struct wsp_ics *ics) { } |
| #endif |
| |
| static void wsp_chip_unmask_irq(struct irq_data *d) |
| { |
| unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); |
| struct wsp_ics *ics; |
| int server; |
| u64 xive; |
| |
| if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) |
| return; |
| |
| ics = d->chip_data; |
| if (WARN_ON(!ics)) |
| return; |
| |
| server = get_irq_server(ics, hw_irq); |
| |
| xive = wsp_ics_get_xive(ics, hw_irq); |
| xive = xive_set_server(xive, server); |
| xive = xive_set_priority(xive, DEFAULT_PRIORITY); |
| wsp_ics_set_xive(ics, hw_irq, xive); |
| } |
| |
| static unsigned int wsp_chip_startup(struct irq_data *d) |
| { |
| /* unmask it */ |
| wsp_chip_unmask_irq(d); |
| return 0; |
| } |
| |
| static void wsp_mask_real_irq(unsigned int hw_irq, struct wsp_ics *ics) |
| { |
| u64 xive; |
| |
| if (hw_irq == XICS_IPI) |
| return; |
| |
| if (WARN_ON(!ics)) |
| return; |
| xive = wsp_ics_get_xive(ics, hw_irq); |
| xive = xive_set_server(xive, xics_default_server); |
| xive = xive_set_priority(xive, LOWEST_PRIORITY); |
| wsp_ics_set_xive(ics, hw_irq, xive); |
| } |
| |
| static void wsp_chip_mask_irq(struct irq_data *d) |
| { |
| unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); |
| struct wsp_ics *ics = d->chip_data; |
| |
| if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) |
| return; |
| |
| wsp_mask_real_irq(hw_irq, ics); |
| } |
| |
| static int wsp_chip_set_affinity(struct irq_data *d, |
| const struct cpumask *cpumask, bool force) |
| { |
| unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); |
| struct wsp_ics *ics; |
| int ret; |
| u64 xive; |
| |
| if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) |
| return -1; |
| |
| ics = d->chip_data; |
| if (WARN_ON(!ics)) |
| return -1; |
| xive = wsp_ics_get_xive(ics, hw_irq); |
| |
| /* |
| * For the moment only implement delivery to all cpus or one cpu. |
| * Get current irq_server for the given irq |
| */ |
| ret = cache_hwirq_map(ics, d->irq, cpumask); |
| if (ret == -1) { |
| char cpulist[128]; |
| cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); |
| pr_warning("%s: No online cpus in the mask %s for irq %d\n", |
| __func__, cpulist, d->irq); |
| return -1; |
| } else if (ret == -ENOMEM) { |
| pr_warning("%s: Out of memory\n", __func__); |
| return -1; |
| } |
| |
| xive = xive_set_server(xive, get_irq_server(ics, hw_irq)); |
| wsp_ics_set_xive(ics, hw_irq, xive); |
| |
| return 0; |
| } |
| |
| static struct irq_chip wsp_irq_chip = { |
| .name = "WSP ICS", |
| .irq_startup = wsp_chip_startup, |
| .irq_mask = wsp_chip_mask_irq, |
| .irq_unmask = wsp_chip_unmask_irq, |
| .irq_set_affinity = wsp_chip_set_affinity |
| }; |
| |
| static int wsp_ics_host_match(struct ics *ics, struct device_node *dn) |
| { |
| /* All ICSs in the system implement a global irq number space, |
| * so match against them all. */ |
| return of_device_is_compatible(dn, "ibm,ppc-xics"); |
| } |
| |
| static int wsp_ics_match_hwirq(struct wsp_ics *wsp_ics, unsigned int hwirq) |
| { |
| if (hwirq >= wsp_ics->hwirq_start && |
| hwirq < wsp_ics->hwirq_start + wsp_ics->count) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int wsp_ics_map(struct ics *ics, unsigned int virq) |
| { |
| struct wsp_ics *wsp_ics = to_wsp_ics(ics); |
| unsigned int hw_irq = virq_to_hw(virq); |
| unsigned long flags; |
| |
| if (!wsp_ics_match_hwirq(wsp_ics, hw_irq)) |
| return -ENOENT; |
| |
| irq_set_chip_and_handler(virq, &wsp_irq_chip, handle_fasteoi_irq); |
| |
| irq_set_chip_data(virq, wsp_ics); |
| |
| spin_lock_irqsave(&wsp_ics->lock, flags); |
| bitmap_allocate_region(wsp_ics->bitmap, hw_irq - wsp_ics->hwirq_start, 0); |
| spin_unlock_irqrestore(&wsp_ics->lock, flags); |
| |
| return 0; |
| } |
| |
| static void wsp_ics_mask_unknown(struct ics *ics, unsigned long hw_irq) |
| { |
| struct wsp_ics *wsp_ics = to_wsp_ics(ics); |
| |
| if (!wsp_ics_match_hwirq(wsp_ics, hw_irq)) |
| return; |
| |
| pr_err("%s: IRQ %lu (real) is invalid, disabling it.\n", __func__, hw_irq); |
| wsp_mask_real_irq(hw_irq, wsp_ics); |
| } |
| |
| static long wsp_ics_get_server(struct ics *ics, unsigned long hw_irq) |
| { |
| struct wsp_ics *wsp_ics = to_wsp_ics(ics); |
| |
| if (!wsp_ics_match_hwirq(wsp_ics, hw_irq)) |
| return -ENOENT; |
| |
| return get_irq_server(wsp_ics, hw_irq); |
| } |
| |
| /* HW Number allocation API */ |
| |
| static struct wsp_ics *wsp_ics_find_dn_ics(struct device_node *dn) |
| { |
| struct device_node *iparent; |
| int i; |
| |
| iparent = of_irq_find_parent(dn); |
| if (!iparent) { |
| pr_err("wsp_ics: Failed to find interrupt parent!\n"); |
| return NULL; |
| } |
| |
| for(i = 0; i < num_ics; i++) { |
| if(ics_list[i].dn == iparent) |
| break; |
| } |
| |
| if (i >= num_ics) { |
| pr_err("wsp_ics: Unable to find parent bitmap!\n"); |
| return NULL; |
| } |
| |
| return &ics_list[i]; |
| } |
| |
| int wsp_ics_alloc_irq(struct device_node *dn, int num) |
| { |
| struct wsp_ics *ics; |
| int order, offset; |
| |
| ics = wsp_ics_find_dn_ics(dn); |
| if (!ics) |
| return -ENODEV; |
| |
| /* Fast, but overly strict if num isn't a power of two */ |
| order = get_count_order(num); |
| |
| spin_lock_irq(&ics->lock); |
| offset = bitmap_find_free_region(ics->bitmap, ics->count, order); |
| spin_unlock_irq(&ics->lock); |
| |
| if (offset < 0) |
| return offset; |
| |
| return offset + ics->hwirq_start; |
| } |
| |
| void wsp_ics_free_irq(struct device_node *dn, unsigned int irq) |
| { |
| struct wsp_ics *ics; |
| |
| ics = wsp_ics_find_dn_ics(dn); |
| if (WARN_ON(!ics)) |
| return; |
| |
| spin_lock_irq(&ics->lock); |
| bitmap_release_region(ics->bitmap, irq, 0); |
| spin_unlock_irq(&ics->lock); |
| } |
| |
| /* Initialisation */ |
| |
| static int __init wsp_ics_bitmap_setup(struct wsp_ics *ics, |
| struct device_node *dn) |
| { |
| int len, i, j, size; |
| u32 start, count; |
| const u32 *p; |
| |
| size = BITS_TO_LONGS(ics->count) * sizeof(long); |
| ics->bitmap = kzalloc(size, GFP_KERNEL); |
| if (!ics->bitmap) { |
| pr_err("wsp_ics: ENOMEM allocating IRQ bitmap!\n"); |
| return -ENOMEM; |
| } |
| |
| spin_lock_init(&ics->lock); |
| |
| p = of_get_property(dn, "available-ranges", &len); |
| if (!p || !len) { |
| /* FIXME this should be a WARN() once mambo is updated */ |
| pr_err("wsp_ics: No available-ranges defined for %s\n", |
| dn->full_name); |
| return 0; |
| } |
| |
| if (len % (2 * sizeof(u32)) != 0) { |
| /* FIXME this should be a WARN() once mambo is updated */ |
| pr_err("wsp_ics: Invalid available-ranges for %s\n", |
| dn->full_name); |
| return 0; |
| } |
| |
| bitmap_fill(ics->bitmap, ics->count); |
| |
| for (i = 0; i < len / sizeof(u32); i += 2) { |
| start = of_read_number(p + i, 1); |
| count = of_read_number(p + i + 1, 1); |
| |
| pr_devel("%s: start: %d count: %d\n", __func__, start, count); |
| |
| if ((start + count) > (ics->hwirq_start + ics->count) || |
| start < ics->hwirq_start) { |
| pr_err("wsp_ics: Invalid range! -> %d to %d\n", |
| start, start + count); |
| break; |
| } |
| |
| for (j = 0; j < count; j++) |
| bitmap_release_region(ics->bitmap, |
| (start + j) - ics->hwirq_start, 0); |
| } |
| |
| /* Ensure LSIs are not available for allocation */ |
| bitmap_allocate_region(ics->bitmap, ics->lsi_base, |
| get_count_order(ics->lsi_count)); |
| |
| return 0; |
| } |
| |
| static int __init wsp_ics_setup(struct wsp_ics *ics, struct device_node *dn) |
| { |
| u32 lsi_buid, msi_buid, msi_base, msi_count; |
| void __iomem *regs; |
| const u32 *p; |
| int rc, len, i; |
| u64 caps, buid; |
| |
| p = of_get_property(dn, "interrupt-ranges", &len); |
| if (!p || len < (2 * sizeof(u32))) { |
| pr_err("wsp_ics: No/bad interrupt-ranges found on %s\n", |
| dn->full_name); |
| return -ENOENT; |
| } |
| |
| if (len > (2 * sizeof(u32))) { |
| pr_err("wsp_ics: Multiple ics ranges not supported.\n"); |
| return -EINVAL; |
| } |
| |
| regs = of_iomap(dn, 0); |
| if (!regs) { |
| pr_err("wsp_ics: of_iomap(%s) failed\n", dn->full_name); |
| return -ENXIO; |
| } |
| |
| ics->hwirq_start = of_read_number(p, 1); |
| ics->count = of_read_number(p + 1, 1); |
| ics->regs = regs; |
| |
| ics->chip_id = wsp_get_chip_id(dn); |
| if (WARN_ON(ics->chip_id < 0)) |
| ics->chip_id = 0; |
| |
| /* Get some informations about the critter */ |
| caps = in_be64(ICS_INT_CAPS_REG(ics->regs)); |
| buid = in_be64(INT_SRC_LAYER_BUID_REG(ics->regs)); |
| ics->lsi_count = caps >> 56; |
| msi_count = (caps >> 44) & 0x7ff; |
| |
| /* Note: LSI BUID is 9 bits, but really only 3 are BUID and the |
| * rest is mixed in the interrupt number. We store the whole |
| * thing though |
| */ |
| lsi_buid = (buid >> 48) & 0x1ff; |
| ics->lsi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | lsi_buid << 5; |
| msi_buid = (buid >> 37) & 0x7; |
| msi_base = (ics->chip_id << WSP_ICS_CHIP_SHIFT) | msi_buid << 11; |
| |
| pr_info("wsp_ics: Found %s\n", dn->full_name); |
| pr_info("wsp_ics: irq range : 0x%06llx..0x%06llx\n", |
| ics->hwirq_start, ics->hwirq_start + ics->count - 1); |
| pr_info("wsp_ics: %4d LSIs : 0x%06x..0x%06x\n", |
| ics->lsi_count, ics->lsi_base, |
| ics->lsi_base + ics->lsi_count - 1); |
| pr_info("wsp_ics: %4d MSIs : 0x%06x..0x%06x\n", |
| msi_count, msi_base, |
| msi_base + msi_count - 1); |
| |
| /* Let's check the HW config is sane */ |
| if (ics->lsi_base < ics->hwirq_start || |
| (ics->lsi_base + ics->lsi_count) > (ics->hwirq_start + ics->count)) |
| pr_warning("wsp_ics: WARNING ! LSIs out of interrupt-ranges !\n"); |
| if (msi_base < ics->hwirq_start || |
| (msi_base + msi_count) > (ics->hwirq_start + ics->count)) |
| pr_warning("wsp_ics: WARNING ! MSIs out of interrupt-ranges !\n"); |
| |
| /* We don't check for overlap between LSI and MSI, which will happen |
| * if we use the same BUID, I'm not sure yet how legit that is. |
| */ |
| |
| rc = wsp_ics_bitmap_setup(ics, dn); |
| if (rc) { |
| iounmap(regs); |
| return rc; |
| } |
| |
| ics->dn = of_node_get(dn); |
| alloc_irq_map(ics); |
| |
| for(i = 0; i < ics->count; i++) |
| wsp_mask_real_irq(ics->hwirq_start + i, ics); |
| |
| ics->ics.map = wsp_ics_map; |
| ics->ics.mask_unknown = wsp_ics_mask_unknown; |
| ics->ics.get_server = wsp_ics_get_server; |
| ics->ics.host_match = wsp_ics_host_match; |
| |
| xics_register_ics(&ics->ics); |
| |
| return 0; |
| } |
| |
| static void __init wsp_ics_set_default_server(void) |
| { |
| struct device_node *np; |
| u32 hwid; |
| |
| /* Find the server number for the boot cpu. */ |
| np = of_get_cpu_node(boot_cpuid, NULL); |
| BUG_ON(!np); |
| |
| hwid = get_hard_smp_processor_id(boot_cpuid); |
| |
| pr_info("wsp_ics: default server is %#x, CPU %s\n", hwid, np->full_name); |
| xics_default_server = hwid; |
| |
| of_node_put(np); |
| } |
| |
| static int __init wsp_ics_init(void) |
| { |
| struct device_node *dn; |
| struct wsp_ics *ics; |
| int rc, found; |
| |
| wsp_ics_set_default_server(); |
| |
| found = 0; |
| for_each_compatible_node(dn, NULL, "ibm,ppc-xics") |
| found++; |
| |
| if (found == 0) { |
| pr_err("wsp_ics: No ICS's found!\n"); |
| return -ENODEV; |
| } |
| |
| ics_list = kmalloc(sizeof(*ics) * found, GFP_KERNEL); |
| if (!ics_list) { |
| pr_err("wsp_ics: No memory for structs.\n"); |
| return -ENOMEM; |
| } |
| |
| num_ics = 0; |
| ics = ics_list; |
| for_each_compatible_node(dn, NULL, "ibm,wsp-xics") { |
| rc = wsp_ics_setup(ics, dn); |
| if (rc == 0) { |
| ics++; |
| num_ics++; |
| } |
| } |
| |
| if (found != num_ics) { |
| pr_err("wsp_ics: Failed setting up %d ICS's\n", |
| found - num_ics); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| void __init wsp_init_irq(void) |
| { |
| wsp_ics_init(); |
| xics_init(); |
| |
| /* We need to patch our irq chip's EOI to point to the right ICP */ |
| wsp_irq_chip.irq_eoi = icp_ops->eoi; |
| } |