19339d432SThomas Petazzoni /* 29339d432SThomas Petazzoni * Marvell Armada 370 and Armada XP SoC IRQ handling 39339d432SThomas Petazzoni * 49339d432SThomas Petazzoni * Copyright (C) 2012 Marvell 59339d432SThomas Petazzoni * 69339d432SThomas Petazzoni * Lior Amsalem <alior@marvell.com> 79339d432SThomas Petazzoni * Gregory CLEMENT <gregory.clement@free-electrons.com> 89339d432SThomas Petazzoni * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 99339d432SThomas Petazzoni * Ben Dooks <ben.dooks@codethink.co.uk> 109339d432SThomas Petazzoni * 119339d432SThomas Petazzoni * This file is licensed under the terms of the GNU General Public 129339d432SThomas Petazzoni * License version 2. This program is licensed "as is" without any 139339d432SThomas Petazzoni * warranty of any kind, whether express or implied. 149339d432SThomas Petazzoni */ 159339d432SThomas Petazzoni 169339d432SThomas Petazzoni #include <linux/kernel.h> 179339d432SThomas Petazzoni #include <linux/module.h> 189339d432SThomas Petazzoni #include <linux/init.h> 199339d432SThomas Petazzoni #include <linux/irq.h> 209339d432SThomas Petazzoni #include <linux/interrupt.h> 2141a83e06SJoel Porquet #include <linux/irqchip.h> 22bc69b8adSEzequiel Garcia #include <linux/irqchip/chained_irq.h> 23d7df84b3SThomas Petazzoni #include <linux/cpu.h> 249339d432SThomas Petazzoni #include <linux/io.h> 259339d432SThomas Petazzoni #include <linux/of_address.h> 269339d432SThomas Petazzoni #include <linux/of_irq.h> 2731f614edSThomas Petazzoni #include <linux/of_pci.h> 289339d432SThomas Petazzoni #include <linux/irqdomain.h> 2931f614edSThomas Petazzoni #include <linux/slab.h> 300f077eb5SThomas Petazzoni #include <linux/syscore_ops.h> 3131f614edSThomas Petazzoni #include <linux/msi.h> 329339d432SThomas Petazzoni #include <asm/mach/arch.h> 339339d432SThomas Petazzoni #include <asm/exception.h> 349339d432SThomas Petazzoni #include <asm/smp_plat.h> 359339d432SThomas Petazzoni #include <asm/mach/irq.h> 369339d432SThomas Petazzoni 379339d432SThomas Petazzoni /* Interrupt Controller Registers Map */ 389339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) 399339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) 4028da06dfSMaxime Ripard #define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) 4128da06dfSMaxime Ripard #define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) 429339d432SThomas Petazzoni 439339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CONTROL (0x00) 449339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) 459339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) 469339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) 478cc3cfc5SThomas Gleixner #define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF 48758e8366SGrzegorz Jaszczyk #define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid) 499339d432SThomas Petazzoni 509339d432SThomas Petazzoni #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 51bc69b8adSEzequiel Garcia #define ARMADA_375_PPI_CAUSE (0x10) 529339d432SThomas Petazzoni 539339d432SThomas Petazzoni #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) 549339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) 559339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) 569339d432SThomas Petazzoni 579339d432SThomas Petazzoni #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 589339d432SThomas Petazzoni 595ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 605ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 615ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 6231f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 6331f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 6431f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 6531f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 669339d432SThomas Petazzoni 679339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 689339d432SThomas Petazzoni static void __iomem *main_int_base; 699339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 700f077eb5SThomas Petazzoni static u32 doorbell_mask_reg; 715724be84SMaxime Ripard static int parent_irq; 7231f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 7331f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 74fcc392d5SThomas Petazzoni static struct irq_domain *armada_370_xp_msi_inner_domain; 7531f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 7631f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 7731f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 7831f614edSThomas Petazzoni #endif 799339d432SThomas Petazzoni 802c299de5SEzequiel Garcia static inline bool is_percpu_irq(irq_hw_number_t irq) 812c299de5SEzequiel Garcia { 82080481f9SMaxime Ripard if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) 832c299de5SEzequiel Garcia return true; 84080481f9SMaxime Ripard 852c299de5SEzequiel Garcia return false; 862c299de5SEzequiel Garcia } 872c299de5SEzequiel Garcia 889339d432SThomas Petazzoni /* 899339d432SThomas Petazzoni * In SMP mode: 909339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 911bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 929339d432SThomas Petazzoni */ 939339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 949339d432SThomas Petazzoni { 959339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 969339d432SThomas Petazzoni 972c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 989339d432SThomas Petazzoni writel(hwirq, main_int_base + 999339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 1009339d432SThomas Petazzoni else 1019339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1029339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 1039339d432SThomas Petazzoni } 1049339d432SThomas Petazzoni 1059339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 1069339d432SThomas Petazzoni { 1079339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 1089339d432SThomas Petazzoni 1092c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 1109339d432SThomas Petazzoni writel(hwirq, main_int_base + 1119339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 1129339d432SThomas Petazzoni else 1139339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1149339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1159339d432SThomas Petazzoni } 1169339d432SThomas Petazzoni 11731f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 11831f614edSThomas Petazzoni 119fcc392d5SThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 120f692a172SThomas Petazzoni .name = "MPIC MSI", 121fcc392d5SThomas Petazzoni .irq_mask = pci_msi_mask_irq, 122fcc392d5SThomas Petazzoni .irq_unmask = pci_msi_unmask_irq, 123fcc392d5SThomas Petazzoni }; 124fcc392d5SThomas Petazzoni 125fcc392d5SThomas Petazzoni static struct msi_domain_info armada_370_xp_msi_domain_info = { 126a71b9412SThomas Petazzoni .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 127a71b9412SThomas Petazzoni MSI_FLAG_MULTI_PCI_MSI), 128fcc392d5SThomas Petazzoni .chip = &armada_370_xp_msi_irq_chip, 129fcc392d5SThomas Petazzoni }; 130fcc392d5SThomas Petazzoni 131fcc392d5SThomas Petazzoni static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 132fcc392d5SThomas Petazzoni { 133fcc392d5SThomas Petazzoni msg->address_lo = lower_32_bits(msi_doorbell_addr); 134fcc392d5SThomas Petazzoni msg->address_hi = upper_32_bits(msi_doorbell_addr); 135fcc392d5SThomas Petazzoni msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START); 136fcc392d5SThomas Petazzoni } 137fcc392d5SThomas Petazzoni 138fcc392d5SThomas Petazzoni static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, 139fcc392d5SThomas Petazzoni const struct cpumask *mask, bool force) 140fcc392d5SThomas Petazzoni { 141fcc392d5SThomas Petazzoni return -EINVAL; 142fcc392d5SThomas Petazzoni } 143fcc392d5SThomas Petazzoni 144fcc392d5SThomas Petazzoni static struct irq_chip armada_370_xp_msi_bottom_irq_chip = { 145f692a172SThomas Petazzoni .name = "MPIC MSI", 146fcc392d5SThomas Petazzoni .irq_compose_msi_msg = armada_370_xp_compose_msi_msg, 147fcc392d5SThomas Petazzoni .irq_set_affinity = armada_370_xp_msi_set_affinity, 148fcc392d5SThomas Petazzoni }; 149fcc392d5SThomas Petazzoni 150fcc392d5SThomas Petazzoni static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, 151fcc392d5SThomas Petazzoni unsigned int nr_irqs, void *args) 15231f614edSThomas Petazzoni { 153a71b9412SThomas Petazzoni int hwirq, i; 15431f614edSThomas Petazzoni 15531f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 156a71b9412SThomas Petazzoni 157a71b9412SThomas Petazzoni hwirq = bitmap_find_next_zero_area(msi_used, PCI_MSI_DOORBELL_NR, 158a71b9412SThomas Petazzoni 0, nr_irqs, 0); 159fcc392d5SThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) { 160fcc392d5SThomas Petazzoni mutex_unlock(&msi_used_lock); 161fcc392d5SThomas Petazzoni return -ENOSPC; 162fcc392d5SThomas Petazzoni } 163fcc392d5SThomas Petazzoni 164a71b9412SThomas Petazzoni bitmap_set(msi_used, hwirq, nr_irqs); 16531f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 16631f614edSThomas Petazzoni 167a71b9412SThomas Petazzoni for (i = 0; i < nr_irqs; i++) { 168a71b9412SThomas Petazzoni irq_domain_set_info(domain, virq + i, hwirq + i, 169a71b9412SThomas Petazzoni &armada_370_xp_msi_bottom_irq_chip, 170fcc392d5SThomas Petazzoni domain->host_data, handle_simple_irq, 171fcc392d5SThomas Petazzoni NULL, NULL); 172a71b9412SThomas Petazzoni } 173fcc392d5SThomas Petazzoni 17431f614edSThomas Petazzoni return hwirq; 17531f614edSThomas Petazzoni } 17631f614edSThomas Petazzoni 177fcc392d5SThomas Petazzoni static void armada_370_xp_msi_free(struct irq_domain *domain, 178fcc392d5SThomas Petazzoni unsigned int virq, unsigned int nr_irqs) 17931f614edSThomas Petazzoni { 180fcc392d5SThomas Petazzoni struct irq_data *d = irq_domain_get_irq_data(domain, virq); 181fcc392d5SThomas Petazzoni 18231f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 183a71b9412SThomas Petazzoni bitmap_clear(msi_used, d->hwirq, nr_irqs); 18431f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 18531f614edSThomas Petazzoni } 18631f614edSThomas Petazzoni 187fcc392d5SThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_domain_ops = { 188fcc392d5SThomas Petazzoni .alloc = armada_370_xp_msi_alloc, 189fcc392d5SThomas Petazzoni .free = armada_370_xp_msi_free, 19031f614edSThomas Petazzoni }; 19131f614edSThomas Petazzoni 19231f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 19331f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 19431f614edSThomas Petazzoni { 19531f614edSThomas Petazzoni u32 reg; 19631f614edSThomas Petazzoni 19731f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 19831f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 19931f614edSThomas Petazzoni 200fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain = 201fcc392d5SThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 202fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_ops, NULL); 203fcc392d5SThomas Petazzoni if (!armada_370_xp_msi_inner_domain) 20431f614edSThomas Petazzoni return -ENOMEM; 20531f614edSThomas Petazzoni 20631f614edSThomas Petazzoni armada_370_xp_msi_domain = 207fcc392d5SThomas Petazzoni pci_msi_create_irq_domain(of_node_to_fwnode(node), 208fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_info, 209fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain); 21031f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 211fcc392d5SThomas Petazzoni irq_domain_remove(armada_370_xp_msi_inner_domain); 21231f614edSThomas Petazzoni return -ENOMEM; 21331f614edSThomas Petazzoni } 21431f614edSThomas Petazzoni 21531f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 21631f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 21731f614edSThomas Petazzoni 21831f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 21931f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 22031f614edSThomas Petazzoni 22131f614edSThomas Petazzoni /* Unmask IPI interrupt */ 22231f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 22331f614edSThomas Petazzoni 22431f614edSThomas Petazzoni return 0; 22531f614edSThomas Petazzoni } 22631f614edSThomas Petazzoni #else 22731f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 22831f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 22931f614edSThomas Petazzoni { 23031f614edSThomas Petazzoni return 0; 23131f614edSThomas Petazzoni } 23231f614edSThomas Petazzoni #endif 23331f614edSThomas Petazzoni 2349339d432SThomas Petazzoni #ifdef CONFIG_SMP 23519e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 23619e61d41SArnaud Ebalard 2379339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 2389339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 2399339d432SThomas Petazzoni { 2409339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 2418cc3cfc5SThomas Gleixner unsigned long reg, mask; 2429339d432SThomas Petazzoni int cpu; 2439339d432SThomas Petazzoni 2448cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 2458cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 2468cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 2479339d432SThomas Petazzoni 2489339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 2499339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2508cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 2519339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2529339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 2539339d432SThomas Petazzoni 2541dacf194SThomas Petazzoni return IRQ_SET_MASK_OK; 2559339d432SThomas Petazzoni } 2569339d432SThomas Petazzoni #endif 2579339d432SThomas Petazzoni 2589339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 259f692a172SThomas Petazzoni .name = "MPIC", 2609339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 2619339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 2629339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 2639339d432SThomas Petazzoni #ifdef CONFIG_SMP 2649339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 2659339d432SThomas Petazzoni #endif 2660d8e1d80SGregory CLEMENT .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 2679339d432SThomas Petazzoni }; 2689339d432SThomas Petazzoni 2699339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 2709339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 2719339d432SThomas Petazzoni { 2729339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 2732c299de5SEzequiel Garcia if (!is_percpu_irq(hw)) 2741bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 2751bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 2761bf25e78SLinus Torvalds else 2779339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 2789339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 2799339d432SThomas Petazzoni 2802c299de5SEzequiel Garcia if (is_percpu_irq(hw)) { 2819339d432SThomas Petazzoni irq_set_percpu_devid(virq); 2829339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 2839339d432SThomas Petazzoni handle_percpu_devid_irq); 2849339d432SThomas Petazzoni 2859339d432SThomas Petazzoni } else { 2869339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 2879339d432SThomas Petazzoni handle_level_irq); 2889339d432SThomas Petazzoni } 289d17cab44SRob Herring irq_set_probe(virq); 290353d6d6cSThomas Petazzoni irq_clear_status_flags(virq, IRQ_NOAUTOEN); 2919339d432SThomas Petazzoni 2929339d432SThomas Petazzoni return 0; 2939339d432SThomas Petazzoni } 2949339d432SThomas Petazzoni 295d7df84b3SThomas Petazzoni static void armada_xp_mpic_smp_cpu_init(void) 2969339d432SThomas Petazzoni { 297b73842b7SThomas Petazzoni u32 control; 298b73842b7SThomas Petazzoni int nr_irqs, i; 299b73842b7SThomas Petazzoni 300b73842b7SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 301b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 302b73842b7SThomas Petazzoni 303b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 304b73842b7SThomas Petazzoni writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 305b73842b7SThomas Petazzoni 3069339d432SThomas Petazzoni /* Clear pending IPIs */ 3079339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 3089339d432SThomas Petazzoni 3099339d432SThomas Petazzoni /* Enable first 8 IPIs */ 3105ec69017SThomas Petazzoni writel(IPI_DOORBELL_MASK, per_cpu_int_base + 3119339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 3129339d432SThomas Petazzoni 3139339d432SThomas Petazzoni /* Unmask IPI interrupt */ 3149339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 3159339d432SThomas Petazzoni } 316d7df84b3SThomas Petazzoni 31728da06dfSMaxime Ripard static void armada_xp_mpic_perf_init(void) 31828da06dfSMaxime Ripard { 31928da06dfSMaxime Ripard unsigned long cpuid = cpu_logical_map(smp_processor_id()); 32028da06dfSMaxime Ripard 32128da06dfSMaxime Ripard /* Enable Performance Counter Overflow interrupts */ 32228da06dfSMaxime Ripard writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), 32328da06dfSMaxime Ripard per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); 32428da06dfSMaxime Ripard } 32528da06dfSMaxime Ripard 326933a24b0SEzequiel Garcia #ifdef CONFIG_SMP 327933a24b0SEzequiel Garcia static void armada_mpic_send_doorbell(const struct cpumask *mask, 328933a24b0SEzequiel Garcia unsigned int irq) 329933a24b0SEzequiel Garcia { 330933a24b0SEzequiel Garcia int cpu; 331933a24b0SEzequiel Garcia unsigned long map = 0; 332933a24b0SEzequiel Garcia 333933a24b0SEzequiel Garcia /* Convert our logical CPU mask into a physical one. */ 334933a24b0SEzequiel Garcia for_each_cpu(cpu, mask) 335933a24b0SEzequiel Garcia map |= 1 << cpu_logical_map(cpu); 336933a24b0SEzequiel Garcia 337933a24b0SEzequiel Garcia /* 338933a24b0SEzequiel Garcia * Ensure that stores to Normal memory are visible to the 339933a24b0SEzequiel Garcia * other CPUs before issuing the IPI. 340933a24b0SEzequiel Garcia */ 341933a24b0SEzequiel Garcia dsb(); 342933a24b0SEzequiel Garcia 343933a24b0SEzequiel Garcia /* submit softirq */ 344933a24b0SEzequiel Garcia writel((map << 8) | irq, main_int_base + 345933a24b0SEzequiel Garcia ARMADA_370_XP_SW_TRIG_INT_OFFS); 346933a24b0SEzequiel Garcia } 347933a24b0SEzequiel Garcia 348cb5ff2d2SRichard Cochran static int armada_xp_mpic_starting_cpu(unsigned int cpu) 349d7df84b3SThomas Petazzoni { 35028da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 351d7df84b3SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 352cb5ff2d2SRichard Cochran return 0; 35328da06dfSMaxime Ripard } 3545724be84SMaxime Ripard 355cb5ff2d2SRichard Cochran static int mpic_cascaded_starting_cpu(unsigned int cpu) 3565724be84SMaxime Ripard { 35728da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 3585724be84SMaxime Ripard enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); 359cb5ff2d2SRichard Cochran return 0; 36028da06dfSMaxime Ripard } 361c76c15e6SArnd Bergmann #endif 3629339d432SThomas Petazzoni 36396009736SKrzysztof Kozlowski static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 3649339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 3659339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 3669339d432SThomas Petazzoni }; 3679339d432SThomas Petazzoni 36831f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 369bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 3709b8cf779SEzequiel Garcia { 37131f614edSThomas Petazzoni u32 msimask, msinr; 37231f614edSThomas Petazzoni 37331f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 37431f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 37531f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 37631f614edSThomas Petazzoni 377c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 37831f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 37931f614edSThomas Petazzoni 38031f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 38131f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 38231f614edSThomas Petazzoni int irq; 38331f614edSThomas Petazzoni 38431f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 38531f614edSThomas Petazzoni continue; 38631f614edSThomas Petazzoni 387e89c6a06SMarc Zyngier if (is_chained) { 388fcc392d5SThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_inner_domain, 3890636bab6SThomas Petazzoni msinr - PCI_MSI_DOORBELL_START); 390bc69b8adSEzequiel Garcia generic_handle_irq(irq); 391e89c6a06SMarc Zyngier } else { 3920636bab6SThomas Petazzoni irq = msinr - PCI_MSI_DOORBELL_START; 393fcc392d5SThomas Petazzoni handle_domain_irq(armada_370_xp_msi_inner_domain, 394e89c6a06SMarc Zyngier irq, regs); 395e89c6a06SMarc Zyngier } 39631f614edSThomas Petazzoni } 39731f614edSThomas Petazzoni } 3989b8cf779SEzequiel Garcia #else 399bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 40031f614edSThomas Petazzoni #endif 40131f614edSThomas Petazzoni 402bd0b9ac4SThomas Gleixner static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) 403bc69b8adSEzequiel Garcia { 4045b29264cSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 405758e8366SGrzegorz Jaszczyk unsigned long irqmap, irqn, irqsrc, cpuid; 406bc69b8adSEzequiel Garcia unsigned int cascade_irq; 407bc69b8adSEzequiel Garcia 408bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 409bc69b8adSEzequiel Garcia 410bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 411758e8366SGrzegorz Jaszczyk cpuid = cpu_logical_map(smp_processor_id()); 412bc69b8adSEzequiel Garcia 413bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 414758e8366SGrzegorz Jaszczyk irqsrc = readl_relaxed(main_int_base + 415758e8366SGrzegorz Jaszczyk ARMADA_370_XP_INT_SOURCE_CTL(irqn)); 416758e8366SGrzegorz Jaszczyk 417758e8366SGrzegorz Jaszczyk /* Check if the interrupt is not masked on current CPU. 418758e8366SGrzegorz Jaszczyk * Test IRQ (0-1) and FIQ (8-9) mask bits. 419758e8366SGrzegorz Jaszczyk */ 420758e8366SGrzegorz Jaszczyk if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) 421758e8366SGrzegorz Jaszczyk continue; 422758e8366SGrzegorz Jaszczyk 423758e8366SGrzegorz Jaszczyk if (irqn == 1) { 424758e8366SGrzegorz Jaszczyk armada_370_xp_handle_msi_irq(NULL, true); 425758e8366SGrzegorz Jaszczyk continue; 426758e8366SGrzegorz Jaszczyk } 427758e8366SGrzegorz Jaszczyk 428bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 429bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 430bc69b8adSEzequiel Garcia } 431bc69b8adSEzequiel Garcia 432bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 433bc69b8adSEzequiel Garcia } 434bc69b8adSEzequiel Garcia 4358783dd3aSStephen Boyd static void __exception_irq_entry 4369b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 4379b8cf779SEzequiel Garcia { 4389b8cf779SEzequiel Garcia u32 irqstat, irqnr; 4399b8cf779SEzequiel Garcia 4409b8cf779SEzequiel Garcia do { 4419b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 4429b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 4439b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 4449b8cf779SEzequiel Garcia 4459b8cf779SEzequiel Garcia if (irqnr > 1022) 4469b8cf779SEzequiel Garcia break; 4479b8cf779SEzequiel Garcia 4489b8cf779SEzequiel Garcia if (irqnr > 1) { 449e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_mpic_domain, 450e89c6a06SMarc Zyngier irqnr, regs); 4519b8cf779SEzequiel Garcia continue; 4529b8cf779SEzequiel Garcia } 4539b8cf779SEzequiel Garcia 4549b8cf779SEzequiel Garcia /* MSI handling */ 4559b8cf779SEzequiel Garcia if (irqnr == 1) 456bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 4579b8cf779SEzequiel Garcia 4589339d432SThomas Petazzoni #ifdef CONFIG_SMP 4599339d432SThomas Petazzoni /* IPI Handling */ 4609339d432SThomas Petazzoni if (irqnr == 0) { 4619339d432SThomas Petazzoni u32 ipimask, ipinr; 4629339d432SThomas Petazzoni 4639339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 4649339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 4655ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 4669339d432SThomas Petazzoni 467a6f089e9SLior Amsalem writel(~ipimask, per_cpu_int_base + 4689339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 4699339d432SThomas Petazzoni 4709339d432SThomas Petazzoni /* Handle all pending doorbells */ 4715ec69017SThomas Petazzoni for (ipinr = IPI_DOORBELL_START; 4725ec69017SThomas Petazzoni ipinr < IPI_DOORBELL_END; ipinr++) { 4739339d432SThomas Petazzoni if (ipimask & (0x1 << ipinr)) 4749339d432SThomas Petazzoni handle_IPI(ipinr, regs); 4759339d432SThomas Petazzoni } 4769339d432SThomas Petazzoni continue; 4779339d432SThomas Petazzoni } 4789339d432SThomas Petazzoni #endif 4799339d432SThomas Petazzoni 4809339d432SThomas Petazzoni } while (1); 4819339d432SThomas Petazzoni } 4829339d432SThomas Petazzoni 4830f077eb5SThomas Petazzoni static int armada_370_xp_mpic_suspend(void) 4840f077eb5SThomas Petazzoni { 4850f077eb5SThomas Petazzoni doorbell_mask_reg = readl(per_cpu_int_base + 4860f077eb5SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 4870f077eb5SThomas Petazzoni return 0; 4880f077eb5SThomas Petazzoni } 4890f077eb5SThomas Petazzoni 4900f077eb5SThomas Petazzoni static void armada_370_xp_mpic_resume(void) 4910f077eb5SThomas Petazzoni { 4920f077eb5SThomas Petazzoni int nirqs; 4930f077eb5SThomas Petazzoni irq_hw_number_t irq; 4940f077eb5SThomas Petazzoni 4950f077eb5SThomas Petazzoni /* Re-enable interrupts */ 4960f077eb5SThomas Petazzoni nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; 4970f077eb5SThomas Petazzoni for (irq = 0; irq < nirqs; irq++) { 4980f077eb5SThomas Petazzoni struct irq_data *data; 4990f077eb5SThomas Petazzoni int virq; 5000f077eb5SThomas Petazzoni 5010f077eb5SThomas Petazzoni virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 5020f077eb5SThomas Petazzoni if (virq == 0) 5030f077eb5SThomas Petazzoni continue; 5040f077eb5SThomas Petazzoni 505080481f9SMaxime Ripard if (!is_percpu_irq(irq)) 5060f077eb5SThomas Petazzoni writel(irq, per_cpu_int_base + 5070f077eb5SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5080f077eb5SThomas Petazzoni else 5090f077eb5SThomas Petazzoni writel(irq, main_int_base + 5100f077eb5SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 5110f077eb5SThomas Petazzoni 5120f077eb5SThomas Petazzoni data = irq_get_irq_data(virq); 5130f077eb5SThomas Petazzoni if (!irqd_irq_disabled(data)) 5140f077eb5SThomas Petazzoni armada_370_xp_irq_unmask(data); 5150f077eb5SThomas Petazzoni } 5160f077eb5SThomas Petazzoni 5170f077eb5SThomas Petazzoni /* Reconfigure doorbells for IPIs and MSIs */ 5180f077eb5SThomas Petazzoni writel(doorbell_mask_reg, 5190f077eb5SThomas Petazzoni per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 5200f077eb5SThomas Petazzoni if (doorbell_mask_reg & IPI_DOORBELL_MASK) 5210f077eb5SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5220f077eb5SThomas Petazzoni if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) 5230f077eb5SThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5240f077eb5SThomas Petazzoni } 5250f077eb5SThomas Petazzoni 5266c880902SBen Dooks static struct syscore_ops armada_370_xp_mpic_syscore_ops = { 5270f077eb5SThomas Petazzoni .suspend = armada_370_xp_mpic_suspend, 5280f077eb5SThomas Petazzoni .resume = armada_370_xp_mpic_resume, 5290f077eb5SThomas Petazzoni }; 5300f077eb5SThomas Petazzoni 531b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 532b313ada8SThomas Petazzoni struct device_node *parent) 533b313ada8SThomas Petazzoni { 534627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 5355724be84SMaxime Ripard int nr_irqs, i; 536b313ada8SThomas Petazzoni u32 control; 537b313ada8SThomas Petazzoni 538627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 539627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 540b313ada8SThomas Petazzoni 541627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 542627dfcc2SThomas Petazzoni resource_size(&main_int_res), 543627dfcc2SThomas Petazzoni node->full_name)); 544627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 545627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 546627dfcc2SThomas Petazzoni node->full_name)); 547627dfcc2SThomas Petazzoni 548627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 549627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 550b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 551627dfcc2SThomas Petazzoni 552627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 553627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 554b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 555b313ada8SThomas Petazzoni 556b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 557b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 558b73842b7SThomas Petazzoni 559b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 560b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 561b313ada8SThomas Petazzoni 562b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 563b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 564b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 565627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 566fcc392d5SThomas Petazzoni armada_370_xp_mpic_domain->bus_token = DOMAIN_BUS_WIRED; 567b313ada8SThomas Petazzoni 568933a24b0SEzequiel Garcia /* Setup for the boot CPU */ 56928da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 570b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 571b313ada8SThomas Petazzoni 57231f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 57331f614edSThomas Petazzoni 574bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 575bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 576bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 577b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 578ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 579ef37d337SThomas Petazzoni set_smp_cross_call(armada_mpic_send_doorbell); 580cb5ff2d2SRichard Cochran cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 58173c1b41eSThomas Gleixner "irqchip/armada/ipi:starting", 582cb5ff2d2SRichard Cochran armada_xp_mpic_starting_cpu, NULL); 583ef37d337SThomas Petazzoni #endif 584bc69b8adSEzequiel Garcia } else { 5855724be84SMaxime Ripard #ifdef CONFIG_SMP 586008b69e4SThomas Gleixner cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 58773c1b41eSThomas Gleixner "irqchip/armada/cascade:starting", 588cb5ff2d2SRichard Cochran mpic_cascaded_starting_cpu, NULL); 5895724be84SMaxime Ripard #endif 590bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 591bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 592bc69b8adSEzequiel Garcia } 593b313ada8SThomas Petazzoni 5940f077eb5SThomas Petazzoni register_syscore_ops(&armada_370_xp_mpic_syscore_ops); 5950f077eb5SThomas Petazzoni 596b313ada8SThomas Petazzoni return 0; 597b313ada8SThomas Petazzoni } 598b313ada8SThomas Petazzoni 5999339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 600