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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user