diff --git a/drivers/platform/tegra/aon/Makefile b/drivers/platform/tegra/aon/Makefile index 77c8dd5c..56daa92d 100644 --- a/drivers/platform/tegra/aon/Makefile +++ b/drivers/platform/tegra/aon/Makefile @@ -10,6 +10,7 @@ LINUXINCLUDE += -I$(srctree.nvidia-oot)/drivers/platform/tegra/aon/include ccflags-y += -Werror obj-m += tegra234-aon.o +obj-m += tegra-aon-ivc-echo.o tegra234-aon-objs += \ tegra-aon-hsp.o \ diff --git a/drivers/platform/tegra/aon/tegra-aon-ivc-echo.c b/drivers/platform/tegra/aon/tegra-aon-ivc-echo.c new file mode 100644 index 00000000..f6d20ea9 --- /dev/null +++ b/drivers/platform/tegra/aon/tegra-aon-ivc-echo.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: Copyright (c) 2017-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + +#include +#include +#include +#include +#include +#include + +#define TX_BLOCK_PERIOD 100 +#define IVC_FRAME_SIZE 64 + +struct tegra_aon_ivc_echo_data { + struct mbox_client cl; + struct mbox_chan *mbox; + char rx_data[IVC_FRAME_SIZE]; +}; + +static ssize_t tegra_aon_ivc_echo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(dev); + + memcpy(buf, drvdata->rx_data, IVC_FRAME_SIZE); + + return IVC_FRAME_SIZE; +} + +static ssize_t tegra_aon_ivc_echo_tx(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(dev); + struct tegra_aon_mbox_msg msg; + int ret; + + if (count > IVC_FRAME_SIZE) { + dev_err(dev, "Message is greater than the frame size %d\n", + IVC_FRAME_SIZE); + return -EINVAL; + } + + msg.length = count; + msg.data = (void *)buf; + ret = mbox_send_message(drvdata->mbox, (void *)&msg); + if (ret < 0) + dev_err(dev, "mbox_send_message failed %d\n", ret); + + return count; +} + +static const DEVICE_ATTR(data_channel, S_IRUGO | S_IWUSR, + tegra_aon_ivc_echo_show, tegra_aon_ivc_echo_tx); + +static void tegra_aon_ivc_echo_rx(struct mbox_client *cl, void *data) +{ + struct tegra_aon_mbox_msg *msg = data; + struct tegra_aon_ivc_echo_data *drvdata = container_of(cl, + struct tegra_aon_ivc_echo_data, + cl); + memcpy(drvdata->rx_data, msg->data, IVC_FRAME_SIZE); +} + +static int tegra_aon_ivc_echo_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct tegra_aon_ivc_echo_data *drvdata; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + dev_set_drvdata(dev, drvdata); + drvdata->cl.dev = dev; + drvdata->cl.tx_block = true; + drvdata->cl.tx_tout = TX_BLOCK_PERIOD; + drvdata->cl.knows_txdone = false; + drvdata->cl.rx_callback = tegra_aon_ivc_echo_rx; + drvdata->mbox = mbox_request_channel(&drvdata->cl, 0); + if (IS_ERR(drvdata->mbox)) { + ret = PTR_ERR(drvdata->mbox); + if (ret != -EPROBE_DEFER) + dev_err(dev, "mbox_request_channel failed. Error %d\n", + ret); + return ret; + } + + ret = device_create_file(dev, &dev_attr_data_channel); + if (ret) { + dev_err(dev, "Failed to create device file. Error %d\n", ret); + mbox_free_channel(drvdata->mbox); + return ret; + } + + return 0; +} + +static int tegra_aon_ivc_echo_remove(struct platform_device *pdev) +{ + struct tegra_aon_ivc_echo_data *drvdata = dev_get_drvdata(&pdev->dev); + + device_remove_file(&pdev->dev, &dev_attr_data_channel); + mbox_free_channel(drvdata->mbox); + + return 0; +} + +static const struct of_device_id tegra_aon_ivc_echo_match[] = { + { .compatible = "nvidia,tegra186-aon-ivc-echo", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_aon_ivc_echo_match); + +static struct platform_driver tegra_aon_ivc_echo_driver = { + .probe = tegra_aon_ivc_echo_probe, + .remove = tegra_aon_ivc_echo_remove, + .driver = { + .name = "tegra-aon-ivc-echo", + .of_match_table = tegra_aon_ivc_echo_match, + }, +}; +module_platform_driver(tegra_aon_ivc_echo_driver); +MODULE_LICENSE("GPL v2");