Files
linux-nv-oot/drivers/platform/tegra/rtcpu/hsp-mailbox-client.c
Frank Chen 304123a3bf video: camera: Port RTCPU drivers to OOT tree
Port RTCPU drivers from kernel/nvidia to kernel/nvidia-oot.
In addition to copying the files this patch:
1) Modifies make files to build rtcpu drivers as modules
2) Modifies licenses of all ported files to SPDX
3) Adds MODULE_LICENSE macro to all modules
4) Removes checks for old kernel versions and the dead code after those checks
5) Fixes style errors according to checkpatch.pl

Change-Id: If64296a22ce958e5326c7509cb69f8f7154f598e
Signed-off-by: Frank Chen <frankc@nvidia.com>
Signed-off-by: Matti Ryttylainen <mryttylainen@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2783040
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Ankur Pawar <ankurp@nvidia.com>
Reviewed-by: Semi Malinen <smalinen@nvidia.com>
Reviewed-by: Pekka Pessi <ppessi@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2022-12-01 11:00:35 -08:00

645 lines
15 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include "hsp-combo.h"
#include <linux/version.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/mailbox_client.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/err.h>
#include "soc/tegra/camrtc-commands.h"
typedef struct mbox_client mbox_client;
struct camrtc_hsp_mbox {
struct mbox_client client;
struct mbox_chan *chan;
};
struct camrtc_hsp_op;
struct camrtc_hsp {
const struct camrtc_hsp_op *op;
struct camrtc_hsp_mbox rx;
struct camrtc_hsp_mbox tx;
u32 cookie;
spinlock_t sendlock;
void (*group_notify)(struct device *dev, u16 group);
struct device dev;
struct mutex mutex;
struct completion emptied;
wait_queue_head_t response_waitq;
atomic_t response;
long timeout;
};
struct camrtc_hsp_op {
int (*send)(struct camrtc_hsp *, int msg, long *timeout);
void (*group_ring)(struct camrtc_hsp *, u16 group);
int (*sync)(struct camrtc_hsp *, long *timeout);
int (*resume)(struct camrtc_hsp *, long *timeout);
int (*suspend)(struct camrtc_hsp *, long *timeout);
int (*bye)(struct camrtc_hsp *, long *timeout);
int (*ch_setup)(struct camrtc_hsp *, dma_addr_t iova, long *timeout);
int (*ping)(struct camrtc_hsp *, u32 data, long *timeout);
int (*get_fw_hash)(struct camrtc_hsp *, u32 index, long *timeout);
};
static int camrtc_hsp_send(struct camrtc_hsp *camhsp,
int request, long *timeout)
{
int ret = camhsp->op->send(camhsp, request, timeout);
if (ret == -ETIME) {
dev_err(&camhsp->dev,
"request 0x%08x: empty mailbox timeout\n", request);
} else if (ret == -EINVAL) {
dev_err(&camhsp->dev,
"request 0x%08x: invalid mbox channel\n", request);
} else if (ret == -ENOBUFS) {
dev_err(&camhsp->dev,
"request 0x%08x: no space left in mbox msg queue\n", request);
} else
dev_dbg(&camhsp->dev,
"request sent: 0x%08x\n", request);
return ret;
}
static int camrtc_hsp_recv(struct camrtc_hsp *camhsp,
int command, long *timeout)
{
int response;
*timeout = wait_event_timeout(
camhsp->response_waitq,
(response = atomic_xchg(&camhsp->response, -1)) >= 0,
*timeout);
if (*timeout <= 0) {
dev_err(&camhsp->dev,
"request 0x%08x: response timeout\n", command);
return -ETIMEDOUT;
}
dev_dbg(&camhsp->dev, "request 0x%08x: response 0x%08x\n",
command, response);
return response;
}
static int camrtc_hsp_sendrecv(struct camrtc_hsp *camhsp,
int command, long *timeout)
{
int response;
response = camrtc_hsp_send(camhsp, command, timeout);
if (response >= 0)
response = camrtc_hsp_recv(camhsp, command, timeout);
return response;
}
/* ---------------------------------------------------------------------- */
/* Protocol nvidia,tegra-camrtc-hsp-vm */
static void camrtc_hsp_rx_full_notify(mbox_client *cl, void *data)
{
struct camrtc_hsp *camhsp = dev_get_drvdata(cl->dev);
u32 status, group;
u32 msg = (u32) (unsigned long) data;
status = CAMRTC_HSP_SS_FW_MASK;
status >>= CAMRTC_HSP_SS_FW_SHIFT;
group = status & CAMRTC_HSP_SS_IVC_MASK;
if (CAMRTC_HSP_MSG_ID(msg) == CAMRTC_HSP_UNKNOWN)
dev_dbg(&camhsp->dev, "request message unknown 0x%08x\n", msg);
if (group != 0)
camhsp->group_notify(camhsp->dev.parent, (u16)group);
/* Other interrupt bits are ignored for now */
if (CAMRTC_HSP_MSG_ID(msg) == CAMRTC_HSP_IRQ) {
/* We are done here */
} else if (CAMRTC_HSP_MSG_ID(msg) < CAMRTC_HSP_HELLO) {
/* Rest of the unidirectional messages are now ignored */
dev_info(&camhsp->dev, "unknown message 0x%08x\n", msg);
} else {
atomic_set(&camhsp->response, msg);
wake_up(&camhsp->response_waitq);
}
}
static void camrtc_hsp_tx_empty_notify(mbox_client *cl, void *data, int empty_value)
{
struct camrtc_hsp *camhsp = dev_get_drvdata(cl->dev);
(void)empty_value; /* ignored */
complete(&camhsp->emptied);
}
static int camrtc_hsp_vm_send(struct camrtc_hsp *camhsp,
int request, long *timeout);
static void camrtc_hsp_vm_group_ring(struct camrtc_hsp *camhsp, u16 group);
static void camrtc_hsp_vm_send_irqmsg(struct camrtc_hsp *camhsp);
static int camrtc_hsp_vm_sync(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_hello(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_protocol(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_resume(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_suspend(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_bye(struct camrtc_hsp *camhsp, long *timeout);
static int camrtc_hsp_vm_ch_setup(struct camrtc_hsp *camhsp,
dma_addr_t iova, long *timeout);
static int camrtc_hsp_vm_ping(struct camrtc_hsp *camhsp,
u32 data, long *timeout);
static int camrtc_hsp_vm_get_fw_hash(struct camrtc_hsp *camhsp,
u32 index, long *timeout);
static const struct camrtc_hsp_op camrtc_hsp_vm_ops = {
.send = camrtc_hsp_vm_send,
.group_ring = camrtc_hsp_vm_group_ring,
.sync = camrtc_hsp_vm_sync,
.resume = camrtc_hsp_vm_resume,
.suspend = camrtc_hsp_vm_suspend,
.bye = camrtc_hsp_vm_bye,
.ping = camrtc_hsp_vm_ping,
.ch_setup = camrtc_hsp_vm_ch_setup,
.get_fw_hash = camrtc_hsp_vm_get_fw_hash,
};
static int camrtc_hsp_vm_send(struct camrtc_hsp *camhsp,
int request, long *timeout)
{
int response;
unsigned long flags;
spin_lock_irqsave(&camhsp->sendlock, flags);
atomic_set(&camhsp->response, -1);
response = mbox_send_message(camhsp->tx.chan, (void *)(unsigned long) request);
spin_unlock_irqrestore(&camhsp->sendlock, flags);
return response;
}
static void camrtc_hsp_vm_group_ring(struct camrtc_hsp *camhsp,
u16 group)
{
camrtc_hsp_vm_send_irqmsg(camhsp);
}
static void camrtc_hsp_vm_send_irqmsg(struct camrtc_hsp *camhsp)
{
int irqmsg = CAMRTC_HSP_MSG(CAMRTC_HSP_IRQ, 1);
int response;
unsigned long flags;
spin_lock_irqsave(&camhsp->sendlock, flags);
response = mbox_send_message(camhsp->tx.chan, (void *)(unsigned long) irqmsg);
spin_unlock_irqrestore(&camhsp->sendlock, flags);
}
static int camrtc_hsp_vm_sendrecv(struct camrtc_hsp *camhsp,
int request, long *timeout)
{
int response = camrtc_hsp_sendrecv(camhsp, request, timeout);
if (response < 0)
return response;
if (CAMRTC_HSP_MSG_ID(request) != CAMRTC_HSP_MSG_ID(response)) {
dev_err(&camhsp->dev,
"request 0x%08x mismatch with response 0x%08x\n",
request, response);
return -EIO;
}
/* Return the 24-bit parameter only */
return CAMRTC_HSP_MSG_PARAM(response);
}
static int camrtc_hsp_vm_sync(struct camrtc_hsp *camhsp, long *timeout)
{
int response = camrtc_hsp_vm_hello(camhsp, timeout);
if (response >= 0) {
camhsp->cookie = response;
response = camrtc_hsp_vm_protocol(camhsp, timeout);
}
return response;
}
static u32 camrtc_hsp_vm_cookie(void)
{
u32 value = CAMRTC_HSP_MSG_PARAM(sched_clock() >> 5U);
if (value == 0)
value++;
return value;
}
static int camrtc_hsp_vm_hello(struct camrtc_hsp *camhsp, long *timeout)
{
int request = CAMRTC_HSP_MSG(CAMRTC_HSP_HELLO, camrtc_hsp_vm_cookie());
int response = camrtc_hsp_send(camhsp, request, timeout);
if (response < 0)
return response;
for (;;) {
response = camrtc_hsp_recv(camhsp, request, timeout);
/* Wait until we get the HELLO message we sent */
if (response == request)
break;
/* ...or timeout */
if (response < 0)
break;
}
return response;
}
static int camrtc_hsp_vm_protocol(struct camrtc_hsp *camhsp, long *timeout)
{
int request = CAMRTC_HSP_MSG(CAMRTC_HSP_PROTOCOL,
RTCPU_DRIVER_SM6_VERSION);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_resume(struct camrtc_hsp *camhsp, long *timeout)
{
int request = CAMRTC_HSP_MSG(CAMRTC_HSP_RESUME, camhsp->cookie);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_suspend(struct camrtc_hsp *camhsp, long *timeout)
{
u32 request = CAMRTC_HSP_MSG(CAMRTC_HSP_SUSPEND, 0);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_bye(struct camrtc_hsp *camhsp, long *timeout)
{
u32 request = CAMRTC_HSP_MSG(CAMRTC_HSP_BYE, 0);
camhsp->cookie = 0U;
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_ch_setup(struct camrtc_hsp *camhsp,
dma_addr_t iova, long *timeout)
{
u32 request = CAMRTC_HSP_MSG(CAMRTC_HSP_CH_SETUP, iova >> 8);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_ping(struct camrtc_hsp *camhsp, u32 data,
long *timeout)
{
u32 request = CAMRTC_HSP_MSG(CAMRTC_HSP_PING, data);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_get_fw_hash(struct camrtc_hsp *camhsp, u32 index,
long *timeout)
{
u32 request = CAMRTC_HSP_MSG(CAMRTC_HSP_FW_HASH, index);
return camrtc_hsp_vm_sendrecv(camhsp, request, timeout);
}
static int camrtc_hsp_vm_probe(struct camrtc_hsp *camhsp)
{
struct device_node *np = camhsp->dev.parent->of_node;
int err = -ENOTSUPP;
const char *obtain = "";
np = of_get_compatible_child(np, "nvidia,tegra-camrtc-hsp-vm");
if (!of_device_is_available(np)) {
of_node_put(np);
dev_err(&camhsp->dev, "no hsp protocol \"%s\"\n",
"nvidia,tegra-camrtc-hsp-vm");
return -ENOTSUPP;
}
camhsp->dev.of_node = np;
camhsp->rx.chan = mbox_request_channel_byname(&camhsp->rx.client, "vm-rx");
if (IS_ERR(camhsp->rx.chan)) {
err = PTR_ERR(camhsp->rx.chan);
goto fail;
}
camhsp->tx.chan = mbox_request_channel_byname(&camhsp->tx.client, "vm-tx");
if (IS_ERR(camhsp->tx.chan)) {
err = PTR_ERR(camhsp->tx.chan);
goto fail;
}
camhsp->op = &camrtc_hsp_vm_ops;
dev_set_name(&camhsp->dev, "%s:%s",
dev_name(camhsp->dev.parent), camhsp->dev.of_node->name);
dev_dbg(&camhsp->dev, "probed\n");
return 0;
fail:
if (err != -EPROBE_DEFER) {
dev_err(&camhsp->dev, "%s: failed to obtain %s: %d\n",
np->name, obtain, err);
}
of_node_put(np);
return err;
}
/* ---------------------------------------------------------------------- */
/* Public interface */
void camrtc_hsp_group_ring(struct camrtc_hsp *camhsp,
u16 group)
{
if (!WARN_ON(camhsp == NULL))
camhsp->op->group_ring(camhsp, group);
}
EXPORT_SYMBOL(camrtc_hsp_group_ring);
/*
* Synchronize the HSP
*/
int camrtc_hsp_sync(struct camrtc_hsp *camhsp)
{
long timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->sync(camhsp, &timeout);
mutex_unlock(&camhsp->mutex);
return response;
}
EXPORT_SYMBOL(camrtc_hsp_sync);
/*
* Resume: resume the firmware
*/
int camrtc_hsp_resume(struct camrtc_hsp *camhsp)
{
long timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->resume(camhsp, &timeout);
mutex_unlock(&camhsp->mutex);
return response;
}
EXPORT_SYMBOL(camrtc_hsp_resume);
/*
* Suspend: set firmware to idle.
*/
int camrtc_hsp_suspend(struct camrtc_hsp *camhsp)
{
long timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->suspend(camhsp, &timeout);
mutex_unlock(&camhsp->mutex);
if (response != 0)
dev_info(&camhsp->dev, "PM_SUSPEND failed: 0x%08x\n",
response);
return response <= 0 ? response : -EIO;
}
EXPORT_SYMBOL(camrtc_hsp_suspend);
/*
* Bye: tell firmware that VM mappings are going away
*/
int camrtc_hsp_bye(struct camrtc_hsp *camhsp)
{
long timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->bye(camhsp, &timeout);
mutex_unlock(&camhsp->mutex);
if (response != 0)
dev_warn(&camhsp->dev, "BYE failed: 0x%08x\n", response);
return response;
}
EXPORT_SYMBOL(camrtc_hsp_bye);
int camrtc_hsp_ch_setup(struct camrtc_hsp *camhsp, dma_addr_t iova)
{
long timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
if (iova >= BIT_ULL(32) || (iova & 0xffU) != 0) {
dev_warn(&camhsp->dev,
"CH_SETUP invalid iova: 0x%08llx\n", iova);
return -EINVAL;
}
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->ch_setup(camhsp, iova, &timeout);
mutex_unlock(&camhsp->mutex);
if (response > 0)
dev_dbg(&camhsp->dev, "CH_SETUP failed: 0x%08x\n", response);
return response;
}
EXPORT_SYMBOL(camrtc_hsp_ch_setup);
int camrtc_hsp_ping(struct camrtc_hsp *camhsp, u32 data, long timeout)
{
long left = timeout;
int response;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
if (left == 0L)
left = camhsp->timeout;
mutex_lock(&camhsp->mutex);
response = camhsp->op->ping(camhsp, data, &left);
mutex_unlock(&camhsp->mutex);
return response;
}
EXPORT_SYMBOL(camrtc_hsp_ping);
int camrtc_hsp_get_fw_hash(struct camrtc_hsp *camhsp,
u8 hash[], size_t hash_size)
{
int i;
int ret = 0;
long timeout;
if (WARN_ON(camhsp == NULL))
return -EINVAL;
memset(hash, 0, hash_size);
timeout = camhsp->timeout;
mutex_lock(&camhsp->mutex);
for (i = 0; i < hash_size; i++) {
int value = camhsp->op->get_fw_hash(camhsp, i, &timeout);
if (value < 0 || value > 255) {
dev_info(&camhsp->dev,
"FW_HASH failed: 0x%08x\n", value);
ret = value < 0 ? value : -EIO;
goto fail;
}
hash[i] = value;
}
fail:
mutex_unlock(&camhsp->mutex);
return ret;
}
EXPORT_SYMBOL(camrtc_hsp_get_fw_hash);
static const struct device_type camrtc_hsp_combo_dev_type = {
.name = "camrtc-hsp-protocol",
};
static void camrtc_hsp_combo_dev_release(struct device *dev)
{
struct camrtc_hsp *camhsp = container_of(dev, struct camrtc_hsp, dev);
if (!IS_ERR_OR_NULL(camhsp->rx.chan))
mbox_free_channel(camhsp->rx.chan);
if (!IS_ERR_OR_NULL(camhsp->tx.chan))
mbox_free_channel(camhsp->tx.chan);
of_node_put(dev->of_node);
kfree(camhsp);
}
static int camrtc_hsp_probe(struct camrtc_hsp *camhsp)
{
int ret;
ret = camrtc_hsp_vm_probe(camhsp);
if (ret != -ENOTSUPP)
return ret;
return -ENODEV;
}
struct camrtc_hsp *camrtc_hsp_create(
struct device *dev,
void (*group_notify)(struct device *dev, u16 group),
long cmd_timeout)
{
struct camrtc_hsp *camhsp;
int ret = -EINVAL;
camhsp = kzalloc(sizeof(*camhsp), GFP_KERNEL);
if (camhsp == NULL)
return ERR_PTR(-ENOMEM);
camhsp->dev.parent = dev;
camhsp->group_notify = group_notify;
camhsp->timeout = cmd_timeout;
mutex_init(&camhsp->mutex);
spin_lock_init(&camhsp->sendlock);
init_waitqueue_head(&camhsp->response_waitq);
init_completion(&camhsp->emptied);
atomic_set(&camhsp->response, -1);
camhsp->dev.type = &camrtc_hsp_combo_dev_type;
camhsp->dev.release = camrtc_hsp_combo_dev_release;
device_initialize(&camhsp->dev);
dev_set_name(&camhsp->dev, "%s:%s", dev_name(dev), "hsp");
pm_runtime_no_callbacks(&camhsp->dev);
pm_runtime_enable(&camhsp->dev);
camhsp->tx.client.tx_block = false;
camhsp->rx.client.rx_callback = camrtc_hsp_rx_full_notify;
camhsp->tx.client.tx_done = camrtc_hsp_tx_empty_notify;
camhsp->rx.client.dev = camhsp->tx.client.dev = &(camhsp->dev);
ret = camrtc_hsp_probe(camhsp);
if (ret < 0)
goto fail;
ret = device_add(&camhsp->dev);
if (ret < 0)
goto fail;
dev_set_drvdata(&camhsp->dev, camhsp);
return camhsp;
fail:
camrtc_hsp_free(camhsp);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(camrtc_hsp_create);
void camrtc_hsp_free(struct camrtc_hsp *camhsp)
{
if (IS_ERR_OR_NULL(camhsp))
return;
pm_runtime_disable(&camhsp->dev);
if (dev_get_drvdata(&camhsp->dev) != NULL)
device_unregister(&camhsp->dev);
else
put_device(&camhsp->dev);
}
EXPORT_SYMBOL(camrtc_hsp_free);
MODULE_LICENSE("GPL v2");