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 37054ea4ceSThomas Petazzoni /* 38054ea4ceSThomas Petazzoni * Overall diagram of the Armada XP interrupt controller: 39054ea4ceSThomas Petazzoni * 40054ea4ceSThomas Petazzoni * To CPU 0 To CPU 1 41054ea4ceSThomas Petazzoni * 42054ea4ceSThomas Petazzoni * /\ /\ 43054ea4ceSThomas Petazzoni * || || 44054ea4ceSThomas Petazzoni * +---------------+ +---------------+ 45054ea4ceSThomas Petazzoni * | | | | 46054ea4ceSThomas Petazzoni * | per-CPU | | per-CPU | 47054ea4ceSThomas Petazzoni * | mask/unmask | | mask/unmask | 48054ea4ceSThomas Petazzoni * | CPU0 | | CPU1 | 49054ea4ceSThomas Petazzoni * | | | | 50054ea4ceSThomas Petazzoni * +---------------+ +---------------+ 51054ea4ceSThomas Petazzoni * /\ /\ 52054ea4ceSThomas Petazzoni * || || 53054ea4ceSThomas Petazzoni * \\_______________________// 54054ea4ceSThomas Petazzoni * || 55054ea4ceSThomas Petazzoni * +-------------------+ 56054ea4ceSThomas Petazzoni * | | 57054ea4ceSThomas Petazzoni * | Global interrupt | 58054ea4ceSThomas Petazzoni * | mask/unmask | 59054ea4ceSThomas Petazzoni * | | 60054ea4ceSThomas Petazzoni * +-------------------+ 61054ea4ceSThomas Petazzoni * /\ 62054ea4ceSThomas Petazzoni * || 63054ea4ceSThomas Petazzoni * interrupt from 64054ea4ceSThomas Petazzoni * device 65054ea4ceSThomas Petazzoni * 66054ea4ceSThomas Petazzoni * The "global interrupt mask/unmask" is modified using the 67054ea4ceSThomas Petazzoni * ARMADA_370_XP_INT_SET_ENABLE_OFFS and 68054ea4ceSThomas Petazzoni * ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS registers, which are relative 69054ea4ceSThomas Petazzoni * to "main_int_base". 70054ea4ceSThomas Petazzoni * 71054ea4ceSThomas Petazzoni * The "per-CPU mask/unmask" is modified using the 72054ea4ceSThomas Petazzoni * ARMADA_370_XP_INT_SET_MASK_OFFS and 73054ea4ceSThomas Petazzoni * ARMADA_370_XP_INT_CLEAR_MASK_OFFS registers, which are relative to 74054ea4ceSThomas Petazzoni * "per_cpu_int_base". This base address points to a special address, 75054ea4ceSThomas Petazzoni * which automatically accesses the registers of the current CPU. 76054ea4ceSThomas Petazzoni * 77054ea4ceSThomas Petazzoni * The per-CPU mask/unmask can also be adjusted using the global 78054ea4ceSThomas Petazzoni * per-interrupt ARMADA_370_XP_INT_SOURCE_CTL register, which we use 79054ea4ceSThomas Petazzoni * to configure interrupt affinity. 80054ea4ceSThomas Petazzoni * 81054ea4ceSThomas Petazzoni * Due to this model, all interrupts need to be mask/unmasked at two 82054ea4ceSThomas Petazzoni * different levels: at the global level and at the per-CPU level. 83054ea4ceSThomas Petazzoni * 84054ea4ceSThomas Petazzoni * This driver takes the following approach to deal with this: 85054ea4ceSThomas Petazzoni * 86054ea4ceSThomas Petazzoni * - For global interrupts: 87054ea4ceSThomas Petazzoni * 88054ea4ceSThomas Petazzoni * At ->map() time, a global interrupt is unmasked at the per-CPU 89054ea4ceSThomas Petazzoni * mask/unmask level. It is therefore unmasked at this level for 90054ea4ceSThomas Petazzoni * the current CPU, running the ->map() code. This allows to have 91054ea4ceSThomas Petazzoni * the interrupt unmasked at this level in non-SMP 92054ea4ceSThomas Petazzoni * configurations. In SMP configurations, the ->set_affinity() 93054ea4ceSThomas Petazzoni * callback is called, which using the 94054ea4ceSThomas Petazzoni * ARMADA_370_XP_INT_SOURCE_CTL() readjusts the per-CPU mask/unmask 95054ea4ceSThomas Petazzoni * for the interrupt. 96054ea4ceSThomas Petazzoni * 97054ea4ceSThomas Petazzoni * The ->mask() and ->unmask() operations only mask/unmask the 98054ea4ceSThomas Petazzoni * interrupt at the "global" level. 99054ea4ceSThomas Petazzoni * 100054ea4ceSThomas Petazzoni * So, a global interrupt is enabled at the per-CPU level as soon 101054ea4ceSThomas Petazzoni * as it is mapped. At run time, the masking/unmasking takes place 102054ea4ceSThomas Petazzoni * at the global level. 103054ea4ceSThomas Petazzoni * 104054ea4ceSThomas Petazzoni * - For per-CPU interrupts 105054ea4ceSThomas Petazzoni * 106054ea4ceSThomas Petazzoni * At ->map() time, a per-CPU interrupt is unmasked at the global 107054ea4ceSThomas Petazzoni * mask/unmask level. 108054ea4ceSThomas Petazzoni * 109054ea4ceSThomas Petazzoni * The ->mask() and ->unmask() operations mask/unmask the interrupt 110054ea4ceSThomas Petazzoni * at the per-CPU level. 111054ea4ceSThomas Petazzoni * 112054ea4ceSThomas Petazzoni * So, a per-CPU interrupt is enabled at the global level as soon 113054ea4ceSThomas Petazzoni * as it is mapped. At run time, the masking/unmasking takes place 114054ea4ceSThomas Petazzoni * at the per-CPU level. 115054ea4ceSThomas Petazzoni */ 1169339d432SThomas Petazzoni 1179a234c9cSThomas Petazzoni /* Registers relative to main_int_base */ 1189339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CONTROL (0x00) 1199a234c9cSThomas Petazzoni #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04) 1209339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) 1219339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) 1229339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) 1238cc3cfc5SThomas Gleixner #define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF 124758e8366SGrzegorz Jaszczyk #define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid) 1259339d432SThomas Petazzoni 1269a234c9cSThomas Petazzoni /* Registers relative to per_cpu_int_base */ 1279a234c9cSThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x08) 1289a234c9cSThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0x0c) 129bc69b8adSEzequiel Garcia #define ARMADA_375_PPI_CAUSE (0x10) 1309a234c9cSThomas Petazzoni #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 1319a234c9cSThomas Petazzoni #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) 1329a234c9cSThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) 1339a234c9cSThomas Petazzoni #define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) 1349a234c9cSThomas Petazzoni #define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) 1359339d432SThomas Petazzoni 1369339d432SThomas Petazzoni #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 1379339d432SThomas Petazzoni 1385ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 1395ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 1405ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 14131f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 14231f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 14331f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 14431f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 1459339d432SThomas Petazzoni 1469339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 1479339d432SThomas Petazzoni static void __iomem *main_int_base; 1489339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 1490f077eb5SThomas Petazzoni static u32 doorbell_mask_reg; 1505724be84SMaxime Ripard static int parent_irq; 15131f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 15231f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 153fcc392d5SThomas Petazzoni static struct irq_domain *armada_370_xp_msi_inner_domain; 15431f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 15531f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 15631f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 15731f614edSThomas Petazzoni #endif 1589339d432SThomas Petazzoni 1592c299de5SEzequiel Garcia static inline bool is_percpu_irq(irq_hw_number_t irq) 1602c299de5SEzequiel Garcia { 161080481f9SMaxime Ripard if (irq <= ARMADA_370_XP_MAX_PER_CPU_IRQS) 1622c299de5SEzequiel Garcia return true; 163080481f9SMaxime Ripard 1642c299de5SEzequiel Garcia return false; 1652c299de5SEzequiel Garcia } 1662c299de5SEzequiel Garcia 1679339d432SThomas Petazzoni /* 1689339d432SThomas Petazzoni * In SMP mode: 1699339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 1701bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 1719339d432SThomas Petazzoni */ 1729339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 1739339d432SThomas Petazzoni { 1749339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 1759339d432SThomas Petazzoni 1762c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 1779339d432SThomas Petazzoni writel(hwirq, main_int_base + 1789339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 1799339d432SThomas Petazzoni else 1809339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1819339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 1829339d432SThomas Petazzoni } 1839339d432SThomas Petazzoni 1849339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 1859339d432SThomas Petazzoni { 1869339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 1879339d432SThomas Petazzoni 1882c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 1899339d432SThomas Petazzoni writel(hwirq, main_int_base + 1909339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 1919339d432SThomas Petazzoni else 1929339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1939339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1949339d432SThomas Petazzoni } 1959339d432SThomas Petazzoni 19631f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 19731f614edSThomas Petazzoni 198fcc392d5SThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 199f692a172SThomas Petazzoni .name = "MPIC MSI", 200fcc392d5SThomas Petazzoni .irq_mask = pci_msi_mask_irq, 201fcc392d5SThomas Petazzoni .irq_unmask = pci_msi_unmask_irq, 202fcc392d5SThomas Petazzoni }; 203fcc392d5SThomas Petazzoni 204fcc392d5SThomas Petazzoni static struct msi_domain_info armada_370_xp_msi_domain_info = { 205a71b9412SThomas Petazzoni .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 206319ec8b3SStefan Roese MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), 207fcc392d5SThomas Petazzoni .chip = &armada_370_xp_msi_irq_chip, 208fcc392d5SThomas Petazzoni }; 209fcc392d5SThomas Petazzoni 210fcc392d5SThomas Petazzoni static void armada_370_xp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 211fcc392d5SThomas Petazzoni { 2128ca61cdeSNathan Rossi unsigned int cpu = cpumask_first(irq_data_get_effective_affinity_mask(data)); 2138ca61cdeSNathan Rossi 214fcc392d5SThomas Petazzoni msg->address_lo = lower_32_bits(msi_doorbell_addr); 215fcc392d5SThomas Petazzoni msg->address_hi = upper_32_bits(msi_doorbell_addr); 2168ca61cdeSNathan Rossi msg->data = BIT(cpu + 8) | (data->hwirq + PCI_MSI_DOORBELL_START); 217fcc392d5SThomas Petazzoni } 218fcc392d5SThomas Petazzoni 219fcc392d5SThomas Petazzoni static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, 220fcc392d5SThomas Petazzoni const struct cpumask *mask, bool force) 221fcc392d5SThomas Petazzoni { 2228ca61cdeSNathan Rossi unsigned int cpu; 2238ca61cdeSNathan Rossi 2248ca61cdeSNathan Rossi if (!force) 2258ca61cdeSNathan Rossi cpu = cpumask_any_and(mask, cpu_online_mask); 2268ca61cdeSNathan Rossi else 2278ca61cdeSNathan Rossi cpu = cpumask_first(mask); 2288ca61cdeSNathan Rossi 2298ca61cdeSNathan Rossi if (cpu >= nr_cpu_ids) 230fcc392d5SThomas Petazzoni return -EINVAL; 2318ca61cdeSNathan Rossi 2328ca61cdeSNathan Rossi irq_data_update_effective_affinity(irq_data, cpumask_of(cpu)); 2338ca61cdeSNathan Rossi 2348ca61cdeSNathan Rossi return IRQ_SET_MASK_OK; 235fcc392d5SThomas Petazzoni } 236fcc392d5SThomas Petazzoni 237fcc392d5SThomas Petazzoni static struct irq_chip armada_370_xp_msi_bottom_irq_chip = { 238f692a172SThomas Petazzoni .name = "MPIC MSI", 239fcc392d5SThomas Petazzoni .irq_compose_msi_msg = armada_370_xp_compose_msi_msg, 240fcc392d5SThomas Petazzoni .irq_set_affinity = armada_370_xp_msi_set_affinity, 241fcc392d5SThomas Petazzoni }; 242fcc392d5SThomas Petazzoni 243fcc392d5SThomas Petazzoni static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, 244fcc392d5SThomas Petazzoni unsigned int nr_irqs, void *args) 24531f614edSThomas Petazzoni { 246a71b9412SThomas Petazzoni int hwirq, i; 24731f614edSThomas Petazzoni 24831f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 249d0a55350SPali Rohár hwirq = bitmap_find_free_region(msi_used, PCI_MSI_DOORBELL_NR, 250d0a55350SPali Rohár order_base_2(nr_irqs)); 251fcc392d5SThomas Petazzoni mutex_unlock(&msi_used_lock); 252d0a55350SPali Rohár 253d0a55350SPali Rohár if (hwirq < 0) 254fcc392d5SThomas Petazzoni return -ENOSPC; 25531f614edSThomas Petazzoni 256a71b9412SThomas Petazzoni for (i = 0; i < nr_irqs; i++) { 257a71b9412SThomas Petazzoni irq_domain_set_info(domain, virq + i, hwirq + i, 258a71b9412SThomas Petazzoni &armada_370_xp_msi_bottom_irq_chip, 259fcc392d5SThomas Petazzoni domain->host_data, handle_simple_irq, 260fcc392d5SThomas Petazzoni NULL, NULL); 261a71b9412SThomas Petazzoni } 262fcc392d5SThomas Petazzoni 263ce20eff5SPali Rohár return 0; 26431f614edSThomas Petazzoni } 26531f614edSThomas Petazzoni 266fcc392d5SThomas Petazzoni static void armada_370_xp_msi_free(struct irq_domain *domain, 267fcc392d5SThomas Petazzoni unsigned int virq, unsigned int nr_irqs) 26831f614edSThomas Petazzoni { 269fcc392d5SThomas Petazzoni struct irq_data *d = irq_domain_get_irq_data(domain, virq); 270fcc392d5SThomas Petazzoni 27131f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 272d0a55350SPali Rohár bitmap_release_region(msi_used, d->hwirq, order_base_2(nr_irqs)); 27331f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 27431f614edSThomas Petazzoni } 27531f614edSThomas Petazzoni 276fcc392d5SThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_domain_ops = { 277fcc392d5SThomas Petazzoni .alloc = armada_370_xp_msi_alloc, 278fcc392d5SThomas Petazzoni .free = armada_370_xp_msi_free, 27931f614edSThomas Petazzoni }; 28031f614edSThomas Petazzoni 2818ca61cdeSNathan Rossi static void armada_370_xp_msi_reenable_percpu(void) 28231f614edSThomas Petazzoni { 28331f614edSThomas Petazzoni u32 reg; 28431f614edSThomas Petazzoni 2858ca61cdeSNathan Rossi /* Enable MSI doorbell mask and combined cpu local interrupt */ 2868ca61cdeSNathan Rossi reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 2878ca61cdeSNathan Rossi | PCI_MSI_DOORBELL_MASK; 2888ca61cdeSNathan Rossi writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 2898ca61cdeSNathan Rossi /* Unmask local doorbell interrupt */ 2908ca61cdeSNathan Rossi writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 2918ca61cdeSNathan Rossi } 2928ca61cdeSNathan Rossi 2938ca61cdeSNathan Rossi static int armada_370_xp_msi_init(struct device_node *node, 2948ca61cdeSNathan Rossi phys_addr_t main_int_phys_base) 2958ca61cdeSNathan Rossi { 29631f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 29731f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 29831f614edSThomas Petazzoni 299fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain = 300fcc392d5SThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 301fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_ops, NULL); 302fcc392d5SThomas Petazzoni if (!armada_370_xp_msi_inner_domain) 30331f614edSThomas Petazzoni return -ENOMEM; 30431f614edSThomas Petazzoni 30531f614edSThomas Petazzoni armada_370_xp_msi_domain = 306fcc392d5SThomas Petazzoni pci_msi_create_irq_domain(of_node_to_fwnode(node), 307fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_info, 308fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain); 30931f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 310fcc392d5SThomas Petazzoni irq_domain_remove(armada_370_xp_msi_inner_domain); 31131f614edSThomas Petazzoni return -ENOMEM; 31231f614edSThomas Petazzoni } 31331f614edSThomas Petazzoni 3148ca61cdeSNathan Rossi armada_370_xp_msi_reenable_percpu(); 31531f614edSThomas Petazzoni 31631f614edSThomas Petazzoni return 0; 31731f614edSThomas Petazzoni } 31831f614edSThomas Petazzoni #else 3198ca61cdeSNathan Rossi static void armada_370_xp_msi_reenable_percpu(void) {} 3208ca61cdeSNathan Rossi 32131f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 32231f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 32331f614edSThomas Petazzoni { 32431f614edSThomas Petazzoni return 0; 32531f614edSThomas Petazzoni } 32631f614edSThomas Petazzoni #endif 32731f614edSThomas Petazzoni 328f02147ddSMarc Zyngier static void armada_xp_mpic_perf_init(void) 329f02147ddSMarc Zyngier { 330a3d66a76SPali Rohár unsigned long cpuid; 331a3d66a76SPali Rohár 332a3d66a76SPali Rohár /* 333a3d66a76SPali Rohár * This Performance Counter Overflow interrupt is specific for 334a3d66a76SPali Rohár * Armada 370 and XP. It is not available on Armada 375, 38x and 39x. 335a3d66a76SPali Rohár */ 336a3d66a76SPali Rohár if (!of_machine_is_compatible("marvell,armada-370-xp")) 337a3d66a76SPali Rohár return; 338a3d66a76SPali Rohár 339a3d66a76SPali Rohár cpuid = cpu_logical_map(smp_processor_id()); 340f02147ddSMarc Zyngier 341f02147ddSMarc Zyngier /* Enable Performance Counter Overflow interrupts */ 342f02147ddSMarc Zyngier writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), 343f02147ddSMarc Zyngier per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); 344f02147ddSMarc Zyngier } 345f02147ddSMarc Zyngier 3469339d432SThomas Petazzoni #ifdef CONFIG_SMP 347f02147ddSMarc Zyngier static struct irq_domain *ipi_domain; 348f02147ddSMarc Zyngier 349f02147ddSMarc Zyngier static void armada_370_xp_ipi_mask(struct irq_data *d) 350f02147ddSMarc Zyngier { 351f02147ddSMarc Zyngier u32 reg; 352f02147ddSMarc Zyngier reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 353f02147ddSMarc Zyngier reg &= ~BIT(d->hwirq); 354f02147ddSMarc Zyngier writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 355f02147ddSMarc Zyngier } 356f02147ddSMarc Zyngier 357f02147ddSMarc Zyngier static void armada_370_xp_ipi_unmask(struct irq_data *d) 358f02147ddSMarc Zyngier { 359f02147ddSMarc Zyngier u32 reg; 360f02147ddSMarc Zyngier reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 361f02147ddSMarc Zyngier reg |= BIT(d->hwirq); 362f02147ddSMarc Zyngier writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 363f02147ddSMarc Zyngier } 364f02147ddSMarc Zyngier 365f02147ddSMarc Zyngier static void armada_370_xp_ipi_send_mask(struct irq_data *d, 366f02147ddSMarc Zyngier const struct cpumask *mask) 367f02147ddSMarc Zyngier { 368f02147ddSMarc Zyngier unsigned long map = 0; 369f02147ddSMarc Zyngier int cpu; 370f02147ddSMarc Zyngier 371f02147ddSMarc Zyngier /* Convert our logical CPU mask into a physical one. */ 372f02147ddSMarc Zyngier for_each_cpu(cpu, mask) 373f02147ddSMarc Zyngier map |= 1 << cpu_logical_map(cpu); 374f02147ddSMarc Zyngier 375f02147ddSMarc Zyngier /* 376f02147ddSMarc Zyngier * Ensure that stores to Normal memory are visible to the 377f02147ddSMarc Zyngier * other CPUs before issuing the IPI. 378f02147ddSMarc Zyngier */ 379f02147ddSMarc Zyngier dsb(); 380f02147ddSMarc Zyngier 381f02147ddSMarc Zyngier /* submit softirq */ 382f02147ddSMarc Zyngier writel((map << 8) | d->hwirq, main_int_base + 383f02147ddSMarc Zyngier ARMADA_370_XP_SW_TRIG_INT_OFFS); 384f02147ddSMarc Zyngier } 385f02147ddSMarc Zyngier 3862a7313dcSMarc Zyngier static void armada_370_xp_ipi_ack(struct irq_data *d) 387f02147ddSMarc Zyngier { 388f02147ddSMarc Zyngier writel(~BIT(d->hwirq), per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 389f02147ddSMarc Zyngier } 390f02147ddSMarc Zyngier 391f02147ddSMarc Zyngier static struct irq_chip ipi_irqchip = { 392f02147ddSMarc Zyngier .name = "IPI", 3932a7313dcSMarc Zyngier .irq_ack = armada_370_xp_ipi_ack, 394f02147ddSMarc Zyngier .irq_mask = armada_370_xp_ipi_mask, 395f02147ddSMarc Zyngier .irq_unmask = armada_370_xp_ipi_unmask, 396f02147ddSMarc Zyngier .ipi_send_mask = armada_370_xp_ipi_send_mask, 397f02147ddSMarc Zyngier }; 398f02147ddSMarc Zyngier 399f02147ddSMarc Zyngier static int armada_370_xp_ipi_alloc(struct irq_domain *d, 400f02147ddSMarc Zyngier unsigned int virq, 401f02147ddSMarc Zyngier unsigned int nr_irqs, void *args) 402f02147ddSMarc Zyngier { 403f02147ddSMarc Zyngier int i; 404f02147ddSMarc Zyngier 405f02147ddSMarc Zyngier for (i = 0; i < nr_irqs; i++) { 406f02147ddSMarc Zyngier irq_set_percpu_devid(virq + i); 407f02147ddSMarc Zyngier irq_domain_set_info(d, virq + i, i, &ipi_irqchip, 408f02147ddSMarc Zyngier d->host_data, 409e52e73b7SValentin Schneider handle_percpu_devid_irq, 410f02147ddSMarc Zyngier NULL, NULL); 411f02147ddSMarc Zyngier } 412f02147ddSMarc Zyngier 413f02147ddSMarc Zyngier return 0; 414f02147ddSMarc Zyngier } 415f02147ddSMarc Zyngier 416f02147ddSMarc Zyngier static void armada_370_xp_ipi_free(struct irq_domain *d, 417f02147ddSMarc Zyngier unsigned int virq, 418f02147ddSMarc Zyngier unsigned int nr_irqs) 419f02147ddSMarc Zyngier { 420f02147ddSMarc Zyngier /* Not freeing IPIs */ 421f02147ddSMarc Zyngier } 422f02147ddSMarc Zyngier 423f02147ddSMarc Zyngier static const struct irq_domain_ops ipi_domain_ops = { 424f02147ddSMarc Zyngier .alloc = armada_370_xp_ipi_alloc, 425f02147ddSMarc Zyngier .free = armada_370_xp_ipi_free, 426f02147ddSMarc Zyngier }; 427f02147ddSMarc Zyngier 428f02147ddSMarc Zyngier static void ipi_resume(void) 429f02147ddSMarc Zyngier { 430f02147ddSMarc Zyngier int i; 431f02147ddSMarc Zyngier 432f02147ddSMarc Zyngier for (i = 0; i < IPI_DOORBELL_END; i++) { 433f02147ddSMarc Zyngier int irq; 434f02147ddSMarc Zyngier 435f02147ddSMarc Zyngier irq = irq_find_mapping(ipi_domain, i); 436f02147ddSMarc Zyngier if (irq <= 0) 437f02147ddSMarc Zyngier continue; 438f02147ddSMarc Zyngier if (irq_percpu_is_enabled(irq)) { 439f02147ddSMarc Zyngier struct irq_data *d; 440f02147ddSMarc Zyngier d = irq_domain_get_irq_data(ipi_domain, irq); 441f02147ddSMarc Zyngier armada_370_xp_ipi_unmask(d); 442f02147ddSMarc Zyngier } 443f02147ddSMarc Zyngier } 444f02147ddSMarc Zyngier } 445f02147ddSMarc Zyngier 446f02147ddSMarc Zyngier static __init void armada_xp_ipi_init(struct device_node *node) 447f02147ddSMarc Zyngier { 448f02147ddSMarc Zyngier int base_ipi; 449f02147ddSMarc Zyngier 450f02147ddSMarc Zyngier ipi_domain = irq_domain_create_linear(of_node_to_fwnode(node), 451f02147ddSMarc Zyngier IPI_DOORBELL_END, 452f02147ddSMarc Zyngier &ipi_domain_ops, NULL); 453f02147ddSMarc Zyngier if (WARN_ON(!ipi_domain)) 454f02147ddSMarc Zyngier return; 455f02147ddSMarc Zyngier 456f02147ddSMarc Zyngier irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI); 457*0e2213feSJohan Hovold base_ipi = irq_domain_alloc_irqs(ipi_domain, IPI_DOORBELL_END, NUMA_NO_NODE, NULL); 458f02147ddSMarc Zyngier if (WARN_ON(!base_ipi)) 459f02147ddSMarc Zyngier return; 460f02147ddSMarc Zyngier 461f02147ddSMarc Zyngier set_smp_ipi_range(base_ipi, IPI_DOORBELL_END); 462f02147ddSMarc Zyngier } 463f02147ddSMarc Zyngier 46419e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 46519e61d41SArnaud Ebalard 4669339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 4679339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 4689339d432SThomas Petazzoni { 4699339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 4708cc3cfc5SThomas Gleixner unsigned long reg, mask; 4719339d432SThomas Petazzoni int cpu; 4729339d432SThomas Petazzoni 4738cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 4748cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 4758cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 4769339d432SThomas Petazzoni 4779339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 4789339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 4798cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 4809339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 4819339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 4829339d432SThomas Petazzoni 483e31793a3SMarc Zyngier irq_data_update_effective_affinity(d, cpumask_of(cpu)); 484e31793a3SMarc Zyngier 4851dacf194SThomas Petazzoni return IRQ_SET_MASK_OK; 4869339d432SThomas Petazzoni } 487f02147ddSMarc Zyngier 488f02147ddSMarc Zyngier static void armada_xp_mpic_smp_cpu_init(void) 489f02147ddSMarc Zyngier { 490f02147ddSMarc Zyngier u32 control; 491f02147ddSMarc Zyngier int nr_irqs, i; 492f02147ddSMarc Zyngier 493f02147ddSMarc Zyngier control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 494f02147ddSMarc Zyngier nr_irqs = (control >> 2) & 0x3ff; 495f02147ddSMarc Zyngier 496f02147ddSMarc Zyngier for (i = 0; i < nr_irqs; i++) 497f02147ddSMarc Zyngier writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 498f02147ddSMarc Zyngier 499f02147ddSMarc Zyngier /* Disable all IPIs */ 500f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 501f02147ddSMarc Zyngier 502f02147ddSMarc Zyngier /* Clear pending IPIs */ 503f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 504f02147ddSMarc Zyngier 505f02147ddSMarc Zyngier /* Unmask IPI interrupt */ 506f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 507f02147ddSMarc Zyngier } 508f02147ddSMarc Zyngier 509f02147ddSMarc Zyngier static void armada_xp_mpic_reenable_percpu(void) 510f02147ddSMarc Zyngier { 511f02147ddSMarc Zyngier unsigned int irq; 512f02147ddSMarc Zyngier 513f02147ddSMarc Zyngier /* Re-enable per-CPU interrupts that were enabled before suspend */ 514f02147ddSMarc Zyngier for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) { 515f02147ddSMarc Zyngier struct irq_data *data; 516f02147ddSMarc Zyngier int virq; 517f02147ddSMarc Zyngier 518f02147ddSMarc Zyngier virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 519f02147ddSMarc Zyngier if (virq == 0) 520f02147ddSMarc Zyngier continue; 521f02147ddSMarc Zyngier 522f02147ddSMarc Zyngier data = irq_get_irq_data(virq); 523f02147ddSMarc Zyngier 524f02147ddSMarc Zyngier if (!irq_percpu_is_enabled(virq)) 525f02147ddSMarc Zyngier continue; 526f02147ddSMarc Zyngier 527f02147ddSMarc Zyngier armada_370_xp_irq_unmask(data); 528f02147ddSMarc Zyngier } 529f02147ddSMarc Zyngier 530f02147ddSMarc Zyngier ipi_resume(); 5318ca61cdeSNathan Rossi 5328ca61cdeSNathan Rossi armada_370_xp_msi_reenable_percpu(); 533f02147ddSMarc Zyngier } 534f02147ddSMarc Zyngier 535f02147ddSMarc Zyngier static int armada_xp_mpic_starting_cpu(unsigned int cpu) 536f02147ddSMarc Zyngier { 537f02147ddSMarc Zyngier armada_xp_mpic_perf_init(); 538f02147ddSMarc Zyngier armada_xp_mpic_smp_cpu_init(); 539f02147ddSMarc Zyngier armada_xp_mpic_reenable_percpu(); 540f02147ddSMarc Zyngier return 0; 541f02147ddSMarc Zyngier } 542f02147ddSMarc Zyngier 543f02147ddSMarc Zyngier static int mpic_cascaded_starting_cpu(unsigned int cpu) 544f02147ddSMarc Zyngier { 545f02147ddSMarc Zyngier armada_xp_mpic_perf_init(); 546f02147ddSMarc Zyngier armada_xp_mpic_reenable_percpu(); 547f02147ddSMarc Zyngier enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); 548f02147ddSMarc Zyngier return 0; 549f02147ddSMarc Zyngier } 550f02147ddSMarc Zyngier #else 551f02147ddSMarc Zyngier static void armada_xp_mpic_smp_cpu_init(void) {} 552f02147ddSMarc Zyngier static void ipi_resume(void) {} 5539339d432SThomas Petazzoni #endif 5549339d432SThomas Petazzoni 5559339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 556f692a172SThomas Petazzoni .name = "MPIC", 5579339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 5589339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 5599339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 5609339d432SThomas Petazzoni #ifdef CONFIG_SMP 5619339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 5629339d432SThomas Petazzoni #endif 5630d8e1d80SGregory CLEMENT .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 5649339d432SThomas Petazzoni }; 5659339d432SThomas Petazzoni 5669339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 5679339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 5689339d432SThomas Petazzoni { 5699339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 5702c299de5SEzequiel Garcia if (!is_percpu_irq(hw)) 5711bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 5721bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5731bf25e78SLinus Torvalds else 5749339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 5759339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 5769339d432SThomas Petazzoni 5772c299de5SEzequiel Garcia if (is_percpu_irq(hw)) { 5789339d432SThomas Petazzoni irq_set_percpu_devid(virq); 5799339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 5809339d432SThomas Petazzoni handle_percpu_devid_irq); 5819339d432SThomas Petazzoni } else { 5829339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 5839339d432SThomas Petazzoni handle_level_irq); 584e31793a3SMarc Zyngier irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq))); 5859339d432SThomas Petazzoni } 586d17cab44SRob Herring irq_set_probe(virq); 5879339d432SThomas Petazzoni 5889339d432SThomas Petazzoni return 0; 5899339d432SThomas Petazzoni } 5909339d432SThomas Petazzoni 59196009736SKrzysztof Kozlowski static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 5929339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 5939339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 5949339d432SThomas Petazzoni }; 5959339d432SThomas Petazzoni 59631f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 597bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 5989b8cf779SEzequiel Garcia { 59931f614edSThomas Petazzoni u32 msimask, msinr; 60031f614edSThomas Petazzoni 60131f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 60231f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 60331f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 60431f614edSThomas Petazzoni 605c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 60631f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 60731f614edSThomas Petazzoni 60831f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 60931f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 610046a6ee2SMarc Zyngier unsigned int irq; 61131f614edSThomas Petazzoni 61231f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 61331f614edSThomas Petazzoni continue; 61431f614edSThomas Petazzoni 6150636bab6SThomas Petazzoni irq = msinr - PCI_MSI_DOORBELL_START; 616046a6ee2SMarc Zyngier 6170953fb26SMark Rutland generic_handle_domain_irq(armada_370_xp_msi_inner_domain, irq); 618e89c6a06SMarc Zyngier } 61931f614edSThomas Petazzoni } 6209b8cf779SEzequiel Garcia #else 621bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 62231f614edSThomas Petazzoni #endif 62331f614edSThomas Petazzoni 624bd0b9ac4SThomas Gleixner static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) 625bc69b8adSEzequiel Garcia { 6265b29264cSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 627758e8366SGrzegorz Jaszczyk unsigned long irqmap, irqn, irqsrc, cpuid; 628bc69b8adSEzequiel Garcia 629bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 630bc69b8adSEzequiel Garcia 631bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 632758e8366SGrzegorz Jaszczyk cpuid = cpu_logical_map(smp_processor_id()); 633bc69b8adSEzequiel Garcia 634bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 635758e8366SGrzegorz Jaszczyk irqsrc = readl_relaxed(main_int_base + 636758e8366SGrzegorz Jaszczyk ARMADA_370_XP_INT_SOURCE_CTL(irqn)); 637758e8366SGrzegorz Jaszczyk 638758e8366SGrzegorz Jaszczyk /* Check if the interrupt is not masked on current CPU. 639758e8366SGrzegorz Jaszczyk * Test IRQ (0-1) and FIQ (8-9) mask bits. 640758e8366SGrzegorz Jaszczyk */ 641758e8366SGrzegorz Jaszczyk if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) 642758e8366SGrzegorz Jaszczyk continue; 643758e8366SGrzegorz Jaszczyk 644758e8366SGrzegorz Jaszczyk if (irqn == 1) { 645758e8366SGrzegorz Jaszczyk armada_370_xp_handle_msi_irq(NULL, true); 646758e8366SGrzegorz Jaszczyk continue; 647758e8366SGrzegorz Jaszczyk } 648758e8366SGrzegorz Jaszczyk 649046a6ee2SMarc Zyngier generic_handle_domain_irq(armada_370_xp_mpic_domain, irqn); 650bc69b8adSEzequiel Garcia } 651bc69b8adSEzequiel Garcia 652bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 653bc69b8adSEzequiel Garcia } 654bc69b8adSEzequiel Garcia 6558783dd3aSStephen Boyd static void __exception_irq_entry 6569b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 6579b8cf779SEzequiel Garcia { 6589b8cf779SEzequiel Garcia u32 irqstat, irqnr; 6599b8cf779SEzequiel Garcia 6609b8cf779SEzequiel Garcia do { 6619b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 6629b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 6639b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 6649b8cf779SEzequiel Garcia 6659b8cf779SEzequiel Garcia if (irqnr > 1022) 6669b8cf779SEzequiel Garcia break; 6679b8cf779SEzequiel Garcia 6689b8cf779SEzequiel Garcia if (irqnr > 1) { 6690953fb26SMark Rutland generic_handle_domain_irq(armada_370_xp_mpic_domain, 6700953fb26SMark Rutland irqnr); 6719b8cf779SEzequiel Garcia continue; 6729b8cf779SEzequiel Garcia } 6739b8cf779SEzequiel Garcia 6749b8cf779SEzequiel Garcia /* MSI handling */ 6759b8cf779SEzequiel Garcia if (irqnr == 1) 676bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 6779b8cf779SEzequiel Garcia 6789339d432SThomas Petazzoni #ifdef CONFIG_SMP 6799339d432SThomas Petazzoni /* IPI Handling */ 6809339d432SThomas Petazzoni if (irqnr == 0) { 681f02147ddSMarc Zyngier unsigned long ipimask; 682f02147ddSMarc Zyngier int ipi; 6839339d432SThomas Petazzoni 6849339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 6859339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 6865ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 6879339d432SThomas Petazzoni 688f02147ddSMarc Zyngier for_each_set_bit(ipi, &ipimask, IPI_DOORBELL_END) 6890953fb26SMark Rutland generic_handle_domain_irq(ipi_domain, ipi); 6909339d432SThomas Petazzoni } 6919339d432SThomas Petazzoni #endif 6929339d432SThomas Petazzoni 6939339d432SThomas Petazzoni } while (1); 6949339d432SThomas Petazzoni } 6959339d432SThomas Petazzoni 6960f077eb5SThomas Petazzoni static int armada_370_xp_mpic_suspend(void) 6970f077eb5SThomas Petazzoni { 6980f077eb5SThomas Petazzoni doorbell_mask_reg = readl(per_cpu_int_base + 6990f077eb5SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 7000f077eb5SThomas Petazzoni return 0; 7010f077eb5SThomas Petazzoni } 7020f077eb5SThomas Petazzoni 7030f077eb5SThomas Petazzoni static void armada_370_xp_mpic_resume(void) 7040f077eb5SThomas Petazzoni { 7050f077eb5SThomas Petazzoni int nirqs; 7060f077eb5SThomas Petazzoni irq_hw_number_t irq; 7070f077eb5SThomas Petazzoni 7080f077eb5SThomas Petazzoni /* Re-enable interrupts */ 7090f077eb5SThomas Petazzoni nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; 7100f077eb5SThomas Petazzoni for (irq = 0; irq < nirqs; irq++) { 7110f077eb5SThomas Petazzoni struct irq_data *data; 7120f077eb5SThomas Petazzoni int virq; 7130f077eb5SThomas Petazzoni 7140f077eb5SThomas Petazzoni virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 7150f077eb5SThomas Petazzoni if (virq == 0) 7160f077eb5SThomas Petazzoni continue; 7170f077eb5SThomas Petazzoni 7180fa4ce74SThomas Petazzoni data = irq_get_irq_data(virq); 7190fa4ce74SThomas Petazzoni 7200fa4ce74SThomas Petazzoni if (!is_percpu_irq(irq)) { 7210fa4ce74SThomas Petazzoni /* Non per-CPU interrupts */ 7220f077eb5SThomas Petazzoni writel(irq, per_cpu_int_base + 7230f077eb5SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 7240fa4ce74SThomas Petazzoni if (!irqd_irq_disabled(data)) 7250fa4ce74SThomas Petazzoni armada_370_xp_irq_unmask(data); 7260fa4ce74SThomas Petazzoni } else { 7270fa4ce74SThomas Petazzoni /* Per-CPU interrupts */ 7280f077eb5SThomas Petazzoni writel(irq, main_int_base + 7290f077eb5SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 7300f077eb5SThomas Petazzoni 7310fa4ce74SThomas Petazzoni /* 7320fa4ce74SThomas Petazzoni * Re-enable on the current CPU, 7330fa4ce74SThomas Petazzoni * armada_xp_mpic_reenable_percpu() will take 7340fa4ce74SThomas Petazzoni * care of secondary CPUs when they come up. 7350fa4ce74SThomas Petazzoni */ 7360fa4ce74SThomas Petazzoni if (irq_percpu_is_enabled(virq)) 7370f077eb5SThomas Petazzoni armada_370_xp_irq_unmask(data); 7380f077eb5SThomas Petazzoni } 7390fa4ce74SThomas Petazzoni } 7400f077eb5SThomas Petazzoni 7410f077eb5SThomas Petazzoni /* Reconfigure doorbells for IPIs and MSIs */ 7420f077eb5SThomas Petazzoni writel(doorbell_mask_reg, 7430f077eb5SThomas Petazzoni per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 7440f077eb5SThomas Petazzoni if (doorbell_mask_reg & IPI_DOORBELL_MASK) 7450f077eb5SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 7460f077eb5SThomas Petazzoni if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) 7470f077eb5SThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 748f02147ddSMarc Zyngier 749f02147ddSMarc Zyngier ipi_resume(); 7500f077eb5SThomas Petazzoni } 7510f077eb5SThomas Petazzoni 7526c880902SBen Dooks static struct syscore_ops armada_370_xp_mpic_syscore_ops = { 7530f077eb5SThomas Petazzoni .suspend = armada_370_xp_mpic_suspend, 7540f077eb5SThomas Petazzoni .resume = armada_370_xp_mpic_resume, 7550f077eb5SThomas Petazzoni }; 7560f077eb5SThomas Petazzoni 757b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 758b313ada8SThomas Petazzoni struct device_node *parent) 759b313ada8SThomas Petazzoni { 760627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 7615724be84SMaxime Ripard int nr_irqs, i; 762b313ada8SThomas Petazzoni u32 control; 763b313ada8SThomas Petazzoni 764627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 765627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 766b313ada8SThomas Petazzoni 767627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 768627dfcc2SThomas Petazzoni resource_size(&main_int_res), 769627dfcc2SThomas Petazzoni node->full_name)); 770627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 771627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 772627dfcc2SThomas Petazzoni node->full_name)); 773627dfcc2SThomas Petazzoni 774627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 775627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 776b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 777627dfcc2SThomas Petazzoni 778627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 779627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 780b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 781b313ada8SThomas Petazzoni 782b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 783b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 784b73842b7SThomas Petazzoni 785b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 786b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 787b313ada8SThomas Petazzoni 788b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 789b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 790b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 791627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 79296f0d93aSMarc Zyngier irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); 793b313ada8SThomas Petazzoni 794933a24b0SEzequiel Garcia /* Setup for the boot CPU */ 79528da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 796b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 797b313ada8SThomas Petazzoni 79831f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 79931f614edSThomas Petazzoni 800bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 801bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 802bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 803b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 804ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 805f02147ddSMarc Zyngier armada_xp_ipi_init(node); 806cb5ff2d2SRichard Cochran cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 80773c1b41eSThomas Gleixner "irqchip/armada/ipi:starting", 808cb5ff2d2SRichard Cochran armada_xp_mpic_starting_cpu, NULL); 809ef37d337SThomas Petazzoni #endif 810bc69b8adSEzequiel Garcia } else { 8115724be84SMaxime Ripard #ifdef CONFIG_SMP 812008b69e4SThomas Gleixner cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 81373c1b41eSThomas Gleixner "irqchip/armada/cascade:starting", 814cb5ff2d2SRichard Cochran mpic_cascaded_starting_cpu, NULL); 8155724be84SMaxime Ripard #endif 816bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 817bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 818bc69b8adSEzequiel Garcia } 819b313ada8SThomas Petazzoni 8200f077eb5SThomas Petazzoni register_syscore_ops(&armada_370_xp_mpic_syscore_ops); 8210f077eb5SThomas Petazzoni 822b313ada8SThomas Petazzoni return 0; 823b313ada8SThomas Petazzoni } 824b313ada8SThomas Petazzoni 8259339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 826