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 469339d432SThomas Petazzoni 479339d432SThomas Petazzoni #define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 48bc69b8adSEzequiel Garcia #define ARMADA_375_PPI_CAUSE (0x10) 499339d432SThomas Petazzoni 509339d432SThomas Petazzoni #define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) 519339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) 529339d432SThomas Petazzoni #define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) 539339d432SThomas Petazzoni 549339d432SThomas Petazzoni #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28) 559339d432SThomas Petazzoni 569339d432SThomas Petazzoni #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5) 579339d432SThomas Petazzoni 585ec69017SThomas Petazzoni #define IPI_DOORBELL_START (0) 595ec69017SThomas Petazzoni #define IPI_DOORBELL_END (8) 605ec69017SThomas Petazzoni #define IPI_DOORBELL_MASK 0xFF 6131f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_START (16) 6231f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_NR (16) 6331f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_END (32) 6431f614edSThomas Petazzoni #define PCI_MSI_DOORBELL_MASK 0xFFFF0000 659339d432SThomas Petazzoni 669339d432SThomas Petazzoni static void __iomem *per_cpu_int_base; 679339d432SThomas Petazzoni static void __iomem *main_int_base; 689339d432SThomas Petazzoni static struct irq_domain *armada_370_xp_mpic_domain; 6931f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 7031f614edSThomas Petazzoni static struct irq_domain *armada_370_xp_msi_domain; 7131f614edSThomas Petazzoni static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); 7231f614edSThomas Petazzoni static DEFINE_MUTEX(msi_used_lock); 7331f614edSThomas Petazzoni static phys_addr_t msi_doorbell_addr; 7431f614edSThomas Petazzoni #endif 759339d432SThomas Petazzoni 769339d432SThomas Petazzoni /* 779339d432SThomas Petazzoni * In SMP mode: 789339d432SThomas Petazzoni * For shared global interrupts, mask/unmask global enable bit 791bf25e78SLinus Torvalds * For CPU interrupts, mask/unmask the calling CPU's bit 809339d432SThomas Petazzoni */ 819339d432SThomas Petazzoni static void armada_370_xp_irq_mask(struct irq_data *d) 829339d432SThomas Petazzoni { 839339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 849339d432SThomas Petazzoni 859339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 869339d432SThomas Petazzoni writel(hwirq, main_int_base + 879339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 889339d432SThomas Petazzoni else 899339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 909339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_MASK_OFFS); 919339d432SThomas Petazzoni } 929339d432SThomas Petazzoni 939339d432SThomas Petazzoni static void armada_370_xp_irq_unmask(struct irq_data *d) 949339d432SThomas Petazzoni { 959339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 969339d432SThomas Petazzoni 979339d432SThomas Petazzoni if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 989339d432SThomas Petazzoni writel(hwirq, main_int_base + 999339d432SThomas Petazzoni ARMADA_370_XP_INT_SET_ENABLE_OFFS); 1009339d432SThomas Petazzoni else 1019339d432SThomas Petazzoni writel(hwirq, per_cpu_int_base + 1029339d432SThomas Petazzoni ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 1039339d432SThomas Petazzoni } 1049339d432SThomas Petazzoni 10531f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 10631f614edSThomas Petazzoni 10731f614edSThomas Petazzoni static int armada_370_xp_alloc_msi(void) 10831f614edSThomas Petazzoni { 10931f614edSThomas Petazzoni int hwirq; 11031f614edSThomas Petazzoni 11131f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 11231f614edSThomas Petazzoni hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR); 11331f614edSThomas Petazzoni if (hwirq >= PCI_MSI_DOORBELL_NR) 11431f614edSThomas Petazzoni hwirq = -ENOSPC; 11531f614edSThomas Petazzoni else 11631f614edSThomas Petazzoni set_bit(hwirq, msi_used); 11731f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 11831f614edSThomas Petazzoni 11931f614edSThomas Petazzoni return hwirq; 12031f614edSThomas Petazzoni } 12131f614edSThomas Petazzoni 12231f614edSThomas Petazzoni static void armada_370_xp_free_msi(int hwirq) 12331f614edSThomas Petazzoni { 12431f614edSThomas Petazzoni mutex_lock(&msi_used_lock); 12531f614edSThomas Petazzoni if (!test_bit(hwirq, msi_used)) 12631f614edSThomas Petazzoni pr_err("trying to free unused MSI#%d\n", hwirq); 12731f614edSThomas Petazzoni else 12831f614edSThomas Petazzoni clear_bit(hwirq, msi_used); 12931f614edSThomas Petazzoni mutex_unlock(&msi_used_lock); 13031f614edSThomas Petazzoni } 13131f614edSThomas Petazzoni 13231f614edSThomas Petazzoni static int armada_370_xp_setup_msi_irq(struct msi_chip *chip, 13331f614edSThomas Petazzoni struct pci_dev *pdev, 13431f614edSThomas Petazzoni struct msi_desc *desc) 13531f614edSThomas Petazzoni { 13631f614edSThomas Petazzoni struct msi_msg msg; 137da343fc7SThomas Petazzoni int virq, hwirq; 13831f614edSThomas Petazzoni 1393930115eSAlexander Gordeev /* We support MSI, but not MSI-X */ 1403930115eSAlexander Gordeev if (desc->msi_attrib.is_msix) 1413930115eSAlexander Gordeev return -EINVAL; 1423930115eSAlexander Gordeev 14331f614edSThomas Petazzoni hwirq = armada_370_xp_alloc_msi(); 14431f614edSThomas Petazzoni if (hwirq < 0) 14531f614edSThomas Petazzoni return hwirq; 14631f614edSThomas Petazzoni 14731f614edSThomas Petazzoni virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq); 14831f614edSThomas Petazzoni if (!virq) { 14931f614edSThomas Petazzoni armada_370_xp_free_msi(hwirq); 15031f614edSThomas Petazzoni return -EINVAL; 15131f614edSThomas Petazzoni } 15231f614edSThomas Petazzoni 15331f614edSThomas Petazzoni irq_set_msi_desc(virq, desc); 15431f614edSThomas Petazzoni 15531f614edSThomas Petazzoni msg.address_lo = msi_doorbell_addr; 15631f614edSThomas Petazzoni msg.address_hi = 0; 15731f614edSThomas Petazzoni msg.data = 0xf00 | (hwirq + 16); 15831f614edSThomas Petazzoni 15931f614edSThomas Petazzoni write_msi_msg(virq, &msg); 16031f614edSThomas Petazzoni return 0; 16131f614edSThomas Petazzoni } 16231f614edSThomas Petazzoni 16331f614edSThomas Petazzoni static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip, 16431f614edSThomas Petazzoni unsigned int irq) 16531f614edSThomas Petazzoni { 16631f614edSThomas Petazzoni struct irq_data *d = irq_get_irq_data(irq); 167ff3c6645SNeil Greatorex unsigned long hwirq = d->hwirq; 168ff3c6645SNeil Greatorex 16931f614edSThomas Petazzoni irq_dispose_mapping(irq); 170ff3c6645SNeil Greatorex armada_370_xp_free_msi(hwirq); 17131f614edSThomas Petazzoni } 17231f614edSThomas Petazzoni 17331f614edSThomas Petazzoni static struct irq_chip armada_370_xp_msi_irq_chip = { 17431f614edSThomas Petazzoni .name = "armada_370_xp_msi_irq", 17531f614edSThomas Petazzoni .irq_enable = unmask_msi_irq, 17631f614edSThomas Petazzoni .irq_disable = mask_msi_irq, 17731f614edSThomas Petazzoni .irq_mask = mask_msi_irq, 17831f614edSThomas Petazzoni .irq_unmask = unmask_msi_irq, 17931f614edSThomas Petazzoni }; 18031f614edSThomas Petazzoni 18131f614edSThomas Petazzoni static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq, 18231f614edSThomas Petazzoni irq_hw_number_t hw) 18331f614edSThomas Petazzoni { 18431f614edSThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip, 18531f614edSThomas Petazzoni handle_simple_irq); 18631f614edSThomas Petazzoni set_irq_flags(virq, IRQF_VALID); 18731f614edSThomas Petazzoni 18831f614edSThomas Petazzoni return 0; 18931f614edSThomas Petazzoni } 19031f614edSThomas Petazzoni 19131f614edSThomas Petazzoni static const struct irq_domain_ops armada_370_xp_msi_irq_ops = { 19231f614edSThomas Petazzoni .map = armada_370_xp_msi_map, 19331f614edSThomas Petazzoni }; 19431f614edSThomas Petazzoni 19531f614edSThomas Petazzoni static int armada_370_xp_msi_init(struct device_node *node, 19631f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 19731f614edSThomas Petazzoni { 19831f614edSThomas Petazzoni struct msi_chip *msi_chip; 19931f614edSThomas Petazzoni u32 reg; 20031f614edSThomas Petazzoni int ret; 20131f614edSThomas Petazzoni 20231f614edSThomas Petazzoni msi_doorbell_addr = main_int_phys_base + 20331f614edSThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS; 20431f614edSThomas Petazzoni 20531f614edSThomas Petazzoni msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL); 20631f614edSThomas Petazzoni if (!msi_chip) 20731f614edSThomas Petazzoni return -ENOMEM; 20831f614edSThomas Petazzoni 20931f614edSThomas Petazzoni msi_chip->setup_irq = armada_370_xp_setup_msi_irq; 21031f614edSThomas Petazzoni msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq; 21131f614edSThomas Petazzoni msi_chip->of_node = node; 21231f614edSThomas Petazzoni 21331f614edSThomas Petazzoni armada_370_xp_msi_domain = 21431f614edSThomas Petazzoni irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR, 21531f614edSThomas Petazzoni &armada_370_xp_msi_irq_ops, 21631f614edSThomas Petazzoni NULL); 21731f614edSThomas Petazzoni if (!armada_370_xp_msi_domain) { 21831f614edSThomas Petazzoni kfree(msi_chip); 21931f614edSThomas Petazzoni return -ENOMEM; 22031f614edSThomas Petazzoni } 22131f614edSThomas Petazzoni 22231f614edSThomas Petazzoni ret = of_pci_msi_chip_add(msi_chip); 22331f614edSThomas Petazzoni if (ret < 0) { 22431f614edSThomas Petazzoni irq_domain_remove(armada_370_xp_msi_domain); 22531f614edSThomas Petazzoni kfree(msi_chip); 22631f614edSThomas Petazzoni return ret; 22731f614edSThomas Petazzoni } 22831f614edSThomas Petazzoni 22931f614edSThomas Petazzoni reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS) 23031f614edSThomas Petazzoni | PCI_MSI_DOORBELL_MASK; 23131f614edSThomas Petazzoni 23231f614edSThomas Petazzoni writel(reg, per_cpu_int_base + 23331f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_MSK_OFFS); 23431f614edSThomas Petazzoni 23531f614edSThomas Petazzoni /* Unmask IPI interrupt */ 23631f614edSThomas Petazzoni writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 23731f614edSThomas Petazzoni 23831f614edSThomas Petazzoni return 0; 23931f614edSThomas Petazzoni } 24031f614edSThomas Petazzoni #else 24131f614edSThomas Petazzoni static inline int armada_370_xp_msi_init(struct device_node *node, 24231f614edSThomas Petazzoni phys_addr_t main_int_phys_base) 24331f614edSThomas Petazzoni { 24431f614edSThomas Petazzoni return 0; 24531f614edSThomas Petazzoni } 24631f614edSThomas Petazzoni #endif 24731f614edSThomas Petazzoni 2489339d432SThomas Petazzoni #ifdef CONFIG_SMP 24919e61d41SArnaud Ebalard static DEFINE_RAW_SPINLOCK(irq_controller_lock); 25019e61d41SArnaud Ebalard 2519339d432SThomas Petazzoni static int armada_xp_set_affinity(struct irq_data *d, 2529339d432SThomas Petazzoni const struct cpumask *mask_val, bool force) 2539339d432SThomas Petazzoni { 2549339d432SThomas Petazzoni irq_hw_number_t hwirq = irqd_to_hwirq(d); 2558cc3cfc5SThomas Gleixner unsigned long reg, mask; 2569339d432SThomas Petazzoni int cpu; 2579339d432SThomas Petazzoni 2588cc3cfc5SThomas Gleixner /* Select a single core from the affinity mask which is online */ 2598cc3cfc5SThomas Gleixner cpu = cpumask_any_and(mask_val, cpu_online_mask); 2608cc3cfc5SThomas Gleixner mask = 1UL << cpu_logical_map(cpu); 2619339d432SThomas Petazzoni 2629339d432SThomas Petazzoni raw_spin_lock(&irq_controller_lock); 2639339d432SThomas Petazzoni reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2648cc3cfc5SThomas Gleixner reg = (reg & (~ARMADA_370_XP_INT_SOURCE_CPU_MASK)) | mask; 2659339d432SThomas Petazzoni writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq)); 2669339d432SThomas Petazzoni raw_spin_unlock(&irq_controller_lock); 2679339d432SThomas Petazzoni 2689339d432SThomas Petazzoni return 0; 2699339d432SThomas Petazzoni } 2709339d432SThomas Petazzoni #endif 2719339d432SThomas Petazzoni 2729339d432SThomas Petazzoni static struct irq_chip armada_370_xp_irq_chip = { 2739339d432SThomas Petazzoni .name = "armada_370_xp_irq", 2749339d432SThomas Petazzoni .irq_mask = armada_370_xp_irq_mask, 2759339d432SThomas Petazzoni .irq_mask_ack = armada_370_xp_irq_mask, 2769339d432SThomas Petazzoni .irq_unmask = armada_370_xp_irq_unmask, 2779339d432SThomas Petazzoni #ifdef CONFIG_SMP 2789339d432SThomas Petazzoni .irq_set_affinity = armada_xp_set_affinity, 2799339d432SThomas Petazzoni #endif 2809339d432SThomas Petazzoni }; 2819339d432SThomas Petazzoni 2829339d432SThomas Petazzoni static int armada_370_xp_mpic_irq_map(struct irq_domain *h, 2839339d432SThomas Petazzoni unsigned int virq, irq_hw_number_t hw) 2849339d432SThomas Petazzoni { 2859339d432SThomas Petazzoni armada_370_xp_irq_mask(irq_get_irq_data(virq)); 2861bf25e78SLinus Torvalds if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) 2871bf25e78SLinus Torvalds writel(hw, per_cpu_int_base + 2881bf25e78SLinus Torvalds ARMADA_370_XP_INT_CLEAR_MASK_OFFS); 2891bf25e78SLinus Torvalds else 2909339d432SThomas Petazzoni writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS); 2919339d432SThomas Petazzoni irq_set_status_flags(virq, IRQ_LEVEL); 2929339d432SThomas Petazzoni 2939339d432SThomas Petazzoni if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) { 2949339d432SThomas Petazzoni irq_set_percpu_devid(virq); 2959339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 2969339d432SThomas Petazzoni handle_percpu_devid_irq); 2979339d432SThomas Petazzoni 2989339d432SThomas Petazzoni } else { 2999339d432SThomas Petazzoni irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip, 3009339d432SThomas Petazzoni handle_level_irq); 3019339d432SThomas Petazzoni } 3029339d432SThomas Petazzoni set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); 3039339d432SThomas Petazzoni 3049339d432SThomas Petazzoni return 0; 3059339d432SThomas Petazzoni } 3069339d432SThomas Petazzoni 3079339d432SThomas Petazzoni #ifdef CONFIG_SMP 308ef37d337SThomas Petazzoni static void armada_mpic_send_doorbell(const struct cpumask *mask, 309ef37d337SThomas Petazzoni unsigned int irq) 3109339d432SThomas Petazzoni { 3119339d432SThomas Petazzoni int cpu; 3129339d432SThomas Petazzoni unsigned long map = 0; 3139339d432SThomas Petazzoni 3149339d432SThomas Petazzoni /* Convert our logical CPU mask into a physical one. */ 3159339d432SThomas Petazzoni for_each_cpu(cpu, mask) 3169339d432SThomas Petazzoni map |= 1 << cpu_logical_map(cpu); 3179339d432SThomas Petazzoni 3189339d432SThomas Petazzoni /* 3199339d432SThomas Petazzoni * Ensure that stores to Normal memory are visible to the 3209339d432SThomas Petazzoni * other CPUs before issuing the IPI. 3219339d432SThomas Petazzoni */ 3229339d432SThomas Petazzoni dsb(); 3239339d432SThomas Petazzoni 3249339d432SThomas Petazzoni /* submit softirq */ 3259339d432SThomas Petazzoni writel((map << 8) | irq, main_int_base + 3269339d432SThomas Petazzoni ARMADA_370_XP_SW_TRIG_INT_OFFS); 3279339d432SThomas Petazzoni } 3289339d432SThomas Petazzoni 329d7df84b3SThomas Petazzoni static void armada_xp_mpic_smp_cpu_init(void) 3309339d432SThomas Petazzoni { 331b73842b7SThomas Petazzoni u32 control; 332b73842b7SThomas Petazzoni int nr_irqs, i; 333b73842b7SThomas Petazzoni 334b73842b7SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 335b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 336b73842b7SThomas Petazzoni 337b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 338b73842b7SThomas Petazzoni writel(i, per_cpu_int_base + ARMADA_370_XP_INT_SET_MASK_OFFS); 339b73842b7SThomas 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 } 350d7df84b3SThomas Petazzoni 351d7df84b3SThomas Petazzoni static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, 352d7df84b3SThomas Petazzoni unsigned long action, void *hcpu) 353d7df84b3SThomas Petazzoni { 354d7df84b3SThomas Petazzoni if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) 355d7df84b3SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 356d7df84b3SThomas Petazzoni return NOTIFY_OK; 357d7df84b3SThomas Petazzoni } 358d7df84b3SThomas Petazzoni 359d7df84b3SThomas Petazzoni static struct notifier_block armada_370_xp_mpic_cpu_notifier = { 360d7df84b3SThomas Petazzoni .notifier_call = armada_xp_mpic_secondary_init, 361d7df84b3SThomas Petazzoni .priority = 100, 362d7df84b3SThomas Petazzoni }; 363d7df84b3SThomas Petazzoni 3649339d432SThomas Petazzoni #endif /* CONFIG_SMP */ 3659339d432SThomas Petazzoni 3669339d432SThomas Petazzoni static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { 3679339d432SThomas Petazzoni .map = armada_370_xp_mpic_irq_map, 3689339d432SThomas Petazzoni .xlate = irq_domain_xlate_onecell, 3699339d432SThomas Petazzoni }; 3709339d432SThomas Petazzoni 37131f614edSThomas Petazzoni #ifdef CONFIG_PCI_MSI 372bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) 3739b8cf779SEzequiel Garcia { 37431f614edSThomas Petazzoni u32 msimask, msinr; 37531f614edSThomas Petazzoni 37631f614edSThomas Petazzoni msimask = readl_relaxed(per_cpu_int_base + 37731f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 37831f614edSThomas Petazzoni & PCI_MSI_DOORBELL_MASK; 37931f614edSThomas Petazzoni 380c7f7bd4aSLior Amsalem writel(~msimask, per_cpu_int_base + 38131f614edSThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 38231f614edSThomas Petazzoni 38331f614edSThomas Petazzoni for (msinr = PCI_MSI_DOORBELL_START; 38431f614edSThomas Petazzoni msinr < PCI_MSI_DOORBELL_END; msinr++) { 38531f614edSThomas Petazzoni int irq; 38631f614edSThomas Petazzoni 38731f614edSThomas Petazzoni if (!(msimask & BIT(msinr))) 38831f614edSThomas Petazzoni continue; 38931f614edSThomas Petazzoni 39031f614edSThomas Petazzoni irq = irq_find_mapping(armada_370_xp_msi_domain, 39131f614edSThomas Petazzoni msinr - 16); 392bc69b8adSEzequiel Garcia 393bc69b8adSEzequiel Garcia if (is_chained) 394bc69b8adSEzequiel Garcia generic_handle_irq(irq); 395bc69b8adSEzequiel Garcia else 39631f614edSThomas Petazzoni handle_IRQ(irq, regs); 39731f614edSThomas Petazzoni } 39831f614edSThomas Petazzoni } 3999b8cf779SEzequiel Garcia #else 400bc69b8adSEzequiel Garcia static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {} 40131f614edSThomas Petazzoni #endif 40231f614edSThomas Petazzoni 403bc69b8adSEzequiel Garcia static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq, 404bc69b8adSEzequiel Garcia struct irq_desc *desc) 405bc69b8adSEzequiel Garcia { 406bc69b8adSEzequiel Garcia struct irq_chip *chip = irq_get_chip(irq); 407bc69b8adSEzequiel Garcia unsigned long irqmap, irqn; 408bc69b8adSEzequiel Garcia unsigned int cascade_irq; 409bc69b8adSEzequiel Garcia 410bc69b8adSEzequiel Garcia chained_irq_enter(chip, desc); 411bc69b8adSEzequiel Garcia 412bc69b8adSEzequiel Garcia irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE); 413bc69b8adSEzequiel Garcia 414bc69b8adSEzequiel Garcia if (irqmap & BIT(0)) { 415bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(NULL, true); 416bc69b8adSEzequiel Garcia irqmap &= ~BIT(0); 417bc69b8adSEzequiel Garcia } 418bc69b8adSEzequiel Garcia 419bc69b8adSEzequiel Garcia for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) { 420bc69b8adSEzequiel Garcia cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn); 421bc69b8adSEzequiel Garcia generic_handle_irq(cascade_irq); 422bc69b8adSEzequiel Garcia } 423bc69b8adSEzequiel Garcia 424bc69b8adSEzequiel Garcia chained_irq_exit(chip, desc); 425bc69b8adSEzequiel Garcia } 426bc69b8adSEzequiel Garcia 4278783dd3aSStephen Boyd static void __exception_irq_entry 4289b8cf779SEzequiel Garcia armada_370_xp_handle_irq(struct pt_regs *regs) 4299b8cf779SEzequiel Garcia { 4309b8cf779SEzequiel Garcia u32 irqstat, irqnr; 4319b8cf779SEzequiel Garcia 4329b8cf779SEzequiel Garcia do { 4339b8cf779SEzequiel Garcia irqstat = readl_relaxed(per_cpu_int_base + 4349b8cf779SEzequiel Garcia ARMADA_370_XP_CPU_INTACK_OFFS); 4359b8cf779SEzequiel Garcia irqnr = irqstat & 0x3FF; 4369b8cf779SEzequiel Garcia 4379b8cf779SEzequiel Garcia if (irqnr > 1022) 4389b8cf779SEzequiel Garcia break; 4399b8cf779SEzequiel Garcia 4409b8cf779SEzequiel Garcia if (irqnr > 1) { 4419b8cf779SEzequiel Garcia irqnr = irq_find_mapping(armada_370_xp_mpic_domain, 4429b8cf779SEzequiel Garcia irqnr); 4439b8cf779SEzequiel Garcia handle_IRQ(irqnr, regs); 4449b8cf779SEzequiel Garcia continue; 4459b8cf779SEzequiel Garcia } 4469b8cf779SEzequiel Garcia 4479b8cf779SEzequiel Garcia /* MSI handling */ 4489b8cf779SEzequiel Garcia if (irqnr == 1) 449bc69b8adSEzequiel Garcia armada_370_xp_handle_msi_irq(regs, false); 4509b8cf779SEzequiel Garcia 4519339d432SThomas Petazzoni #ifdef CONFIG_SMP 4529339d432SThomas Petazzoni /* IPI Handling */ 4539339d432SThomas Petazzoni if (irqnr == 0) { 4549339d432SThomas Petazzoni u32 ipimask, ipinr; 4559339d432SThomas Petazzoni 4569339d432SThomas Petazzoni ipimask = readl_relaxed(per_cpu_int_base + 4579339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS) 4585ec69017SThomas Petazzoni & IPI_DOORBELL_MASK; 4599339d432SThomas Petazzoni 460a6f089e9SLior Amsalem writel(~ipimask, per_cpu_int_base + 4619339d432SThomas Petazzoni ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); 4629339d432SThomas Petazzoni 4639339d432SThomas Petazzoni /* Handle all pending doorbells */ 4645ec69017SThomas Petazzoni for (ipinr = IPI_DOORBELL_START; 4655ec69017SThomas Petazzoni ipinr < IPI_DOORBELL_END; ipinr++) { 4669339d432SThomas Petazzoni if (ipimask & (0x1 << ipinr)) 4679339d432SThomas Petazzoni handle_IPI(ipinr, regs); 4689339d432SThomas Petazzoni } 4699339d432SThomas Petazzoni continue; 4709339d432SThomas Petazzoni } 4719339d432SThomas Petazzoni #endif 4729339d432SThomas Petazzoni 4739339d432SThomas Petazzoni } while (1); 4749339d432SThomas Petazzoni } 4759339d432SThomas Petazzoni 476b313ada8SThomas Petazzoni static int __init armada_370_xp_mpic_of_init(struct device_node *node, 477b313ada8SThomas Petazzoni struct device_node *parent) 478b313ada8SThomas Petazzoni { 479627dfcc2SThomas Petazzoni struct resource main_int_res, per_cpu_int_res; 480b73842b7SThomas Petazzoni int parent_irq, nr_irqs, i; 481b313ada8SThomas Petazzoni u32 control; 482b313ada8SThomas Petazzoni 483627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 484627dfcc2SThomas Petazzoni BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res)); 485b313ada8SThomas Petazzoni 486627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(main_int_res.start, 487627dfcc2SThomas Petazzoni resource_size(&main_int_res), 488627dfcc2SThomas Petazzoni node->full_name)); 489627dfcc2SThomas Petazzoni BUG_ON(!request_mem_region(per_cpu_int_res.start, 490627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res), 491627dfcc2SThomas Petazzoni node->full_name)); 492627dfcc2SThomas Petazzoni 493627dfcc2SThomas Petazzoni main_int_base = ioremap(main_int_res.start, 494627dfcc2SThomas Petazzoni resource_size(&main_int_res)); 495b313ada8SThomas Petazzoni BUG_ON(!main_int_base); 496627dfcc2SThomas Petazzoni 497627dfcc2SThomas Petazzoni per_cpu_int_base = ioremap(per_cpu_int_res.start, 498627dfcc2SThomas Petazzoni resource_size(&per_cpu_int_res)); 499b313ada8SThomas Petazzoni BUG_ON(!per_cpu_int_base); 500b313ada8SThomas Petazzoni 501b313ada8SThomas Petazzoni control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL); 502b73842b7SThomas Petazzoni nr_irqs = (control >> 2) & 0x3ff; 503b73842b7SThomas Petazzoni 504b73842b7SThomas Petazzoni for (i = 0; i < nr_irqs; i++) 505b73842b7SThomas Petazzoni writel(i, main_int_base + ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS); 506b313ada8SThomas Petazzoni 507b313ada8SThomas Petazzoni armada_370_xp_mpic_domain = 508b73842b7SThomas Petazzoni irq_domain_add_linear(node, nr_irqs, 509b313ada8SThomas Petazzoni &armada_370_xp_mpic_irq_ops, NULL); 510b313ada8SThomas Petazzoni 511627dfcc2SThomas Petazzoni BUG_ON(!armada_370_xp_mpic_domain); 512b313ada8SThomas Petazzoni 513b313ada8SThomas Petazzoni #ifdef CONFIG_SMP 514b313ada8SThomas Petazzoni armada_xp_mpic_smp_cpu_init(); 515b313ada8SThomas Petazzoni #endif 516b313ada8SThomas Petazzoni 51731f614edSThomas Petazzoni armada_370_xp_msi_init(node, main_int_res.start); 51831f614edSThomas Petazzoni 519bc69b8adSEzequiel Garcia parent_irq = irq_of_parse_and_map(node, 0); 520bc69b8adSEzequiel Garcia if (parent_irq <= 0) { 521bc69b8adSEzequiel Garcia irq_set_default_host(armada_370_xp_mpic_domain); 522b313ada8SThomas Petazzoni set_handle_irq(armada_370_xp_handle_irq); 523ef37d337SThomas Petazzoni #ifdef CONFIG_SMP 524ef37d337SThomas Petazzoni set_smp_cross_call(armada_mpic_send_doorbell); 525d7df84b3SThomas Petazzoni register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); 526ef37d337SThomas Petazzoni #endif 527bc69b8adSEzequiel Garcia } else { 528bc69b8adSEzequiel Garcia irq_set_chained_handler(parent_irq, 529bc69b8adSEzequiel Garcia armada_370_xp_mpic_handle_cascade_irq); 530bc69b8adSEzequiel Garcia } 531b313ada8SThomas Petazzoni 532b313ada8SThomas Petazzoni return 0; 533b313ada8SThomas Petazzoni } 534b313ada8SThomas Petazzoni 5359339d432SThomas Petazzoni IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init); 536