mirror of
git://nv-tegra.nvidia.com/linux-hwpm.git
synced 2025-12-23 01:35:10 +03:00
Using this patch we are destroying the device if
clock API gets failed and handling the failure
case gracefully otherwise we are are going to get
below warning if the probe gets deferred.
[ 9.383997] sysfs: cannot create duplicate
filename '/devices/virtual/tegra-soc-hwpm'
[ 9.384000] CPU: 1 PID: 92 Comm: kworker/1:1
Tainted: G W 5.10.104-rt63-tegra #8
[ 9.384002] Hardware name: p3710-0010 (DT)
[ 9.384004] Workqueue: events deferred_probe_work_func
[ 9.384010] Call trace:
[ 9.384011] dump_backtrace+0x0/0x1d0
[ 9.384016] show_stack+0x30/0x50
[ 9.384019] dump_stack+0xd8/0x140
[ 9.384022] sysfs_warn_dup+0x6c/0x90
[ 9.384026] sysfs_create_dir_ns+0xf0/0x110
[ 9.384028] kobject_add_internal+0x94/0x2a0
[ 9.384032] kobject_add+0x90/0x110
[ 9.384033] get_device_parent.isra.0+0x184/0x1b0
[ 9.384036] device_add+0xcc/0x760
[ 9.384039] device_create_groups_vargs+0xec/0x110
[ 9.384041] device_create+0x94/0xd0
[ 9.384043] tegra_hwpm_probe+0x114/0x4c0
[ 9.384048] platform_drv_probe+0x5c/0xd0
[ 9.384050] really_probe+0xf8/0x3e0
[ 9.384051] driver_probe_device+0x60/0xd0
[ 9.384053] __device_attach_driver+0x8c/0xf0
[ 9.384054] bus_for_each_drv+0x8c/0x100
[ 9.384057] __device_attach+0x100/0x160
[ 9.384058] device_initial_probe+0x28/0x40
[ 9.384060] bus_probe_device+0xac/0xd0
[ 9.384061] deferred_probe_work_func+0x90/0xd0
[ 9.384062] process_one_work+0x1c4/0x4f0
[ 9.384064] worker_thread+0x200/0x430
[ 9.384065] kthread+0x180/0x1c0
[ 9.384068] ret_from_fork+0x10/0x24
[ 9.384070] kobject_add_internal failed for tegra-soc-hwpm
with -EEXIST, don't try to register things with
the same name in the same directory.
[ 9.384073] tegra-soc-hwpm: tegra_hwpm_probe: 127: ERROR:
Failed to create device
[ 9.384080] tegra-soc-hwpm: tegra_hwpm_probe: 208: ERROR:
Probe failed!
JIRA ESLC-6775
Signed-off-by: Manish Bhardwaj <mbhardwaj@nvidia.com>
Change-Id: Ide0d16f420a1c52eadf1b6166859c02906c2ac2f
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2737713
Reviewed-by: Vedashree Vidwans <vvidwans@nvidia.com>
Reviewed-by: Vipin Kumar <vipink@nvidia.com>
GVS: Gerrit_Virtual_Submit
281 lines
6.7 KiB
C
281 lines
6.7 KiB
C
/*
|
|
* Copyright (c) 2021-2022, 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 <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/module.h>
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <soc/tegra/fuse.h>
|
|
|
|
#include <tegra_hwpm_log.h>
|
|
#include <tegra_hwpm_debugfs.h>
|
|
#include <tegra_hwpm.h>
|
|
#include <tegra_hwpm_common.h>
|
|
|
|
static const struct of_device_id tegra_soc_hwpm_of_match[] = {
|
|
{
|
|
.compatible = "nvidia,t234-soc-hwpm",
|
|
},
|
|
#ifdef CONFIG_TEGRA_NEXT1_HWPM
|
|
#include <os/linux/tegra_hwpm_next_linux.h>
|
|
#endif
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, tegra_soc_hwpm_of_match);
|
|
|
|
static char *tegra_hwpm_get_devnode(struct device *dev, umode_t *mode)
|
|
{
|
|
if (!mode) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Allow root:debug ownership */
|
|
*mode = 0660;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool tegra_hwpm_read_support_soc_tools_prop(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
bool allow_node = of_property_read_bool(np, "support-soc-tools");
|
|
|
|
if (!allow_node) {
|
|
tegra_hwpm_err(NULL, "support-soc-tools is absent");
|
|
}
|
|
|
|
return allow_node;
|
|
}
|
|
|
|
static int tegra_hwpm_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct device *dev = NULL;
|
|
struct tegra_soc_hwpm *hwpm = NULL;
|
|
|
|
if (!pdev) {
|
|
tegra_hwpm_err(NULL, "Invalid platform device");
|
|
ret = -ENODEV;
|
|
goto fail;
|
|
}
|
|
|
|
if (!tegra_hwpm_read_support_soc_tools_prop(pdev)) {
|
|
tegra_hwpm_err(NULL, "SOC HWPM not supported in this config");
|
|
ret = -ENODEV;
|
|
goto fail;
|
|
}
|
|
|
|
hwpm = kzalloc(sizeof(struct tegra_soc_hwpm), GFP_KERNEL);
|
|
if (!hwpm) {
|
|
tegra_hwpm_err(hwpm, "Couldn't allocate memory for hwpm struct");
|
|
ret = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
hwpm->pdev = pdev;
|
|
hwpm->dev = &pdev->dev;
|
|
hwpm->np = pdev->dev.of_node;
|
|
hwpm->class.owner = THIS_MODULE;
|
|
hwpm->class.name = TEGRA_SOC_HWPM_MODULE_NAME;
|
|
|
|
/* Create device node */
|
|
ret = class_register(&hwpm->class);
|
|
if (ret) {
|
|
tegra_hwpm_err(hwpm, "Failed to register class");
|
|
goto class_register;
|
|
}
|
|
|
|
/* Set devnode to retrieve device permissions */
|
|
hwpm->class.devnode = tegra_hwpm_get_devnode;
|
|
|
|
ret = alloc_chrdev_region(&hwpm->dev_t, 0, 1, dev_name(hwpm->dev));
|
|
if (ret) {
|
|
tegra_hwpm_err(hwpm, "Failed to allocate device region");
|
|
goto alloc_chrdev_region;
|
|
}
|
|
|
|
cdev_init(&hwpm->cdev, &tegra_soc_hwpm_ops);
|
|
hwpm->cdev.owner = THIS_MODULE;
|
|
|
|
ret = cdev_add(&hwpm->cdev, hwpm->dev_t, 1);
|
|
if (ret) {
|
|
tegra_hwpm_err(hwpm, "Failed to add cdev");
|
|
goto cdev_add;
|
|
}
|
|
|
|
dev = device_create(&hwpm->class,
|
|
NULL,
|
|
hwpm->dev_t,
|
|
NULL,
|
|
TEGRA_SOC_HWPM_MODULE_NAME);
|
|
if (IS_ERR(dev)) {
|
|
tegra_hwpm_err(hwpm, "Failed to create device");
|
|
ret = PTR_ERR(dev);
|
|
goto device_create;
|
|
}
|
|
|
|
(void) dma_set_mask_and_coherent(hwpm->dev, DMA_BIT_MASK(39));
|
|
|
|
if (tegra_platform_is_silicon()) {
|
|
hwpm->la_clk = devm_clk_get(hwpm->dev, "la");
|
|
if (IS_ERR(hwpm->la_clk)) {
|
|
tegra_hwpm_err(hwpm, "Missing la clock");
|
|
ret = PTR_ERR(hwpm->la_clk);
|
|
goto clock_reset_fail;
|
|
}
|
|
|
|
hwpm->la_parent_clk = devm_clk_get(hwpm->dev, "parent");
|
|
if (IS_ERR(hwpm->la_parent_clk)) {
|
|
tegra_hwpm_err(hwpm, "Missing la parent clk");
|
|
ret = PTR_ERR(hwpm->la_parent_clk);
|
|
goto clock_reset_fail;
|
|
}
|
|
|
|
hwpm->la_rst = devm_reset_control_get(hwpm->dev, "la");
|
|
if (IS_ERR(hwpm->la_rst)) {
|
|
tegra_hwpm_err(hwpm, "Missing la reset");
|
|
ret = PTR_ERR(hwpm->la_rst);
|
|
goto clock_reset_fail;
|
|
}
|
|
|
|
hwpm->hwpm_rst = devm_reset_control_get(hwpm->dev, "hwpm");
|
|
if (IS_ERR(hwpm->hwpm_rst)) {
|
|
tegra_hwpm_err(hwpm, "Missing hwpm reset");
|
|
ret = PTR_ERR(hwpm->hwpm_rst);
|
|
goto clock_reset_fail;
|
|
}
|
|
}
|
|
|
|
tegra_hwpm_debugfs_init(hwpm);
|
|
ret = tegra_hwpm_init_sw_components(hwpm);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm, "Failed to init sw components");
|
|
goto init_sw_components_fail;
|
|
}
|
|
|
|
/*
|
|
* Currently VDK doesn't have a fmodel for SOC HWPM. Therefore, we
|
|
* enable fake registers on VDK for minimal testing.
|
|
*/
|
|
if (tegra_platform_is_vdk())
|
|
hwpm->fake_registers_enabled = true;
|
|
else
|
|
hwpm->fake_registers_enabled = false;
|
|
|
|
platform_set_drvdata(pdev, hwpm);
|
|
tegra_soc_hwpm_pdev = pdev;
|
|
|
|
tegra_hwpm_dbg(hwpm, hwpm_info, "Probe successful!");
|
|
goto success;
|
|
|
|
init_sw_components_fail:
|
|
if (tegra_platform_is_silicon()) {
|
|
if (hwpm->la_clk)
|
|
devm_clk_put(hwpm->dev, hwpm->la_clk);
|
|
if (hwpm->la_parent_clk)
|
|
devm_clk_put(hwpm->dev, hwpm->la_parent_clk);
|
|
if (hwpm->la_rst)
|
|
reset_control_assert(hwpm->la_rst);
|
|
if (hwpm->hwpm_rst)
|
|
reset_control_assert(hwpm->hwpm_rst);
|
|
}
|
|
clock_reset_fail:
|
|
device_destroy(&hwpm->class, hwpm->dev_t);
|
|
device_create:
|
|
cdev_del(&hwpm->cdev);
|
|
cdev_add:
|
|
unregister_chrdev_region(hwpm->dev_t, 1);
|
|
alloc_chrdev_region:
|
|
class_unregister(&hwpm->class);
|
|
class_register:
|
|
kfree(hwpm);
|
|
fail:
|
|
tegra_hwpm_err(NULL, "Probe failed!");
|
|
success:
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_hwpm_remove(struct platform_device *pdev)
|
|
{
|
|
struct tegra_soc_hwpm *hwpm = NULL;
|
|
|
|
if (!pdev) {
|
|
tegra_hwpm_err(hwpm, "Invalid platform device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
hwpm = platform_get_drvdata(pdev);
|
|
if (!hwpm) {
|
|
tegra_hwpm_err(hwpm, "Invalid hwpm struct");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (tegra_platform_is_silicon()) {
|
|
if (hwpm->la_clk)
|
|
devm_clk_put(hwpm->dev, hwpm->la_clk);
|
|
if (hwpm->la_parent_clk)
|
|
devm_clk_put(hwpm->dev, hwpm->la_parent_clk);
|
|
if (hwpm->la_rst)
|
|
reset_control_assert(hwpm->la_rst);
|
|
if (hwpm->hwpm_rst)
|
|
reset_control_assert(hwpm->hwpm_rst);
|
|
}
|
|
|
|
device_destroy(&hwpm->class, hwpm->dev_t);
|
|
cdev_del(&hwpm->cdev);
|
|
unregister_chrdev_region(hwpm->dev_t, 1);
|
|
class_unregister(&hwpm->class);
|
|
|
|
tegra_hwpm_debugfs_deinit(hwpm);
|
|
tegra_hwpm_release_sw_components(hwpm);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver tegra_soc_hwpm_pdrv = {
|
|
.probe = tegra_hwpm_probe,
|
|
.remove = tegra_hwpm_remove,
|
|
.driver = {
|
|
.name = TEGRA_SOC_HWPM_MODULE_NAME,
|
|
.of_match_table = of_match_ptr(tegra_soc_hwpm_of_match),
|
|
},
|
|
};
|
|
|
|
static int __init tegra_hwpm_init(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = platform_driver_register(&tegra_soc_hwpm_pdrv);
|
|
if (ret < 0)
|
|
tegra_hwpm_err(NULL, "Platform driver register failed");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit tegra_hwpm_exit(void)
|
|
{
|
|
platform_driver_unregister(&tegra_soc_hwpm_pdrv);
|
|
}
|
|
|
|
postcore_initcall(tegra_hwpm_init);
|
|
module_exit(tegra_hwpm_exit);
|
|
|
|
MODULE_ALIAS(TEGRA_SOC_HWPM_MODULE_NAME);
|
|
MODULE_DESCRIPTION("Tegra SOC HWPM Driver");
|
|
MODULE_LICENSE("GPL v2");
|