tegra: hwpm: read MC config fuse

On production board, MC config details are available through fuses. Add
function to read MC config fuse. Use the floorsweep fuse info to find
available elements.

Bug 3936487

Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com>
Change-Id: I9e1549e3dfb9c06d8013ca2e1d43eb21bf0289f4
(cherry picked from commit f38e98a94ab8d478af3ebe1c922da606df9b67dc)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-hwpm/+/2888554
Reviewed-by: Adeel Raza <araza@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Vedashree Vidwans
2023-02-22 13:59:48 -08:00
committed by mobile promotions
parent 4ecc672c3e
commit 9a9f2f3635
3 changed files with 115 additions and 1 deletions

View File

@@ -301,11 +301,23 @@ static int tegra_hwpm_func_single_element(struct tegra_soc_hwpm *hwpm,
e_info->element_arr[idx] = element; e_info->element_arr[idx] = element;
break; break;
case TEGRA_HWPM_UPDATE_IP_INST_MASK: case TEGRA_HWPM_UPDATE_IP_INST_MASK:
/* HWPM perfmuxes can be assumed to be available */ /* HWPM perfmuxes (PMA,RTR) can be assumed to be available */
if (element->element_type == HWPM_ELEMENT_PERFMUX) { if (element->element_type == HWPM_ELEMENT_PERFMUX) {
break; break;
} }
/* Handle IPs with some value of fuse_fs_mask */
if (ip_inst->fuse_fs_mask != 0U) {
if ((element->element_index_mask &
ip_inst->fuse_fs_mask) == 0U) {
/* This element is floorswept */
tegra_hwpm_dbg(hwpm, hwpm_dbg_floorsweep_info,
"skip floorswept element 0x%llx",
element->start_abs_pa);
break;
}
}
/* Validate perfmux availability by reading 1st alist offset */ /* Validate perfmux availability by reading 1st alist offset */
ret = tegra_hwpm_regops_readl(hwpm, ip_inst, element, ret = tegra_hwpm_regops_readl(hwpm, ip_inst, element,
tegra_hwpm_safe_add_u64(element->start_abs_pa, tegra_hwpm_safe_add_u64(element->start_abs_pa,

View File

@@ -198,6 +198,96 @@ fail:
return ret; return ret;
} }
static int t234_hwpm_validate_emc_config(struct tegra_soc_hwpm *hwpm)
{
struct tegra_soc_hwpm_chip *active_chip = hwpm->active_chip;
# if defined(CONFIG_T234_HWPM_IP_MSS_CHANNEL) || \
defined(CONFIG_T234_HWPM_IP_MSS_ISO_NISO_HUBS) || \
defined(CONFIG_T234_HWPM_IP_MSS_MCF)
struct hwpm_ip *chip_ip = NULL;
struct hwpm_ip_inst *ip_inst = NULL;
u32 inst_idx = 0U;
u32 element_mask_max = 0U;
#endif
u32 emc_disable_fuse_val = 0U;
u32 emc_disable_fuse_val_mask = 0xFU;
u32 emc_element_floorsweep_mask = 0U;
u32 idx = 0U;
int err;
tegra_hwpm_fn(hwpm, " ");
#define TEGRA_FUSE_EMC_DISABLE 0x8c0U
err = tegra_hwpm_fuse_readl(hwpm,
TEGRA_FUSE_EMC_DISABLE, &emc_disable_fuse_val);
if (err != 0) {
tegra_hwpm_err(hwpm, "emc_disable fuse read failed");
return err;
}
/*
* In floorsweep fuse value,
* each bit corresponds to 4 elements.
* Bit value 0 indicates those elements are
* available and bit value 1 indicates
* corresponding elements are floorswept.
*
* Convert floorsweep fuse value to available EMC elements.
*/
do {
if (emc_disable_fuse_val & 0x1U) {
emc_element_floorsweep_mask =
(emc_element_floorsweep_mask << 4U) | 0xFU;
}
emc_disable_fuse_val = (emc_disable_fuse_val >> 1U);
emc_disable_fuse_val_mask = (emc_disable_fuse_val_mask >> 1U);
} while (emc_disable_fuse_val_mask != 0U);
/* Set fuse value in MSS IP instances */
for (idx = 0U; idx < active_chip->get_ip_max_idx(hwpm); idx++) {
switch (idx) {
#if defined(CONFIG_T234_HWPM_IP_MSS_CHANNEL)
case T234_HWPM_IP_MSS_CHANNEL:
#endif
#if defined(CONFIG_T234_HWPM_IP_MSS_ISO_NISO_HUBS)
case T234_HWPM_IP_MSS_ISO_NISO_HUBS:
#endif
#if defined(CONFIG_T234_HWPM_IP_MSS_MCF)
case T234_HWPM_IP_MSS_MCF:
#endif
# if defined(CONFIG_T234_HWPM_IP_MSS_CHANNEL) || \
defined(CONFIG_T234_HWPM_IP_MSS_ISO_NISO_HUBS) || \
defined(CONFIG_T234_HWPM_IP_MSS_MCF)
chip_ip = active_chip->chip_ips[idx];
for (inst_idx = 0U; inst_idx < chip_ip->num_instances;
inst_idx++) {
ip_inst = &chip_ip->ip_inst_static_array[
inst_idx];
/*
* Hence use max element mask to get correct
* fs info to use in HWPM driver.
*/
element_mask_max = tegra_hwpm_safe_sub_u32(
tegra_hwpm_safe_cast_u64_to_u32(BIT(
ip_inst->num_core_elements_per_inst)),
1U);
ip_inst->fuse_fs_mask =
(emc_element_floorsweep_mask &
element_mask_max);
tegra_hwpm_dbg(hwpm, hwpm_info,
"ip %d, fuse_mask 0x%x",
idx, ip_inst->fuse_fs_mask);
}
break;
#endif
default:
continue;
}
}
return 0;
}
int t234_hwpm_validate_current_config(struct tegra_soc_hwpm *hwpm) int t234_hwpm_validate_current_config(struct tegra_soc_hwpm *hwpm)
{ {
u32 production_mode = 0U; u32 production_mode = 0U;
@@ -215,6 +305,12 @@ int t234_hwpm_validate_current_config(struct tegra_soc_hwpm *hwpm)
return 0; return 0;
} }
err = t234_hwpm_validate_emc_config(hwpm);
if (err != 0) {
tegra_hwpm_err(hwpm, "failed to validate emc config");
return err;
}
/* Read production mode fuse */ /* Read production mode fuse */
err = tegra_hwpm_fuse_readl_prod_mode(hwpm, &production_mode); err = tegra_hwpm_fuse_readl_prod_mode(hwpm, &production_mode);
if (err != 0) { if (err != 0) {

View File

@@ -335,6 +335,12 @@ struct hwpm_ip_inst {
*/ */
struct tegra_hwpm_ip_ops ip_ops; struct tegra_hwpm_ip_ops ip_ops;
/*
* Some IPs set fuses to indicate floorsweeping info on platforms.
* This mask will contain fuse fs info if any.
*/
u32 fuse_fs_mask;
/* /*
* An IP contains perfmux-perfmon groups that correspond to each other. * An IP contains perfmux-perfmon groups that correspond to each other.
* If a perfmux is present, it indicates that the corresponding * If a perfmux is present, it indicates that the corresponding