This adds support for Vybrids interrupt router for the shared
peripherals. The router is part of the MSCM (Miscellaneous System
Control Module).

Signed-off-by: Stefan Agner <[email protected]>
---
 arch/arm/mach-imx/Kconfig      |   4 ++
 arch/arm/mach-imx/Makefile     |   1 +
 arch/arm/mach-imx/common.h     |   1 +
 arch/arm/mach-imx/mach-vf610.c |   7 ++
 arch/arm/mach-imx/mscm-vf610.c | 141 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 154 insertions(+)
 create mode 100644 arch/arm/mach-imx/mscm-vf610.c

diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index e8627e0..2935972 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -58,6 +58,9 @@ config HAVE_IMX_SRC
        def_bool y if SMP
        select ARCH_HAS_RESET_CONTROLLER
 
+config HAVE_VF610_MSCM
+       bool
+
 config IMX_HAVE_IOMUX_V1
        bool
 
@@ -631,6 +634,7 @@ config SOC_IMX6SX
 
 config SOC_VF610
        bool "Vybrid Family VF610 support"
+       select HAVE_VF610_MSCM
        select ARM_GIC
        select PINCTRL_VF610
        select PL310_ERRATA_769419 if CACHE_L2X0
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index f5ac685..82b1159 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -108,6 +108,7 @@ obj-$(CONFIG_SOC_IMX50) += mach-imx50.o
 obj-$(CONFIG_SOC_IMX51) += mach-imx51.o
 obj-$(CONFIG_SOC_IMX53) += mach-imx53.o
 
+obj-$(CONFIG_HAVE_VF610_MSCM) += mscm-vf610.o
 obj-$(CONFIG_SOC_VF610) += clk-vf610.o mach-vf610.o
 
 obj-$(CONFIG_SOC_LS1021A) += mach-ls1021a.o
diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h
index 59ce8f3..6b5cad9 100644
--- a/arch/arm/mach-imx/common.h
+++ b/arch/arm/mach-imx/common.h
@@ -103,6 +103,7 @@ static inline void imx_smp_prepare(void) {}
 #endif
 void imx_src_init(void);
 void imx_gpc_init(void);
+void vf610_mscm_init(void);
 void imx_gpc_pre_suspend(bool arm_power_off);
 void imx_gpc_post_resume(void);
 void imx_gpc_mask_all(void);
diff --git a/arch/arm/mach-imx/mach-vf610.c b/arch/arm/mach-imx/mach-vf610.c
index c11ab6a..d1d200d 100644
--- a/arch/arm/mach-imx/mach-vf610.c
+++ b/arch/arm/mach-imx/mach-vf610.c
@@ -12,6 +12,12 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/cache-l2x0.h>
 
+static void __init vf610_init_irq(void)
+{
+       vf610_mscm_init();
+       irqchip_init();
+}
+
 static const char * const vf610_dt_compat[] __initconst = {
        "fsl,vf610",
        NULL,
@@ -20,5 +26,6 @@ static const char * const vf610_dt_compat[] __initconst = {
 DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF610 (Device Tree)")
        .l2c_aux_val    = 0,
        .l2c_aux_mask   = ~0,
+       .init_irq       = vf610_init_irq,
        .dt_compat      = vf610_dt_compat,
 MACHINE_END
diff --git a/arch/arm/mach-imx/mscm-vf610.c b/arch/arm/mach-imx/mscm-vf610.c
new file mode 100644
index 0000000..211ea10
--- /dev/null
+++ b/arch/arm/mach-imx/mscm-vf610.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014 Stefan Agner
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/irqchip/arm-nvic.h>
+#include "common.h"
+
+#define MSCM_CPxNUM            0x4
+#define MSCM_IRSPRC(n)         (0x880 + 2 * (n))
+#define MSCM_IRSPRC_CPEN_MASK  0x3
+
+#define MSCM_IRSPRC_NUM                112
+
+#ifdef CONFIG_ARM_GIC
+#define MSCM_IRQ_OFFSET                32
+#elif CONFIG_ARM_NVIC
+#define MSCM_IRQ_OFFSET                0
+#endif
+
+static void __iomem *mscm_base;
+static u16 mscm_saved_irsprc[MSCM_IRSPRC_NUM];
+static u16 cpu_id;
+
+static int vf610_mscm_notifier(struct notifier_block *self, unsigned long cmd,
+                              void *v)
+{
+       int i;
+
+       /* Only the primary (boot CPU) should do suspend/resume */
+       if (cpu_id > 0)
+               return NOTIFY_OK;
+
+       switch (cmd) {
+       case CPU_CLUSTER_PM_ENTER:
+               for (i = 0; i < MSCM_IRSPRC_NUM; i++)
+                       mscm_saved_irsprc[i] =
+                               readw_relaxed(mscm_base + MSCM_IRSPRC(i));
+               break;
+       case CPU_CLUSTER_PM_ENTER_FAILED:
+       case CPU_CLUSTER_PM_EXIT:
+               for (i = 0; i < MSCM_IRSPRC_NUM; i++)
+                       writew_relaxed(mscm_saved_irsprc[i],
+                                      mscm_base + MSCM_IRSPRC(i));
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block mscm_notifier_block = {
+       .notifier_call = vf610_mscm_notifier,
+};
+
+static int vf610_mscm_domain_map(struct irq_domain *d, unsigned int irq,
+                              irq_hw_number_t hw)
+{
+       u16 irsprc;
+
+       /* Do not handle non interrupt router IRQs */
+       if (hw < MSCM_IRQ_OFFSET)
+               return 0;
+
+       hw -= MSCM_IRQ_OFFSET;
+       irsprc = readw_relaxed(mscm_base + MSCM_IRSPRC(hw));
+       irsprc &= MSCM_IRSPRC_CPEN_MASK;
+
+       WARN_ON(irsprc);
+
+       writew_relaxed(0x1 << cpu_id, mscm_base + MSCM_IRSPRC(hw));
+
+       return 0;
+}
+
+static void vf610_mscm_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+       irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq;
+       u16 irsprc;
+
+       /* Do not handle non interrupt router IRQs */
+       if (hw < MSCM_IRQ_OFFSET)
+               return;
+
+       hw -= MSCM_IRQ_OFFSET;
+       irsprc = readw_relaxed(mscm_base + MSCM_IRSPRC(hw));
+       irsprc &= MSCM_IRSPRC_CPEN_MASK;
+
+       WARN_ON(irsprc & ~(0x1 << cpu_id));
+
+       writew_relaxed(0x1 << cpu_id, mscm_base + MSCM_IRSPRC(hw));
+}
+
+static int vf610_mscm_domain_xlate(struct irq_domain *d,
+                               struct device_node *controller,
+                               const u32 *intspec, unsigned int intsize,
+                               unsigned long *out_hwirq,
+                               unsigned int *out_type)
+{
+#ifdef CONFIG_ARM_GIC
+       *out_hwirq += 16;
+#endif
+       return 0;
+}
+static const struct irq_domain_ops routable_irq_domain_ops = {
+       .map = vf610_mscm_domain_map,
+       .unmap = vf610_mscm_domain_unmap,
+       .xlate = vf610_mscm_domain_xlate,
+};
+
+void __init vf610_mscm_init(void)
+{
+       struct device_node *np;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,vf610-mscm");
+       mscm_base = of_iomap(np, 0);
+
+       if (!mscm_base) {
+               WARN_ON(1);
+               return;
+       }
+
+       cpu_id = readl_relaxed(mscm_base + MSCM_CPxNUM);
+
+       /* Register MSCM as interrupt router */
+       register_routable_domain_ops(&routable_irq_domain_ops);
+
+       cpu_pm_register_notifier(&mscm_notifier_block);
+}
-- 
2.1.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to