diff options
-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 | 148 | ||||
-rw-r--r-- | arch/arm/mach-imx/mcc_linux.c | 281 | ||||
-rw-r--r-- | include/linux/mcc_imx6sx.h | 43 | ||||
-rw-r--r-- | include/linux/mcc_linux.h | 53 |
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__ */ |