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> 21bc69b8adSEzequiel Garcia #include <linux/irqchip/chained_irq.h> 22d7df84b3SThomas Petazzoni #include <linux/cpu.h> 239339d432SThomas Petazzoni #include <linux/io.h> 249339d432SThomas Petazzoni #include <linux/of_address.h> 259339d432SThomas Petazzoni #include <linux/of_irq.h> 2631f614edSThomas Petazzoni #include <linux/of_pci.h> 279339d432SThomas Petazzoni #include <linux/irqdomain.h> 2831f614edSThomas Petazzoni #include <linux/slab.h> 2931f614edSThomas Petazzoni #include <linux/msi.h> 309339d432SThomas Petazzoni #include <asm/mach/arch.h> 319339d432SThomas Petazzoni #include <asm/exception.h> 329339d432SThomas Petazzoni #include <asm/smp_plat.h> 339339d432SThomas Petazzoni #include <asm/mach/irq.h> 349339d432SThomas Petazzoni 359339d432SThomas Petazzoni #include "irqchip.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) 409339d432SThomas Petazzoni 419339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CONTROL (0x00) 429339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30) 439339d432SThomas Petazzoni #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34) 449339d432SThomas Petazzoni #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) 458cc3cfc5SThomas Gleixner #define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF 46758e8366SGrzegorz Jaszczyk #define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid) 479339d432SThomas Petazzoni 489339d432SThomas Petazzoni #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 49bc69b8adSEzequiel Garcia #define ARMADA_375_PPI_CAUSE (0x10) 509339d432SThomas Petazzoni 519339d432SThomas Petazzoni #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) 529339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) 539339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) 549339d432SThomas Petazzoni 559339d432SThomas Petazzoni #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 569339d432SThomas Petazzoni 579339d432SThomas Petazzoni #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) 589339d432SThomas Petazzoni 595ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 605ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 615ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 6231f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 6331f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 6431f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 6531f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 669339d432SThomas Petazzoni 679339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 689339d432SThomas Petazzoni static void __iomem *main_int_base; 699339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 7031f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 7131f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 7231f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 7331f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 7431f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 7531f614edSThomas Petazzoni #endif 769339d432SThomas Petazzoni 779339d432SThomas Petazzoni /* 789339d432SThomas Petazzoni * In SMP mode: 799339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 801bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 819339d432SThomas Petazzoni */ 829339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 839339d432SThomas Petazzoni { 849339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 859339d432SThomas Petazzoni 869339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 879339d432SThomas Petazzoni writel(hwirq, main_int_base + 889339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 899339d432SThomas Petazzoni else 909339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 919339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 929339d432SThomas Petazzoni } 939339d432SThomas Petazzoni 949339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 959339d432SThomas Petazzoni { 969339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 979339d432SThomas Petazzoni 989339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 999339d432SThomas Petazzoni writel(hwirq, main_int_base + 1009339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 1019339d432SThomas Petazzoni else 1029339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1039339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1049339d432SThomas Petazzoni } 1059339d432SThomas Petazzoni 10631f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 10731f614edSThomas Petazzoni 10831f614edSThomas Petazzoni static int armada_370_xp_alloc_msi(void) 10931f614edSThomas Petazzoni { 11031f614edSThomas Petazzoni int hwirq; 11131f614edSThomas Petazzoni 11231f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 11331f614edSThomas Petazzoni hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR); 11431f614edSThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) 11531f614edSThomas Petazzoni hwirq = -ENOSPC; 11631f614edSThomas Petazzoni else 11731f614edSThomas Petazzoni set_bit(hwirq, msi_used); 11831f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 11931f614edSThomas Petazzoni 12031f614edSThomas Petazzoni return hwirq; 12131f614edSThomas Petazzoni } 12231f614edSThomas Petazzoni 12331f614edSThomas Petazzoni static void armada_370_xp_free_msi(int hwirq) 12431f614edSThomas Petazzoni { 12531f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 12631f614edSThomas Petazzoni if (!test_bit(hwirq, msi_used)) 12731f614edSThomas Petazzoni pr_err("trying to free unused MSI#%d\n", hwirq); 12831f614edSThomas Petazzoni else 12931f614edSThomas Petazzoni clear_bit(hwirq, msi_used); 13031f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 13131f614edSThomas Petazzoni } 13231f614edSThomas Petazzoni 13331f614edSThomas Petazzoni static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, 13431f614edSThomas Petazzoni struct pci_dev *pdev, 13531f614edSThomas Petazzoni struct msi_desc *desc) 13631f614edSThomas Petazzoni { 13731f614edSThomas Petazzoni struct msi_msg msg; 138da343fc7SThomas Petazzoni int virq, hwirq; 13931f614edSThomas Petazzoni 1403930115eSAlexander Gordeev /* We support MSI, but not MSI-X */ 1413930115eSAlexander Gordeev if (desc->msi_attrib.is_msix) 1423930115eSAlexander Gordeev return -EINVAL; 1433930115eSAlexander Gordeev 14431f614edSThomas Petazzoni hwirq = armada_370_xp_alloc_msi(); 14531f614edSThomas Petazzoni if (hwirq < 0) 14631f614edSThomas Petazzoni return hwirq; 14731f614edSThomas Petazzoni 14831f614edSThomas Petazzoni virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq); 14931f614edSThomas Petazzoni if (!virq) { 15031f614edSThomas Petazzoni armada_370_xp_free_msi(hwirq); 15131f614edSThomas Petazzoni return -EINVAL; 15231f614edSThomas Petazzoni } 15331f614edSThomas Petazzoni 15431f614edSThomas Petazzoni irq_set_msi_desc(virq, desc); 15531f614edSThomas Petazzoni 15631f614edSThomas Petazzoni msg.address_lo = msi_doorbell_addr; 15731f614edSThomas Petazzoni msg.address_hi = 0; 15831f614edSThomas Petazzoni msg.data = 0xf00 | (hwirq + 16); 15931f614edSThomas Petazzoni 16031f614edSThomas Petazzoni write_msi_msg(virq, &msg); 16131f614edSThomas Petazzoni return 0; 16231f614edSThomas Petazzoni } 16331f614edSThomas Petazzoni 16431f614edSThomas Petazzoni static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, 16531f614edSThomas Petazzoni unsigned int irq) 16631f614edSThomas Petazzoni { 16731f614edSThomas Petazzoni struct irq_data *d = irq_get_irq_data(irq); 168ff3c6645SNeil Greatorex unsigned long hwirq = d->hwirq; 169ff3c6645SNeil Greatorex 17031f614edSThomas Petazzoni irq_dispose_mapping(irq); 171ff3c6645SNeil Greatorex armada_370_xp_free_msi(hwirq); 17231f614edSThomas Petazzoni } 17331f614edSThomas Petazzoni 17431f614edSThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 17531f614edSThomas Petazzoni .name = "armada_370_xp_msi_irq", 17631f614edSThomas Petazzoni .irq_enable = unmask_msi_irq, 17731f614edSThomas Petazzoni .irq_disable = mask_msi_irq, 17831f614edSThomas Petazzoni .irq_mask = mask_msi_irq, 17931f614edSThomas Petazzoni .irq_unmask = unmask_msi_irq, 18031f614edSThomas Petazzoni }; 18131f614edSThomas Petazzoni 18231f614edSThomas Petazzoni static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, 18331f614edSThomas Petazzoni irq_hw_number_t hw) 18431f614edSThomas Petazzoni { 18531f614edSThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, 18631f614edSThomas Petazzoni handle_simple_irq); 18731f614edSThomas Petazzoni set_irq_flags(virq, IRQF_VALID); 18831f614edSThomas Petazzoni 18931f614edSThomas Petazzoni return 0; 19031f614edSThomas Petazzoni } 19131f614edSThomas Petazzoni 19231f614edSThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { 19331f614edSThomas Petazzoni .map = armada_370_xp_msi_map, 19431f614edSThomas Petazzoni }; 19531f614edSThomas Petazzoni 19631f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 19731f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 19831f614edSThomas Petazzoni { 19931f614edSThomas Petazzoni struct msi_chip *msi_chip; 20031f614edSThomas Petazzoni u32 reg; 20131f614edSThomas Petazzoni int ret; 20231f614edSThomas Petazzoni 20331f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 20431f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 20531f614edSThomas Petazzoni 20631f614edSThomas Petazzoni msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL); 20731f614edSThomas Petazzoni if (!msi_chip) 20831f614edSThomas Petazzoni return -ENOMEM; 20931f614edSThomas Petazzoni 21031f614edSThomas Petazzoni msi_chip->setup_irq = armada_370_xp_setup_msi_irq; 21131f614edSThomas Petazzoni msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; 21231f614edSThomas Petazzoni msi_chip->of_node = node; 21331f614edSThomas Petazzoni 21431f614edSThomas Petazzoni armada_370_xp_msi_domain = 21531f614edSThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 21631f614edSThomas Petazzoni &armada_370_xp_msi_irq_ops, 21731f614edSThomas Petazzoni NULL); 21831f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 21931f614edSThomas Petazzoni kfree(msi_chip); 22031f614edSThomas Petazzoni return -ENOMEM; 22131f614edSThomas Petazzoni } 22231f614edSThomas Petazzoni 22331f614edSThomas Petazzoni ret = of_pci_msi_chip_add(msi_chip); 22431f614edSThomas Petazzoni if (ret < 0) { 22531f614edSThomas Petazzoni irq_domain_remove(armada_370_xp_msi_domain); 22631f614edSThomas Petazzoni kfree(msi_chip); 22731f614edSThomas Petazzoni return ret; 22831f614edSThomas Petazzoni } 22931f614edSThomas Petazzoni 23031f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 23131f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 23231f614edSThomas Petazzoni 23331f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 23431f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 23531f614edSThomas Petazzoni 23631f614edSThomas Petazzoni /* Unmask IPI interrupt */ 23731f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 23831f614edSThomas Petazzoni 23931f614edSThomas Petazzoni return 0; 24031f614edSThomas Petazzoni } 24131f614edSThomas Petazzoni #else 24231f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 24331f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 24431f614edSThomas Petazzoni { 24531f614edSThomas Petazzoni return 0; 24631f614edSThomas Petazzoni } 24731f614edSThomas Petazzoni #endif 24831f614edSThomas Petazzoni 2499339d432SThomas Petazzoni #ifdef CONFIG_SMP 25019e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 25119e61d41SArnaud Ebalard 2529339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 2539339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 2549339d432SThomas Petazzoni { 2559339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 2568cc3cfc5SThomas Gleixner unsigned long reg, mask; 2579339d432SThomas Petazzoni int cpu; 2589339d432SThomas Petazzoni 2598cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 2608cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 2618cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 2629339d432SThomas Petazzoni 2639339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 2649339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2658cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 2669339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2679339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 2689339d432SThomas Petazzoni 2699339d432SThomas Petazzoni return 0; 2709339d432SThomas Petazzoni } 2719339d432SThomas Petazzoni #endif 2729339d432SThomas Petazzoni 2739339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 2749339d432SThomas Petazzoni .name = "armada_370_xp_irq", 2759339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 2769339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 2779339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 2789339d432SThomas Petazzoni #ifdef CONFIG_SMP 2799339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 2809339d432SThomas Petazzoni #endif 2819339d432SThomas Petazzoni }; 2829339d432SThomas Petazzoni 2839339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 2849339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 2859339d432SThomas Petazzoni { 2869339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 2871bf25e78SLinus Torvalds if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 2881bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 2891bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 2901bf25e78SLinus Torvalds else 2919339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 2929339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 2939339d432SThomas Petazzoni 2949339d432SThomas Petazzoni if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { 2959339d432SThomas Petazzoni irq_set_percpu_devid(virq); 2969339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 2979339d432SThomas Petazzoni handle_percpu_devid_irq); 2989339d432SThomas Petazzoni 2999339d432SThomas Petazzoni } else { 3009339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3019339d432SThomas Petazzoni handle_level_irq); 3029339d432SThomas Petazzoni } 3039339d432SThomas Petazzoni set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 3049339d432SThomas Petazzoni 3059339d432SThomas Petazzoni return 0; 3069339d432SThomas Petazzoni } 3079339d432SThomas Petazzoni 3089339d432SThomas Petazzoni #ifdef CONFIG_SMP 309ef37d337SThomas Petazzoni static void armada_mpic_send_doorbell(const struct cpumask *mask, 310ef37d337SThomas Petazzoni unsigned int irq) 3119339d432SThomas Petazzoni { 3129339d432SThomas Petazzoni int cpu; 3139339d432SThomas Petazzoni unsigned long map = 0; 3149339d432SThomas Petazzoni 3159339d432SThomas Petazzoni /* Convert our logical CPU mask into a physical one. */ 3169339d432SThomas Petazzoni for_each_cpu(cpu, mask) 3179339d432SThomas Petazzoni map |= 1 << cpu_logical_map(cpu); 3189339d432SThomas Petazzoni 3199339d432SThomas Petazzoni /* 3209339d432SThomas Petazzoni * Ensure that stores to Normal memory are visible to the 3219339d432SThomas Petazzoni * other CPUs before issuing the IPI. 3229339d432SThomas Petazzoni */ 3239339d432SThomas Petazzoni dsb(); 3249339d432SThomas Petazzoni 3259339d432SThomas Petazzoni /* submit softirq */ 3269339d432SThomas Petazzoni writel((map << 8) | irq, main_int_base + 3279339d432SThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS); 3289339d432SThomas Petazzoni } 3299339d432SThomas Petazzoni 330d7df84b3SThomas Petazzoni static void armada_xp_mpic_smp_cpu_init(void) 3319339d432SThomas Petazzoni { 332b73842b7SThomas Petazzoni u32 control; 333b73842b7SThomas Petazzoni int nr_irqs, i; 334b73842b7SThomas Petazzoni 335b73842b7SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 336b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 337b73842b7SThomas Petazzoni 338b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 339b73842b7SThomas Petazzoni writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 340b73842b7SThomas Petazzoni 3419339d432SThomas Petazzoni /* Clear pending IPIs */ 3429339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 3439339d432SThomas Petazzoni 3449339d432SThomas Petazzoni /* Enable first 8 IPIs */ 3455ec69017SThomas Petazzoni writel(IPI_DOORBELL_MASK, per_cpu_int_base + 3469339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 3479339d432SThomas Petazzoni 3489339d432SThomas Petazzoni /* Unmask IPI interrupt */ 3499339d432SThomas Petazzoni writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 3509339d432SThomas Petazzoni } 351d7df84b3SThomas Petazzoni 352d7df84b3SThomas Petazzoni static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, 353d7df84b3SThomas Petazzoni unsigned long action, void *hcpu) 354d7df84b3SThomas Petazzoni { 355d7df84b3SThomas Petazzoni if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 356d7df84b3SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 357d7df84b3SThomas Petazzoni return NOTIFY_OK; 358d7df84b3SThomas Petazzoni } 359d7df84b3SThomas Petazzoni 360d7df84b3SThomas Petazzoni static struct notifier_block armada_370_xp_mpic_cpu_notifier = { 361d7df84b3SThomas Petazzoni .notifier_call = armada_xp_mpic_secondary_init, 362d7df84b3SThomas Petazzoni .priority = 100, 363d7df84b3SThomas Petazzoni }; 364d7df84b3SThomas Petazzoni 3659339d432SThomas Petazzoni #endif /* CONFIG_SMP */ 3669339d432SThomas Petazzoni 3679339d432SThomas Petazzoni static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 3689339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 3699339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 3709339d432SThomas Petazzoni }; 3719339d432SThomas Petazzoni 37231f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 373bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 3749b8cf779SEzequiel Garcia { 37531f614edSThomas Petazzoni u32 msimask, msinr; 37631f614edSThomas Petazzoni 37731f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 37831f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 37931f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 38031f614edSThomas Petazzoni 381c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 38231f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 38331f614edSThomas Petazzoni 38431f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 38531f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 38631f614edSThomas Petazzoni int irq; 38731f614edSThomas Petazzoni 38831f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 38931f614edSThomas Petazzoni continue; 39031f614edSThomas Petazzoni 391e89c6a06SMarc Zyngier if (is_chained) { 39231f614edSThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_domain, 39331f614edSThomas Petazzoni msinr - 16); 394bc69b8adSEzequiel Garcia generic_handle_irq(irq); 395e89c6a06SMarc Zyngier } else { 396e89c6a06SMarc Zyngier irq = msinr - 16; 397e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_msi_domain, 398e89c6a06SMarc Zyngier irq, regs); 399e89c6a06SMarc Zyngier } 40031f614edSThomas Petazzoni } 40131f614edSThomas Petazzoni } 4029b8cf779SEzequiel Garcia #else 403bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 40431f614edSThomas Petazzoni #endif 40531f614edSThomas Petazzoni 406bc69b8adSEzequiel Garcia static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, 407bc69b8adSEzequiel Garcia struct irq_desc *desc) 408bc69b8adSEzequiel Garcia { 409bc69b8adSEzequiel Garcia struct irq_chip *chip = irq_get_chip(irq); 410758e8366SGrzegorz Jaszczyk unsigned long irqmap, irqn, irqsrc, cpuid; 411bc69b8adSEzequiel Garcia unsigned int cascade_irq; 412bc69b8adSEzequiel Garcia 413bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 414bc69b8adSEzequiel Garcia 415bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 416758e8366SGrzegorz Jaszczyk cpuid = cpu_logical_map(smp_processor_id()); 417bc69b8adSEzequiel Garcia 418bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 419758e8366SGrzegorz Jaszczyk irqsrc = readl_relaxed(main_int_base + 420758e8366SGrzegorz Jaszczyk ARMADA_370_XP_INT_SOURCE_CTL(irqn)); 421758e8366SGrzegorz Jaszczyk 422758e8366SGrzegorz Jaszczyk /* Check if the interrupt is not masked on current CPU. 423758e8366SGrzegorz Jaszczyk * Test IRQ (0-1) and FIQ (8-9) mask bits. 424758e8366SGrzegorz Jaszczyk */ 425758e8366SGrzegorz Jaszczyk if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid))) 426758e8366SGrzegorz Jaszczyk continue; 427758e8366SGrzegorz Jaszczyk 428758e8366SGrzegorz Jaszczyk if (irqn == 1) { 429758e8366SGrzegorz Jaszczyk armada_370_xp_handle_msi_irq(NULL, true); 430758e8366SGrzegorz Jaszczyk continue; 431758e8366SGrzegorz Jaszczyk } 432758e8366SGrzegorz Jaszczyk 433bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 434bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 435bc69b8adSEzequiel Garcia } 436bc69b8adSEzequiel Garcia 437bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 438bc69b8adSEzequiel Garcia } 439bc69b8adSEzequiel Garcia 4408783dd3aSStephen Boyd static void __exception_irq_entry 4419b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 4429b8cf779SEzequiel Garcia { 4439b8cf779SEzequiel Garcia u32 irqstat, irqnr; 4449b8cf779SEzequiel Garcia 4459b8cf779SEzequiel Garcia do { 4469b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 4479b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 4489b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 4499b8cf779SEzequiel Garcia 4509b8cf779SEzequiel Garcia if (irqnr > 1022) 4519b8cf779SEzequiel Garcia break; 4529b8cf779SEzequiel Garcia 4539b8cf779SEzequiel Garcia if (irqnr > 1) { 454e89c6a06SMarc Zyngier handle_domain_irq(armada_370_xp_mpic_domain, 455e89c6a06SMarc Zyngier irqnr, regs); 4569b8cf779SEzequiel Garcia continue; 4579b8cf779SEzequiel Garcia } 4589b8cf779SEzequiel Garcia 4599b8cf779SEzequiel Garcia /* MSI handling */ 4609b8cf779SEzequiel Garcia if (irqnr == 1) 461bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 4629b8cf779SEzequiel Garcia 4639339d432SThomas Petazzoni #ifdef CONFIG_SMP 4649339d432SThomas Petazzoni /* IPI Handling */ 4659339d432SThomas Petazzoni if (irqnr == 0) { 4669339d432SThomas Petazzoni u32 ipimask, ipinr; 4679339d432SThomas Petazzoni 4689339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 4699339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 4705ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 4719339d432SThomas Petazzoni 472a6f089e9SLior Amsalem writel(~ipimask, per_cpu_int_base + 4739339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 4749339d432SThomas Petazzoni 4759339d432SThomas Petazzoni /* Handle all pending doorbells */ 4765ec69017SThomas Petazzoni for (ipinr = IPI_DOORBELL_START; 4775ec69017SThomas Petazzoni ipinr < IPI_DOORBELL_END; ipinr++) { 4789339d432SThomas Petazzoni if (ipimask & (0x1 << ipinr)) 4799339d432SThomas Petazzoni handle_IPI(ipinr, regs); 4809339d432SThomas Petazzoni } 4819339d432SThomas Petazzoni continue; 4829339d432SThomas Petazzoni } 4839339d432SThomas Petazzoni #endif 4849339d432SThomas Petazzoni 4859339d432SThomas Petazzoni } while (1); 4869339d432SThomas Petazzoni } 4879339d432SThomas Petazzoni 488b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 489b313ada8SThomas Petazzoni struct device_node *parent) 490b313ada8SThomas Petazzoni { 491627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 492b73842b7SThomas Petazzoni int parent_irq, nr_irqs, i; 493b313ada8SThomas Petazzoni u32 control; 494b313ada8SThomas Petazzoni 495627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 496627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 497b313ada8SThomas Petazzoni 498627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 499627dfcc2SThomas Petazzoni resource_size(&main_int_res), 500627dfcc2SThomas Petazzoni node->full_name)); 501627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 502627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 503627dfcc2SThomas Petazzoni node->full_name)); 504627dfcc2SThomas Petazzoni 505627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 506627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 507b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 508627dfcc2SThomas Petazzoni 509627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 510627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 511b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 512b313ada8SThomas Petazzoni 513b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 514b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 515b73842b7SThomas Petazzoni 516b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 517b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 518b313ada8SThomas Petazzoni 519b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 520b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 521b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 522b313ada8SThomas Petazzoni 523627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 524b313ada8SThomas Petazzoni 525b313ada8SThomas Petazzoni #ifdef CONFIG_SMP 526b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 527b313ada8SThomas Petazzoni #endif 528b313ada8SThomas Petazzoni 52931f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 53031f614edSThomas Petazzoni 531bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 532bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 533bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 534b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 535ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 536ef37d337SThomas Petazzoni set_smp_cross_call(armada_mpic_send_doorbell); 537d7df84b3SThomas Petazzoni register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); 538ef37d337SThomas Petazzoni #endif 539bc69b8adSEzequiel Garcia } else { 540bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 541bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 542bc69b8adSEzequiel Garcia } 543b313ada8SThomas Petazzoni 544b313ada8SThomas Petazzoni return 0; 545b313ada8SThomas Petazzoni } 546b313ada8SThomas Petazzoni 5479339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 548