Skip to content

Commit

Permalink
drivers: ipm: add driver based on stm32 hsem
Browse files Browse the repository at this point in the history
Some STM32 SOC, like stm32h745 and stm32h747 doesn't have IPCC.
Provide a STM32 HSEM based ipm driver for these SOC.

Signed-off-by: HaiLong Yang <[email protected]>
  • Loading branch information
HaiLong Yang authored and carlescufi committed Aug 9, 2021
1 parent 9a80196 commit 559e126
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/ipm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ zephyr_library_sources_ifdef(CONFIG_IPM_STM32_IPCC ipm_stm32_ipcc.c)
zephyr_library_sources_ifdef(CONFIG_IPM_NRFX ipm_nrfx_ipc.c)
zephyr_library_sources_ifdef(CONFIG_IPM_CAVS_IDC ipm_cavs_idc.c)
zephyr_library_sources_ifdef(CONFIG_IPM_INTEL_ADSP ipm_intel_adsp.c)
zephyr_library_sources_ifdef(CONFIG_IPM_STM32_HSEM ipm_stm32_hsem.c)

zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c)
14 changes: 14 additions & 0 deletions drivers/ipm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,20 @@ config IPM_INTEL_ADSP
help
Driver for the Host-DSP Mailbox Communication channel.

config IPM_STM32_HSEM
bool "STM32 HSEM controller"
depends on STM32H7_DUAL_CORE
help
Driver for stm32 HSEM mailbox

config IPM_STM32_HSEM_CPU
int "HSEM CPU ID"
default 1
range 1 2
depends on IPM_STM32_HSEM
help
use to define the CPU ID used by HSEM

module = IPM
module-str = ipm
source "subsys/logging/Kconfig.template.log_config"
Expand Down
214 changes: 214 additions & 0 deletions drivers/ipm/ipm_stm32_hsem.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright (c) 2021 BrainCo Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT st_stm32_hsem_mailbox

#include <device.h>
#include <drivers/clock_control.h>
#include <drivers/ipm.h>
#include <drivers/clock_control/stm32_clock_control.h>

#include "stm32_hsem.h"

#include <logging/log.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 *clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);

/* Config transfer semaphore */
switch (CONFIG_IPM_STM32_HSEM_CPU) {
case HSEM_CPU1:
/* 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)

0 comments on commit 559e126

Please sign in to comment.