/* * Copyright (c) 2021 BrainCo Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT st_stm32_hsem_mailbox #include #include #include #include #include "stm32_hsem.h" #include #include LOG_MODULE_REGISTER(ipm_stm32_hsem, CONFIG_IPM_LOG_LEVEL); #define HSEM_CPU1 1 #define HSEM_CPU2 2 #if CONFIG_IPM_STM32_HSEM_CPU == HSEM_CPU1 #define ll_hsem_enableit_cier LL_HSEM_EnableIT_C1IER #define ll_hsem_disableit_cier LL_HSEM_DisableIT_C1IER #define ll_hsem_clearflag_cicr LL_HSEM_ClearFlag_C1ICR #define ll_hsem_isactiveflag_cmisr LL_HSEM_IsActiveFlag_C1MISR #else /* HSEM_CPU2 */ #define ll_hsem_enableit_cier LL_HSEM_EnableIT_C2IER #define ll_hsem_disableit_cier LL_HSEM_DisableIT_C2IER #define ll_hsem_clearflag_cicr LL_HSEM_ClearFlag_C2ICR #define ll_hsem_isactiveflag_cmisr LL_HSEM_IsActiveFlag_C2MISR #endif /* CONFIG_IPM_STM32_HSEM_CPU */ struct stm32_hsem_mailbox_config { void (*irq_config_func)(const struct device *dev); struct stm32_pclken pclken; }; struct stm32_hsem_mailbox_data { uint32_t tx_semid; uint32_t rx_semid; ipm_callback_t callback; void *user_data; }; static struct stm32_hsem_mailbox_data stm32_hsem_mailbox_0_data; void stm32_hsem_mailbox_ipm_rx_isr(const struct device *dev) { struct stm32_hsem_mailbox_data *data = dev->data; uint32_t mask_semid = (1U << data->rx_semid); /* Check semaphore rx_semid interrupt status */ if (!ll_hsem_isactiveflag_cmisr(HSEM, mask_semid)) return; /* Notify user with NULL data pointer */ if (data->callback) { data->callback(dev, data->user_data, 0, NULL); } /* Clear semaphore rx_semid interrupt status and masked status */ ll_hsem_clearflag_cicr(HSEM, mask_semid); } static void stm32_hsem_mailbox_irq_config_func(const struct device *dev) { ARG_UNUSED(dev); IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), stm32_hsem_mailbox_ipm_rx_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQN(0)); } int stm32_hsem_mailbox_ipm_send(const struct device *dev, int wait, uint32_t id, const void *buff, int size) { struct stm32_hsem_mailbox_data *data = dev->data; ARG_UNUSED(wait); ARG_UNUSED(buff); if (size) { LOG_WRN("stm32 HSEM not support data transfer"); return -EMSGSIZE; } if (id) { LOG_WRN("stm32 HSEM only support a single instance of mailbox"); return -EINVAL; } /* Lock the semaphore tx_semid */ z_stm32_hsem_lock(data->tx_semid, HSEM_LOCK_DEFAULT_RETRY); /** * Release the semaphore tx_semid. * This will trigger a HSEMx interrupt on another CPU. */ z_stm32_hsem_unlock(data->tx_semid); return 0; } void stm32_hsem_mailbox_ipm_register_callback(const struct device *dev, ipm_callback_t cb, void *user_data) { struct stm32_hsem_mailbox_data *data = dev->data; data->callback = cb; data->user_data = user_data; } int stm32_hsem_mailbox_ipm_max_data_size_get(const struct device *dev) { ARG_UNUSED(dev); /* stm32 HSEM not support data transfer */ return 0; } uint32_t stm32_hsem_mailbox_ipm_max_id_val_get(const struct device *dev) { ARG_UNUSED(dev); /* stm32 HSEM only support a single instance of mailbox */ return 0; } int stm32_hsem_mailbox_ipm_set_enabled(const struct device *dev, int enable) { struct stm32_hsem_mailbox_data *data = dev->data; uint32_t mask_semid = (1U << data->rx_semid); if (enable) { /* Clear semaphore rx_semid interrupt status and masked status */ ll_hsem_clearflag_cicr(HSEM, mask_semid); /* Enable semaphore rx_semid on HESMx interrupt */ ll_hsem_enableit_cier(HSEM, mask_semid); } else { /* Disable semaphore rx_semid on HSEMx interrupt */ ll_hsem_disableit_cier(HSEM, mask_semid); } return 0; } static int stm32_hsem_mailbox_init(const struct device *dev) { struct stm32_hsem_mailbox_data *data = dev->data; const struct stm32_hsem_mailbox_config *cfg = dev->config; const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); /* Config transfer semaphore */ switch (CONFIG_IPM_STM32_HSEM_CPU) { case HSEM_CPU1: if (!device_is_ready(clk)) { LOG_ERR("clock control device not ready"); return -ENODEV; } /* Enable clock */ if (clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken) != 0) { LOG_WRN("Failed to enable clock"); return -EIO; } data->tx_semid = CFG_HW_IPM_CPU2_SEMID; data->rx_semid = CFG_HW_IPM_CPU1_SEMID; break; case HSEM_CPU2: data->tx_semid = CFG_HW_IPM_CPU1_SEMID; data->rx_semid = CFG_HW_IPM_CPU2_SEMID; break; } cfg->irq_config_func(dev); return 0; } static const struct ipm_driver_api stm32_hsem_mailbox_ipm_dirver_api = { .send = stm32_hsem_mailbox_ipm_send, .register_callback = stm32_hsem_mailbox_ipm_register_callback, .max_data_size_get = stm32_hsem_mailbox_ipm_max_data_size_get, .max_id_val_get = stm32_hsem_mailbox_ipm_max_id_val_get, .set_enabled = stm32_hsem_mailbox_ipm_set_enabled, }; static const struct stm32_hsem_mailbox_config stm32_hsem_mailbox_0_config = { .irq_config_func = stm32_hsem_mailbox_irq_config_func, .pclken = { .bus = DT_INST_CLOCKS_CELL(0, bus), .enr = DT_INST_CLOCKS_CELL(0, bits) }, }; /* * STM32 HSEM has its own LL_HSEM(low-level HSEM) API provided by the hal_stm32 module. * The ipm_stm32_hsem driver only picks up two semaphore IDs from stm32_hsem.h to simulate * a virtual mailbox device. So there will have only one instance. */ #define IPM_STM32_HSEM_INIT(inst) \ BUILD_ASSERT((inst) == 0, \ "multiple instances not supported"); \ DEVICE_DT_INST_DEFINE(0, \ &stm32_hsem_mailbox_init, \ NULL, \ &stm32_hsem_mailbox_0_data, \ &stm32_hsem_mailbox_0_config, \ POST_KERNEL, \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ &stm32_hsem_mailbox_ipm_dirver_api); \ DT_INST_FOREACH_STATUS_OKAY(IPM_STM32_HSEM_INIT)