From f328584f7bff86858249bb358f1adcecde48388a Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Jun 2019 12:28:15 -0400 Subject: [PATCH 01/22] cpufreq: Add sun50i nvmem based CPU scaling driver For some SoCs, the CPU frequency subset and voltage value of each OPP varies based on the silicon variant in use. The sun50i-cpufreq-nvmem driver reads the efuse value from the SoC to provide the OPP framework with required information. Signed-off-by: Yangtao Li Acked-by: Maxime Ripard Signed-off-by: Viresh Kumar --- MAINTAINERS | 7 + drivers/cpufreq/Kconfig.arm | 12 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/cpufreq-dt-platdev.c | 2 + drivers/cpufreq/sun50i-cpufreq-nvmem.c | 226 +++++++++++++++++++++++++ 5 files changed, 248 insertions(+) create mode 100644 drivers/cpufreq/sun50i-cpufreq-nvmem.c diff --git a/MAINTAINERS b/MAINTAINERS index 783569e3c4b4..d3964379841b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -676,6 +676,13 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/staging/media/allegro-dvt/ +ALLWINNER CPUFREQ DRIVER +M: Yangtao Li +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/opp/sun50i-nvmem-cpufreq.txt +F: drivers/cpufreq/sun50i-cpufreq-nvmem.c + ALLWINNER SECURITY SYSTEM M: Corentin Labbe L: linux-crypto@vger.kernel.org diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 56c31a78c692..70c2b4bea55c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -19,6 +19,18 @@ config ACPI_CPPC_CPUFREQ If in doubt, say N. +config ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM + tristate "Allwinner nvmem based SUN50I CPUFreq driver" + depends on ARCH_SUNXI + depends on NVMEM_SUNXI_SID + select PM_OPP + help + This adds the nvmem based CPUFreq driver for Allwinner + h6 SoC. + + To compile this driver as a module, choose M here: the + module will be called sun50i-cpufreq-nvmem. + config ARM_ARMADA_37XX_CPUFREQ tristate "Armada 37xx CPUFreq support" depends on ARCH_MVEBU && CPUFREQ_DT diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 5a6c70d26c98..7f2d2e1079d4 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_ARM_SCMI_CPUFREQ) += scmi-cpufreq.o obj-$(CONFIG_ARM_SCPI_CPUFREQ) += scpi-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o +obj-$(CONFIG_ARM_ALLWINNER_SUN50I_CPUFREQ_NVMEM) += sun50i-cpufreq-nvmem.o obj-$(CONFIG_ARM_TANGO_CPUFREQ) += tango-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 03dc4244ab00..f9444ddd35ab 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -101,6 +101,8 @@ static const struct of_device_id whitelist[] __initconst = { * platforms using "operating-points-v2" property. */ static const struct of_device_id blacklist[] __initconst = { + { .compatible = "allwinner,sun50i-h6", }, + { .compatible = "calxeda,highbank", }, { .compatible = "calxeda,ecx-2000", }, diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c new file mode 100644 index 000000000000..eca32e443716 --- /dev/null +++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Allwinner CPUFreq nvmem based driver + * + * The sun50i-cpufreq-nvmem driver reads the efuse value from the SoC to + * provide the OPP framework with required information. + * + * Copyright (C) 2019 Yangtao Li + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#define MAX_NAME_LEN 7 + +#define NVMEM_MASK 0x7 +#define NVMEM_SHIFT 5 + +static struct platform_device *cpufreq_dt_pdev, *sun50i_cpufreq_pdev; + +/** + * sun50i_cpufreq_get_efuse() - Parse and return efuse value present on SoC + * @versions: Set to the value parsed from efuse + * + * Returns 0 if success. + */ +static int sun50i_cpufreq_get_efuse(u32 *versions) +{ + struct nvmem_cell *speedbin_nvmem; + struct device_node *np; + struct device *cpu_dev; + u32 *speedbin, efuse_value; + size_t len; + int ret; + + cpu_dev = get_cpu_device(0); + if (!cpu_dev) + return -ENODEV; + + np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); + if (!np) + return -ENOENT; + + ret = of_device_is_compatible(np, + "allwinner,sun50i-h6-operating-points"); + if (!ret) { + of_node_put(np); + return -ENOENT; + } + + speedbin_nvmem = of_nvmem_cell_get(np, NULL); + of_node_put(np); + if (IS_ERR(speedbin_nvmem)) { + if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER) + pr_err("Could not get nvmem cell: %ld\n", + PTR_ERR(speedbin_nvmem)); + return PTR_ERR(speedbin_nvmem); + } + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + nvmem_cell_put(speedbin_nvmem); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + + efuse_value = (*speedbin >> NVMEM_SHIFT) & NVMEM_MASK; + switch (efuse_value) { + case 0b0001: + *versions = 1; + break; + case 0b0011: + *versions = 2; + break; + default: + /* + * For other situations, we treat it as bin0. + * This vf table can be run for any good cpu. + */ + *versions = 0; + break; + } + + kfree(speedbin); + return 0; +}; + +static int sun50i_cpufreq_nvmem_probe(struct platform_device *pdev) +{ + struct opp_table **opp_tables; + char name[MAX_NAME_LEN]; + unsigned int cpu; + u32 speed = 0; + int ret; + + opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), + GFP_KERNEL); + if (!opp_tables) + return -ENOMEM; + + ret = sun50i_cpufreq_get_efuse(&speed); + if (ret) + return ret; + + snprintf(name, MAX_NAME_LEN, "speed%d", speed); + + for_each_possible_cpu(cpu) { + struct device *cpu_dev = get_cpu_device(cpu); + + if (!cpu_dev) { + ret = -ENODEV; + goto free_opp; + } + + opp_tables[cpu] = dev_pm_opp_set_prop_name(cpu_dev, name); + if (IS_ERR(opp_tables[cpu])) { + ret = PTR_ERR(opp_tables[cpu]); + pr_err("Failed to set prop name\n"); + goto free_opp; + } + } + + cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, + NULL, 0); + if (!IS_ERR(cpufreq_dt_pdev)) { + platform_set_drvdata(pdev, opp_tables); + return 0; + } + + ret = PTR_ERR(cpufreq_dt_pdev); + pr_err("Failed to register platform device\n"); + +free_opp: + for_each_possible_cpu(cpu) { + if (IS_ERR_OR_NULL(opp_tables[cpu])) + break; + dev_pm_opp_put_prop_name(opp_tables[cpu]); + } + kfree(opp_tables); + + return ret; +} + +static int sun50i_cpufreq_nvmem_remove(struct platform_device *pdev) +{ + struct opp_table **opp_tables = platform_get_drvdata(pdev); + unsigned int cpu; + + platform_device_unregister(cpufreq_dt_pdev); + + for_each_possible_cpu(cpu) + dev_pm_opp_put_prop_name(opp_tables[cpu]); + + kfree(opp_tables); + + return 0; +} + +static struct platform_driver sun50i_cpufreq_driver = { + .probe = sun50i_cpufreq_nvmem_probe, + .remove = sun50i_cpufreq_nvmem_remove, + .driver = { + .name = "sun50i-cpufreq-nvmem", + }, +}; + +static const struct of_device_id sun50i_cpufreq_match_list[] = { + { .compatible = "allwinner,sun50i-h6" }, + {} +}; + +static const struct of_device_id *sun50i_cpufreq_match_node(void) +{ + const struct of_device_id *match; + struct device_node *np; + + np = of_find_node_by_path("/"); + match = of_match_node(sun50i_cpufreq_match_list, np); + of_node_put(np); + + return match; +} + +/* + * Since the driver depends on nvmem drivers, which may return EPROBE_DEFER, + * all the real activity is done in the probe, which may be defered as well. + * The init here is only registering the driver and the platform device. + */ +static int __init sun50i_cpufreq_init(void) +{ + const struct of_device_id *match; + int ret; + + match = sun50i_cpufreq_match_node(); + if (!match) + return -ENODEV; + + ret = platform_driver_register(&sun50i_cpufreq_driver); + if (unlikely(ret < 0)) + return ret; + + sun50i_cpufreq_pdev = + platform_device_register_simple("sun50i-cpufreq-nvmem", + -1, NULL, 0); + ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev); + if (ret == 0) + return 0; + + platform_driver_unregister(&sun50i_cpufreq_driver); + return ret; +} +module_init(sun50i_cpufreq_init); + +static void __exit sun50i_cpufreq_exit(void) +{ + platform_device_unregister(sun50i_cpufreq_pdev); + platform_driver_unregister(&sun50i_cpufreq_driver); +} +module_exit(sun50i_cpufreq_exit); + +MODULE_DESCRIPTION("Sun50i-h6 cpufreq driver"); +MODULE_LICENSE("GPL v2"); From b880c18336252df4866d07f850ea7872b6e83f0a Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Wed, 12 Jun 2019 12:28:16 -0400 Subject: [PATCH 02/22] dt-bindings: cpufreq: Document allwinner,sun50i-h6-operating-points Allwinner Process Voltage Scaling Tables defines the voltage and frequency value based on the speedbin blown in the efuse combination. The sunxi-cpufreq-nvmem driver reads the efuse value from the SoC to provide the OPP framework with required information. This is used to determine the voltage and frequency value for each OPP of operating-points-v2 table when it is parsed by the OPP framework. The "allwinner,sun50i-h6-operating-points" DT extends the "operating-points-v2" with following parameters: - nvmem-cells (NVMEM area containig the speedbin information) - opp-microvolt-: voltage in micro Volts. At runtime, the platform can pick a and matching opp-microvolt- property. HW: : sun50i-h6 speed0 speed1 speed2 Signed-off-by: Yangtao Li Acked-by: Maxime Ripard Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- .../bindings/opp/sun50i-nvmem-cpufreq.txt | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 Documentation/devicetree/bindings/opp/sun50i-nvmem-cpufreq.txt diff --git a/Documentation/devicetree/bindings/opp/sun50i-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/sun50i-nvmem-cpufreq.txt new file mode 100644 index 000000000000..7deae57a587b --- /dev/null +++ b/Documentation/devicetree/bindings/opp/sun50i-nvmem-cpufreq.txt @@ -0,0 +1,167 @@ +Allwinner Technologies, Inc. NVMEM CPUFreq and OPP bindings +=================================== + +For some SoCs, the CPU frequency subset and voltage value of each OPP +varies based on the silicon variant in use. Allwinner Process Voltage +Scaling Tables defines the voltage and frequency value based on the +speedbin blown in the efuse combination. The sun50i-cpufreq-nvmem driver +reads the efuse value from the SoC to provide the OPP framework with +required information. + +Required properties: +-------------------- +In 'cpus' nodes: +- operating-points-v2: Phandle to the operating-points-v2 table to use. + +In 'operating-points-v2' table: +- compatible: Should be + - 'allwinner,sun50i-h6-operating-points'. +- nvmem-cells: A phandle pointing to a nvmem-cells node representing the + efuse registers that has information about the speedbin + that is used to select the right frequency/voltage value + pair. Please refer the for nvmem-cells bindings + Documentation/devicetree/bindings/nvmem/nvmem.txt and + also examples below. + +In every OPP node: +- opp-microvolt-: Voltage in micro Volts. + At runtime, the platform can pick a and + matching opp-microvolt- property. + [See: opp.txt] + HW: : + sun50i-h6 speed0 speed1 speed2 + +Example 1: +--------- + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <0>; + enable-method = "psci"; + clocks = <&ccu CLK_CPUX>; + clock-latency-ns = <244144>; /* 8 32k periods */ + operating-points-v2 = <&cpu_opp_table>; + #cooling-cells = <2>; + }; + + cpu1: cpu@1 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <1>; + enable-method = "psci"; + clocks = <&ccu CLK_CPUX>; + clock-latency-ns = <244144>; /* 8 32k periods */ + operating-points-v2 = <&cpu_opp_table>; + #cooling-cells = <2>; + }; + + cpu2: cpu@2 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <2>; + enable-method = "psci"; + clocks = <&ccu CLK_CPUX>; + clock-latency-ns = <244144>; /* 8 32k periods */ + operating-points-v2 = <&cpu_opp_table>; + #cooling-cells = <2>; + }; + + cpu3: cpu@3 { + compatible = "arm,cortex-a53"; + device_type = "cpu"; + reg = <3>; + enable-method = "psci"; + clocks = <&ccu CLK_CPUX>; + clock-latency-ns = <244144>; /* 8 32k periods */ + operating-points-v2 = <&cpu_opp_table>; + #cooling-cells = <2>; + }; + }; + + cpu_opp_table: opp_table { + compatible = "allwinner,sun50i-h6-operating-points"; + nvmem-cells = <&speedbin_efuse>; + opp-shared; + + opp@480000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <480000000>; + + opp-microvolt-speed0 = <880000>; + opp-microvolt-speed1 = <820000>; + opp-microvolt-speed2 = <800000>; + }; + + opp@720000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <720000000>; + + opp-microvolt-speed0 = <880000>; + opp-microvolt-speed1 = <820000>; + opp-microvolt-speed2 = <800000>; + }; + + opp@816000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <816000000>; + + opp-microvolt-speed0 = <880000>; + opp-microvolt-speed1 = <820000>; + opp-microvolt-speed2 = <800000>; + }; + + opp@888000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <888000000>; + + opp-microvolt-speed0 = <940000>; + opp-microvolt-speed1 = <820000>; + opp-microvolt-speed2 = <800000>; + }; + + opp@1080000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <1080000000>; + + opp-microvolt-speed0 = <1060000>; + opp-microvolt-speed1 = <880000>; + opp-microvolt-speed2 = <840000>; + }; + + opp@1320000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <1320000000>; + + opp-microvolt-speed0 = <1160000>; + opp-microvolt-speed1 = <940000>; + opp-microvolt-speed2 = <900000>; + }; + + opp@1488000000 { + clock-latency-ns = <244144>; /* 8 32k periods */ + opp-hz = /bits/ 64 <1488000000>; + + opp-microvolt-speed0 = <1160000>; + opp-microvolt-speed1 = <1000000>; + opp-microvolt-speed2 = <960000>; + }; + }; +.... +soc { +.... + sid: sid@3006000 { + compatible = "allwinner,sun50i-h6-sid"; + reg = <0x03006000 0x400>; + #address-cells = <1>; + #size-cells = <1>; + .... + speedbin_efuse: speed@1c { + reg = <0x1c 4>; + }; + }; +}; From 3355c91b79394593ebbb197c8e930a91826f4ff3 Mon Sep 17 00:00:00 2001 From: Hariprasad Kelam Date: Sun, 21 Jul 2019 23:38:15 +0530 Subject: [PATCH 03/22] cpufreq: ap806: Add NULL check after kcalloc Add NULL check after kcalloc. Fix below issue reported by coccicheck ./drivers/cpufreq/armada-8k-cpufreq.c:138:1-12: alloc with no test, possible model on line 151 Signed-off-by: Hariprasad Kelam Signed-off-by: Viresh Kumar --- drivers/cpufreq/armada-8k-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/armada-8k-cpufreq.c b/drivers/cpufreq/armada-8k-cpufreq.c index 988ebc326bdb..39e34f5066d3 100644 --- a/drivers/cpufreq/armada-8k-cpufreq.c +++ b/drivers/cpufreq/armada-8k-cpufreq.c @@ -136,6 +136,8 @@ static int __init armada_8k_cpufreq_init(void) nb_cpus = num_possible_cpus(); freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL); + if (!freq_tables) + return -ENOMEM; cpumask_copy(&cpus, cpu_possible_mask); /* From baea35e4db17a72145c84a401f70d496c8ebf833 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 17 Jul 2019 11:20:17 +0530 Subject: [PATCH 04/22] opp: Not all power-domains are scalable A device may have multiple power-domains and not all of them may be scalable (i.e. support performance states). But dev_pm_opp_attach_genpd() doesn't take that into account currently. Fix that by not verifying the names argument with "power-domain-names" DT property and finding the index into the required-opps array. The names argument will anyway get verified later on when we call dev_pm_domain_attach_by_name(). Fixes: 6319aee10e53 ("opp: Attach genpds to devices from within OPP core") Reported-by: Rajendra Nayak Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index c094d5d20fd7..08b830f84d8c 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1784,12 +1784,15 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * * This helper needs to be called once with a list of all genpd to attach. * Otherwise the original device structure will be used instead by the OPP core. + * + * The order of entries in the names array must match the order in which + * "required-opps" are added in DT. */ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names) { struct opp_table *opp_table; struct device *virt_dev; - int index, ret = -EINVAL; + int index = 0, ret = -EINVAL; const char **name = names; opp_table = dev_pm_opp_get_opp_table(dev); @@ -1815,14 +1818,6 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names goto unlock; while (*name) { - index = of_property_match_string(dev->of_node, - "power-domain-names", *name); - if (index < 0) { - dev_err(dev, "Failed to find power domain: %s (%d)\n", - *name, index); - goto err; - } - if (index >= opp_table->required_opp_count) { dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n", *name, opp_table->required_opp_count, index); @@ -1843,6 +1838,7 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names } opp_table->genpd_virt_devs[index] = virt_dev; + index++; name++; } From 17a8f868ae3e85a173843b1ac65e744e8585bc5a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 8 Jul 2019 11:24:56 +0530 Subject: [PATCH 05/22] opp: Return genpd virtual devices from dev_pm_opp_attach_genpd() The cpufreq drivers don't need to do runtime PM operations on the virtual devices returned by dev_pm_domain_attach_by_name() and so the virtual devices weren't shared with the callers of dev_pm_opp_attach_genpd() earlier. But the IO device drivers would want to do that. This patch updates the prototype of dev_pm_opp_attach_genpd() to accept another argument to return the pointer to the array of genpd virtual devices. Reported-by: Rajendra Nayak Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 6 +++++- include/linux/pm_opp.h | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 08b830f84d8c..bdb435822401 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1771,6 +1771,7 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer * @dev: Consumer device for which the genpd is getting attached. * @names: Null terminated array of pointers containing names of genpd to attach. + * @virt_devs: Pointer to return the array of virtual devices. * * Multiple generic power domains for a device are supported with the help of * virtual genpd devices, which are created for each consumer device - genpd @@ -1788,7 +1789,8 @@ static void _opp_detach_genpd(struct opp_table *opp_table) * The order of entries in the names array must match the order in which * "required-opps" are added in DT. */ -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names) +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, + const char **names, struct device ***virt_devs) { struct opp_table *opp_table; struct device *virt_dev; @@ -1842,6 +1844,8 @@ struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names name++; } + if (virt_devs) + *virt_devs = opp_table->genpd_virt_devs; mutex_unlock(&opp_table->genpd_virt_dev_lock); return opp_table; diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index af5021f27cb7..5bdceca5125d 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -128,7 +128,7 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); -struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names); +struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs); void dev_pm_opp_detach_genpd(struct opp_table *opp_table); int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); @@ -292,7 +292,7 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {} -static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names) +static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names, struct device ***virt_devs) { return ERR_PTR(-ENOTSUPP); } From 71419d84c216cee8da3b19fb843b4242f112cde4 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 25 Jul 2019 12:41:29 +0200 Subject: [PATCH 06/22] opp: Add dev_pm_opp_find_level_exact() Since the performance states in the OPP table are unique, implement a dev_pm_opp_find_level_exact() in order to be able to fetch a specific OPP. Signed-off-by: Niklas Cassel [ Viresh: Updated commit log ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 48 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 8 +++++++ 2 files changed, 56 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index bdb435822401..0ee8c0133d3e 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -401,6 +401,54 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); +/** + * dev_pm_opp_find_level_exact() - search for an exact level + * @dev: device for which we do this operation + * @level: level to search for + * + * Return: Searches for exact match in the opp table and returns pointer to the + * matching opp if found, else returns ERR_PTR in case of error and should + * be handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level) +{ + struct opp_table *opp_table; + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); + + opp_table = _find_opp_table(dev); + if (IS_ERR(opp_table)) { + int r = PTR_ERR(opp_table); + + dev_err(dev, "%s: OPP table not found (%d)\n", __func__, r); + return ERR_PTR(r); + } + + mutex_lock(&opp_table->lock); + + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { + if (temp_opp->level == level) { + opp = temp_opp; + + /* Increment the reference count of OPP */ + dev_pm_opp_get(opp); + break; + } + } + + mutex_unlock(&opp_table->lock); + dev_pm_opp_put_opp_table(opp_table); + + return opp; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_level_exact); + static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 5bdceca5125d..b8197ab014f2 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -96,6 +96,8 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available); +struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level); struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); @@ -200,6 +202,12 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, return ERR_PTR(-ENOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level) +{ + return ERR_PTR(-ENOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { From 8cfda0df3a3020454848f55ed23a781169770c99 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 25 Jul 2019 12:41:30 +0200 Subject: [PATCH 07/22] dt-bindings: opp: Re-organise kryo cpufreq to use it for other nvmem based qcom socs The kryo cpufreq driver reads the nvmem cell and uses that data to populate the opps. There are other qcom cpufreq socs like krait which does similar thing. Except for the interpretation of the read data, rest of the driver is same for both the cases. So pull the common things out for reuse. Signed-off-by: Sricharan R [niklas.cassel@linaro.org: split dt-binding into a separate patch and do not rename the compatible string.] Signed-off-by: Niklas Cassel Reviewed-by: Ilia Lin Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- .../opp/{kryo-cpufreq.txt => qcom-nvmem-cpufreq.txt} | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename Documentation/devicetree/bindings/opp/{kryo-cpufreq.txt => qcom-nvmem-cpufreq.txt} (98%) diff --git a/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt similarity index 98% rename from Documentation/devicetree/bindings/opp/kryo-cpufreq.txt rename to Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt index c2127b96805a..198441e80ba8 100644 --- a/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt @@ -1,13 +1,13 @@ -Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings +Qualcomm Technologies, Inc. NVMEM CPUFreq and OPP bindings =================================== -In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996 -that have KRYO processors, the CPU ferequencies subset and voltage value -of each OPP varies based on the silicon variant in use. +In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996, +the CPU frequencies subset and voltage value of each OPP varies based on +the silicon variant in use. Qualcomm Technologies, Inc. Process Voltage Scaling Tables defines the voltage and frequency value based on the msm-id in SMEM and speedbin blown in the efuse combination. -The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC +The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC to provide the OPP framework with required information (existing HW bitmap). This is used to determine the voltage and frequency value for each OPP of operating-points-v2 table when it is parsed by the OPP framework. From a409906003a2b5418e6e60ac2524948ea80819f2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 25 Jul 2019 12:41:32 +0200 Subject: [PATCH 08/22] dt-bindings: opp: qcom-nvmem: Make speedbin related properties optional Not all Qualcomm platforms need to care about the speedbin efuse, nor the value blown into the speedbin efuse. Therefore, make the nvmem-cells and opp-supported-hw properties optional. Signed-off-by: Niklas Cassel Reviewed-by: Ilia Lin Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt index 198441e80ba8..c5ea8b90e35d 100644 --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt @@ -20,6 +20,10 @@ In 'cpus' nodes: In 'operating-points-v2' table: - compatible: Should be - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996. + +Optional properties: +-------------------- +In 'operating-points-v2' table: - nvmem-cells: A phandle pointing to a nvmem-cells node representing the efuse registers that has information about the speedbin that is used to select the right frequency/voltage From 51dbe89768289c32406f47ad89957b79220c1b63 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 29 Jul 2019 17:49:33 -0500 Subject: [PATCH 09/22] cpufreq: ti-cpufreq: Mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark switch cases where we are expecting to fall through. This patch fixes the following warning (Building: arm): drivers/cpufreq/ti-cpufreq.c: In function ‘dra7_efuse_xlate’: drivers/cpufreq/ti-cpufreq.c:79:20: warning: this statement may fall through [-Wimplicit-fallthrough=] calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; drivers/cpufreq/ti-cpufreq.c:80:2: note: here case DRA7_EFUSE_HAS_OD_MPU_OPP: ^~~~ Signed-off-by: Gustavo A. R. Silva Signed-off-by: Viresh Kumar --- drivers/cpufreq/ti-cpufreq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 2ad1ae17932d..aeaa883a8c9d 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -77,6 +77,7 @@ static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data, case DRA7_EFUSE_HAS_ALL_MPU_OPP: case DRA7_EFUSE_HAS_HIGH_MPU_OPP: calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; + /* Fall through */ case DRA7_EFUSE_HAS_OD_MPU_OPP: calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP; } From 683df830e6bd12e9eb7da1fbdfccc6ac444508ae Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Tue, 6 Aug 2019 11:50:29 +0200 Subject: [PATCH 10/22] cpufreq: mediatek-cpufreq: Add compatible for MT8516 Add the compatible for MT8516 in order to take advantage of the MediaTek CPUFreq driver for Mediatek's MT8516 SoC. Signed-off-by: Fabien Parent Signed-off-by: Viresh Kumar --- drivers/cpufreq/mediatek-cpufreq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index f14f3a85f2f7..10bc06f5dd45 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -535,6 +535,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = { { .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8176", }, + { .compatible = "mediatek,mt8516", }, { } }; From 0eae1e37db8acef1e511cdbc63634cb700188644 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 7 Aug 2019 17:15:43 +0530 Subject: [PATCH 11/22] cpufreq: qcom-hw: Update logic to detect turbo frequency The core count read back from the each domain's look up table serves as an indicator for the onset of the turbo frequency and not accurate representation of number of cores in a paticular domain. Update turbo detection logic accordingly to add support for SM8150 SoCs. Signed-off-by: Sibi Sankar Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 4b0b50403901..c9a9da592f10 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -20,6 +20,7 @@ #define LUT_VOLT GENMASK(11, 0) #define LUT_ROW_SIZE 32 #define CLK_HW_DIV 2 +#define LUT_TURBO_IND 1 /* Register offsets */ #define REG_ENABLE 0x0 @@ -77,9 +78,8 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct cpufreq_policy *policy, void __iomem *base) { - u32 data, src, lval, i, core_count, prev_cc = 0, prev_freq = 0, freq; + u32 data, src, lval, i, core_count, prev_freq = 0, freq; u32 volt; - unsigned int max_cores = cpumask_weight(policy->cpus); struct cpufreq_frequency_table *table; table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); @@ -102,12 +102,12 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, else freq = cpu_hw_rate / 1000; - if (freq != prev_freq && core_count == max_cores) { + if (freq != prev_freq && core_count != LUT_TURBO_IND) { table[i].frequency = freq; dev_pm_opp_add(cpu_dev, freq * 1000, volt); dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i, freq, core_count); - } else { + } else if (core_count == LUT_TURBO_IND) { table[i].frequency = CPUFREQ_ENTRY_INVALID; } @@ -115,14 +115,14 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, * Two of the same frequencies with the same core counts means * end of table */ - if (i > 0 && prev_freq == freq && prev_cc == core_count) { + if (i > 0 && prev_freq == freq) { struct cpufreq_frequency_table *prev = &table[i - 1]; /* * Only treat the last frequency that might be a boost * as the boost frequency */ - if (prev_cc != max_cores) { + if (prev->frequency == CPUFREQ_ENTRY_INVALID) { prev->frequency = prev_freq; prev->flags = CPUFREQ_BOOST_FREQ; dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt); @@ -131,7 +131,6 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, break; } - prev_cc = core_count; prev_freq = freq; } From ada54f35b2270942f45250fa1c553a426b579f9e Mon Sep 17 00:00:00 2001 From: Douglas RAILLARD Date: Thu, 8 Aug 2019 14:18:57 +0100 Subject: [PATCH 12/22] cpufreq: qcom-hw: invoke frequency-invariance setter function Add calls to arch_set_freq_scale() in qcom-cpufreq-hw driver to enable frequency invariance. Signed-off-by: Douglas RAILLARD Reviewed-by: Quentin Perret Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index c9a9da592f10..a9ae2f84a4ef 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -35,9 +35,12 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { void __iomem *perf_state_reg = policy->driver_data; + unsigned long freq = policy->freq_table[index].frequency; writel_relaxed(index, perf_state_reg); + arch_set_freq_scale(policy->related_cpus, freq, + policy->cpuinfo.max_freq); return 0; } @@ -64,6 +67,7 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, { void __iomem *perf_state_reg = policy->driver_data; int index; + unsigned long freq; index = policy->cached_resolved_idx; if (index < 0) @@ -71,7 +75,11 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, writel_relaxed(index, perf_state_reg); - return policy->freq_table[index].frequency; + freq = policy->freq_table[index].frequency; + arch_set_freq_scale(policy->related_cpus, freq, + policy->cpuinfo.max_freq); + + return freq; } static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, From 8ec5035039c743888c5ecd04a680676ef1537fe8 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Sun, 18 Aug 2019 02:32:21 -0400 Subject: [PATCH 13/22] cpufreq: Use imx-cpufreq-dt for i.MX8MN's speed grading Add i.MX8MN to blacklist, so that imx-cpufreq-dt driver can handle speed grading bits just like other i.MX8M SoCs. Signed-off-by: Anson Huang Reviewed-by: Leonard Crestez Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index f9444ddd35ab..6fce11f63403 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -109,6 +109,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "fsl,imx7d", }, { .compatible = "fsl,imx8mq", }, { .compatible = "fsl,imx8mm", }, + { .compatible = "fsl,imx8mn", }, { .compatible = "marvell,armadaxp", }, From 75c000c4bcbe2b0eb82baf90c7dd75c7380cc3fd Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Sun, 18 Aug 2019 02:32:22 -0400 Subject: [PATCH 14/22] cpufreq: imx-cpufreq-dt: Add i.MX8MN support i.MX8MN has different speed grading definition as below, it has 4 bits to define speed grading, add support for it. SPEED_GRADE[3:0] MHz 0000 2300 0001 2200 0010 2100 0011 2000 0100 1900 0101 1800 0110 1700 0111 1600 1000 1500 1001 1400 1010 1300 1011 1200 1100 1100 1101 1000 1110 900 1111 800 Signed-off-by: Anson Huang Reviewed-by: Leonard Crestez Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx-cpufreq-dt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/imx-cpufreq-dt.c b/drivers/cpufreq/imx-cpufreq-dt.c index 4f85f3112784..35db14cf3102 100644 --- a/drivers/cpufreq/imx-cpufreq-dt.c +++ b/drivers/cpufreq/imx-cpufreq-dt.c @@ -16,6 +16,7 @@ #define OCOTP_CFG3_SPEED_GRADE_SHIFT 8 #define OCOTP_CFG3_SPEED_GRADE_MASK (0x3 << 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) @@ -34,7 +35,12 @@ static int imx_cpufreq_dt_probe(struct platform_device *pdev) if (ret) return ret; - speed_grade = (cell_value & OCOTP_CFG3_SPEED_GRADE_MASK) >> OCOTP_CFG3_SPEED_GRADE_SHIFT; + if (of_machine_is_compatible("fsl,imx8mn")) + speed_grade = (cell_value & IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK) + >> OCOTP_CFG3_SPEED_GRADE_SHIFT; + 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; /* From dce0bb84a73bac8a5bf3ad54e922215b61350481 Mon Sep 17 00:00:00 2001 From: "Andrew-sh.Cheng" Date: Tue, 13 Aug 2019 21:31:46 +0800 Subject: [PATCH 15/22] cpufreq: mediatek: change to regulator_get_optional For new mediatek chip mt8183, cci and little cluster share the same buck, so need to modify the attribute of regulator from exclusive to optional Signed-off-by: Andrew-sh.Cheng Signed-off-by: Viresh Kumar --- drivers/cpufreq/mediatek-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 10bc06f5dd45..42bc4c9044f6 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -338,7 +338,7 @@ static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu) goto out_free_resources; } - proc_reg = regulator_get_exclusive(cpu_dev, "proc"); + proc_reg = regulator_get_optional(cpu_dev, "proc"); if (IS_ERR(proc_reg)) { if (PTR_ERR(proc_reg) == -EPROBE_DEFER) pr_warn("proc regulator for cpu%d not ready, retry.\n", From 9176b425bf8ff98fd12bffeea8e39373345cd745 Mon Sep 17 00:00:00 2001 From: "Andrew-sh.Cheng" Date: Tue, 13 Aug 2019 21:31:48 +0800 Subject: [PATCH 16/22] cpufreq: mediatek: Add support for mt8183 Add compatible string for mediatek mt8183 Signed-off-by: Andrew-sh.Cheng [ VK: Fixed rebase conflict ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + drivers/cpufreq/mediatek-cpufreq.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 6fce11f63403..d51bfef286e3 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -120,6 +120,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8176", }, + { .compatible = "mediatek,mt8183", }, { .compatible = "nvidia,tegra124", }, { .compatible = "nvidia,tegra210", }, diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c index 42bc4c9044f6..0c98dd08273d 100644 --- a/drivers/cpufreq/mediatek-cpufreq.c +++ b/drivers/cpufreq/mediatek-cpufreq.c @@ -535,6 +535,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = { { .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8176", }, + { .compatible = "mediatek,mt8183", }, { .compatible = "mediatek,mt8516", }, { } From f6081a73091c0902efb45f47706d35284ebb4e9a Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Fri, 30 Aug 2019 12:29:15 +0200 Subject: [PATCH 17/22] dt-bindings: opp: qcom-nvmem: Support pstates provided by a power domain Some Qualcomm SoCs have support for Core Power Reduction (CPR). On these platforms, we need to attach to the power domain provider providing the performance states, so that the leaky device (the CPU) can configure the performance states (which represent different CPU clock frequencies). Signed-off-by: Niklas Cassel Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- .../bindings/opp/qcom-nvmem-cpufreq.txt | 113 +++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt index c5ea8b90e35d..4751029b9b74 100644 --- a/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt +++ b/Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt @@ -14,7 +14,7 @@ operating-points-v2 table when it is parsed by the OPP framework. Required properties: -------------------- -In 'cpus' nodes: +In 'cpu' nodes: - operating-points-v2: Phandle to the operating-points-v2 table to use. In 'operating-points-v2' table: @@ -23,6 +23,15 @@ In 'operating-points-v2' table: Optional properties: -------------------- +In 'cpu' nodes: +- power-domains: A phandle pointing to the PM domain specifier which provides + the performance states available for active state management. + Please refer to the power-domains bindings + Documentation/devicetree/bindings/power/power_domain.txt + and also examples below. +- power-domain-names: Should be + - 'cpr' for qcs404. + In 'operating-points-v2' table: - nvmem-cells: A phandle pointing to a nvmem-cells node representing the efuse registers that has information about the @@ -682,3 +691,105 @@ soc { }; }; }; + +Example 2: +--------- + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + CPU0: cpu@100 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x100>; + .... + clocks = <&apcs_glb>; + operating-points-v2 = <&cpu_opp_table>; + power-domains = <&cpr>; + power-domain-names = "cpr"; + }; + + CPU1: cpu@101 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x101>; + .... + clocks = <&apcs_glb>; + operating-points-v2 = <&cpu_opp_table>; + power-domains = <&cpr>; + power-domain-names = "cpr"; + }; + + CPU2: cpu@102 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x102>; + .... + clocks = <&apcs_glb>; + operating-points-v2 = <&cpu_opp_table>; + power-domains = <&cpr>; + power-domain-names = "cpr"; + }; + + CPU3: cpu@103 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x103>; + .... + clocks = <&apcs_glb>; + operating-points-v2 = <&cpu_opp_table>; + power-domains = <&cpr>; + power-domain-names = "cpr"; + }; + }; + + cpu_opp_table: cpu-opp-table { + compatible = "operating-points-v2-kryo-cpu"; + opp-shared; + + opp-1094400000 { + opp-hz = /bits/ 64 <1094400000>; + required-opps = <&cpr_opp1>; + }; + opp-1248000000 { + opp-hz = /bits/ 64 <1248000000>; + required-opps = <&cpr_opp2>; + }; + opp-1401600000 { + opp-hz = /bits/ 64 <1401600000>; + required-opps = <&cpr_opp3>; + }; + }; + + cpr_opp_table: cpr-opp-table { + compatible = "operating-points-v2-qcom-level"; + + cpr_opp1: opp1 { + opp-level = <1>; + qcom,opp-fuse-level = <1>; + }; + cpr_opp2: opp2 { + opp-level = <2>; + qcom,opp-fuse-level = <2>; + }; + cpr_opp3: opp3 { + opp-level = <3>; + qcom,opp-fuse-level = <3>; + }; + }; + +.... + +soc { +.... + cpr: power-controller@b018000 { + compatible = "qcom,qcs404-cpr", "qcom,cpr"; + reg = <0x0b018000 0x1000>; + .... + vdd-apc-supply = <&pms405_s3>; + #power-domain-cells = <0>; + operating-points-v2 = <&cpr_opp_table>; + .... + }; +}; From 475a21e0eade5227488d97aebeaa882b16f38ca1 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 19 Aug 2019 12:12:38 +0200 Subject: [PATCH 18/22] dt-bindings: opp: Add qcom-opp bindings with properties needed for CPR Add qcom-opp bindings with properties needed for Core Power Reduction (CPR). CPR is included in a great variety of Qualcomm SoCs, e.g. msm8916 and msm8996. CPR was first introduced in msm8974. Co-developed-by: Jorge Ramirez-Ortiz Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Niklas Cassel Reviewed-by: Stephen Boyd Reviewed-by: Rob Herring Signed-off-by: Viresh Kumar --- .../devicetree/bindings/opp/qcom-opp.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/opp/qcom-opp.txt diff --git a/Documentation/devicetree/bindings/opp/qcom-opp.txt b/Documentation/devicetree/bindings/opp/qcom-opp.txt new file mode 100644 index 000000000000..32eb0793c7e6 --- /dev/null +++ b/Documentation/devicetree/bindings/opp/qcom-opp.txt @@ -0,0 +1,19 @@ +Qualcomm OPP bindings to describe OPP nodes + +The bindings are based on top of the operating-points-v2 bindings +described in Documentation/devicetree/bindings/opp/opp.txt +Additional properties are described below. + +* OPP Table Node + +Required properties: +- compatible: Allow OPPs to express their compatibility. It should be: + "operating-points-v2-qcom-level" + +* OPP Node + +Required properties: +- qcom,opp-fuse-level: A positive value representing the fuse corner/level + associated with this OPP node. Sometimes several corners/levels shares + a certain fuse corner/level. A fuse corner/level contains e.g. ref uV, + min uV, and max uV. From 7d12709544b8b3fb9727a34a664b8380e1e3493a Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 25 Jul 2019 12:41:31 +0200 Subject: [PATCH 19/22] cpufreq: qcom: Re-organise kryo cpufreq to use it for other nvmem based qcom socs The kryo cpufreq driver reads the nvmem cell and uses that data to populate the opps. There are other qcom cpufreq socs like krait which does similar thing. Except for the interpretation of the read data, rest of the driver is same for both the cases. So pull the common things out for reuse. Signed-off-by: Sricharan R [niklas.cassel@linaro.org: split dt-binding into a separate patch and do not rename the compatible string. Update MAINTAINERS file.] Signed-off-by: Niklas Cassel Reviewed-by: Ilia Lin Reviewed-by: Stephen Boyd Signed-off-by: Viresh Kumar --- MAINTAINERS | 4 +- drivers/cpufreq/Kconfig.arm | 4 +- drivers/cpufreq/Makefile | 2 +- ...om-cpufreq-kryo.c => qcom-cpufreq-nvmem.c} | 122 +++++++++++------- 4 files changed, 78 insertions(+), 54 deletions(-) rename drivers/cpufreq/{qcom-cpufreq-kryo.c => qcom-cpufreq-nvmem.c} (69%) diff --git a/MAINTAINERS b/MAINTAINERS index 783569e3c4b4..cc73f908d5f9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13285,8 +13285,8 @@ QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin L: linux-pm@vger.kernel.org S: Maintained -F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt -F: drivers/cpufreq/qcom-cpufreq-kryo.c +F: Documentation/devicetree/bindings/opp/qcom-nvmem-cpufreq.txt +F: drivers/cpufreq/qcom-cpufreq-nvmem.c QUALCOMM EMAC GIGABIT ETHERNET DRIVER M: Timur Tabi diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 56c31a78c692..b1aa485a28dd 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -120,8 +120,8 @@ config ARM_OMAP2PLUS_CPUFREQ depends on ARCH_OMAP2PLUS default ARCH_OMAP2PLUS -config ARM_QCOM_CPUFREQ_KRYO - tristate "Qualcomm Kryo based CPUFreq" +config ARM_QCOM_CPUFREQ_NVMEM + tristate "Qualcomm nvmem based CPUFreq" depends on ARM64 depends on QCOM_QFPROM depends on QCOM_SMEM diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 5a6c70d26c98..8572a918aa75 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -64,7 +64,7 @@ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o -obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_NVMEM) += qcom-cpufreq-nvmem.o obj-$(CONFIG_ARM_RASPBERRYPI_CPUFREQ) += raspberrypi-cpufreq.o obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c similarity index 69% rename from drivers/cpufreq/qcom-cpufreq-kryo.c rename to drivers/cpufreq/qcom-cpufreq-nvmem.c index dd64dcf89c74..fd08120768af 100644 --- a/drivers/cpufreq/qcom-cpufreq-kryo.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -9,7 +9,7 @@ * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables * defines the voltage and frequency value based on the msm-id in SMEM * and speedbin blown in the efuse combination. - * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC + * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC * to provide the OPP framework with required information. * This is used to determine the voltage and frequency value for each OPP of * operating-points-v2 table when it is parsed by the OPP framework. @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -42,9 +43,9 @@ enum _msm8996_version { NUM_OF_MSM8996_VERSIONS, }; -static struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev; +static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; -static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void) +static enum _msm8996_version qcom_cpufreq_get_msm_id(void) { size_t len; u32 *msm_id; @@ -73,28 +74,62 @@ static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void) return version; } -static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) +static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, + struct nvmem_cell *speedbin_nvmem, + u32 *versions) +{ + size_t len; + u8 *speedbin; + enum _msm8996_version msm8996_version; + + msm8996_version = qcom_cpufreq_get_msm_id(); + if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { + dev_err(cpu_dev, "Not Snapdragon 820/821!"); + return -ENODEV; + } + + speedbin = nvmem_cell_read(speedbin_nvmem, &len); + if (IS_ERR(speedbin)) + return PTR_ERR(speedbin); + + switch (msm8996_version) { + case MSM8996_V3: + *versions = 1 << (unsigned int)(*speedbin); + break; + case MSM8996_SG: + *versions = 1 << ((unsigned int)(*speedbin) + 4); + break; + default: + BUG(); + break; + } + + kfree(speedbin); + return 0; +} + +static int qcom_cpufreq_probe(struct platform_device *pdev) { struct opp_table **opp_tables; - enum _msm8996_version msm8996_version; + int (*get_version)(struct device *cpu_dev, + struct nvmem_cell *speedbin_nvmem, + u32 *versions); struct nvmem_cell *speedbin_nvmem; struct device_node *np; struct device *cpu_dev; unsigned cpu; - u8 *speedbin; u32 versions; - size_t len; + const struct of_device_id *match; int ret; cpu_dev = get_cpu_device(0); if (!cpu_dev) return -ENODEV; - msm8996_version = qcom_cpufreq_kryo_get_msm_id(); - if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { - dev_err(cpu_dev, "Not Snapdragon 820/821!"); + match = pdev->dev.platform_data; + get_version = match->data; + if (!get_version) return -ENODEV; - } np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); if (!np) @@ -115,23 +150,10 @@ static int qcom_cpufreq_kryo_probe(struct platform_device *pdev) return PTR_ERR(speedbin_nvmem); } - speedbin = nvmem_cell_read(speedbin_nvmem, &len); + ret = get_version(cpu_dev, speedbin_nvmem, &versions); nvmem_cell_put(speedbin_nvmem); - if (IS_ERR(speedbin)) - return PTR_ERR(speedbin); - - switch (msm8996_version) { - case MSM8996_V3: - versions = 1 << (unsigned int)(*speedbin); - break; - case MSM8996_SG: - versions = 1 << ((unsigned int)(*speedbin) + 4); - break; - default: - BUG(); - break; - } - kfree(speedbin); + if (ret) + return ret; opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL); if (!opp_tables) @@ -174,7 +196,7 @@ free_opp: return ret; } -static int qcom_cpufreq_kryo_remove(struct platform_device *pdev) +static int qcom_cpufreq_remove(struct platform_device *pdev) { struct opp_table **opp_tables = platform_get_drvdata(pdev); unsigned int cpu; @@ -189,18 +211,20 @@ static int qcom_cpufreq_kryo_remove(struct platform_device *pdev) return 0; } -static struct platform_driver qcom_cpufreq_kryo_driver = { - .probe = qcom_cpufreq_kryo_probe, - .remove = qcom_cpufreq_kryo_remove, +static struct platform_driver qcom_cpufreq_driver = { + .probe = qcom_cpufreq_probe, + .remove = qcom_cpufreq_remove, .driver = { - .name = "qcom-cpufreq-kryo", + .name = "qcom-cpufreq-nvmem", }, }; -static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = { - { .compatible = "qcom,apq8096", }, - { .compatible = "qcom,msm8996", }, - {} +static const struct of_device_id qcom_cpufreq_match_list[] __initconst = { + { .compatible = "qcom,apq8096", + .data = qcom_cpufreq_kryo_name_version }, + { .compatible = "qcom,msm8996", + .data = qcom_cpufreq_kryo_name_version }, + {}, }; /* @@ -209,7 +233,7 @@ static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = { * which may be defered as well. The init here is only registering * the driver and the platform device. */ -static int __init qcom_cpufreq_kryo_init(void) +static int __init qcom_cpufreq_init(void) { struct device_node *np = of_find_node_by_path("/"); const struct of_device_id *match; @@ -218,32 +242,32 @@ static int __init qcom_cpufreq_kryo_init(void) if (!np) return -ENODEV; - match = of_match_node(qcom_cpufreq_kryo_match_list, np); + match = of_match_node(qcom_cpufreq_match_list, np); of_node_put(np); if (!match) return -ENODEV; - ret = platform_driver_register(&qcom_cpufreq_kryo_driver); + ret = platform_driver_register(&qcom_cpufreq_driver); if (unlikely(ret < 0)) return ret; - kryo_cpufreq_pdev = platform_device_register_simple( - "qcom-cpufreq-kryo", -1, NULL, 0); - ret = PTR_ERR_OR_ZERO(kryo_cpufreq_pdev); + cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem", + -1, match, sizeof(*match)); + ret = PTR_ERR_OR_ZERO(cpufreq_pdev); if (0 == ret) return 0; - platform_driver_unregister(&qcom_cpufreq_kryo_driver); + platform_driver_unregister(&qcom_cpufreq_driver); return ret; } -module_init(qcom_cpufreq_kryo_init); +module_init(qcom_cpufreq_init); -static void __exit qcom_cpufreq_kryo_exit(void) +static void __exit qcom_cpufreq_exit(void) { - platform_device_unregister(kryo_cpufreq_pdev); - platform_driver_unregister(&qcom_cpufreq_kryo_driver); + platform_device_unregister(cpufreq_pdev); + platform_driver_unregister(&qcom_cpufreq_driver); } -module_exit(qcom_cpufreq_kryo_exit); +module_exit(qcom_cpufreq_exit); -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver"); MODULE_LICENSE("GPL v2"); From 57f2f8b4aa0c6b41a284da82bfa40dc3b2abe9a5 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 25 Jul 2019 12:41:33 +0200 Subject: [PATCH 20/22] cpufreq: qcom: Refactor the driver to make it easier to extend Refactor the driver to make it easier to extend in a later commit. Create a driver struct to collect all common resources, in order to make it easier to free up all common resources. Create a driver match_data struct to make it easier to extend the driver with support for new features that might only be supported on certain SoCs. Co-developed-by: Jorge Ramirez-Ortiz Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Niklas Cassel Reviewed-by: Ilia Lin Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-nvmem.c | 123 +++++++++++++++++---------- 1 file changed, 79 insertions(+), 44 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index fd08120768af..2d798a1685c5 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -43,6 +43,20 @@ enum _msm8996_version { NUM_OF_MSM8996_VERSIONS, }; +struct qcom_cpufreq_drv; + +struct qcom_cpufreq_match_data { + int (*get_version)(struct device *cpu_dev, + struct nvmem_cell *speedbin_nvmem, + struct qcom_cpufreq_drv *drv); +}; + +struct qcom_cpufreq_drv { + struct opp_table **opp_tables; + u32 versions; + const struct qcom_cpufreq_match_data *data; +}; + static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; static enum _msm8996_version qcom_cpufreq_get_msm_id(void) @@ -76,7 +90,7 @@ 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, - u32 *versions) + struct qcom_cpufreq_drv *drv) { size_t len; u8 *speedbin; @@ -94,10 +108,10 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, switch (msm8996_version) { case MSM8996_V3: - *versions = 1 << (unsigned int)(*speedbin); + drv->versions = 1 << (unsigned int)(*speedbin); break; case MSM8996_SG: - *versions = 1 << ((unsigned int)(*speedbin) + 4); + drv->versions = 1 << ((unsigned int)(*speedbin) + 4); break; default: BUG(); @@ -108,17 +122,17 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, return 0; } +static const struct qcom_cpufreq_match_data match_data_kryo = { + .get_version = qcom_cpufreq_kryo_name_version, +}; + static int qcom_cpufreq_probe(struct platform_device *pdev) { - struct opp_table **opp_tables; - int (*get_version)(struct device *cpu_dev, - struct nvmem_cell *speedbin_nvmem, - u32 *versions); + struct qcom_cpufreq_drv *drv; struct nvmem_cell *speedbin_nvmem; struct device_node *np; struct device *cpu_dev; unsigned cpu; - u32 versions; const struct of_device_id *match; int ret; @@ -126,11 +140,6 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) if (!cpu_dev) return -ENODEV; - match = pdev->dev.platform_data; - get_version = match->data; - if (!get_version) - return -ENODEV; - np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); if (!np) return -ENOENT; @@ -141,23 +150,43 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) return -ENOENT; } - speedbin_nvmem = of_nvmem_cell_get(np, NULL); - of_node_put(np); - if (IS_ERR(speedbin_nvmem)) { - if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER) - dev_err(cpu_dev, "Could not get nvmem cell: %ld\n", - PTR_ERR(speedbin_nvmem)); - return PTR_ERR(speedbin_nvmem); + drv = kzalloc(sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + match = pdev->dev.platform_data; + drv->data = match->data; + if (!drv->data) { + ret = -ENODEV; + goto free_drv; } - ret = get_version(cpu_dev, speedbin_nvmem, &versions); - nvmem_cell_put(speedbin_nvmem); - if (ret) - return ret; + if (drv->data->get_version) { + speedbin_nvmem = of_nvmem_cell_get(np, NULL); + if (IS_ERR(speedbin_nvmem)) { + if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER) + dev_err(cpu_dev, + "Could not get nvmem cell: %ld\n", + PTR_ERR(speedbin_nvmem)); + ret = PTR_ERR(speedbin_nvmem); + goto free_drv; + } - opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL); - if (!opp_tables) - return -ENOMEM; + ret = drv->data->get_version(cpu_dev, speedbin_nvmem, drv); + if (ret) { + nvmem_cell_put(speedbin_nvmem); + goto free_drv; + } + nvmem_cell_put(speedbin_nvmem); + } + of_node_put(np); + + drv->opp_tables = kcalloc(num_possible_cpus(), sizeof(*drv->opp_tables), + GFP_KERNEL); + if (!drv->opp_tables) { + ret = -ENOMEM; + goto free_drv; + } for_each_possible_cpu(cpu) { cpu_dev = get_cpu_device(cpu); @@ -166,19 +195,23 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) goto free_opp; } - opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev, - &versions, 1); - if (IS_ERR(opp_tables[cpu])) { - ret = PTR_ERR(opp_tables[cpu]); - dev_err(cpu_dev, "Failed to set supported hardware\n"); - goto free_opp; + 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]); + dev_err(cpu_dev, + "Failed to set supported hardware\n"); + goto free_opp; + } } } cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0); if (!IS_ERR(cpufreq_dt_pdev)) { - platform_set_drvdata(pdev, opp_tables); + platform_set_drvdata(pdev, drv); return 0; } @@ -187,26 +220,30 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) free_opp: for_each_possible_cpu(cpu) { - if (IS_ERR_OR_NULL(opp_tables[cpu])) + if (IS_ERR_OR_NULL(drv->opp_tables[cpu])) break; - dev_pm_opp_put_supported_hw(opp_tables[cpu]); + dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]); } - kfree(opp_tables); + kfree(drv->opp_tables); +free_drv: + kfree(drv); return ret; } static int qcom_cpufreq_remove(struct platform_device *pdev) { - struct opp_table **opp_tables = platform_get_drvdata(pdev); + struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev); unsigned int cpu; platform_device_unregister(cpufreq_dt_pdev); for_each_possible_cpu(cpu) - dev_pm_opp_put_supported_hw(opp_tables[cpu]); + if (drv->opp_tables[cpu]) + dev_pm_opp_put_supported_hw(drv->opp_tables[cpu]); - kfree(opp_tables); + kfree(drv->opp_tables); + kfree(drv); return 0; } @@ -220,10 +257,8 @@ static struct platform_driver qcom_cpufreq_driver = { }; static const struct of_device_id qcom_cpufreq_match_list[] __initconst = { - { .compatible = "qcom,apq8096", - .data = qcom_cpufreq_kryo_name_version }, - { .compatible = "qcom,msm8996", - .data = qcom_cpufreq_kryo_name_version }, + { .compatible = "qcom,apq8096", .data = &match_data_kryo }, + { .compatible = "qcom,msm8996", .data = &match_data_kryo }, {}, }; From 1cb8339ca225601610b11c7a47e69faa48943077 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 25 Jul 2019 12:41:35 +0200 Subject: [PATCH 21/22] cpufreq: qcom: Add support for qcs404 on nvmem driver Add support for qcs404 on nvmem driver. The qcs404 SoC has support for Core Power Reduction (CPR), which is implemented as a power domain provider, therefore add optional support in this driver to attach to a genpd power domain. Co-developed-by: Jorge Ramirez-Ortiz Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Niklas Cassel Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-nvmem.c | 50 ++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index 2d798a1685c5..f0d2d5035413 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -49,10 +50,12 @@ struct qcom_cpufreq_match_data { int (*get_version)(struct device *cpu_dev, struct nvmem_cell *speedbin_nvmem, struct qcom_cpufreq_drv *drv); + const char **genpd_names; }; struct qcom_cpufreq_drv { struct opp_table **opp_tables; + struct opp_table **genpd_opp_tables; u32 versions; const struct qcom_cpufreq_match_data *data; }; @@ -126,6 +129,12 @@ static const struct qcom_cpufreq_match_data match_data_kryo = { .get_version = qcom_cpufreq_kryo_name_version, }; +static const char *qcs404_genpd_names[] = { "cpr", NULL }; + +static const struct qcom_cpufreq_match_data match_data_qcs404 = { + .genpd_names = qcs404_genpd_names, +}; + static int qcom_cpufreq_probe(struct platform_device *pdev) { struct qcom_cpufreq_drv *drv; @@ -188,11 +197,19 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) goto free_drv; } + drv->genpd_opp_tables = kcalloc(num_possible_cpus(), + sizeof(*drv->genpd_opp_tables), + GFP_KERNEL); + if (!drv->genpd_opp_tables) { + ret = -ENOMEM; + goto free_opp; + } + for_each_possible_cpu(cpu) { cpu_dev = get_cpu_device(cpu); if (NULL == cpu_dev) { ret = -ENODEV; - goto free_opp; + goto free_genpd_opp; } if (drv->data->get_version) { @@ -203,7 +220,22 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) ret = PTR_ERR(drv->opp_tables[cpu]); dev_err(cpu_dev, "Failed to set supported hardware\n"); - goto free_opp; + goto free_genpd_opp; + } + } + + if (drv->data->genpd_names) { + drv->genpd_opp_tables[cpu] = + dev_pm_opp_attach_genpd(cpu_dev, + drv->data->genpd_names, + NULL); + if (IS_ERR(drv->genpd_opp_tables[cpu])) { + ret = PTR_ERR(drv->genpd_opp_tables[cpu]); + if (ret != -EPROBE_DEFER) + dev_err(cpu_dev, + "Could not attach to pm_domain: %d\n", + ret); + goto free_genpd_opp; } } } @@ -218,6 +250,13 @@ static int qcom_cpufreq_probe(struct platform_device *pdev) ret = PTR_ERR(cpufreq_dt_pdev); dev_err(cpu_dev, "Failed to register platform device\n"); +free_genpd_opp: + for_each_possible_cpu(cpu) { + if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu])) + break; + dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); + } + kfree(drv->genpd_opp_tables); free_opp: for_each_possible_cpu(cpu) { if (IS_ERR_OR_NULL(drv->opp_tables[cpu])) @@ -238,11 +277,15 @@ static int qcom_cpufreq_remove(struct platform_device *pdev) platform_device_unregister(cpufreq_dt_pdev); - for_each_possible_cpu(cpu) + for_each_possible_cpu(cpu) { if (drv->opp_tables[cpu]) dev_pm_opp_put_supported_hw(drv->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->genpd_opp_tables); kfree(drv); return 0; @@ -259,6 +302,7 @@ static struct platform_driver qcom_cpufreq_driver = { 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 }, {}, }; From 248b5f297cf809b6a61e38db22049011f619518b Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 25 Jul 2019 12:41:36 +0200 Subject: [PATCH 22/22] cpufreq: Add qcs404 to cpufreq-dt-platdev blacklist Add qcs404 to cpufreq-dt-platdev blacklist. Signed-off-by: Jorge Ramirez-Ortiz Co-developed-by: Niklas Cassel Signed-off-by: Niklas Cassel Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index 03dc4244ab00..ec6ef996e637 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -123,6 +123,7 @@ static const struct of_device_id blacklist[] __initconst = { { .compatible = "qcom,apq8096", }, { .compatible = "qcom,msm8996", }, + { .compatible = "qcom,qcs404", }, { .compatible = "st,stih407", }, { .compatible = "st,stih410", },