18e4bebe0SHaojian Zhuang /* 28e4bebe0SHaojian Zhuang * Hisilicon HiP04 INTC 38e4bebe0SHaojian Zhuang * 48e4bebe0SHaojian Zhuang * Copyright (C) 2002-2014 ARM Limited. 58e4bebe0SHaojian Zhuang * Copyright (c) 2013-2014 Hisilicon Ltd. 68e4bebe0SHaojian Zhuang * Copyright (c) 2013-2014 Linaro Ltd. 78e4bebe0SHaojian Zhuang * 88e4bebe0SHaojian Zhuang * This program is free software; you can redistribute it and/or modify 98e4bebe0SHaojian Zhuang * it under the terms of the GNU General Public License version 2 as 108e4bebe0SHaojian Zhuang * published by the Free Software Foundation. 118e4bebe0SHaojian Zhuang * 128e4bebe0SHaojian Zhuang * Interrupt architecture for the HIP04 INTC: 138e4bebe0SHaojian Zhuang * 148e4bebe0SHaojian Zhuang * o There is one Interrupt Distributor, which receives interrupts 158e4bebe0SHaojian Zhuang * from system devices and sends them to the Interrupt Controllers. 168e4bebe0SHaojian Zhuang * 178e4bebe0SHaojian Zhuang * o There is one CPU Interface per CPU, which sends interrupts sent 188e4bebe0SHaojian Zhuang * by the Distributor, and interrupts generated locally, to the 198e4bebe0SHaojian Zhuang * associated CPU. The base address of the CPU interface is usually 208e4bebe0SHaojian Zhuang * aliased so that the same address points to different chips depending 218e4bebe0SHaojian Zhuang * on the CPU it is accessed from. 228e4bebe0SHaojian Zhuang * 238e4bebe0SHaojian Zhuang * Note that IRQs 0-31 are special - they are local to each CPU. 248e4bebe0SHaojian Zhuang * As such, the enable set/clear, pending set/clear and active bit 258e4bebe0SHaojian Zhuang * registers are banked per-cpu for these sources. 268e4bebe0SHaojian Zhuang */ 278e4bebe0SHaojian Zhuang 288e4bebe0SHaojian Zhuang #include <linux/init.h> 298e4bebe0SHaojian Zhuang #include <linux/kernel.h> 308e4bebe0SHaojian Zhuang #include <linux/err.h> 318e4bebe0SHaojian Zhuang #include <linux/module.h> 328e4bebe0SHaojian Zhuang #include <linux/list.h> 338e4bebe0SHaojian Zhuang #include <linux/smp.h> 348e4bebe0SHaojian Zhuang #include <linux/cpu.h> 358e4bebe0SHaojian Zhuang #include <linux/cpu_pm.h> 368e4bebe0SHaojian Zhuang #include <linux/cpumask.h> 378e4bebe0SHaojian Zhuang #include <linux/io.h> 388e4bebe0SHaojian Zhuang #include <linux/of.h> 398e4bebe0SHaojian Zhuang #include <linux/of_address.h> 408e4bebe0SHaojian Zhuang #include <linux/of_irq.h> 418e4bebe0SHaojian Zhuang #include <linux/irqdomain.h> 428e4bebe0SHaojian Zhuang #include <linux/interrupt.h> 438e4bebe0SHaojian Zhuang #include <linux/slab.h> 4441a83e06SJoel Porquet #include <linux/irqchip.h> 458e4bebe0SHaojian Zhuang #include <linux/irqchip/arm-gic.h> 468e4bebe0SHaojian Zhuang 478e4bebe0SHaojian Zhuang #include <asm/irq.h> 488e4bebe0SHaojian Zhuang #include <asm/exception.h> 498e4bebe0SHaojian Zhuang #include <asm/smp_plat.h> 508e4bebe0SHaojian Zhuang 518e4bebe0SHaojian Zhuang #include "irq-gic-common.h" 528e4bebe0SHaojian Zhuang 538e4bebe0SHaojian Zhuang #define HIP04_MAX_IRQS 510 548e4bebe0SHaojian Zhuang 558e4bebe0SHaojian Zhuang struct hip04_irq_data { 568e4bebe0SHaojian Zhuang void __iomem *dist_base; 578e4bebe0SHaojian Zhuang void __iomem *cpu_base; 588e4bebe0SHaojian Zhuang struct irq_domain *domain; 598e4bebe0SHaojian Zhuang unsigned int nr_irqs; 608e4bebe0SHaojian Zhuang }; 618e4bebe0SHaojian Zhuang 628e4bebe0SHaojian Zhuang static DEFINE_RAW_SPINLOCK(irq_controller_lock); 638e4bebe0SHaojian Zhuang 648e4bebe0SHaojian Zhuang /* 658e4bebe0SHaojian Zhuang * The GIC mapping of CPU interfaces does not necessarily match 668e4bebe0SHaojian Zhuang * the logical CPU numbering. Let's use a mapping as returned 678e4bebe0SHaojian Zhuang * by the GIC itself. 688e4bebe0SHaojian Zhuang */ 698e4bebe0SHaojian Zhuang #define NR_HIP04_CPU_IF 16 708e4bebe0SHaojian Zhuang static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly; 718e4bebe0SHaojian Zhuang 728e4bebe0SHaojian Zhuang static struct hip04_irq_data hip04_data __read_mostly; 738e4bebe0SHaojian Zhuang 748e4bebe0SHaojian Zhuang static inline void __iomem *hip04_dist_base(struct irq_data *d) 758e4bebe0SHaojian Zhuang { 768e4bebe0SHaojian Zhuang struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); 778e4bebe0SHaojian Zhuang return hip04_data->dist_base; 788e4bebe0SHaojian Zhuang } 798e4bebe0SHaojian Zhuang 808e4bebe0SHaojian Zhuang static inline void __iomem *hip04_cpu_base(struct irq_data *d) 818e4bebe0SHaojian Zhuang { 828e4bebe0SHaojian Zhuang struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); 838e4bebe0SHaojian Zhuang return hip04_data->cpu_base; 848e4bebe0SHaojian Zhuang } 858e4bebe0SHaojian Zhuang 868e4bebe0SHaojian Zhuang static inline unsigned int hip04_irq(struct irq_data *d) 878e4bebe0SHaojian Zhuang { 888e4bebe0SHaojian Zhuang return d->hwirq; 898e4bebe0SHaojian Zhuang } 908e4bebe0SHaojian Zhuang 918e4bebe0SHaojian Zhuang /* 928e4bebe0SHaojian Zhuang * Routines to acknowledge, disable and enable interrupts 938e4bebe0SHaojian Zhuang */ 948e4bebe0SHaojian Zhuang static void hip04_mask_irq(struct irq_data *d) 958e4bebe0SHaojian Zhuang { 968e4bebe0SHaojian Zhuang u32 mask = 1 << (hip04_irq(d) % 32); 978e4bebe0SHaojian Zhuang 988e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 998e4bebe0SHaojian Zhuang writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR + 1008e4bebe0SHaojian Zhuang (hip04_irq(d) / 32) * 4); 1018e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1028e4bebe0SHaojian Zhuang } 1038e4bebe0SHaojian Zhuang 1048e4bebe0SHaojian Zhuang static void hip04_unmask_irq(struct irq_data *d) 1058e4bebe0SHaojian Zhuang { 1068e4bebe0SHaojian Zhuang u32 mask = 1 << (hip04_irq(d) % 32); 1078e4bebe0SHaojian Zhuang 1088e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1098e4bebe0SHaojian Zhuang writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET + 1108e4bebe0SHaojian Zhuang (hip04_irq(d) / 32) * 4); 1118e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1128e4bebe0SHaojian Zhuang } 1138e4bebe0SHaojian Zhuang 1148e4bebe0SHaojian Zhuang static void hip04_eoi_irq(struct irq_data *d) 1158e4bebe0SHaojian Zhuang { 1168e4bebe0SHaojian Zhuang writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI); 1178e4bebe0SHaojian Zhuang } 1188e4bebe0SHaojian Zhuang 1198e4bebe0SHaojian Zhuang static int hip04_irq_set_type(struct irq_data *d, unsigned int type) 1208e4bebe0SHaojian Zhuang { 1218e4bebe0SHaojian Zhuang void __iomem *base = hip04_dist_base(d); 1228e4bebe0SHaojian Zhuang unsigned int irq = hip04_irq(d); 123fb7e7debSLiviu Dudau int ret; 1248e4bebe0SHaojian Zhuang 1258e4bebe0SHaojian Zhuang /* Interrupt configuration for SGIs can't be changed */ 1268e4bebe0SHaojian Zhuang if (irq < 16) 1278e4bebe0SHaojian Zhuang return -EINVAL; 1288e4bebe0SHaojian Zhuang 129fb7e7debSLiviu Dudau /* SPIs have restrictions on the supported types */ 130fb7e7debSLiviu Dudau if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && 131fb7e7debSLiviu Dudau type != IRQ_TYPE_EDGE_RISING) 1328e4bebe0SHaojian Zhuang return -EINVAL; 1338e4bebe0SHaojian Zhuang 1348e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1358e4bebe0SHaojian Zhuang 136fb7e7debSLiviu Dudau ret = gic_configure_irq(irq, type, base, NULL); 1378e4bebe0SHaojian Zhuang 1388e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1398e4bebe0SHaojian Zhuang 140fb7e7debSLiviu Dudau return ret; 1418e4bebe0SHaojian Zhuang } 1428e4bebe0SHaojian Zhuang 1438e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 1448e4bebe0SHaojian Zhuang static int hip04_irq_set_affinity(struct irq_data *d, 1458e4bebe0SHaojian Zhuang const struct cpumask *mask_val, 1468e4bebe0SHaojian Zhuang bool force) 1478e4bebe0SHaojian Zhuang { 1488e4bebe0SHaojian Zhuang void __iomem *reg; 1498e4bebe0SHaojian Zhuang unsigned int cpu, shift = (hip04_irq(d) % 2) * 16; 1508e4bebe0SHaojian Zhuang u32 val, mask, bit; 1518e4bebe0SHaojian Zhuang 1528e4bebe0SHaojian Zhuang if (!force) 1538e4bebe0SHaojian Zhuang cpu = cpumask_any_and(mask_val, cpu_online_mask); 1548e4bebe0SHaojian Zhuang else 1558e4bebe0SHaojian Zhuang cpu = cpumask_first(mask_val); 1568e4bebe0SHaojian Zhuang 1578e4bebe0SHaojian Zhuang if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids) 1588e4bebe0SHaojian Zhuang return -EINVAL; 1598e4bebe0SHaojian Zhuang 1608e4bebe0SHaojian Zhuang raw_spin_lock(&irq_controller_lock); 1618e4bebe0SHaojian Zhuang reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3); 1628e4bebe0SHaojian Zhuang mask = 0xffff << shift; 1638e4bebe0SHaojian Zhuang bit = hip04_cpu_map[cpu] << shift; 1648e4bebe0SHaojian Zhuang val = readl_relaxed(reg) & ~mask; 1658e4bebe0SHaojian Zhuang writel_relaxed(val | bit, reg); 1668e4bebe0SHaojian Zhuang raw_spin_unlock(&irq_controller_lock); 1678e4bebe0SHaojian Zhuang 1688e4bebe0SHaojian Zhuang return IRQ_SET_MASK_OK; 1698e4bebe0SHaojian Zhuang } 1708e4bebe0SHaojian Zhuang #endif 1718e4bebe0SHaojian Zhuang 1728e4bebe0SHaojian Zhuang static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs) 1738e4bebe0SHaojian Zhuang { 1748e4bebe0SHaojian Zhuang u32 irqstat, irqnr; 1758e4bebe0SHaojian Zhuang void __iomem *cpu_base = hip04_data.cpu_base; 1768e4bebe0SHaojian Zhuang 1778e4bebe0SHaojian Zhuang do { 1788e4bebe0SHaojian Zhuang irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); 1798e4bebe0SHaojian Zhuang irqnr = irqstat & GICC_IAR_INT_ID_MASK; 1808e4bebe0SHaojian Zhuang 1818e4bebe0SHaojian Zhuang if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) { 1823fe14927SMarc Zyngier handle_domain_irq(hip04_data.domain, irqnr, regs); 1838e4bebe0SHaojian Zhuang continue; 1848e4bebe0SHaojian Zhuang } 1858e4bebe0SHaojian Zhuang if (irqnr < 16) { 1868e4bebe0SHaojian Zhuang writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); 1878e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 1888e4bebe0SHaojian Zhuang handle_IPI(irqnr, regs); 1898e4bebe0SHaojian Zhuang #endif 1908e4bebe0SHaojian Zhuang continue; 1918e4bebe0SHaojian Zhuang } 1928e4bebe0SHaojian Zhuang break; 1938e4bebe0SHaojian Zhuang } while (1); 1948e4bebe0SHaojian Zhuang } 1958e4bebe0SHaojian Zhuang 1968e4bebe0SHaojian Zhuang static struct irq_chip hip04_irq_chip = { 1978e4bebe0SHaojian Zhuang .name = "HIP04 INTC", 1988e4bebe0SHaojian Zhuang .irq_mask = hip04_mask_irq, 1998e4bebe0SHaojian Zhuang .irq_unmask = hip04_unmask_irq, 2008e4bebe0SHaojian Zhuang .irq_eoi = hip04_eoi_irq, 2018e4bebe0SHaojian Zhuang .irq_set_type = hip04_irq_set_type, 2028e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 2038e4bebe0SHaojian Zhuang .irq_set_affinity = hip04_irq_set_affinity, 2048e4bebe0SHaojian Zhuang #endif 205aec89ef7SSudeep Holla .flags = IRQCHIP_SET_TYPE_MASKED | 206aec89ef7SSudeep Holla IRQCHIP_SKIP_SET_WAKE | 207aec89ef7SSudeep Holla IRQCHIP_MASK_ON_SUSPEND, 2088e4bebe0SHaojian Zhuang }; 2098e4bebe0SHaojian Zhuang 2108e4bebe0SHaojian Zhuang static u16 hip04_get_cpumask(struct hip04_irq_data *intc) 2118e4bebe0SHaojian Zhuang { 2128e4bebe0SHaojian Zhuang void __iomem *base = intc->dist_base; 2138e4bebe0SHaojian Zhuang u32 mask, i; 2148e4bebe0SHaojian Zhuang 2158e4bebe0SHaojian Zhuang for (i = mask = 0; i < 32; i += 2) { 2168e4bebe0SHaojian Zhuang mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2); 2178e4bebe0SHaojian Zhuang mask |= mask >> 16; 2188e4bebe0SHaojian Zhuang if (mask) 2198e4bebe0SHaojian Zhuang break; 2208e4bebe0SHaojian Zhuang } 2218e4bebe0SHaojian Zhuang 2228e4bebe0SHaojian Zhuang if (!mask) 2238e4bebe0SHaojian Zhuang pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); 2248e4bebe0SHaojian Zhuang 2258e4bebe0SHaojian Zhuang return mask; 2268e4bebe0SHaojian Zhuang } 2278e4bebe0SHaojian Zhuang 2288e4bebe0SHaojian Zhuang static void __init hip04_irq_dist_init(struct hip04_irq_data *intc) 2298e4bebe0SHaojian Zhuang { 2308e4bebe0SHaojian Zhuang unsigned int i; 2318e4bebe0SHaojian Zhuang u32 cpumask; 2328e4bebe0SHaojian Zhuang unsigned int nr_irqs = intc->nr_irqs; 2338e4bebe0SHaojian Zhuang void __iomem *base = intc->dist_base; 2348e4bebe0SHaojian Zhuang 2358e4bebe0SHaojian Zhuang writel_relaxed(0, base + GIC_DIST_CTRL); 2368e4bebe0SHaojian Zhuang 2378e4bebe0SHaojian Zhuang /* 2388e4bebe0SHaojian Zhuang * Set all global interrupts to this CPU only. 2398e4bebe0SHaojian Zhuang */ 2408e4bebe0SHaojian Zhuang cpumask = hip04_get_cpumask(intc); 2418e4bebe0SHaojian Zhuang cpumask |= cpumask << 16; 2428e4bebe0SHaojian Zhuang for (i = 32; i < nr_irqs; i += 2) 2438e4bebe0SHaojian Zhuang writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3)); 2448e4bebe0SHaojian Zhuang 2458e4bebe0SHaojian Zhuang gic_dist_config(base, nr_irqs, NULL); 2468e4bebe0SHaojian Zhuang 2478e4bebe0SHaojian Zhuang writel_relaxed(1, base + GIC_DIST_CTRL); 2488e4bebe0SHaojian Zhuang } 2498e4bebe0SHaojian Zhuang 2508e4bebe0SHaojian Zhuang static void hip04_irq_cpu_init(struct hip04_irq_data *intc) 2518e4bebe0SHaojian Zhuang { 2528e4bebe0SHaojian Zhuang void __iomem *dist_base = intc->dist_base; 2538e4bebe0SHaojian Zhuang void __iomem *base = intc->cpu_base; 2548e4bebe0SHaojian Zhuang unsigned int cpu_mask, cpu = smp_processor_id(); 2558e4bebe0SHaojian Zhuang int i; 2568e4bebe0SHaojian Zhuang 2578e4bebe0SHaojian Zhuang /* 2588e4bebe0SHaojian Zhuang * Get what the GIC says our CPU mask is. 2598e4bebe0SHaojian Zhuang */ 2608e4bebe0SHaojian Zhuang BUG_ON(cpu >= NR_HIP04_CPU_IF); 2618e4bebe0SHaojian Zhuang cpu_mask = hip04_get_cpumask(intc); 2628e4bebe0SHaojian Zhuang hip04_cpu_map[cpu] = cpu_mask; 2638e4bebe0SHaojian Zhuang 2648e4bebe0SHaojian Zhuang /* 2658e4bebe0SHaojian Zhuang * Clear our mask from the other map entries in case they're 2668e4bebe0SHaojian Zhuang * still undefined. 2678e4bebe0SHaojian Zhuang */ 2688e4bebe0SHaojian Zhuang for (i = 0; i < NR_HIP04_CPU_IF; i++) 2698e4bebe0SHaojian Zhuang if (i != cpu) 2708e4bebe0SHaojian Zhuang hip04_cpu_map[i] &= ~cpu_mask; 2718e4bebe0SHaojian Zhuang 2728e4bebe0SHaojian Zhuang gic_cpu_config(dist_base, NULL); 2738e4bebe0SHaojian Zhuang 2748e4bebe0SHaojian Zhuang writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); 2758e4bebe0SHaojian Zhuang writel_relaxed(1, base + GIC_CPU_CTRL); 2768e4bebe0SHaojian Zhuang } 2778e4bebe0SHaojian Zhuang 2788e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 2798e4bebe0SHaojian Zhuang static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq) 2808e4bebe0SHaojian Zhuang { 2818e4bebe0SHaojian Zhuang int cpu; 2828e4bebe0SHaojian Zhuang unsigned long flags, map = 0; 2838e4bebe0SHaojian Zhuang 2848e4bebe0SHaojian Zhuang raw_spin_lock_irqsave(&irq_controller_lock, flags); 2858e4bebe0SHaojian Zhuang 2868e4bebe0SHaojian Zhuang /* Convert our logical CPU mask into a physical one. */ 2878e4bebe0SHaojian Zhuang for_each_cpu(cpu, mask) 2888e4bebe0SHaojian Zhuang map |= hip04_cpu_map[cpu]; 2898e4bebe0SHaojian Zhuang 2908e4bebe0SHaojian Zhuang /* 2918e4bebe0SHaojian Zhuang * Ensure that stores to Normal memory are visible to the 2928e4bebe0SHaojian Zhuang * other CPUs before they observe us issuing the IPI. 2938e4bebe0SHaojian Zhuang */ 2948e4bebe0SHaojian Zhuang dmb(ishst); 2958e4bebe0SHaojian Zhuang 2968e4bebe0SHaojian Zhuang /* this always happens on GIC0 */ 2978e4bebe0SHaojian Zhuang writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT); 2988e4bebe0SHaojian Zhuang 2998e4bebe0SHaojian Zhuang raw_spin_unlock_irqrestore(&irq_controller_lock, flags); 3008e4bebe0SHaojian Zhuang } 3018e4bebe0SHaojian Zhuang #endif 3028e4bebe0SHaojian Zhuang 3038e4bebe0SHaojian Zhuang static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, 3048e4bebe0SHaojian Zhuang irq_hw_number_t hw) 3058e4bebe0SHaojian Zhuang { 3068e4bebe0SHaojian Zhuang if (hw < 32) { 3078e4bebe0SHaojian Zhuang irq_set_percpu_devid(irq); 3088e4bebe0SHaojian Zhuang irq_set_chip_and_handler(irq, &hip04_irq_chip, 3098e4bebe0SHaojian Zhuang handle_percpu_devid_irq); 310d17cab44SRob Herring irq_set_status_flags(irq, IRQ_NOAUTOEN); 3118e4bebe0SHaojian Zhuang } else { 3128e4bebe0SHaojian Zhuang irq_set_chip_and_handler(irq, &hip04_irq_chip, 3138e4bebe0SHaojian Zhuang handle_fasteoi_irq); 314d17cab44SRob Herring irq_set_probe(irq); 3158e4bebe0SHaojian Zhuang } 3168e4bebe0SHaojian Zhuang irq_set_chip_data(irq, d->host_data); 3178e4bebe0SHaojian Zhuang return 0; 3188e4bebe0SHaojian Zhuang } 3198e4bebe0SHaojian Zhuang 3208e4bebe0SHaojian Zhuang static int hip04_irq_domain_xlate(struct irq_domain *d, 3218e4bebe0SHaojian Zhuang struct device_node *controller, 3228e4bebe0SHaojian Zhuang const u32 *intspec, unsigned int intsize, 3238e4bebe0SHaojian Zhuang unsigned long *out_hwirq, 3248e4bebe0SHaojian Zhuang unsigned int *out_type) 3258e4bebe0SHaojian Zhuang { 3268e4bebe0SHaojian Zhuang unsigned long ret = 0; 3278e4bebe0SHaojian Zhuang 3285d4c9bc7SMarc Zyngier if (irq_domain_get_of_node(d) != controller) 3298e4bebe0SHaojian Zhuang return -EINVAL; 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 3428e4bebe0SHaojian Zhuang return ret; 3438e4bebe0SHaojian Zhuang } 3448e4bebe0SHaojian Zhuang 345*6c034d17SRichard Cochran static int hip04_irq_starting_cpu(unsigned int cpu) 3468e4bebe0SHaojian Zhuang { 3478e4bebe0SHaojian Zhuang hip04_irq_cpu_init(&hip04_data); 348*6c034d17SRichard 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 irq_hw_number_t hwirq_base = 16; 3608e4bebe0SHaojian Zhuang int nr_irqs, irq_base, i; 3618e4bebe0SHaojian Zhuang 3628e4bebe0SHaojian Zhuang if (WARN_ON(!node)) 3638e4bebe0SHaojian Zhuang return -ENODEV; 3648e4bebe0SHaojian Zhuang 3658e4bebe0SHaojian Zhuang hip04_data.dist_base = of_iomap(node, 0); 3668e4bebe0SHaojian Zhuang WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n"); 3678e4bebe0SHaojian Zhuang 3688e4bebe0SHaojian Zhuang hip04_data.cpu_base = of_iomap(node, 1); 3698e4bebe0SHaojian Zhuang WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n"); 3708e4bebe0SHaojian Zhuang 3718e4bebe0SHaojian Zhuang /* 3728e4bebe0SHaojian Zhuang * Initialize the CPU interface map to all CPUs. 3738e4bebe0SHaojian Zhuang * It will be refined as each CPU probes its ID. 3748e4bebe0SHaojian Zhuang */ 3758e4bebe0SHaojian Zhuang for (i = 0; i < NR_HIP04_CPU_IF; i++) 37603d3d45bSWang Long hip04_cpu_map[i] = 0xffff; 3778e4bebe0SHaojian Zhuang 3788e4bebe0SHaojian Zhuang /* 3798e4bebe0SHaojian Zhuang * Find out how many interrupts are supported. 3808e4bebe0SHaojian Zhuang * The HIP04 INTC only supports up to 510 interrupt sources. 3818e4bebe0SHaojian Zhuang */ 3828e4bebe0SHaojian Zhuang nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f; 3838e4bebe0SHaojian Zhuang nr_irqs = (nr_irqs + 1) * 32; 3848e4bebe0SHaojian Zhuang if (nr_irqs > HIP04_MAX_IRQS) 3858e4bebe0SHaojian Zhuang nr_irqs = HIP04_MAX_IRQS; 3868e4bebe0SHaojian Zhuang hip04_data.nr_irqs = nr_irqs; 3878e4bebe0SHaojian Zhuang 3888e4bebe0SHaojian Zhuang nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */ 3898e4bebe0SHaojian Zhuang 3908e4bebe0SHaojian Zhuang irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id()); 391287980e4SArnd Bergmann if (irq_base < 0) { 3928e4bebe0SHaojian Zhuang pr_err("failed to allocate IRQ numbers\n"); 3938e4bebe0SHaojian Zhuang return -EINVAL; 3948e4bebe0SHaojian Zhuang } 3958e4bebe0SHaojian Zhuang 3968e4bebe0SHaojian Zhuang hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 3978e4bebe0SHaojian Zhuang hwirq_base, 3988e4bebe0SHaojian Zhuang &hip04_irq_domain_ops, 3998e4bebe0SHaojian Zhuang &hip04_data); 4008e4bebe0SHaojian Zhuang 4018e4bebe0SHaojian Zhuang if (WARN_ON(!hip04_data.domain)) 4028e4bebe0SHaojian Zhuang return -EINVAL; 4038e4bebe0SHaojian Zhuang 4048e4bebe0SHaojian Zhuang #ifdef CONFIG_SMP 4058e4bebe0SHaojian Zhuang set_smp_cross_call(hip04_raise_softirq); 4068e4bebe0SHaojian Zhuang #endif 4078e4bebe0SHaojian Zhuang set_handle_irq(hip04_handle_irq); 4088e4bebe0SHaojian Zhuang 4098e4bebe0SHaojian Zhuang hip04_irq_dist_init(&hip04_data); 410*6c034d17SRichard Cochran cpuhp_setup_state(CPUHP_AP_IRQ_HIP04_STARTING, "AP_IRQ_HIP04_STARTING", 411*6c034d17SRichard Cochran hip04_irq_starting_cpu, NULL); 4128e4bebe0SHaojian Zhuang return 0; 4138e4bebe0SHaojian Zhuang } 4148e4bebe0SHaojian Zhuang IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init); 415