diff --git a/drivers/ipm/CMakeLists.txt b/drivers/ipm/CMakeLists.txt index 1da9b759b289f8b..f2d37bb67c95159 100644 --- a/drivers/ipm/CMakeLists.txt +++ b/drivers/ipm/CMakeLists.txt @@ -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) diff --git a/drivers/ipm/Kconfig b/drivers/ipm/Kconfig index d8aef8e0d3c3299..17c92b7e2417dd9 100644 --- a/drivers/ipm/Kconfig +++ b/drivers/ipm/Kconfig @@ -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" diff --git a/drivers/ipm/ipm_xlnx_ipi.c b/drivers/ipm/ipm_xlnx_ipi.c new file mode 100644 index 000000000000000..fe5b76a5d549e0d --- /dev/null +++ b/drivers/ipm/ipm_xlnx_ipi.c @@ -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 +#include +#include +#include +#include + +#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) diff --git a/dts/arm/xilinx/zynqmp_rpu.dtsi b/dts/arm/xilinx/zynqmp_rpu.dtsi index 88fd4d7014148ae..4c7ff8634585f7a 100644 --- a/dts/arm/xilinx/zynqmp_rpu.dtsi +++ b/dts/arm/xilinx/zynqmp_rpu.dtsi @@ -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 = ; + 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 = ; + + 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 { diff --git a/dts/bindings/ipm/xlnx,zynqmp-ipi-mailbox.yaml b/dts/bindings/ipm/xlnx,zynqmp-ipi-mailbox.yaml new file mode 100644 index 000000000000000..88416ee43860229 --- /dev/null +++ b/dts/bindings/ipm/xlnx,zynqmp-ipi-mailbox.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: Apache-2.0 + +description: | + The Xilinx IPI(Inter Processor Interrupt) mailbox controller is to manage + messaging between two Xilinx Zynq UltraScale+ MPSoC IPI agents. Each IPI + agent owns registers used for notification and buffers for message. + +compatible: "xlnx,zynqmp-ipi-mailbox" + +include: base.yaml + +properties: + + reg: + type: array + description: IPI control and status register space + + reg-names: + type: string-array + description: host_ipi_reg + + local-ipi-id: + type: int + description: Host Xilinx IPI agent ID of which the mailbox is connected to. + required: true + +child-binding: + description: Xilinx IPI agent child node + + properties: + reg: + type: array + description: register space for IPI message buffers + required: true + + reg-names: + type: string-array + description: name of each register space + required: true + + remote-ipi-id: + type: int + description: + Remote Xilinx IPI agent ID of which the mailbox is connected to. + required: true