mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
- introduced a panic_flag in the tegra_rtcpu_trace. - set panic_flag to 1 when kmd receives panic hsp message from RCE. - the trace buffer readout thread calls rtcpu_trace_snapshot to dump out the snapshot section when panic_flag is set to 1. Bug 5293085 Change-Id: Ief5d9431bfc944fc588f38a69f7758f829cf2e6e Signed-off-by: yizhou <yizhou@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3368252 Reviewed-by: Evgeny Kornev <ekornev@nvidia.com> GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: Chinniah Poosapadi <cpoosapadi@nvidia.com> Reviewed-by: Mohit Ingale <mohiti@nvidia.com> Reviewed-by: Kalle Jokiniemi <kjokiniemi@nvidia.com>
1933 lines
56 KiB
C
1933 lines
56 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES.
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*/
|
|
|
|
#include <nvidia/conftest.h>
|
|
#include <linux/tegra-camera-rtcpu.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interconnect.h>
|
|
#include <dt-bindings/interconnect/tegra_icc_id.h>
|
|
#include <linux/io.h>
|
|
#include <linux/iommu.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/seq_buf.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/tegra-ivc-bus.h>
|
|
#include <linux/pm_domain.h>
|
|
#include <soc/tegra/fuse.h>
|
|
#include <linux/tegra-rtcpu-monitor.h>
|
|
#include <linux/tegra-rtcpu-trace.h>
|
|
#include <linux/version.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/string.h>
|
|
#include <linux/sysfs.h>
|
|
|
|
#include "clk-group.h"
|
|
#include "device-group.h"
|
|
#include "reset-group.h"
|
|
#include "linux/tegra-hsp-combo.h"
|
|
|
|
#include "soc/tegra/camrtc-commands.h"
|
|
|
|
#define CAMRTC_NUM_REGS 2
|
|
#define CAMRTC_NUM_RESETS 2
|
|
|
|
struct tegra_cam_rtcpu_pdata {
|
|
const char *name;
|
|
void (*assert_resets)(struct device *);
|
|
int (*deassert_resets)(struct device *);
|
|
int (*wait_for_idle)(struct device *);
|
|
const char * const *reset_names;
|
|
const char * const *reg_names;
|
|
u32 (*pm_r5_ctrl)(void);
|
|
u32 (*pm_pwr_status)(void);
|
|
};
|
|
|
|
/* Register specifics */
|
|
#define TEGRA_APS_FRSC_SC_CTL_0 0x0
|
|
#define TEGRA_APS_FRSC_SC_MODEIN_0 0x14
|
|
|
|
#define TEGRA_R5R_SC_DISABLE 0x5
|
|
#define TEGRA_FN_MODEIN 0x29527
|
|
#define TEGRA_PM_FWLOADDONE 0x2
|
|
#define TEGRA_PM_WFIPIPESTOPPED 0x200000
|
|
|
|
#define AMISC_ADSP_STATUS 0x14
|
|
#define AMISC_ADSP_L2_IDLE BIT(31)
|
|
#define AMISC_ADSP_L2_CLKSTOPPED BIT(30)
|
|
|
|
static int tegra_rce_cam_wait_for_idle(struct device *dev);
|
|
static void tegra_rce_cam_assert_resets(struct device *dev);
|
|
static int tegra_rce_cam_deassert_resets(struct device *dev);
|
|
static int tegra_camrtc_fw_set_operating_point(struct device *dev, uint32_t op);
|
|
|
|
static const char * const rce_reset_names[] = {
|
|
"reset-names", /* all named resets */
|
|
NULL,
|
|
};
|
|
|
|
/* SCE and RCE share the PM regs */
|
|
static const char * const rce_reg_names[] = {
|
|
"rce-pm",
|
|
NULL,
|
|
};
|
|
|
|
static u32 rce_pm_r5_ctrl(void)
|
|
{
|
|
return 0x40;
|
|
}
|
|
|
|
static u32 rce_pm_pwr_status(void)
|
|
{
|
|
return 0x20;
|
|
}
|
|
|
|
static const struct tegra_cam_rtcpu_pdata rce_pdata = {
|
|
.name = "rce",
|
|
.wait_for_idle = tegra_rce_cam_wait_for_idle,
|
|
.assert_resets = tegra_rce_cam_assert_resets,
|
|
.deassert_resets = tegra_rce_cam_deassert_resets,
|
|
.reset_names = rce_reset_names,
|
|
.reg_names = rce_reg_names,
|
|
.pm_r5_ctrl = rce_pm_r5_ctrl,
|
|
.pm_pwr_status = rce_pm_pwr_status,
|
|
};
|
|
|
|
static u32 t264_rce_pm_r5_ctrl(void)
|
|
{
|
|
return 0x3008;
|
|
}
|
|
|
|
static u32 t264_rce_pm_pwr_status(void)
|
|
{
|
|
return 0x5004;
|
|
}
|
|
|
|
static const struct tegra_cam_rtcpu_pdata t264_rce_pdata = {
|
|
.name = "t264_rce",
|
|
.wait_for_idle = tegra_rce_cam_wait_for_idle,
|
|
.assert_resets = tegra_rce_cam_assert_resets,
|
|
.deassert_resets = tegra_rce_cam_deassert_resets,
|
|
.reset_names = rce_reset_names,
|
|
.reg_names = rce_reg_names,
|
|
.pm_r5_ctrl = t264_rce_pm_r5_ctrl,
|
|
.pm_pwr_status = t264_rce_pm_pwr_status,
|
|
};
|
|
|
|
#define NV(p) "nvidia," #p
|
|
|
|
struct tegra_cam_rtcpu {
|
|
const char *name;
|
|
struct tegra_ivc_bus *ivc;
|
|
struct device_dma_parameters dma_parms;
|
|
struct camrtc_hsp *hsp;
|
|
struct tegra_rtcpu_trace *tracer;
|
|
u32 cmd_timeout;
|
|
u32 fw_version;
|
|
u8 fw_hash[RTCPU_FW_HASH_SIZE];
|
|
struct {
|
|
u64 reset_complete;
|
|
u64 boot_handshake;
|
|
} stats;
|
|
union {
|
|
void __iomem *regs[CAMRTC_NUM_REGS];
|
|
struct {
|
|
void __iomem *pm_base;
|
|
void __iomem *cfg_base;
|
|
};
|
|
};
|
|
struct camrtc_clk_group *clocks;
|
|
struct camrtc_reset_group *resets[CAMRTC_NUM_RESETS];
|
|
const struct tegra_cam_rtcpu_pdata *pdata;
|
|
struct camrtc_device_group *camera_devices;
|
|
struct icc_path *icc_path;
|
|
u32 mem_bw;
|
|
struct tegra_camrtc_mon *monitor;
|
|
u32 max_reboot_retry;
|
|
atomic_t rebooting;
|
|
bool powered;
|
|
bool boot_sync_done;
|
|
bool fw_active;
|
|
bool online;
|
|
};
|
|
|
|
static struct device *s_dev;
|
|
|
|
static uint32_t operating_point;
|
|
|
|
static ssize_t show_operating_point(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
|
|
{
|
|
sprintf(buff, "%d", operating_point);
|
|
|
|
return strlen(buff);
|
|
}
|
|
|
|
static ssize_t store_operating_point(struct kobject *kobj, struct kobj_attribute *attr,
|
|
const char *buff, size_t count)
|
|
{
|
|
u32 temp;
|
|
|
|
if (kstrtou32(buff, 10, &temp) == 0) {
|
|
if ((temp == 0) || (temp == 6)) {
|
|
operating_point = (uint32_t)temp;
|
|
tegra_camrtc_fw_set_operating_point(s_dev, operating_point);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct kobj_attribute operating_point_attribute =
|
|
__ATTR(operating_point, 0644, show_operating_point, store_operating_point);
|
|
|
|
static struct attribute *attrs[] = {
|
|
&operating_point_attribute.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group attr_group = {
|
|
.attrs = attrs,
|
|
};
|
|
|
|
static struct kobject *kobj;
|
|
|
|
/**
|
|
* @brief Initialize operating point sysfs entries
|
|
*
|
|
* This function creates sysfs entries for controlling the operating point.
|
|
* - Initializes the operating_point variable to 0 using assignment
|
|
* - Creates a kobject under kernel_kobj named "operating_point" using @ref kobject_create_and_add()
|
|
* - Checks if the kobject creation was successful
|
|
* - Returns -ENOMEM if kobject creation fails
|
|
* - Creates a sysfs attribute group using @ref sysfs_create_group()
|
|
* - Cleans up the kobject if attribute group creation fails using @ref kobject_put()
|
|
* - Returns the result (success or error) of the operations
|
|
*
|
|
* @retval -ENOMEM If kobject creation fails
|
|
* @retval (int) Value returned by @ref sysfs_create_group()
|
|
*/
|
|
static int init_operating_point_sysfs(void)
|
|
{
|
|
int ret;
|
|
|
|
operating_point = 0;
|
|
|
|
kobj = kobject_create_and_add("operating_point", kernel_kobj);
|
|
if (!kobj)
|
|
return -ENOMEM;
|
|
ret = sysfs_create_group(kobj, &attr_group);
|
|
if (ret)
|
|
kobject_put(kobj);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Clean up operating point sysfs entries
|
|
*
|
|
* This function removes the sysfs entries for controlling the operating point.
|
|
* - Releases the kobject created by @ref init_operating_point_sysfs() using @ref kobject_put()
|
|
*/
|
|
static void deinit_operating_point_sysfs(void)
|
|
{
|
|
kobject_put(kobj);
|
|
}
|
|
|
|
/**
|
|
* @brief Map a device resource to an ioremap
|
|
*
|
|
* This function maps a device resource to an ioremap
|
|
* - Retrieves the resource from the device tree using @ref of_address_to_resource()
|
|
* - Returns an error pointer if the resource retrieval fails
|
|
* - Maps the resource using @ref devm_ioremap_resource()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] index Index of the resource to map
|
|
* Valid value: [INT_MIN, INT_MAX]
|
|
*
|
|
* @retval (void *) Return value from @ref devm_ioremap_resource()
|
|
* @retval IOMEM_ERR_PTR(int) If the resource retrieval fails
|
|
*/
|
|
static void __iomem *tegra_cam_ioremap(struct device *dev, int index)
|
|
{
|
|
struct resource mem;
|
|
int err = of_address_to_resource(dev->of_node, index, &mem);
|
|
if (err)
|
|
return IOMEM_ERR_PTR(err);
|
|
|
|
/* NOTE: assumes size is large enough for caller */
|
|
return devm_ioremap_resource(dev, &mem);
|
|
}
|
|
|
|
/**
|
|
* @brief Map a device resource by name
|
|
*
|
|
* This function maps a device resource by name
|
|
* - Retrieves the index of the resource from the device tree using @ref of_property_match_string()
|
|
* - Returns an error pointer if the resource retrieval fails
|
|
* - Maps the resource using @ref tegra_cam_ioremap()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] name Name of the resource to map
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (void *) Return value from @ref tegra_cam_ioremap()
|
|
* @retval IOMEM_ERR_PTR(-ENOENT) If the resource retrieval fails
|
|
*/
|
|
static void __iomem *tegra_cam_ioremap_byname(struct device *dev,
|
|
const char *name)
|
|
{
|
|
int index = of_property_match_string(dev->of_node, "reg-names", name);
|
|
if (index < 0)
|
|
return IOMEM_ERR_PTR(-ENOENT);
|
|
return tegra_cam_ioremap(dev, index);
|
|
}
|
|
|
|
/**
|
|
* @brief Get resources for the camera RTCPU
|
|
*
|
|
* This function gets the resources for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the platform data for the device using @ref rtcpu->pdata
|
|
* - Retrieves the clocks for the device using @ref camrtc_clk_group_get()
|
|
* - Retrieves the device group for the device using @ref camrtc_device_group_get()
|
|
* - Retrieves the reset group for the device using @ref camrtc_reset_group_get()
|
|
* - Retrieves the registers for the device using @ref tegra_cam_ioremap_byname()
|
|
* - Returns an error if the resource retrieval fails
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval 0 On successful resource retrieval
|
|
* @retval PTR_ERR(err) If error from @ref camrtc_reset_group_get() or
|
|
* @ref camrtc_device_group_get()
|
|
* @retval -EPROBE_DEFER If the resource retrieval fails
|
|
*/
|
|
static int tegra_camrtc_get_resources(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
const struct tegra_cam_rtcpu_pdata *pdata = rtcpu->pdata;
|
|
struct camrtc_device_group *devgrp;
|
|
int i, err;
|
|
|
|
rtcpu->clocks = camrtc_clk_group_get(dev);
|
|
if (IS_ERR(rtcpu->clocks)) {
|
|
err = PTR_ERR(rtcpu->clocks);
|
|
if (err == -EPROBE_DEFER)
|
|
dev_info(dev, "defer %s probe because of %s\n",
|
|
rtcpu->name, "clocks");
|
|
else
|
|
dev_warn(dev, "clocks not available: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
devgrp = camrtc_device_group_get(dev, "nvidia,camera-devices",
|
|
"nvidia,camera-device-names");
|
|
if (!IS_ERR(devgrp)) {
|
|
rtcpu->camera_devices = devgrp;
|
|
} else {
|
|
err = PTR_ERR(devgrp);
|
|
if (err == -EPROBE_DEFER)
|
|
return err;
|
|
if (err != -ENODATA && err != -ENOENT)
|
|
dev_warn(dev, "get %s: failed: %d\n",
|
|
"nvidia,camera-devices", err);
|
|
}
|
|
|
|
#define GET_RESOURCES(_res_, _get_, _null_, _toerr) \
|
|
for (i = 0; i < ARRAY_SIZE(rtcpu->_res_##s); i++) { \
|
|
if (!pdata->_res_##_names[i]) \
|
|
break; \
|
|
rtcpu->_res_##s[i] = _get_(dev, pdata->_res_##_names[i]); \
|
|
err = _toerr(rtcpu->_res_##s[i]); \
|
|
if (err == 0) \
|
|
continue; \
|
|
rtcpu->_res_##s[i] = _null_; \
|
|
if (err == -EPROBE_DEFER) { \
|
|
dev_info(dev, "defer %s probe because %s %s\n", \
|
|
rtcpu->name, #_res_, pdata->_res_##_names[i]); \
|
|
return err; \
|
|
} \
|
|
if (err != -ENODATA && err != -ENOENT) \
|
|
dev_warn(dev, "%s %s not available: %d\n", #_res_, \
|
|
pdata->_res_##_names[i], err); \
|
|
}
|
|
|
|
#define _PTR2ERR(x) (IS_ERR(x) ? PTR_ERR(x) : 0)
|
|
|
|
GET_RESOURCES(reset, camrtc_reset_group_get, NULL, _PTR2ERR);
|
|
GET_RESOURCES(reg, tegra_cam_ioremap_byname, NULL, _PTR2ERR);
|
|
|
|
#undef _PTR2ERR
|
|
|
|
if (rtcpu->resets[0] == NULL) {
|
|
struct camrtc_reset_group *resets;
|
|
|
|
resets = camrtc_reset_group_get(dev, NULL);
|
|
|
|
if (!IS_ERR(resets))
|
|
rtcpu->resets[0] = resets;
|
|
else if (PTR_ERR(resets) == -EPROBE_DEFER) {
|
|
dev_info(dev, "defer %s probe because of %s\n",
|
|
rtcpu->name, "resets");
|
|
return -EPROBE_DEFER;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable the clocks for the camera RTCPU
|
|
*
|
|
* This function enables the clocks for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the clocks for the device using @ref camrtc_clk_group_enable()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_clk_group_enable()
|
|
*/
|
|
static int tegra_camrtc_enable_clks(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return camrtc_clk_group_enable(rtcpu->clocks);
|
|
}
|
|
|
|
/**
|
|
* @brief Disable the clocks for the camera RTCPU
|
|
*
|
|
* This function disables the clocks for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the clocks for the device using @ref camrtc_clk_group_disable()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_clk_group_disable()
|
|
*/
|
|
static void tegra_camrtc_disable_clks(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return camrtc_clk_group_disable(rtcpu->clocks);
|
|
}
|
|
|
|
/**
|
|
* @brief Assert the resets for the camera RTCPU
|
|
*
|
|
* This function asserts the resets for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the platform data for the device using @ref rtcpu->pdata
|
|
* - Calls the platform data's assert_resets function
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_assert_resets(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->pdata->assert_resets)
|
|
rtcpu->pdata->assert_resets(dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Deassert the resets for the camera RTCPU
|
|
*
|
|
* This function deasserts the resets for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the platform data for the device using @ref rtcpu->pdata
|
|
* - Calls the platform data's @ref deassert_resets function
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_reset_group_deassert()
|
|
*/
|
|
static int tegra_camrtc_deassert_resets(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int ret = 0;
|
|
|
|
if (rtcpu->pdata->deassert_resets) {
|
|
ret = rtcpu->pdata->deassert_resets(dev);
|
|
rtcpu->stats.reset_complete = ktime_get_ns();
|
|
rtcpu->stats.boot_handshake = 0;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define CAMRTC_MAX_BW (0xFFFFFFFFU)
|
|
#define RCE_MAX_BW_MBPS (160)
|
|
|
|
/**
|
|
* @brief Initialize the ICC for the camera RTCPU
|
|
*
|
|
* This function initializes the ICC for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - If the bandwidth is CAMRTC_MAX_BW, sets the memory bandwidth to RCE_MAX_BW_MBPS using
|
|
* @ref MBps_to_icc()
|
|
* - Otherwise, sets the memory bandwidth to the provided bandwidth
|
|
* - Retrieves the ICC path for the device using @ref devm_of_icc_get()
|
|
* - Sets the icc path to NULL if the retrieval fails
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] bw Memory bandwidth to set
|
|
* Valid value: [0, CAMRTC_MAX_BW]
|
|
*/
|
|
static void tegra_camrtc_init_icc(struct device *dev, u32 bw)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (bw == CAMRTC_MAX_BW)
|
|
rtcpu->mem_bw = MBps_to_icc(RCE_MAX_BW_MBPS);
|
|
else
|
|
rtcpu->mem_bw = bw;
|
|
|
|
rtcpu->icc_path = devm_of_icc_get(dev, "write");
|
|
if (IS_ERR(rtcpu->icc_path)) {
|
|
dev_warn(dev, "no interconnect control, err:%ld\n",
|
|
PTR_ERR(rtcpu->icc_path));
|
|
rtcpu->icc_path = NULL;
|
|
return;
|
|
}
|
|
|
|
dev_dbg(dev, "using icc rate %u for power-on\n", rtcpu->mem_bw);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the memory bandwidth for the camera RTCPU
|
|
*
|
|
* This function initializes the memory bandwidth for the camera RTCPU
|
|
* - Calls @ref tegra_camrtc_init_icc() with CAMRTC_MAX_BW
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_init_membw(struct device *dev)
|
|
{
|
|
tegra_camrtc_init_icc(dev, CAMRTC_MAX_BW);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the full memory bandwidth for the camera RTCPU
|
|
*
|
|
* This function sets the full memory bandwidth for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the ICC path for the device using @ref rtcpu->icc_path
|
|
* - If the ICC path is not NULL, sets the memory bandwidth to the provided bandwidth using
|
|
* @ref icc_set_bw()
|
|
* - Returns an error if the memory bandwidth setting fails
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_full_mem_bw(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->icc_path != NULL) {
|
|
int ret = icc_set_bw(rtcpu->icc_path, 0, rtcpu->mem_bw);
|
|
|
|
if (ret)
|
|
dev_err(dev, "set icc bw [%u] failed: %d\n", rtcpu->mem_bw, ret);
|
|
else
|
|
dev_dbg(dev, "requested icc bw %u\n", rtcpu->mem_bw);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Set the slow memory bandwidth for the camera RTCPU
|
|
*
|
|
* This function sets the slow memory bandwidth for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the ICC path for the device using @ref rtcpu->icc_path
|
|
* - If the ICC path is not NULL, sets the memory bandwidth to 0 using @ref icc_set_bw()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_slow_mem_bw(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->icc_path != NULL)
|
|
(void)icc_set_bw(rtcpu->icc_path, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the fwloaddone flag for the camera RTCPU
|
|
*
|
|
* This function sets the fwloaddone flag for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the PM base for the device using @ref rtcpu->pm_base
|
|
* - Reads the PM R5 control register using @ref readl()
|
|
* - If the fwloaddone flag is true, sets the TEGRA_PM_FWLOADDONE bit using @ref writel()
|
|
* - Otherwise, clears the TEGRA_PM_FWLOADDONE bit using @ref writel()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] fwloaddone FWLOADDONE flag to set
|
|
* Valid value: true or false
|
|
*/
|
|
static void tegra_camrtc_set_fwloaddone(struct device *dev, bool fwloaddone)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->pm_base != NULL) {
|
|
u32 val = readl(rtcpu->pm_base + rtcpu->pdata->pm_r5_ctrl());
|
|
|
|
if (fwloaddone)
|
|
val |= TEGRA_PM_FWLOADDONE;
|
|
else
|
|
val &= ~TEGRA_PM_FWLOADDONE;
|
|
|
|
writel(val, rtcpu->pm_base + rtcpu->pdata->pm_r5_ctrl());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Wait for the camera RTCPU to be idle
|
|
*
|
|
* This function waits for the camera RTCPU to be idle
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the PM base for the device using @ref rtcpu->pm_base
|
|
* - Reads the PM power status register using @ref readl()
|
|
* - If the PM power status register is not NULL, polls for the WFI assert using @ref readl()
|
|
* - If the timeout is less than 0, returns -EBUSY
|
|
* - Calls @ref msleep() with a delay stride of HZ / 50
|
|
* - Returns 0 on success
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval 0 If @ref rtcpu->pm_base is NULL or successful
|
|
* @retval -EBUSY If timeout occurs
|
|
*/
|
|
static int tegra_rce_cam_wait_for_idle(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
long timeout = rtcpu->cmd_timeout;
|
|
long delay_stride = HZ / 50;
|
|
|
|
if (rtcpu->pm_base == NULL)
|
|
return 0;
|
|
|
|
/* Poll for WFI assert.*/
|
|
for (;;) {
|
|
u32 val = readl(rtcpu->pm_base + rtcpu->pdata->pm_pwr_status());
|
|
|
|
if ((val & TEGRA_PM_WFIPIPESTOPPED) == 0)
|
|
break;
|
|
|
|
if (timeout < 0) {
|
|
dev_info(dev, "timeout waiting for WFI\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
msleep(delay_stride);
|
|
timeout -= delay_stride;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the operating point for the camera RTCPU
|
|
*
|
|
* This function sets the operating point for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the HSP for the device using @ref rtcpu->hsp
|
|
* - Returns 0 if the HSP is not NULL
|
|
* - Returns the return value from @ref camrtc_hsp_set_operating_point()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] op Operating point to set
|
|
* Valid value: [0, UINT32_MAX]
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_set_operating_point()
|
|
* @retval 0 if the HSP is NULL
|
|
*/
|
|
static int tegra_camrtc_fw_set_operating_point(struct device *dev, uint32_t op)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (!rtcpu->hsp)
|
|
return 0;
|
|
|
|
return camrtc_hsp_set_operating_point(rtcpu->hsp, op);
|
|
}
|
|
|
|
/**
|
|
* @brief Deassert the resets for the camera RTCPU
|
|
*
|
|
* This function deasserts the resets for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the reset group for the device using @ref camrtc_reset_group_deassert()
|
|
* - Returns an error if the reset group deassertion fails
|
|
* - Sets the fwloaddone flag to true using @ref tegra_camrtc_set_fwloaddone()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_reset_group_deassert()
|
|
* @retval 0 On successful reset group deassertion
|
|
*/
|
|
static int tegra_rce_cam_deassert_resets(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int err;
|
|
|
|
err = camrtc_reset_group_deassert(rtcpu->resets[0]);
|
|
if (err)
|
|
return err;
|
|
|
|
/* nCPUHALT is a reset controlled by PM, not by CAR. */
|
|
tegra_camrtc_set_fwloaddone(dev, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Assert the resets for the camera RTCPU
|
|
*
|
|
* This function asserts the resets for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the reset group for the device using @ref camrtc_reset_group_assert()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_rce_cam_assert_resets(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
camrtc_reset_group_assert(rtcpu->resets[0]);
|
|
}
|
|
|
|
/**
|
|
* @brief Wait for the camera RTCPU to be idle
|
|
*
|
|
* This function waits for the camera RTCPU to be idle
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Retrieves the wait_for_idle function from the platform data using
|
|
* @ref rtcpu->pdata->wait_for_idle
|
|
* - Returns the return value from @ref rtcpu->pdata->wait_for_idle
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref rtcpu->pdata->wait_for_idle
|
|
*/
|
|
static int tegra_camrtc_wait_for_idle(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return rtcpu->pdata->wait_for_idle(dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Suspend the camera RTCPU
|
|
*
|
|
* This function suspends the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the fw_active flag is true and the HSP is not NULL
|
|
* - Sets the fw_active flag to false
|
|
* - Calls @ref camrtc_hsp_suspend() to suspend the RTCPU
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_suspend()
|
|
* @retval 0 if the fw_active flag is false or the HSP is NULL
|
|
*/
|
|
static int tegra_camrtc_fw_suspend(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (!rtcpu->fw_active || !rtcpu->hsp)
|
|
return 0;
|
|
|
|
rtcpu->fw_active = false;
|
|
|
|
return camrtc_hsp_suspend(rtcpu->hsp);
|
|
}
|
|
|
|
/**
|
|
* @brief Setup the shared memory for the camera RTCPU
|
|
*
|
|
* This function sets up the shared memory for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_rtcpu_trace_boot_sync() to set up the trace
|
|
* - Calls @ref tegra_ivc_bus_boot_sync() to set up the IVC services
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_ivc_bus_boot_sync()
|
|
*/
|
|
static int tegra_camrtc_setup_shared_memory(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
/*
|
|
* Set-up trace
|
|
*/
|
|
ret = tegra_rtcpu_trace_boot_sync(rtcpu->tracer);
|
|
if (ret < 0)
|
|
dev_err(dev, "trace boot sync failed: %d\n", ret);
|
|
|
|
/*
|
|
* Set-up and activate the IVC services in firmware
|
|
*/
|
|
ret = tegra_ivc_bus_boot_sync(rtcpu->ivc, &tegra_camrtc_iovm_setup);
|
|
if (ret < 0)
|
|
dev_err(dev, "ivc-bus boot sync failed: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Set the online status for the camera RTCPU
|
|
*
|
|
* This function sets the online status for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the online status is already set
|
|
* - If the online status is already set, returns
|
|
* - If the online status is not set, calls @ref tegra_camrtc_setup_shared_memory()
|
|
* to set up the shared memory
|
|
* - Sets the online status to the provided status
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] online Online status to set
|
|
* Valid value: true or false
|
|
*/
|
|
static void tegra_camrtc_set_online(struct device *dev, bool online)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (online == rtcpu->online)
|
|
return;
|
|
|
|
if (online) {
|
|
if (tegra_camrtc_setup_shared_memory(dev) < 0)
|
|
return;
|
|
}
|
|
|
|
/* Postpone the online transition if still probing */
|
|
if (!IS_ERR_OR_NULL(rtcpu->ivc)) {
|
|
rtcpu->online = online;
|
|
tegra_ivc_bus_ready(rtcpu->ivc, online);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Ping the camera RTCPU
|
|
*
|
|
* This function pings the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref camrtc_hsp_ping() to ping the RTCPU
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] data Data to ping the RTCPU with
|
|
* Valid value: [0, UINT32_MAX]
|
|
* @param[in] timeout Timeout for the ping
|
|
* Valid value: [0, UINT32_MAX]
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_ping()
|
|
*/
|
|
int tegra_camrtc_ping(struct device *dev, u32 data, long timeout)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return camrtc_hsp_ping(rtcpu->hsp, data, timeout);
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_ping);
|
|
|
|
/**
|
|
* @brief Notify the camera RTCPU
|
|
*
|
|
* This function notifies the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the IVC bus is not NULL
|
|
* - Calls @ref tegra_ivc_bus_notify() to notify the RTCPU
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] group Group to notify the RTCPU with
|
|
* Valid value: [0, UINT16_MAX]
|
|
*/
|
|
static void tegra_camrtc_ivc_notify(struct device *dev, u16 group)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->ivc)
|
|
tegra_ivc_bus_notify(rtcpu->ivc, group);
|
|
}
|
|
|
|
/**
|
|
* @brief Power on the camera RTCPU
|
|
*
|
|
* This function powers on the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the RTCPU is already powered
|
|
* - If the RTCPU is already powered, returns 0
|
|
* - If the RTCPU is not powered, calls @ref tegra_camrtc_enable_clks() to enable the clocks
|
|
* - Calls @ref tegra_camrtc_deassert_resets() to deassert the resets
|
|
* - Sets the powered flag to true
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] full_speed Full speed flag to set
|
|
* Valid value: true or false
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_enable_clks() or
|
|
* @ref tegra_camrtc_deassert_resets()
|
|
* @retval 0 On successful power on
|
|
*/
|
|
static int tegra_camrtc_poweron(struct device *dev, bool full_speed)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
if (rtcpu->powered) {
|
|
if (full_speed)
|
|
camrtc_clk_group_adjust_fast(rtcpu->clocks);
|
|
return 0;
|
|
}
|
|
|
|
/* Power on and let core run */
|
|
ret = tegra_camrtc_enable_clks(dev);
|
|
if (ret) {
|
|
dev_err(dev, "failed to turn on %s clocks: %d\n",
|
|
rtcpu->name, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (full_speed)
|
|
camrtc_clk_group_adjust_fast(rtcpu->clocks);
|
|
|
|
ret = tegra_camrtc_deassert_resets(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
rtcpu->powered = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Power off the camera RTCPU
|
|
*
|
|
* This function powers off the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the RTCPU is already powered
|
|
* - If the RTCPU is not powered, returns
|
|
* - Calls @ref tegra_camrtc_assert_resets() to assert the resets
|
|
* - Calls @ref tegra_camrtc_disable_clks() to disable the clocks
|
|
* - Sets the powered flag to false
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_poweroff(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (!rtcpu->powered)
|
|
return;
|
|
|
|
rtcpu->powered = false;
|
|
rtcpu->boot_sync_done = false;
|
|
rtcpu->fw_active = false;
|
|
|
|
tegra_camrtc_assert_resets(dev);
|
|
tegra_camrtc_disable_clks(dev);
|
|
}
|
|
|
|
/**
|
|
* @brief Boot sync the camera RTCPU
|
|
*
|
|
* This function boots syncs the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the boot sync is already done
|
|
* - If the boot sync is already done, returns
|
|
* - Calls @ref camrtc_hsp_sync() to sync the RTCPU
|
|
* - Sets the boot sync done flag to true
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_sync() or
|
|
* @ref camrtc_hsp_resume()
|
|
* @retval 0 On successful boot sync
|
|
*/
|
|
static int tegra_camrtc_boot_sync(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int ret;
|
|
|
|
if (!rtcpu->boot_sync_done) {
|
|
ret = camrtc_hsp_sync(rtcpu->hsp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
rtcpu->fw_version = ret;
|
|
rtcpu->boot_sync_done = true;
|
|
}
|
|
|
|
if (!rtcpu->fw_active) {
|
|
ret = camrtc_hsp_resume(rtcpu->hsp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
rtcpu->fw_active = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Boot the camera RTCPU
|
|
*
|
|
* This function boots the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_camrtc_poweron() to power on the RTCPU
|
|
* - Calls @ref tegra_camrtc_full_mem_bw() to set the full memory bandwidth
|
|
* - Loops until the RTCPU is online
|
|
* - If the RTCPU is online, breaks the loop
|
|
* - If the RTCPU is not online, retries the boot sequence
|
|
* - If the RTCPU is not online after the max number of retries, breaks the loop
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_poweron()
|
|
* @retval 0 On successful boot
|
|
*/
|
|
static int tegra_camrtc_boot(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int retry = 0, max_retries = rtcpu->max_reboot_retry;
|
|
int ret;
|
|
|
|
ret = tegra_camrtc_poweron(dev, true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
tegra_camrtc_full_mem_bw(dev);
|
|
|
|
for (;;) {
|
|
ret = tegra_camrtc_boot_sync(dev);
|
|
|
|
tegra_camrtc_set_online(dev, ret == 0);
|
|
|
|
if (ret == 0)
|
|
break;
|
|
if (retry++ == max_retries)
|
|
break;
|
|
if (retry > 1) {
|
|
dev_warn(dev, "%s full reset, retry %u/%u\n",
|
|
rtcpu->name, retry, max_retries);
|
|
tegra_camrtc_assert_resets(dev);
|
|
usleep_range(10, 30);
|
|
tegra_camrtc_deassert_resets(dev);
|
|
}
|
|
}
|
|
|
|
tegra_camrtc_slow_mem_bw(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Setup the IOVM for the camera RTCPU
|
|
*
|
|
* This function sets up the IOVM for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref camrtc_hsp_ch_setup() to setup the IOVM
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] iova IOVA to setup the IOVM with
|
|
* Valid value: [0, UINT64_MAX]
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_ch_setup()
|
|
*/
|
|
int tegra_camrtc_iovm_setup(struct device *dev, dma_addr_t iova)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return camrtc_hsp_ch_setup(rtcpu->hsp, iova);
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_iovm_setup);
|
|
|
|
/**
|
|
* @brief Print the version of the camera RTCPU
|
|
*
|
|
* This function prints the version of the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref seq_buf_init() to initialize the sequence buffer
|
|
* - Calls @ref seq_buf_printf() to print the version of the RTCPU
|
|
* - Returns the number of bytes printed
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] buf Buffer to print the version to
|
|
* Valid value: non-NULL
|
|
* @param[in] size Size of the buffer
|
|
* Valid value: [0, UINT32_MAX]
|
|
*
|
|
* @retval (ssize_t) Number of bytes printed using @ref seq_buf_used()
|
|
*/
|
|
ssize_t tegra_camrtc_print_version(struct device *dev,
|
|
char *buf, size_t size)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
struct seq_buf s;
|
|
int i;
|
|
|
|
seq_buf_init(&s, buf, size);
|
|
seq_buf_printf(&s, "version cpu=%s cmd=%u sha1=",
|
|
rtcpu->name, rtcpu->fw_version);
|
|
|
|
for (i = 0; i < RTCPU_FW_HASH_SIZE; i++)
|
|
seq_buf_printf(&s, "%02x", rtcpu->fw_hash[i]);
|
|
|
|
return seq_buf_used(&s);
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_print_version);
|
|
|
|
/**
|
|
* @brief Log the firmware version of the camera RTCPU
|
|
*
|
|
* This function logs the firmware version of the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_camrtc_print_version() to print the version of the RTCPU
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_log_fw_version(struct device *dev)
|
|
{
|
|
char version[TEGRA_CAMRTC_VERSION_LEN];
|
|
|
|
tegra_camrtc_print_version(dev, version, sizeof(version));
|
|
|
|
dev_info(dev, "firmware %s\n", version);
|
|
}
|
|
|
|
/**
|
|
* @brief Start the PM of the camera RTCPU
|
|
*
|
|
* This function starts the PM of the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] op Operation to log
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_camrtc_pm_start(struct device *dev, char const *op)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
dev_dbg(dev, "start %s [powered=%d synced=%d active=%d online=%d]\n",
|
|
op, rtcpu->powered, rtcpu->boot_sync_done,
|
|
rtcpu->fw_active, rtcpu->online);
|
|
}
|
|
|
|
/**
|
|
* @brief Done the PM of the camera RTCPU
|
|
*
|
|
* This function done the PM of the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] op Operation to log
|
|
* Valid value: non-NULL
|
|
* @param[in] err Error code
|
|
* Valid value: [0, INT32_MAX]
|
|
*/
|
|
static void tegra_camrtc_pm_done(struct device *dev, char const *op, int err)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
dev_dbg(dev, "done %s err=%d [powered=%d synced=%d active=%d online=%d]\n",
|
|
op, err, rtcpu->powered, rtcpu->boot_sync_done,
|
|
rtcpu->fw_active, rtcpu->online);
|
|
}
|
|
|
|
/**
|
|
* @brief Runtime suspend the camera RTCPU
|
|
*
|
|
* This function runtime suspends the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_camrtc_pm_start() to start the PM
|
|
* - Calls @ref tegra_camrtc_fw_suspend() to suspend the RTCPU
|
|
* - If the RTCPU suspend fails, resets the RTCPU
|
|
* - Calls @ref tegra_camrtc_poweroff() to power off the RTCPU
|
|
* - Sets the online flag to false
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval 0 On successful runtime suspend
|
|
*/
|
|
static int tegra_cam_rtcpu_runtime_suspend(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int err;
|
|
|
|
tegra_camrtc_pm_start(dev, "runtime_suspend");
|
|
|
|
err = tegra_camrtc_fw_suspend(dev);
|
|
/* Try full reset if an error occurred while suspending core. */
|
|
if (err < 0) {
|
|
|
|
dev_info(dev, "RTCPU suspend failed, resetting it");
|
|
|
|
/* runtime_resume() powers RTCPU back on */
|
|
tegra_camrtc_poweroff(dev);
|
|
|
|
/* We want to boot sync IVC and trace when resuming */
|
|
tegra_camrtc_set_online(dev, false);
|
|
}
|
|
|
|
camrtc_clk_group_adjust_slow(rtcpu->clocks);
|
|
|
|
tegra_camrtc_pm_done(dev, "runtime_suspend", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Runtime resume the camera RTCPU
|
|
*
|
|
* This function runtime resumes the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_camrtc_pm_start() to start the PM
|
|
* - Calls @ref tegra_camrtc_boot() to boot the RTCPU
|
|
* - Calls @ref tegra_camrtc_pm_done() to done the PM
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_boot()
|
|
* @retval 0 On successful runtime resume
|
|
*/
|
|
static int tegra_cam_rtcpu_runtime_resume(struct device *dev)
|
|
{
|
|
int err;
|
|
|
|
tegra_camrtc_pm_start(dev, "runtime_resume");
|
|
|
|
err = tegra_camrtc_boot(dev);
|
|
|
|
tegra_camrtc_pm_done(dev, "runtime_resume", err);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Runtime idle the camera RTCPU
|
|
*
|
|
* This function runtime idles the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref pm_runtime_mark_last_busy() to mark the last busy time
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) 0
|
|
*/
|
|
static int tegra_cam_rtcpu_runtime_idle(struct device *dev)
|
|
{
|
|
pm_runtime_mark_last_busy(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Callback function triggered upon receiving CAMRTC_HSP_PANIC message.
|
|
*
|
|
* This function is registered with the HSP mailbox client. When an RCE panic
|
|
* occurs, this callback retrieves the RTCPU tracer associated with the device
|
|
* and flushes the snapshot portion of the trace buffer to capture RCE state
|
|
* at the time of panic.
|
|
*
|
|
* Checks for NULL input parameters (`dev`, `rtcpu`, `tracer`) before proceeding.
|
|
*
|
|
* @param[in] dev Pointer to the parent device associated with the HSP client.
|
|
* Must not be NULL. Used to retrieve driver data.
|
|
*/
|
|
void rtcpu_trace_panic_callback(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = NULL;
|
|
struct tegra_rtcpu_trace *tracer = NULL;
|
|
if (dev == NULL) {
|
|
dev_err(dev, "%s: input dev handle is null\n", __func__);
|
|
return;
|
|
}
|
|
|
|
rtcpu = dev_get_drvdata(dev);
|
|
if (rtcpu == NULL) {
|
|
dev_err(dev, "%s: input rtcpu handle is null\n", __func__);
|
|
return;
|
|
}
|
|
|
|
tracer = rtcpu->tracer;
|
|
if (tracer == NULL) {
|
|
dev_err(dev, "%s: input tracer handle is null\n", __func__);
|
|
return;
|
|
}
|
|
|
|
/* Call the accessor function to set panic flag */
|
|
tegra_rtcpu_trace_set_panic_flag(tracer);
|
|
}
|
|
EXPORT_SYMBOL(rtcpu_trace_panic_callback);
|
|
|
|
/**
|
|
* @brief Initialize the HSP for the camera RTCPU
|
|
*
|
|
* This function initializes the HSP for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the HSP is already initialized
|
|
* - If the HSP is already initialized, returns 0
|
|
* - Calls @ref camrtc_hsp_create() to create the HSP
|
|
* - If the HSP creation fails, returns the error code
|
|
* - Sets the HSP to the driver data
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref camrtc_hsp_create()
|
|
* @retval 0 On successful HSP initialization or if the HSP is already initialized
|
|
*/
|
|
static int tegra_camrtc_hsp_init(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int err;
|
|
|
|
if (!IS_ERR_OR_NULL(rtcpu->hsp))
|
|
return 0;
|
|
|
|
rtcpu->hsp = camrtc_hsp_create(dev, tegra_camrtc_ivc_notify,
|
|
rtcpu->cmd_timeout);
|
|
if (IS_ERR(rtcpu->hsp)) {
|
|
err = PTR_ERR(rtcpu->hsp);
|
|
rtcpu->hsp = NULL;
|
|
dev_err(dev, "%s: failed to create hsp, err=%d\n", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
/* Register panic callback to capture trace on RCE panic */
|
|
if (rtcpu->hsp && rtcpu->tracer) {
|
|
err = camrtc_hsp_set_panic_callback(rtcpu->hsp, rtcpu_trace_panic_callback);
|
|
if (err < 0)
|
|
dev_err(dev, "%s: failed to set panic callback, err=%d\n", __func__, err);
|
|
} else {
|
|
dev_err(dev, "%s: cannot register RCE panic callback.\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Remove the camera RTCPU
|
|
*
|
|
* This function removes the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref platform_get_drvdata()
|
|
* - Checks if the HSP is already initialized
|
|
* - If the HSP is initialized, calls @ref camrtc_hsp_bye() to bye the HSP
|
|
* - Calls @ref camrtc_hsp_free() to free the HSP
|
|
* - Sets the HSP to NULL
|
|
* - Destroys the tracer using @ref tegra_rtcpu_trace_destroy()
|
|
* - Sets the tracer to NULL
|
|
* - Powers off the device using @ref tegra_camrtc_poweroff()
|
|
* - Sets the ICC path to NULL
|
|
* - Removes the device from the PM genpd using @ref pm_genpd_remove_device()
|
|
* - Destroys the monitor using @ref tegra_cam_rtcpu_mon_destroy()
|
|
* - Destroys the IVC bus using @ref tegra_ivc_bus_destroy()
|
|
* - Sets the DMA parameters to NULL
|
|
* - Deinitializes the operating point sysfs using @ref deinit_operating_point_sysfs()
|
|
*
|
|
* @param[in] pdev Pointer to the platform device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval 0 On successful removal
|
|
*/
|
|
static int tegra_cam_rtcpu_remove(struct platform_device *pdev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = platform_get_drvdata(pdev);
|
|
bool online = rtcpu->online;
|
|
bool pm_is_active = pm_runtime_active(&pdev->dev);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_set_suspended(&pdev->dev);
|
|
|
|
tegra_camrtc_set_online(&pdev->dev, false);
|
|
|
|
if (rtcpu->hsp) {
|
|
if (pm_is_active)
|
|
tegra_cam_rtcpu_runtime_suspend(&pdev->dev);
|
|
if (online)
|
|
camrtc_hsp_bye(rtcpu->hsp);
|
|
camrtc_hsp_free(rtcpu->hsp);
|
|
rtcpu->hsp = NULL;
|
|
}
|
|
|
|
tegra_rtcpu_trace_destroy(rtcpu->tracer);
|
|
rtcpu->tracer = NULL;
|
|
|
|
tegra_camrtc_poweroff(&pdev->dev);
|
|
rtcpu->icc_path = NULL;
|
|
pm_genpd_remove_device(&pdev->dev);
|
|
tegra_cam_rtcpu_mon_destroy(rtcpu->monitor);
|
|
tegra_ivc_bus_destroy(rtcpu->ivc);
|
|
|
|
pdev->dev.dma_parms = NULL;
|
|
|
|
deinit_operating_point_sysfs();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Probe the camera RTCPU
|
|
*
|
|
* This function probes the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref of_device_get_match_data()
|
|
* - Reads the device name from the device tree using @ref of_property_read_string()
|
|
* - Reads the device properties using @ref of_property_read_u32()
|
|
* - Allocates memory for the camera RTCPU using @ref devm_kzalloc()
|
|
* - Sets the driver data for the device using @ref platform_set_drvdata()
|
|
* - Sets the DMA parameters for the device using @ref dma_set_mask_and_coherent()
|
|
* - Enables runtime power management for the device using @ref pm_runtime_enable()
|
|
* - Retrieves the resources for the device using @ref tegra_camrtc_get_resources()
|
|
* - Sets the reboot retry count using @ref of_property_read_u32()
|
|
* - Reads the command timeout using @ref of_property_read_u32()
|
|
* - Reads the autosuspend delay using @ref of_property_read_u32()
|
|
* - Sets the autosuspend delay using @ref pm_runtime_set_autosuspend_delay()
|
|
* - Initializes the memory bandwidth for the device using @ref tegra_camrtc_init_membw()
|
|
* - Sets the DMA parameters for the device using @ref dev->dma_parms
|
|
* - Sets the tracer for the device using @ref tegra_rtcpu_trace_create()
|
|
* - Initializes the HSP for the device using @ref tegra_camrtc_hsp_init()
|
|
* - Powers on the device using @ref pm_runtime_get_sync()
|
|
* - Creates the IVC bus for the device using @ref tegra_ivc_bus_create()
|
|
* - Creates the monitor for the device using @ref tegra_camrtc_mon_create()
|
|
* - Reads the firmware hash using @ref camrtc_hsp_get_fw_hash()
|
|
* - Logs the firmware version using @ref tegra_camrtc_log_fw_version()
|
|
* - Sets the online flag to true using @ref tegra_camrtc_set_online()
|
|
* - Puts the device using @ref pm_runtime_put()
|
|
* - Sets the device to the global variable s_dev
|
|
* - Initializes the operating point sysfs using @ref init_operating_point_sysfs()
|
|
* - In case of failure, call @ref pm_runtime_dont_use_autosuspend() and
|
|
* @ref pm_runtime_put_sync_suspend()
|
|
* - In case of failure, call @ref tegra_cam_rtcpu_remove()
|
|
*
|
|
* @param[in] pdev Pointer to the platform device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_get_resources()
|
|
* or @ref tegra_camrtc_hsp_init() or @ref pm_runtime_get_sync() or @ref camrtc_hsp_get_fw_hash()
|
|
* @retval -ENOMEM On memory allocation failure
|
|
* @retval -ENODEV On device match failure
|
|
* @retval 0 On successful probe
|
|
*/
|
|
static int tegra_cam_rtcpu_probe(struct platform_device *pdev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu;
|
|
const struct tegra_cam_rtcpu_pdata *pdata;
|
|
struct device *dev = &pdev->dev;
|
|
int ret;
|
|
const char *name;
|
|
uint32_t timeout;
|
|
|
|
pdata = of_device_get_match_data(dev);
|
|
if (pdata == NULL) {
|
|
dev_err(dev, "no device match\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
name = pdata->name;
|
|
ret = of_property_read_string(dev->of_node, "nvidia,cpu-name", &name);
|
|
if (ret)
|
|
dev_dbg(dev, "no device property, cpu-name, setting to parent name\n");
|
|
|
|
dev_dbg(dev, "probing RTCPU on %s\n", name);
|
|
|
|
rtcpu = devm_kzalloc(dev, sizeof(*rtcpu), GFP_KERNEL);
|
|
if (rtcpu == NULL)
|
|
return -ENOMEM;
|
|
|
|
rtcpu->pdata = pdata;
|
|
rtcpu->name = name;
|
|
platform_set_drvdata(pdev, rtcpu);
|
|
|
|
(void) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
|
|
|
/* Enable runtime power management */
|
|
pm_runtime_enable(dev);
|
|
|
|
ret = tegra_camrtc_get_resources(dev);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
atomic_set(&rtcpu->rebooting, 0);
|
|
rtcpu->max_reboot_retry = 3;
|
|
ret = of_property_read_u32(dev->of_node, NV(max-reboot),
|
|
&rtcpu->max_reboot_retry);
|
|
if (ret)
|
|
dev_dbg(dev, "no device property, max-reboot, setting to default (3)\n");
|
|
timeout = 2000;
|
|
|
|
ret = of_property_read_u32(dev->of_node, "nvidia,cmd-timeout", &timeout);
|
|
if (ret)
|
|
dev_dbg(dev, "no device property, cmd-timeout, setting to default (2000)\n");
|
|
|
|
rtcpu->cmd_timeout = msecs_to_jiffies(timeout);
|
|
|
|
timeout = 60000;
|
|
ret = of_property_read_u32(dev->of_node, NV(autosuspend-delay-ms), &timeout);
|
|
if (ret == 0) {
|
|
pm_runtime_use_autosuspend(dev);
|
|
pm_runtime_set_autosuspend_delay(&pdev->dev, timeout);
|
|
}
|
|
|
|
tegra_camrtc_init_membw(dev);
|
|
|
|
dev->dma_parms = &rtcpu->dma_parms;
|
|
dma_set_max_seg_size(dev, UINT_MAX);
|
|
|
|
rtcpu->tracer = tegra_rtcpu_trace_create(dev, rtcpu->camera_devices);
|
|
|
|
ret = tegra_camrtc_hsp_init(dev);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
/* Power on device */
|
|
ret = pm_runtime_get_sync(dev);
|
|
if (ret < 0)
|
|
goto fail;
|
|
|
|
rtcpu->ivc = tegra_ivc_bus_create(dev, rtcpu->hsp);
|
|
if (IS_ERR(rtcpu->ivc)) {
|
|
ret = PTR_ERR(rtcpu->ivc);
|
|
rtcpu->ivc = NULL;
|
|
goto put_and_fail;
|
|
}
|
|
|
|
rtcpu->monitor = tegra_camrtc_mon_create(dev);
|
|
if (IS_ERR(rtcpu->monitor)) {
|
|
ret = PTR_ERR(rtcpu->monitor);
|
|
goto put_and_fail;
|
|
}
|
|
|
|
if (of_property_read_bool(dev->of_node, "nvidia,disable-runtime-pm"))
|
|
pm_runtime_get(dev);
|
|
|
|
ret = camrtc_hsp_get_fw_hash(rtcpu->hsp,
|
|
rtcpu->fw_hash, sizeof(rtcpu->fw_hash));
|
|
if (ret)
|
|
dev_err(dev, "failed to get firmware hash!\n");
|
|
else
|
|
tegra_camrtc_log_fw_version(dev);
|
|
|
|
tegra_camrtc_set_online(dev, true);
|
|
|
|
pm_runtime_put(dev);
|
|
|
|
s_dev = dev;
|
|
|
|
init_operating_point_sysfs();
|
|
|
|
dev_dbg(dev, "successfully probed RTCPU on %s\n", name);
|
|
|
|
return 0;
|
|
|
|
put_and_fail:
|
|
pm_runtime_dont_use_autosuspend(dev);
|
|
pm_runtime_put_sync_suspend(dev);
|
|
fail:
|
|
tegra_cam_rtcpu_remove(pdev);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Reboot the camera RTCPU
|
|
*
|
|
* This function reboots the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the device is suspended using @ref pm_runtime_suspended()
|
|
* - Checks if the RTCPU is powered using @ref rtcpu->powered
|
|
* - Sets the rebooting flag to 1 using @ref atomic_cmpxchg()
|
|
* - Calls @ref tegra_camrtc_pm_start() to start the PM operation
|
|
* - Calls @ref tegra_camrtc_set_online() to set the online flag to false
|
|
* - Calls @ref tegra_camrtc_wait_for_idle() to wait for the RTCPU to enter WFI
|
|
* - Calls @ref tegra_camrtc_assert_resets() to assert the resets
|
|
* - Sets the powered flag to false using @ref rtcpu->powered
|
|
* - Calls @ref tegra_camrtc_boot() to boot the RTCPU
|
|
* - Sets the rebooting flag to 0 using @ref atomic_set()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_boot()
|
|
* @retval -EIO On device suspended or RTCPU not powered
|
|
* @retval -EBUSY On rebooting flag already set
|
|
*/
|
|
int tegra_camrtc_reboot(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
int err = 0;
|
|
int ret;
|
|
|
|
if (pm_runtime_suspended(dev)) {
|
|
dev_info(dev, "cannot reboot while suspended\n");
|
|
return -EIO;
|
|
}
|
|
|
|
if (!rtcpu->powered)
|
|
return -EIO;
|
|
|
|
if (atomic_cmpxchg(&rtcpu->rebooting, 0, 1) == 1) {
|
|
dev_info(dev, "reboot already in progress\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
rtcpu->boot_sync_done = false;
|
|
rtcpu->fw_active = false;
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
|
|
tegra_camrtc_set_online(dev, false);
|
|
|
|
/* Signal CAMRTC to suspend its operations.
|
|
* Incompleted memory operations by CAMRTC can cause a memory fabric
|
|
* error if the reset signal is asserted in middle of shared memory
|
|
* transaction.
|
|
*/
|
|
if (rtcpu->hsp)
|
|
err = camrtc_hsp_bye(rtcpu->hsp);
|
|
|
|
/* Wait for the core to enter WFI before asserting the reset.
|
|
* Don't bother if the core was unresponsive.
|
|
*/
|
|
if (err == 0)
|
|
tegra_camrtc_wait_for_idle(dev);
|
|
|
|
tegra_camrtc_assert_resets(dev);
|
|
|
|
rtcpu->powered = false;
|
|
|
|
ret = tegra_camrtc_boot(dev);
|
|
|
|
atomic_set(&rtcpu->rebooting, 0);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_reboot);
|
|
|
|
/**
|
|
* @brief Restore the camera RTCPU
|
|
*
|
|
* This function restores the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the monitor is not NULL using @ref rtcpu->monitor
|
|
* - Calls @ref tegra_camrtc_mon_restore_rtcpu() to restore the RTCPU
|
|
* - Returns the return value from @ref tegra_camrtc_mon_restore_rtcpu()
|
|
* - If the monitor is NULL, calls @ref tegra_camrtc_reboot() to reboot the RTCPU
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_mon_restore_rtcpu() or
|
|
* @ref tegra_camrtc_reboot()
|
|
*/
|
|
int tegra_camrtc_restore(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
if (rtcpu->monitor)
|
|
return tegra_camrtc_mon_restore_rtcpu(rtcpu->monitor);
|
|
else
|
|
return tegra_camrtc_reboot(dev);
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_restore);
|
|
|
|
/**
|
|
* @brief Check if the camera RTCPU is alive
|
|
*
|
|
* This function checks if the camera RTCPU is alive
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Returns the online flag from @ref rtcpu->online
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (bool) Value of @ref rtcpu->online
|
|
*/
|
|
bool tegra_camrtc_is_rtcpu_alive(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
return rtcpu->online;
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_is_rtcpu_alive);
|
|
|
|
/**
|
|
* @brief Check if the camera RTCPU is powered
|
|
*
|
|
* This function checks if the camera RTCPU is powered
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Returns the powered flag from @ref rtcpu->powered
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (bool) Value of @ref rtcpu->powered
|
|
* @retval false if the device is not found
|
|
*/
|
|
bool tegra_camrtc_is_rtcpu_powered(void)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu;
|
|
|
|
if (s_dev) {
|
|
rtcpu = dev_get_drvdata(s_dev);
|
|
return rtcpu->powered;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_is_rtcpu_powered);
|
|
|
|
/**
|
|
* @brief Flush the trace for the camera RTCPU
|
|
*
|
|
* This function flushes the trace for the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Calls @ref tegra_rtcpu_trace_flush() to flush the trace
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*/
|
|
void tegra_camrtc_flush_trace(struct device *dev)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
|
|
tegra_rtcpu_trace_flush(rtcpu->tracer);
|
|
}
|
|
EXPORT_SYMBOL(tegra_camrtc_flush_trace);
|
|
|
|
/**
|
|
* @brief Halt the camera RTCPU
|
|
*
|
|
* This function halts the camera RTCPU
|
|
* - Retrieves the driver data for the device using @ref dev_get_drvdata()
|
|
* - Checks if the online flag is true using @ref rtcpu->online
|
|
* - Calls @ref tegra_camrtc_pm_start() to start the PM operation
|
|
* - Calls @ref tegra_camrtc_set_online() to set the online flag to false
|
|
* - Checks if the powered flag is false using @ref rtcpu->powered
|
|
* - Calls @ref tegra_camrtc_pm_done() to finish the PM operation
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
* @param[in] op Operation to perform
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval 0 if the RTCPU is not powered or the operation is successful
|
|
*/
|
|
static int tegra_camrtc_halt(struct device *dev, char const *op)
|
|
{
|
|
struct tegra_cam_rtcpu *rtcpu = dev_get_drvdata(dev);
|
|
bool online = rtcpu->online;
|
|
int err = 0;
|
|
|
|
tegra_camrtc_pm_start(dev, op);
|
|
|
|
tegra_camrtc_set_online(dev, false);
|
|
|
|
if (!rtcpu->powered) {
|
|
tegra_camrtc_pm_done(dev, op, 0);
|
|
return 0;
|
|
}
|
|
|
|
if (!pm_runtime_suspended(dev))
|
|
/* Tell CAMRTC that it should power down camera devices */
|
|
err = tegra_camrtc_fw_suspend(dev);
|
|
|
|
if (online && rtcpu->hsp && err == 0)
|
|
/* Tell CAMRTC that shared memory is going away */
|
|
err = camrtc_hsp_bye(rtcpu->hsp);
|
|
|
|
if (err == 0)
|
|
/* Don't bother to check for WFI if core is unresponsive */
|
|
tegra_camrtc_wait_for_idle(dev);
|
|
|
|
tegra_camrtc_poweroff(dev);
|
|
|
|
tegra_camrtc_pm_done(dev, op, err); /* note this is not returned */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Suspend the camera RTCPU
|
|
*
|
|
* This function suspends the camera RTCPU
|
|
* - Calls @ref tegra_camrtc_halt() to halt the RTCPU
|
|
* - Returns the return value from @ref tegra_camrtc_halt()
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref tegra_camrtc_halt()
|
|
*/
|
|
static int tegra_camrtc_suspend(struct device *dev)
|
|
{
|
|
return tegra_camrtc_halt(dev, "suspend");
|
|
}
|
|
|
|
/**
|
|
* @brief Resume the camera RTCPU
|
|
*
|
|
* This function resumes the camera RTCPU
|
|
* - Calls @ref tegra_camrtc_pm_start() to start the PM operation
|
|
* - Calls @ref pm_runtime_mark_last_busy() to mark the device as busy
|
|
* - Calls @ref pm_runtime_resume() to resume the device
|
|
* - Calls @ref tegra_camrtc_pm_done() to finish the PM operation
|
|
*
|
|
* @param[in] dev Pointer to the device
|
|
* Valid value: non-NULL
|
|
*
|
|
* @retval (int) Return value from @ref pm_runtime_resume() or
|
|
* @ref tegra_camrtc_boot()
|
|
*/
|
|
static int tegra_camrtc_resume(struct device *dev)
|
|
{
|
|
int err;
|
|
|
|
tegra_camrtc_pm_start(dev, "resume");
|
|
|
|
pm_runtime_mark_last_busy(dev);
|
|
|
|
/* Call tegra_cam_rtcpu_runtime_resume() - unless PM thinks dev is ACTIVE */
|
|
err = pm_runtime_resume(dev);
|
|
if (err == 1)
|
|
/* Already marked ACTIVE, boot explicitly */
|
|
err = tegra_camrtc_boot(dev);
|
|
|
|
tegra_camrtc_pm_done(dev, "resume", err);
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* @brief Shutdown the camera RTCPU
|
|
*
|
|
* This function shuts down the camera RTCPU
|
|
* - Calls @ref tegra_camrtc_halt() to halt the RTCPU
|
|
* - Returns the return value from @ref tegra_camrtc_halt()
|
|
*
|
|
* @param[in] pdev Pointer to the platform device
|
|
* Valid value: non-NULL
|
|
*/
|
|
static void tegra_cam_rtcpu_shutdown(struct platform_device *pdev)
|
|
{
|
|
tegra_camrtc_halt(&pdev->dev, "shutdown");
|
|
}
|
|
|
|
static const struct of_device_id tegra_cam_rtcpu_of_match[] = {
|
|
{
|
|
.compatible = NV(tegra194-rce), .data = &rce_pdata
|
|
},
|
|
{
|
|
.compatible = NV(tegra264-rce), .data = &t264_rce_pdata
|
|
},
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, tegra_cam_rtcpu_of_match);
|
|
|
|
static const struct dev_pm_ops tegra_cam_rtcpu_pm_ops = {
|
|
.suspend = tegra_camrtc_suspend,
|
|
.resume = tegra_camrtc_resume,
|
|
.runtime_suspend = tegra_cam_rtcpu_runtime_suspend,
|
|
.runtime_resume = tegra_cam_rtcpu_runtime_resume,
|
|
.runtime_idle = tegra_cam_rtcpu_runtime_idle,
|
|
};
|
|
|
|
/**
|
|
* @brief Remove the camera RTCPU
|
|
*
|
|
* This function removes the camera RTCPU
|
|
* - Calls @ref tegra_cam_rtcpu_remove() to remove the RTCPU
|
|
*
|
|
* @param[in] pdev Pointer to the platform device
|
|
* Valid value: non-NULL
|
|
*/
|
|
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
|
|
static void tegra_cam_rtcpu_remove_wrapper(struct platform_device *pdev)
|
|
{
|
|
tegra_cam_rtcpu_remove(pdev);
|
|
}
|
|
#else
|
|
static int tegra_cam_rtcpu_remove_wrapper(struct platform_device *pdev)
|
|
{
|
|
return tegra_cam_rtcpu_remove(pdev);
|
|
}
|
|
#endif
|
|
|
|
static struct platform_driver tegra_cam_rtcpu_driver = {
|
|
.driver = {
|
|
.name = "tegra186-cam-rtcpu",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(tegra_cam_rtcpu_of_match),
|
|
#ifdef CONFIG_PM
|
|
.pm = &tegra_cam_rtcpu_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = tegra_cam_rtcpu_probe,
|
|
.remove = tegra_cam_rtcpu_remove_wrapper,
|
|
.shutdown = tegra_cam_rtcpu_shutdown,
|
|
};
|
|
module_platform_driver(tegra_cam_rtcpu_driver);
|
|
|
|
MODULE_DESCRIPTION("CAMERA RTCPU driver");
|
|
MODULE_AUTHOR("NVIDIA");
|
|
MODULE_LICENSE("GPL v2");
|