Skip to content
Permalink
Browse files

drivers: ipm: mhu: Add MHU driver for V2M Musca

MHU (Message Handling Unit) enables software to raise interrupts to
the processor cores. It is enabled in SSE 200 subsystems.

This patch aims to implement inter processor communication.

Signed-off-by: Karl Zhang <karl.zhang@linaro.org>
  • Loading branch information...
karl-zh authored and galak committed Jan 18, 2019
1 parent 07d21be commit 2a7824a8b05e6d4ac012428b456c81dd99543e32
@@ -122,6 +122,7 @@
/drivers/i2s/i2s_ll_stm32* @avisconti
/drivers/ieee802154/ @jukkar @tbursztyka
/drivers/interrupt_controller/ @andrewboie
/drivers/ipm/ipm_mhu* @karl-zh
/drivers/*/vexriscv_litex.c @mateusz-holenko @kgugala @pgielda
/drivers/led/ @Mani-Sadhasivam
/drivers/led_strip/ @mbolivar
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018 Linaro Limited
* Copyright (c) 2018-2019 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -25,6 +25,20 @@ dtimer0: dtimer@2000 {
label = "DTIMER_0";
};

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

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

wdog0: wdog@81000 {
compatible = "arm,cmsdk-watchdog";
reg = <0x81000 0x1000>;
@@ -5,5 +5,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)
@@ -76,3 +76,9 @@ 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)
@@ -0,0 +1,235 @@
/*
* 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)

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 &
SSE_200_DEVICE_BASE_REG_MSK) +
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);
ARG_UNUSED(data);
const u32_t set_val = 0x01;

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,
};

static void ipm_mhu_irq_config_func_0(struct device *d);

static const 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,
DT_ARM_MHU_0_LABEL,
&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);
}

static void ipm_mhu_irq_config_func_1(struct device *d);

static const 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,
DT_ARM_MHU_1_LABEL,
&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);
}
@@ -0,0 +1,75 @@
/*
* 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))
#define SSE_200_DEVICE_BASE_REG_MSK (0xF0000000UL)

/* 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 2a7824a

Please sign in to comment.
You can’t perform that action at this time.