diff options
author | Richard Zhu <Richard.Zhu@freescale.com> | 2015-01-08 15:09:38 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2015-01-15 21:19:02 -0600 |
commit | a2f51f0e7d256da97d247a5da20be5c59841a9a8 (patch) | |
tree | 9f707ac3ff2241705f504fedb101b7d4dbaed26d /arch/arm/mach-imx | |
parent | 58d805939bf2eabcd115928a7d843a415bfd36c0 (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/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/mach-imx/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/mcc_imx6sx.c | 35 | ||||
-rw-r--r-- | arch/arm/mach-imx/mcc_linux.c | 199 | ||||
-rw-r--r-- | arch/arm/mach-imx/mu.c | 261 |
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); |