NVIDIA: SAUCE: driver: cpufreq: put offline cpu cores to fmin

BugLink: https://bugs.launchpad.net/bugs/2072591

Offline CPU's in a cluster also participate in the final frequency
determination for that cluster as the max of currently requested
freq to all CPU's of the cluster is selected. So, an offline CPU
with stale high freq request can override the current low freq
request to the online CPU's. To fix this, set the frequency of
offline CPU to Fmin in hotplug off notifier so it can't override
the freq request to the online CPU's in the same cluster. Again
during system resume, MCE updates that Fmin for offline cores to
the default configured value as the MCE doesn't differentiate
between normal boot and system resume. This is resulting in high
power consumption. So, updating the freq of offline cores to Fmin
during system resume also.

Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
Reviewed-by: Bibek Basu <bbasu@nvidia.com>
Tested-by: Pohsun Su <pohsuns@nvidia.com>
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Sumit Gupta
2023-03-13 19:39:59 +05:30
committed by Noah Wager
parent 17420d8329
commit 6e23023359
+66
View File
@@ -69,6 +69,14 @@ struct tegra_cpufreq_soc {
unsigned int num_clusters;
phys_addr_t actmon_cntr_base;
u32 refclk_delta_min;
bool fmin_offline_cpus;
};
struct physical_ids {
u32 cpuid;
u32 clusterid;
u64 mpidr_id;
void __iomem *freq_core_reg;
};
struct tegra194_cpufreq_data {
@@ -77,9 +85,11 @@ struct tegra194_cpufreq_data {
const struct tegra_cpufreq_soc *soc;
bool icc_dram_bw_scaling;
struct tegra_cpu_data *cpu_data;
struct physical_ids *phys_ids;
};
static struct workqueue_struct *read_counters_wq;
static enum cpuhp_state hp_state;
static int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
{
@@ -194,6 +204,7 @@ static const struct tegra_cpufreq_soc tegra234_cpufreq_soc = {
.maxcpus_per_cluster = 4,
.num_clusters = 3,
.refclk_delta_min = 16000,
.fmin_offline_cpus = true,
};
static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
@@ -202,6 +213,7 @@ static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
.maxcpus_per_cluster = 8,
.num_clusters = 1,
.refclk_delta_min = 16000,
.fmin_offline_cpus = true,
};
static void tegra194_get_cpu_cluster_id(u32 cpu, u32 *cpuid, u32 *clusterid)
@@ -606,6 +618,7 @@ static const struct tegra_cpufreq_soc tegra194_cpufreq_soc = {
.maxcpus_per_cluster = 2,
.num_clusters = 4,
.refclk_delta_min = 16000,
.fmin_offline_cpus = false,
};
static void tegra194_cpufreq_free_resources(void)
@@ -685,6 +698,17 @@ tegra_cpufreq_bpmp_read_lut(struct platform_device *pdev, struct tegra_bpmp *bpm
return freq_table;
}
static int tegra234_cpufreq_offline(unsigned int cpu)
{
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
u32 clusterid = data->phys_ids[cpu].clusterid;
/* Set CPU to Fmin */
writel(data->bpmp_luts[clusterid]->driver_data, data->phys_ids[cpu].freq_core_reg);
return 0;
}
static int tegra194_cpufreq_store_physids(unsigned int cpu, struct tegra194_cpufreq_data *data)
{
int num_cpus = data->soc->maxcpus_per_cluster * data->soc->num_clusters;
@@ -793,6 +817,17 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
if (!err)
goto put_bpmp;
if (data->soc->fmin_offline_cpus) {
err = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"tegra234_cpufreq:online", NULL,
tegra234_cpufreq_offline);
if (err < 0) {
dev_info(&pdev->dev, "fail to register cpuhp state\n");
goto err_free_res;
}
hp_state = err;
}
err_free_res:
tegra194_cpufreq_free_resources();
put_bpmp:
@@ -804,6 +839,8 @@ static void tegra194_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&tegra194_cpufreq_driver);
tegra194_cpufreq_free_resources();
if (hp_state > 0)
cpuhp_remove_state_nocalls(hp_state);
}
static const struct of_device_id tegra194_cpufreq_of_match[] = {
@@ -814,10 +851,39 @@ static const struct of_device_id tegra194_cpufreq_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra194_cpufreq_of_match);
#ifdef CONFIG_PM_SLEEP
static int tegra194_cpufreq_resume(struct device *dev)
{
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
u32 cpu;
if (data->regs && data->soc->fmin_offline_cpus) {
/*
* If mmio registers are used for frequency requests and
* hp notifier is enabled to set offline cores to Fmin,
* then use the mmio register to keep offline cpu core to fmin.
* If sysreg are used then we can't set fmin as sysreg can
* be accessed from the target CPU only but that is offline.
*/
for_each_possible_cpu(cpu) {
if (!cpu_online(cpu))
tegra234_cpufreq_offline(cpu);
}
}
return 0;
}
#endif
static const struct dev_pm_ops tegra194_cpufreq_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, tegra194_cpufreq_resume)
};
static struct platform_driver tegra194_ccplex_driver = {
.driver = {
.name = "tegra194-cpufreq",
.of_match_table = tegra194_cpufreq_of_match,
.pm = &tegra194_cpufreq_pm_ops,
},
.probe = tegra194_cpufreq_probe,
.remove_new = tegra194_cpufreq_remove,