driver: cpufreq: use refclk delta based loop instead of udelay
Subsystems (2)
PATCH 1/1 driver: cpufreq: use refclk delta based loop instead of udelay
Use reference clock count based loop instead of "udelay()" for sampling of counters to improve the accuracy of re-generated CPU frequency. "udelay()" internally calls "WFE" which stops the counters and results in bigger delta between the last set freq and the re-generated value from counters. The counter sampling window used in loop is the minimum number of reference clock cycles which is known to give a stable value of CPU frequency. The change also helps to reduce the sampling window from "500us" to "<50us".
--- drivers/cpufreq/tegra194-cpufreq.c | 72 +++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 17 deletions(-)
drivers/cpufreq/tegra194-cpufreq.c +55 -17
@@ -5,7 +5,6 @@#include <linux/cpu.h> #include <linux/cpufreq.h> -#include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of.h>
@@ -21,10 +20,11 @@#define KHZ 1000#define REF_CLK_MHZ 408/* 408 MHz */ -#define US_DELAY 500#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)#define MAX_CNT ~0U +#define MAX_DELTA_KHZ 115200 +#define NDIV_MASK 0x1FF#define CORE_OFFSET (cpu) (cpu * 8)
@@ -62,6 +62,7 @@ struct tegra_cpufreq_soc {int maxcpus_per_cluster;unsigned int num_clusters;phys_addr_t actmon_cntr_base; +u32 refclk_delta_min; };struct tegra194_cpufreq_data {
@@ -158,6 +159,8 @@ static void tegra234_read_counters(struct tegra_cpu_ctr *c)struct tegra194_cpufreq_data *data =cpufreq_get_driver_data ();void __iomem *actmon_reg;u32 cpuid, clusterid; +u32 delta_refcnt; +int cnt =0 ;u64 val; data->soc->ops->get_cpu_cluster_id (c->cpu, &cpuid, &clusterid);
@@ -166,10 +169,25 @@ static void tegra234_read_counters(struct tegra_cpu_ctr *c) val =readq (actmon_reg); c->last_refclk_cnt =upper_32_bits (val); c->last_coreclk_cnt =lower_32_bits (val); -udelay (US_DELAY ); - val =readq (actmon_reg); - c->refclk_cnt =upper_32_bits (val); - c->coreclk_cnt =lower_32_bits (val); + +/* +* The sampling window is based on the minimum number of reference +* clock cycles which is known to give a stable value of CPU frequency. +*/ +do { + val =readq (actmon_reg); + c->refclk_cnt =upper_32_bits (val); + c->coreclk_cnt =lower_32_bits (val); +if (c->refclk_cnt < c->last_refclk_cnt) + delta_refcnt = c->refclk_cnt + (MAX_CNT - c->last_refclk_cnt); +else + delta_refcnt = c->refclk_cnt - c->last_refclk_cnt; +if (++cnt >=0xFFFF ) { +pr_warn ("cpufreq: problem with refclk on cpu:%d, delta_refcnt:%u, cnt:%d\n" , + c->cpu, delta_refcnt, cnt); +break ; + } + }while (delta_refcnt < data->soc->refclk_delta_min); }static struct tegra_cpufreq_ops tegra234_cpufreq_ops = {
@@ -184,6 +202,7 @@ static const struct tegra_cpufreq_soc tegra234_cpufreq_soc = { .actmon_cntr_base = 0x9000, .maxcpus_per_cluster = 4, .num_clusters = 3, + .refclk_delta_min = 16000, };static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = {
@@ -191,6 +210,7 @@ static const struct tegra_cpufreq_soc tegra239_cpufreq_soc = { .actmon_cntr_base = 0x4000, .maxcpus_per_cluster = 8, .num_clusters = 1, + .refclk_delta_min = 16000, };static void tegra194_get_cpu_cluster_id (u32 cpu,u32 *cpuid,u32 *clusterid)
@@ -231,15 +251,33 @@ static inline u32 map_ndiv_to_freq(struct mrq_cpu_ndiv_limits_responsestatic void tegra194_read_counters (struct tegra_cpu_ctr *c) { +struct tegra194_cpufreq_data *data =cpufreq_get_driver_data (); +u32 delta_refcnt; +int cnt =0 ;u64 val; val =read_freq_feedback (); c->last_refclk_cnt =lower_32_bits (val); c->last_coreclk_cnt =upper_32_bits (val); -udelay (US_DELAY ); - val =read_freq_feedback (); - c->refclk_cnt =lower_32_bits (val); - c->coreclk_cnt =upper_32_bits (val); + +/* +* The sampling window is based on the minimum number of reference +* clock cycles which is known to give a stable value of CPU frequency. +*/ +do { + val =read_freq_feedback (); + c->refclk_cnt =lower_32_bits (val); + c->coreclk_cnt =upper_32_bits (val); +if (c->refclk_cnt < c->last_refclk_cnt) + delta_refcnt = c->refclk_cnt + (MAX_CNT - c->last_refclk_cnt); +else + delta_refcnt = c->refclk_cnt - c->last_refclk_cnt; +if (++cnt >=0xFFFF ) { +pr_warn ("cpufreq: problem with refclk on cpu:%d, delta_refcnt:%u, cnt:%d\n" , + c->cpu, delta_refcnt, cnt); +break ; + } + }while (delta_refcnt < data->soc->refclk_delta_min); }static void tegra_read_counters (struct work_struct *work)
@@ -297,9 +335,8 @@ static unsigned int tegra194_calculate_speed(u32 cpu)u32 rate_mhz;/* -* udelay() is required to reconstruct cpu frequency over an -* observation window. Using workqueue to call udelay() with -* interrupts enabled. +* Reconstruct cpu frequency over an observation/sampling window. +* Using workqueue to keep interrupts enabled during the interval. */ read_counters_work.c.cpu = cpu;INIT_WORK_ONSTACK (&read_counters_work.work, tegra_read_counters);
@@ -383,9 +420,9 @@ static unsigned int tegra194_get_speed(u32 cpu)if (pos->driver_data != ndiv)continue ; -if (abs (pos->frequency - rate) >115200 ) { -pr_warn ("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n" , - cpu, rate, pos->frequency, ndiv); +if (abs (pos->frequency - rate) >MAX_DELTA_KHZ ) { +pr_warn ("cpufreq: cpu%d,cur:%u,set:%u,delta:%d,set ndiv:%llu\n" , + cpu, rate, pos->frequency,abs (rate - pos->frequency), ndiv); }else { rate = pos->frequency; }
@@ -580,6 +617,7 @@ static const struct tegra_cpufreq_soc tegra194_cpufreq_soc = { .ops = &tegra194_cpufreq_ops, .maxcpus_per_cluster = 2, .num_clusters = 4, + .refclk_delta_min = 16000, };static void tegra194_cpufreq_free_resources (void )
@@ -673,7 +711,7 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev) soc =of_device_get_match_data (&pdev->dev); -if (soc->ops && soc->maxcpus_per_cluster && soc->num_clusters) { +if (soc->ops && soc->maxcpus_per_cluster && soc->num_clusters && soc->refclk_delta_min) { data->soc = soc; }else {dev_err (&pdev->dev,"soc data missing\n" );
Signature · 1 line
2.17.1
Jon Hunter
maintainerQuoted 10 lines · Sumit Gupta
Use reference clock count based loop instead of "
udelay()" for sampling of counters to improve the accuracy of re-generated CPU frequency. "udelay()" internally calls "WFE" which stops the counters and results in bigger delta between the last set freq and the re-generated value from counters. The counter sampling window used in loop is the minimum number of reference clock cycles which is known to give a stable value of CPU frequency. The change also helps to reduce the sampling window from "500us" to "<50us".
Quoted 2 lines
Suggested-by Antti Miettinen <[email protected]>Signed-off-by Sumit Gupta <[email protected]>
Please can you correct the subject and drop the 'driver:'.
Jon
Signature · 1 line
nvpublic
Quoted 13 lines · Jon Hunter
Quoted 10 lines · Sumit Gupta
Use reference clock count based loop instead of "
udelay()" for sampling of counters to improve the accuracy of re-generated CPU frequency. "udelay()" internally calls "WFE" which stops the counters and results in bigger delta between the last set freq and the re-generated value from counters. The counter sampling window used in loop is the minimum number of reference clock cycles which is known to give a stable value of CPU frequency. The change also helps to reduce the sampling window from "500us" to "<50us".
Quoted 8 lines
Quoted 2 lines
Suggested-by Antti Miettinen <[email protected]>Signed-off-by Sumit Gupta <[email protected]>Please can you correct the subject and drop the 'driver:'.
Jon
Corrected subject and added another patch in v2 at [1].
[1] https://lore.kernel.org/lkml/[email protected]/
Thank you,
Sumit Gupta