mbox series

[v2,0/5] Introduce LMh driver for Qualcomm SoCs

Message ID 20210624115813.3613290-1-thara.gopinath@linaro.org
Headers show
Series Introduce LMh driver for Qualcomm SoCs | expand

Message

Thara Gopinath June 24, 2021, 11:58 a.m. UTC
Limits Management Hardware(LMh) is a hardware infrastructure on some
Qualcomm SoCs that can enforce temperature and current limits as programmed
by software for certain IPs like CPU. On many newer SoCs LMh is configured
by firmware/TZ and no programming is needed from the kernel side. But on
certain SoCs like sdm845 the firmware does not do a complete programming of
the h/w block. On such SoCs kernel software has to explicitly set up the
temperature limits and turn on various monitoring and enforcing algorithms
on the hardware.

Introduce support for enabling and programming various limit settings and
monitoring capabilities of Limits Management Hardware(LMh) associated with
cpu clusters. Also introduce support in cpufreq hardware driver to monitor
the interrupt associated with cpu frequency throttling so that this
information can be conveyed to the schdeuler via thermal pressure
interface.

With this patch series following cpu performance improvement(30-70%) is
observed on sdm845. The reasoning here is that without LMh being programmed
properly from the kernel, the default settings were enabling thermal
mitigation for CPUs at too low a temperature (around 70-75 degree C).  This
in turn meant that many a time CPUs were never actually allowed to hit the
maximum possible/required frequencies.

UnixBench whets and dhry (./Run whets dhry)
System Benchmarks Index Score

                Without LMh Support             With LMh Support
1 copy test     1353.7                          1773.2

8 copy tests    4473.6                          7402.3

Sysbench cpu
sysbench cpu --threads=8 --time=60 --cpu-max-prime=100000 run

                Without LMh Support             With LMh Support
Events per
second                  355                             614

Avg Latency(ms)         21.84                           13.02

Thara Gopinath (5):
  firmware: qcom_scm: Introduce SCM calls to access LMh
  thermal: qcom: Add support for LMh driver
  cpufreq: qcom-cpufreq-hw: Add dcvs interrupt support
  arm64: boot: dts: qcom: sdm45: Add support for LMh node
  arm64: boot: dts: qcom: sdm845: Remove passive trip points for thermal
    zones 0-7

 arch/arm64/boot/dts/qcom/sdm845.dtsi | 162 +++--------------
 drivers/cpufreq/qcom-cpufreq-hw.c    | 103 +++++++++++
 drivers/firmware/qcom_scm.c          |  54 ++++++
 drivers/firmware/qcom_scm.h          |   4 +
 drivers/thermal/qcom/Kconfig         |  10 ++
 drivers/thermal/qcom/Makefile        |   1 +
 drivers/thermal/qcom/lmh.c           | 251 +++++++++++++++++++++++++++
 include/linux/qcom_scm.h             |  14 ++
 8 files changed, 463 insertions(+), 136 deletions(-)
 create mode 100644 drivers/thermal/qcom/lmh.c

Comments

Matthias Kaehlcke June 24, 2021, 4:51 p.m. UTC | #1
On Thu, Jun 24, 2021 at 07:58:13AM -0400, Thara Gopinath wrote:

> Subject: arm64: boot: dts: qcom: sdm845: Remove passive trip points for thermal zones 0-7

The patch doesn't remove the passive trip points (anymore?), but the cooling
maps/devices. Also talking about 'thermal zones 0-7' doesn't really convey
any useful information (and the enumeration could potentially change in the
future), better talk about the CPU thermal zones.

> Now that Limits h/w is enabled to monitor thermal events around cpus and
> throttle the cpu frequencies, remove cpufreq cooling device for the cpus which
> does software throttling of cpu frequencies.
Thara Gopinath June 25, 2021, 3:44 p.m. UTC | #2
On 6/24/21 12:51 PM, Matthias Kaehlcke wrote:
> On Thu, Jun 24, 2021 at 07:58:13AM -0400, Thara Gopinath wrote:
> 
>> Subject: arm64: boot: dts: qcom: sdm845: Remove passive trip points for thermal zones 0-7
> 
> The patch doesn't remove the passive trip points (anymore?), but the cooling
> maps/devices. Also talking about 'thermal zones 0-7' doesn't really convey
> any useful information (and the enumeration could potentially change in the
> future), better talk about the CPU thermal zones.

Hi Matthias,

Yes you are right. I forgot to change the subject. Will fix the subject 
and provide better description of the zones in the next rev

> 
>> Now that Limits h/w is enabled to monitor thermal events around cpus and
>> throttle the cpu frequencies, remove cpufreq cooling device for the cpus which
>> does software throttling of cpu frequencies.
Viresh Kumar June 29, 2021, 2:35 a.m. UTC | #3
On 24-06-21, 07:58, Thara Gopinath wrote:
> Add interrupt support to notify the kernel of h/w initiated frequency

> throttling by LMh. Convey this to scheduler via thermal presssure

> interface.

> 

> Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>

> ---

> 

> v1->v2:

> 	- Introduced qcom_cpufreq_hw_lmh_init to consolidate LMh related initializations

> 	  as per Viresh's review comment.

> 	- Moved the piece of code restarting polling/re-enabling LMh interrupt to

> 	  qcom_lmh_dcvs_notify therby simplifying isr and timer callback as per Viresh's

> 	  suggestion.

> 	- Droped cpus from qcom_cpufreq_data and instead using cpus from cpufreq_policy in

> 	  qcom_lmh_dcvs_notify as per Viresh's review comment.

> 	- Dropped dt property qcom,support-lmh as per Bjorn's suggestion.

> 	- Other minor/cosmetic fixes

> 

>  drivers/cpufreq/qcom-cpufreq-hw.c | 103 ++++++++++++++++++++++++++++++

>  1 file changed, 103 insertions(+)

> 

> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c

> index f86859bf76f1..241f6f2b441f 100644

> --- a/drivers/cpufreq/qcom-cpufreq-hw.c

> +++ b/drivers/cpufreq/qcom-cpufreq-hw.c

> @@ -13,6 +13,7 @@

>  #include <linux/of_platform.h>

>  #include <linux/pm_opp.h>

>  #include <linux/slab.h>

> +#include <linux/interrupt.h>


Please don't break the alphabetical order here.

>  #define LUT_MAX_ENTRIES			40U

>  #define LUT_SRC				GENMASK(31, 30)

> @@ -22,10 +23,13 @@

>  #define CLK_HW_DIV			2

>  #define LUT_TURBO_IND			1

>  

> +#define HZ_PER_KHZ			1000

>

>  struct qcom_cpufreq_soc_data {

>  	u32 reg_enable;

>  	u32 reg_freq_lut;

>  	u32 reg_volt_lut;

> +	u32 reg_current_vote;

>  	u32 reg_perf_state;

>  	u8 lut_row_size;

>  };

> @@ -33,7 +37,10 @@ struct qcom_cpufreq_soc_data {

>  struct qcom_cpufreq_data {

>  	void __iomem *base;

>  	struct resource *res;

> +	struct delayed_work lmh_dcvs_poll_work;

>  	const struct qcom_cpufreq_soc_data *soc_data;

> +	struct cpufreq_policy *policy;

> +	int lmh_dcvs_irq;

>  };

>  

>  static unsigned long cpu_hw_rate, xo_rate;

> @@ -251,10 +258,79 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)

>  	}

>  }

>  

> +static inline unsigned long qcom_lmh_vote_to_freq(u32 val)

> +{

> +	return (val & 0x3FF) * 19200;

> +}

> +

> +static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)

> +{

> +	struct cpufreq_policy *policy = data->policy;

> +	struct dev_pm_opp *opp;

> +	struct device *dev;

> +	unsigned long max_capacity, capacity, freq_hz, throttled_freq;

> +	unsigned int val, freq;

> +

> +	/*

> +	 * Get the h/w throttled frequency, normalize it using the

> +	 * registered opp table and use it to calculate thermal pressure.

> +	 */

> +	val = readl_relaxed(data->base + data->soc_data->reg_current_vote);

> +	freq = qcom_lmh_vote_to_freq(val);

> +	freq_hz = freq * HZ_PER_KHZ;

> +

> +	dev = get_cpu_device(cpumask_first(policy->cpus));

> +	opp = dev_pm_opp_find_freq_floor(dev, &freq_hz);

> +	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)

> +		opp = dev_pm_opp_find_freq_ceil(dev, &freq_hz);

> +

> +	throttled_freq = freq_hz / HZ_PER_KHZ;

> +

> +	/* Update thermal pressure */

> +	max_capacity = arch_scale_cpu_capacity(cpumask_first(policy->cpus));

> +	capacity = throttled_freq * max_capacity;

> +	capacity /= policy->cpuinfo.max_freq;

> +	/* Don't pass boost capacity to scheduler */

> +	if (capacity > max_capacity)

> +		capacity = max_capacity;


I wonder why this check isn't present for cpufreq_cooling.c .

> +	arch_set_thermal_pressure(policy->cpus, max_capacity - capacity);

> +	/*


Whenever you mix code and comments, please separate them with a blank
line, else it becomes a bit messy and harder to read.

> +	 * If h/w throttled frequency is higher than what cpufreq has requested for, stop

> +	 * polling and switch back to interrupt mechanism

> +	 */

> +	if (throttled_freq >= qcom_cpufreq_hw_get(cpumask_first(policy->cpus)))

> +		/* Clear the existing interrupts and enable it back */

> +		enable_irq(data->lmh_dcvs_irq);

> +	else

> +		mod_delayed_work(system_highpri_wq, &data->lmh_dcvs_poll_work,

> +				 msecs_to_jiffies(10));

> +}

> +

> +static void qcom_lmh_dcvs_poll(struct work_struct *work)

> +{

> +	struct qcom_cpufreq_data *data;

> +

> +	data = container_of(work, struct qcom_cpufreq_data, lmh_dcvs_poll_work.work);

> +

> +	qcom_lmh_dcvs_notify(data);

> +}

> +

> +static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data)

> +{

> +	struct qcom_cpufreq_data *c_data = data;

> +

> +	/* Disable interrupt and enable polling */

> +	disable_irq_nosync(c_data->lmh_dcvs_irq);

> +	qcom_lmh_dcvs_notify(c_data);

> +

> +	return 0;

> +}

> +

>  static const struct qcom_cpufreq_soc_data qcom_soc_data = {

>  	.reg_enable = 0x0,

>  	.reg_freq_lut = 0x110,

>  	.reg_volt_lut = 0x114,

> +	.reg_current_vote = 0x704,

>  	.reg_perf_state = 0x920,

>  	.lut_row_size = 32,

>  };

> @@ -274,6 +350,23 @@ static const struct of_device_id qcom_cpufreq_hw_match[] = {

>  };

>  MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);

>  

> +static void qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy)

> +{

> +	struct qcom_cpufreq_data *data = policy->driver_data;

> +	struct platform_device *pdev = cpufreq_get_driver_data();

> +	struct device *dev = &pdev->dev;

> +	int ret;

> +

> +	ret = devm_request_irq(dev, data->lmh_dcvs_irq, qcom_lmh_dcvs_handle_irq,

> +			       0, "dcvsh-irq", data);

> +	if (ret) {

> +		dev_err(dev, "Error %d registering irq %x\n", ret, data->lmh_dcvs_irq);

> +		return;

> +	}

> +	data->policy = policy;

> +	INIT_DEFERRABLE_WORK(&data->lmh_dcvs_poll_work, qcom_lmh_dcvs_poll);

> +}

> +

>  static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)

>  {

>  	struct platform_device *pdev = cpufreq_get_driver_data();

> @@ -370,6 +463,16 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)

>  			dev_warn(cpu_dev, "failed to enable boost: %d\n", ret);

>  	}

>  

> +	/* Look for LMh interrupt. If no interrupt line is specified /

> +	 * if there is an error, allow cpufreq to be enabled as usual.

> +	 */


Proper comment style please..

> +	data->lmh_dcvs_irq = platform_get_irq(pdev, index);

> +	if (data->lmh_dcvs_irq > 0) {

> +		qcom_cpufreq_hw_lmh_init(policy);

> +	} else if (data->lmh_dcvs_irq != -ENXIO) {

> +		ret = data->lmh_dcvs_irq;

> +		goto error;

> +	}


Move all of this to qcom_cpufreq_hw_lmh_init().

And I don't see any cleanup for this stuff. There is no guarantee that
the irq won't fire and queue up a work right after cpufreq driver is
unregistered and before the devm_ stuff gets released.

>  	return 0;

>  error:

>  	kfree(data);

> -- 

> 2.25.1


-- 
viresh
Taniya Das June 29, 2021, 2:50 a.m. UTC | #4
On 6/24/2021 5:28 PM, Thara Gopinath wrote:
> Add interrupt support to notify the kernel of h/w initiated frequency

> throttling by LMh. Convey this to scheduler via thermal presssure

> interface.

> 

> Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>

> ---

> 

> v1->v2:

> 	- Introduced qcom_cpufreq_hw_lmh_init to consolidate LMh related initializations

> 	  as per Viresh's review comment.

> 	- Moved the piece of code restarting polling/re-enabling LMh interrupt to

> 	  qcom_lmh_dcvs_notify therby simplifying isr and timer callback as per Viresh's

> 	  suggestion.

> 	- Droped cpus from qcom_cpufreq_data and instead using cpus from cpufreq_policy in

> 	  qcom_lmh_dcvs_notify as per Viresh's review comment.

> 	- Dropped dt property qcom,support-lmh as per Bjorn's suggestion.

> 	- Other minor/cosmetic fixes

> 

>   drivers/cpufreq/qcom-cpufreq-hw.c | 103 ++++++++++++++++++++++++++++++

>   1 file changed, 103 insertions(+)

> 

> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c

> index f86859bf76f1..241f6f2b441f 100644

> --- a/drivers/cpufreq/qcom-cpufreq-hw.c

> +++ b/drivers/cpufreq/qcom-cpufreq-hw.c

> @@ -13,6 +13,7 @@

>   #include <linux/of_platform.h>

>   #include <linux/pm_opp.h>

>   #include <linux/slab.h>

> +#include <linux/interrupt.h>

>   

>   #define LUT_MAX_ENTRIES			40U

>   #define LUT_SRC				GENMASK(31, 30)

> @@ -22,10 +23,13 @@

>   #define CLK_HW_DIV			2

>   #define LUT_TURBO_IND			1

>   

> +#define HZ_PER_KHZ			1000

> +

>   struct qcom_cpufreq_soc_data {

>   	u32 reg_enable;

>   	u32 reg_freq_lut;

>   	u32 reg_volt_lut;

> +	u32 reg_current_vote;

>   	u32 reg_perf_state;

>   	u8 lut_row_size;

>   };

> @@ -33,7 +37,10 @@ struct qcom_cpufreq_soc_data {

>   struct qcom_cpufreq_data {

>   	void __iomem *base;

>   	struct resource *res;

> +	struct delayed_work lmh_dcvs_poll_work;

>   	const struct qcom_cpufreq_soc_data *soc_data;

> +	struct cpufreq_policy *policy;

> +	int lmh_dcvs_irq;

>   };

>   

>   static unsigned long cpu_hw_rate, xo_rate;

> @@ -251,10 +258,79 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)

>   	}

>   }

>   

> +static inline unsigned long qcom_lmh_vote_to_freq(u32 val)

> +{

> +	return (val & 0x3FF) * 19200;

> +}

> +

> +static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)

> +{

> +	struct cpufreq_policy *policy = data->policy;

> +	struct dev_pm_opp *opp;

> +	struct device *dev;

> +	unsigned long max_capacity, capacity, freq_hz, throttled_freq;

> +	unsigned int val, freq;

> +

> +	/*

> +	 * Get the h/w throttled frequency, normalize it using the

> +	 * registered opp table and use it to calculate thermal pressure.

> +	 */

> +	val = readl_relaxed(data->base + data->soc_data->reg_current_vote);

> +	freq = qcom_lmh_vote_to_freq(val);

> +	freq_hz = freq * HZ_PER_KHZ;

> +

> +	dev = get_cpu_device(cpumask_first(policy->cpus));

> +	opp = dev_pm_opp_find_freq_floor(dev, &freq_hz);

> +	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)

> +		opp = dev_pm_opp_find_freq_ceil(dev, &freq_hz);

> +

> +	throttled_freq = freq_hz / HZ_PER_KHZ;

> +

> +	/* Update thermal pressure */

> +	max_capacity = arch_scale_cpu_capacity(cpumask_first(policy->cpus));

> +	capacity = throttled_freq * max_capacity;

> +	capacity /= policy->cpuinfo.max_freq;

> +	/* Don't pass boost capacity to scheduler */

> +	if (capacity > max_capacity)

> +		capacity = max_capacity;

> +	arch_set_thermal_pressure(policy->cpus, max_capacity - capacity);

> +	/*

> +	 * If h/w throttled frequency is higher than what cpufreq has requested for, stop

> +	 * polling and switch back to interrupt mechanism

> +	 */

> +	if (throttled_freq >= qcom_cpufreq_hw_get(cpumask_first(policy->cpus)))

> +		/* Clear the existing interrupts and enable it back */

> +		enable_irq(data->lmh_dcvs_irq);

> +	else

> +		mod_delayed_work(system_highpri_wq, &data->lmh_dcvs_poll_work,

> +				 msecs_to_jiffies(10));

> +}

> +

> +static void qcom_lmh_dcvs_poll(struct work_struct *work)

> +{

> +	struct qcom_cpufreq_data *data;

> +

> +	data = container_of(work, struct qcom_cpufreq_data, lmh_dcvs_poll_work.work);

> +

> +	qcom_lmh_dcvs_notify(data);

> +}

> +

> +static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data)

> +{

> +	struct qcom_cpufreq_data *c_data = data;

> +

> +	/* Disable interrupt and enable polling */

> +	disable_irq_nosync(c_data->lmh_dcvs_irq);

> +	qcom_lmh_dcvs_notify(c_data);

> +

> +	return 0;

> +}

> +

>   static const struct qcom_cpufreq_soc_data qcom_soc_data = {

>   	.reg_enable = 0x0,

>   	.reg_freq_lut = 0x110,

>   	.reg_volt_lut = 0x114,

> +	.reg_current_vote = 0x704,

>   	.reg_perf_state = 0x920,

>   	.lut_row_size = 32,

>   };

> @@ -274,6 +350,23 @@ static const struct of_device_id qcom_cpufreq_hw_match[] = {

>   };

>   MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);

>   

> +static void qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy)

> +{

> +	struct qcom_cpufreq_data *data = policy->driver_data;

> +	struct platform_device *pdev = cpufreq_get_driver_data();

> +	struct device *dev = &pdev->dev;

> +	int ret;

> +

> +	ret = devm_request_irq(dev, data->lmh_dcvs_irq, qcom_lmh_dcvs_handle_irq,

> +			       0, "dcvsh-irq", data);



It is better if you tag the CPU id while registering the IRQ.
"dcvsh-irq-x" (0/4/7)

> +	if (ret) {

> +		dev_err(dev, "Error %d registering irq %x\n", ret, data->lmh_dcvs_irq);

> +		return;

> +	}

> +	data->policy = policy;

> +	INIT_DEFERRABLE_WORK(&data->lmh_dcvs_poll_work, qcom_lmh_dcvs_poll);

> +}

> +

>   static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)

>   {

>   	struct platform_device *pdev = cpufreq_get_driver_data();

> @@ -370,6 +463,16 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)

>   			dev_warn(cpu_dev, "failed to enable boost: %d\n", ret);

>   	}

>   

> +	/* Look for LMh interrupt. If no interrupt line is specified /

> +	 * if there is an error, allow cpufreq to be enabled as usual.

> +	 */

> +	data->lmh_dcvs_irq = platform_get_irq(pdev, index);

> +	if (data->lmh_dcvs_irq > 0) {

> +		qcom_cpufreq_hw_lmh_init(policy);

> +	} else if (data->lmh_dcvs_irq != -ENXIO) {

> +		ret = data->lmh_dcvs_irq;

> +		goto error;

> +	}

>   	return 0;

>   error:

>   	kfree(data);

> 


-- 
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member
of Code Aurora Forum, hosted by The Linux Foundation.

--
Thara Gopinath June 30, 2021, 2:25 a.m. UTC | #5
On 6/28/21 10:35 PM, Viresh Kumar wrote:
> On 24-06-21, 07:58, Thara Gopinath wrote:
>> Add interrupt support to notify the kernel of h/w initiated frequency
>> throttling by LMh. Convey this to scheduler via thermal presssure
>> interface.
>>
>> Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>
>> ---
>>
>> v1->v2:
>> 	- Introduced qcom_cpufreq_hw_lmh_init to consolidate LMh related initializations
>> 	  as per Viresh's review comment.
>> 	- Moved the piece of code restarting polling/re-enabling LMh interrupt to
>> 	  qcom_lmh_dcvs_notify therby simplifying isr and timer callback as per Viresh's
>> 	  suggestion.
>> 	- Droped cpus from qcom_cpufreq_data and instead using cpus from cpufreq_policy in
>> 	  qcom_lmh_dcvs_notify as per Viresh's review comment.
>> 	- Dropped dt property qcom,support-lmh as per Bjorn's suggestion.
>> 	- Other minor/cosmetic fixes
>>
>>   drivers/cpufreq/qcom-cpufreq-hw.c | 103 ++++++++++++++++++++++++++++++
>>   1 file changed, 103 insertions(+)
>>
>> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c
>> index f86859bf76f1..241f6f2b441f 100644
>> --- a/drivers/cpufreq/qcom-cpufreq-hw.c
>> +++ b/drivers/cpufreq/qcom-cpufreq-hw.c
>> @@ -13,6 +13,7 @@
>>   #include <linux/of_platform.h>
>>   #include <linux/pm_opp.h>
>>   #include <linux/slab.h>
>> +#include <linux/interrupt.h>
> 
> Please don't break the alphabetical order here.
> 
>>   #define LUT_MAX_ENTRIES			40U
>>   #define LUT_SRC				GENMASK(31, 30)
>> @@ -22,10 +23,13 @@
>>   #define CLK_HW_DIV			2
>>   #define LUT_TURBO_IND			1
>>   
>> +#define HZ_PER_KHZ			1000
>>
>>   struct qcom_cpufreq_soc_data {
>>   	u32 reg_enable;
>>   	u32 reg_freq_lut;
>>   	u32 reg_volt_lut;
>> +	u32 reg_current_vote;
>>   	u32 reg_perf_state;
>>   	u8 lut_row_size;
>>   };
>> @@ -33,7 +37,10 @@ struct qcom_cpufreq_soc_data {
>>   struct qcom_cpufreq_data {
>>   	void __iomem *base;
>>   	struct resource *res;
>> +	struct delayed_work lmh_dcvs_poll_work;
>>   	const struct qcom_cpufreq_soc_data *soc_data;
>> +	struct cpufreq_policy *policy;
>> +	int lmh_dcvs_irq;
>>   };
>>   
>>   static unsigned long cpu_hw_rate, xo_rate;
>> @@ -251,10 +258,79 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
>>   	}
>>   }
>>   
>> +static inline unsigned long qcom_lmh_vote_to_freq(u32 val)
>> +{
>> +	return (val & 0x3FF) * 19200;
>> +}
>> +
>> +static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)
>> +{
>> +	struct cpufreq_policy *policy = data->policy;
>> +	struct dev_pm_opp *opp;
>> +	struct device *dev;
>> +	unsigned long max_capacity, capacity, freq_hz, throttled_freq;
>> +	unsigned int val, freq;
>> +
>> +	/*
>> +	 * Get the h/w throttled frequency, normalize it using the
>> +	 * registered opp table and use it to calculate thermal pressure.
>> +	 */
>> +	val = readl_relaxed(data->base + data->soc_data->reg_current_vote);
>> +	freq = qcom_lmh_vote_to_freq(val);
>> +	freq_hz = freq * HZ_PER_KHZ;
>> +
>> +	dev = get_cpu_device(cpumask_first(policy->cpus));
>> +	opp = dev_pm_opp_find_freq_floor(dev, &freq_hz);
>> +	if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
>> +		opp = dev_pm_opp_find_freq_ceil(dev, &freq_hz);
>> +
>> +	throttled_freq = freq_hz / HZ_PER_KHZ;
>> +
>> +	/* Update thermal pressure */
>> +	max_capacity = arch_scale_cpu_capacity(cpumask_first(policy->cpus));
>> +	capacity = throttled_freq * max_capacity;
>> +	capacity /= policy->cpuinfo.max_freq;
>> +	/* Don't pass boost capacity to scheduler */
>> +	if (capacity > max_capacity)
>> +		capacity = max_capacity;
> 
> I wonder why this check isn't present for cpufreq_cooling.c .

Hi Viresh,

I don't think cpufreq_cooling recognizes boost frequencies. The max 
state there is the max of nominal frequencies , right? If not, it might 
be a good idea to add this check there as well.

I will fix rest of your comments in v3.
Thara Gopinath June 30, 2021, 2:27 a.m. UTC | #6
On 6/28/21 10:50 PM, Taniya Das wrote:
> 

> 

> On 6/24/2021 5:28 PM, Thara Gopinath wrote:

>> Add interrupt support to notify the kernel of h/w initiated frequency

>> throttling by LMh. Convey this to scheduler via thermal presssure

>> interface.

>>

>> Signed-off-by: Thara Gopinath <thara.gopinath@linaro.org>

>> ---

>>

>> v1->v2:

>>     - Introduced qcom_cpufreq_hw_lmh_init to consolidate LMh related 

>> initializations

>>       as per Viresh's review comment.

>>     - Moved the piece of code restarting polling/re-enabling LMh 

>> interrupt to

>>       qcom_lmh_dcvs_notify therby simplifying isr and timer callback 

>> as per Viresh's

>>       suggestion.

>>     - Droped cpus from qcom_cpufreq_data and instead using cpus from 

>> cpufreq_policy in

>>       qcom_lmh_dcvs_notify as per Viresh's review comment.

>>     - Dropped dt property qcom,support-lmh as per Bjorn's suggestion.

>>     - Other minor/cosmetic fixes

>>

>>   drivers/cpufreq/qcom-cpufreq-hw.c | 103 ++++++++++++++++++++++++++++++

>>   1 file changed, 103 insertions(+)

>>

>> diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c 

>> b/drivers/cpufreq/qcom-cpufreq-hw.c

>> index f86859bf76f1..241f6f2b441f 100644

>> --- a/drivers/cpufreq/qcom-cpufreq-hw.c

>> +++ b/drivers/cpufreq/qcom-cpufreq-hw.c


[snip]

>>   static const struct qcom_cpufreq_soc_data qcom_soc_data = {

>>       .reg_enable = 0x0,

>>       .reg_freq_lut = 0x110,

>>       .reg_volt_lut = 0x114,

>> +    .reg_current_vote = 0x704,

>>       .reg_perf_state = 0x920,

>>       .lut_row_size = 32,

>>   };

>> @@ -274,6 +350,23 @@ static const struct of_device_id 

>> qcom_cpufreq_hw_match[] = {

>>   };

>>   MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);

>> +static void qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy)

>> +{

>> +    struct qcom_cpufreq_data *data = policy->driver_data;

>> +    struct platform_device *pdev = cpufreq_get_driver_data();

>> +    struct device *dev = &pdev->dev;

>> +    int ret;

>> +

>> +    ret = devm_request_irq(dev, data->lmh_dcvs_irq, 

>> qcom_lmh_dcvs_handle_irq,

>> +                   0, "dcvsh-irq", data);

> 

> 

> It is better if you tag the CPU id while registering the IRQ.

> "dcvsh-irq-x" (0/4/7)


Sure. Will fix it.

> 

>> +    if (ret) {

>> +        dev_err(dev, "Error %d registering irq %x\n", ret, 

>> data->lmh_dcvs_irq);

>> +        return;

>> +    }

>> +    data->policy = policy;

>> +    INIT_DEFERRABLE_WORK(&data->lmh_dcvs_poll_work, qcom_lmh_dcvs_poll);

>> +}

>> +

>>   static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)

>>   {

>>       struct platform_device *pdev = cpufreq_get_driver_data();

>> @@ -370,6 +463,16 @@ static int qcom_cpufreq_hw_cpu_init(struct 

>> cpufreq_policy *policy)

>>               dev_warn(cpu_dev, "failed to enable boost: %d\n", ret);

>>       }

>> +    /* Look for LMh interrupt. If no interrupt line is specified /

>> +     * if there is an error, allow cpufreq to be enabled as usual.

>> +     */

>> +    data->lmh_dcvs_irq = platform_get_irq(pdev, index);

>> +    if (data->lmh_dcvs_irq > 0) {

>> +        qcom_cpufreq_hw_lmh_init(policy);

>> +    } else if (data->lmh_dcvs_irq != -ENXIO) {

>> +        ret = data->lmh_dcvs_irq;

>> +        goto error;

>> +    }

>>       return 0;

>>   error:

>>       kfree(data);

>>

> 


-- 
Warm Regards
Thara (She/Her/Hers)
Viresh Kumar June 30, 2021, 3:53 a.m. UTC | #7
On 29-06-21, 22:25, Thara Gopinath wrote:
> I don't think cpufreq_cooling recognizes boost frequencies. The max state
> there is the max of nominal frequencies , right? If not, it might be a good
> idea to add this check there as well.

Ahh, that explains it.