The CPUfreq HW present in some Mediatek chipsets offloads the steps necessary for changing the frequency of CPUs.
The driver implements the cpufreq driver interface for this hardware engine.
From v14 to v15, there are three modifications.
1. Move platform data from per-policy to probe
2. Update energy model register to callback function
From v13 to v14, there are three modifications.
1. Replace cpu domain map to policy driver data
2. Remove dummy performance-domain parsing
3. Separate modification in cpufreq.h to another patch
From v12 to v13, there are two modifications.
1. Move related_cpus function to common place, so all performance-domain cpufreq driver can refer.
2. Make cpu resource init to each policy rather than per-cpu
From v11 to v12, there are two modifications.
1. Based on patchset[1], align binding with scmi for performance domain(latest version).
2. Shrink binding example wording.
From v8 to v9, there are three more modifications.
1. Based on patchset[2], align binding with scmi for performance domain.
2. Add the CPUFREQ fast switch function support and define DVFS latency.
3. Based on patchser[3], add energy model API parameter for mW.
From v7 to v8, there are three more patches based on patchset v8[4].
This patchset is about to register power table to Energy model for EAS and thermal usage.
1. EM CPU power table
- Register energy model table for EAS and thermal cooling device usage.
- Read the coresponding LUT for power table.
2. SVS initialization
- The SVS(Smart Voltage Scaling) engine is a hardware which is
used to calculate optimized voltage values for CPU power domain.
DVFS driver could apply those optimized voltage values to reduce power consumption.
- Driver will polling if HW engine is done for SVS initialization.
After that, driver will read power table and register it to EAS.
- CPUs must be in power on state when doing SVS. Use pm_qos to block cpu-idle state for SVS initializing.
3. Cooling device flag
- Add cooling device flag for thermal
[1] https://lore.kernel.org/linux-devicetree/20210517155458.1016707-1-sudeep.holla@arm.com/
[2] https://lore.kernel.org/lkml/20201116181356.804590-1-sudeep.holla@arm.com/
[3] https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git/commit/?h=linux-next&id=c250d50fe2ce627ca9805d9c8ac11cbbf922a4a6
[4] https://lkml.org/lkml/2020/9/23/384
Hector.Yuan (3):
dt-bindings: cpufreq: add bindings for MediaTek cpufreq HW
cpufreq: Add of_perf_domain_get_sharing_cpumask
cpufreq: mediatek-hw: Add support for CPUFREQ HW
.../bindings/cpufreq/cpufreq-mediatek-hw.yaml | 70 ++++
drivers/cpufreq/Kconfig.arm | 12 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/mediatek-cpufreq-hw.c | 340 ++++++++++++++++++
include/linux/cpufreq.h | 46 ++-
5 files changed, 468 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml
create mode 100644 drivers/cpufreq/mediatek-cpufreq-hw.c
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
@@ -0,0 +1,70 @@+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)+%YAML1.2+---+$id:http://devicetree.org/schemas/cpufreq/cpufreq-mediatek-hw.yaml#+$schema:http://devicetree.org/meta-schemas/core.yaml#++title:MediaTek's CPUFREQ Bindings++maintainers:+-Hector Yuan <hector.yuan@mediatek.com>++description:+CPUFREQ HW is a hardware engine used by MediaTek SoCs to+manage frequency in hardware. It is capable of controlling+frequency for multiple clusters.++properties:+compatible:+const:mediatek,cpufreq-hw++reg:+minItems:1+maxItems:2+description:+Addresses and sizes for the memory of the HW bases in+each frequency domain. Each entry corresponds to+a register bank for each frequency domain present.++"#performance-domain-cells":+description:+Number of cells in a performance domain specifier.+Set const to 1 here for nodes providing multiple+performance domains.+const:1++required:+-compatible+-reg+-"#performance-domain-cells"++additionalProperties:false++examples:+-|+cpus {+#address-cells = <1>;+#size-cells = <0>;++cpu0:cpu@0 {+device_type = "cpu";+compatible = "arm,cortex-a55";+enable-method = "psci";+performance-domains = <&performance 0>;+reg = <0x000>;+};+};++/* ... */++soc {+#address-cells = <2>;+#size-cells = <2>;++performance:performance-controller@11bc00 {+compatible = "mediatek,cpufreq-hw";+reg = <0 0x0011bc10 0 0x120>, <0 0x0011bd30 0 0x120>;++#performance-domain-cells = <1>;+};+};
--
1.7.9.5
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: "Hector.Yuan" <redacted>
Add of_perf_domain_get_sharing_cpumask function to group cpu
to specific performance domain.
Signed-off-by: Hector.Yuan <redacted>
---
include/linux/cpufreq.h | 46 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
@@ -0,0 +1,340 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright(c)2020MediaTekInc.+*/++#include<linux/bitfield.h>+#include<linux/cpufreq.h>+#include<linux/energy_model.h>+#include<linux/init.h>+#include<linux/iopoll.h>+#include<linux/kernel.h>+#include<linux/module.h>+#include<linux/of_address.h>+#include<linux/of_platform.h>+#include<linux/slab.h>++#define LUT_MAX_ENTRIES 32U+#define LUT_FREQ GENMASK(11, 0)+#define LUT_ROW_SIZE 0x4+#define CPUFREQ_HW_STATUS BIT(0)+#define SVS_HW_STATUS BIT(1)+#define POLL_USEC 1000+#define TIMEOUT_USEC 300000++enum{+REG_FREQ_LUT_TABLE,+REG_FREQ_ENABLE,+REG_FREQ_PERF_STATE,+REG_FREQ_HW_STATE,+REG_EM_POWER_TBL,+REG_FREQ_LATENCY,++REG_ARRAY_SIZE,+};++structmtk_cpufreq_drv;++structmtk_cpufreq_drv{+constu16*offsets;+};++structmtk_cpufreq_data{+structcpufreq_frequency_table*table;+void__iomem*reg_bases[REG_ARRAY_SIZE];+intnr_opp;+};++staticconstu16cpufreq_mtk_offsets[REG_ARRAY_SIZE]={+[REG_FREQ_LUT_TABLE]=0x0,+[REG_FREQ_ENABLE]=0x84,+[REG_FREQ_PERF_STATE]=0x88,+[REG_FREQ_HW_STATE]=0x8c,+[REG_EM_POWER_TBL]=0x90,+[REG_FREQ_LATENCY]=0x110,+};++staticint__maybe_unused+mtk_cpufreq_get_cpu_power(unsignedlong*mW,+unsignedlong*KHz,structdevice*cpu_dev)+{+structmtk_cpufreq_data*data;+structcpufreq_policy*policy;+inti;++policy=cpufreq_cpu_get_raw(cpu_dev->id);+if(!policy)+return0;++data=policy->driver_data;++for(i=0;i<data->nr_opp;i++){+if(data->table[i].frequency<*KHz)+break;+}+i--;++*KHz=data->table[i].frequency;+*mW=readl_relaxed(data->reg_bases[REG_EM_POWER_TBL]++i*LUT_ROW_SIZE)/1000;++return0;+}++staticintmtk_cpufreq_hw_target_index(structcpufreq_policy*policy,+unsignedintindex)+{+structmtk_cpufreq_data*data=policy->driver_data;++writel_relaxed(index,data->reg_bases[REG_FREQ_PERF_STATE]);++return0;+}++staticunsignedintmtk_cpufreq_hw_get(unsignedintcpu)+{+structmtk_cpufreq_data*data;+structcpufreq_policy*policy;+unsignedintindex;++policy=cpufreq_cpu_get_raw(cpu);+if(!policy)+return0;++data=policy->driver_data;++index=readl_relaxed(data->reg_bases[REG_FREQ_PERF_STATE]);+index=min(index,LUT_MAX_ENTRIES-1);++returndata->table[index].frequency;+}++staticunsignedintmtk_cpufreq_hw_fast_switch(structcpufreq_policy*policy,+unsignedinttarget_freq)+{+structmtk_cpufreq_data*data=policy->driver_data;+unsignedintindex;++index=cpufreq_table_find_index_dl(policy,target_freq);++writel_relaxed(index,data->reg_bases[REG_FREQ_PERF_STATE]);++returnpolicy->freq_table[index].frequency;+}++staticintmtk_cpu_create_freq_table(structplatform_device*pdev,+structmtk_cpufreq_data*data)+{+structdevice*dev=&pdev->dev;+void__iomem*base_table;+u32temp,i,freq,prev_freq=0;++data->table=devm_kcalloc(dev,LUT_MAX_ENTRIES+1,+sizeof(*data->table),GFP_KERNEL);+if(!data->table)+return-ENOMEM;++base_table=data->reg_bases[REG_FREQ_LUT_TABLE];++for(i=0;i<LUT_MAX_ENTRIES;i++){+temp=readl_relaxed(base_table+(i*LUT_ROW_SIZE));+freq=FIELD_GET(LUT_FREQ,temp)*1000;++if(freq==prev_freq)+break;++data->table[i].frequency=freq;++dev_dbg(dev,"index=%d freq=%d\n",i,data->table[i].frequency);++prev_freq=freq;+}++data->table[i].frequency=CPUFREQ_TABLE_END;+data->nr_opp=i;++return0;+}++staticintmtk_cpu_resources_init(structplatform_device*pdev,+structcpufreq_policy*policy,+constu16*offsets)+{+structmtk_cpufreq_data*data;+structdevice*dev=&pdev->dev;+void__iomem*base;+intret,i;+intindex;++data=devm_kzalloc(dev,sizeof(*data),GFP_KERNEL);+if(!data)+return-ENOMEM;++index=of_perf_domain_get_sharing_cpumask(policy->cpu,"performance-domains",+"#performance-domain-cells",+policy->cpus);+if(index<0)+returnindex;++base=devm_platform_ioremap_resource(pdev,index);+if(IS_ERR(base))+returnPTR_ERR(base);++for(i=REG_FREQ_LUT_TABLE;i<REG_ARRAY_SIZE;i++)+data->reg_bases[i]=base+offsets[i];++ret=mtk_cpu_create_freq_table(pdev,data);+if(ret){+dev_info(dev,"Domain-%d failed to create freq table\n",index);+returnret;+}++policy->freq_table=data->table;+policy->driver_data=data;++return0;+}++staticintmtk_cpufreq_hw_cpu_init(structcpufreq_policy*policy)+{+structplatform_device*pdev=cpufreq_get_driver_data();+structmtk_cpufreq_drv*drv=platform_get_drvdata(pdev);+intsig,pwr_hw=CPUFREQ_HW_STATUS|SVS_HW_STATUS;+structmtk_cpufreq_data*data;+structdevice*cpu_dev;+unsignedintlatency;+intret;++/* Get the bases of cpufreq for domains */+ret=mtk_cpu_resources_init(pdev,policy,drv->offsets);+if(ret){+dev_info(&pdev->dev,"CPUFreq resource init failed\n");+returnret;+}++data=policy->driver_data;++latency=readl_relaxed(data->reg_bases[REG_FREQ_LATENCY])*1000;+if(!latency)+latency=CPUFREQ_ETERNAL;++/* us convert to ns */+policy->cpuinfo.transition_latency=latency;++policy->fast_switch_possible=true;++/* HW should be in enabled state to proceed now */+writel_relaxed(0x1,data->reg_bases[REG_FREQ_ENABLE]);+if(readl_poll_timeout(data->reg_bases[REG_FREQ_HW_STATE],sig,+(sig&pwr_hw)==pwr_hw,POLL_USEC,+TIMEOUT_USEC)){+if(!(sig&CPUFREQ_HW_STATUS)){+pr_info("cpufreq hardware of CPU%d is not enabled\n",+policy->cpu);+return-ENODEV;+}++pr_info("SVS of CPU%d is not enabled\n",policy->cpu);+}++cpu_dev=get_cpu_device(policy->cpu);+if(!cpu_dev){+pr_info("failed to get cpu%d device\n",policy->cpu);+return-ENODEV;+}++return0;+}++staticintmtk_cpufreq_hw_cpu_exit(structcpufreq_policy*policy)+{+structmtk_cpufreq_data*data=policy->driver_data;++/* HW should be in paused state now */+writel_relaxed(0x0,data->reg_bases[REG_FREQ_ENABLE]);++return0;+}++staticvoidmtk_cpufreq_register_em(structcpufreq_policy*policy)+{+structem_data_callbackem_cb=EM_DATA_CB(mtk_cpufreq_get_cpu_power);+structmtk_cpufreq_data*data=policy->driver_data;++if(!data->nr_opp)+return;++em_dev_register_perf_domain(get_cpu_device(policy->cpu),data->nr_opp,+&em_cb,policy->cpus,true);+}++staticstructcpufreq_drivercpufreq_mtk_hw_driver={+.flags=CPUFREQ_NEED_INITIAL_FREQ_CHECK|+CPUFREQ_HAVE_GOVERNOR_PER_POLICY|+CPUFREQ_IS_COOLING_DEV,+.verify=cpufreq_generic_frequency_table_verify,+.target_index=mtk_cpufreq_hw_target_index,+.get=mtk_cpufreq_hw_get,+.init=mtk_cpufreq_hw_cpu_init,+.exit=mtk_cpufreq_hw_cpu_exit,+.register_em=mtk_cpufreq_register_em,+.fast_switch=mtk_cpufreq_hw_fast_switch,+.name="mtk-cpufreq-hw",+.attr=cpufreq_generic_attr,+};++staticintmtk_cpufreq_hw_driver_probe(structplatform_device*pdev)+{+structmtk_cpufreq_drv*drv;+constu16*offsets;+intret;++offsets=of_device_get_match_data(&pdev->dev);+if(!offsets)+return-EINVAL;++drv=kzalloc(sizeof(*drv),GFP_KERNEL);+if(!drv)+return-ENOMEM;++drv->offsets=offsets;++platform_set_drvdata(pdev,drv);++cpufreq_mtk_hw_driver.driver_data=pdev;++ret=cpufreq_register_driver(&cpufreq_mtk_hw_driver);+if(ret)+dev_err(&pdev->dev,"CPUFreq HW driver failed to register\n");++returnret;+}++staticintmtk_cpufreq_hw_driver_remove(structplatform_device*pdev)+{+structmtk_cpufreq_drv*drv=platform_get_drvdata(pdev);++kfree(drv->offsets);+kfree(drv);++returncpufreq_unregister_driver(&cpufreq_mtk_hw_driver);+}++staticconststructof_device_idmtk_cpufreq_hw_match[]={+{.compatible="mediatek,cpufreq-hw",.data=&cpufreq_mtk_offsets},+{}+};++staticstructplatform_drivermtk_cpufreq_hw_driver={+.probe=mtk_cpufreq_hw_driver_probe,+.remove=mtk_cpufreq_hw_driver_remove,+.driver={+.name="mtk-cpufreq-hw",+.of_match_table=mtk_cpufreq_hw_match,+},+};+module_platform_driver(mtk_cpufreq_hw_driver);++MODULE_AUTHOR("Hector Yuan <hector.yuan@mediatek.com>");+MODULE_DESCRIPTION("Mediatek cpufreq-hw driver");+MODULE_LICENSE("GPL v2");
--
1.7.9.5
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
From: "Hector.Yuan" <redacted>
Add of_perf_domain_get_sharing_cpumask function to group cpu
to specific performance domain.
Signed-off-by: Hector.Yuan <redacted>
---
include/linux/cpufreq.h | 46 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
@@ -126,8 +120,8 @@ static int mtk_cpu_create_freq_table(struct platform_device *pdev,structmtk_cpufreq_data*data){structdevice*dev=&pdev->dev;-void__iomem*base_table;u32temp,i,freq,prev_freq=0;+void__iomem*base_table;data->table=devm_kcalloc(dev,LUT_MAX_ENTRIES+1,sizeof(*data->table),GFP_KERNEL);
@@ -198,15 +192,13 @@ static int mtk_cpu_resources_init(struct platform_device *pdev,staticintmtk_cpufreq_hw_cpu_init(structcpufreq_policy*policy){structplatform_device*pdev=cpufreq_get_driver_data();-structmtk_cpufreq_drv*drv=platform_get_drvdata(pdev);intsig,pwr_hw=CPUFREQ_HW_STATUS|SVS_HW_STATUS;structmtk_cpufreq_data*data;-structdevice*cpu_dev;unsignedintlatency;intret;/* Get the bases of cpufreq for domains */-ret=mtk_cpu_resources_init(pdev,policy,drv->offsets);+ret=mtk_cpu_resources_init(pdev,policy,platform_get_drvdata(pdev));if(ret){dev_info(&pdev->dev,"CPUFreq resource init failed\n");returnret;
@@ -218,9 +210,7 @@ static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)if(!latency)latency=CPUFREQ_ETERNAL;-/* us convert to ns */policy->cpuinfo.transition_latency=latency;-policy->fast_switch_possible=true;/* HW should be in enabled state to proceed now */
@@ -237,12 +227,6 @@ static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)pr_info("SVS of CPU%d is not enabled\n",policy->cpu);}-cpu_dev=get_cpu_device(policy->cpu);-if(!cpu_dev){-pr_info("failed to get cpu%d device\n",policy->cpu);-return-ENODEV;-}-return0;}
On Mon, 2021-09-06 at 15:13 +0530, Viresh Kumar wrote:
On 03-09-21, 16:39, Hector Yuan wrote:
quoted
From: "Hector.Yuan" <redacted>
Add of_perf_domain_get_sharing_cpumask function to group cpu
to specific performance domain.
Signed-off-by: Hector.Yuan <redacted>
---
include/linux/cpufreq.h | 46
+++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
To speed things up, I have applied this with following changes.
Please
test my branch and see if something breaks:
Hi, Viresh:
I've tested the code in your branch and its okay. Thanks for the review
and your guideness. Thanks.
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel