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)