Files
linux-hwpm/drivers/tegra/hwpm/os/linux/aperture_utils.c
Vedashree Vidwans 48e85a9c07 tegra: hwpm: update logic to use static indexes
HWPM driver uses nested structures and arrays of structures. The IP
structure setup logic allocates pointer arrays based on dynamic list of
IPs and aperture addresses. This dynamic list is required to search
given regops address in less amount of time.
However, there is a chance that the number of pointers computed
dynamically is huge. And huge amount of memory will be required for the
dynamic pointers array, which is impractical.
This, this patch modifies ip structure setup and address to aperture
conversion logic to use static indexes if the pointer array size is
huge.
This patch modifies relevant functions to always use static arrays
to access instance and aperture structures.

If dynamic pointers array is allocated, the patch adds logic to
translate dynamic index to static index using inst_index_mask for
instances and new added aperture_index for element level structures.

Add/update few log message to improve relayed information.

Bug 4707244

Change-Id: Ib4847e6575f82b628a3ce838ad69196a4bc08fed
Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-hwpm/+/3186843
Reviewed-by: Vasuki Shankar <vasukis@nvidia.com>
Reviewed-by: Seema Khowala <seemaj@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
2024-09-06 11:58:53 -07:00

196 lines
5.5 KiB
C

/*
* SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: GPL-2.0-only
*
* 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.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/bitops.h>
#include <tegra_hwpm_static_analysis.h>
#include <tegra_hwpm_aperture.h>
#include <tegra_hwpm_kmem.h>
#include <tegra_hwpm_log.h>
#include <tegra_hwpm_io.h>
#include <tegra_hwpm.h>
#include <os/linux/driver.h>
u32 tegra_hwpm_ffs_impl(struct tegra_soc_hwpm *hwpm, u64 number)
{
if (number == 0) {
return 0;
}
return __ffs(number);
}
int tegra_hwpm_perfmon_reserve_impl(struct tegra_soc_hwpm *hwpm,
struct hwpm_ip_inst *ip_inst, struct hwpm_ip_aperture *perfmon)
{
struct resource *res = NULL;
struct tegra_hwpm_os_linux *hwpm_linux = NULL;
tegra_hwpm_fn(hwpm, " ");
hwpm_linux = tegra_hwpm_os_linux_from_hwpm(hwpm);
if (!hwpm_linux) {
tegra_hwpm_err(NULL, "Invalid hwpm_linux struct");
return -ENODEV;
}
/* Reserve */
if ((perfmon->element_type == HWPM_ELEMENT_PERFMON) ||
(perfmon->element_type == HWPM_ELEMENT_PERFMUX)) {
/* Make sure that resource exists in device node */
res = platform_get_resource(hwpm_linux->pdev,
IORESOURCE_MEM, perfmon->device_index);
} else {
tegra_hwpm_err(hwpm,
"Unknown perfmon type, execution shouldn't reach here");
return -EINVAL;
}
if ((!res) || (res->start == 0) || (res->end == 0)) {
tegra_hwpm_err(hwpm, "Failed to get perfmon %s", perfmon->name);
return -ENOMEM;
}
/* Confirm that correct resource is retrived */
if (res->start != perfmon->start_pa) {
tegra_hwpm_err(hwpm, "Failed to get correct"
"perfmon address for %s,"
"Expected - 0x%llx, Returned - 0x%llx",
perfmon->name, perfmon->start_pa, res->start);
return -ENOMEM;
}
if (hwpm->fake_registers_enabled) {
/* Allocate resource memory as MMIO */
u64 address_range = tegra_hwpm_safe_add_u64(
tegra_hwpm_safe_sub_u64(res->end, res->start), 1ULL);
u64 num_regs = address_range / sizeof(u32);
perfmon->fake_registers =
tegra_hwpm_kcalloc(hwpm, num_regs, sizeof(u32));
if (perfmon->fake_registers == NULL) {
tegra_hwpm_err(hwpm, "Perfmon (0x%llx - 0x%llx) "
"Couldn't allocate memory for fake regs",
perfmon->start_abs_pa, perfmon->end_abs_pa);
return -ENOMEM;
}
} else {
/* Map resource memory in kernel space */
perfmon->dt_mmio = devm_ioremap(
hwpm_linux->dev, res->start, resource_size(res));
if (IS_ERR(perfmon->dt_mmio)) {
tegra_hwpm_err(hwpm,
"Couldn't map perfmon %s", perfmon->name);
return PTR_ERR(perfmon->dt_mmio);
}
}
return 0;
}
int tegra_hwpm_perfmux_reserve_impl(struct tegra_soc_hwpm *hwpm,
struct hwpm_ip_inst *ip_inst, struct hwpm_ip_aperture *perfmux)
{
int ret = 0;
u32 reg_val = 0U;
tegra_hwpm_fn(hwpm, " ");
/* Allocate fake registers */
if (hwpm->fake_registers_enabled) {
u64 address_range = tegra_hwpm_safe_add_u64(
tegra_hwpm_safe_sub_u64(perfmux->end_abs_pa,
perfmux->start_abs_pa), 1ULL);
u64 num_regs = address_range / sizeof(u32);
perfmux->fake_registers =
tegra_hwpm_kcalloc(hwpm, num_regs, sizeof(u32));
if (perfmux->fake_registers == NULL) {
tegra_hwpm_err(hwpm, "Aperture(0x%llx - 0x%llx):"
" Couldn't allocate memory for fake registers",
perfmux->start_abs_pa, perfmux->end_abs_pa);
return -ENOMEM;
}
}
/* Validate perfmux availability by reading 1st alist offset */
ret = tegra_hwpm_regops_readl(hwpm, ip_inst, perfmux,
tegra_hwpm_safe_add_u64(perfmux->start_abs_pa,
perfmux->alist[0U].reg_offset), &reg_val);
if (ret != 0) {
/*
* If an IP element is unavailable, perfmux register
* read will return with failure.
* Mark corresponding element as unavailable.
* NOTE: This is possible if IP elements are floorswept.
* Hence, failure should not be propagated.
*/
tegra_hwpm_dbg(hwpm, hwpm_dbg_reserve_resource,
"perfmux start_abs_pa 0x%llx unavailable",
perfmux->start_abs_pa);
ip_inst->element_fs_mask &= ~(perfmux->element_index_mask);
}
return 0;
}
int tegra_hwpm_perfmon_release_impl(struct tegra_soc_hwpm *hwpm,
struct hwpm_ip_aperture *perfmon)
{
struct tegra_hwpm_os_linux *hwpm_linux = NULL;
tegra_hwpm_fn(hwpm, " ");
hwpm_linux = tegra_hwpm_os_linux_from_hwpm(hwpm);
if (!hwpm_linux) {
tegra_hwpm_err(NULL, "Invalid hwpm_linux struct");
return -ENODEV;
}
if (perfmon->dt_mmio == NULL) {
tegra_hwpm_err(hwpm, "Perfmon was not mapped");
return -EINVAL;
}
devm_iounmap(hwpm_linux->dev, perfmon->dt_mmio);
perfmon->dt_mmio = NULL;
if (perfmon->fake_registers) {
tegra_hwpm_kfree(hwpm, perfmon->fake_registers);
perfmon->fake_registers = NULL;
}
return 0;
}
int tegra_hwpm_perfmux_release_impl(struct tegra_soc_hwpm *hwpm,
struct hwpm_ip_aperture *perfmux)
{
tegra_hwpm_fn(hwpm, " ");
/*
* Release
* This is only required for fake registers
*/
if (perfmux->fake_registers) {
tegra_hwpm_kfree(hwpm, perfmux->fake_registers);
perfmux->fake_registers = NULL;
}
return 0;
}