blob: a7a967e3d1b8bae76cefb746bf5faee4d1f5c5e4 [file] [log] [blame]
/*
* Copyright (c) 2023 Advanced Micro Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT xlnx_zynqmp_ipi_mailbox
#include "ipm_xlnx_ipi.h"
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/ipm.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.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_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)