|  | /* | 
|  | * 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); | 
|  | } |