You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
5.9 KiB
221 lines
5.9 KiB
/* |
|
* Copyright (c) 2021 BrainCo Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT st_stm32_hsem_mailbox |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <zephyr/drivers/ipm.h> |
|
#include <zephyr/drivers/clock_control/stm32_clock_control.h> |
|
|
|
#include "stm32_hsem.h" |
|
|
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/irq.h> |
|
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 DEVICE_API(ipm, 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)
|
|
|