164 lines
3.6 KiB
C
164 lines
3.6 KiB
C
// 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() + 10000000; /* 10s 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;
|
|
}
|