sdk-hwV1.3/lichee/linux-4.9/drivers/rpbuf/rpbuf_service_rpmsg.c

157 lines
4.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* drivers/rpbuf/rpbuf_service_rpmsg.c
*
* (C) Copyright 2020-2025
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Junyan Lin <junyanlin@allwinnertech.com>
*
* RPMsg-based RPBuf service driver.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/rpmsg.h>
#include "rpbuf_internal.h"
struct rpmsg_rpbuf_service_instance {
struct rpmsg_device *rpdev;
};
static int rpmsg_rpbuf_service_cb(struct rpmsg_device *rpdev, void *data, int len,
void *priv, u32 src)
{
struct rpbuf_service *service = dev_get_drvdata(&rpdev->dev);
return rpbuf_service_get_notification(service, data, len);
}
static int rpmsg_rpbuf_service_notify(void *msg, int msg_len, void *priv)
{
struct rpmsg_rpbuf_service_instance *inst = priv;
struct rpmsg_device *rpdev = inst->rpdev;
return rpmsg_trysend(rpdev->ept, msg, msg_len);
}
static const struct rpbuf_service_ops rpmsg_rpbuf_service_ops = {
.notify = rpmsg_rpbuf_service_notify,
};
static int rpmsg_rpbuf_service_probe(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
struct rpmsg_rpbuf_service_instance *inst;
struct rpbuf_service *service;
struct device *dev_tmp;
int i;
int ret;
rpdev->announce = rpdev->src != RPMSG_ADDR_ANY;
inst = devm_kzalloc(dev, sizeof(struct rpmsg_rpbuf_service_instance), GFP_KERNEL);
if (IS_ERR_OR_NULL(inst)) {
dev_err(dev, "kzalloc for rpmsg_rpbuf_service_instance failed\n");
ret = -ENOMEM;
goto err_out;
}
inst->rpdev = rpdev;
service = rpbuf_create_service(dev, &rpmsg_rpbuf_service_ops, (void *)inst);
if (!service) {
dev_err(dev, "rpbuf_create_service failed\n");
ret = -ENOMEM;
goto err_free_inst;
}
/*
* We should find the underlying remoteproc device.
*
* Normally, the rpmsg device is created from:
* remoteproc device <--- defined by vendors, usually in device tree
* |--- rproc <--- rproc_alloc()
* |--- virtio <--- register_virtio_device()
* |--- rpmsg <--- rpmsg_register_device()
*
* Therefore the 4th parent of rpmsg device is the underlying remoteproc
* device.
*/
dev_tmp = dev;
for (i = 0; i < 3; i++) {
dev_tmp = dev_tmp->parent;
if (!dev_tmp) {
dev_err(dev, "cannot get rpmsg device parent %d\n", i);
ret = -ENOENT;
goto err_destroy_service;
}
dev_dbg(dev, "rpmsg device parent %d: %s\n", i, dev_name(dev_tmp));
}
/* We use the underlying remoteproc device as rpbuf link token */
ret = rpbuf_register_service(service, dev_tmp);
if (ret < 0) {
dev_err(dev, "rpbuf_register_service failed\n");
goto err_destroy_service;
}
dev_set_drvdata(dev, service);
return 0;
err_destroy_service:
rpbuf_destroy_service(service);
err_free_inst:
devm_kfree(dev, inst);
err_out:
return ret;
}
static void rpmsg_rpbuf_service_remove(struct rpmsg_device *rpdev)
{
struct device *dev = &rpdev->dev;
struct rpbuf_service *service = dev_get_drvdata(dev);
struct rpmsg_rpbuf_service_instance *inst = service->priv;
if (IS_ERR_OR_NULL(service)) {
dev_err(dev, "invalid rpbuf_service ptr\n");
return;
}
dev_set_drvdata(dev, NULL);
rpbuf_unregister_service(service);
rpbuf_destroy_service(service);
devm_kfree(dev, inst);
}
static struct rpmsg_device_id rpmsg_rpbuf_service_id_table[] = {
{ .name = "rpbuf-service" },
{ },
};
MODULE_DEVICE_TABLE(rpmsg, rpmsg_rpbuf_service_id_table);
static struct rpmsg_driver rpmsg_rpbuf_service = {
.drv.name = KBUILD_MODNAME,
.id_table = rpmsg_rpbuf_service_id_table,
.probe = rpmsg_rpbuf_service_probe,
.callback = rpmsg_rpbuf_service_cb,
.remove = rpmsg_rpbuf_service_remove,
};
#ifdef CONFIG_SUNXI_RPROC_FASTBOOT
fast_rpmsg_driver(rpmsg_rpbuf_service);
#else
module_rpmsg_driver(rpmsg_rpbuf_service);
#endif
MODULE_DESCRIPTION("RPMsg-based RPBuf service driver");
MODULE_AUTHOR("Junyan Lin <junyanlin@allwinnertech.com>");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0.0");