diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2014-12-17 17:58:40 +0100 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2014-12-17 18:38:46 +0100 |
commit | 6f0bbad6b805cf2014eec54531dbe4ddb4867a91 (patch) | |
tree | 111322498c811383e22463057b76b59cd2848434 | |
parent | 4f10615fb1b80f0f959095c87c662089376d168c (diff) |
ARM: imx: vf610: extend MSCM with CPU2CPU interrupt support
Extend the MSCM driver with CPU2CPU interrupt support. Those
interrupts are mainly used for the external MCC kernel module
which uses the functionality to pass messages to the secondary
Cortex-M4 core.
In Timesys version (at least up to 1.06), the interrupt handling
was directly done in the MCC kernel module. However, because newer
kernels use virtual interrupt numbers, it is not possible to use
hardcoded hardware IRQ number by default. By requesting the
interupts within the MSCM driver and exporting some helper
functions.
-rw-r--r-- | arch/arm/mach-imx/mscm-vf610.c | 90 | ||||
-rw-r--r-- | include/linux/vf610_mscm.h | 11 |
2 files changed, 97 insertions, 4 deletions
diff --git a/arch/arm/mach-imx/mscm-vf610.c b/arch/arm/mach-imx/mscm-vf610.c index 6dc45b52b811..e36a7936b57e 100644 --- a/arch/arm/mach-imx/mscm-vf610.c +++ b/arch/arm/mach-imx/mscm-vf610.c @@ -11,25 +11,50 @@ #include <linux/cpu_pm.h> #include <linux/io.h> +#include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/irqchip/arm-gic.h> #include "common.h" #define MSCM_CPxNUM 0x4 + +#define MSCM_IRCP0IR 0x800 +#define MSCM_IRCP1IR 0x804 +#define MSCM_IRCPnIR(n) ((n) * 0x4 + MSCM_IRCP0IR) +#define MSCM_IRCPnIR_INT(n) (0x1 << (n)) +#define MSCM_IRCPGIR 0x820 + +#define MSCM_INTID_MASK 0x3 +#define MSCM_INTID(n) ((n) & MSCM_INTID_MASK) +#define MSCM_CPUTL(n) (((n) == 0 ? 1 : 2) << 16) + #define MSCM_IRSPRC(n) (0x880 + 2 * (n)) #define MSCM_IRSPRC_CPEN_MASK 0x3 #define MSCM_IRSPRC_NUM 112 +#define MSCM_CPU2CPU_NUM 4 + #define MSCM_IRQ_OFFSET 32 static void __iomem *mscm_base; +struct device_node *mscm_node; static u16 mscm_saved_irsprc[MSCM_IRSPRC_NUM]; static u16 cpu_id; +struct mscm_cpu2cpu_irq_data { + int intid; + int irq; + irq_handler_t handler; + void *priv; +}; + +static struct mscm_cpu2cpu_irq_data cpu2cpu_irq_data[MSCM_CPU2CPU_NUM]; + static int vf610_mscm_notifier(struct notifier_block *self, unsigned long cmd, void *v) { @@ -112,12 +137,69 @@ static const struct irq_domain_ops routable_irq_domain_ops = { .xlate = vf610_mscm_domain_xlate, }; -void __init vf610_mscm_init(void) +static irqreturn_t mscm_cpu2cpu_irq_handler(int irq, void *dev_id) +{ + irqreturn_t ret; + struct mscm_cpu2cpu_irq_data *data = dev_id; + + + ret = data->handler(data->intid, data->priv); + if (ret == IRQ_HANDLED) + writel(MSCM_IRCPnIR_INT(data->intid), mscm_base + MSCM_IRCPnIR(cpu_id)); + + return ret; +} + +int mscm_request_cpu2cpu_irq(unsigned int intid, irq_handler_t handler, + const char *name, void *priv) +{ + int irq; + struct mscm_cpu2cpu_irq_data *data; + + + if (intid >= MSCM_CPU2CPU_NUM) + return -EINVAL; + + irq = of_irq_get(mscm_node, intid); + if (irq < 0) + return irq; + + data = &cpu2cpu_irq_data[intid]; + data->intid = intid; + data->irq = irq; + data->handler = handler; + data->priv = priv; + + return request_irq(irq, mscm_cpu2cpu_irq_handler, 0, name, data); +} +EXPORT_SYMBOL(mscm_request_cpu2cpu_irq); + +void mscm_free_cpu2cpu_irq(unsigned int intid, void *priv) { - struct device_node *np; + struct mscm_cpu2cpu_irq_data *data; - np = of_find_compatible_node(NULL, NULL, "fsl,vf610-mscm"); - mscm_base = of_iomap(np, 0); + if (intid >= MSCM_CPU2CPU_NUM) + return; + + data = &cpu2cpu_irq_data[intid]; + + if (data->irq < 0) + return; + + free_irq(data->irq, data); +} +EXPORT_SYMBOL(mscm_free_cpu2cpu_irq); + +void mscm_trigger_cpu2cpu_irq(unsigned int intid, int cpuid) +{ + writel(MSCM_INTID(intid) | MSCM_CPUTL(cpuid), mscm_base + MSCM_IRCPGIR); +} +EXPORT_SYMBOL(mscm_trigger_cpu2cpu_irq); + +void __init vf610_mscm_init(void) +{ + mscm_node = of_find_compatible_node(NULL, NULL, "fsl,vf610-mscm"); + mscm_base = of_iomap(mscm_node, 0); if (!mscm_base) { WARN_ON(1); diff --git a/include/linux/vf610_mscm.h b/include/linux/vf610_mscm.h new file mode 100644 index 000000000000..881fbd772a95 --- /dev/null +++ b/include/linux/vf610_mscm.h @@ -0,0 +1,11 @@ +#ifndef __VF610_MSCM__ +#define __VF610_MSCM__ + +#include <linux/interrupt.h> + +int mscm_request_cpu2cpu_irq(unsigned int intid, irq_handler_t handler, + const char *name, void *priv); +void mscm_free_cpu2cpu_irq(unsigned int intid, void *priv); +void mscm_trigger_cpu2cpu_irq(unsigned int intid, int cpuid); + +#endif /* __VF610_MSCM__ */ |