Skip to content

Commit

Permalink
MHU: Add MHU driver for V2M Musca
Browse files Browse the repository at this point in the history
MHU (Message Handling Unit) enable software to raise interrupts to
the processor cores. It enabled in SSE 200 subsystems.

This patch aims to implement inter processor communication. It performs
similar mechanism with existing ipm except data registers.

Signed-off-by: Karl Zhang <karl.zhang@linaro.org>
  • Loading branch information
Karl Zhang committed Jan 26, 2019
1 parent 7117b73 commit cece68c
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 1 deletion.
14 changes: 13 additions & 1 deletion boards/arm/v2m_musca/v2m_musca-common.dtsi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018 Linaro Limited
* Copyright (c) 2018-2019 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -22,6 +22,18 @@ dtimer0: dtimer@2000 {
interrupts = <5 3>;
};

mhu0: mhu@3000 {
compatible = "arm,mhu";
reg = <0x3000 0x1000>;
interrupts = <6 3>;
};

mhu1: mhu@4000 {
compatible = "arm,mhu";
reg = <0x4000 0x1000>;
interrupts = <7 3>;
};

wdog0: wdog@81000 {
compatible = "arm,cmsdk-watchdog";
reg = <0x81000 0x1000>;
Expand Down
1 change: 1 addition & 0 deletions drivers/ipm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ zephyr_library()
zephyr_library_sources_ifdef(CONFIG_IPM_MCUX ipm_mcux.c)
zephyr_library_sources_ifdef(CONFIG_IPM_IMX ipm_imx.c)
zephyr_library_sources_ifdef(CONFIG_IPM_QUARK_SE ipm_quark_se.c)
zephyr_library_sources_ifdef(CONFIG_IPM_MHU ipm_mhu.c)

zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c)
18 changes: 18 additions & 0 deletions drivers/ipm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,21 @@ config IPM_IMX_MAX_ID_VAL
default 1 if IPM_IMX_MAX_DATA_SIZE_8
default 0 if IPM_IMX_MAX_DATA_SIZE_16
depends on IPM_IMX

config IPM_MHU
bool "IPM MHU driver"
depends on IPM
help
Driver for SSE 200 MHU (Message Handling Unit)

config IPM_MHU0
bool "MHU0 driver"
depends on IPM_MHU
help
Enable Driver for MHU0

config IPM_MHU1
bool "MHU1 driver"
depends on IPM_MHU
help
Enable Driver for MHU1
245 changes: 245 additions & 0 deletions drivers/ipm/ipm_mhu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
/*
* Copyright (c) 2019 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <device.h>
#include <soc.h>
#include "ipm_mhu.h"

#define DEV_CFG(dev) \
((const struct ipm_mhu_device_config * const)(dev)->config->config_info)
#define DEV_DATA(dev) \
((struct ipm_mhu_data *)(dev)->driver_data)
#define IPM_MHU_REGS(dev) \
((volatile struct ipm_mhu_reg_map_t *)(DEV_CFG(dev))->base)

#if defined(CONFIG_IPM_MHU0) || defined(CONFIG_IPM_MHU1)

static enum ipm_mhu_cpu_id_t ipm_mhu_get_cpu_id(const struct device *d)
{
volatile u32_t *p_mhu_dev_base;
volatile u32_t *p_cpu_id;

p_mhu_dev_base = (volatile u32_t *)IPM_MHU_REGS(d);

p_cpu_id = (volatile u32_t *)(((u32_t)p_mhu_dev_base & 0xF0000000) +
SSE_200_CPU_ID_UNIT_OFFSET);

return (enum ipm_mhu_cpu_id_t)*p_cpu_id;
}

static u32_t ipm_mhu_get_status(const struct device *d,
enum ipm_mhu_cpu_id_t cpu_id,
u32_t *status)
{
struct ipm_mhu_reg_map_t *p_mhu_dev;

if (status == NULL) {
return IPM_MHU_ERR_INVALID_ARG;
}

p_mhu_dev = (struct ipm_mhu_reg_map_t *)IPM_MHU_REGS(d);

switch (cpu_id) {
case IPM_MHU_CPU1:
*status = p_mhu_dev->cpu1intr_stat;
break;
case IPM_MHU_CPU0:
default:
*status = p_mhu_dev->cpu0intr_stat;
break;
}

return IPM_MHU_ERR_NONE;
}

static int ipm_mhu_send(struct device *d, int wait, u32_t cpu_id,
const void *data, int size)
{
ARG_UNUSED(wait);
u32_t set_val = *(u32_t *)data;

struct ipm_mhu_reg_map_t *p_mhu_dev;

if (cpu_id >= IPM_MHU_CPU_MAX) {
return -EINVAL;
}

if (size > IPM_MHU_MAX_DATA_SIZE) {
return -EMSGSIZE;
}

p_mhu_dev = (struct ipm_mhu_reg_map_t *)IPM_MHU_REGS(d);

switch (cpu_id) {
case IPM_MHU_CPU1:
p_mhu_dev->cpu1intr_set = set_val;
break;
case IPM_MHU_CPU0:
default:
p_mhu_dev->cpu0intr_set = set_val;
break;
}

return 0;
}

static void ipm_mhu_clear_val(const struct device *d,
enum ipm_mhu_cpu_id_t cpu_id,
u32_t clear_val)
{
struct ipm_mhu_reg_map_t *p_mhu_dev;

p_mhu_dev = (struct ipm_mhu_reg_map_t *)IPM_MHU_REGS(d);

switch (cpu_id) {
case IPM_MHU_CPU1:
p_mhu_dev->cpu1intr_clr = clear_val;
break;
case IPM_MHU_CPU0:
default:
p_mhu_dev->cpu0intr_clr = clear_val;
break;
}
}

static u32_t ipm_mhu_max_id_val_get(struct device *d)
{
ARG_UNUSED(d);

return IPM_MHU_MAX_ID_VAL;
}

static int ipm_mhu_init(struct device *d)
{
const struct ipm_mhu_device_config *config = DEV_CFG(d);

config->irq_config_func(d);

return 0;
}

static void ipm_mhu_isr(void *arg)
{
struct device *d = arg;
struct ipm_mhu_data *driver_data = DEV_DATA(d);
enum ipm_mhu_cpu_id_t cpu_id;
u32_t ipm_mhu_status;

cpu_id = ipm_mhu_get_cpu_id(d);

ipm_mhu_get_status(d, cpu_id, &ipm_mhu_status);
ipm_mhu_clear_val(d, cpu_id, ipm_mhu_status);

if (driver_data->callback) {
driver_data->callback(driver_data->callback_ctx, cpu_id,
&ipm_mhu_status);
}
}

static int ipm_mhu_set_enabled(struct device *d, int enable)
{
ARG_UNUSED(d);
ARG_UNUSED(enable);
return 0;
}

static int ipm_mhu_max_data_size_get(struct device *d)
{
ARG_UNUSED(d);

return IPM_MHU_MAX_DATA_SIZE;
}

static void ipm_mhu_register_cb(struct device *d,
ipm_callback_t cb,
void *context)
{
struct ipm_mhu_data *driver_data = DEV_DATA(d);

driver_data->callback = cb;
driver_data->callback_ctx = context;
}

static const struct ipm_driver_api ipm_mhu_driver_api = {
.send = ipm_mhu_send,
.register_callback = ipm_mhu_register_cb,
.max_data_size_get = ipm_mhu_max_data_size_get,
.max_id_val_get = ipm_mhu_max_id_val_get,
.set_enabled = ipm_mhu_set_enabled,
};

#endif

#ifdef CONFIG_IPM_MHU0

static void ipm_mhu_irq_config_func_0(struct device *d);

static struct ipm_mhu_device_config ipm_mhu_cfg_0 = {
.base = (u8_t *)DT_ARM_MHU_0_BASE_ADDRESS,
.irq_config_func = ipm_mhu_irq_config_func_0,
};

static struct ipm_mhu_data ipm_mhu_data_0 = {
.callback = NULL,
.callback_ctx = NULL,
};

DEVICE_AND_API_INIT(mhu_0,
"MHU_0",
&ipm_mhu_init,
&ipm_mhu_data_0,
&ipm_mhu_cfg_0, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&ipm_mhu_driver_api);

static void ipm_mhu_irq_config_func_0(struct device *d)
{
ARG_UNUSED(d);
IRQ_CONNECT(DT_ARM_MHU_0_IRQ_0,
DT_ARM_MHU_0_IRQ_0,
ipm_mhu_isr,
DEVICE_GET(mhu_0),
0);
irq_enable(DT_ARM_MHU_0_IRQ_0);
}

#endif /* CONFIG_IPM_MHU0 */

#ifdef CONFIG_IPM_MHU1

static void ipm_mhu_irq_config_func_1(struct device *d);

static struct ipm_mhu_device_config ipm_mhu_cfg_1 = {
.base = (u8_t *)DT_ARM_MHU_1_BASE_ADDRESS,
.irq_config_func = ipm_mhu_irq_config_func_1,
};

static struct ipm_mhu_data ipm_mhu_data_1 = {
.callback = NULL,
.callback_ctx = NULL,
};

DEVICE_AND_API_INIT(mhu_1,
"MHU_1",
&ipm_mhu_init,
&ipm_mhu_data_1,
&ipm_mhu_cfg_1, PRE_KERNEL_1,
CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&ipm_mhu_driver_api);

static void ipm_mhu_irq_config_func_1(struct device *d)
{
ARG_UNUSED(d);
IRQ_CONNECT(DT_ARM_MHU_1_IRQ_0,
DT_ARM_MHU_1_IRQ_0_PRIORITY,
ipm_mhu_isr,
DEVICE_GET(mhu_1),
0);
irq_enable(DT_ARM_MHU_1_IRQ_0);
}

#endif /* CONFIG_IPM_MHU1 */
74 changes: 74 additions & 0 deletions drivers/ipm/ipm_mhu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2019 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_DRIVERS_IPM_IPM_MHU_H_
#define ZEPHYR_DRIVERS_IPM_IPM_MHU_H_

#include <kernel.h>
#include <ipm.h>
#include <device.h>

#ifdef __cplusplus
extern "C" {
#endif

#define IPM_MHU_MAX_DATA_SIZE 1
#define IPM_MHU_MAX_ID_VAL 0
#define SSE_200_CPU_ID_UNIT_OFFSET ((0x1F000UL))

/* SSE 200 MHU register map structure */
struct ipm_mhu_reg_map_t {
/* (R/ ) CPU 0 Interrupt Status Register */
volatile u32_t cpu0intr_stat;
volatile u32_t cpu0intr_set; /* ( /W) CPU 0 Interrupt Set Register */
volatile u32_t cpu0intr_clr; /* ( /W) CPU 0 Interrupt Clear Register */
volatile u32_t reserved0;
/* (R/ ) CPU 1 Interrupt Status Register */
volatile u32_t cpu1intr_stat;
volatile u32_t cpu1intr_set; /* ( /W) CPU 1 Interrupt Set Register */
volatile u32_t cpu1intr_clr; /* ( /W) CPU 1 Interrupt Clear Register */
volatile u32_t reserved1[1004];
volatile u32_t pidr4; /* ( /W) Peripheral ID 4 */
volatile u32_t reserved2[3];
volatile u32_t pidr0; /* ( /W) Peripheral ID 0 */
volatile u32_t pidr1; /* ( /W) Peripheral ID 1 */
volatile u32_t pidr2; /* ( /W) Peripheral ID 2 */
volatile u32_t pidr3; /* ( /W) Peripheral ID 3 */
volatile u32_t cidr0; /* ( /W) Component ID 0 */
volatile u32_t cidr1; /* ( /W) Component ID 1 */
volatile u32_t cidr2; /* ( /W) Component ID 2 */
volatile u32_t cidr3; /* ( /W) Component ID 3 */
};

/* MHU enumeration types */
enum ipm_mhu_error_t {
IPM_MHU_ERR_NONE = 0, /* No error */
IPM_MHU_ERR_INVALID_ARG, /* Invalid argument */
};

/* MHU enumeration types */
enum ipm_mhu_cpu_id_t {
IPM_MHU_CPU0 = 0,
IPM_MHU_CPU1,
IPM_MHU_CPU_MAX,
};

struct ipm_mhu_device_config {
u8_t *base;
void (*irq_config_func)(struct device *d);
};

/* Device data structure */
struct ipm_mhu_data {
ipm_callback_t callback;
void *callback_ctx;
};

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_DRIVERS_IPM_IPM_MHU_H_ */

0 comments on commit cece68c

Please sign in to comment.