Merge tag 'hwmon-for-linus-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / irqchip / irq-armada-370-xp.c
index 4387dae..0d3b0fe 100644 (file)
@@ -38,6 +38,8 @@
 /* Interrupt Controller Registers Map */
 #define ARMADA_370_XP_INT_SET_MASK_OFFS                (0x48)
 #define ARMADA_370_XP_INT_CLEAR_MASK_OFFS      (0x4C)
+#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS     (0x54)
+#define ARMADA_370_XP_INT_CAUSE_PERF(cpu)      (1 << cpu)
 
 #define ARMADA_370_XP_INT_CONTROL              (0x00)
 #define ARMADA_370_XP_INT_SET_ENABLE_OFFS      (0x30)
@@ -56,6 +58,7 @@
 #define ARMADA_370_XP_MAX_PER_CPU_IRQS         (28)
 
 #define ARMADA_370_XP_TIMER0_PER_CPU_IRQ       (5)
+#define ARMADA_370_XP_FABRIC_IRQ               (3)
 
 #define IPI_DOORBELL_START                      (0)
 #define IPI_DOORBELL_END                        (8)
@@ -77,6 +80,17 @@ static DEFINE_MUTEX(msi_used_lock);
 static phys_addr_t msi_doorbell_addr;
 #endif
 
+static inline bool is_percpu_irq(irq_hw_number_t irq)
+{
+       switch (irq) {
+       case ARMADA_370_XP_TIMER0_PER_CPU_IRQ:
+       case ARMADA_370_XP_FABRIC_IRQ:
+               return true;
+       default:
+               return false;
+       }
+}
+
 /*
  * In SMP mode:
  * For shared global interrupts, mask/unmask global enable bit
@@ -86,7 +100,7 @@ static void armada_370_xp_irq_mask(struct irq_data *d)
 {
        irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+       if (!is_percpu_irq(hwirq))
                writel(hwirq, main_int_base +
                                ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
        else
@@ -98,7 +112,7 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
 {
        irq_hw_number_t hwirq = irqd_to_hwirq(d);
 
-       if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+       if (!is_percpu_irq(hwirq))
                writel(hwirq, main_int_base +
                                ARMADA_370_XP_INT_SET_ENABLE_OFFS);
        else
@@ -281,20 +295,21 @@ static struct irq_chip armada_370_xp_irq_chip = {
 #ifdef CONFIG_SMP
        .irq_set_affinity = armada_xp_set_affinity,
 #endif
+       .flags          = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
 };
 
 static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
                                      unsigned int virq, irq_hw_number_t hw)
 {
        armada_370_xp_irq_mask(irq_get_irq_data(virq));
-       if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+       if (!is_percpu_irq(hw))
                writel(hw, per_cpu_int_base +
                        ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
        else
                writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
        irq_set_status_flags(virq, IRQ_LEVEL);
 
-       if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) {
+       if (is_percpu_irq(hw)) {
                irq_set_percpu_devid(virq);
                irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
                                        handle_percpu_devid_irq);
@@ -308,28 +323,6 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
        return 0;
 }
 
-#ifdef CONFIG_SMP
-static void armada_mpic_send_doorbell(const struct cpumask *mask,
-                                     unsigned int irq)
-{
-       int cpu;
-       unsigned long map = 0;
-
-       /* Convert our logical CPU mask into a physical one. */
-       for_each_cpu(cpu, mask)
-               map |= 1 << cpu_logical_map(cpu);
-
-       /*
-        * Ensure that stores to Normal memory are visible to the
-        * other CPUs before issuing the IPI.
-        */
-       dsb();
-
-       /* submit softirq */
-       writel((map << 8) | irq, main_int_base +
-               ARMADA_370_XP_SW_TRIG_INT_OFFS);
-}
-
 static void armada_xp_mpic_smp_cpu_init(void)
 {
        u32 control;
@@ -352,11 +345,44 @@ static void armada_xp_mpic_smp_cpu_init(void)
        writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 }
 
+static void armada_xp_mpic_perf_init(void)
+{
+       unsigned long cpuid = cpu_logical_map(smp_processor_id());
+
+       /* Enable Performance Counter Overflow interrupts */
+       writel(ARMADA_370_XP_INT_CAUSE_PERF(cpuid),
+              per_cpu_int_base + ARMADA_370_XP_INT_FABRIC_MASK_OFFS);
+}
+
+#ifdef CONFIG_SMP
+static void armada_mpic_send_doorbell(const struct cpumask *mask,
+                                     unsigned int irq)
+{
+       int cpu;
+       unsigned long map = 0;
+
+       /* Convert our logical CPU mask into a physical one. */
+       for_each_cpu(cpu, mask)
+               map |= 1 << cpu_logical_map(cpu);
+
+       /*
+        * Ensure that stores to Normal memory are visible to the
+        * other CPUs before issuing the IPI.
+        */
+       dsb();
+
+       /* submit softirq */
+       writel((map << 8) | irq, main_int_base +
+               ARMADA_370_XP_SW_TRIG_INT_OFFS);
+}
+
 static int armada_xp_mpic_secondary_init(struct notifier_block *nfb,
                                         unsigned long action, void *hcpu)
 {
-       if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+       if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
+               armada_xp_mpic_perf_init();
                armada_xp_mpic_smp_cpu_init();
+       }
 
        return NOTIFY_OK;
 }
@@ -369,8 +395,10 @@ static struct notifier_block armada_370_xp_mpic_cpu_notifier = {
 static int mpic_cascaded_secondary_init(struct notifier_block *nfb,
                                        unsigned long action, void *hcpu)
 {
-       if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+       if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) {
+               armada_xp_mpic_perf_init();
                enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
+       }
 
        return NOTIFY_OK;
 }
@@ -379,10 +407,9 @@ static struct notifier_block mpic_cascaded_cpu_notifier = {
        .notifier_call = mpic_cascaded_secondary_init,
        .priority = 100,
 };
-
 #endif /* CONFIG_SMP */
 
-static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+static const struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
        .map = armada_370_xp_mpic_irq_map,
        .xlate = irq_domain_xlate_onecell,
 };
@@ -588,9 +615,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 
        BUG_ON(!armada_370_xp_mpic_domain);
 
-#ifdef CONFIG_SMP
+       /* Setup for the boot CPU */
+       armada_xp_mpic_perf_init();
        armada_xp_mpic_smp_cpu_init();
-#endif
 
        armada_370_xp_msi_init(node, main_int_res.start);