blob: 33f248645380c0e96d3d9bb3042336a0a0a5d2f4 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 NXP
*
*/
#include <common.h>
#include <asm/io.h>
#include <dm.h>
#include <dm/lists.h>
#include <dm/root.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <misc.h>
#include <asm/mach-imx/imx_vservice.h>
#include <imx_m4_mu.h>
static LIST_HEAD(vservice_channels);
void * __weak board_imx_vservice_get_buffer(struct imx_vservice_channel *node, u32 size)
{
if (size <= CONFIG_IMX_VSERVICE_SHARED_BUFFER_SIZE)
return (void * )CONFIG_IMX_VSERVICE_SHARED_BUFFER;
return NULL;
}
void * imx_vservice_get_buffer(struct imx_vservice_channel *node, u32 size)
{
return board_imx_vservice_get_buffer(node, size);
}
int imx_vservice_blocking_request(struct imx_vservice_channel *node, u8 *buf, u32* size)
{
int ret = 0;
union imx_m4_msg msg;
msg.format.seq = node->msg_seq;
msg.format.type = MU_MSG_REQ;
msg.format.buffer = (u32)(ulong)buf;
msg.format.size = *size;
ret = misc_call(node->mu_dev, 1000000, &msg, 4, &msg, 4);
if (ret) {
printf("%s: Send request MU message failed, ret %d\n", __func__, ret);
goto MU_ERR;
}
if (msg.format.type != MU_MSG_RESP|| msg.format.seq != node->msg_seq) {
printf("%s: wrong msg response: type %d, seq %d, expect seq %d\n",
__func__, msg.format.type, msg.format.seq, node->msg_seq);
ret = -EIO;
goto MU_ERR;
}
*size = msg.format.size;
MU_ERR:
node->msg_seq++;
return ret;
}
static int imx_vservice_connect(struct imx_vservice_channel *node)
{
int ret = 0;
union imx_m4_msg msg;
unsigned long timeout = timer_get_us() + 2000000; /* 2s timeout */
for (;;) {
msg.format.seq = 0;
msg.format.type = MU_MSG_READY_A;
msg.format.buffer = 0;
msg.format.size = 0;
ret = misc_call(node->mu_dev, 100000, &msg, 4, &msg, 4);
if (!ret && msg.format.type == MU_MSG_READY_B)
return 0;
if (time_after(timer_get_us(), timeout)) {
printf("%s: Timeout to connect peer, %d\n", __func__, ret);
return -ETIMEDOUT;
}
}
return -EIO;
}
struct udevice * __weak board_imx_vservice_find_mu(struct udevice *virt_dev)
{
int ret;
struct ofnode_phandle_args args;
struct udevice *mu_dev;
/* Default get mu from "fsl,vservice-mu" property*/
ret = dev_read_phandle_with_args(virt_dev, "fsl,vservice-mu",
NULL, 0, 0, &args);
if (ret) {
printf("Can't find \"fsl,vservice-mu\" property\n");
return NULL;
}
ret = uclass_find_device_by_ofnode(UCLASS_MISC, args.node, &mu_dev);
if (ret) {
printf("Can't find MU device, err %d\n", ret);
return NULL;
}
return mu_dev;
}
static struct udevice * imx_vservice_find_mu(struct udevice *virt_dev)
{
return board_imx_vservice_find_mu(virt_dev);
}
struct imx_vservice_channel * imx_vservice_setup(struct udevice *virt_dev)
{
int ret;
struct udevice *mu_dev;
struct imx_vservice_channel *channel;
mu_dev = imx_vservice_find_mu(virt_dev);
if (mu_dev == NULL) {
printf("No MU device for virtual service %s connection\n", virt_dev->name);
return NULL;
}
ret = device_probe(mu_dev);
if (ret) {
printf("Probe MU device failed\n");
return NULL;
}
list_for_each_entry(channel, &vservice_channels, channel_head) {
if (channel->mu_dev == mu_dev)
return channel;
}
channel = malloc(sizeof(struct imx_vservice_channel));
if (!channel) {
printf("Malloc vservice channel is failed\n");
return NULL;
}
channel->msg_seq = 0;
channel->mu_dev = mu_dev;
INIT_LIST_HEAD(&channel->channel_head);
ret = imx_vservice_connect(channel);
if (ret) {
printf("VService: Connection is failed, ret %d\n", ret);
free(channel);
return NULL;
}
list_add_tail(&channel->channel_head, &vservice_channels);
printf("VService: Connection is ok on MU %s\n", mu_dev->name);
return channel;
}