Commit 09e2a4e9 authored by Tanmay Shah's avatar Tanmay Shah Committed by Fabio Baltieri
Browse files

drivers: ipm: add zynqmp r5f support



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: default avatarTanmay Shah <tanmay.shah@amd.com>
parent 33a25371
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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"
+262 −0
Original line number Diff line number Diff line
/*
 * 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)
+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2023, Advanced Micro Devices Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef _IPM_XLNX_IPI_H_
#define _IPM_XLNX_IPI_H_

/* IPI Channel ID bits */
#define IPI_CH0_BIT  0
#define IPI_CH1_BIT  8
#define IPI_CH2_BIT  9
#define IPI_CH3_BIT  16
#define IPI_CH4_BIT  17
#define IPI_CH5_BIT  18
#define IPI_CH6_BIT  19
#define IPI_CH7_BIT  24
#define IPI_CH8_BIT  25
#define IPI_CH9_BIT  26
#define IPI_CH10_BIT 27

/* Register offsets */
#define IPI_TRIG 0x00
#define IPI_OBS  0x04
#define IPI_ISR  0x10
#define IPI_IMR  0x14
#define IPI_IER  0x18
#define IPI_IDR  0x1C

#endif /* _IPM_XLNX_IPI_H_ */
+77 −0
Original line number Diff line number Diff line
@@ -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 {
Loading