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 599339d432SThomas Petazzoni #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) 6028da06dfSMaxime Ripard #define ARMADA_370_XP_FABRIC_IRQ (3) 619339d432SThomas Petazzoni 625ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 635ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 645ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 6531f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 6631f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 6731f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 6831f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 699339d432SThomas Petazzoni 709339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 719339d432SThomas Petazzoni static void __iomem *main_int_base; 729339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 730f077eb5SThomas Petazzoni static u32 doorbell_mask_reg; 745724be84SMaxime Ripard static int parent_irq; 7531f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 7631f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 7731f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 7831f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 7931f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 8031f614edSThomas Petazzoni #endif 819339d432SThomas Petazzoni 822c299de5SEzequiel Garcia static inline bool is_percpu_irq(irq_hw_number_t irq) 832c299de5SEzequiel Garcia { 842c299de5SEzequiel Garcia switch (irq) { 852c299de5SEzequiel Garcia case ARMADA_370_XP_TIMER0_PER_CPU_IRQ: 8628da06dfSMaxime Ripard case ARMADA_370_XP_FABRIC_IRQ: 872c299de5SEzequiel Garcia return true; 882c299de5SEzequiel Garcia default: 892c299de5SEzequiel Garcia return false; 902c299de5SEzequiel Garcia } 912c299de5SEzequiel Garcia } 922c299de5SEzequiel Garcia 939339d432SThomas Petazzoni /* 949339d432SThomas Petazzoni * In SMP mode: 959339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 961bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 979339d432SThomas Petazzoni */ 989339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 999339d432SThomas Petazzoni { 1009339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 1019339d432SThomas Petazzoni 1022c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 1039339d432SThomas Petazzoni writel(hwirq, main_int_base + 1049339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 1059339d432SThomas Petazzoni else 1069339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1079339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 1089339d432SThomas Petazzoni } 1099339d432SThomas Petazzoni 1109339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 1119339d432SThomas Petazzoni { 1129339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 1139339d432SThomas Petazzoni 1142c299de5SEzequiel Garcia if (!is_percpu_irq(hwirq)) 1159339d432SThomas Petazzoni writel(hwirq, main_int_base + 1169339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 1179339d432SThomas Petazzoni else 1189339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1199339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1209339d432SThomas Petazzoni } 1219339d432SThomas Petazzoni 12231f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 12331f614edSThomas Petazzoni 12431f614edSThomas Petazzoni static int armada_370_xp_alloc_msi(void) 12531f614edSThomas Petazzoni { 12631f614edSThomas Petazzoni int hwirq; 12731f614edSThomas Petazzoni 12831f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 12931f614edSThomas Petazzoni hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR); 13031f614edSThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) 13131f614edSThomas Petazzoni hwirq = -ENOSPC; 13231f614edSThomas Petazzoni else 13331f614edSThomas Petazzoni set_bit(hwirq, msi_used); 13431f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 13531f614edSThomas Petazzoni 13631f614edSThomas Petazzoni return hwirq; 13731f614edSThomas Petazzoni } 13831f614edSThomas Petazzoni 13931f614edSThomas Petazzoni static void armada_370_xp_free_msi(int hwirq) 14031f614edSThomas Petazzoni { 14131f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 14231f614edSThomas Petazzoni if (!test_bit(hwirq, msi_used)) 14331f614edSThomas Petazzoni pr_err("trying to free unused MSI#%d\n", hwirq); 14431f614edSThomas Petazzoni else 14531f614edSThomas Petazzoni clear_bit(hwirq, msi_used); 14631f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 14731f614edSThomas Petazzoni } 14831f614edSThomas Petazzoni 149c2791b80SYijing Wang static int armada_370_xp_setup_msi_irq(struct msi_controller *chip, 15031f614edSThomas Petazzoni struct pci_dev *pdev, 15131f614edSThomas Petazzoni struct msi_desc *desc) 15231f614edSThomas Petazzoni { 15331f614edSThomas Petazzoni struct msi_msg msg; 154da343fc7SThomas Petazzoni int virq, hwirq; 15531f614edSThomas Petazzoni 1563930115eSAlexander Gordeev /* We support MSI, but not MSI-X */ 1573930115eSAlexander Gordeev if (desc->msi_attrib.is_msix) 1583930115eSAlexander Gordeev return -EINVAL; 1593930115eSAlexander Gordeev 16031f614edSThomas Petazzoni hwirq = armada_370_xp_alloc_msi(); 16131f614edSThomas Petazzoni if (hwirq < 0) 16231f614edSThomas Petazzoni return hwirq; 16331f614edSThomas Petazzoni 16431f614edSThomas Petazzoni virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq); 16531f614edSThomas Petazzoni if (!virq) { 16631f614edSThomas Petazzoni armada_370_xp_free_msi(hwirq); 16731f614edSThomas Petazzoni return -EINVAL; 16831f614edSThomas Petazzoni } 16931f614edSThomas Petazzoni 17031f614edSThomas Petazzoni irq_set_msi_desc(virq, desc); 17131f614edSThomas Petazzoni 17231f614edSThomas Petazzoni msg.address_lo = msi_doorbell_addr; 17331f614edSThomas Petazzoni msg.address_hi = 0; 17431f614edSThomas Petazzoni msg.data = 0xf00 | (hwirq + 16); 17531f614edSThomas Petazzoni 17683a18912SJiang Liu pci_write_msi_msg(virq, &msg); 17731f614edSThomas Petazzoni return 0; 17831f614edSThomas Petazzoni } 17931f614edSThomas Petazzoni 180c2791b80SYijing Wang static void armada_370_xp_teardown_msi_irq(struct msi_controller *chip, 18131f614edSThomas Petazzoni unsigned int irq) 18231f614edSThomas Petazzoni { 18331f614edSThomas Petazzoni struct irq_data *d = irq_get_irq_data(irq); 184ff3c6645SNeil Greatorex unsigned long hwirq = d->hwirq; 185ff3c6645SNeil Greatorex 18631f614edSThomas Petazzoni irq_dispose_mapping(irq); 187ff3c6645SNeil Greatorex armada_370_xp_free_msi(hwirq); 18831f614edSThomas Petazzoni } 18931f614edSThomas Petazzoni 19031f614edSThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 19131f614edSThomas Petazzoni .name = "armada_370_xp_msi_irq", 192280510f1SThomas Gleixner .irq_enable = pci_msi_unmask_irq, 193280510f1SThomas Gleixner .irq_disable = pci_msi_mask_irq, 194280510f1SThomas Gleixner .irq_mask = pci_msi_mask_irq, 195280510f1SThomas Gleixner .irq_unmask = pci_msi_unmask_irq, 19631f614edSThomas Petazzoni }; 19731f614edSThomas Petazzoni 19831f614edSThomas Petazzoni static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, 19931f614edSThomas Petazzoni irq_hw_number_t hw) 20031f614edSThomas Petazzoni { 20131f614edSThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, 20231f614edSThomas Petazzoni handle_simple_irq); 20331f614edSThomas Petazzoni set_irq_flags(virq, IRQF_VALID); 20431f614edSThomas Petazzoni 20531f614edSThomas Petazzoni return 0; 20631f614edSThomas Petazzoni } 20731f614edSThomas Petazzoni 20831f614edSThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { 20931f614edSThomas Petazzoni .map = armada_370_xp_msi_map, 21031f614edSThomas Petazzoni }; 21131f614edSThomas Petazzoni 21231f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 21331f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 21431f614edSThomas Petazzoni { 215c2791b80SYijing Wang struct msi_controller *msi_chip; 21631f614edSThomas Petazzoni u32 reg; 21731f614edSThomas Petazzoni int ret; 21831f614edSThomas Petazzoni 21931f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 22031f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 22131f614edSThomas Petazzoni 22231f614edSThomas Petazzoni msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL); 22331f614edSThomas Petazzoni if (!msi_chip) 22431f614edSThomas Petazzoni return -ENOMEM; 22531f614edSThomas Petazzoni 22631f614edSThomas Petazzoni msi_chip->setup_irq = armada_370_xp_setup_msi_irq; 22731f614edSThomas Petazzoni msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; 22831f614edSThomas Petazzoni msi_chip->of_node = node; 22931f614edSThomas Petazzoni 23031f614edSThomas Petazzoni armada_370_xp_msi_domain = 23131f614edSThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 23231f614edSThomas Petazzoni &armada_370_xp_msi_irq_ops, 23331f614edSThomas Petazzoni NULL); 23431f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 23531f614edSThomas Petazzoni kfree(msi_chip); 23631f614edSThomas Petazzoni return -ENOMEM; 23731f614edSThomas Petazzoni } 23831f614edSThomas Petazzoni 23931f614edSThomas Petazzoni ret = of_pci_msi_chip_add(msi_chip); 24031f614edSThomas Petazzoni if (ret < 0) { 24131f614edSThomas Petazzoni irq_domain_remove(armada_370_xp_msi_domain); 24231f614edSThomas Petazzoni kfree(msi_chip); 24331f614edSThomas Petazzoni return ret; 24431f614edSThomas Petazzoni } 24531f614edSThomas Petazzoni 24631f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 24731f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 24831f614edSThomas Petazzoni 24931f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 25031f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 25131f614edSThomas Petazzoni 25231f614edSThomas Petazzoni /* Unmask IPI interrupt */ 25331f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 25431f614edSThomas Petazzoni 25531f614edSThomas Petazzoni return 0; 25631f614edSThomas Petazzoni } 25731f614edSThomas Petazzoni #else 25831f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 25931f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 26031f614edSThomas Petazzoni { 26131f614edSThomas Petazzoni return 0; 26231f614edSThomas Petazzoni } 26331f614edSThomas Petazzoni #endif 26431f614edSThomas Petazzoni 2659339d432SThomas Petazzoni #ifdef CONFIG_SMP 26619e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 26719e61d41SArnaud Ebalard 2689339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 2699339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 2709339d432SThomas Petazzoni { 2719339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 2728cc3cfc5SThomas Gleixner unsigned long reg, mask; 2739339d432SThomas Petazzoni int cpu; 2749339d432SThomas Petazzoni 2758cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 2768cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 2778cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 2789339d432SThomas Petazzoni 2799339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 2809339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2818cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 2829339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2839339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 2849339d432SThomas Petazzoni 2851dacf194SThomas Petazzoni return IRQ_SET_MASK_OK; 2869339d432SThomas Petazzoni } 2879339d432SThomas Petazzoni #endif 2889339d432SThomas Petazzoni 2899339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 2909339d432SThomas Petazzoni .name = "armada_370_xp_irq", 2919339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 2929339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 2939339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 2949339d432SThomas Petazzoni #ifdef CONFIG_SMP 2959339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 2969339d432SThomas Petazzoni #endif 2970d8e1d80SGregory CLEMENT .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 2989339d432SThomas Petazzoni }; 2999339d432SThomas Petazzoni 3009339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 3019339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 3029339d432SThomas Petazzoni { 3039339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 3042c299de5SEzequiel Garcia if (!is_percpu_irq(hw)) 3051bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 3061bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 3071bf25e78SLinus Torvalds else 3089339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 3099339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 3109339d432SThomas Petazzoni 3112c299de5SEzequiel Garcia if (is_percpu_irq(hw)) { 3129339d432SThomas Petazzoni irq_set_percpu_devid(virq); 3139339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3149339d432SThomas Petazzoni handle_percpu_devid_irq); 3159339d432SThomas Petazzoni 3169339d432SThomas Petazzoni } else { 3179339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3189339d432SThomas Petazzoni handle_level_irq); 3199339d432SThomas Petazzoni } 3209339d432SThomas Petazzoni set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 3219339d432SThomas Petazzoni 3229339d432SThomas Petazzoni return 0; 3239339d432SThomas Petazzoni } 3249339d432SThomas Petazzoni 325d7df84b3SThomas Petazzoni static void armada_xp_mpic_smp_cpu_init(void) 3269339d432SThomas Petazzoni { 327b73842b7SThomas Petazzoni u32 control; 328b73842b7SThomas Petazzoni int nr_irqs, i; 329b73842b7SThomas Petazzoni 330b73842b7SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 331b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 332b73842b7SThomas Petazzoni 333b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 334b73842b7SThomas Petazzoni writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 335b73842b7SThomas Petazzoni 3369339d432SThomas Petazzoni /* Clear pending IPIs */ 3379339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 3389339d432SThomas Petazzoni 3399339d432SThomas Petazzoni /* Enable first 8 IPIs */ 3405ec69017SThomas Petazzoni writel(IPI_DOORBELL_MASK, per_cpu_int_base + 3419339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 3429339d432SThomas Petazzoni 3439339d432SThomas Petazzoni /* Unmask IPI interrupt */ 3449339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 3459339d432SThomas Petazzoni } 346d7df84b3SThomas Petazzoni 34728da06dfSMaxime Ripard static void armada_xp_mpic_perf_init(void) 34828da06dfSMaxime Ripard { 34928da06dfSMaxime Ripard unsigned long cpuid = cpu_logical_map(smp_processor_id()); 35028da06dfSMaxime Ripard 35128da06dfSMaxime Ripard /* Enable Performance Counter Overflow interrupts */ 35228da06dfSMaxime Ripard writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid), 35328da06dfSMaxime Ripard per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS); 35428da06dfSMaxime Ripard } 35528da06dfSMaxime Ripard 356933a24b0SEzequiel Garcia #ifdef CONFIG_SMP 357933a24b0SEzequiel Garcia static void armada_mpic_send_doorbell(const struct cpumask *mask, 358933a24b0SEzequiel Garcia unsigned int irq) 359933a24b0SEzequiel Garcia { 360933a24b0SEzequiel Garcia int cpu; 361933a24b0SEzequiel Garcia unsigned long map = 0; 362933a24b0SEzequiel Garcia 363933a24b0SEzequiel Garcia /* Convert our logical CPU mask into a physical one. */ 364933a24b0SEzequiel Garcia for_each_cpu(cpu, mask) 365933a24b0SEzequiel Garcia map |= 1 << cpu_logical_map(cpu); 366933a24b0SEzequiel Garcia 367933a24b0SEzequiel Garcia /* 368933a24b0SEzequiel Garcia * Ensure that stores to Normal memory are visible to the 369933a24b0SEzequiel Garcia * other CPUs before issuing the IPI. 370933a24b0SEzequiel Garcia */ 371933a24b0SEzequiel Garcia dsb(); 372933a24b0SEzequiel Garcia 373933a24b0SEzequiel Garcia /* submit softirq */ 374933a24b0SEzequiel Garcia writel((map << 8) | irq, main_int_base + 375933a24b0SEzequiel Garcia ARMADA_370_XP_SW_TRIG_INT_OFFS); 376933a24b0SEzequiel Garcia } 377933a24b0SEzequiel Garcia 378d7df84b3SThomas Petazzoni static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, 379d7df84b3SThomas Petazzoni unsigned long action, void *hcpu) 380d7df84b3SThomas Petazzoni { 38128da06dfSMaxime Ripard if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { 38228da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 383d7df84b3SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 38428da06dfSMaxime Ripard } 3855724be84SMaxime Ripard 386d7df84b3SThomas Petazzoni return NOTIFY_OK; 387d7df84b3SThomas Petazzoni } 388d7df84b3SThomas Petazzoni 389d7df84b3SThomas Petazzoni static struct notifier_block armada_370_xp_mpic_cpu_notifier = { 390d7df84b3SThomas Petazzoni .notifier_call = armada_xp_mpic_secondary_init, 391d7df84b3SThomas Petazzoni .priority = 100, 392d7df84b3SThomas Petazzoni }; 393d7df84b3SThomas Petazzoni 3945724be84SMaxime Ripard static int mpic_cascaded_secondary_init(struct notifier_block *nfb, 3955724be84SMaxime Ripard unsigned long action, void *hcpu) 3965724be84SMaxime Ripard { 39728da06dfSMaxime Ripard if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { 39828da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 3995724be84SMaxime Ripard enable_percpu_irq(parent_irq, IRQ_TYPE_NONE); 40028da06dfSMaxime Ripard } 4015724be84SMaxime Ripard 4025724be84SMaxime Ripard return NOTIFY_OK; 4035724be84SMaxime Ripard } 4045724be84SMaxime Ripard 4055724be84SMaxime Ripard static struct notifier_block mpic_cascaded_cpu_notifier = { 4065724be84SMaxime Ripard .notifier_call = mpic_cascaded_secondary_init, 4075724be84SMaxime Ripard .priority = 100, 4085724be84SMaxime Ripard }; 4099339d432SThomas Petazzoni #endif /* CONFIG_SMP */ 4109339d432SThomas Petazzoni 41196009736SKrzysztof Kozlowski static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 4129339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 4139339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 4149339d432SThomas Petazzoni }; 4159339d432SThomas Petazzoni 41631f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 417bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 4189b8cf779SEzequiel Garcia { 41931f614edSThomas Petazzoni u32 msimask, msinr; 42031f614edSThomas Petazzoni 42131f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 42231f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 42331f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 42431f614edSThomas Petazzoni 425c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 42631f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 42731f614edSThomas Petazzoni 42831f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 42931f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 43031f614edSThomas Petazzoni int irq; 43131f614edSThomas Petazzoni 43231f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 43331f614edSThomas Petazzoni continue; 43431f614edSThomas Petazzoni 435e89c6a06SMarc Zyngier if (is_chained) { 43631f614edSThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_domain, 43731f614edSThomas Petazzoni msinr - 16); 438bc69b8adSEzequiel Garcia generic_handle_irq(irq); 439e89c6a06SMarc Zyngier } else { 440e89c6a06SMarc Zyngier irq = msinr - 16; 441e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_msi_domain, 442e89c6a06SMarc Zyngier irq, regs); 443e89c6a06SMarc Zyngier } 44431f614edSThomas Petazzoni } 44531f614edSThomas Petazzoni } 4469b8cf779SEzequiel Garcia #else 447bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 44831f614edSThomas Petazzoni #endif 44931f614edSThomas Petazzoni 450bc69b8adSEzequiel Garcia static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, 451bc69b8adSEzequiel Garcia struct irq_desc *desc) 452bc69b8adSEzequiel Garcia { 4535b29264cSJiang Liu struct irq_chip *chip = irq_desc_get_chip(desc); 454758e8366SGrzegorz Jaszczyk unsigned long irqmap, irqn, irqsrc, cpuid; 455bc69b8adSEzequiel Garcia unsigned int cascade_irq; 456bc69b8adSEzequiel Garcia 457bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 458bc69b8adSEzequiel Garcia 459bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 460758e8366SGrzegorz Jaszczyk cpuid = cpu_logical_map(smp_processor_id()); 461bc69b8adSEzequiel Garcia 462bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 463758e8366SGrzegorz Jaszczyk irqsrc = readl_relaxed(main_int_base + 464758e8366SGrzegorz Jaszczyk ARMADA_370_XP_INT_SOURCE_CTL(irqn)); 465758e8366SGrzegorz Jaszczyk 466758e8366SGrzegorz Jaszczyk /* Check if the interrupt is not masked on current CPU. 467758e8366SGrzegorz Jaszczyk * Test IRQ (0-1) and FIQ (8-9) mask bits. 468758e8366SGrzegorz Jaszczyk */ 469758e8366SGrzegorz Jaszczyk if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) 470758e8366SGrzegorz Jaszczyk continue; 471758e8366SGrzegorz Jaszczyk 472758e8366SGrzegorz Jaszczyk if (irqn == 1) { 473758e8366SGrzegorz Jaszczyk armada_370_xp_handle_msi_irq(NULL, true); 474758e8366SGrzegorz Jaszczyk continue; 475758e8366SGrzegorz Jaszczyk } 476758e8366SGrzegorz Jaszczyk 477bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 478bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 479bc69b8adSEzequiel Garcia } 480bc69b8adSEzequiel Garcia 481bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 482bc69b8adSEzequiel Garcia } 483bc69b8adSEzequiel Garcia 4848783dd3aSStephen Boyd static void __exception_irq_entry 4859b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 4869b8cf779SEzequiel Garcia { 4879b8cf779SEzequiel Garcia u32 irqstat, irqnr; 4889b8cf779SEzequiel Garcia 4899b8cf779SEzequiel Garcia do { 4909b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 4919b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 4929b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 4939b8cf779SEzequiel Garcia 4949b8cf779SEzequiel Garcia if (irqnr > 1022) 4959b8cf779SEzequiel Garcia break; 4969b8cf779SEzequiel Garcia 4979b8cf779SEzequiel Garcia if (irqnr > 1) { 498e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_mpic_domain, 499e89c6a06SMarc Zyngier irqnr, regs); 5009b8cf779SEzequiel Garcia continue; 5019b8cf779SEzequiel Garcia } 5029b8cf779SEzequiel Garcia 5039b8cf779SEzequiel Garcia /* MSI handling */ 5049b8cf779SEzequiel Garcia if (irqnr == 1) 505bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 5069b8cf779SEzequiel Garcia 5079339d432SThomas Petazzoni #ifdef CONFIG_SMP 5089339d432SThomas Petazzoni /* IPI Handling */ 5099339d432SThomas Petazzoni if (irqnr == 0) { 5109339d432SThomas Petazzoni u32 ipimask, ipinr; 5119339d432SThomas Petazzoni 5129339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 5139339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 5145ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 5159339d432SThomas Petazzoni 516a6f089e9SLior Amsalem writel(~ipimask, per_cpu_int_base + 5179339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 5189339d432SThomas Petazzoni 5199339d432SThomas Petazzoni /* Handle all pending doorbells */ 5205ec69017SThomas Petazzoni for (ipinr = IPI_DOORBELL_START; 5215ec69017SThomas Petazzoni ipinr < IPI_DOORBELL_END; ipinr++) { 5229339d432SThomas Petazzoni if (ipimask & (0x1 << ipinr)) 5239339d432SThomas Petazzoni handle_IPI(ipinr, regs); 5249339d432SThomas Petazzoni } 5259339d432SThomas Petazzoni continue; 5269339d432SThomas Petazzoni } 5279339d432SThomas Petazzoni #endif 5289339d432SThomas Petazzoni 5299339d432SThomas Petazzoni } while (1); 5309339d432SThomas Petazzoni } 5319339d432SThomas Petazzoni 5320f077eb5SThomas Petazzoni static int armada_370_xp_mpic_suspend(void) 5330f077eb5SThomas Petazzoni { 5340f077eb5SThomas Petazzoni doorbell_mask_reg = readl(per_cpu_int_base + 5350f077eb5SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 5360f077eb5SThomas Petazzoni return 0; 5370f077eb5SThomas Petazzoni } 5380f077eb5SThomas Petazzoni 5390f077eb5SThomas Petazzoni static void armada_370_xp_mpic_resume(void) 5400f077eb5SThomas Petazzoni { 5410f077eb5SThomas Petazzoni int nirqs; 5420f077eb5SThomas Petazzoni irq_hw_number_t irq; 5430f077eb5SThomas Petazzoni 5440f077eb5SThomas Petazzoni /* Re-enable interrupts */ 5450f077eb5SThomas Petazzoni nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; 5460f077eb5SThomas Petazzoni for (irq = 0; irq < nirqs; irq++) { 5470f077eb5SThomas Petazzoni struct irq_data *data; 5480f077eb5SThomas Petazzoni int virq; 5490f077eb5SThomas Petazzoni 5500f077eb5SThomas Petazzoni virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); 5510f077eb5SThomas Petazzoni if (virq == 0) 5520f077eb5SThomas Petazzoni continue; 5530f077eb5SThomas Petazzoni 5540f077eb5SThomas Petazzoni if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 5550f077eb5SThomas Petazzoni writel(irq, per_cpu_int_base + 5560f077eb5SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5570f077eb5SThomas Petazzoni else 5580f077eb5SThomas Petazzoni writel(irq, main_int_base + 5590f077eb5SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 5600f077eb5SThomas Petazzoni 5610f077eb5SThomas Petazzoni data = irq_get_irq_data(virq); 5620f077eb5SThomas Petazzoni if (!irqd_irq_disabled(data)) 5630f077eb5SThomas Petazzoni armada_370_xp_irq_unmask(data); 5640f077eb5SThomas Petazzoni } 5650f077eb5SThomas Petazzoni 5660f077eb5SThomas Petazzoni /* Reconfigure doorbells for IPIs and MSIs */ 5670f077eb5SThomas Petazzoni writel(doorbell_mask_reg, 5680f077eb5SThomas Petazzoni per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 5690f077eb5SThomas Petazzoni if (doorbell_mask_reg & IPI_DOORBELL_MASK) 5700f077eb5SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5710f077eb5SThomas Petazzoni if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) 5720f077eb5SThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 5730f077eb5SThomas Petazzoni } 5740f077eb5SThomas Petazzoni 5750f077eb5SThomas Petazzoni struct syscore_ops armada_370_xp_mpic_syscore_ops = { 5760f077eb5SThomas Petazzoni .suspend = armada_370_xp_mpic_suspend, 5770f077eb5SThomas Petazzoni .resume = armada_370_xp_mpic_resume, 5780f077eb5SThomas Petazzoni }; 5790f077eb5SThomas Petazzoni 580b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 581b313ada8SThomas Petazzoni struct device_node *parent) 582b313ada8SThomas Petazzoni { 583627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 5845724be84SMaxime Ripard int nr_irqs, i; 585b313ada8SThomas Petazzoni u32 control; 586b313ada8SThomas Petazzoni 587627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 588627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 589b313ada8SThomas Petazzoni 590627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 591627dfcc2SThomas Petazzoni resource_size(&main_int_res), 592627dfcc2SThomas Petazzoni node->full_name)); 593627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 594627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 595627dfcc2SThomas Petazzoni node->full_name)); 596627dfcc2SThomas Petazzoni 597627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 598627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 599b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 600627dfcc2SThomas Petazzoni 601627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 602627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 603b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 604b313ada8SThomas Petazzoni 605b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 606b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 607b73842b7SThomas Petazzoni 608b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 609b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 610b313ada8SThomas Petazzoni 611b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 612b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 613b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 614b313ada8SThomas Petazzoni 615627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 616b313ada8SThomas Petazzoni 617933a24b0SEzequiel Garcia /* Setup for the boot CPU */ 61828da06dfSMaxime Ripard armada_xp_mpic_perf_init(); 619b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 620b313ada8SThomas Petazzoni 62131f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 62231f614edSThomas Petazzoni 623bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 624bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 625bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 626b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 627ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 628ef37d337SThomas Petazzoni set_smp_cross_call(armada_mpic_send_doorbell); 629d7df84b3SThomas Petazzoni register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); 630ef37d337SThomas Petazzoni #endif 631bc69b8adSEzequiel Garcia } else { 6325724be84SMaxime Ripard #ifdef CONFIG_SMP 6335724be84SMaxime Ripard register_cpu_notifier(&mpic_cascaded_cpu_notifier); 6345724be84SMaxime Ripard #endif 635bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 636bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 637bc69b8adSEzequiel Garcia } 638b313ada8SThomas Petazzoni 6390f077eb5SThomas Petazzoni register_syscore_ops(&armada_370_xp_mpic_syscore_ops); 6400f077eb5SThomas Petazzoni 641b313ada8SThomas Petazzoni return 0; 642b313ada8SThomas Petazzoni } 643b313ada8SThomas Petazzoni 6449339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 645