Files
opensbi/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c
Anup Patel e4bc55930b lib: utils: Populate MPXY channel attributes from RPMI channel attributes
Use the RPMI mailbox channel attributes to populate MPXY channel
attributes instead of hard coding them.

Signed-off-by: Anup Patel <apatel@ventanamicro.com>
2025-02-13 11:10:03 +05:30

346 lines
9.0 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2024 Ventana Micro Systems Inc.
*
* Authors:
* Rahul Pathak <rpathak@ventanamicro.com>
* Anup Patel <apatel@ventanamicro.com>
*/
#include <libfdt.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_heap.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/mpxy/fdt_mpxy_rpmi_mbox.h>
#include <sbi/sbi_console.h>
/**
* MPXY mbox instance per MPXY channel. This ties
* an MPXY channel with an RPMI Service group.
*/
struct mpxy_rpmi_mbox {
struct mbox_chan *chan;
const struct mpxy_rpmi_mbox_data *mbox_data;
struct mpxy_rpmi_channel_attrs msgprot_attrs;
struct sbi_mpxy_channel channel;
void *group_context;
};
/**
* Discover the RPMI service data using message_id
* MPXY message_id == RPMI service_id
*/
static const struct mpxy_rpmi_service_data *mpxy_find_rpmi_srvid(u32 message_id,
const struct mpxy_rpmi_mbox_data *mbox_data)
{
const struct mpxy_rpmi_service_data *srv = mbox_data->service_data;
int mid = 0;
for (mid = 0; srv[mid].id < mbox_data->num_services; mid++) {
if (srv[mid].id == (u8)message_id)
return &srv[mid];
}
return NULL;
}
/** Copy attributes word size */
static void mpxy_copy_attrs(u32 *outmem, u32 *inmem, u32 count)
{
u32 idx;
for (idx = 0; idx < count; idx++)
outmem[idx] = cpu_to_le32(inmem[idx]);
}
static int mpxy_mbox_read_attributes(struct sbi_mpxy_channel *channel,
u32 *outmem, u32 base_attr_id,
u32 attr_count)
{
struct mpxy_rpmi_mbox *rmb =
container_of(channel, struct mpxy_rpmi_mbox, channel);
u32 *attr_array = (u32 *)&rmb->msgprot_attrs;
u32 end_id = base_attr_id + attr_count - 1;
if (end_id >= MPXY_MSGPROT_RPMI_ATTR_MAX_ID)
return SBI_EBAD_RANGE;
mpxy_copy_attrs(outmem, &attr_array[attr_id2index(base_attr_id)],
attr_count);
return SBI_OK;
}
/**
* Verify the channel standard attribute wrt to write permission
* and the value to be set if valid or not.
* Only attributes needs to be checked which are defined Read/Write
* permission. Other with Readonly permission will result in error.
*
* Attributes values to be written must also be checked because
* before writing a range of attributes, we need to make sure that
* either complete range of attributes is written successfully or not
* at all.
*/
static int mpxy_check_write_attr(u32 attr_id, u32 attr_val)
{
int ret = SBI_OK;
switch(attr_id) {
/** All RO access attributes falls under default */
default:
ret = SBI_EBAD_RANGE;
};
return ret;
}
static void mpxy_write_attr(struct mpxy_rpmi_channel_attrs *attrs,
u32 attr_id,
u32 attr_val)
{
/* No writable attributes in RPMI */
}
static int mpxy_mbox_write_attributes(struct sbi_mpxy_channel *channel,
u32 *outmem, u32 base_attr_id,
u32 attr_count)
{
struct mpxy_rpmi_mbox *rmb =
container_of(channel, struct mpxy_rpmi_mbox, channel);
u32 end_id = base_attr_id + attr_count - 1;
u32 attr_val, idx;
int ret, mem_idx;
if (end_id >= MPXY_MSGPROT_RPMI_ATTR_MAX_ID)
return SBI_EBAD_RANGE;
mem_idx = 0;
for (idx = base_attr_id; idx <= end_id; idx++) {
attr_val = le32_to_cpu(outmem[mem_idx++]);
ret = mpxy_check_write_attr(idx, attr_val);
if (ret)
return ret;
}
mem_idx = 0;
for (idx = base_attr_id; idx <= end_id; idx++) {
attr_val = le32_to_cpu(outmem[mem_idx++]);
mpxy_write_attr(&rmb->msgprot_attrs, idx, attr_val);
}
return SBI_OK;
}
static int __mpxy_mbox_send_message(struct sbi_mpxy_channel *channel,
u32 message_id, void *tx, u32 tx_len,
void *rx, u32 rx_max_len,
unsigned long *ack_len)
{
struct mpxy_rpmi_mbox *rmb =
container_of(channel, struct mpxy_rpmi_mbox, channel);
const struct mpxy_rpmi_mbox_data *data = rmb->mbox_data;
const struct mpxy_rpmi_service_data *srv =
mpxy_find_rpmi_srvid(message_id, data);
struct rpmi_message_args args = {0};
struct mbox_xfer xfer;
u32 rx_len = 0;
int ret;
if (!srv)
return SBI_ENOTSUPP;
/** message data size to be sent is in the
* supported service data size range */
if (tx_len < srv->min_tx_len || tx_len > srv->max_tx_len)
return SBI_EFAIL;
if (ack_len) {
/** MPXY buffer cannnot hold service min response data */
if (rx_max_len < srv->min_rx_len)
return SBI_EFAIL;
/* Max rx data size it can expect */
if (srv->max_rx_len < channel->attrs.msg_data_maxlen)
rx_len = srv->max_rx_len;
else
rx_len = channel->attrs.msg_data_maxlen;
args.type = RPMI_MSG_NORMAL_REQUEST;
args.flags = (rx) ? 0 : RPMI_MSG_FLAGS_NO_RX;
args.service_id = srv->id;
mbox_xfer_init_txrx(&xfer, &args,
tx, tx_len, RPMI_DEF_TX_TIMEOUT,
rx, rx_len, RPMI_DEF_RX_TIMEOUT);
}
else {
args.type = RPMI_MSG_POSTED_REQUEST;
args.flags = RPMI_MSG_FLAGS_NO_RX;
args.service_id = srv->id;
mbox_xfer_init_tx(&xfer, &args,
tx, tx_len, RPMI_DEF_TX_TIMEOUT);
}
if (data->xfer_group)
ret = data->xfer_group(rmb->group_context, rmb->chan, &xfer);
else
ret = mbox_chan_xfer(rmb->chan, &xfer);
if (ret)
return ret;
if (ack_len)
*ack_len = args.rx_data_len;
return SBI_OK;
}
static int mpxy_mbox_send_message_withresp(struct sbi_mpxy_channel *channel,
u32 message_id, void *tx, u32 tx_len,
void *rx, u32 rx_max_len,
unsigned long *ack_len)
{
return __mpxy_mbox_send_message(channel, message_id, tx, tx_len,
rx, rx_max_len, ack_len);
}
static int mpxy_mbox_send_message_withoutresp(struct sbi_mpxy_channel *channel,
u32 message_id, void *tx, u32 tx_len)
{
return __mpxy_mbox_send_message(channel, message_id, tx, tx_len,
NULL, 0, NULL);
}
int mpxy_rpmi_mbox_init(const void *fdt, int nodeoff, const struct fdt_match *match)
{
u32 channel_id, servicegrp_ver, pro_ver, max_data_len, tx_tout, rx_tout;
const struct mpxy_rpmi_mbox_data *data = match->data;
struct mpxy_rpmi_mbox *rmb;
struct mbox_chan *chan;
const fdt32_t *val;
int rc, len;
/* Allocate context for MPXY mbox client */
rmb = sbi_zalloc(sizeof(*rmb));
if (!rmb)
return SBI_ENOMEM;
/*
* If channel request failed then other end does not support
* service group so do nothing.
*/
rc = fdt_mailbox_request_chan(fdt, nodeoff, 0, &chan);
if (rc) {
rc = SBI_ENODEV;
goto fail_free_client;
}
/* Match channel service group id */
if (data->servicegrp_id != chan->chan_args[0]) {
rc = SBI_EINVAL;
goto fail_free_chan;
}
/* Get channel protocol version */
rc = mbox_chan_get_attribute(chan, RPMI_CHANNEL_ATTR_PROTOCOL_VERSION,
&pro_ver);
if (rc)
goto fail_free_chan;
/* Get channel maximum data length */
rc = mbox_chan_get_attribute(chan, RPMI_CHANNEL_ATTR_MAX_DATA_LEN,
&max_data_len);
if (rc)
goto fail_free_chan;
/* Get channel Tx timeout */
rc = mbox_chan_get_attribute(chan, RPMI_CHANNEL_ATTR_TX_TIMEOUT,
&tx_tout);
if (rc)
goto fail_free_chan;
/* Get channel Rx timeout */
rc = mbox_chan_get_attribute(chan, RPMI_CHANNEL_ATTR_RX_TIMEOUT,
&rx_tout);
if (rc)
goto fail_free_chan;
/* Get channel service group version */
rc = mbox_chan_get_attribute(chan, RPMI_CHANNEL_ATTR_SERVICEGROUP_VERSION,
&servicegrp_ver);
if (rc)
goto fail_free_chan;
/*
* The "riscv,sbi-mpxy-channel-id" DT property is mandatory
* for MPXY RPMI mailbox client driver so if this is not
* present then try other drivers.
*/
val = fdt_getprop(fdt, nodeoff, "riscv,sbi-mpxy-channel-id", &len);
if (len > 0 && val) {
channel_id = fdt32_to_cpu(*val);
} else {
rc = SBI_ENODEV;
goto fail_free_chan;
}
/* Setup MPXY mbox client */
/* Channel ID*/
rmb->channel.channel_id = channel_id;
/* Callback for read RPMI attributes */
rmb->channel.read_attributes = mpxy_mbox_read_attributes;
/* Callback for write RPMI attributes */
rmb->channel.write_attributes = mpxy_mbox_write_attributes;
/* Callback for sending RPMI message */
rmb->channel.send_message_with_response =
mpxy_mbox_send_message_withresp;
rmb->channel.send_message_without_response =
mpxy_mbox_send_message_withoutresp;
/* RPMI Message Protocol ID */
rmb->channel.attrs.msg_proto_id = SBI_MPXY_MSGPROTO_RPMI_ID;
/* RPMI Message Protocol Version */
rmb->channel.attrs.msg_proto_version = pro_ver;
/*
* RPMI supported max message data length(bytes), same for
* all service groups
*/
rmb->channel.attrs.msg_data_maxlen = max_data_len;
/*
* RPMI message send timeout(milliseconds)
* same for all service groups
*/
rmb->channel.attrs.msg_send_timeout = tx_tout;
rmb->channel.attrs.msg_completion_timeout = tx_tout + rx_tout;
/* RPMI service group attributes */
rmb->msgprot_attrs.servicegrp_id = data->servicegrp_id;
rmb->msgprot_attrs.servicegrp_ver = servicegrp_ver;
rmb->mbox_data = data;
rmb->chan = chan;
/* Setup RPMI service group context */
if (data->setup_group) {
rc = data->setup_group(&rmb->group_context, chan, data);
if (rc)
goto fail_free_chan;
}
/* Register RPMI service group */
rc = sbi_mpxy_register_channel(&rmb->channel);
if (rc) {
if (data->cleanup_group)
data->cleanup_group(rmb->group_context);
goto fail_free_chan;
}
return SBI_OK;
fail_free_chan:
mbox_controller_free_chan(chan);
fail_free_client:
sbi_free(rmb);
return rc;
}