/* * Copyright (c) 2018, NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_imx_mu #include #include #include #include #include #include #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_info; MU_Type *base = MU(config); struct imx_mu_data *data = dev->driver_data; uint32_t data32[IMX_IPM_DATA_REGS]; uint32_t status_reg; int32_t id; int32_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, (uint32_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, uint32_t id, const void *data, int size) { const struct imx_mu_config *config = dev->config_info; MU_Type *base = MU(config); uint32_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 uint32_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_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_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_INST_REG_ADDR(0), .irq_config_func = imx_mu_config_func_b, }; static struct imx_mu_data imx_mu_b_data; DEVICE_AND_API_INIT(mu_b, DT_INST_LABEL(0), &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_INST_IRQN(0), DT_INST_IRQ(0, priority), imx_mu_isr, DEVICE_GET(mu_b), 0); irq_enable(DT_INST_IRQN(0)); }