From 36eb7dc1bd42fe5f850329c893768ff89b696fba Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Tue, 11 Feb 2020 12:58:07 +0100 Subject: [PATCH 01/13] cpufreq: imx6q: Fixes unwanted cpu overclocking on i.MX6ULL imx6ul_opp_check_speed_grading is called for both i.MX6UL and i.MX6ULL. Since the i.MX6ULL was introduced to a separate ocotp compatible node later, it is possible that the i.MX6ULL has also dtbs with "fsl,imx6ull-ocotp". On a system without nvmem-cell speed grade a missing check on this node causes a driver fail without considering the cpu speed grade. This patch prevents unwanted cpu overclocking on i.MX6ULL with compatible node "fsl,imx6ull-ocotp" in old dtbs without nvmem-cell speed grade. Fixes: 2733fb0d0699 ("cpufreq: imx6q: read OCOTP through nvmem for imx6ul/imx6ull") Signed-off-by: Christoph Niedermaier Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 648a09a1778a3..1fcbbd53a48a2 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -280,6 +280,9 @@ static int imx6ul_opp_check_speed_grading(struct device *dev) void __iomem *base; np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-ocotp"); + if (!np) + np = of_find_compatible_node(NULL, NULL, + "fsl,imx6ull-ocotp"); if (!np) return -ENOENT; From 0ea4fb29c68950fbeea4e507e3c59ea35bb98ffd Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 11 Feb 2020 12:53:55 +0530 Subject: [PATCH 02/13] cpufreq: ti-cpufreq: Add support for OPP_PLUS DRA762 SoC introduces OPP_PLUS which runs at 1.8GHz. Add support for this OPP in ti-cpufreq driver. Acked-by: Dave Gerlach Signed-off-by: Lokesh Vutla Signed-off-by: Viresh Kumar --- drivers/cpufreq/ti-cpufreq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 557cb513bf7f7..ab0de27539ad4 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -25,11 +25,14 @@ #define DRA7_EFUSE_HAS_OD_MPU_OPP 11 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15 +#define DRA76_EFUSE_HAS_PLUS_MPU_OPP 18 #define DRA7_EFUSE_HAS_ALL_MPU_OPP 23 +#define DRA76_EFUSE_HAS_ALL_MPU_OPP 24 #define DRA7_EFUSE_NOM_MPU_OPP BIT(0) #define DRA7_EFUSE_OD_MPU_OPP BIT(1) #define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) +#define DRA76_EFUSE_PLUS_MPU_OPP BIT(3) #define OMAP3_CONTROL_DEVICE_STATUS 0x4800244C #define OMAP3_CONTROL_IDCODE 0x4830A204 @@ -80,6 +83,10 @@ static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, */ switch (efuse) { + case DRA76_EFUSE_HAS_PLUS_MPU_OPP: + case DRA76_EFUSE_HAS_ALL_MPU_OPP: + calculated_efuse |= DRA76_EFUSE_PLUS_MPU_OPP; + /* Fall through */ case DRA7_EFUSE_HAS_ALL_MPU_OPP: case DRA7_EFUSE_HAS_HIGH_MPU_OPP: calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; From a30f8a91f3c25ad7df897bf7877bad410ad19f79 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 17 Feb 2020 17:42:55 +0800 Subject: [PATCH 03/13] cpufreq: imx-cpufreq-dt: Add "cpu-supply" property check The cpufreq-dt driver allows cpufreq driver enabled without valid regulator assigned, however, all i.MX platforms using cpufreq-dt driver now require valid regulator, add "cpu-supply" property check to avoid i.MX platforms' cpufreq enabled without valid regulator and lead to system unstable. Signed-off-by: Anson Huang Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx-cpufreq-dt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 6cb8193421ea1..0e29d88618c94 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -31,6 +31,9 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) int speed_grade, mkt_segment; int ret; + if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL)) + return -ENODEV; + ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value); if (ret) return ret; From 3646f50a3838c5949a89ecbdb868497cdc05b8fd Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 3 Mar 2020 10:14:49 +0800 Subject: [PATCH 04/13] cpufreq: imx6q: fix error handling When speed checking failed, direclty jumping to put_node label is not correct. Need jump to out_free_opp to avoid resources leak. Fixes: 2733fb0d0699 ("cpufreq: imx6q: read OCOTP through nvmem for imx6ul/imx6ull") Signed-off-by: Peng Fan Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 1fcbbd53a48a2..edef3399c9794 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -381,23 +381,24 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) goto put_reg; } + /* Because we have added the OPPs here, we must free them */ + free_opp = true; + if (of_machine_is_compatible("fsl,imx6ul") || of_machine_is_compatible("fsl,imx6ull")) { ret = imx6ul_opp_check_speed_grading(cpu_dev); if (ret) { if (ret == -EPROBE_DEFER) - goto put_node; + goto out_free_opp; dev_err(cpu_dev, "failed to read ocotp: %d\n", ret); - goto put_node; + goto out_free_opp; } } else { imx6q_opp_check_speed_grading(cpu_dev); } - /* Because we have added the OPPs here, we must free them */ - free_opp = true; num = dev_pm_opp_get_opp_count(cpu_dev); if (num < 0) { ret = num; From 4bd8459b6c3f59eaa93d5e942b58346f1b8c98da Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 3 Mar 2020 10:14:50 +0800 Subject: [PATCH 05/13] cpufreq: imx6q: read OCOTP through nvmem for imx6q Directly accessing OCOTP registers should be avoided, because it could not handle OCOTP clks and could not handle defer proper. With nvmem API, it is safe to access OCOTP registers. To make sure old dtb could work, the original code still kept. Signed-off-by: Peng Fan Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 67 +++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index edef3399c9794..285b8e9aa1854 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -216,31 +216,41 @@ static struct cpufreq_driver imx6q_cpufreq_driver = { #define OCOTP_CFG3_SPEED_996MHZ 0x2 #define OCOTP_CFG3_SPEED_852MHZ 0x1 -static void imx6q_opp_check_speed_grading(struct device *dev) +static int imx6q_opp_check_speed_grading(struct device *dev) { struct device_node *np; void __iomem *base; u32 val; + int ret; - np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); - if (!np) - return; + if (of_find_property(dev->of_node, "nvmem-cells", NULL)) { + ret = nvmem_cell_read_u32(dev, "speed_grade", &val); + if (ret) + return ret; + } else { + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); + if (!np) + return -ENOENT; - base = of_iomap(np, 0); - if (!base) { - dev_err(dev, "failed to map ocotp\n"); - goto put_node; + base = of_iomap(np, 0); + of_node_put(np); + if (!base) { + dev_err(dev, "failed to map ocotp\n"); + return -EFAULT; + } + + /* + * SPEED_GRADING[1:0] defines the max speed of ARM: + * 2b'11: 1200000000Hz; + * 2b'10: 996000000Hz; + * 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. + * 2b'00: 792000000Hz; + * We need to set the max speed of ARM according to fuse map. + */ + val = readl_relaxed(base + OCOTP_CFG3); + iounmap(base); } - /* - * SPEED_GRADING[1:0] defines the max speed of ARM: - * 2b'11: 1200000000Hz; - * 2b'10: 996000000Hz; - * 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz. - * 2b'00: 792000000Hz; - * We need to set the max speed of ARM according to fuse map. - */ - val = readl_relaxed(base + OCOTP_CFG3); val >>= OCOTP_CFG3_SPEED_SHIFT; val &= 0x3; @@ -257,9 +267,8 @@ static void imx6q_opp_check_speed_grading(struct device *dev) if (dev_pm_opp_disable(dev, 1200000000)) dev_warn(dev, "failed to disable 1.2GHz OPP\n"); } - iounmap(base); -put_node: - of_node_put(np); + + return 0; } #define OCOTP_CFG3_6UL_SPEED_696MHZ 0x2 @@ -387,16 +396,16 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) if (of_machine_is_compatible("fsl,imx6ul") || of_machine_is_compatible("fsl,imx6ull")) { ret = imx6ul_opp_check_speed_grading(cpu_dev); - if (ret) { - if (ret == -EPROBE_DEFER) - goto out_free_opp; - - dev_err(cpu_dev, "failed to read ocotp: %d\n", - ret); - goto out_free_opp; - } } else { - imx6q_opp_check_speed_grading(cpu_dev); + ret = imx6q_opp_check_speed_grading(cpu_dev); + } + if (ret) { + if (ret == -EPROBE_DEFER) + goto out_free_opp; + + dev_err(cpu_dev, "failed to read ocotp: %d\n", + ret); + goto out_free_opp; } num = dev_pm_opp_get_opp_count(cpu_dev); From c98330446c32da8898a7dacd81b8d09dc0b34b60 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 10 Mar 2020 13:48:16 +0800 Subject: [PATCH 06/13] cpufreq: imx-cpufreq-dt: Correct i.MX8MP's market segment fuse location i.MX8MP's market segment fuse field is bit[6:5], correct it. Fixes: 83fe39ad0a48 ("cpufreq: imx-cpufreq-dt: Add i.MX8MP support") Signed-off-by: Anson Huang Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx-cpufreq-dt.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 0e29d88618c94..de206d2745feb 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -19,6 +19,8 @@ #define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK (0xf << 8) #define OCOTP_CFG3_MKT_SEGMENT_SHIFT 6 #define OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 6) +#define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT 5 +#define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 5) /* cpufreq-dt device registered by imx-cpufreq-dt */ static struct platform_device *cpufreq_dt_pdev; @@ -45,7 +47,13 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) else speed_grade = (cell_value & OCOTP_CFG3_SPEED_GRADE_MASK) >> OCOTP_CFG3_SPEED_GRADE_SHIFT; - mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) >> OCOTP_CFG3_MKT_SEGMENT_SHIFT; + + if (of_machine_is_compatible("fsl,imx8mp")) + mkt_segment = (cell_value & IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK) + >> IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT; + else + mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK) + >> OCOTP_CFG3_MKT_SEGMENT_SHIFT; /* * Early samples without fuses written report "0 0" which may NOT From 0c868627e617e43a295d8e7d542ec40387853694 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 19 Feb 2020 15:59:53 +0800 Subject: [PATCH 07/13] cpufreq: dt: Allow platform specific intermediate callbacks Platforms may need to implement platform specific get_intermediate and target_intermediate hooks. Update cpufreq-dt driver's platform data to contain those for such platforms. Signed-off-by: Peng Fan Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt.c | 4 ++++ drivers/cpufreq/cpufreq-dt.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index d2b5f062a07b3..26fe8dfb9ce62 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -363,6 +363,10 @@ static int dt_cpufreq_probe(struct platform_device *pdev) dt_cpufreq_driver.resume = data->resume; if (data->suspend) dt_cpufreq_driver.suspend = data->suspend; + if (data->get_intermediate) { + dt_cpufreq_driver.target_intermediate = data->target_intermediate; + dt_cpufreq_driver.get_intermediate = data->get_intermediate; + } } ret = cpufreq_register_driver(&dt_cpufreq_driver); diff --git a/drivers/cpufreq/cpufreq-dt.h b/drivers/cpufreq/cpufreq-dt.h index a5a45b547d0bc..28c8af7ec5ef3 100644 --- a/drivers/cpufreq/cpufreq-dt.h +++ b/drivers/cpufreq/cpufreq-dt.h @@ -14,6 +14,10 @@ struct cpufreq_policy; struct cpufreq_dt_platform_data { bool have_governor_per_policy; + unsigned int (*get_intermediate)(struct cpufreq_policy *policy, + unsigned int index); + int (*target_intermediate)(struct cpufreq_policy *policy, + unsigned int index); int (*suspend)(struct cpufreq_policy *policy); int (*resume)(struct cpufreq_policy *policy); }; From d5a2a6bb27f390f36c899907490d25ce1e83eec5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Mar 2020 00:05:34 +0100 Subject: [PATCH 08/13] cpufreq: intel_pstate: Consolidate policy verification There is still some code duplication between intel_pstate_verify_policy() and intel_cpufreq_verify_policy(), so avoid it by moving the common code into a separate function and calling it from both these places. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index c81e1ff290697..404982d655e24 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2155,15 +2155,19 @@ static void intel_pstate_adjust_policy_max(struct cpudata *cpu, } } -static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) +static void intel_pstate_verify_cpu_policy(struct cpudata *cpu, + struct cpufreq_policy_data *policy) { - struct cpudata *cpu = all_cpu_data[policy->cpu]; - update_turbo_state(); cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, intel_pstate_get_max_freq(cpu)); intel_pstate_adjust_policy_max(cpu, policy); +} + +static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) +{ + intel_pstate_verify_cpu_policy(all_cpu_data[policy->cpu], policy); return 0; } @@ -2268,12 +2272,7 @@ static int intel_cpufreq_verify_policy(struct cpufreq_policy_data *policy) { struct cpudata *cpu = all_cpu_data[policy->cpu]; - update_turbo_state(); - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - intel_pstate_get_max_freq(cpu)); - - intel_pstate_adjust_policy_max(cpu, policy); - + intel_pstate_verify_cpu_policy(cpu, policy); intel_pstate_update_perf_limits(cpu, policy->min, policy->max); return 0; From c1f59a3782ee00a92849cb64e15fa6480620f281 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Mar 2020 17:23:50 -0700 Subject: [PATCH 09/13] Documentation: intel_pstate: update links for references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit URLs for presentation and Intel Software Developer’s Manual are updated as they were using "http" which are gradually replaced by "https". Signed-off-by: Alex Hung Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/pm/intel_pstate.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst index 67e414e34f379..ad392f3aee061 100644 --- a/Documentation/admin-guide/pm/intel_pstate.rst +++ b/Documentation/admin-guide/pm/intel_pstate.rst @@ -734,10 +734,10 @@ References ========== .. [1] Kristen Accardi, *Balancing Power and Performance in the Linux Kernel*, - http://events.linuxfoundation.org/sites/events/files/slides/LinuxConEurope_2015.pdf + https://events.static.linuxfound.org/sites/events/files/slides/LinuxConEurope_2015.pdf .. [2] *Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3: System Programming Guide*, - http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html + https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-system-programming-manual-325384.html .. [3] *Advanced Configuration and Power Interface Specification*, https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf From 3c0897c180c6605ebbdfa1b4badc71e181fc4cdc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Mar 2020 08:13:41 +0100 Subject: [PATCH 10/13] cpufreq: Use scnprintf() for avoiding potential buffer overflow Since snprintf() returns the would-be-output size instead of the actual output size, the succeeding calls may go beyond the given buffer limit. Fix it by replacing with scnprintf(). Signed-off-by: Takashi Iwai Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_stats.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index f9bcf0f3ea304..94d959a8e954e 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -90,35 +90,35 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) if (policy->fast_switch_enabled) return 0; - len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); - len += snprintf(buf + len, PAGE_SIZE - len, " : "); + len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n"); + len += scnprintf(buf + len, PAGE_SIZE - len, " : "); for (i = 0; i < stats->state_num; i++) { if (len >= PAGE_SIZE) break; - len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", + len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", stats->freq_table[i]); } if (len >= PAGE_SIZE) return PAGE_SIZE; - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); for (i = 0; i < stats->state_num; i++) { if (len >= PAGE_SIZE) break; - len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", + len += scnprintf(buf + len, PAGE_SIZE - len, "%9u: ", stats->freq_table[i]); for (j = 0; j < stats->state_num; j++) { if (len >= PAGE_SIZE) break; - len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", + len += scnprintf(buf + len, PAGE_SIZE - len, "%9u ", stats->trans_table[i*stats->max_state+j]); } if (len >= PAGE_SIZE) break; - len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); } if (len >= PAGE_SIZE) { From 74a189ef079090f186e3abd6be295615cd9bf3b1 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 17 Mar 2020 12:38:54 +0800 Subject: [PATCH 11/13] cpufreq: imx6q-cpufreq: Improve the logic of -EPROBE_DEFER handling Improve the -EPROBE_DEFER handling logic to simplify the code. Signed-off-by: Anson Huang Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 285b8e9aa1854..fdb2ffffbd15a 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -400,11 +400,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ret = imx6q_opp_check_speed_grading(cpu_dev); } if (ret) { - if (ret == -EPROBE_DEFER) - goto out_free_opp; - - dev_err(cpu_dev, "failed to read ocotp: %d\n", - ret); + if (ret != -EPROBE_DEFER) + dev_err(cpu_dev, "failed to read ocotp: %d\n", + ret); goto out_free_opp; } From a8811ec764f95a04ba82f6f457e28c5e9e36e36b Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 13 Mar 2020 18:52:13 +0100 Subject: [PATCH 12/13] cpufreq: qcom: Add support for krait based socs In Certain QCOM SoCs like ipq8064, apq8064, msm8960, msm8974 that has KRAIT processors the voltage/current value of each OPP varies based on the silicon variant in use. The required OPP related data is determined based on the efuse value. This is similar to the existing code for kryo cores. So adding support for krait cores here. Signed-off-by: Sricharan R Signed-off-by: Ansuel Smith Signed-off-by: Viresh Kumar --- .../bindings/opp/qcom-nvmem-cpufreq.txt | 3 +- drivers/cpufreq/Kconfig.arm | 2 +- drivers/cpufreq/cpufreq-dt-platdev.c | 5 + drivers/cpufreq/qcom-cpufreq-nvmem.c | 191 ++++++++++++++++-- 4 files changed, 183 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt index 4751029b9b743..64f07417ecfb5 100644 --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt @@ -19,7 +19,8 @@ In 'cpu' nodes: In 'operating-points-v2' table: - compatible: Should be - - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996. + - 'operating-points-v2-kryo-cpu' for apq8096, msm8996, msm8974, + apq8064, ipq8064, msm8960 and ipq8074. Optional properties: -------------------- diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 3858d86cf409c..15c1a12315164 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -128,7 +128,7 @@ config ARM_OMAP2PLUS_CPUFREQ config ARM_QCOM_CPUFREQ_NVMEM tristate "Qualcomm nvmem based CPUFreq" - depends on ARM64 + depends on ARCH_QCOM depends on QCOM_QFPROM depends on QCOM_SMEM select PM_OPP diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index f2ae9cd455c17..cb9db16bea617 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -141,6 +141,11 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "ti,dra7", }, { .compatible = "ti,omap3", }, + { .compatible = "qcom,ipq8064", }, + { .compatible = "qcom,apq8064", }, + { .compatible = "qcom,msm8974", }, + { .compatible = "qcom,msm8960", }, + { } }; diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index f0d2d5035413b..a1b8238872a21 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -49,12 +49,14 @@ struct qcom_cpufreq_drv; struct qcom_cpufreq_match_data { int (*get_version)(struct device *cpu_dev, struct nvmem_cell *speedbin_nvmem, + char **pvs_name, struct qcom_cpufreq_drv *drv); const char **genpd_names; }; struct qcom_cpufreq_drv { - struct opp_table **opp_tables; + struct opp_table **names_opp_tables; + struct opp_table **hw_opp_tables; struct opp_table **genpd_opp_tables; u32 versions; const struct qcom_cpufreq_match_data *data; @@ -62,6 +64,84 @@ struct qcom_cpufreq_drv { static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; +static void get_krait_bin_format_a(struct device *cpu_dev, + int *speed, int *pvs, int *pvs_ver, + struct nvmem_cell *pvs_nvmem, u8 *buf) +{ + u32 pte_efuse; + + pte_efuse = *((u32 *)buf); + + *speed = pte_efuse & 0xf; + if (*speed == 0xf) + *speed = (pte_efuse >> 4) & 0xf; + + if (*speed == 0xf) { + *speed = 0; + dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed); + } else { + dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); + } + + *pvs = (pte_efuse >> 10) & 0x7; + if (*pvs == 0x7) + *pvs = (pte_efuse >> 13) & 0x7; + + if (*pvs == 0x7) { + *pvs = 0; + dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs); + } else { + dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); + } +} + +static void get_krait_bin_format_b(struct device *cpu_dev, + int *speed, int *pvs, int *pvs_ver, + struct nvmem_cell *pvs_nvmem, u8 *buf) +{ + u32 pte_efuse, redundant_sel; + + pte_efuse = *((u32 *)buf); + redundant_sel = (pte_efuse >> 24) & 0x7; + + *pvs_ver = (pte_efuse >> 4) & 0x3; + + switch (redundant_sel) { + case 1: + *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); + *speed = (pte_efuse >> 27) & 0xf; + break; + case 2: + *pvs = (pte_efuse >> 27) & 0xf; + *speed = pte_efuse & 0x7; + break; + default: + /* 4 bits of PVS are in efuse register bits 31, 8-6. */ + *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); + *speed = pte_efuse & 0x7; + } + + /* Check SPEED_BIN_BLOW_STATUS */ + if (pte_efuse & BIT(3)) { + dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); + } else { + dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n"); + *speed = 0; + } + + /* Check PVS_BLOW_STATUS */ + pte_efuse = *(((u32 *)buf) + 4); + pte_efuse &= BIT(21); + if (pte_efuse) { + dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); + } else { + dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n"); + *pvs = 0; + } + + dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver); +} + static enum _msm8996_version qcom_cpufreq_get_msm_id(void) { size_t len; @@ -93,11 +173,13 @@ static enum _msm8996_version qcom_cpufreq_get_msm_id(void) static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, struct nvmem_cell *speedbin_nvmem, + char **pvs_name, struct qcom_cpufreq_drv *drv) { size_t len; u8 *speedbin; enum _msm8996_version msm8996_version; + *pvs_name = NULL; msm8996_version = qcom_cpufreq_get_msm_id(); if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { @@ -125,10 +207,51 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, return 0; } +static int qcom_cpufreq_krait_name_version(struct device *cpu_dev, + struct nvmem_cell *speedbin_nvmem, + char **pvs_name, + struct qcom_cpufreq_drv *drv) +{ + int speed = 0, pvs = 0, pvs_ver = 0; + u8 *speedbin; + size_t len; + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + + switch (len) { + case 4: + get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver, + speedbin_nvmem, speedbin); + break; + case 8: + get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver, + speedbin_nvmem, speedbin); + break; + default: + dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n"); + return -ENODEV; + } + + snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d", + speed, pvs, pvs_ver); + + drv->versions = (1 << speed); + + kfree(speedbin); + return 0; +} + static const struct qcom_cpufreq_match_data match_data_kryo = { .get_version = qcom_cpufreq_kryo_name_version, }; +static const struct qcom_cpufreq_match_data match_data_krait = { + .get_version = qcom_cpufreq_krait_name_version, +}; + static const char *qcs404_genpd_names[] = { "cpr", NULL }; static const struct qcom_cpufreq_match_data match_data_qcs404 = { @@ -141,6 +264,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) struct nvmem_cell *speedbin_nvmem; struct device_node *np; struct device *cpu_dev; + char *pvs_name = "speedXX-pvsXX-vXX"; unsigned cpu; const struct of_device_id *match; int ret; @@ -153,7 +277,7 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) if (!np) return -ENOENT; - ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu"); + ret = of_device_is_compatible(np, "operating-points-v2-qcom-cpu"); if (!ret) { of_node_put(np); return -ENOENT; @@ -181,7 +305,8 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) goto free_drv; } - ret = drv->data->get_version(cpu_dev, speedbin_nvmem, drv); + ret = drv->data->get_version(cpu_dev, + speedbin_nvmem, &pvs_name, drv); if (ret) { nvmem_cell_put(speedbin_nvmem); goto free_drv; @@ -190,12 +315,20 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) } of_node_put(np); - drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables), + drv->names_opp_tables = kcalloc(num_possible_cpus(), + sizeof(*drv->names_opp_tables), GFP_KERNEL); - if (!drv->opp_tables) { + if (!drv->names_opp_tables) { ret = -ENOMEM; goto free_drv; } + drv->hw_opp_tables = kcalloc(num_possible_cpus(), + sizeof(*drv->hw_opp_tables), + GFP_KERNEL); + if (!drv->hw_opp_tables) { + ret = -ENOMEM; + goto free_opp_names; + } drv->genpd_opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->genpd_opp_tables), @@ -213,11 +346,23 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) } if (drv->data->get_version) { - drv->opp_tables[cpu] = - dev_pm_opp_set_supported_hw(cpu_dev, - &drv->versions, 1); - if (IS_ERR(drv->opp_tables[cpu])) { - ret = PTR_ERR(drv->opp_tables[cpu]); + + if (pvs_name) { + drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name( + cpu_dev, + pvs_name); + if (IS_ERR(drv->names_opp_tables[cpu])) { + ret = PTR_ERR(drv->names_opp_tables[cpu]); + dev_err(cpu_dev, "Failed to add OPP name %s\n", + pvs_name); + goto free_opp; + } + } + + drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw( + cpu_dev, &drv->versions, 1); + if (IS_ERR(drv->hw_opp_tables[cpu])) { + ret = PTR_ERR(drv->hw_opp_tables[cpu]); dev_err(cpu_dev, "Failed to set supported hardware\n"); goto free_genpd_opp; @@ -259,11 +404,18 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) kfree(drv->genpd_opp_tables); free_opp: for_each_possible_cpu(cpu) { - if (IS_ERR_OR_NULL(drv->opp_tables[cpu])) + if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu])) + break; + dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]); + } + for_each_possible_cpu(cpu) { + if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu])) break; - dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]); + dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); } - kfree(drv->opp_tables); + kfree(drv->hw_opp_tables); +free_opp_names: + kfree(drv->names_opp_tables); free_drv: kfree(drv); @@ -278,13 +430,16 @@ static int qcom_cpufreq_remove(struct platform_device *pdev) platform_device_unregister(cpufreq_dt_pdev); for_each_possible_cpu(cpu) { - if (drv->opp_tables[cpu]) - dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]); + if (drv->names_opp_tables[cpu]) + dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]); + if (drv->hw_opp_tables[cpu]) + dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); if (drv->genpd_opp_tables[cpu]) dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); } - kfree(drv->opp_tables); + kfree(drv->names_opp_tables); + kfree(drv->hw_opp_tables); kfree(drv->genpd_opp_tables); kfree(drv); @@ -303,6 +458,10 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = { { .compatible = "qcom,apq8096", .data = &match_data_kryo }, { .compatible = "qcom,msm8996", .data = &match_data_kryo }, { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, + { .compatible = "qcom,ipq8064", .data = &match_data_krait }, + { .compatible = "qcom,apq8064", .data = &match_data_krait }, + { .compatible = "qcom,msm8974", .data = &match_data_krait }, + { .compatible = "qcom,msm8960", .data = &match_data_krait }, {}, }; From 5ac54113dd6fed13a65048bf820d73c058c7440d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 25 Mar 2020 16:18:09 +0100 Subject: [PATCH 13/13] cpufreq: intel_pstate: Simplify intel_pstate_cpu_init() The initial policy value set by intel_pstate_cpu_init() depends on whether or not CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is set, but that is not necessary, because the core will set the policy to "performance" in cpufreq_init_policy() if the default governor is "performance" anyway. Accordingly, change intel_pstate_cpu_init() to always set policy to CPUFREQ_POLICY_POWERSAVE initially to provide a valid fallback value to cpufreq_init_policy() in case the default cpufreq governor is neither "powersave" nor "performance". Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 404982d655e24..d2297839374d9 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2247,10 +2247,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) if (ret) return ret; - if (IS_ENABLED(CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE)) - policy->policy = CPUFREQ_POLICY_PERFORMANCE; - else - policy->policy = CPUFREQ_POLICY_POWERSAVE; + /* + * Set the policy to powersave to provide a valid fallback value in case + * the default cpufreq governor is neither powersave nor performance. + */ + policy->policy = CPUFREQ_POLICY_POWERSAVE; return 0; }