1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 28e4bebe0SHaojian Zhuang /* 38e4bebe0SHaojian Zhuang * Hisilicon HiP04 INTC 48e4bebe0SHaojian Zhuang * 58e4bebe0SHaojian Zhuang * Copyright (C) 2002-2014 ARM Limited. 68e4bebe0SHaojian Zhuang * Copyright (c) 2013-2014 Hisilicon Ltd. 78e4bebe0SHaojian Zhuang * Copyright (c) 2013-2014 Linaro Ltd. 88e4bebe0SHaojian Zhuang * 98e4bebe0SHaojian Zhuang * Interrupt architecture for the HIP04 INTC: 108e4bebe0SHaojian Zhuang * 118e4bebe0SHaojian Zhuang * o There is one Interrupt Distributor, which receives interrupts 128e4bebe0SHaojian Zhuang * from system devices and sends them to the Interrupt Controllers. 138e4bebe0SHaojian Zhuang * 148e4bebe0SHaojian Zhuang * o There is one CPU Interface per CPU, which sends interrupts sent 158e4bebe0SHaojian Zhuang * by the Distributor, and interrupts generated locally, to the 168e4bebe0SHaojian Zhuang * associated CPU. The base address of the CPU interface is usually 178e4bebe0SHaojian Zhuang * aliased so that the same address points to different chips depending 188e4bebe0SHaojian Zhuang * on the CPU it is accessed from. 198e4bebe0SHaojian Zhuang * 208e4bebe0SHaojian Zhuang * Note that IRQs 0-31 are special - they are local to each CPU. 218e4bebe0SHaojian Zhuang * As such, the enable set/clear, pending set/clear and active bit 228e4bebe0SHaojian Zhuang * registers are banked per-cpu for these sources. 238e4bebe0SHaojian Zhuang */ 248e4bebe0SHaojian Zhuang 258e4bebe0SHaojian Zhuang #include <linux/init.h> 268e4bebe0SHaojian Zhuang #include <linux/kernel.h> 278e4bebe0SHaojian Zhuang #include <linux/err.h> 288e4bebe0SHaojian Zhuang #include <linux/module.h> 298e4bebe0SHaojian Zhuang #include <linux/list.h> 308e4bebe0SHaojian Zhuang #include <linux/smp.h> 318e4bebe0SHaojian Zhuang #include <linux/cpu.h> 328e4bebe0SHaojian Zhuang #include <linux/cpu_pm.h> 338e4bebe0SHaojian Zhuang #include <linux/cpumask.h> 348e4bebe0SHaojian Zhuang #include <linux/io.h> 358e4bebe0SHaojian Zhuang #include <linux/of.h> 368e4bebe0SHaojian Zhuang #include <linux/of_address.h> 378e4bebe0SHaojian Zhuang #include <linux/of_irq.h> 388e4bebe0SHaojian Zhuang #include <linux/irqdomain.h> 398e4bebe0SHaojian Zhuang #include <linux/interrupt.h> 408e4bebe0SHaojian Zhuang #include <linux/slab.h> 4141a83e06SJoel Porquet #include <linux/irqchip.h> 428e4bebe0SHaojian Zhuang #include <linux/irqchip/arm-gic.h> 438e4bebe0SHaojian Zhuang 448e4bebe0SHaojian Zhuang #include <asm/irq.h> 458e4bebe0SHaojian Zhuang #include <asm/exception.h> 468e4bebe0SHaojian Zhuang #include <asm/smp_plat.h> 478e4bebe0SHaojian Zhuang 488e4bebe0SHaojian Zhuang #include "irq-gic-common.h" 498e4bebe0SHaojian Zhuang 508e4bebe0SHaojian Zhuang #define HIP04_MAX_IRQS 510 518e4bebe0SHaojian Zhuang 528e4bebe0SHaojian Zhuang struct hip04_irq_data { 538e4bebe0SHaojian Zhuang void __iomem *dist_base; 548e4bebe0SHaojian Zhuang void __iomem *cpu_base; 558e4bebe0SHaojian Zhuang struct irq_domain *domain; 568e4bebe0SHaojian Zhuang unsigned int nr_irqs; 578e4bebe0SHaojian Zhuang }; 588e4bebe0SHaojian Zhuang 598e4bebe0SHaojian Zhuang static DEFINE_RAW_SPINLOCK(irq_controller_lock); 608e4bebe0SHaojian Zhuang 618e4bebe0SHaojian Zhuang /* 628e4bebe0SHaojian Zhuang * The GIC mapping of CPU interfaces does not necessarily match 638e4bebe0SHaojian Zhuang * the logical CPU numbering. Let's use a mapping as returned 648e4bebe0SHaojian Zhuang * by the GIC itself. 658e4bebe0SHaojian Zhuang */ 668e4bebe0SHaojian Zhuang #define NR_HIP04_CPU_IF 16 678e4bebe0SHaojian Zhuang static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly; 688e4bebe0SHaojian Zhuang 698e4bebe0SHaojian Zhuang static struct hip04_irq_data hip04_data __read_mostly; 708e4bebe0SHaojian Zhuang 718e4bebe0SHaojian Zhuang static inline void __iomem *hip04_dist_base(struct irq_data *d) 728e4bebe0SHaojian Zhuang { 738e4bebe0SHaojian Zhuang struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); 748e4bebe0SHaojian Zhuang return hip04_data->dist_base; 758e4bebe0SHaojian Zhuang } 768e4bebe0SHaojian Zhuang 778e4bebe0SHaojian Zhuang static inline void __iomem *hip04_cpu_base(struct irq_data *d) 788e4bebe0SHaojian Zhuang { 798e4bebe0SHaojian Zhuang struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); 808e4bebe0SHaojian Zhuang return hip04_data->cpu_base; 818e4bebe0SHaojian Zhuang } 828e4bebe0SHaojian Zhuang 838e4bebe0SHaojian Zhuang static inline unsigned int hip04_irq(struct irq_data *d) 848e4bebe0SHaojian Zhuang { 858e4bebe0SHaojian Zhuang return d->hwirq; 868e4bebe0SHaojian Zhuang } 878e4bebe0SHaojian Zhuang 888e4bebe0SHaojian Zhuang /* 898e4bebe0SHaojian Zhuang * Routines to acknowledge, disable and enable interrupts 908e4bebe0SHaojian Zhuang */ 918e4bebe0SHaojian Zhuang static void hip04_mask_irq(struct irq_data *d) 928e4bebe0SHaojian Zhuang { 938e4bebe0SHaojian Zhuang u32 mask = 1 << (hip04_irq(d) % 32); 948e4bebe0SHaojian Zhuang 958e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 968e4bebe0SHaojian Zhuang writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR + 978e4bebe0SHaojian Zhuang (hip04_irq(d) / 32) * 4); 988e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 998e4bebe0SHaojian Zhuang } 1008e4bebe0SHaojian Zhuang 1018e4bebe0SHaojian Zhuang static void hip04_unmask_irq(struct irq_data *d) 1028e4bebe0SHaojian Zhuang { 1038e4bebe0SHaojian Zhuang u32 mask = 1 << (hip04_irq(d) % 32); 1048e4bebe0SHaojian Zhuang 1058e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1068e4bebe0SHaojian Zhuang writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET + 1078e4bebe0SHaojian Zhuang (hip04_irq(d) / 32) * 4); 1088e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1098e4bebe0SHaojian Zhuang } 1108e4bebe0SHaojian Zhuang 1118e4bebe0SHaojian Zhuang static void hip04_eoi_irq(struct irq_data *d) 1128e4bebe0SHaojian Zhuang { 1138e4bebe0SHaojian Zhuang writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI); 1148e4bebe0SHaojian Zhuang } 1158e4bebe0SHaojian Zhuang 1168e4bebe0SHaojian Zhuang static int hip04_irq_set_type(struct irq_data *d, unsigned int type) 1178e4bebe0SHaojian Zhuang { 1188e4bebe0SHaojian Zhuang void __iomem *base = hip04_dist_base(d); 1198e4bebe0SHaojian Zhuang unsigned int irq = hip04_irq(d); 120fb7e7debSLiviu Dudau int ret; 1218e4bebe0SHaojian Zhuang 1228e4bebe0SHaojian Zhuang /* Interrupt configuration for SGIs can't be changed */ 1238e4bebe0SHaojian Zhuang if (irq < 16) 1248e4bebe0SHaojian Zhuang return -EINVAL; 1258e4bebe0SHaojian Zhuang 126fb7e7debSLiviu Dudau /* SPIs have restrictions on the supported types */ 127fb7e7debSLiviu Dudau if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && 128fb7e7debSLiviu Dudau type != IRQ_TYPE_EDGE_RISING) 1298e4bebe0SHaojian Zhuang return -EINVAL; 1308e4bebe0SHaojian Zhuang 1318e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1328e4bebe0SHaojian Zhuang 13313d22e2eSMarc Zyngier ret = gic_configure_irq(irq, type, base + GIC_DIST_CONFIG, NULL); 13413d22e2eSMarc Zyngier if (ret && irq < 32) { 13513d22e2eSMarc Zyngier /* Misconfigured PPIs are usually not fatal */ 13613d22e2eSMarc Zyngier pr_warn("GIC: PPI%d is secure or misconfigured\n", irq - 16); 13713d22e2eSMarc Zyngier ret = 0; 13813d22e2eSMarc Zyngier } 1398e4bebe0SHaojian Zhuang 1408e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1418e4bebe0SHaojian Zhuang 142fb7e7debSLiviu Dudau return ret; 1438e4bebe0SHaojian Zhuang } 1448e4bebe0SHaojian Zhuang 1458e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 1468e4bebe0SHaojian Zhuang static int hip04_irq_set_affinity(struct irq_data *d, 1478e4bebe0SHaojian Zhuang const struct cpumask *mask_val, 1488e4bebe0SHaojian Zhuang bool force) 1498e4bebe0SHaojian Zhuang { 1508e4bebe0SHaojian Zhuang void __iomem *reg; 1518e4bebe0SHaojian Zhuang unsigned int cpu, shift = (hip04_irq(d) % 2) * 16; 1528e4bebe0SHaojian Zhuang u32 val, mask, bit; 1538e4bebe0SHaojian Zhuang 1548e4bebe0SHaojian Zhuang if (!force) 1558e4bebe0SHaojian Zhuang cpu = cpumask_any_and(mask_val, cpu_online_mask); 1568e4bebe0SHaojian Zhuang else 1578e4bebe0SHaojian Zhuang cpu = cpumask_first(mask_val); 1588e4bebe0SHaojian Zhuang 1598e4bebe0SHaojian Zhuang if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids) 1608e4bebe0SHaojian Zhuang return -EINVAL; 1618e4bebe0SHaojian Zhuang 1628e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1638e4bebe0SHaojian Zhuang reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3); 1648e4bebe0SHaojian Zhuang mask = 0xffff << shift; 1658e4bebe0SHaojian Zhuang bit = hip04_cpu_map[cpu] << shift; 1668e4bebe0SHaojian Zhuang val = readl_relaxed(reg) & ~mask; 1678e4bebe0SHaojian Zhuang writel_relaxed(val | bit, reg); 1688e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1698e4bebe0SHaojian Zhuang 17079a0d4d8SMarc Zyngier irq_data_update_effective_affinity(d, cpumask_of(cpu)); 17179a0d4d8SMarc Zyngier 1728e4bebe0SHaojian Zhuang return IRQ_SET_MASK_OK; 1738e4bebe0SHaojian Zhuang } 174*a2df12c5SMarc Zyngier 175*a2df12c5SMarc Zyngier static void hip04_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) 176*a2df12c5SMarc Zyngier { 177*a2df12c5SMarc Zyngier int cpu; 178*a2df12c5SMarc Zyngier unsigned long flags, map = 0; 179*a2df12c5SMarc Zyngier 180*a2df12c5SMarc Zyngier raw_spin_lock_irqsave(&irq_controller_lock, flags); 181*a2df12c5SMarc Zyngier 182*a2df12c5SMarc Zyngier /* Convert our logical CPU mask into a physical one. */ 183*a2df12c5SMarc Zyngier for_each_cpu(cpu, mask) 184*a2df12c5SMarc Zyngier map |= hip04_cpu_map[cpu]; 185*a2df12c5SMarc Zyngier 186*a2df12c5SMarc Zyngier /* 187*a2df12c5SMarc Zyngier * Ensure that stores to Normal memory are visible to the 188*a2df12c5SMarc Zyngier * other CPUs before they observe us issuing the IPI. 189*a2df12c5SMarc Zyngier */ 190*a2df12c5SMarc Zyngier dmb(ishst); 191*a2df12c5SMarc Zyngier 192*a2df12c5SMarc Zyngier /* this always happens on GIC0 */ 193*a2df12c5SMarc Zyngier writel_relaxed(map << 8 | d->hwirq, hip04_data.dist_base + GIC_DIST_SOFTINT); 194*a2df12c5SMarc Zyngier 195*a2df12c5SMarc Zyngier raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 196*a2df12c5SMarc Zyngier } 1978e4bebe0SHaojian Zhuang #endif 1988e4bebe0SHaojian Zhuang 1998e4bebe0SHaojian Zhuang static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs) 2008e4bebe0SHaojian Zhuang { 2018e4bebe0SHaojian Zhuang u32 irqstat, irqnr; 2028e4bebe0SHaojian Zhuang void __iomem *cpu_base = hip04_data.cpu_base; 2038e4bebe0SHaojian Zhuang 2048e4bebe0SHaojian Zhuang do { 2058e4bebe0SHaojian Zhuang irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); 2068e4bebe0SHaojian Zhuang irqnr = irqstat & GICC_IAR_INT_ID_MASK; 2078e4bebe0SHaojian Zhuang 208*a2df12c5SMarc Zyngier if (irqnr <= HIP04_MAX_IRQS) 2093fe14927SMarc Zyngier handle_domain_irq(hip04_data.domain, irqnr, regs); 210*a2df12c5SMarc Zyngier } while (irqnr > HIP04_MAX_IRQS); 2118e4bebe0SHaojian Zhuang } 2128e4bebe0SHaojian Zhuang 2138e4bebe0SHaojian Zhuang static struct irq_chip hip04_irq_chip = { 2148e4bebe0SHaojian Zhuang .name = "HIP04 INTC", 2158e4bebe0SHaojian Zhuang .irq_mask = hip04_mask_irq, 2168e4bebe0SHaojian Zhuang .irq_unmask = hip04_unmask_irq, 2178e4bebe0SHaojian Zhuang .irq_eoi = hip04_eoi_irq, 2188e4bebe0SHaojian Zhuang .irq_set_type = hip04_irq_set_type, 2198e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 2208e4bebe0SHaojian Zhuang .irq_set_affinity = hip04_irq_set_affinity, 221*a2df12c5SMarc Zyngier .ipi_send_mask = hip04_ipi_send_mask, 2228e4bebe0SHaojian Zhuang #endif 223aec89ef7SSudeep Holla .flags = IRQCHIP_SET_TYPE_MASKED | 224aec89ef7SSudeep Holla IRQCHIP_SKIP_SET_WAKE | 225aec89ef7SSudeep Holla IRQCHIP_MASK_ON_SUSPEND, 2268e4bebe0SHaojian Zhuang }; 2278e4bebe0SHaojian Zhuang 2288e4bebe0SHaojian Zhuang static u16 hip04_get_cpumask(struct hip04_irq_data *intc) 2298e4bebe0SHaojian Zhuang { 2308e4bebe0SHaojian Zhuang void __iomem *base = intc->dist_base; 2318e4bebe0SHaojian Zhuang u32 mask, i; 2328e4bebe0SHaojian Zhuang 2338e4bebe0SHaojian Zhuang for (i = mask = 0; i < 32; i += 2) { 2348e4bebe0SHaojian Zhuang mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2); 2358e4bebe0SHaojian Zhuang mask |= mask >> 16; 2368e4bebe0SHaojian Zhuang if (mask) 2378e4bebe0SHaojian Zhuang break; 2388e4bebe0SHaojian Zhuang } 2398e4bebe0SHaojian Zhuang 2408e4bebe0SHaojian Zhuang if (!mask) 2418e4bebe0SHaojian Zhuang pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); 2428e4bebe0SHaojian Zhuang 2438e4bebe0SHaojian Zhuang return mask; 2448e4bebe0SHaojian Zhuang } 2458e4bebe0SHaojian Zhuang 2468e4bebe0SHaojian Zhuang static void __init hip04_irq_dist_init(struct hip04_irq_data *intc) 2478e4bebe0SHaojian Zhuang { 2488e4bebe0SHaojian Zhuang unsigned int i; 2498e4bebe0SHaojian Zhuang u32 cpumask; 2508e4bebe0SHaojian Zhuang unsigned int nr_irqs = intc->nr_irqs; 2518e4bebe0SHaojian Zhuang void __iomem *base = intc->dist_base; 2528e4bebe0SHaojian Zhuang 2538e4bebe0SHaojian Zhuang writel_relaxed(0, base + GIC_DIST_CTRL); 2548e4bebe0SHaojian Zhuang 2558e4bebe0SHaojian Zhuang /* 2568e4bebe0SHaojian Zhuang * Set all global interrupts to this CPU only. 2578e4bebe0SHaojian Zhuang */ 2588e4bebe0SHaojian Zhuang cpumask = hip04_get_cpumask(intc); 2598e4bebe0SHaojian Zhuang cpumask |= cpumask << 16; 2608e4bebe0SHaojian Zhuang for (i = 32; i < nr_irqs; i += 2) 2618e4bebe0SHaojian Zhuang writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3)); 2628e4bebe0SHaojian Zhuang 2638e4bebe0SHaojian Zhuang gic_dist_config(base, nr_irqs, NULL); 2648e4bebe0SHaojian Zhuang 2658e4bebe0SHaojian Zhuang writel_relaxed(1, base + GIC_DIST_CTRL); 2668e4bebe0SHaojian Zhuang } 2678e4bebe0SHaojian Zhuang 2688e4bebe0SHaojian Zhuang static void hip04_irq_cpu_init(struct hip04_irq_data *intc) 2698e4bebe0SHaojian Zhuang { 2708e4bebe0SHaojian Zhuang void __iomem *dist_base = intc->dist_base; 2718e4bebe0SHaojian Zhuang void __iomem *base = intc->cpu_base; 2728e4bebe0SHaojian Zhuang unsigned int cpu_mask, cpu = smp_processor_id(); 2738e4bebe0SHaojian Zhuang int i; 2748e4bebe0SHaojian Zhuang 2758e4bebe0SHaojian Zhuang /* 2768e4bebe0SHaojian Zhuang * Get what the GIC says our CPU mask is. 2778e4bebe0SHaojian Zhuang */ 2788e4bebe0SHaojian Zhuang BUG_ON(cpu >= NR_HIP04_CPU_IF); 2798e4bebe0SHaojian Zhuang cpu_mask = hip04_get_cpumask(intc); 2808e4bebe0SHaojian Zhuang hip04_cpu_map[cpu] = cpu_mask; 2818e4bebe0SHaojian Zhuang 2828e4bebe0SHaojian Zhuang /* 2838e4bebe0SHaojian Zhuang * Clear our mask from the other map entries in case they're 2848e4bebe0SHaojian Zhuang * still undefined. 2858e4bebe0SHaojian Zhuang */ 2868e4bebe0SHaojian Zhuang for (i = 0; i < NR_HIP04_CPU_IF; i++) 2878e4bebe0SHaojian Zhuang if (i != cpu) 2888e4bebe0SHaojian Zhuang hip04_cpu_map[i] &= ~cpu_mask; 2898e4bebe0SHaojian Zhuang 2901a60e1e6SMarc Zyngier gic_cpu_config(dist_base, 32, NULL); 2918e4bebe0SHaojian Zhuang 2928e4bebe0SHaojian Zhuang writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); 2938e4bebe0SHaojian Zhuang writel_relaxed(1, base + GIC_CPU_CTRL); 2948e4bebe0SHaojian Zhuang } 2958e4bebe0SHaojian Zhuang 2968e4bebe0SHaojian Zhuang static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, 2978e4bebe0SHaojian Zhuang irq_hw_number_t hw) 2988e4bebe0SHaojian Zhuang { 299*a2df12c5SMarc Zyngier if (hw < 16) { 300*a2df12c5SMarc Zyngier irq_set_percpu_devid(irq); 301*a2df12c5SMarc Zyngier irq_set_chip_and_handler(irq, &hip04_irq_chip, 302*a2df12c5SMarc Zyngier handle_percpu_devid_fasteoi_ipi); 303*a2df12c5SMarc Zyngier } else if (hw < 32) { 3048e4bebe0SHaojian Zhuang irq_set_percpu_devid(irq); 3058e4bebe0SHaojian Zhuang irq_set_chip_and_handler(irq, &hip04_irq_chip, 3068e4bebe0SHaojian Zhuang handle_percpu_devid_irq); 3078e4bebe0SHaojian Zhuang } else { 3088e4bebe0SHaojian Zhuang irq_set_chip_and_handler(irq, &hip04_irq_chip, 3098e4bebe0SHaojian Zhuang handle_fasteoi_irq); 310d17cab44SRob Herring irq_set_probe(irq); 31179a0d4d8SMarc Zyngier irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq))); 3128e4bebe0SHaojian Zhuang } 3138e4bebe0SHaojian Zhuang irq_set_chip_data(irq, d->host_data); 3148e4bebe0SHaojian Zhuang return 0; 3158e4bebe0SHaojian Zhuang } 3168e4bebe0SHaojian Zhuang 3178e4bebe0SHaojian Zhuang static int hip04_irq_domain_xlate(struct irq_domain *d, 3188e4bebe0SHaojian Zhuang struct device_node *controller, 3198e4bebe0SHaojian Zhuang const u32 *intspec, unsigned int intsize, 3208e4bebe0SHaojian Zhuang unsigned long *out_hwirq, 3218e4bebe0SHaojian Zhuang unsigned int *out_type) 3228e4bebe0SHaojian Zhuang { 3235d4c9bc7SMarc Zyngier if (irq_domain_get_of_node(d) != controller) 3248e4bebe0SHaojian Zhuang return -EINVAL; 325*a2df12c5SMarc Zyngier if (intsize == 1 && intspec[0] < 16) { 326*a2df12c5SMarc Zyngier *out_hwirq = intspec[0]; 327*a2df12c5SMarc Zyngier *out_type = IRQ_TYPE_EDGE_RISING; 328*a2df12c5SMarc Zyngier return 0; 329*a2df12c5SMarc Zyngier } 3308e4bebe0SHaojian Zhuang if (intsize < 3) 3318e4bebe0SHaojian Zhuang return -EINVAL; 3328e4bebe0SHaojian Zhuang 3338e4bebe0SHaojian Zhuang /* Get the interrupt number and add 16 to skip over SGIs */ 3348e4bebe0SHaojian Zhuang *out_hwirq = intspec[1] + 16; 3358e4bebe0SHaojian Zhuang 3368e4bebe0SHaojian Zhuang /* For SPIs, we need to add 16 more to get the irq ID number */ 3378e4bebe0SHaojian Zhuang if (!intspec[0]) 3388e4bebe0SHaojian Zhuang *out_hwirq += 16; 3398e4bebe0SHaojian Zhuang 3408e4bebe0SHaojian Zhuang *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; 3418e4bebe0SHaojian Zhuang 342*a2df12c5SMarc Zyngier return 0; 3438e4bebe0SHaojian Zhuang } 3448e4bebe0SHaojian Zhuang 3456c034d17SRichard Cochran static int hip04_irq_starting_cpu(unsigned int cpu) 3468e4bebe0SHaojian Zhuang { 3478e4bebe0SHaojian Zhuang hip04_irq_cpu_init(&hip04_data); 3486c034d17SRichard Cochran return 0; 3498e4bebe0SHaojian Zhuang } 3508e4bebe0SHaojian Zhuang 3518e4bebe0SHaojian Zhuang static const struct irq_domain_ops hip04_irq_domain_ops = { 3528e4bebe0SHaojian Zhuang .map = hip04_irq_domain_map, 3538e4bebe0SHaojian Zhuang .xlate = hip04_irq_domain_xlate, 3548e4bebe0SHaojian Zhuang }; 3558e4bebe0SHaojian Zhuang 3568e4bebe0SHaojian Zhuang static int __init 3578e4bebe0SHaojian Zhuang hip04_of_init(struct device_node *node, struct device_node *parent) 3588e4bebe0SHaojian Zhuang { 3598e4bebe0SHaojian Zhuang int nr_irqs, irq_base, i; 3608e4bebe0SHaojian Zhuang 3618e4bebe0SHaojian Zhuang if (WARN_ON(!node)) 3628e4bebe0SHaojian Zhuang return -ENODEV; 3638e4bebe0SHaojian Zhuang 3648e4bebe0SHaojian Zhuang hip04_data.dist_base = of_iomap(node, 0); 3658e4bebe0SHaojian Zhuang WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n"); 3668e4bebe0SHaojian Zhuang 3678e4bebe0SHaojian Zhuang hip04_data.cpu_base = of_iomap(node, 1); 3688e4bebe0SHaojian Zhuang WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n"); 3698e4bebe0SHaojian Zhuang 3708e4bebe0SHaojian Zhuang /* 3718e4bebe0SHaojian Zhuang * Initialize the CPU interface map to all CPUs. 3728e4bebe0SHaojian Zhuang * It will be refined as each CPU probes its ID. 3738e4bebe0SHaojian Zhuang */ 3748e4bebe0SHaojian Zhuang for (i = 0; i < NR_HIP04_CPU_IF; i++) 37503d3d45bSWang Long hip04_cpu_map[i] = 0xffff; 3768e4bebe0SHaojian Zhuang 3778e4bebe0SHaojian Zhuang /* 3788e4bebe0SHaojian Zhuang * Find out how many interrupts are supported. 3798e4bebe0SHaojian Zhuang * The HIP04 INTC only supports up to 510 interrupt sources. 3808e4bebe0SHaojian Zhuang */ 3818e4bebe0SHaojian Zhuang nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f; 3828e4bebe0SHaojian Zhuang nr_irqs = (nr_irqs + 1) * 32; 3838e4bebe0SHaojian Zhuang if (nr_irqs > HIP04_MAX_IRQS) 3848e4bebe0SHaojian Zhuang nr_irqs = HIP04_MAX_IRQS; 3858e4bebe0SHaojian Zhuang hip04_data.nr_irqs = nr_irqs; 3868e4bebe0SHaojian Zhuang 387*a2df12c5SMarc Zyngier irq_base = irq_alloc_descs(-1, 0, nr_irqs, numa_node_id()); 388287980e4SArnd Bergmann if (irq_base < 0) { 3898e4bebe0SHaojian Zhuang pr_err("failed to allocate IRQ numbers\n"); 3908e4bebe0SHaojian Zhuang return -EINVAL; 3918e4bebe0SHaojian Zhuang } 3928e4bebe0SHaojian Zhuang 3938e4bebe0SHaojian Zhuang hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 394*a2df12c5SMarc Zyngier 0, 3958e4bebe0SHaojian Zhuang &hip04_irq_domain_ops, 3968e4bebe0SHaojian Zhuang &hip04_data); 3978e4bebe0SHaojian Zhuang if (WARN_ON(!hip04_data.domain)) 3988e4bebe0SHaojian Zhuang return -EINVAL; 3998e4bebe0SHaojian Zhuang 4008e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 401*a2df12c5SMarc Zyngier set_smp_ipi_range(irq_base, 16); 4028e4bebe0SHaojian Zhuang #endif 4038e4bebe0SHaojian Zhuang set_handle_irq(hip04_handle_irq); 4048e4bebe0SHaojian Zhuang 4058e4bebe0SHaojian Zhuang hip04_irq_dist_init(&hip04_data); 40673c1b41eSThomas Gleixner cpuhp_setup_state(CPUHP_AP_IRQ_HIP04_STARTING, "irqchip/hip04:starting", 4076c034d17SRichard Cochran hip04_irq_starting_cpu, NULL); 4088e4bebe0SHaojian Zhuang return 0; 4098e4bebe0SHaojian Zhuang } 4108e4bebe0SHaojian Zhuang IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init); 411