mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
- This patch includes AON-KMD module as part of OOT kernel. Bug 3583580 Change-Id: I531731136189d76ebb4d3f2880e8f46913f390f4 Signed-off-by: Akhilesh Khumbum <akhumbum@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2870990 Reviewed-by: Robert Collins <rcollins@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
220 lines
4.8 KiB
C
220 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/version.h>
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/mailbox_client.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/err.h>
|
|
|
|
#include <aon.h>
|
|
#include <aon-hsp-combo.h>
|
|
|
|
#define TX_BLOCK_PERIOD 20
|
|
|
|
struct aon_hsp_sm {
|
|
struct mbox_client client;
|
|
struct mbox_chan *chan;
|
|
};
|
|
|
|
struct aon_hsp {
|
|
struct aon_hsp_sm rx;
|
|
struct aon_hsp_sm tx;
|
|
struct device dev;
|
|
struct completion emptied;
|
|
void (*full_notify)(void *data, u32 value);
|
|
void *pdata;
|
|
};
|
|
|
|
static void aon_hsp_rx_full_notify(struct mbox_client *cl, void *data)
|
|
{
|
|
struct aon_hsp *aonhsp = dev_get_drvdata(cl->dev);
|
|
u32 msg = (u32) (unsigned long) data;
|
|
|
|
aonhsp->full_notify(aonhsp->pdata, msg);
|
|
}
|
|
|
|
static void aon_hsp_tx_empty_notify(struct mbox_client *cl, void *data,
|
|
int empty_value)
|
|
{
|
|
struct aon_hsp *aonhsp = dev_get_drvdata(cl->dev);
|
|
|
|
(void)empty_value; /* ignored */
|
|
|
|
complete(&aonhsp->emptied);
|
|
}
|
|
|
|
static int aon_hsp_probe(struct aon_hsp *aonhsp)
|
|
{
|
|
struct device_node *np = aonhsp->dev.parent->of_node;
|
|
int err = -ENODEV;
|
|
|
|
np = of_get_compatible_child(np, "nvidia,tegra-aon-hsp");
|
|
if (np == NULL || !of_device_is_available(np)) {
|
|
of_node_put(np);
|
|
dev_err(&aonhsp->dev, "no hsp protocol \"%s\"\n",
|
|
"nvidia,tegra-aon-hsp");
|
|
return -ENODEV;
|
|
}
|
|
|
|
aonhsp->dev.of_node = np;
|
|
|
|
aonhsp->rx.chan = mbox_request_channel_byname(&aonhsp->rx.client,
|
|
"ivc-rx");
|
|
if (IS_ERR(aonhsp->rx.chan)) {
|
|
err = PTR_ERR(aonhsp->rx.chan);
|
|
goto fail;
|
|
}
|
|
|
|
aonhsp->tx.chan = mbox_request_channel_byname(&aonhsp->tx.client,
|
|
"ivc-tx");
|
|
if (IS_ERR(aonhsp->tx.chan)) {
|
|
err = PTR_ERR(aonhsp->tx.chan);
|
|
goto fail;
|
|
}
|
|
|
|
dev_set_name(&aonhsp->dev, "%s:%s", dev_name(aonhsp->dev.parent),
|
|
aonhsp->dev.of_node->name);
|
|
dev_info(&aonhsp->dev, "probed\n");
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
if (err != -EPROBE_DEFER) {
|
|
dev_err(&aonhsp->dev, "%s: failed to obtain : %d\n",
|
|
np->name, err);
|
|
}
|
|
of_node_put(np);
|
|
return err;
|
|
}
|
|
|
|
static const struct device_type aon_hsp_combo_dev_type = {
|
|
.name = "aon-hsp-protocol",
|
|
};
|
|
|
|
static void aon_hsp_combo_dev_release(struct device *dev)
|
|
{
|
|
struct aon_hsp *aonhsp = container_of(dev, struct aon_hsp, dev);
|
|
|
|
if (!IS_ERR_OR_NULL(aonhsp->rx.chan))
|
|
mbox_free_channel(aonhsp->rx.chan);
|
|
if (!IS_ERR_OR_NULL(aonhsp->tx.chan))
|
|
mbox_free_channel(aonhsp->tx.chan);
|
|
|
|
of_node_put(dev->of_node);
|
|
kfree(aonhsp);
|
|
}
|
|
|
|
static void aon_hsp_free(struct aon_hsp *aonhsp)
|
|
{
|
|
if (IS_ERR_OR_NULL(aonhsp))
|
|
return;
|
|
|
|
if (dev_get_drvdata(&aonhsp->dev) != NULL)
|
|
device_unregister(&aonhsp->dev);
|
|
else
|
|
put_device(&aonhsp->dev);
|
|
}
|
|
|
|
static struct aon_hsp *aon_hsp_create(struct device *dev,
|
|
void (*full_notify)(void *data, u32 value),
|
|
void *pdata)
|
|
{
|
|
struct aon_hsp *aonhsp;
|
|
int ret = -EINVAL;
|
|
|
|
aonhsp = kzalloc(sizeof(*aonhsp), GFP_KERNEL);
|
|
if (aonhsp == NULL)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
aonhsp->dev.parent = dev;
|
|
aonhsp->full_notify = full_notify;
|
|
aonhsp->pdata = pdata;
|
|
|
|
init_completion(&aonhsp->emptied);
|
|
|
|
aonhsp->dev.type = &aon_hsp_combo_dev_type;
|
|
aonhsp->dev.release = aon_hsp_combo_dev_release;
|
|
device_initialize(&aonhsp->dev);
|
|
|
|
dev_set_name(&aonhsp->dev, "%s:%s", dev_name(dev), "hsp");
|
|
|
|
aonhsp->tx.client.tx_block = false;
|
|
aonhsp->rx.client.rx_callback = aon_hsp_rx_full_notify;
|
|
aonhsp->tx.client.tx_done = aon_hsp_tx_empty_notify;
|
|
aonhsp->rx.client.dev = aonhsp->tx.client.dev = &(aonhsp->dev);
|
|
|
|
ret = aon_hsp_probe(aonhsp);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
ret = device_add(&aonhsp->dev);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
dev_set_drvdata(&aonhsp->dev, aonhsp);
|
|
|
|
return aonhsp;
|
|
|
|
fail:
|
|
aon_hsp_free(aonhsp);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
bool tegra_aon_hsp_sm_tx_is_empty(struct tegra_aon *aon)
|
|
{
|
|
struct aon_hsp *aonhsp = aon->hsp;
|
|
|
|
return try_wait_for_completion(&aonhsp->emptied);
|
|
}
|
|
|
|
int tegra_aon_hsp_sm_tx_write(struct tegra_aon *aon, u32 value)
|
|
{
|
|
struct aon_hsp *aonhsp = aon->hsp;
|
|
|
|
return mbox_send_message(aonhsp->tx.chan,
|
|
(void *) (unsigned long) value);
|
|
}
|
|
|
|
int tegra_aon_hsp_sm_pair_request(struct tegra_aon *aon,
|
|
void (*full_notify)(void *data, u32 value),
|
|
void *pdata)
|
|
{
|
|
struct device_node *hsp_node;
|
|
struct device *dev = aon->dev;
|
|
struct device_node *dn = dev->of_node;
|
|
|
|
hsp_node = of_get_child_by_name(dn, "hsp");
|
|
if (hsp_node == NULL) {
|
|
dev_err(dev, "No hsp child node for AON\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
aon->hsp = aon_hsp_create(dev, full_notify, pdata);
|
|
if (IS_ERR(aon->hsp)) {
|
|
aon->hsp = NULL;
|
|
return PTR_ERR(aon->hsp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tegra_aon_hsp_sm_pair_free(struct tegra_aon *aon)
|
|
{
|
|
if (!IS_ERR_OR_NULL(aon) && !IS_ERR_OR_NULL(aon->hsp)) {
|
|
aon_hsp_free(aon->hsp);
|
|
aon->hsp = NULL;
|
|
}
|
|
}
|