blob: 1d29f1b2fdbdc7d41c3d2f6ed85e396112821379 [file] [log] [blame]
/*
* 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)