mirror of
git://nv-tegra.nvidia.com/linux-hwpm.git
synced 2025-12-23 01:35:10 +03:00
Add below HALs to make code chip agnostic. This will allow us to use t234 specific HALs for next chips. - get_pma_int_idx: get PMA's internal index corresponding to active chip - get_rtr_int_idx: get RTR's internal index corresponding to active chip - get_ip_max_idx: get MAX IP index corresponding to active chip Move chip agnostic code to common files. Jira THWPM-41 Change-Id: I5518469b1473fe7f66b6517cee729cf46520bbac Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2675515 Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: Vasuki Shankar <vasukis@nvidia.com> Reviewed-by: Seema Khowala <seemaj@nvidia.com> GVS: Gerrit_Virtual_Submit
472 lines
11 KiB
C
472 lines
11 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 <soc/tegra/fuse.h>
|
|
#include <uapi/linux/tegra-soc-hwpm-uapi.h>
|
|
|
|
#include <tegra_hwpm_log.h>
|
|
#include <tegra_hwpm.h>
|
|
#include <tegra_hwpm_common.h>
|
|
#include <tegra_hwpm_static_analysis.h>
|
|
|
|
/* ip_idx indicates internal active ip index */
|
|
static int tegra_hwpm_reserve_given_resource(struct tegra_soc_hwpm *hwpm,
|
|
u32 ip_idx)
|
|
{
|
|
int err = 0, ret = 0;
|
|
u32 perfmux_idx, perfmon_idx;
|
|
unsigned long inst_idx = 0UL;
|
|
unsigned long floorsweep_info = 0UL, reserved_insts = 0UL;
|
|
struct tegra_soc_hwpm_chip *active_chip = hwpm->active_chip;
|
|
struct hwpm_ip *chip_ip = active_chip->chip_ips[ip_idx];
|
|
hwpm_ip_perfmon *perfmon = NULL;
|
|
hwpm_ip_perfmux *perfmux = NULL;
|
|
|
|
floorsweep_info = (unsigned long)chip_ip->fs_mask;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
tegra_hwpm_dbg(hwpm, hwpm_info, "Reserve IP %d, fs_mask 0x%x",
|
|
ip_idx, chip_ip->fs_mask);
|
|
|
|
/* PMA and RTR are already reserved */
|
|
if ((ip_idx == active_chip->get_pma_int_idx(hwpm)) ||
|
|
(ip_idx == active_chip->get_rtr_int_idx(hwpm))) {
|
|
return 0;
|
|
}
|
|
|
|
for_each_set_bit(inst_idx, &floorsweep_info, 32U) {
|
|
/* Reserve all perfmon belonging to this instance */
|
|
for (perfmon_idx = 0U; perfmon_idx < chip_ip->num_perfmon_slots;
|
|
perfmon_idx++) {
|
|
perfmon = chip_ip->ip_perfmon[perfmon_idx];
|
|
|
|
if (perfmon == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmon->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = tegra_hwpm_perfmon_reserve(hwpm, perfmon);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmon %d reserve failed",
|
|
ip_idx, perfmon_idx);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* Reserve all perfmux belonging to this instance */
|
|
for (perfmux_idx = 0U; perfmux_idx < chip_ip->num_perfmux_slots;
|
|
perfmux_idx++) {
|
|
perfmux = chip_ip->ip_perfmux[perfmux_idx];
|
|
|
|
if (perfmux == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmux->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = tegra_hwpm_perfmux_reserve(hwpm, perfmux);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmux %d reserve failed",
|
|
ip_idx, perfmux_idx);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
reserved_insts |= BIT(inst_idx);
|
|
}
|
|
chip_ip->reserved = true;
|
|
|
|
return 0;
|
|
fail:
|
|
if (hwpm->active_chip->perfmon_disable == NULL) {
|
|
tegra_hwpm_err(hwpm, "perfmon_disable HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (hwpm->active_chip->perfmux_disable == NULL) {
|
|
tegra_hwpm_err(hwpm, "perfmux_disable HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* release reserved instances */
|
|
for_each_set_bit(inst_idx, &reserved_insts, 32U) {
|
|
/* Release all perfmon belonging to this instance */
|
|
for (perfmon_idx = 0U; perfmon_idx < chip_ip->num_perfmon_slots;
|
|
perfmon_idx++) {
|
|
perfmon = chip_ip->ip_perfmon[perfmon_idx];
|
|
|
|
if (perfmon == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmon->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
ret = hwpm->active_chip->perfmon_disable(hwpm, perfmon);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmon %d disable failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
|
|
ret = tegra_hwpm_perfmon_release(hwpm, perfmon);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmon %d release failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
}
|
|
|
|
/* Release all perfmux belonging to this instance */
|
|
for (perfmux_idx = 0U; perfmux_idx < chip_ip->num_perfmux_slots;
|
|
perfmux_idx++) {
|
|
perfmux = chip_ip->ip_perfmux[perfmux_idx];
|
|
|
|
if (perfmux == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmux->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
ret = hwpm->active_chip->perfmux_disable(hwpm, perfmux);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmux %d disable failed",
|
|
ip_idx, perfmux_idx);
|
|
}
|
|
|
|
ret = tegra_hwpm_perfmux_release(hwpm, perfmux);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm,
|
|
"IP %d perfmux %d release failed",
|
|
ip_idx, perfmux_idx);
|
|
}
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int tegra_hwpm_reserve_resource(struct tegra_soc_hwpm *hwpm, u32 resource)
|
|
{
|
|
struct tegra_soc_hwpm_chip *active_chip = hwpm->active_chip;
|
|
struct hwpm_ip *chip_ip = NULL;
|
|
u32 ip_idx = TEGRA_SOC_HWPM_IP_INACTIVE;
|
|
int ret = 0;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
tegra_hwpm_dbg(hwpm, hwpm_info,
|
|
"User requesting to reserve resource %d", resource);
|
|
|
|
/* Translate resource to ip_idx */
|
|
if (!active_chip->is_resource_active(hwpm, resource, &ip_idx)) {
|
|
tegra_hwpm_err(hwpm, "Requested resource %d is unavailable",
|
|
resource);
|
|
/* Remove after uapi update */
|
|
if (resource == TEGRA_SOC_HWPM_RESOURCE_MSS_NVLINK) {
|
|
tegra_hwpm_dbg(hwpm, hwpm_verbose,
|
|
"ignoring resource %d", resource);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Get IP structure from ip_idx */
|
|
chip_ip = active_chip->chip_ips[ip_idx];
|
|
|
|
/* Skip IPs which are already reserved (covers PMA and RTR case) */
|
|
if (chip_ip->reserved) {
|
|
tegra_hwpm_dbg(hwpm, hwpm_info,
|
|
"Chip IP %d already reserved", ip_idx);
|
|
return 0;
|
|
}
|
|
|
|
/* Make sure IP override is not enabled */
|
|
if (chip_ip->override_enable) {
|
|
tegra_hwpm_dbg(hwpm, hwpm_info,
|
|
"Chip IP %d not available", ip_idx);
|
|
return 0;
|
|
}
|
|
|
|
ret = tegra_hwpm_reserve_given_resource(hwpm, ip_idx);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm, "Failed to reserve resource %d", resource);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_hwpm_bind_reserved_resources(struct tegra_soc_hwpm *hwpm)
|
|
{
|
|
struct tegra_soc_hwpm_chip *active_chip = hwpm->active_chip;
|
|
struct hwpm_ip *chip_ip = NULL;
|
|
u32 ip_idx;
|
|
u32 perfmux_idx, perfmon_idx;
|
|
unsigned long inst_idx = 0UL;
|
|
unsigned long floorsweep_info = 0UL;
|
|
int err = 0;
|
|
hwpm_ip_perfmon *perfmon = NULL;
|
|
hwpm_ip_perfmux *perfmux = NULL;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
if (hwpm->active_chip->zero_alist_regs == NULL) {
|
|
tegra_hwpm_err(hwpm,
|
|
"zero_alist_regs HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (hwpm->active_chip->perfmon_enable == NULL) {
|
|
tegra_hwpm_err(hwpm,
|
|
"perfmon_enable HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
for (ip_idx = 0U; ip_idx < active_chip->get_ip_max_idx(hwpm);
|
|
ip_idx++) {
|
|
chip_ip = active_chip->chip_ips[ip_idx];
|
|
|
|
/* Skip unavailable IPs */
|
|
if (!chip_ip->reserved) {
|
|
continue;
|
|
}
|
|
|
|
if (chip_ip->fs_mask == 0U) {
|
|
/* No IP instance is available */
|
|
continue;
|
|
}
|
|
|
|
floorsweep_info = (unsigned long)chip_ip->fs_mask;
|
|
|
|
for_each_set_bit(inst_idx, &floorsweep_info, 32U) {
|
|
/* Zero out necessary perfmux registers */
|
|
for (perfmux_idx = 0U;
|
|
perfmux_idx < chip_ip->num_perfmux_slots;
|
|
perfmux_idx++) {
|
|
perfmux = chip_ip->ip_perfmux[perfmux_idx];
|
|
|
|
if (perfmux == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmux->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = hwpm->active_chip->zero_alist_regs(
|
|
hwpm, perfmux);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmux %d zero regs failed",
|
|
ip_idx, perfmux_idx);
|
|
}
|
|
}
|
|
|
|
/* Zero out necessary perfmon registers */
|
|
/* And enable reporting of PERFMON status */
|
|
for (perfmon_idx = 0U;
|
|
perfmon_idx < chip_ip->num_perfmon_slots;
|
|
perfmon_idx++) {
|
|
perfmon = chip_ip->ip_perfmon[perfmon_idx];
|
|
|
|
if (perfmon == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmon->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = hwpm->active_chip->zero_alist_regs(
|
|
hwpm, perfmon);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmon %d zero regs failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
|
|
err = hwpm->active_chip->perfmon_enable(
|
|
hwpm, perfmon);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmon %d enable failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
int tegra_hwpm_bind_resources(struct tegra_soc_hwpm *hwpm)
|
|
{
|
|
int ret = 0;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
ret = tegra_hwpm_bind_reserved_resources(hwpm);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm, "failed to bind resources");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tegra_hwpm_release_all_resources(struct tegra_soc_hwpm *hwpm)
|
|
{
|
|
struct tegra_soc_hwpm_chip *active_chip = hwpm->active_chip;
|
|
struct hwpm_ip *chip_ip = NULL;
|
|
hwpm_ip_perfmon *perfmon = NULL;
|
|
hwpm_ip_perfmux *perfmux = NULL;
|
|
u32 ip_idx;
|
|
u32 perfmux_idx, perfmon_idx;
|
|
unsigned long floorsweep_info = 0UL;
|
|
unsigned long inst_idx = 0UL;
|
|
int err = 0;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
for (ip_idx = 0U; ip_idx < active_chip->get_ip_max_idx(hwpm);
|
|
ip_idx++) {
|
|
chip_ip = active_chip->chip_ips[ip_idx];
|
|
|
|
/* PMA and RTR will be released later */
|
|
if ((ip_idx == active_chip->get_pma_int_idx(hwpm)) ||
|
|
(ip_idx == active_chip->get_rtr_int_idx(hwpm))) {
|
|
continue;
|
|
}
|
|
|
|
/* Disable only available IPs */
|
|
if (chip_ip->override_enable) {
|
|
/* IP not available */
|
|
continue;
|
|
}
|
|
|
|
/* Disable and release only reserved IPs */
|
|
if (!chip_ip->reserved) {
|
|
continue;
|
|
}
|
|
|
|
if (chip_ip->fs_mask == 0U) {
|
|
/* No IP instance is available */
|
|
continue;
|
|
}
|
|
|
|
if (hwpm->active_chip->perfmon_disable == NULL) {
|
|
tegra_hwpm_err(hwpm,
|
|
"perfmon_disable HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (hwpm->active_chip->perfmux_disable == NULL) {
|
|
tegra_hwpm_err(hwpm,
|
|
"perfmux_disable HAL uninitialized");
|
|
return -ENODEV;
|
|
}
|
|
|
|
floorsweep_info = (unsigned long)chip_ip->fs_mask;
|
|
|
|
for_each_set_bit(inst_idx, &floorsweep_info, 32U) {
|
|
/* Release all perfmon associated with inst_idx */
|
|
for (perfmon_idx = 0U;
|
|
perfmon_idx < chip_ip->num_perfmon_slots;
|
|
perfmon_idx++) {
|
|
perfmon = chip_ip->ip_perfmon[perfmon_idx];
|
|
|
|
if (perfmon == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmon->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = hwpm->active_chip->perfmon_disable(
|
|
hwpm, perfmon);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmon %d disable failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
|
|
err = tegra_hwpm_perfmon_release(hwpm, perfmon);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmon %d release failed",
|
|
ip_idx, perfmon_idx);
|
|
}
|
|
}
|
|
|
|
/* Release all perfmux associated with inst_idx */
|
|
for (perfmux_idx = 0U;
|
|
perfmux_idx < chip_ip->num_perfmux_slots;
|
|
perfmux_idx++) {
|
|
perfmux = chip_ip->ip_perfmux[perfmux_idx];
|
|
|
|
if (perfmux == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (perfmux->hw_inst_mask != BIT(inst_idx)) {
|
|
continue;
|
|
}
|
|
|
|
err = hwpm->active_chip->perfmux_disable(
|
|
hwpm, perfmux);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmux %d disable failed",
|
|
ip_idx, perfmux_idx);
|
|
}
|
|
|
|
err = tegra_hwpm_perfmux_release(hwpm, perfmux);
|
|
if (err != 0) {
|
|
tegra_hwpm_err(hwpm, "IP %d"
|
|
" perfmux %d release failed",
|
|
ip_idx, perfmux_idx);
|
|
}
|
|
}
|
|
}
|
|
chip_ip->reserved = false;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int tegra_hwpm_release_resources(struct tegra_soc_hwpm *hwpm)
|
|
{
|
|
int ret = 0;
|
|
|
|
tegra_hwpm_fn(hwpm, " ");
|
|
|
|
ret = tegra_hwpm_release_all_resources(hwpm);
|
|
if (ret != 0) {
|
|
tegra_hwpm_err(hwpm, "failed to release resources");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|