summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx
diff options
context:
space:
mode:
authorRichard Zhu <Richard.Zhu@freescale.com>2015-01-08 15:09:38 +0800
committerNitin Garg <nitin.garg@freescale.com>2015-01-15 21:19:02 -0600
commita2f51f0e7d256da97d247a5da20be5c59841a9a8 (patch)
tree9f707ac3ff2241705f504fedb101b7d4dbaed26d /arch/arm/mach-imx
parent58d805939bf2eabcd115928a7d843a415bfd36c0 (diff)
MLK-10078-02 arm: mcc: platform related codes changes
The platform related codes changes when enable mcc2.0 on imx_3.14 kernel, and tested on imx6sx sdb board. - keep imx6sx soc related apis in mcc_imx6sx.c/h - keep linux os related apis in mcc_linux.c/h - add some new mcc callback in mu driver, since the gie3 of mu is used as cpu2cpu interrupter in the mcc implementation on imx6sx. Signed-off-by: Richard Zhu <Richard.Zhu@freescale.com>
Diffstat (limited to 'arch/arm/mach-imx')
-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.c35
-rw-r--r--arch/arm/mach-imx/mcc_linux.c199
-rw-r--r--arch/arm/mach-imx/mu.c261
5 files changed, 500 insertions, 1 deletions
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 7db7179e5f2f..9072b670a4c5 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -69,6 +69,10 @@ config HAVE_IMX_MMDC
config HAVE_IMX_SRC
def_bool y if SMP
+config HAVE_IMX_MCC
+ select IMX_SEMA4
+ bool
+
config HAVE_IMX_AMP
bool
@@ -833,6 +837,7 @@ config SOC_IMX6SX
bool "i.MX6 SoloX support"
select PINCTRL_IMX6SX
select HAVE_IMX_AMP
+ select HAVE_IMX_MCC
select SOC_IMX6
help
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 43242c735b22..b76a214cf00a 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -98,6 +98,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..ad501cfe4d74
--- /dev/null
+++ b/arch/arm/mach-imx/mcc_imx6sx.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier: GPL-2.0+ and/or BSD-3-Clause
+ * The GPL-2.0+ license for this file can be found in the COPYING.GPL file
+ * included with this distribution or at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ * The BSD-3-Clause License for this file can be found in the COPYING.BSD file
+ * included with this distribution or at
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <linux/mcc_config_linux.h>
+#include <linux/mcc_common.h>
+#include <linux/mcc_linux.h>
+
+/*!
+ * \brief This function returns the core number
+ *
+ * \return int
+ */
+unsigned int _psp_core_num(void)
+{
+ return 0;
+}
+
+/*!
+ * \brief This function returns the node number
+ *
+ * \return unsigned int
+ */
+unsigned int _psp_node_num(void)
+{
+ return MCC_LINUX_NODE_NUMBER;
+}
diff --git a/arch/arm/mach-imx/mcc_linux.c b/arch/arm/mach-imx/mcc_linux.c
new file mode 100644
index 000000000000..dee595878082
--- /dev/null
+++ b/arch/arm/mach-imx/mcc_linux.c
@@ -0,0 +1,199 @@
+/*
+ * This file contains Linux-specific MCC library functions
+ *
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ *
+ * SPDX-License-Identifier: GPL-2.0+ and/or BSD-3-Clause
+ * The GPL-2.0+ license for this file can be found in the COPYING.GPL file
+ * included with this distribution or at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ * The BSD-3-Clause License for this file can be found in the COPYING.BSD file
+ * included with this distribution or at
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/imx_sema4.h>
+#include "mcc_config.h"
+#include <linux/mcc_imx6sx.h>
+
+/* Global variables */
+static unsigned long mcc_shm_offset;
+
+MCC_BOOKEEPING_STRUCT *bookeeping_data;
+
+/*!
+ * \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 */
+ mcc_shm_ptr = imx_sema4_mutex_create(0, sem_num);
+
+ if (NULL == mcc_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(mcc_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()) {
+ pr_err("ERR:failed to disable mcc int.\n");
+ return MCC_ERR_SEMAPHORE;
+ }
+
+ if (0 == imx_sema4_mutex_lock(mcc_shm_ptr)) {
+ return MCC_SUCCESS;
+ } else {
+ if (imx_mcc_bsp_int_enable()) {
+ 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(mcc_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 (mcc_shm_ptr->gate_val != (MCC_CORE_NUMBER + 1))
+ imx_mcc_bsp_int_enable();
+ 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)
+{
+ /*
+ * CPU to CPU ISR had been registered in MU driver.
+ * return success directly.
+ */
+ return MCC_SUCCESS;
+}
+
+/*!
+ * \brief This function triggers an interrupt to other core(s).
+ *
+ */
+int mcc_generate_cpu_to_cpu_interrupt(void)
+{
+ int ret;
+
+ /*
+ * Assert directed CPU interrupts for all processors except
+ * the requesting core
+ */
+ ret = mcc_triger_cpu_to_cpu_interrupt();
+
+ if (ret == 0)
+ return MCC_SUCCESS;
+ else
+ return ret;
+}
+
+/*!
+ * \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 *mcc_virt_to_phys(void *x)
+{
+ if (null == x)
+ return NULL;
+ else
+ return (void *)((unsigned long) (x) - mcc_shm_offset);
+}
+
+void *mcc_phys_to_virt(void *x)
+{
+ if (null == x)
+ return NULL;
+ else
+ return (void *)((unsigned long) (x) + mcc_shm_offset);
+}
+
+int mcc_init_os_sync(void)
+{
+ /* No used in linux */
+ return MCC_SUCCESS;
+}
+
+int mcc_deinit_os_sync(void)
+{
+ /* No used in linux */
+ return MCC_SUCCESS;
+}
+
+void mcc_clear_os_sync_for_ep(MCC_ENDPOINT *endpoint)
+{
+ /* No used in linux */
+}
+
+MCC_BOOKEEPING_STRUCT *mcc_get_bookeeping_data(void)
+{
+ bookeeping_data = (MCC_BOOKEEPING_STRUCT *)ioremap_nocache
+ (MCC_BASE_ADDRESS, sizeof(struct mcc_bookeeping_struct));
+ mcc_shm_offset = (unsigned long)bookeeping_data
+ - (unsigned long)MCC_BASE_ADDRESS;
+
+ return bookeeping_data;
+}
diff --git a/arch/arm/mach-imx/mu.c b/arch/arm/mach-imx/mu.c
index c312ce403d4e..b4839cb846e6 100644
--- a/arch/arm/mach-imx/mu.c
+++ b/arch/arm/mach-imx/mu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
@@ -10,6 +10,7 @@
*/
#include <linux/busfreq-imx6.h>
+#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -20,6 +21,10 @@
#include <linux/platform_device.h>
#include "common.h"
#include "hardware.h"
+#include "mcc_config.h"
+#include <linux/imx_sema4.h>
+#include <linux/mcc_imx6sx.h>
+#include <linux/mcc_linux.h>
#define MU_ATR0_OFFSET 0x0
#define MU_ARR0_OFFSET 0x10
@@ -44,6 +49,13 @@ static struct delayed_work mu_work;
static u32 m4_wake_irqs[4];
static bool m4_freq_low;
+struct imx_sema4_mutex *mcc_shm_ptr;
+unsigned int imx_mcc_buffer_freed = 0, imx_mcc_buffer_queued = 0;
+/* Used for blocking send */
+static DECLARE_WAIT_QUEUE_HEAD(buffer_freed_wait_queue);
+/* Used for blocking recv */
+static DECLARE_WAIT_QUEUE_HEAD(buffer_queued_wait_queue);
+
bool imx_mu_is_m4_in_low_freq(void)
{
return m4_freq_low;
@@ -144,6 +156,187 @@ static void mu_work_handler(struct work_struct *work)
mu_base + MU_ACR);
}
+/*!
+ * \brief This function clears the CPU-to-CPU int flag for the particular core.
+ *
+ * Implementation is platform-specific.
+ */
+void mcc_clear_cpu_to_cpu_interrupt(void)
+{
+ u32 val;
+
+ val = readl_relaxed(mu_base + MU_ASR);
+ /* write 1 to BIT31 to clear the bit31(GIP3) of MU_ASR */
+ val = val | (1 << 31);
+ writel_relaxed(val, mu_base + MU_ASR);
+}
+
+/*!
+ * \brief This function triggers the CPU-to-CPU interrupt.
+ *
+ * Platform-specific software triggering the inter-CPU interrupts.
+ */
+int mcc_triger_cpu_to_cpu_interrupt(void)
+{
+ int i = 0;
+ u32 val;
+
+ val = readl_relaxed(mu_base + MU_ACR);
+
+ if ((val & BIT(19)) != 0) {
+ do {
+ val = readl_relaxed(mu_base + MU_ACR);
+ msleep(1);
+ } while (((val & BIT(19)) > 0) && (i++ < 100));
+ }
+
+ if ((val & BIT(19)) == 0) {
+ /* Enable the bit19(GIR3) of MU_ACR */
+ val = readl_relaxed(mu_base + MU_ACR);
+ val |= BIT(19);
+ writel_relaxed(val, mu_base + MU_ACR);
+ return 0;
+ } else {
+ pr_info("mcc int still be triggered after %d ms polling!\n", i);
+ return -EIO;
+ }
+}
+
+/*!
+ * \brief This function disable the CPU-to-CPU interrupt.
+ *
+ * Platform-specific software disable the inter-CPU interrupts.
+ */
+int imx_mcc_bsp_int_disable(void)
+{
+ u32 val;
+
+ /* Disablethe bit31(GIE3) and bit19(GIR3) of MU_ACR */
+ val = readl_relaxed(mu_base + MU_ACR);
+ val &= ~(BIT(31) | BIT(27));
+ writel_relaxed(val, mu_base + MU_ACR);
+
+ /* flush */
+ val = readl_relaxed(mu_base + MU_ACR);
+ 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(void)
+{
+ u32 val;
+
+ /* Enable bit31(GIE3) and bit19(GIR3) of MU_ACR */
+ val = readl_relaxed(mu_base + MU_ACR);
+ val |= (BIT(31) | BIT(27));
+ writel_relaxed(val, mu_base + MU_ACR);
+
+ /* flush */
+ val = readl_relaxed(mu_base + MU_ACR);
+ return 0;
+}
+
+int mcc_wait_for_buffer_freed(MCC_RECEIVE_BUFFER **buffer, unsigned int timeout)
+{
+ int return_value;
+ unsigned long timeout_j; /* jiffies */
+ MCC_RECEIVE_BUFFER *buf = null;
+
+ /*
+ * Blocking calls: CPU-to-CPU ISR sets the event and thus
+ * resumes tasks waiting for a free MCC buffer.
+ * As the interrupt request is send to all cores when a buffer
+ * is freed it could happen that several tasks from different
+ * cores/nodes are waiting for a free buffer and all of them
+ * are notified that the buffer has been freed. This function
+ * has to check (after the wake up) that a buffer is really
+ * available and has not been already grabbed by another
+ * "competitor task" that has been faster. If so, it has to
+ * wait again for the next notification.
+ */
+ while (buf == null) {
+ if (timeout == 0xffffffff) {
+ /*
+ * In order to level up the robust, do not always
+ * wait event here. Wake up itself after every 1~s.
+ */
+ timeout_j = usecs_to_jiffies(1000);
+ wait_event_timeout(buffer_freed_wait_queue,
+ imx_mcc_buffer_freed == 1, timeout_j);
+ } else {
+ timeout_j = msecs_to_jiffies(timeout);
+ wait_event_timeout(buffer_freed_wait_queue,
+ imx_mcc_buffer_freed == 1, timeout_j);
+ }
+
+ return_value = mcc_get_semaphore();
+ if (return_value != MCC_SUCCESS)
+ return return_value;
+
+ MCC_DCACHE_INVALIDATE_MLINES((void *)
+ &bookeeping_data->free_list,
+ sizeof(MCC_RECEIVE_LIST *));
+
+ buf = mcc_dequeue_buffer(&bookeeping_data->free_list);
+ mcc_release_semaphore();
+ if (imx_mcc_buffer_freed)
+ imx_mcc_buffer_freed = 0;
+ }
+
+ *buffer = buf;
+ return MCC_SUCCESS;
+}
+
+int mcc_wait_for_buffer_queued(MCC_ENDPOINT *endpoint, unsigned int timeout)
+{
+ unsigned long timeout_j; /* jiffies */
+ MCC_RECEIVE_LIST *tmp_list;
+
+ /* Get list of buffers kept by the particular endpoint */
+ tmp_list = mcc_get_endpoint_list(*endpoint);
+
+ if (timeout == 0xffffffff) {
+ wait_event(buffer_queued_wait_queue,
+ imx_mcc_buffer_queued == 1);
+ mcc_get_semaphore();
+ /*
+ * double check if the tmp_list head is still null
+ * or not, if yes, wait again.
+ */
+ while (tmp_list->head == null) {
+ imx_mcc_buffer_queued = 0;
+ mcc_release_semaphore();
+ wait_event(buffer_queued_wait_queue,
+ imx_mcc_buffer_queued == 1);
+ mcc_get_semaphore();
+ }
+ } else {
+ timeout_j = msecs_to_jiffies(timeout);
+ wait_event_timeout(buffer_queued_wait_queue,
+ imx_mcc_buffer_queued == 1, timeout_j);
+ mcc_get_semaphore();
+ }
+
+ if (imx_mcc_buffer_queued)
+ imx_mcc_buffer_queued = 0;
+
+ if (tmp_list->head == null) {
+ pr_err("%s can't get queued buffer.\n", __func__);
+ mcc_release_semaphore();
+ return MCC_ERR_TIMEOUT;
+ }
+
+ tmp_list->head = (MCC_RECEIVE_BUFFER *)
+ MCC_MEM_PHYS_TO_VIRT(tmp_list->head);
+ mcc_release_semaphore();
+
+ return MCC_SUCCESS;
+}
+
static irqreturn_t imx_mu_isr(int irq, void *param)
{
u32 irqs;
@@ -159,6 +352,69 @@ static irqreturn_t imx_mu_isr(int irq, void *param)
schedule_delayed_work(&mu_work, 0);
}
+ /*
+ * MCC CPU-to-CPU interrupt.
+ * Each core can interrupt the other. There are two logical signals:
+ * - Receive data available for (Node,Port)
+ * - signaled when a buffer is queued to a Receive Data Queue.
+ * - Buffer available
+ * - signaled when a buffer is queued to the Free Buffer Queue.
+ * 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.
+ */
+ if (irqs & (1 << 31)) {
+ /*
+ * 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.
+ */
+ MCC_SIGNAL serviced_signal;
+ if (SEMA4_A9_LOCK == imx_sema4_mutex_trylock(mcc_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();
+
+ /* Unlocks the core mutex */
+ imx_sema4_mutex_unlock(mcc_shm_ptr);
+ }
+ }
+
return IRQ_HANDLED;
}
@@ -186,6 +442,9 @@ static int imx_mu_probe(struct platform_device *pdev)
/* enable the bit27(RIE3) of MU_ACR */
writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(27),
mu_base + MU_ACR);
+ /* enable the bit31(GIE3) of MU_ACR, used for MCC */
+ writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(31),
+ mu_base + MU_ACR);
/* MU always as a wakeup source for low power mode */
imx_gpc_add_m4_wake_up_irq(irq, true);