222 lines
5.9 KiB
C
222 lines
5.9 KiB
C
/*
|
|
* 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 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)
|