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 { 212fcc392d5SThomas Petazzoni msg->address_lo = lower_32_bits(msi_doorbell_addr); 213fcc392d5SThomas Petazzoni msg->address_hi = upper_32_bits(msi_doorbell_addr); 214fcc392d5SThomas Petazzoni msg->data = 0xf00 | (data->hwirq + PCI_MSI_DOORBELL_START); 215fcc392d5SThomas Petazzoni } 216fcc392d5SThomas Petazzoni 217fcc392d5SThomas Petazzoni static int armada_370_xp_msi_set_affinity(struct irq_data *irq_data, 218fcc392d5SThomas Petazzoni const struct cpumask *mask, bool force) 219fcc392d5SThomas Petazzoni { 220fcc392d5SThomas Petazzoni return -EINVAL; 221fcc392d5SThomas Petazzoni } 222fcc392d5SThomas Petazzoni 223fcc392d5SThomas Petazzoni static struct irq_chip armada_370_xp_msi_bottom_irq_chip = { 224f692a172SThomas Petazzoni .name = "MPIC MSI", 225fcc392d5SThomas Petazzoni .irq_compose_msi_msg = armada_370_xp_compose_msi_msg, 226fcc392d5SThomas Petazzoni .irq_set_affinity = armada_370_xp_msi_set_affinity, 227fcc392d5SThomas Petazzoni }; 228fcc392d5SThomas Petazzoni 229fcc392d5SThomas Petazzoni static int armada_370_xp_msi_alloc(struct irq_domain *domain, unsigned int virq, 230fcc392d5SThomas Petazzoni unsigned int nr_irqs, void *args) 23131f614edSThomas Petazzoni { 232a71b9412SThomas Petazzoni int hwirq, i; 23331f614edSThomas Petazzoni 23431f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 235a71b9412SThomas Petazzoni 236a71b9412SThomas Petazzoni hwirq = bitmap_find_next_zero_area(msi_used, PCI_MSI_DOORBELL_NR, 237a71b9412SThomas Petazzoni 0, nr_irqs, 0); 238fcc392d5SThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) { 239fcc392d5SThomas Petazzoni mutex_unlock(&msi_used_lock); 240fcc392d5SThomas Petazzoni return -ENOSPC; 241fcc392d5SThomas Petazzoni } 242fcc392d5SThomas Petazzoni 243a71b9412SThomas Petazzoni bitmap_set(msi_used, hwirq, nr_irqs); 24431f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 24531f614edSThomas Petazzoni 246a71b9412SThomas Petazzoni for (i = 0; i < nr_irqs; i++) { 247a71b9412SThomas Petazzoni irq_domain_set_info(domain, virq + i, hwirq + i, 248a71b9412SThomas Petazzoni &armada_370_xp_msi_bottom_irq_chip, 249fcc392d5SThomas Petazzoni domain->host_data, handle_simple_irq, 250fcc392d5SThomas Petazzoni NULL, NULL); 251a71b9412SThomas Petazzoni } 252fcc392d5SThomas Petazzoni 25331f614edSThomas Petazzoni return hwirq; 25431f614edSThomas Petazzoni } 25531f614edSThomas Petazzoni 256fcc392d5SThomas Petazzoni static void armada_370_xp_msi_free(struct irq_domain *domain, 257fcc392d5SThomas Petazzoni unsigned int virq, unsigned int nr_irqs) 25831f614edSThomas Petazzoni { 259fcc392d5SThomas Petazzoni struct irq_data *d = irq_domain_get_irq_data(domain, virq); 260fcc392d5SThomas Petazzoni 26131f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 262a71b9412SThomas Petazzoni bitmap_clear(msi_used, d->hwirq, nr_irqs); 26331f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 26431f614edSThomas Petazzoni } 26531f614edSThomas Petazzoni 266fcc392d5SThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_domain_ops = { 267fcc392d5SThomas Petazzoni .alloc = armada_370_xp_msi_alloc, 268fcc392d5SThomas Petazzoni .free = armada_370_xp_msi_free, 26931f614edSThomas Petazzoni }; 27031f614edSThomas Petazzoni 27131f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 27231f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 27331f614edSThomas Petazzoni { 27431f614edSThomas Petazzoni u32 reg; 27531f614edSThomas Petazzoni 27631f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 27731f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 27831f614edSThomas Petazzoni 279fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain = 280fcc392d5SThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 281fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_ops, NULL); 282fcc392d5SThomas Petazzoni if (!armada_370_xp_msi_inner_domain) 28331f614edSThomas Petazzoni return -ENOMEM; 28431f614edSThomas Petazzoni 28531f614edSThomas Petazzoni armada_370_xp_msi_domain = 286fcc392d5SThomas Petazzoni pci_msi_create_irq_domain(of_node_to_fwnode(node), 287fcc392d5SThomas Petazzoni &armada_370_xp_msi_domain_info, 288fcc392d5SThomas Petazzoni armada_370_xp_msi_inner_domain); 28931f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 290fcc392d5SThomas Petazzoni irq_domain_remove(armada_370_xp_msi_inner_domain); 29131f614edSThomas Petazzoni return -ENOMEM; 29231f614edSThomas Petazzoni } 29331f614edSThomas Petazzoni 29431f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 29531f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 29631f614edSThomas Petazzoni 29731f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 29831f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 29931f614edSThomas Petazzoni 30031f614edSThomas Petazzoni /* Unmask IPI interrupt */ 30131f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 30231f614edSThomas Petazzoni 30331f614edSThomas Petazzoni return 0; 30431f614edSThomas Petazzoni } 30531f614edSThomas Petazzoni #else 30631f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 30731f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 30831f614edSThomas Petazzoni { 30931f614edSThomas Petazzoni return 0; 31031f614edSThomas Petazzoni } 31131f614edSThomas Petazzoni #endif 31231f614edSThomas Petazzoni 313f02147ddSMarc Zyngier static void armada_xp_mpic_perf_init(void) 314f02147ddSMarc Zyngier { 315f02147ddSMarc Zyngier unsigned long cpuid = cpu_logical_map(smp_processor_id()); 316f02147ddSMarc Zyngier 317f02147ddSMarc Zyngier /* Enable Performance Counter Overflow interrupts */ 318f02147ddSMarc Zyngier writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), 319f02147ddSMarc Zyngier per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); 320f02147ddSMarc Zyngier } 321f02147ddSMarc Zyngier 3229339d432SThomas Petazzoni #ifdef CONFIG_SMP 323f02147ddSMarc Zyngier static struct irq_domain *ipi_domain; 324f02147ddSMarc Zyngier 325f02147ddSMarc Zyngier static void armada_370_xp_ipi_mask(struct irq_data *d) 326f02147ddSMarc Zyngier { 327f02147ddSMarc Zyngier u32 reg; 328f02147ddSMarc Zyngier reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 329f02147ddSMarc Zyngier reg &= ~BIT(d->hwirq); 330f02147ddSMarc Zyngier writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 331f02147ddSMarc Zyngier } 332f02147ddSMarc Zyngier 333f02147ddSMarc Zyngier static void armada_370_xp_ipi_unmask(struct irq_data *d) 334f02147ddSMarc Zyngier { 335f02147ddSMarc Zyngier u32 reg; 336f02147ddSMarc Zyngier reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 337f02147ddSMarc Zyngier reg |= BIT(d->hwirq); 338f02147ddSMarc Zyngier writel(reg, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 339f02147ddSMarc Zyngier } 340f02147ddSMarc Zyngier 341f02147ddSMarc Zyngier static void armada_370_xp_ipi_send_mask(struct irq_data *d, 342f02147ddSMarc Zyngier const struct cpumask *mask) 343f02147ddSMarc Zyngier { 344f02147ddSMarc Zyngier unsigned long map = 0; 345f02147ddSMarc Zyngier int cpu; 346f02147ddSMarc Zyngier 347f02147ddSMarc Zyngier /* Convert our logical CPU mask into a physical one. */ 348f02147ddSMarc Zyngier for_each_cpu(cpu, mask) 349f02147ddSMarc Zyngier map |= 1 << cpu_logical_map(cpu); 350f02147ddSMarc Zyngier 351f02147ddSMarc Zyngier /* 352f02147ddSMarc Zyngier * Ensure that stores to Normal memory are visible to the 353f02147ddSMarc Zyngier * other CPUs before issuing the IPI. 354f02147ddSMarc Zyngier */ 355f02147ddSMarc Zyngier dsb(); 356f02147ddSMarc Zyngier 357f02147ddSMarc Zyngier /* submit softirq */ 358f02147ddSMarc Zyngier writel((map << 8) | d->hwirq, main_int_base + 359f02147ddSMarc Zyngier ARMADA_370_XP_SW_TRIG_INT_OFFS); 360f02147ddSMarc Zyngier } 361f02147ddSMarc Zyngier 362f02147ddSMarc Zyngier static void armada_370_xp_ipi_eoi(struct irq_data *d) 363f02147ddSMarc Zyngier { 364f02147ddSMarc Zyngier writel(~BIT(d->hwirq), per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 365f02147ddSMarc Zyngier } 366f02147ddSMarc Zyngier 367f02147ddSMarc Zyngier static struct irq_chip ipi_irqchip = { 368f02147ddSMarc Zyngier .name = "IPI", 369f02147ddSMarc Zyngier .irq_mask = armada_370_xp_ipi_mask, 370f02147ddSMarc Zyngier .irq_unmask = armada_370_xp_ipi_unmask, 371f02147ddSMarc Zyngier .irq_eoi = armada_370_xp_ipi_eoi, 372f02147ddSMarc Zyngier .ipi_send_mask = armada_370_xp_ipi_send_mask, 373f02147ddSMarc Zyngier }; 374f02147ddSMarc Zyngier 375f02147ddSMarc Zyngier static int armada_370_xp_ipi_alloc(struct irq_domain *d, 376f02147ddSMarc Zyngier unsigned int virq, 377f02147ddSMarc Zyngier unsigned int nr_irqs, void *args) 378f02147ddSMarc Zyngier { 379f02147ddSMarc Zyngier int i; 380f02147ddSMarc Zyngier 381f02147ddSMarc Zyngier for (i = 0; i < nr_irqs; i++) { 382f02147ddSMarc Zyngier irq_set_percpu_devid(virq + i); 383f02147ddSMarc Zyngier irq_domain_set_info(d, virq + i, i, &ipi_irqchip, 384f02147ddSMarc Zyngier d->host_data, 385f02147ddSMarc Zyngier handle_percpu_devid_fasteoi_ipi, 386f02147ddSMarc Zyngier NULL, NULL); 387f02147ddSMarc Zyngier } 388f02147ddSMarc Zyngier 389f02147ddSMarc Zyngier return 0; 390f02147ddSMarc Zyngier } 391f02147ddSMarc Zyngier 392f02147ddSMarc Zyngier static void armada_370_xp_ipi_free(struct irq_domain *d, 393f02147ddSMarc Zyngier unsigned int virq, 394f02147ddSMarc Zyngier unsigned int nr_irqs) 395f02147ddSMarc Zyngier { 396f02147ddSMarc Zyngier /* Not freeing IPIs */ 397f02147ddSMarc Zyngier } 398f02147ddSMarc Zyngier 399f02147ddSMarc Zyngier static const struct irq_domain_ops ipi_domain_ops = { 400f02147ddSMarc Zyngier .alloc = armada_370_xp_ipi_alloc, 401f02147ddSMarc Zyngier .free = armada_370_xp_ipi_free, 402f02147ddSMarc Zyngier }; 403f02147ddSMarc Zyngier 404f02147ddSMarc Zyngier static void ipi_resume(void) 405f02147ddSMarc Zyngier { 406f02147ddSMarc Zyngier int i; 407f02147ddSMarc Zyngier 408f02147ddSMarc Zyngier for (i = 0; i < IPI_DOORBELL_END; i++) { 409f02147ddSMarc Zyngier int irq; 410f02147ddSMarc Zyngier 411f02147ddSMarc Zyngier irq = irq_find_mapping(ipi_domain, i); 412f02147ddSMarc Zyngier if (irq <= 0) 413f02147ddSMarc Zyngier continue; 414f02147ddSMarc Zyngier if (irq_percpu_is_enabled(irq)) { 415f02147ddSMarc Zyngier struct irq_data *d; 416f02147ddSMarc Zyngier d = irq_domain_get_irq_data(ipi_domain, irq); 417f02147ddSMarc Zyngier armada_370_xp_ipi_unmask(d); 418f02147ddSMarc Zyngier } 419f02147ddSMarc Zyngier } 420f02147ddSMarc Zyngier } 421f02147ddSMarc Zyngier 422f02147ddSMarc Zyngier static __init void armada_xp_ipi_init(struct device_node *node) 423f02147ddSMarc Zyngier { 424f02147ddSMarc Zyngier int base_ipi; 425f02147ddSMarc Zyngier 426f02147ddSMarc Zyngier ipi_domain = irq_domain_create_linear(of_node_to_fwnode(node), 427f02147ddSMarc Zyngier IPI_DOORBELL_END, 428f02147ddSMarc Zyngier &ipi_domain_ops, NULL); 429f02147ddSMarc Zyngier if (WARN_ON(!ipi_domain)) 430f02147ddSMarc Zyngier return; 431f02147ddSMarc Zyngier 432f02147ddSMarc Zyngier irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI); 433f02147ddSMarc Zyngier base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, IPI_DOORBELL_END, 434f02147ddSMarc Zyngier NUMA_NO_NODE, NULL, false, NULL); 435f02147ddSMarc Zyngier if (WARN_ON(!base_ipi)) 436f02147ddSMarc Zyngier return; 437f02147ddSMarc Zyngier 438f02147ddSMarc Zyngier set_smp_ipi_range(base_ipi, IPI_DOORBELL_END); 439f02147ddSMarc Zyngier } 440f02147ddSMarc Zyngier 44119e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 44219e61d41SArnaud Ebalard 4439339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 4449339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 4459339d432SThomas Petazzoni { 4469339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 4478cc3cfc5SThomas Gleixner unsigned long reg, mask; 4489339d432SThomas Petazzoni int cpu; 4499339d432SThomas Petazzoni 4508cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 4518cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 4528cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 4539339d432SThomas Petazzoni 4549339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 4559339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 4568cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 4579339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 4589339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 4599339d432SThomas Petazzoni 460e31793a3SMarc Zyngier irq_data_update_effective_affinity(d, cpumask_of(cpu)); 461e31793a3SMarc Zyngier 4621dacf194SThomas Petazzoni return IRQ_SET_MASK_OK; 4639339d432SThomas Petazzoni } 464f02147ddSMarc Zyngier 465f02147ddSMarc Zyngier static void armada_xp_mpic_smp_cpu_init(void) 466f02147ddSMarc Zyngier { 467f02147ddSMarc Zyngier u32 control; 468f02147ddSMarc Zyngier int nr_irqs, i; 469f02147ddSMarc Zyngier 470f02147ddSMarc Zyngier control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 471f02147ddSMarc Zyngier nr_irqs = (control >> 2) & 0x3ff; 472f02147ddSMarc Zyngier 473f02147ddSMarc Zyngier for (i = 0; i < nr_irqs; i++) 474f02147ddSMarc Zyngier writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 475f02147ddSMarc Zyngier 476f02147ddSMarc Zyngier /* Disable all IPIs */ 477f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 478f02147ddSMarc Zyngier 479f02147ddSMarc Zyngier /* Clear pending IPIs */ 480f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 481f02147ddSMarc Zyngier 482f02147ddSMarc Zyngier /* Unmask IPI interrupt */ 483f02147ddSMarc Zyngier writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 484f02147ddSMarc Zyngier } 485f02147ddSMarc Zyngier 486f02147ddSMarc Zyngier static void armada_xp_mpic_reenable_percpu(void) 487f02147ddSMarc Zyngier { 488f02147ddSMarc Zyngier unsigned int irq; 489f02147ddSMarc Zyngier 490f02147ddSMarc Zyngier /* Re-enable per-CPU interrupts that were enabled before suspend */ 491f02147ddSMarc Zyngier for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) { 492f02147ddSMarc Zyngier struct irq_data *data; 493f02147ddSMarc Zyngier int virq; 494f02147ddSMarc Zyngier 495f02147ddSMarc Zyngier virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 496f02147ddSMarc Zyngier if (virq == 0) 497f02147ddSMarc Zyngier continue; 498f02147ddSMarc Zyngier 499f02147ddSMarc Zyngier data = irq_get_irq_data(virq); 500f02147ddSMarc Zyngier 501f02147ddSMarc Zyngier if (!irq_percpu_is_enabled(virq)) 502f02147ddSMarc Zyngier continue; 503f02147ddSMarc Zyngier 504f02147ddSMarc Zyngier armada_370_xp_irq_unmask(data); 505f02147ddSMarc Zyngier } 506f02147ddSMarc Zyngier 507f02147ddSMarc Zyngier ipi_resume(); 508f02147ddSMarc Zyngier } 509f02147ddSMarc Zyngier 510f02147ddSMarc Zyngier static int armada_xp_mpic_starting_cpu(unsigned int cpu) 511f02147ddSMarc Zyngier { 512f02147ddSMarc Zyngier armada_xp_mpic_perf_init(); 513f02147ddSMarc Zyngier armada_xp_mpic_smp_cpu_init(); 514f02147ddSMarc Zyngier armada_xp_mpic_reenable_percpu(); 515f02147ddSMarc Zyngier return 0; 516f02147ddSMarc Zyngier } 517f02147ddSMarc Zyngier 518f02147ddSMarc Zyngier static int mpic_cascaded_starting_cpu(unsigned int cpu) 519f02147ddSMarc Zyngier { 520f02147ddSMarc Zyngier armada_xp_mpic_perf_init(); 521f02147ddSMarc Zyngier armada_xp_mpic_reenable_percpu(); 522f02147ddSMarc Zyngier enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); 523f02147ddSMarc Zyngier return 0; 524f02147ddSMarc Zyngier } 525f02147ddSMarc Zyngier #else 526f02147ddSMarc Zyngier static void armada_xp_mpic_smp_cpu_init(void) {} 527f02147ddSMarc Zyngier static void ipi_resume(void) {} 5289339d432SThomas Petazzoni #endif 5299339d432SThomas Petazzoni 5309339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 531f692a172SThomas Petazzoni .name = "MPIC", 5329339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 5339339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 5349339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 5359339d432SThomas Petazzoni #ifdef CONFIG_SMP 5369339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 5379339d432SThomas Petazzoni #endif 5380d8e1d80SGregory CLEMENT .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 5399339d432SThomas Petazzoni }; 5409339d432SThomas Petazzoni 5419339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 5429339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 5439339d432SThomas Petazzoni { 5449339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 5452c299de5SEzequiel Garcia if (!is_percpu_irq(hw)) 5461bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 5471bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5481bf25e78SLinus Torvalds else 5499339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 5509339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 5519339d432SThomas Petazzoni 5522c299de5SEzequiel Garcia if (is_percpu_irq(hw)) { 5539339d432SThomas Petazzoni irq_set_percpu_devid(virq); 5549339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 5559339d432SThomas Petazzoni handle_percpu_devid_irq); 5569339d432SThomas Petazzoni } else { 5579339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 5589339d432SThomas Petazzoni handle_level_irq); 559e31793a3SMarc Zyngier irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq))); 5609339d432SThomas Petazzoni } 561d17cab44SRob Herring irq_set_probe(virq); 5629339d432SThomas Petazzoni 5639339d432SThomas Petazzoni return 0; 5649339d432SThomas Petazzoni } 5659339d432SThomas Petazzoni 56696009736SKrzysztof Kozlowski static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 5679339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 5689339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 5699339d432SThomas Petazzoni }; 5709339d432SThomas Petazzoni 57131f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 572bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 5739b8cf779SEzequiel Garcia { 57431f614edSThomas Petazzoni u32 msimask, msinr; 57531f614edSThomas Petazzoni 57631f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 57731f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 57831f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 57931f614edSThomas Petazzoni 580c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 58131f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 58231f614edSThomas Petazzoni 58331f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 58431f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 58531f614edSThomas Petazzoni int irq; 58631f614edSThomas Petazzoni 58731f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 58831f614edSThomas Petazzoni continue; 58931f614edSThomas Petazzoni 590e89c6a06SMarc Zyngier if (is_chained) { 591fcc392d5SThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_inner_domain, 5920636bab6SThomas Petazzoni msinr - PCI_MSI_DOORBELL_START); 593bc69b8adSEzequiel Garcia generic_handle_irq(irq); 594e89c6a06SMarc Zyngier } else { 5950636bab6SThomas Petazzoni irq = msinr - PCI_MSI_DOORBELL_START; 596fcc392d5SThomas Petazzoni handle_domain_irq(armada_370_xp_msi_inner_domain, 597e89c6a06SMarc Zyngier irq, regs); 598e89c6a06SMarc Zyngier } 59931f614edSThomas Petazzoni } 60031f614edSThomas Petazzoni } 6019b8cf779SEzequiel Garcia #else 602bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 60331f614edSThomas Petazzoni #endif 60431f614edSThomas Petazzoni 605bd0b9ac4SThomas Gleixner static void armada_370_xp_mpic_handle_cascade_irq(struct irq_desc *desc) 606bc69b8adSEzequiel Garcia { 6075b29264cSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 608758e8366SGrzegorz Jaszczyk unsigned long irqmap, irqn, irqsrc, cpuid; 609bc69b8adSEzequiel Garcia unsigned int cascade_irq; 610bc69b8adSEzequiel Garcia 611bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 612bc69b8adSEzequiel Garcia 613bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 614758e8366SGrzegorz Jaszczyk cpuid = cpu_logical_map(smp_processor_id()); 615bc69b8adSEzequiel Garcia 616bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 617758e8366SGrzegorz Jaszczyk irqsrc = readl_relaxed(main_int_base + 618758e8366SGrzegorz Jaszczyk ARMADA_370_XP_INT_SOURCE_CTL(irqn)); 619758e8366SGrzegorz Jaszczyk 620758e8366SGrzegorz Jaszczyk /* Check if the interrupt is not masked on current CPU. 621758e8366SGrzegorz Jaszczyk * Test IRQ (0-1) and FIQ (8-9) mask bits. 622758e8366SGrzegorz Jaszczyk */ 623758e8366SGrzegorz Jaszczyk if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) 624758e8366SGrzegorz Jaszczyk continue; 625758e8366SGrzegorz Jaszczyk 626758e8366SGrzegorz Jaszczyk if (irqn == 1) { 627758e8366SGrzegorz Jaszczyk armada_370_xp_handle_msi_irq(NULL, true); 628758e8366SGrzegorz Jaszczyk continue; 629758e8366SGrzegorz Jaszczyk } 630758e8366SGrzegorz Jaszczyk 631bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 632bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 633bc69b8adSEzequiel Garcia } 634bc69b8adSEzequiel Garcia 635bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 636bc69b8adSEzequiel Garcia } 637bc69b8adSEzequiel Garcia 6388783dd3aSStephen Boyd static void __exception_irq_entry 6399b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 6409b8cf779SEzequiel Garcia { 6419b8cf779SEzequiel Garcia u32 irqstat, irqnr; 6429b8cf779SEzequiel Garcia 6439b8cf779SEzequiel Garcia do { 6449b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 6459b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 6469b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 6479b8cf779SEzequiel Garcia 6489b8cf779SEzequiel Garcia if (irqnr > 1022) 6499b8cf779SEzequiel Garcia break; 6509b8cf779SEzequiel Garcia 6519b8cf779SEzequiel Garcia if (irqnr > 1) { 652e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_mpic_domain, 653e89c6a06SMarc Zyngier irqnr, regs); 6549b8cf779SEzequiel Garcia continue; 6559b8cf779SEzequiel Garcia } 6569b8cf779SEzequiel Garcia 6579b8cf779SEzequiel Garcia /* MSI handling */ 6589b8cf779SEzequiel Garcia if (irqnr == 1) 659bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 6609b8cf779SEzequiel Garcia 6619339d432SThomas Petazzoni #ifdef CONFIG_SMP 6629339d432SThomas Petazzoni /* IPI Handling */ 6639339d432SThomas Petazzoni if (irqnr == 0) { 664f02147ddSMarc Zyngier unsigned long ipimask; 665f02147ddSMarc Zyngier int ipi; 6669339d432SThomas Petazzoni 6679339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 6689339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 6695ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 6709339d432SThomas Petazzoni 671f02147ddSMarc Zyngier for_each_set_bit(ipi, &ipimask, IPI_DOORBELL_END) 672f02147ddSMarc Zyngier handle_domain_irq(ipi_domain, ipi, regs); 6739339d432SThomas Petazzoni } 6749339d432SThomas Petazzoni #endif 6759339d432SThomas Petazzoni 6769339d432SThomas Petazzoni } while (1); 6779339d432SThomas Petazzoni } 6789339d432SThomas Petazzoni 6790f077eb5SThomas Petazzoni static int armada_370_xp_mpic_suspend(void) 6800f077eb5SThomas Petazzoni { 6810f077eb5SThomas Petazzoni doorbell_mask_reg = readl(per_cpu_int_base + 6820f077eb5SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 6830f077eb5SThomas Petazzoni return 0; 6840f077eb5SThomas Petazzoni } 6850f077eb5SThomas Petazzoni 6860f077eb5SThomas Petazzoni static void armada_370_xp_mpic_resume(void) 6870f077eb5SThomas Petazzoni { 6880f077eb5SThomas Petazzoni int nirqs; 6890f077eb5SThomas Petazzoni irq_hw_number_t irq; 6900f077eb5SThomas Petazzoni 6910f077eb5SThomas Petazzoni /* Re-enable interrupts */ 6920f077eb5SThomas Petazzoni nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; 6930f077eb5SThomas Petazzoni for (irq = 0; irq < nirqs; irq++) { 6940f077eb5SThomas Petazzoni struct irq_data *data; 6950f077eb5SThomas Petazzoni int virq; 6960f077eb5SThomas Petazzoni 6970f077eb5SThomas Petazzoni virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 6980f077eb5SThomas Petazzoni if (virq == 0) 6990f077eb5SThomas Petazzoni continue; 7000f077eb5SThomas Petazzoni 7010fa4ce74SThomas Petazzoni data = irq_get_irq_data(virq); 7020fa4ce74SThomas Petazzoni 7030fa4ce74SThomas Petazzoni if (!is_percpu_irq(irq)) { 7040fa4ce74SThomas Petazzoni /* Non per-CPU interrupts */ 7050f077eb5SThomas Petazzoni writel(irq, per_cpu_int_base + 7060f077eb5SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 7070fa4ce74SThomas Petazzoni if (!irqd_irq_disabled(data)) 7080fa4ce74SThomas Petazzoni armada_370_xp_irq_unmask(data); 7090fa4ce74SThomas Petazzoni } else { 7100fa4ce74SThomas Petazzoni /* Per-CPU interrupts */ 7110f077eb5SThomas Petazzoni writel(irq, main_int_base + 7120f077eb5SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 7130f077eb5SThomas Petazzoni 7140fa4ce74SThomas Petazzoni /* 7150fa4ce74SThomas Petazzoni * Re-enable on the current CPU, 7160fa4ce74SThomas Petazzoni * armada_xp_mpic_reenable_percpu() will take 7170fa4ce74SThomas Petazzoni * care of secondary CPUs when they come up. 7180fa4ce74SThomas Petazzoni */ 7190fa4ce74SThomas Petazzoni if (irq_percpu_is_enabled(virq)) 7200f077eb5SThomas Petazzoni armada_370_xp_irq_unmask(data); 7210f077eb5SThomas Petazzoni } 7220fa4ce74SThomas Petazzoni } 7230f077eb5SThomas Petazzoni 7240f077eb5SThomas Petazzoni /* Reconfigure doorbells for IPIs and MSIs */ 7250f077eb5SThomas Petazzoni writel(doorbell_mask_reg, 7260f077eb5SThomas Petazzoni per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 7270f077eb5SThomas Petazzoni if (doorbell_mask_reg & IPI_DOORBELL_MASK) 7280f077eb5SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 7290f077eb5SThomas Petazzoni if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) 7300f077eb5SThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 731f02147ddSMarc Zyngier 732f02147ddSMarc Zyngier ipi_resume(); 7330f077eb5SThomas Petazzoni } 7340f077eb5SThomas Petazzoni 7356c880902SBen Dooks static struct syscore_ops armada_370_xp_mpic_syscore_ops = { 7360f077eb5SThomas Petazzoni .suspend = armada_370_xp_mpic_suspend, 7370f077eb5SThomas Petazzoni .resume = armada_370_xp_mpic_resume, 7380f077eb5SThomas Petazzoni }; 7390f077eb5SThomas Petazzoni 740b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 741b313ada8SThomas Petazzoni struct device_node *parent) 742b313ada8SThomas Petazzoni { 743627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 7445724be84SMaxime Ripard int nr_irqs, i; 745b313ada8SThomas Petazzoni u32 control; 746b313ada8SThomas Petazzoni 747627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 748627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 749b313ada8SThomas Petazzoni 750627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 751627dfcc2SThomas Petazzoni resource_size(&main_int_res), 752627dfcc2SThomas Petazzoni node->full_name)); 753627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 754627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 755627dfcc2SThomas Petazzoni node->full_name)); 756627dfcc2SThomas Petazzoni 757627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 758627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 759b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 760627dfcc2SThomas Petazzoni 761627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 762627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 763b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 764b313ada8SThomas Petazzoni 765b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 766b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 767b73842b7SThomas Petazzoni 768b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 769b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 770b313ada8SThomas Petazzoni 771b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 772b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 773b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 774627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 77596f0d93aSMarc Zyngier irq_domain_update_bus_token(armada_370_xp_mpic_domain, DOMAIN_BUS_WIRED); 776b313ada8SThomas Petazzoni 777933a24b0SEzequiel Garcia /* Setup for the boot CPU */ 77828da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 779b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 780b313ada8SThomas Petazzoni 78131f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 78231f614edSThomas Petazzoni 783bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 784bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 785bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 786b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 787ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 788f02147ddSMarc Zyngier armada_xp_ipi_init(node); 789cb5ff2d2SRichard Cochran cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 79073c1b41eSThomas Gleixner "irqchip/armada/ipi:starting", 791cb5ff2d2SRichard Cochran armada_xp_mpic_starting_cpu, NULL); 792ef37d337SThomas Petazzoni #endif 793bc69b8adSEzequiel Garcia } else { 7945724be84SMaxime Ripard #ifdef CONFIG_SMP 795008b69e4SThomas Gleixner cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_ARMADA_XP_STARTING, 79673c1b41eSThomas Gleixner "irqchip/armada/cascade:starting", 797cb5ff2d2SRichard Cochran mpic_cascaded_starting_cpu, NULL); 7985724be84SMaxime Ripard #endif 799bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 800bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 801bc69b8adSEzequiel Garcia } 802b313ada8SThomas Petazzoni 8030f077eb5SThomas Petazzoni register_syscore_ops(&armada_370_xp_mpic_syscore_ops); 8040f077eb5SThomas Petazzoni 805b313ada8SThomas Petazzoni return 0; 806b313ada8SThomas Petazzoni } 807b313ada8SThomas Petazzoni 8089339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 809