Files
linux-hwpm/os/linux/tegra_hwpm_ip.c
Vedashree Vidwans fe7a4734a2 tegra: hwpm: update IP registration interface
Enum tegra_soc_hwpm_ip is defined to query IP information. To untangle
its use from HWPM related functionality, enum tegra_soc_hwpm_resource
should be used by IP drivers to register with HWPM.

An IP can have multiple instances with multiple IP core elements in
each instance. Currently, the IP <-> HWPM register access API passes
register offset only. However, it is not possible to figure out
requested specific register access only with register offset. For
example, MC device has one instance with 16 duplicate channels. To
access channel x perfmux register, HWPM driver should pass perfmux
register offset alongwith channel number (index).
Add instance element index as an argument to the register access API,
tegra_soc_hwpm_ip_ops.hwpm_ip_reg_op().
-Update internal copy of tegra_soc_hwpm_ip_ops.
-Update hwpm_ip_reg_op() implemented in flcn driver.
-Update hwpm_ip_reg_op() implemented in pva driver.

Update hwpm_ip_pm() in flcn driver to use nvhost power management APIs.

Bug 3573882
Jira THWPM-8

Change-Id: I0138927f383e9a67085816132ce33538bd609560
Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2713274
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: svc-mobile-cert <svc-mobile-cert@nvidia.com>
Reviewed-by: Vasuki Shankar <vasukis@nvidia.com>
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
Reviewed-by: Seema Khowala <seemaj@nvidia.com>
GVS: Gerrit_Virtual_Submit
2022-05-18 22:56:56 -07:00

175 lines
4.4 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/slab.h>
#include <soc/tegra/fuse.h>
#include <uapi/linux/tegra-soc-hwpm-uapi.h>
#include <tegra_hwpm_log.h>
#include <tegra_hwpm_io.h>
#include <tegra_hwpm.h>
struct platform_device *tegra_soc_hwpm_pdev;
struct hwpm_ip_register_list *ip_register_list_head;
#define REGISTER_IP true
#define UNREGISTER_IP false
static int tegra_hwpm_alloc_ip_register_list_node(
struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops,
struct hwpm_ip_register_list **node_ptr)
{
struct hwpm_ip_register_list *new_node = NULL;
new_node = kzalloc(sizeof(struct hwpm_ip_register_list), GFP_KERNEL);
if (new_node == NULL) {
tegra_hwpm_err(NULL,
"struct hwpm_ip_register_list node allocation failed");
return -ENOMEM;
}
new_node->next = NULL;
/* Copy given ip register details to node */
memcpy(&new_node->ip_ops, hwpm_ip_ops,
sizeof(struct tegra_soc_hwpm_ip_ops));
(*node_ptr) = new_node;
return 0;
}
static int tegra_hwpm_note_ip_register(
struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops)
{
int err = 0;
struct hwpm_ip_register_list *node;
if (ip_register_list_head == NULL) {
err = tegra_hwpm_alloc_ip_register_list_node(hwpm_ip_ops,
&ip_register_list_head);
if (err != 0) {
tegra_hwpm_err(NULL,
"failed to note ip registration");
return err;
}
} else {
node = ip_register_list_head;
while (node->next != NULL) {
node = node->next;
}
err = tegra_hwpm_alloc_ip_register_list_node(hwpm_ip_ops,
&node->next);
if (err != 0) {
tegra_hwpm_err(NULL,
"failed to note ip registration");
return err;
}
}
return err;
}
void tegra_soc_hwpm_ip_register(struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops)
{
struct tegra_soc_hwpm *hwpm = NULL;
int ret = 0;
if (hwpm_ip_ops == NULL) {
tegra_hwpm_err(NULL, "IP details missing");
return;
}
if (tegra_soc_hwpm_pdev == NULL) {
ret = tegra_hwpm_note_ip_register(hwpm_ip_ops);
if (ret != 0) {
tegra_hwpm_err(NULL,
"Couldn't save IP register details");
return;
}
} else {
if (hwpm_ip_ops->ip_dev == NULL) {
tegra_hwpm_err(hwpm, "IP dev to register is NULL");
return;
}
hwpm = platform_get_drvdata(tegra_soc_hwpm_pdev);
tegra_hwpm_dbg(hwpm, hwpm_info,
"Register IP 0x%llx", hwpm_ip_ops->ip_base_address);
ret = hwpm->active_chip->extract_ip_ops(hwpm,
hwpm_ip_ops, REGISTER_IP);
if (ret < 0) {
tegra_hwpm_err(hwpm, "Failed to set IP ops for IP %d",
hwpm_ip_ops->resource_enum);
}
}
}
void tegra_soc_hwpm_ip_unregister(struct tegra_soc_hwpm_ip_ops *hwpm_ip_ops)
{
struct tegra_soc_hwpm *hwpm = NULL;
int ret = 0;
if (hwpm_ip_ops == NULL) {
tegra_hwpm_err(NULL, "IP details missing");
return;
}
if (tegra_soc_hwpm_pdev == NULL) {
tegra_hwpm_dbg(hwpm, hwpm_info, "HWPM device not available");
} else {
if (hwpm_ip_ops->ip_dev == NULL) {
tegra_hwpm_err(hwpm, "IP dev to unregister is NULL");
return;
}
hwpm = platform_get_drvdata(tegra_soc_hwpm_pdev);
tegra_hwpm_dbg(hwpm, hwpm_info,
"Unregister IP 0x%llx", hwpm_ip_ops->ip_base_address);
ret = hwpm->active_chip->extract_ip_ops(hwpm,
hwpm_ip_ops, UNREGISTER_IP);
if (ret < 0) {
tegra_hwpm_err(hwpm, "Failed to reset IP ops for IP %d",
hwpm_ip_ops->resource_enum);
}
}
}
int tegra_hwpm_get_floorsweep_info(struct tegra_soc_hwpm *hwpm,
struct tegra_soc_hwpm_ip_floorsweep_info *fs_info)
{
int ret = 0;
u32 i = 0U;
tegra_hwpm_fn(hwpm, " ");
for (i = 0U; i < fs_info->num_queries; i++) {
ret = hwpm->active_chip->get_fs_info(
hwpm, (u32)fs_info->ip_fsinfo[i].ip_type,
&fs_info->ip_fsinfo[i].ip_inst_mask,
&fs_info->ip_fsinfo[i].status);
if (ret < 0) {
/* Print error for debug purpose. */
tegra_hwpm_err(hwpm, "Failed to get fs_info");
}
tegra_hwpm_dbg(hwpm, hwpm_verbose,
"Query %d: ip_type %d: ip_status: %d inst_mask 0x%llx",
i, fs_info->ip_fsinfo[i].ip_type,
fs_info->ip_fsinfo[i].status,
fs_info->ip_fsinfo[i].ip_inst_mask);
}
return ret;
}