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> 21*bc69b8adSEzequiel Garcia #include <linux/irqchip/chained_irq.h> 229339d432SThomas Petazzoni #include <linux/io.h> 239339d432SThomas Petazzoni #include <linux/of_address.h> 249339d432SThomas Petazzoni #include <linux/of_irq.h> 2531f614edSThomas Petazzoni #include <linux/of_pci.h> 269339d432SThomas Petazzoni #include <linux/irqdomain.h> 2731f614edSThomas Petazzoni #include <linux/slab.h> 2831f614edSThomas Petazzoni #include <linux/msi.h> 299339d432SThomas Petazzoni #include <asm/mach/arch.h> 309339d432SThomas Petazzoni #include <asm/exception.h> 319339d432SThomas Petazzoni #include <asm/smp_plat.h> 329339d432SThomas Petazzoni #include <asm/mach/irq.h> 339339d432SThomas Petazzoni 349339d432SThomas Petazzoni #include "irqchip.h" 359339d432SThomas Petazzoni 369339d432SThomas Petazzoni /* Interrupt Controller Registers Map */ 379339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) 389339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) 399339d432SThomas Petazzoni 409339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CONTROL (0x00) 419339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) 429339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) 439339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) 449339d432SThomas Petazzoni 459339d432SThomas Petazzoni #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 46*bc69b8adSEzequiel Garcia #define ARMADA_375_PPI_CAUSE (0x10) 479339d432SThomas Petazzoni 489339d432SThomas Petazzoni #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) 499339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) 509339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) 519339d432SThomas Petazzoni 529339d432SThomas Petazzoni #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 539339d432SThomas Petazzoni 549339d432SThomas Petazzoni #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) 559339d432SThomas Petazzoni 565ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 575ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 585ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 5931f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 6031f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 6131f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 6231f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 639339d432SThomas Petazzoni 649339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 659339d432SThomas Petazzoni static void __iomem *main_int_base; 669339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 6731f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 6831f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 6931f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 7031f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 7131f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 7231f614edSThomas Petazzoni #endif 739339d432SThomas Petazzoni 749339d432SThomas Petazzoni /* 759339d432SThomas Petazzoni * In SMP mode: 769339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 771bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 789339d432SThomas Petazzoni */ 799339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 809339d432SThomas Petazzoni { 819339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 829339d432SThomas Petazzoni 839339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 849339d432SThomas Petazzoni writel(hwirq, main_int_base + 859339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 869339d432SThomas Petazzoni else 879339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 889339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 899339d432SThomas Petazzoni } 909339d432SThomas Petazzoni 919339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 929339d432SThomas Petazzoni { 939339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 949339d432SThomas Petazzoni 959339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 969339d432SThomas Petazzoni writel(hwirq, main_int_base + 979339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 989339d432SThomas Petazzoni else 999339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1009339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1019339d432SThomas Petazzoni } 1029339d432SThomas Petazzoni 10331f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 10431f614edSThomas Petazzoni 10531f614edSThomas Petazzoni static int armada_370_xp_alloc_msi(void) 10631f614edSThomas Petazzoni { 10731f614edSThomas Petazzoni int hwirq; 10831f614edSThomas Petazzoni 10931f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 11031f614edSThomas Petazzoni hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR); 11131f614edSThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) 11231f614edSThomas Petazzoni hwirq = -ENOSPC; 11331f614edSThomas Petazzoni else 11431f614edSThomas Petazzoni set_bit(hwirq, msi_used); 11531f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 11631f614edSThomas Petazzoni 11731f614edSThomas Petazzoni return hwirq; 11831f614edSThomas Petazzoni } 11931f614edSThomas Petazzoni 12031f614edSThomas Petazzoni static void armada_370_xp_free_msi(int hwirq) 12131f614edSThomas Petazzoni { 12231f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 12331f614edSThomas Petazzoni if (!test_bit(hwirq, msi_used)) 12431f614edSThomas Petazzoni pr_err("trying to free unused MSI#%d\n", hwirq); 12531f614edSThomas Petazzoni else 12631f614edSThomas Petazzoni clear_bit(hwirq, msi_used); 12731f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 12831f614edSThomas Petazzoni } 12931f614edSThomas Petazzoni 13031f614edSThomas Petazzoni static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, 13131f614edSThomas Petazzoni struct pci_dev *pdev, 13231f614edSThomas Petazzoni struct msi_desc *desc) 13331f614edSThomas Petazzoni { 13431f614edSThomas Petazzoni struct msi_msg msg; 13531f614edSThomas Petazzoni irq_hw_number_t hwirq; 13631f614edSThomas Petazzoni int virq; 13731f614edSThomas Petazzoni 13831f614edSThomas Petazzoni hwirq = armada_370_xp_alloc_msi(); 13931f614edSThomas Petazzoni if (hwirq < 0) 14031f614edSThomas Petazzoni return hwirq; 14131f614edSThomas Petazzoni 14231f614edSThomas Petazzoni virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq); 14331f614edSThomas Petazzoni if (!virq) { 14431f614edSThomas Petazzoni armada_370_xp_free_msi(hwirq); 14531f614edSThomas Petazzoni return -EINVAL; 14631f614edSThomas Petazzoni } 14731f614edSThomas Petazzoni 14831f614edSThomas Petazzoni irq_set_msi_desc(virq, desc); 14931f614edSThomas Petazzoni 15031f614edSThomas Petazzoni msg.address_lo = msi_doorbell_addr; 15131f614edSThomas Petazzoni msg.address_hi = 0; 15231f614edSThomas Petazzoni msg.data = 0xf00 | (hwirq + 16); 15331f614edSThomas Petazzoni 15431f614edSThomas Petazzoni write_msi_msg(virq, &msg); 15531f614edSThomas Petazzoni return 0; 15631f614edSThomas Petazzoni } 15731f614edSThomas Petazzoni 15831f614edSThomas Petazzoni static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, 15931f614edSThomas Petazzoni unsigned int irq) 16031f614edSThomas Petazzoni { 16131f614edSThomas Petazzoni struct irq_data *d = irq_get_irq_data(irq); 16231f614edSThomas Petazzoni irq_dispose_mapping(irq); 16331f614edSThomas Petazzoni armada_370_xp_free_msi(d->hwirq); 16431f614edSThomas Petazzoni } 16531f614edSThomas Petazzoni 16631f614edSThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 16731f614edSThomas Petazzoni .name = "armada_370_xp_msi_irq", 16831f614edSThomas Petazzoni .irq_enable = unmask_msi_irq, 16931f614edSThomas Petazzoni .irq_disable = mask_msi_irq, 17031f614edSThomas Petazzoni .irq_mask = mask_msi_irq, 17131f614edSThomas Petazzoni .irq_unmask = unmask_msi_irq, 17231f614edSThomas Petazzoni }; 17331f614edSThomas Petazzoni 17431f614edSThomas Petazzoni static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, 17531f614edSThomas Petazzoni irq_hw_number_t hw) 17631f614edSThomas Petazzoni { 17731f614edSThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, 17831f614edSThomas Petazzoni handle_simple_irq); 17931f614edSThomas Petazzoni set_irq_flags(virq, IRQF_VALID); 18031f614edSThomas Petazzoni 18131f614edSThomas Petazzoni return 0; 18231f614edSThomas Petazzoni } 18331f614edSThomas Petazzoni 18431f614edSThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { 18531f614edSThomas Petazzoni .map = armada_370_xp_msi_map, 18631f614edSThomas Petazzoni }; 18731f614edSThomas Petazzoni 18831f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 18931f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 19031f614edSThomas Petazzoni { 19131f614edSThomas Petazzoni struct msi_chip *msi_chip; 19231f614edSThomas Petazzoni u32 reg; 19331f614edSThomas Petazzoni int ret; 19431f614edSThomas Petazzoni 19531f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 19631f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 19731f614edSThomas Petazzoni 19831f614edSThomas Petazzoni msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL); 19931f614edSThomas Petazzoni if (!msi_chip) 20031f614edSThomas Petazzoni return -ENOMEM; 20131f614edSThomas Petazzoni 20231f614edSThomas Petazzoni msi_chip->setup_irq = armada_370_xp_setup_msi_irq; 20331f614edSThomas Petazzoni msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; 20431f614edSThomas Petazzoni msi_chip->of_node = node; 20531f614edSThomas Petazzoni 20631f614edSThomas Petazzoni armada_370_xp_msi_domain = 20731f614edSThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 20831f614edSThomas Petazzoni &armada_370_xp_msi_irq_ops, 20931f614edSThomas Petazzoni NULL); 21031f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 21131f614edSThomas Petazzoni kfree(msi_chip); 21231f614edSThomas Petazzoni return -ENOMEM; 21331f614edSThomas Petazzoni } 21431f614edSThomas Petazzoni 21531f614edSThomas Petazzoni ret = of_pci_msi_chip_add(msi_chip); 21631f614edSThomas Petazzoni if (ret < 0) { 21731f614edSThomas Petazzoni irq_domain_remove(armada_370_xp_msi_domain); 21831f614edSThomas Petazzoni kfree(msi_chip); 21931f614edSThomas Petazzoni return ret; 22031f614edSThomas Petazzoni } 22131f614edSThomas Petazzoni 22231f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 22331f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 22431f614edSThomas Petazzoni 22531f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 22631f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 22731f614edSThomas Petazzoni 22831f614edSThomas Petazzoni /* Unmask IPI interrupt */ 22931f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 23031f614edSThomas Petazzoni 23131f614edSThomas Petazzoni return 0; 23231f614edSThomas Petazzoni } 23331f614edSThomas Petazzoni #else 23431f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 23531f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 23631f614edSThomas Petazzoni { 23731f614edSThomas Petazzoni return 0; 23831f614edSThomas Petazzoni } 23931f614edSThomas Petazzoni #endif 24031f614edSThomas Petazzoni 2419339d432SThomas Petazzoni #ifdef CONFIG_SMP 24219e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 24319e61d41SArnaud Ebalard 2449339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 2459339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 2469339d432SThomas Petazzoni { 2479339d432SThomas Petazzoni unsigned long reg; 2489339d432SThomas Petazzoni unsigned long new_mask = 0; 2499339d432SThomas Petazzoni unsigned long online_mask = 0; 2509339d432SThomas Petazzoni unsigned long count = 0; 2519339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 2529339d432SThomas Petazzoni int cpu; 2539339d432SThomas Petazzoni 2549339d432SThomas Petazzoni for_each_cpu(cpu, mask_val) { 2559339d432SThomas Petazzoni new_mask |= 1 << cpu_logical_map(cpu); 2569339d432SThomas Petazzoni count++; 2579339d432SThomas Petazzoni } 2589339d432SThomas Petazzoni 2599339d432SThomas Petazzoni /* 2609339d432SThomas Petazzoni * Forbid mutlicore interrupt affinity 2619339d432SThomas Petazzoni * This is required since the MPIC HW doesn't limit 2629339d432SThomas Petazzoni * several CPUs from acknowledging the same interrupt. 2639339d432SThomas Petazzoni */ 2649339d432SThomas Petazzoni if (count > 1) 2659339d432SThomas Petazzoni return -EINVAL; 2669339d432SThomas Petazzoni 2679339d432SThomas Petazzoni for_each_cpu(cpu, cpu_online_mask) 2689339d432SThomas Petazzoni online_mask |= 1 << cpu_logical_map(cpu); 2699339d432SThomas Petazzoni 2709339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 2719339d432SThomas Petazzoni 2729339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2739339d432SThomas Petazzoni reg = (reg & (~online_mask)) | new_mask; 2749339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2759339d432SThomas Petazzoni 2769339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 2779339d432SThomas Petazzoni 2789339d432SThomas Petazzoni return 0; 2799339d432SThomas Petazzoni } 2809339d432SThomas Petazzoni #endif 2819339d432SThomas Petazzoni 2829339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 2839339d432SThomas Petazzoni .name = "armada_370_xp_irq", 2849339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 2859339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 2869339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 2879339d432SThomas Petazzoni #ifdef CONFIG_SMP 2889339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 2899339d432SThomas Petazzoni #endif 2909339d432SThomas Petazzoni }; 2919339d432SThomas Petazzoni 2929339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 2939339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 2949339d432SThomas Petazzoni { 2959339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 2961bf25e78SLinus Torvalds if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 2971bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 2981bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 2991bf25e78SLinus Torvalds else 3009339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 3019339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 3029339d432SThomas Petazzoni 3039339d432SThomas Petazzoni if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { 3049339d432SThomas Petazzoni irq_set_percpu_devid(virq); 3059339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3069339d432SThomas Petazzoni handle_percpu_devid_irq); 3079339d432SThomas Petazzoni 3089339d432SThomas Petazzoni } else { 3099339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3109339d432SThomas Petazzoni handle_level_irq); 3119339d432SThomas Petazzoni } 3129339d432SThomas Petazzoni set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 3139339d432SThomas Petazzoni 3149339d432SThomas Petazzoni return 0; 3159339d432SThomas Petazzoni } 3169339d432SThomas Petazzoni 3179339d432SThomas Petazzoni #ifdef CONFIG_SMP 3189339d432SThomas Petazzoni void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) 3199339d432SThomas Petazzoni { 3209339d432SThomas Petazzoni int cpu; 3219339d432SThomas Petazzoni unsigned long map = 0; 3229339d432SThomas Petazzoni 3239339d432SThomas Petazzoni /* Convert our logical CPU mask into a physical one. */ 3249339d432SThomas Petazzoni for_each_cpu(cpu, mask) 3259339d432SThomas Petazzoni map |= 1 << cpu_logical_map(cpu); 3269339d432SThomas Petazzoni 3279339d432SThomas Petazzoni /* 3289339d432SThomas Petazzoni * Ensure that stores to Normal memory are visible to the 3299339d432SThomas Petazzoni * other CPUs before issuing the IPI. 3309339d432SThomas Petazzoni */ 3319339d432SThomas Petazzoni dsb(); 3329339d432SThomas Petazzoni 3339339d432SThomas Petazzoni /* submit softirq */ 3349339d432SThomas Petazzoni writel((map << 8) | irq, main_int_base + 3359339d432SThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS); 3369339d432SThomas Petazzoni } 3379339d432SThomas Petazzoni 3389339d432SThomas Petazzoni void armada_xp_mpic_smp_cpu_init(void) 3399339d432SThomas Petazzoni { 3409339d432SThomas Petazzoni /* Clear pending IPIs */ 3419339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 3429339d432SThomas Petazzoni 3439339d432SThomas Petazzoni /* Enable first 8 IPIs */ 3445ec69017SThomas Petazzoni writel(IPI_DOORBELL_MASK, per_cpu_int_base + 3459339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 3469339d432SThomas Petazzoni 3479339d432SThomas Petazzoni /* Unmask IPI interrupt */ 3489339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 3499339d432SThomas Petazzoni } 3509339d432SThomas Petazzoni #endif /* CONFIG_SMP */ 3519339d432SThomas Petazzoni 3529339d432SThomas Petazzoni static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 3539339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 3549339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 3559339d432SThomas Petazzoni }; 3569339d432SThomas Petazzoni 35731f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 358*bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 3599b8cf779SEzequiel Garcia { 36031f614edSThomas Petazzoni u32 msimask, msinr; 36131f614edSThomas Petazzoni 36231f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 36331f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 36431f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 36531f614edSThomas Petazzoni 366c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 36731f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 36831f614edSThomas Petazzoni 36931f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 37031f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 37131f614edSThomas Petazzoni int irq; 37231f614edSThomas Petazzoni 37331f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 37431f614edSThomas Petazzoni continue; 37531f614edSThomas Petazzoni 37631f614edSThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_domain, 37731f614edSThomas Petazzoni msinr - 16); 378*bc69b8adSEzequiel Garcia 379*bc69b8adSEzequiel Garcia if (is_chained) 380*bc69b8adSEzequiel Garcia generic_handle_irq(irq); 381*bc69b8adSEzequiel Garcia else 38231f614edSThomas Petazzoni handle_IRQ(irq, regs); 38331f614edSThomas Petazzoni } 38431f614edSThomas Petazzoni } 3859b8cf779SEzequiel Garcia #else 386*bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 38731f614edSThomas Petazzoni #endif 38831f614edSThomas Petazzoni 389*bc69b8adSEzequiel Garcia static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, 390*bc69b8adSEzequiel Garcia struct irq_desc *desc) 391*bc69b8adSEzequiel Garcia { 392*bc69b8adSEzequiel Garcia struct irq_chip *chip = irq_get_chip(irq); 393*bc69b8adSEzequiel Garcia unsigned long irqmap, irqn; 394*bc69b8adSEzequiel Garcia unsigned int cascade_irq; 395*bc69b8adSEzequiel Garcia 396*bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 397*bc69b8adSEzequiel Garcia 398*bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 399*bc69b8adSEzequiel Garcia 400*bc69b8adSEzequiel Garcia if (irqmap & BIT(0)) { 401*bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(NULL, true); 402*bc69b8adSEzequiel Garcia irqmap &= ~BIT(0); 403*bc69b8adSEzequiel Garcia } 404*bc69b8adSEzequiel Garcia 405*bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 406*bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 407*bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 408*bc69b8adSEzequiel Garcia } 409*bc69b8adSEzequiel Garcia 410*bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 411*bc69b8adSEzequiel Garcia } 412*bc69b8adSEzequiel Garcia 4139b8cf779SEzequiel Garcia static asmlinkage void __exception_irq_entry 4149b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 4159b8cf779SEzequiel Garcia { 4169b8cf779SEzequiel Garcia u32 irqstat, irqnr; 4179b8cf779SEzequiel Garcia 4189b8cf779SEzequiel Garcia do { 4199b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 4209b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 4219b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 4229b8cf779SEzequiel Garcia 4239b8cf779SEzequiel Garcia if (irqnr > 1022) 4249b8cf779SEzequiel Garcia break; 4259b8cf779SEzequiel Garcia 4269b8cf779SEzequiel Garcia if (irqnr > 1) { 4279b8cf779SEzequiel Garcia irqnr = irq_find_mapping(armada_370_xp_mpic_domain, 4289b8cf779SEzequiel Garcia irqnr); 4299b8cf779SEzequiel Garcia handle_IRQ(irqnr, regs); 4309b8cf779SEzequiel Garcia continue; 4319b8cf779SEzequiel Garcia } 4329b8cf779SEzequiel Garcia 4339b8cf779SEzequiel Garcia /* MSI handling */ 4349b8cf779SEzequiel Garcia if (irqnr == 1) 435*bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 4369b8cf779SEzequiel Garcia 4379339d432SThomas Petazzoni #ifdef CONFIG_SMP 4389339d432SThomas Petazzoni /* IPI Handling */ 4399339d432SThomas Petazzoni if (irqnr == 0) { 4409339d432SThomas Petazzoni u32 ipimask, ipinr; 4419339d432SThomas Petazzoni 4429339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 4439339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 4445ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 4459339d432SThomas Petazzoni 446a6f089e9SLior Amsalem writel(~ipimask, per_cpu_int_base + 4479339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 4489339d432SThomas Petazzoni 4499339d432SThomas Petazzoni /* Handle all pending doorbells */ 4505ec69017SThomas Petazzoni for (ipinr = IPI_DOORBELL_START; 4515ec69017SThomas Petazzoni ipinr < IPI_DOORBELL_END; ipinr++) { 4529339d432SThomas Petazzoni if (ipimask & (0x1 << ipinr)) 4539339d432SThomas Petazzoni handle_IPI(ipinr, regs); 4549339d432SThomas Petazzoni } 4559339d432SThomas Petazzoni continue; 4569339d432SThomas Petazzoni } 4579339d432SThomas Petazzoni #endif 4589339d432SThomas Petazzoni 4599339d432SThomas Petazzoni } while (1); 4609339d432SThomas Petazzoni } 4619339d432SThomas Petazzoni 462b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 463b313ada8SThomas Petazzoni struct device_node *parent) 464b313ada8SThomas Petazzoni { 465627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 466*bc69b8adSEzequiel Garcia int parent_irq; 467b313ada8SThomas Petazzoni u32 control; 468b313ada8SThomas Petazzoni 469627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 470627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 471b313ada8SThomas Petazzoni 472627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 473627dfcc2SThomas Petazzoni resource_size(&main_int_res), 474627dfcc2SThomas Petazzoni node->full_name)); 475627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 476627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 477627dfcc2SThomas Petazzoni node->full_name)); 478627dfcc2SThomas Petazzoni 479627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 480627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 481b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 482627dfcc2SThomas Petazzoni 483627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 484627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 485b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 486b313ada8SThomas Petazzoni 487b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 488b313ada8SThomas Petazzoni 489b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 490b313ada8SThomas Petazzoni irq_domain_add_linear(node, (control >> 2) & 0x3ff, 491b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 492b313ada8SThomas Petazzoni 493627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 494b313ada8SThomas Petazzoni 495b313ada8SThomas Petazzoni #ifdef CONFIG_SMP 496b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 497b313ada8SThomas Petazzoni 498b313ada8SThomas Petazzoni /* 499b313ada8SThomas Petazzoni * Set the default affinity from all CPUs to the boot cpu. 500b313ada8SThomas Petazzoni * This is required since the MPIC doesn't limit several CPUs 501b313ada8SThomas Petazzoni * from acknowledging the same interrupt. 502b313ada8SThomas Petazzoni */ 503b313ada8SThomas Petazzoni cpumask_clear(irq_default_affinity); 504b313ada8SThomas Petazzoni cpumask_set_cpu(smp_processor_id(), irq_default_affinity); 505b313ada8SThomas Petazzoni 506b313ada8SThomas Petazzoni #endif 507b313ada8SThomas Petazzoni 50831f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 50931f614edSThomas Petazzoni 510*bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 511*bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 512*bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 513b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 514*bc69b8adSEzequiel Garcia } else { 515*bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 516*bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 517*bc69b8adSEzequiel Garcia } 518b313ada8SThomas Petazzoni 519b313ada8SThomas Petazzoni return 0; 520b313ada8SThomas Petazzoni } 521b313ada8SThomas Petazzoni 5229339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 523