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. This patch depends on MT6779 DTS patch[1] submitted by Hanks Chen. From v7 to v8, there are three more patches based on patchset[2]. 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://lkml.org/lkml/2020/8/4/1094 [2] https://lkml.org/lkml/2020/9/23/384 Hector.Yuan (3): cpufreq: mediatek-hw: Add support for CPUFREQ HW dt-bindings: arm: cpus: Document 'mediatek,freq-domain' property dt-bindings: cpufreq: add bindings for MediaTek cpufreq HW Documentation/devicetree/bindings/arm/cpus.yaml | 6 + .../bindings/cpufreq/cpufreq-mediatek-hw.yaml | 113 +++++++ drivers/cpufreq/Kconfig.arm | 12 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/mediatek-cpufreq-hw.c | 343 ++++++++++++++++++++ 5 files changed, 475 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml create mode 100644 drivers/cpufreq/mediatek-cpufreq-hw.c
From: "Hector.Yuan" <hector.yuan@mediatek.com> Add cpufreq HW support. Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> --- drivers/cpufreq/Kconfig.arm | 12 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/mediatek-cpufreq-hw.c | 343 +++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 drivers/cpufreq/mediatek-cpufreq-hw.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index cb72fb5..b9d17c5 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -123,6 +123,18 @@ config ARM_MEDIATEK_CPUFREQ help This adds the CPUFreq driver support for MediaTek SoCs. +config ARM_MEDIATEK_CPUFREQ_HW + tristate "MediaTek CPUFreq HW driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + default m + help + Support for the CPUFreq HW driver. + Some MediaTek chipsets have a HW engine to offload the steps + necessary for changing the frequency of the CPUs. Firmware loaded + in this engine exposes a programming interface to the OS. + The driver implements the cpufreq interface for this HW engine. + Say Y if you want to support CPUFreq HW. + config ARM_OMAP2PLUS_CPUFREQ bool "TI OMAP2+" depends on ARCH_OMAP2PLUS diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index f1b7e3d..ffc61cd 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o obj-$(CONFIG_ARM_IMX_CPUFREQ_DT) += imx-cpufreq-dt.o obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o +obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ_HW) += mediatek-cpufreq-hw.o obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o diff --git a/drivers/cpufreq/mediatek-cpufreq-hw.c b/drivers/cpufreq/mediatek-cpufreq-hw.c new file mode 100644 index 0000000..3af36b0 --- /dev/null +++ b/drivers/cpufreq/mediatek-cpufreq-hw.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#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/pm_qos.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_ARRAY_SIZE, +}; + +struct cpufreq_mtk { + struct cpufreq_frequency_table *table; + void __iomem *reg_bases[REG_ARRAY_SIZE]; + int nr_opp; + cpumask_t related_cpus; +}; + +static const u16 cpufreq_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] = 0x3D0, +}; + +static struct cpufreq_mtk *mtk_freq_domain_map[NR_CPUS]; + +static int mtk_cpufreq_get_cpu_power(unsigned long *mW, + unsigned long *KHz, struct device *cpu_dev) +{ + struct cpufreq_mtk *c = mtk_freq_domain_map[cpu_dev->id]; + int i; + + for (i = 0; i < c->nr_opp; i++) { + if (c->table[i].frequency < *KHz) + break; + } + i--; + + *KHz = c->table[i].frequency; + *mW = readl_relaxed(c->reg_bases[REG_EM_POWER_TBL] + + i * LUT_ROW_SIZE) / 1000; + + return 0; +} + +static int mtk_cpufreq_hw_target_index(struct cpufreq_policy *policy, + unsigned int index) +{ + struct cpufreq_mtk *c = policy->driver_data; + + writel_relaxed(index, c->reg_bases[REG_FREQ_PERF_STATE]); + + return 0; +} + +static unsigned int mtk_cpufreq_hw_get(unsigned int cpu) +{ + struct cpufreq_mtk *c; + unsigned int index; + + c = mtk_freq_domain_map[cpu]; + + index = readl_relaxed(c->reg_bases[REG_FREQ_PERF_STATE]); + index = min(index, LUT_MAX_ENTRIES - 1); + + return c->table[index].frequency; +} + +static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) +{ + struct cpufreq_mtk *c; + struct device *cpu_dev; + struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power); + struct pm_qos_request *qos_request; + int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS; + + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL); + if (!qos_request) + return -ENOMEM; + + cpu_dev = get_cpu_device(policy->cpu); + if (!cpu_dev) { + pr_err("failed to get cpu%d device\n", policy->cpu); + return -ENODEV; + } + + c = mtk_freq_domain_map[policy->cpu]; + if (!c) { + pr_err("No scaling support for CPU%d\n", policy->cpu); + return -ENODEV; + } + + cpumask_copy(policy->cpus, &c->related_cpus); + + policy->freq_table = c->table; + policy->driver_data = c; + + /* Let CPUs leave idle-off state for SVS CPU initializing */ + cpu_latency_qos_add_request(qos_request, 0); + + /* HW should be in enabled state to proceed now */ + writel_relaxed(0x1, c->reg_bases[REG_FREQ_ENABLE]); + + if (readl_poll_timeout(c->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); + } + + em_dev_register_perf_domain(cpu_dev, c->nr_opp, &em_cb, policy->cpus); + + cpu_latency_qos_remove_request(qos_request); + kfree(qos_request); + + return 0; +} + +static int mtk_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) +{ + struct cpufreq_mtk *c; + + c = mtk_freq_domain_map[policy->cpu]; + if (!c) { + pr_err("No scaling support for CPU%d\n", policy->cpu); + return -ENODEV; + } + + /* HW should be in paused state now */ + writel_relaxed(0x0, c->reg_bases[REG_FREQ_ENABLE]); + + return 0; +} + +static struct cpufreq_driver cpufreq_mtk_hw_driver = { + .flags = CPUFREQ_STICKY | 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, + .name = "mtk-cpufreq-hw", + .attr = cpufreq_generic_attr, +}; + +static int mtk_cpu_create_freq_table(struct platform_device *pdev, + struct cpufreq_mtk *c) +{ + struct device *dev = &pdev->dev; + void __iomem *base_table; + u32 data, i, freq, prev_freq = 0; + + c->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1, + sizeof(*c->table), GFP_KERNEL); + if (!c->table) + return -ENOMEM; + + base_table = c->reg_bases[REG_FREQ_LUT_TABLE]; + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + data = readl_relaxed(base_table + (i * LUT_ROW_SIZE)); + freq = FIELD_GET(LUT_FREQ, data) * 1000; + + if (freq == prev_freq) + break; + + c->table[i].frequency = freq; + + dev_dbg(dev, "index=%d freq=%d\n", + i, c->table[i].frequency); + + prev_freq = freq; + } + + c->table[i].frequency = CPUFREQ_TABLE_END; + c->nr_opp = i; + + return 0; +} + +static int mtk_get_related_cpus(int index, struct cpufreq_mtk *c) +{ + struct device_node *cpu_np; + struct of_phandle_args args; + int cpu, ret; + + for_each_possible_cpu(cpu) { + cpu_np = of_cpu_device_node_get(cpu); + if (!cpu_np) + continue; + + ret = of_parse_phandle_with_args(cpu_np, "mediatek,freq-domain", + "#freq-domain-cells", 0, + &args); + of_node_put(cpu_np); + if (ret < 0) + continue; + + if (index == args.args[0]) { + cpumask_set_cpu(cpu, &c->related_cpus); + mtk_freq_domain_map[cpu] = c; + } + } + + return 0; +} + +static int mtk_cpu_resources_init(struct platform_device *pdev, + unsigned int cpu, int index, + const u16 *offsets) +{ + struct cpufreq_mtk *c; + struct device *dev = &pdev->dev; + int ret, i; + void __iomem *base; + + if (mtk_freq_domain_map[cpu]) + return 0; + + c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + + base = devm_platform_ioremap_resource(pdev, index); + if (IS_ERR(base)) + return PTR_ERR(base); + + for (i = REG_FREQ_LUT_TABLE; i < REG_ARRAY_SIZE; i++) + c->reg_bases[i] = base + offsets[i]; + + ret = mtk_get_related_cpus(index, c); + if (ret) { + dev_err(dev, "Domain-%d failed to get related CPUs\n", index); + return ret; + } + + ret = mtk_cpu_create_freq_table(pdev, c); + if (ret) { + dev_err(dev, "Domain-%d failed to create freq table\n", index); + return ret; + } + + return 0; +} + +static int mtk_cpufreq_hw_driver_probe(struct platform_device *pdev) +{ + struct device_node *cpu_np; + struct of_phandle_args args; + const u16 *offsets; + unsigned int cpu; + int ret; + + offsets = of_device_get_match_data(&pdev->dev); + if (!offsets) + return -EINVAL; + + for_each_possible_cpu(cpu) { + cpu_np = of_cpu_device_node_get(cpu); + if (!cpu_np) { + dev_err(&pdev->dev, "Failed to get cpu %d device\n", + cpu); + return -ENODEV; + } + + ret = of_parse_phandle_with_args(cpu_np, "mediatek,freq-domain", + "#freq-domain-cells", 0, + &args); + if (ret < 0) + return ret; + + /* Get the bases of cpufreq for domains */ + ret = mtk_cpu_resources_init(pdev, cpu, args.args[0], offsets); + if (ret) { + dev_err(&pdev->dev, "CPUFreq resource init failed\n"); + return ret; + } + } + + ret = cpufreq_register_driver(&cpufreq_mtk_hw_driver); + if (ret) { + dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n"); + return ret; + } + + return 0; +} + +static int mtk_cpufreq_hw_driver_remove(struct platform_device *pdev) +{ + return cpufreq_unregister_driver(&cpufreq_mtk_hw_driver); +} + +static const struct of_device_id mtk_cpufreq_hw_match[] = { + { .compatible = "mediatek,cpufreq-hw", .data = &cpufreq_mtk_offsets }, + {} +}; + +static struct platform_driver mtk_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_DESCRIPTION("Mediatek cpufreq-hw driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5
From: "Hector.Yuan" <hector.yuan@mediatek.com> Add devicetree documentation for 'mediatek,freq-domain' property specific to Mediatek CPUs. This property is used to reference the CPUFREQ node along with the domain id. Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> --- Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 1222bf1..e995b26 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -255,6 +255,12 @@ properties: where voltage is in V, frequency is in MHz. + mediatek,freq-domain: + $ref: '/schemas/types.yaml#/definitions/phandle-array' + description: + CPUs supporting freq-domain must set their "mediatek,freq-domain" property + with phandle to a cpufreq_hw node followed by the domain id. + power-domains: $ref: '/schemas/types.yaml#/definitions/phandle-array' description: -- 1.7.9.5
From: "Hector.Yuan" <hector.yuan@mediatek.com> Add devicetree bindings for MediaTek HW driver. Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> --- .../bindings/cpufreq/cpufreq-mediatek-hw.yaml | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml new file mode 100644 index 0000000..32d2ad4 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.yaml @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.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. + +required: + - compatible + - reg + +examples: + - | + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 0>; + reg = <0x000>; + }; + + cpu1: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 0>; + reg = <0x100>; + }; + + cpu2: cpu@200 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 0>; + reg = <0x200>; + }; + + cpu3: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 0>; + reg = <0x300>; + }; + + cpu4: cpu@400 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 1>; + reg = <0x400>; + }; + + cpu5: cpu@500 { + device_type = "cpu"; + compatible = "arm,cortex-a55"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 1>; + reg = <0x500>; + }; + + cpu6: cpu@600 { + device_type = "cpu"; + compatible = "arm,cortex-a75"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 1>; + reg = <0x600>; + }; + + cpu7: cpu@700 { + device_type = "cpu"; + compatible = "arm,cortex-a75"; + enable-method = "psci"; + mediatek,freq-domain = <&cpufreq_hw 1>; + reg = <0x700>; + }; + }; + + /* ... */ + + soc { + #address-cells = <2>; + #size-cells = <2>; + + cpufreq_hw: cpufreq@11bc00 { + compatible = "mediatek,cpufreq-hw"; + reg = <0 0x11bc10 0 0x8c>, + <0 0x11bca0 0 0x8c>; + }; + }; -- 1.7.9.5
On Mon, Oct 26, 2020 at 04:19:08PM +0800, Hector Yuan wrote: > From: "Hector.Yuan" <hector.yuan@mediatek.com> > > Add devicetree documentation for 'mediatek,freq-domain' property specific > to Mediatek CPUs. This property is used to reference the CPUFREQ node > along with the domain id. > > Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> > --- > Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++ > 1 file changed, 6 insertions(+) > > diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml > index 1222bf1..e995b26 100644 > --- a/Documentation/devicetree/bindings/arm/cpus.yaml > +++ b/Documentation/devicetree/bindings/arm/cpus.yaml > @@ -255,6 +255,12 @@ properties: > > where voltage is in V, frequency is in MHz. > > + mediatek,freq-domain: > + $ref: '/schemas/types.yaml#/definitions/phandle-array' > + description: > + CPUs supporting freq-domain must set their "mediatek,freq-domain" property > + with phandle to a cpufreq_hw node followed by the domain id. This needs to be a common binding shared with SCMI domains. > + > power-domains: > $ref: '/schemas/types.yaml#/definitions/phandle-array' > description: > -- > 1.7.9.5
Hi Hector, On 10/26/20 8:19 AM, Hector Yuan wrote: > From: "Hector.Yuan" <hector.yuan@mediatek.com> > > Add cpufreq HW support. > > Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> [snip] > + > +static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) > +{ > + struct cpufreq_mtk *c; > + struct device *cpu_dev; > + struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power); > + struct pm_qos_request *qos_request; > + int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS; > + > + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL); > + if (!qos_request) > + return -ENOMEM; > + > + cpu_dev = get_cpu_device(policy->cpu); > + if (!cpu_dev) { > + pr_err("failed to get cpu%d device\n", policy->cpu); > + return -ENODEV; > + } > + > + c = mtk_freq_domain_map[policy->cpu]; > + if (!c) { > + pr_err("No scaling support for CPU%d\n", policy->cpu); > + return -ENODEV; > + } > + > + cpumask_copy(policy->cpus, &c->related_cpus); > + > + policy->freq_table = c->table; > + policy->driver_data = c; To control frequency transition rate in schedutil, you might be interested in setting: policy->cpuinfo.transition_latency = <mtk_value_here>; Example, when this latency value comes from FW [1] > + > + /* Let CPUs leave idle-off state for SVS CPU initializing */ > + cpu_latency_qos_add_request(qos_request, 0); > + > + /* HW should be in enabled state to proceed now */ > + writel_relaxed(0x1, c->reg_bases[REG_FREQ_ENABLE]); > + > + if (readl_poll_timeout(c->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); > + } > + > + em_dev_register_perf_domain(cpu_dev, c->nr_opp, &em_cb, policy->cpus); Please keep in mind that this is going to be changed soon with a new argument: 'milliwatts'. It's queued in pm/linux-next [2]. Regards, Lukasz [1] https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/scmi-cpufreq.c#L194 [2] https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git/commit/?h=linux-next&id=c250d50fe2ce627ca9805d9c8ac11cbbf922a4a6
On Thu, 2020-11-19 at 12:41 +0000, Lukasz Luba wrote: > Hi Hector, > > On 10/26/20 8:19 AM, Hector Yuan wrote: > > From: "Hector.Yuan" <hector.yuan@mediatek.com> > > > > Add cpufreq HW support. > > > > Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> > > [snip] > > > + > > +static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) > > +{ > > + struct cpufreq_mtk *c; > > + struct device *cpu_dev; > > + struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power); > > + struct pm_qos_request *qos_request; > > + int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS; > > + > > + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL); > > + if (!qos_request) > > + return -ENOMEM; > > + > > + cpu_dev = get_cpu_device(policy->cpu); > > + if (!cpu_dev) { > > + pr_err("failed to get cpu%d device\n", policy->cpu); > > + return -ENODEV; > > + } > > + > > + c = mtk_freq_domain_map[policy->cpu]; > > + if (!c) { > > + pr_err("No scaling support for CPU%d\n", policy->cpu); > > + return -ENODEV; > > + } > > + > > + cpumask_copy(policy->cpus, &c->related_cpus); > > + > > + policy->freq_table = c->table; > > + policy->driver_data = c; > > To control frequency transition rate in schedutil, you might > be interested in setting: > > policy->cpuinfo.transition_latency = <mtk_value_here>; > > Example, when this latency value comes from FW [1] > OK, I will add it in v9. > > + > > + /* Let CPUs leave idle-off state for SVS CPU initializing */ > > + cpu_latency_qos_add_request(qos_request, 0); > > + > > + /* HW should be in enabled state to proceed now */ > > + writel_relaxed(0x1, c->reg_bases[REG_FREQ_ENABLE]); > > + > > + if (readl_poll_timeout(c->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); > > + } > > + > > + em_dev_register_perf_domain(cpu_dev, c->nr_opp, &em_cb, policy->cpus); > > Please keep in mind that this is going to be changed soon with a new > argument: 'milliwatts'. It's queued in pm/linux-next [2]. > OK, thanks for the remind. > Regards, > Lukasz > > [1] > https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/scmi-cpufreq.c#L194 > [2] > https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git/commit/?h=linux-next&id=c250d50fe2ce627ca9805d9c8ac11cbbf922a4a6 >
On 11/19/20 1:40 PM, Hector Yuan wrote:
> On Thu, 2020-11-19 at 12:41 +0000, Lukasz Luba wrote:
>> Hi Hector,
>>
>> On 10/26/20 8:19 AM, Hector Yuan wrote:
>>> From: "Hector.Yuan" <hector.yuan@mediatek.com>
>>>
>>> Add cpufreq HW support.
>>>
>>> Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com>
>>
>> [snip]
>>
>>> +
>>> +static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
>>> +{
>>> + struct cpufreq_mtk *c;
>>> + struct device *cpu_dev;
>>> + struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power);
>>> + struct pm_qos_request *qos_request;
>>> + int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS;
>>> +
>>> + qos_request = kzalloc(sizeof(*qos_request), GFP_KERNEL);
>>> + if (!qos_request)
>>> + return -ENOMEM;
>>> +
>>> + cpu_dev = get_cpu_device(policy->cpu);
>>> + if (!cpu_dev) {
>>> + pr_err("failed to get cpu%d device\n", policy->cpu);
>>> + return -ENODEV;
>>> + }
>>> +
>>> + c = mtk_freq_domain_map[policy->cpu];
>>> + if (!c) {
>>> + pr_err("No scaling support for CPU%d\n", policy->cpu);
>>> + return -ENODEV;
>>> + }
>>> +
>>> + cpumask_copy(policy->cpus, &c->related_cpus);
>>> +
>>> + policy->freq_table = c->table;
>>> + policy->driver_data = c;
>>
>> To control frequency transition rate in schedutil, you might
>> be interested in setting:
>>
>> policy->cpuinfo.transition_latency = <mtk_value_here>;
>>
>> Example, when this latency value comes from FW [1]
>>
> OK, I will add it in v9.
>>> +
>>> + /* Let CPUs leave idle-off state for SVS CPU initializing */
>>> + cpu_latency_qos_add_request(qos_request, 0);
>>> +
>>> + /* HW should be in enabled state to proceed now */
>>> + writel_relaxed(0x1, c->reg_bases[REG_FREQ_ENABLE]);
>>> +
>>> + if (readl_poll_timeout(c->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);
>>> + }
>>> +
>>> + em_dev_register_perf_domain(cpu_dev, c->nr_opp, &em_cb, policy->cpus);
>>
>> Please keep in mind that this is going to be changed soon with a new
>> argument: 'milliwatts'. It's queued in pm/linux-next [2].
>>
> OK, thanks for the remind.
>> Regards,
>> Lukasz
>>
>> [1]
>> https://elixir.bootlin.com/linux/latest/source/drivers/cpufreq/scmi-cpufreq.c#L194
>> [2]
>> https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git/commit/?h=linux-next&id=c250d50fe2ce627ca9805d9c8ac11cbbf922a4a6
>>
>
Also, based on function mtk_cpufreq_hw_target_index(), which looks
really simple, you might consider to have fast_switch enabled.
It will allow SchedUtil governor to change frequency directly
and not create a dedicated deadline thread for it. It pays off.
You have to experiment with something like:
policy->fast_switch_possible = true;
static struct cpufreq_driver cpufreq_mtk_hw_driver = {
...
.fast_switch = mtk_cpufreq_hw_fast_switch
...
}
Again, scmi-cpufreq.c would be a good pattern to follow.
On 10/28/20 3:08 PM, Rob Herring wrote:
> On Mon, Oct 26, 2020 at 04:19:08PM +0800, Hector Yuan wrote:
>> From: "Hector.Yuan" <hector.yuan@mediatek.com>
>>
>> Add devicetree documentation for 'mediatek,freq-domain' property specific
>> to Mediatek CPUs. This property is used to reference the CPUFREQ node
>> along with the domain id.
>>
>> Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com>
>> ---
>> Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++
>> 1 file changed, 6 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml
>> index 1222bf1..e995b26 100644
>> --- a/Documentation/devicetree/bindings/arm/cpus.yaml
>> +++ b/Documentation/devicetree/bindings/arm/cpus.yaml
>> @@ -255,6 +255,12 @@ properties:
>>
>> where voltage is in V, frequency is in MHz.
>>
>> + mediatek,freq-domain:
>> + $ref: '/schemas/types.yaml#/definitions/phandle-array'
>> + description:
>> + CPUs supporting freq-domain must set their "mediatek,freq-domain" property
>> + with phandle to a cpufreq_hw node followed by the domain id.
>
> This needs to be a common binding shared with SCMI domains.
Would it be accurate to create a new binding file:
Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.txt
?
There is already cpufreq-qcom-hw.txt with 'qcom,freq-domain'
and analogous purpose.
Regards,
Lukasz
On Thu, Nov 19, 2020 at 03:23:20PM +0000, Lukasz Luba wrote: > > > On 10/28/20 3:08 PM, Rob Herring wrote: > > On Mon, Oct 26, 2020 at 04:19:08PM +0800, Hector Yuan wrote: > > > From: "Hector.Yuan" <hector.yuan@mediatek.com> > > > > > > Add devicetree documentation for 'mediatek,freq-domain' property specific > > > to Mediatek CPUs. This property is used to reference the CPUFREQ node > > > along with the domain id. > > > > > > Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com> > > > --- > > > Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++ > > > 1 file changed, 6 insertions(+) > > > > > > diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml > > > index 1222bf1..e995b26 100644 > > > --- a/Documentation/devicetree/bindings/arm/cpus.yaml > > > +++ b/Documentation/devicetree/bindings/arm/cpus.yaml > > > @@ -255,6 +255,12 @@ properties: > > > where voltage is in V, frequency is in MHz. > > > + mediatek,freq-domain: > > > + $ref: '/schemas/types.yaml#/definitions/phandle-array' > > > + description: > > > + CPUs supporting freq-domain must set their "mediatek,freq-domain" property > > > + with phandle to a cpufreq_hw node followed by the domain id. > > > > This needs to be a common binding shared with SCMI domains. > > Would it be accurate to create a new binding file: > Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.txt > ? > Nope, Rob already asked to unify all such bindings and generalise it. Here is my attempt[1] and this must just use it or help to enhance that in order to make use of that binding. -- Regards, Sudeep [1] https://lore.kernel.org/lkml/20201116181356.804590-1-sudeep.holla@arm.com
On 11/19/20 5:13 PM, Sudeep Holla wrote:
> On Thu, Nov 19, 2020 at 03:23:20PM +0000, Lukasz Luba wrote:
>>
>>
>> On 10/28/20 3:08 PM, Rob Herring wrote:
>>> On Mon, Oct 26, 2020 at 04:19:08PM +0800, Hector Yuan wrote:
>>>> From: "Hector.Yuan" <hector.yuan@mediatek.com>
>>>>
>>>> Add devicetree documentation for 'mediatek,freq-domain' property specific
>>>> to Mediatek CPUs. This property is used to reference the CPUFREQ node
>>>> along with the domain id.
>>>>
>>>> Signed-off-by: Hector.Yuan <hector.yuan@mediatek.com>
>>>> ---
>>>> Documentation/devicetree/bindings/arm/cpus.yaml | 6 ++++++
>>>> 1 file changed, 6 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml
>>>> index 1222bf1..e995b26 100644
>>>> --- a/Documentation/devicetree/bindings/arm/cpus.yaml
>>>> +++ b/Documentation/devicetree/bindings/arm/cpus.yaml
>>>> @@ -255,6 +255,12 @@ properties:
>>>> where voltage is in V, frequency is in MHz.
>>>> + mediatek,freq-domain:
>>>> + $ref: '/schemas/types.yaml#/definitions/phandle-array'
>>>> + description:
>>>> + CPUs supporting freq-domain must set their "mediatek,freq-domain" property
>>>> + with phandle to a cpufreq_hw node followed by the domain id.
>>>
>>> This needs to be a common binding shared with SCMI domains.
>>
>> Would it be accurate to create a new binding file:
>> Documentation/devicetree/bindings/cpufreq/cpufreq-mediatek-hw.txt
>> ?
>>
>
> Nope, Rob already asked to unify all such bindings and generalise it.
> Here is my attempt[1] and this must just use it or help to enhance that
> in order to make use of that binding.
>
That's great. Thank you for the information.
Regards,
Lukasz