/* * Copyright (c) 2023 AMD-Xilinx Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT xlnx_zynqmp_ipi_mailbox #include "ipm_xlnx_ipi.h" #include #include #include #include #include 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_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, NULL, 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)