-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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: Tanmay Shah <tanmay.shah@amd.com>
- Loading branch information
Showing
5 changed files
with
393 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.