summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-imx/Kconfig5
-rw-r--r--arch/arm/mach-imx/Makefile1
-rw-r--r--arch/arm/mach-imx/mcc_imx6sx.c148
-rw-r--r--arch/arm/mach-imx/mcc_linux.c281
-rw-r--r--include/linux/mcc_imx6sx.h43
-rw-r--r--include/linux/mcc_linux.h53
6 files changed, 531 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 7875adb8c7f4..65d5476679c6 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -81,6 +81,10 @@ config HAVE_IMX_MMDC
config HAVE_IMX_SRC
def_bool y if SMP
+config HAVE_IMX_MCC
+ select IMX_SEMA4
+ bool
+
config IMX_HAVE_IOMUX_V1
bool
@@ -860,6 +864,7 @@ config SOC_IMX6SX
select HAVE_IMX_SRC
select MIGHT_HAVE_PCI
select PCI_DOMAINS if PCI
+ select HAVE_IMX_MCC
select PINCTRL
select PINCTRL_IMX6SX
select PL310_ERRATA_588369 if CACHE_PL310
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index d56ea8b34182..ed67b70a7a24 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
+obj-$(CONFIG_HAVE_IMX_MCC) += mcc_api.o mcc_common.o mcc_linux.o mcc_imx6sx.o
AFLAGS_headsmp.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-imx/mcc_imx6sx.c b/arch/arm/mach-imx/mcc_imx6sx.c
new file mode 100644
index 000000000000..4e22ffc6f6f4
--- /dev/null
+++ b/arch/arm/mach-imx/mcc_imx6sx.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Freescale IMX Linux-specific MCC implementation.
+ * iMX6sx-specific MCC library functions.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+#include <linux/mcc_imx6sx.h>
+#include <linux/mcc_linux.h>
+
+/*!
+ * \brief This function returns the core number
+ *
+ * \return int
+ */
+unsigned int _psp_core_num(void)
+{
+ return 0;
+}
+
+/*
+ * This field contains CPU-to-CPU interrupt vector numbers
+ * for all device cores.
+ */
+static const unsigned int mcc_cpu_to_cpu_vectors[] = {INT_CPU_TO_CPU_MU_A2M,
+ INT_CPU_TO_CPU_MU_M2A};
+
+/*!
+ * \brief This function gets the CPU-to-CPU vector num for the particular core.
+ *
+ * Platform-specific inter-CPU vector numbers for each core are defined in the
+ * mcc_cpu_to_cpu_vectors[] field.
+ *
+ * \param[in] core Core number.
+ *
+ * \return vector number for the particular core
+ * \return MCC_VECTOR_NUMBER_INVALID (vector number for the particular core
+ * number not found)
+ */
+unsigned int mcc_get_cpu_to_cpu_vector(unsigned int core)
+{
+ u32 val;
+
+ val = sizeof(mcc_cpu_to_cpu_vectors)/sizeof(mcc_cpu_to_cpu_vectors[0]);
+ if (core < val)
+ return mcc_cpu_to_cpu_vectors[core];
+
+ return MCC_VECTOR_NUMBER_INVALID;
+}
+
+/*!
+ * \brief This function clears the CPU-to-CPU int flag for the particular core.
+ *
+ * Implementation is platform-specific.
+ *
+ * \param[in] core Core number.
+ */
+void mcc_clear_cpu_to_cpu_interrupt(unsigned int core)
+{
+ u32 val;
+
+ regmap_read(imx_mu_reg, MU_ASR, &val);
+ /* write 1 to BIT31 to clear the bit31(GIP3) of MU_ASR */
+ val = val | (1 << 31);
+ regmap_write(imx_mu_reg, MU_ASR, val);
+}
+
+/*!
+ * \brief This function triggers the CPU-to-CPU interrupt.
+ *
+ * Platform-specific software triggering the inter-CPU interrupts.
+ */
+void mcc_triger_cpu_to_cpu_interrupt(void)
+{
+ int i = 0;
+ u32 val;
+
+ regmap_read(imx_mu_reg, MU_ACR, &val);
+
+ if ((val & BIT(19)) != 0) {
+ do {
+ regmap_read(imx_mu_reg, MU_ACR, &val);
+ msleep(1);
+ } while (((val & BIT(19)) > 0) && (i++ < 100));
+ }
+
+ if ((val & BIT(19)) == 0)
+ /* Enable the bit19(GIR3) of MU_ACR */
+ regmap_update_bits(imx_mu_reg, MU_ACR, BIT(19), BIT(19));
+ else
+ pr_info("mcc int still be triggered after %d ms polling!\n", i);
+}
+
+/*!
+ * \brief This function disable the CPU-to-CPU interrupt.
+ *
+ * Platform-specific software disable the inter-CPU interrupts.
+ */
+int imx_mcc_bsp_int_disable(unsigned int vector_number)
+{
+ u32 val;
+
+ if (vector_number == INT_CPU_TO_CPU_MU_A2M) {
+ /* Disable the bit31(GIE3) of MU_ACR */
+ regmap_update_bits(imx_mu_reg, MU_ACR, BIT(31), 0);
+ /* flush */
+ regmap_read(imx_mu_reg, MU_ACR, &val);
+ } else
+ pr_err("ERR:wrong vector number in mcc.\n");
+
+ return 0;
+}
+
+/*!
+ * \brief This function enable the CPU-to-CPU interrupt.
+ *
+ * Platform-specific software enable the inter-CPU interrupts.
+ */
+int imx_mcc_bsp_int_enable(unsigned int vector_number)
+{
+ u32 val;
+
+ if (vector_number == INT_CPU_TO_CPU_MU_A2M) {
+ /* Enable the bit31(GIE3) of MU_ACR */
+ regmap_update_bits(imx_mu_reg, MU_ACR, BIT(31), BIT(31));
+ /* flush */
+ regmap_read(imx_mu_reg, MU_ACR, &val);
+ return 0;
+ } else {
+ pr_err("ERR:wrong vector number in mcc.\n");
+ return -EINVAL;
+ }
+}
diff --git a/arch/arm/mach-imx/mcc_linux.c b/arch/arm/mach-imx/mcc_linux.c
new file mode 100644
index 000000000000..551cc12ed4a5
--- /dev/null
+++ b/arch/arm/mach-imx/mcc_linux.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Freescale IMX Linux-specific MCC implementation.
+ * Linux-specific MCC library functions
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/imx_sema4.h>
+#include "mcc_config.h"
+#include <linux/mcc_common.h>
+#include <linux/mcc_api.h>
+#include <linux/mcc_imx6sx.h>
+#include <linux/mcc_linux.h>
+
+/* Global variables */
+struct regmap *imx_mu_reg;
+unsigned long mcc_shm_offset;
+static struct imx_sema4_mutex *shm_ptr;
+static unsigned int cpu_to_cpu_isr_vector = MCC_VECTOR_NUMBER_INVALID;
+
+unsigned int imx_mcc_buffer_freed = 0, imx_mcc_buffer_queued = 0;
+DECLARE_WAIT_QUEUE_HEAD(buffer_freed_wait_queue); /* Used for blocking send */
+DECLARE_WAIT_QUEUE_HEAD(buffer_queued_wait_queue); /* Used for blocking recv */
+
+/*!
+ * \brief This function is the CPU-to-CPU interrupt handler.
+ *
+ * Each core can interrupt the other. There are two logical signals:
+ * \n - Receive data available for (Node,Port) - signaled when a buffer
+ * is queued to a Receive Data Queue.
+ * \n - Buffer available - signaled when a buffer is queued to the Free
+ * Buffer Queue.
+ * \n It is possible that several signals can occur while one interrupt
+ * is being processed.
+ * Therefore, a Receive Signal Queue of received signals is also required
+ * - one for each core.
+ * The interrupting core queues to the tail and the interrupted core pulls
+ * from the head.
+ * For a circular file, no semaphore is required since only the sender
+ * modifies the tail and only the receiver modifies the head.
+ *
+ * \param[in] param Pointer to data being passed to the ISR.
+ */
+static irqreturn_t mcc_cpu_to_cpu_isr(int irq, void *param)
+{
+ MCC_SIGNAL serviced_signal;
+
+ /*
+ * Try to lock the core mutex. If successfully locked, perform
+ * mcc_dequeue_signal(), release the gate and finally clear the
+ * interrupt flag. If trylock fails (HW semaphore already locked
+ * by another core), do not clear the interrupt flag – this
+ * way the CPU-to-CPU isr is re-issued again until the HW semaphore
+ * is locked. Higher priority ISRs will be serviced while issued at
+ * the time we are waiting for the unlocked gate. To prevent trylog
+ * failure due to core mutex currently locked by our own core(a task),
+ * the cpu-to-cpu isr is temporarily disabled when mcc_get_semaphore()
+ * is called and re-enabled again when mcc_release_semaphore()
+ * is issued.
+ */
+ if (SEMA4_A9_LOCK == imx_sema4_mutex_trylock(shm_ptr)) {
+ while (MCC_SUCCESS == mcc_dequeue_signal(MCC_CORE_NUMBER,
+ &serviced_signal)) {
+ if ((serviced_signal.type == BUFFER_QUEUED) &&
+ (serviced_signal.destination.core == MCC_CORE_NUMBER)) {
+ /*
+ * Unblock receiver, in case of asynchronous
+ * communication
+ */
+ imx_mcc_buffer_queued = 1;
+ wake_up(&buffer_queued_wait_queue);
+ } else if (serviced_signal.type == BUFFER_FREED) {
+ /* Unblock sender, in case of asynchronous
+ * communication
+ */
+ imx_mcc_buffer_freed = 1;
+ wake_up(&buffer_freed_wait_queue);
+ }
+ }
+
+ /* Clear the interrupt flag */
+ mcc_clear_cpu_to_cpu_interrupt(MCC_CORE_NUMBER);
+
+ /* Unlocks the core mutex */
+ imx_sema4_mutex_unlock(shm_ptr);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * \brief This function initializes the hw semaphore (SEMA4).
+ *
+ * Calls core-mutex driver to create a core mutex.
+ *
+ * \param[in] sem_num SEMA4 gate number.
+ */
+int mcc_init_semaphore(unsigned int sem_num)
+{
+ /* Create a core mutex */
+ shm_ptr = imx_sema4_mutex_create(0, sem_num);
+
+ if (NULL == shm_ptr)
+ return MCC_ERR_SEMAPHORE;
+ else
+ return MCC_SUCCESS;
+}
+
+/*!
+ * \brief This function de-initializes the hw semaphore (SEMA4).
+ *
+ * Calls core-mutex driver to destroy a core mutex.
+ *
+ * \param[in] sem_num SEMA4 gate number.
+ */
+int mcc_deinit_semaphore(unsigned int sem_num)
+{
+ /* Destroy the core mutex */
+ if (0 == imx_sema4_mutex_destroy(shm_ptr))
+ return MCC_SUCCESS;
+ else
+ return MCC_ERR_SEMAPHORE;
+}
+
+/*!
+ * \brief This function locks the specified core mutex.
+ *
+ * Calls core-mutex driver to lock the core mutex.
+ *
+ */
+int mcc_get_semaphore(void)
+{
+ if (imx_mcc_bsp_int_disable(cpu_to_cpu_isr_vector)) {
+ pr_err("ERR:failed to disable mcc int.\n");
+ return MCC_ERR_SEMAPHORE;
+ }
+
+ if (0 == imx_sema4_mutex_lock(shm_ptr)) {
+ return MCC_SUCCESS;
+ } else {
+ if (imx_mcc_bsp_int_enable(cpu_to_cpu_isr_vector)) {
+ pr_err("ERR:failed to enable mcc int.\n");
+ return MCC_ERR_INT;
+ }
+ return MCC_ERR_SEMAPHORE;
+ }
+}
+
+/*!
+ * \brief This function unlocks the specified core mutex.
+ *
+ * Calls core-mutex driver to unlock the core mutex.
+ *
+ */
+int mcc_release_semaphore(void)
+{
+ if (0 == imx_sema4_mutex_unlock(shm_ptr)) {
+ /*
+ * Enable the cpu-to-cpu isr just in case imx_semae_mutex_unlock
+ * function has not woke up another task waiting for the core
+ * mutex.
+ */
+ if (shm_ptr->gate_val != (MCC_CORE_NUMBER + 1))
+ imx_mcc_bsp_int_enable(cpu_to_cpu_isr_vector);
+ return MCC_SUCCESS;
+ }
+
+ return MCC_ERR_SEMAPHORE;
+}
+
+/*!
+ * \brief This function registers the CPU-to-CPU interrupt.
+ *
+ * Calls interrupt component functions to install and enable the
+ * CPU-to-CPU interrupt.
+ *
+ */
+int mcc_register_cpu_to_cpu_isr(void)
+{
+ int ret;
+ unsigned int vector_number;
+
+ vector_number = mcc_get_cpu_to_cpu_vector(MCC_CORE_NUMBER);
+
+ if (vector_number != MCC_VECTOR_NUMBER_INVALID) {
+ imx_mu_reg =
+ syscon_regmap_lookup_by_compatible("fsl,imx6sx-mu");
+ if (IS_ERR(imx_mu_reg))
+ pr_err("ERR:unable to find imx mu registers\n");
+
+ ret = request_irq(vector_number, mcc_cpu_to_cpu_isr,
+ 0, "imx-linux-mcc", NULL);
+ if (ret) {
+ pr_err("%s: register interrupt %d failed, rc %d\n",
+ __func__, vector_number, ret);
+ return MCC_ERR_INT;
+ }
+ /* Make sure the INT is cleared */
+ mcc_clear_cpu_to_cpu_interrupt(MCC_CORE_NUMBER);
+
+ /* Enable INT */
+ ret = imx_mcc_bsp_int_enable(vector_number);
+ if (ret < 0) {
+ pr_err("ERR:failed to enable mcc int.\n");
+ return MCC_ERR_INT;
+ }
+
+ cpu_to_cpu_isr_vector = vector_number;
+ return MCC_SUCCESS;
+ } else {
+ return MCC_ERR_INT;
+ }
+}
+
+/*!
+ * \brief This function triggers an interrupt to other core(s).
+ *
+ */
+int mcc_generate_cpu_to_cpu_interrupt(void)
+{
+ /*
+ * Assert directed CPU interrupts for all processors except
+ * the requesting core
+ */
+ mcc_triger_cpu_to_cpu_interrupt();
+
+ return MCC_SUCCESS;
+}
+
+/*!
+ * \brief This function copies data.
+ *
+ * Copies the number of single-addressable units from the source address
+ * to destination address.
+ *
+ * \param[in] src Source address.
+ * \param[in] dest Destination address.
+ * \param[in] size Number of single-addressable units to copy.
+ */
+void mcc_memcpy(void *src, void *dest, unsigned int size)
+{
+ memcpy(dest, src, size);
+}
+
+void _mem_zero(void *ptr, unsigned int size)
+{
+ memset(ptr, 0, size);
+}
+
+void *virt_to_mqx(void *x)
+{
+ if (null == x)
+ return NULL;
+ else
+ return (void *)((unsigned long) (x) - mcc_shm_offset);
+}
+
+void *mqx_to_virt(void *x)
+{
+ if (null == x)
+ return NULL;
+ else
+ return (void *)((unsigned long) (x) + mcc_shm_offset);
+}
diff --git a/include/linux/mcc_imx6sx.h b/include/linux/mcc_imx6sx.h
new file mode 100644
index 000000000000..86a6de7da31d
--- /dev/null
+++ b/include/linux/mcc_imx6sx.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Freescale IMX Linux-specific MCC implementation.
+ * Prototypes for iMX6sx-specific MCC library functions.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Define the phiscal memory address on A9 and shared M4,
+ * This definition should be aligned on both A9 and M4
+ */
+#define MCC_VECTOR_NUMBER_INVALID (0)
+
+enum {
+ /* FIXME */
+ INT_CPU_TO_CPU_MU_A2M = 122,
+ INT_CPU_TO_CPU_MU_M2A = 90,
+
+ MU_ASR = 0x20,
+ MU_ACR = 0x24,
+};
+
+extern struct regmap *imx_mu_reg;
+
+/* Return core num. A9 0, M4 1 */
+unsigned int _psp_core_num(void);
+
+unsigned int mcc_get_cpu_to_cpu_vector(unsigned int);
+void mcc_clear_cpu_to_cpu_interrupt(unsigned int);
+void mcc_triger_cpu_to_cpu_interrupt(void);
+int imx_mcc_bsp_int_disable(unsigned int vector_number);
+int imx_mcc_bsp_int_enable(unsigned int vector_number);
diff --git a/include/linux/mcc_linux.h b/include/linux/mcc_linux.h
new file mode 100644
index 000000000000..f8591de63e74
--- /dev/null
+++ b/include/linux/mcc_linux.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Freescale IMX Linux-specific MCC implementation.
+ * Prototypes for Linunx-specific MCC library functions
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __MCC_LINUX__
+#define __MCC_LINUX__
+
+/* Define the kinds of cache macros */
+#define MCC_DCACHE_ENABLE(n)
+#define MCC_DCACHE_DISABLE()
+#define MCC_DCACHE_FLUSH()
+#define MCC_DCACHE_FLUSH_LINE(p)
+#define MCC_DCACHE_FLUSH_MLINES(p, m)
+#define MCC_DCACHE_INVALIDATE()
+#define MCC_DCACHE_INVALIDATE_LINE(p)
+#define MCC_DCACHE_INVALIDATE_MLINES(p, m)
+
+#define _mem_size unsigned int
+
+void * virt_to_mqx(void *);
+void * mqx_to_virt(void *);
+#define VIRT_TO_MQX(x) virt_to_mqx(x)
+#define MQX_TO_VIRT(x) mqx_to_virt(x)
+
+/* Semaphore-related functions */
+int mcc_init_semaphore(unsigned int);
+int mcc_deinit_semaphore(unsigned int);
+int mcc_get_semaphore(void);
+int mcc_release_semaphore(void);
+
+/* CPU-to-CPU interrupt-related functions */
+int mcc_register_cpu_to_cpu_isr(void);
+int mcc_generate_cpu_to_cpu_interrupt(void);
+
+/* Memory management-related functions */
+void mcc_memcpy(void *, void *, unsigned int);
+void _mem_zero(void *, unsigned int);
+
+#endif /* __MCC_LINUX__ */