255 lines
8.9 KiB
C
255 lines
8.9 KiB
C
/*
|
|
* 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 <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)
|