imx: Add IMX IPM driver for i.MX socs
Add driver for i.MX Messaging Unit peripheral which can be used for
i.MX6SoloX, i.MX7D and other i.MX socs.
Origin: Original
Signed-off-by: Stanislav Poboril <stanislav.poboril@nxp.com>
diff --git a/drivers/ipm/CMakeLists.txt b/drivers/ipm/CMakeLists.txt
index 3121e87..8c9b2ee 100644
--- a/drivers/ipm/CMakeLists.txt
+++ b/drivers/ipm/CMakeLists.txt
@@ -1,6 +1,7 @@
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_IPM_MCUX ipm_mcux.c)
+zephyr_library_sources_ifdef(CONFIG_IPM_IMX ipm_imx.c)
zephyr_library_sources_ifdef(CONFIG_IPM_QUARK_SE ipm_quark_se.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c)
diff --git a/drivers/ipm/Kconfig b/drivers/ipm/Kconfig
index 1b81428..069da7a 100644
--- a/drivers/ipm/Kconfig
+++ b/drivers/ipm/Kconfig
@@ -23,3 +23,55 @@
depends on IPM && HAS_MCUX
help
Driver for MCUX mailbox
+
+config IPM_IMX
+ bool "IMX IPM driver"
+ default n
+ depends on IPM && HAS_IMX_HAL
+ help
+ Driver for NXP i.MX messaging unit
+
+choice
+ prompt "IMX IPM max data size"
+ default IPM_IMX_MAX_DATA_SIZE_16
+ depends on IPM_IMX
+ help
+ Select maximum message size for NXP i.MX messaging unit.
+
+config IPM_IMX_MAX_DATA_SIZE_4
+ bool "4 bytes"
+ help
+ There will be four message types with ids 0, 1, 2 or 3
+ and a maximum size of 4 bytes each.
+
+config IPM_IMX_MAX_DATA_SIZE_8
+ bool "8 bytes"
+ help
+ There will be two message types with ids 0 or 1
+ and a maximum size of 8 bytes each.
+
+config IPM_IMX_MAX_DATA_SIZE_16
+ bool "16 bytes"
+ help
+ There will be a single message type with id 0
+ and a maximum size of 16 bytes.
+
+endchoice
+
+config IPM_IMX_MAX_DATA_SIZE
+ int
+ range 4 16
+ # omit prompt to signify a "hidden" option
+ default 4 if IPM_IMX_MAX_DATA_SIZE_4
+ default 8 if IPM_IMX_MAX_DATA_SIZE_8
+ default 16 if IPM_IMX_MAX_DATA_SIZE_16
+ depends on IPM_IMX
+
+config IPM_IMX_MAX_ID_VAL
+ int
+ range 0 3
+ # omit prompt to signify a "hidden" option
+ default 3 if IPM_IMX_MAX_DATA_SIZE_4
+ default 1 if IPM_IMX_MAX_DATA_SIZE_8
+ default 0 if IPM_IMX_MAX_DATA_SIZE_16
+ depends on IPM_IMX
diff --git a/drivers/ipm/ipm_imx.c b/drivers/ipm/ipm_imx.c
new file mode 100644
index 0000000..f4ded0a
--- /dev/null
+++ b/drivers/ipm/ipm_imx.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2018, NXP
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <device.h>
+#include <soc.h>
+#include <ipm.h>
+#include <mu_imx.h>
+
+#define MU(config) ((MU_Type *)config->base)
+
+#if ((CONFIG_IPM_IMX_MAX_DATA_SIZE % 4) != 0)
+#error CONFIG_IPM_IMX_MAX_DATA_SIZE is invalid
+#endif
+
+#define IMX_IPM_DATA_REGS (CONFIG_IPM_IMX_MAX_DATA_SIZE / 4)
+
+struct imx_mu_config {
+ MU_Type *base;
+ void (*irq_config_func)(struct device *dev);
+};
+
+struct imx_mu_data {
+ ipm_callback_t callback;
+ void *callback_ctx;
+};
+
+static void imx_mu_isr(void *arg)
+{
+ struct device *dev = (struct device *)arg;
+ const struct imx_mu_config *config = dev->config->config_info;
+ MU_Type *base = MU(config);
+ struct imx_mu_data *data = dev->driver_data;
+ u32_t data32[IMX_IPM_DATA_REGS];
+ u32_t status_reg;
+ s32_t id;
+ s32_t i;
+ bool all_registers_full;
+
+ status_reg = base->SR >>= MU_SR_RFn_SHIFT;
+
+ for (id = CONFIG_IPM_IMX_MAX_ID_VAL; id >= 0; id--) {
+ if (status_reg & 0x1U) {
+ /*
+ * Check if all receive registers are full. If not,
+ * it is violation of the protocol (status register
+ * are set earlier than all receive registers).
+ * Do not read any of the registers in such situation.
+ */
+ all_registers_full = true;
+ for (i = 0; i < IMX_IPM_DATA_REGS; i++) {
+ if (!MU_IsRxFull(base,
+ (id * IMX_IPM_DATA_REGS) + i)) {
+ all_registers_full = false;
+ break;
+ }
+ }
+ if (all_registers_full) {
+ for (i = 0; i < IMX_IPM_DATA_REGS; i++) {
+ MU_ReceiveMsg(base,
+ (id * IMX_IPM_DATA_REGS) + i,
+ &data32[i]);
+ }
+
+ if (data->callback) {
+ data->callback(data->callback_ctx,
+ (u32_t)id,
+ &data32[0]);
+ }
+ }
+ }
+ status_reg >>= IMX_IPM_DATA_REGS;
+ }
+
+ /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
+ * Store immediate overlapping exception return operation
+ * might vector to incorrect interrupt
+ */
+#if defined __CORTEX_M && (__CORTEX_M == 4U)
+ __DSB();
+#endif
+}
+
+static int imx_mu_ipm_send(struct device *dev, int wait, u32_t id,
+ const void *data, int size)
+{
+ const struct imx_mu_config *config = dev->config->config_info;
+ MU_Type *base = MU(config);
+ u32_t data32[IMX_IPM_DATA_REGS];
+ mu_status_t status;
+ int i;
+
+ if (id > CONFIG_IPM_IMX_MAX_ID_VAL) {
+ return -EINVAL;
+ }
+
+ if (size > CONFIG_IPM_IMX_MAX_DATA_SIZE) {
+ return -EMSGSIZE;
+ }
+
+ /* Actual message is passing using 32 bits registers */
+ memcpy(data32, data, size);
+
+ for (i = 0; i < IMX_IPM_DATA_REGS; i++) {
+ status = MU_TrySendMsg(base, id * IMX_IPM_DATA_REGS + i,
+ data32[i]);
+ if (status == kStatus_MU_TxNotEmpty) {
+ return -EBUSY;
+ }
+ }
+
+ if (wait) {
+ while (!MU_IsTxEmpty(base,
+ (id * IMX_IPM_DATA_REGS) + IMX_IPM_DATA_REGS - 1)) {
+ }
+ }
+
+ return 0;
+}
+
+static int imx_mu_ipm_max_data_size_get(struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ return CONFIG_IPM_IMX_MAX_DATA_SIZE;
+}
+
+static u32_t imx_mu_ipm_max_id_val_get(struct device *dev)
+{
+ ARG_UNUSED(dev);
+
+ return CONFIG_IPM_IMX_MAX_ID_VAL;
+}
+
+static void imx_mu_ipm_register_callback(struct device *dev,
+ ipm_callback_t cb,
+ void *context)
+{
+ struct imx_mu_data *driver_data = dev->driver_data;
+
+ driver_data->callback = cb;
+ driver_data->callback_ctx = context;
+}
+
+static int imx_mu_ipm_set_enabled(struct device *dev, int enable)
+{
+ const struct imx_mu_config *config = dev->config->config_info;
+ MU_Type *base = MU(config);
+
+#if CONFIG_IPM_IMX_MAX_DATA_SIZE_4
+ if (enable) {
+ MU_EnableRxFullInt(base, 0U);
+ MU_EnableRxFullInt(base, 1U);
+ MU_EnableRxFullInt(base, 2U);
+ MU_EnableRxFullInt(base, 3U);
+ } else {
+ MU_DisableRxFullInt(base, 0U);
+ MU_DisableRxFullInt(base, 1U);
+ MU_DisableRxFullInt(base, 2U);
+ MU_DisableRxFullInt(base, 3U);
+ }
+#elif CONFIG_IPM_IMX_MAX_DATA_SIZE_8
+ if (enable) {
+ MU_EnableRxFullInt(base, 1U);
+ MU_EnableRxFullInt(base, 3U);
+ } else {
+ MU_DisableRxFullInt(base, 1U);
+ MU_DisableRxFullInt(base, 3U);
+ }
+#elif CONFIG_IPM_IMX_MAX_DATA_SIZE_16
+ if (enable) {
+ MU_EnableRxFullInt(base, 3U);
+ } else {
+ MU_DisableRxFullInt(base, 3U);
+ }
+#else
+#error "CONFIG_IPM_IMX_MAX_DATA_SIZE_n is not set"
+#endif
+
+ return 0;
+}
+
+static int imx_mu_init(struct device *dev)
+{
+ const struct imx_mu_config *config = dev->config->config_info;
+
+ MU_Init(MU(config));
+ config->irq_config_func(dev);
+
+ return 0;
+}
+
+static const struct ipm_driver_api imx_mu_driver_api = {
+ .send = imx_mu_ipm_send,
+ .register_callback = imx_mu_ipm_register_callback,
+ .max_data_size_get = imx_mu_ipm_max_data_size_get,
+ .max_id_val_get = imx_mu_ipm_max_id_val_get,
+ .set_enabled = imx_mu_ipm_set_enabled
+};
+
+/* Config MU */
+
+static void imx_mu_config_func_b(struct device *dev);
+
+static const struct imx_mu_config imx_mu_b_config = {
+ .base = (MU_Type *)DT_IPM_IMX_MU_B_BASE_ADDRESS,
+ .irq_config_func = imx_mu_config_func_b,
+};
+
+static struct imx_mu_data imx_mu_b_data;
+
+DEVICE_AND_API_INIT(mu_b, DT_IPM_IMX_MU_B_NAME,
+ &imx_mu_init,
+ &imx_mu_b_data, &imx_mu_b_config,
+ PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
+ &imx_mu_driver_api);
+
+static void imx_mu_config_func_b(struct device *dev)
+{
+ IRQ_CONNECT(DT_IPM_IMX_MU_B_IRQ,
+ DT_IPM_IMX_MU_B_IRQ_PRI,
+ imx_mu_isr, DEVICE_GET(mu_b), 0);
+
+ irq_enable(DT_IPM_IMX_MU_B_IRQ);
+}
diff --git a/dts/bindings/arm/nxp,imx-mu.yaml b/dts/bindings/arm/nxp,imx-mu.yaml
new file mode 100644
index 0000000..70cf8ec
--- /dev/null
+++ b/dts/bindings/arm/nxp,imx-mu.yaml
@@ -0,0 +1,44 @@
+#
+# Copyright (c) 2018, NXP
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+---
+title: IMX MESSAGING UNIT
+version: 0.1
+
+description: >
+ This binding gives a base representation of the i.MX Messaging Unit
+
+properties:
+ compatible:
+ type: string
+ category: required
+ description: compatible strings
+ constraint: "nxp,imx-mu"
+
+ reg:
+ type: array
+ description: mmio register space
+ generation: define
+ category: required
+
+ interrupts:
+ type: array
+ category: required
+ description: required interrupts
+ generation: define
+
+ label:
+ type: string
+ category: required
+ description: Human readable string describing the device (used by Zephyr for API name)
+ generation: define
+
+ rdc:
+ type: int
+ category: required
+ description: Set the RDC permission for this peripheral
+ generation: define
+
+...
diff --git a/ext/hal/nxp/imx/drivers/CMakeLists.txt b/ext/hal/nxp/imx/drivers/CMakeLists.txt
index 2a30b65..7fd034c 100644
--- a/ext/hal/nxp/imx/drivers/CMakeLists.txt
+++ b/ext/hal/nxp/imx/drivers/CMakeLists.txt
@@ -13,3 +13,4 @@
zephyr_sources_ifdef(CONFIG_UART_IMX uart_imx.c)
zephyr_sources_ifdef(CONFIG_GPIO_IMX gpio_imx.c)
zephyr_sources_ifdef(CONFIG_I2C_IMX i2c_imx.c)
+zephyr_sources_ifdef(CONFIG_IPM_IMX mu_imx.c)