Skip to content

Commit

Permalink
drivers: ipm: add zynqmp r5f support
Browse files Browse the repository at this point in the history
Add ipm driver to use Inter Processor Interrupts
on Xilinx ZynqMP platform. This patch also adds sample
application that shows use of xlnx ipm driver.

This driver uses default arm gic interrupt controller
and works only for lockstep mode of cortex-r5f
cluster for now.

In split mode the cortex-r5 cluster will
have two r5f cores and they are expected to work in AMP
mode. If both r5f cores run simultaneouly, only one of
the core is able to receive IPI interrupts at this time
and it will be the one that started later. In future
this limitation shall be removed.

Signed-off-by: Tanmay Shah <tanmay.shah@amd.com>
  • Loading branch information
tnmysh committed Aug 14, 2023
1 parent 58f6db9 commit 77a9a59
Show file tree
Hide file tree
Showing 5 changed files with 393 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 @@ -14,5 +14,6 @@ zephyr_library_sources_ifdef(CONFIG_IPM_CAVS_IDC ipm_cavs_idc.c)
zephyr_library_sources_ifdef(CONFIG_IPM_STM32_HSEM ipm_stm32_hsem.c)
zephyr_library_sources_ifdef(CONFIG_IPM_CAVS_HOST ipm_cavs_host.c)
zephyr_library_sources_ifdef(CONFIG_ESP32_SOFT_IPM ipm_esp32.c)
zephyr_library_sources_ifdef(CONFIG_XLNX_IPI ipm_xlnx_ipi.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE ipm_handlers.c)
zephyr_library_sources_ifdef(CONFIG_IPM_IVSHMEM ipm_ivshmem.c)
8 changes: 8 additions & 0 deletions drivers/ipm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ config IPM_IVSHMEM
help
Interprocessor driver using IVSHMEM Doorbell mechanism.

config XLNX_IPI
bool "AMD-Xilinx IPM driver"
default y
depends on DT_HAS_XLNX_ZYNQMP_IPI_MAILBOX_ENABLED
help
Inter Processor Interrupt driver for AMD-Xilinx
platforms such as ZynqMP Ultrascale+.

source "drivers/ipm/Kconfig.nrfx"
source "drivers/ipm/Kconfig.imx"
source "drivers/ipm/Kconfig.stm32"
Expand Down
262 changes: 262 additions & 0 deletions drivers/ipm/ipm_xlnx_ipi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/*
* Copyright (c) 2023 AMD-Xilinx Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT xlnx_zynqmp_ipi_mailbox

#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/ipm.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>

#include "ipm_xlnx_ipi.h"
LOG_MODULE_REGISTER(ipm_xlnx_ipi, CONFIG_IPM_LOG_LEVEL);

#define XLNX_IPI_MAX_BUF_SIZE_BYTES 32

struct xlnx_ipi_data {
size_t len;
void *user_data;
uint8_t data[];
};

struct xlnx_ipi_reg_info {
uint32_t ipi_ch_bit;
};

static const struct xlnx_ipi_reg_info xlnx_ipi_reg_info_zynqmp[] = {
{.ipi_ch_bit = IPI_CH0_BIT}, /* IPI CH ID 0 - Default APU */
{.ipi_ch_bit = IPI_CH1_BIT}, /* IPI CH ID 1 - Default RPU0 */
{.ipi_ch_bit = IPI_CH2_BIT}, /* IPI CH ID 2 - Default RPU1 */
{.ipi_ch_bit = IPI_CH3_BIT}, /* IPI CH ID 3 - Default PMU0 */
{.ipi_ch_bit = IPI_CH4_BIT}, /* IPI CH ID 4 - Default PMU1 */
{.ipi_ch_bit = IPI_CH5_BIT}, /* IPI CH ID 5 - Default PMU2 */
{.ipi_ch_bit = IPI_CH6_BIT}, /* IPI CH ID 6 - Default PMU3 */
{.ipi_ch_bit = IPI_CH7_BIT}, /* IPI CH ID 7 - Default PL0 */
{.ipi_ch_bit = IPI_CH8_BIT}, /* IPI CH ID 8 - Default PL1 */
{.ipi_ch_bit = IPI_CH9_BIT}, /* IPI CH ID 9 - Default PL2 */
{.ipi_ch_bit = IPI_CH10_BIT}, /* IPI CH ID 10 - Default PL3 */
};

struct xlnx_ipi_config {
uint32_t ipi_ch_bit;
uint32_t host_ipi_reg;
int (*xlnx_ipi_config_func)(const struct device *dev);
const struct device **cdev_list;
int num_cdev;
};

struct xlnx_ipi_child_data {
bool enabled;
ipm_callback_t ipm_callback;
void *user_data;
};

struct xlnx_ipi_child_config {
const char *node_id;
uint32_t local_request_region;
uint32_t local_response_region;
uint32_t remote_request_region;
uint32_t remote_response_region;
uint32_t host_ipi_reg;
uint32_t remote_ipi_id;
uint32_t remote_ipi_ch_bit;
};

static void xlnx_mailbox_rx_isr(const struct device *dev)
{
const struct xlnx_ipi_config *config;
const struct device **cdev_list;
const struct xlnx_ipi_child_config *cdev_conf;
const struct xlnx_ipi_child_data *cdev_data;
uint8_t ipi_buf[XLNX_IPI_MAX_BUF_SIZE_BYTES + sizeof(struct xlnx_ipi_data)];
int num_cdev;
struct xlnx_ipi_data *msg;
const struct device *cdev;
uint32_t remote_ipi_ch_bit;
int i, j;

config = dev->config;
cdev_list = config->cdev_list;
num_cdev = config->num_cdev;
msg = (struct xlnx_ipi_data *)ipi_buf;
for (i = 0; i < num_cdev; i++) {
cdev = cdev_list[i];
cdev_conf = cdev->config;
cdev_data = cdev->data;

if (!cdev_data->enabled) {
continue;
}

remote_ipi_ch_bit = cdev_conf->remote_ipi_ch_bit;
if (!sys_test_bit(config->host_ipi_reg + IPI_ISR, remote_ipi_ch_bit)) {
continue;
}

msg->len = XLNX_IPI_MAX_BUF_SIZE_BYTES;
msg->user_data = cdev_data->user_data;
for (j = 0; j < XLNX_IPI_MAX_BUF_SIZE_BYTES; j++) {
msg->data[j] = sys_read8(cdev_conf->remote_request_region + j);
}
if (cdev_data->ipm_callback) {
cdev_data->ipm_callback(cdev, cdev_data->user_data,
cdev_conf->remote_ipi_id, msg);
}
sys_set_bit(config->host_ipi_reg + IPI_ISR, remote_ipi_ch_bit);
}
}

static int xlnx_ipi_child_init(const struct device *dev)
{
const struct xlnx_ipi_child_config *cdev_conf;

cdev_conf = dev->config;

return 0;
}

static int xlnx_ipi_send(const struct device *ipmdev, int wait, uint32_t id, const void *data,
int size)
{
const uint8_t *msg = (uint8_t *)data;
const struct xlnx_ipi_child_config *config = ipmdev->config;
unsigned int key;
int i, obs_bit;

ARG_UNUSED(id);

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

key = irq_lock();
if (msg) {
/* Write buffer to send data */
for (i = 0; i < size; i++) {
sys_write8(msg[i], config->local_request_region + i);
}
}
irq_unlock(key);

sys_set_bit(config->host_ipi_reg + IPI_TRIG, config->remote_ipi_ch_bit);

obs_bit = 0;
do {
obs_bit = sys_test_bit(config->host_ipi_reg + IPI_OBS, config->remote_ipi_ch_bit);
} while (obs_bit && wait);

return 0;
}

static void xlnx_ipi_register_callback(const struct device *port, ipm_callback_t cb,
void *user_data)
{
struct xlnx_ipi_child_data *data = port->data;

data->ipm_callback = cb;
data->user_data = user_data;
}

static int xlnx_ipi_max_data_size_get(const struct device *ipmdev)
{
return XLNX_IPI_MAX_BUF_SIZE_BYTES;
}

static uint32_t xlnx_ipi_max_id_val_get(const struct device *ipmdev)
{
return UINT32_MAX;
}

static int xlnx_ipi_set_enabled(const struct device *ipmdev, int enable)
{
const struct xlnx_ipi_child_config *config = ipmdev->config;
struct xlnx_ipi_child_data *data = ipmdev->data;

if (enable) {
sys_set_bit(config->host_ipi_reg + IPI_IER, config->remote_ipi_ch_bit);
} else {
sys_set_bit(config->host_ipi_reg + IPI_IDR, config->remote_ipi_ch_bit);
}

/* If IPI channel bit in IPI Mask Register is not set, then interrupt is enabled */
if (!sys_test_bit(config->host_ipi_reg + IPI_IMR, config->remote_ipi_ch_bit)) {
data->enabled = enable;
return 0;
}

return -EINVAL;
}

static int xlnx_ipi_init(const struct device *dev)
{
const struct xlnx_ipi_config *conf = dev->config;

/* disable all the interrupts */
sys_write32(0xFFFFFFFF, conf->host_ipi_reg + IPI_IDR);

/* clear status of any previous interrupts */
sys_write32(0xFFFFFFFF, conf->host_ipi_reg + IPI_ISR);

conf->xlnx_ipi_config_func(dev);

return 0;
}

static struct ipm_driver_api xlnx_ipi_api = {
.send = xlnx_ipi_send,
.register_callback = xlnx_ipi_register_callback,
.max_data_size_get = xlnx_ipi_max_data_size_get,
.max_id_val_get = xlnx_ipi_max_id_val_get,
.set_enabled = xlnx_ipi_set_enabled,
};

#define GET_CHILD_DEV(node_id) DEVICE_DT_GET(node_id),

#define XLNX_IPI_CHILD(ch_node) \
struct xlnx_ipi_child_data xlnx_ipi_child_data##ch_node = { \
.enabled = false, \
.ipm_callback = NULL, \
}; \
struct xlnx_ipi_child_config xlnx_ipi_child_config##ch_node = { \
.local_request_region = DT_REG_ADDR_BY_NAME(ch_node, local_request_region), \
.local_response_region = DT_REG_ADDR_BY_NAME(ch_node, local_response_region), \
.remote_request_region = DT_REG_ADDR_BY_NAME(ch_node, remote_request_region), \
.remote_response_region = DT_REG_ADDR_BY_NAME(ch_node, remote_response_region), \
.remote_ipi_id = DT_PROP(ch_node, remote_ipi_id), \
.remote_ipi_ch_bit = \
xlnx_ipi_reg_info_zynqmp[DT_PROP(ch_node, remote_ipi_id)].ipi_ch_bit, \
.host_ipi_reg = DT_REG_ADDR_BY_NAME(DT_PARENT(ch_node), host_ipi_reg), \
}; \
DEVICE_DT_DEFINE(ch_node, &xlnx_ipi_child_init, NULL, &xlnx_ipi_child_data##ch_node, \
&xlnx_ipi_child_config##ch_node, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &xlnx_ipi_api);

#define XLNX_IPI(inst) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, XLNX_IPI_CHILD); \
static const struct device *cdev##inst[] = { \
DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, GET_CHILD_DEV)}; \
static int xlnx_ipi_config_func##inst(const struct device *dev); \
struct xlnx_ipi_config xlnx_ipi_config##inst = { \
.host_ipi_reg = DT_INST_REG_ADDR_BY_NAME(inst, host_ipi_reg), \
.xlnx_ipi_config_func = xlnx_ipi_config_func##inst, \
.cdev_list = cdev##inst, \
.num_cdev = ARRAY_SIZE(cdev##inst), \
}; \
DEVICE_DT_INST_DEFINE(inst, &xlnx_ipi_init, NULL, NULL, /* data */ \
&xlnx_ipi_config##inst, /* conf */ \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, NULL); \
static int xlnx_ipi_config_func##inst(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), xlnx_mailbox_rx_isr, \
DEVICE_DT_INST_GET(inst), 0); \
irq_enable(DT_INST_IRQN(inst)); \
LOG_DBG("irq %d is enabled: %s\n", DT_INST_IRQN(inst), \
irq_is_enabled(DT_INST_IRQN(inst)) ? "true" : "false"); \
return 0; \
}

DT_INST_FOREACH_STATUS_OKAY(XLNX_IPI)
77 changes: 77 additions & 0 deletions dts/arm/xilinx/zynqmp_rpu.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,83 @@
};

soc {

rpu0_ipi: zynqmp-ipi@ff310000 {
status = "disabled";
compatible = "xlnx,zynqmp-ipi-mailbox";
#address-cells = <1>;
#size-cells = <1>;

reg = <0xff310000 0x10000>;
reg-names = "host_ipi_reg";
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL
IRQ_DEFAULT_PRIORITY>;
local-ipi-id = <1>;

rpu0_apu_mailbox: mailbox@ff990200 {
remote-ipi-id = <0>;
reg = <0xff990200 0x20>,
<0xff990220 0x20>,
<0xff990040 0x20>,
<0xff990060 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
};

rpu0_rpu1_mailbox: mailbox@ff990260 {
remote-ipi-id = <2>;
reg = <0xff990260 0x20>,
<0xff990280 0x20>,
<0xff990420 0x20>,
<0xff990440 0x20>;

reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
};
};

rpu1_ipi: zynqmp-ipi@ff320000 {
status = "disabled";
#address-cells = <1>;
#size-cells = <1>;
compatible = "xlnx,zynqmp-ipi-mailbox";
local-ipi-id = <2>;

reg = <0xff320000 0x10000>;
reg-names = "host_ipi_reg";

interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL
IRQ_DEFAULT_PRIORITY>;

rpu1_apu_mailbox: mailbox@ff990400 {
remote-ipi-id = <0>;
reg = <0xff990400 0x20>,
<0xff990420 0x20>,
<0xff990080 0x20>,
<0xff9900a0 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
};

rpu1_rpu0_mailbox: mailbox@ff990420 {
remote-ipi-id = <1>;
reg = <0xff990420 0x20>,
<0xff990440 0x20>,
<0xff990260 0x20>,
<0xff990280 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
};
};

interrupt-parent = <&gic>;

gic: interrupt-controller@f9000000 {
Expand Down
Loading

0 comments on commit 77a9a59

Please sign in to comment.