Re: [v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Hi Taniya, Thank you for the patch! Yet something to improve: [auto build test ERROR on pm/linux-next] [also build test ERROR on v4.17-rc5 next-20180517] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Taniya-Das/Add-support-for-QCOM-cpufreq-FW-driver/20180519-050902 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next config: arm-allmodconfig (attached as .config) compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm All errors (new ones prefixed by >>): drivers/cpufreq/qcom-cpufreq-fw.c: In function 'qcom_cpufreq_fw_cpu_init': >> drivers/cpufreq/qcom-cpufreq-fw.c:77:8: error: 'struct cpufreq_policy' has >> no member named 'table' policy->table = c->table; ^~ drivers/cpufreq/qcom-cpufreq-fw.c: In function 'qcom_cpu_resources_init': >> drivers/cpufreq/qcom-cpufreq-fw.c:187:37: error: passing argument 1 of >> 'platform_get_resource_byname' from incompatible pointer type >> [-Werror=incompatible-pointer-types] res = platform_get_resource_byname(dev, IORESOURCE_MEM, "en_base"); ^~~ In file included from include/linux/of_device.h:6:0, from include/linux/of_platform.h:12, from drivers/cpufreq/qcom-cpufreq-fw.c:11: include/linux/platform_device.h:56:25: note: expected 'struct platform_device *' but argument is of type 'struct device *' extern struct resource *platform_get_resource_byname(struct platform_device *, ^~~~ >> drivers/cpufreq/qcom-cpufreq-fw.c:187:6: error: incompatible types when >> assigning to type 'struct resource' from type 'struct resource *' res = platform_get_resource_byname(dev, IORESOURCE_MEM, "en_base"); ^ >> drivers/cpufreq/qcom-cpufreq-fw.c:188:6: error: wrong type argument to unary >> exclamation mark if (!res) { ^ >> drivers/cpufreq/qcom-cpufreq-fw.c:193:33: error: invalid type argument of >> '->' (have 'struct resource') en_base = devm_ioremap(dev, res->start, resource_size(res)); ^~ >> drivers/cpufreq/qcom-cpufreq-fw.c:193:56: error: incompatible type for >> argument 1 of 'resource_size' en_base = devm_ioremap(dev, res->start, resource_size(res)); ^~~ In file included from include/linux/of_address.h:4:0, from drivers/cpufreq/qcom-cpufreq-fw.c:10: include/linux/ioport.h:196:31: note: expected 'const struct resource *' but argument is of type 'struct resource' static inline resource_size_t resource_size(const struct resource *res) ^ cc1: some warnings being treated as errors vim +77 drivers/cpufreq/qcom-cpufreq-fw.c > 11 #include 12 13 #define INIT_RATE 3UL 14 #define XO_RATE 1920UL 15 #define LUT_MAX_ENTRIES 40U 16 #define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16) 17 #define LUT_ROW_SIZE32 18 19 struct cpufreq_qcom { 20 struct cpufreq_frequency_table *table; 21 struct device *dev; 22 void __iomem *perf_base; 23 void __iomem *lut_base; 24 cpumask_t related_cpus; 25 unsigned int max_cores; 26 }; 27 28 static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS]; 29 30 static int 31 qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int index) 32 { 33 struct cpufreq_qcom *c = policy->driver_data; 34 35 if (index >= LUT_MAX_ENTRIES) { 36 dev_err(c->dev, 37 "Passing an index (%u) that's greater than max (%d)\n", 38 index, LUT_MAX_ENTRIES - 1); 39 return -EINVAL; 40 } 41 42 writel_relaxed(index, c->perf_base); 43 44 /* Make sure the write goes through before proceeding */ 45 mb(); 46 return 0; 47 } 48 49 static unsigned int qcom_cpufreq_fw_get(unsigned int cpu) 50 { 51 struct cpufreq_qcom *c; 52 unsigned int index; 53 54 c = qcom_freq_domain_map[cpu]; 55 if (!c) 56 return -ENODEV; 57 58 index = readl_relaxed(c->perf_base); 59 index = min(index, LUT_MAX_ENTRIES - 1); 60 61 return c->t
Re: [v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
On 05/17/2018 03:14 AM, Viresh Kumar wrote: On 17-05-18, 15:00, Taniya Das wrote: The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for hanging the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware. Signed-off-by: Taniya Das --- ## diff --git a/drivers/cpufreq/qcom-cpufreq-fw.c b/drivers/cpufreq/qcom-cpufreq-fw.c + +static int qcom_read_lut(struct platform_device *pdev, + struct cpufreq_qcom *c) +{ + struct device *dev = &pdev->dev; + u32 data, src, lval, i, core_count, prev_cc = 0; + + c->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1, + sizeof(*c->table), GFP_KERNEL); + if (!c->table) + return -ENOMEM; + + for (i = 0; i < LUT_MAX_ENTRIES; i++) { + data = readl_relaxed(c->lut_base + i * LUT_ROW_SIZE); + src = ((data & GENMASK(31, 30)) >> 30); + lval = (data & GENMASK(7, 0)); + core_count = CORE_COUNT_VAL(data); Why do you need this here ? And why do below in case this doesn't match max-cores count ? This is how we detect boost frequencies. + + if (!src) + c->table[i].frequency = INIT_RATE / 1000; + else + c->table[i].frequency = XO_RATE * lval / 1000; + + c->table[i].driver_data = c->table[i].frequency; + + dev_dbg(dev, "index=%d freq=%d, core_count %d\n", + i, c->table[i].frequency, core_count); + + if (core_count != c->max_cores) + c->table[i].frequency = CPUFREQ_ENTRY_INVALID; The FW might has some frequencies marked as "boost frequencies" when there are higher non-boost frequencies. So, we mark them as invalid. + + /* +* Two of the same frequencies with the same core counts means +* end of table. +*/ + if (i > 0 && c->table[i - 1].driver_data == + c->table[i].driver_data + && prev_cc == core_count) { + struct cpufreq_frequency_table *prev = &c->table[i - 1]; + + if (prev->frequency == CPUFREQ_ENTRY_INVALID) { + prev->flags = CPUFREQ_BOOST_FREQ; + prev->frequency = prev->driver_data; + } + + break; + } + prev_cc = core_count; + } + c->table[i].frequency = CPUFREQ_TABLE_END; + + return 0; +} + +static int qcom_get_related_cpus(struct device_node *np, struct cpumask *m) +{ + struct device_node *dev_phandle; + struct device *cpu_dev; + int cpu, i = 0, ret = -ENOENT; + + dev_phandle = of_parse_phandle(np, "qcom,cpulist", i++); TBH, I am not a great fan of the CPU phandle list you have created here. Lets see what Rob has to say on this. Neither do we, but this is the only real way of mapping the logical CPU numbers to the real CPUs in HW that belong to the same freq domain. Because boot CPU is always going to be CPU0 if I'm not mistaken. + while (dev_phandle) { + for_each_possible_cpu(cpu) { + cpu_dev = get_cpu_device(cpu); + if (cpu_dev && cpu_dev->of_node == dev_phandle) { + cpumask_set_cpu(cpu, m); + ret = 0; Maybe just remove this line ... + break; + } + } + dev_phandle = of_parse_phandle(np, "qcom,cpulist", i++); + } + + return ret; -- Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
Hi Viresh, Thanks for the review comments, I have already fixed the resource comment in the v1 series which I sent across. I will fix the rest of the comments and send it for review. On 5/17/2018 3:44 PM, Viresh Kumar wrote: On 17-05-18, 15:00, Taniya Das wrote: The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for hanging the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware. Signed-off-by: Taniya Das --- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 318 ++ 3 files changed, 328 insertions(+) create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 96b35b8..a50aa6e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ARM_QCOM_CPUFREQ_FW + tristate "QCOM CPUFreq FW driver" + help +Support for the CPUFreq FW driver. +The CPUfreq FW preset in some QCOM chipsets offloads the steps +necessary for changing the frequency of CPUs. The driver +implements the cpufreq driver interface for this firmware. +Say Y if you want to support CPUFreq FW. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 8d24ade..a3edbce 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ)+= tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ)+= tegra186-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ)+= vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o ## diff --git a/drivers/cpufreq/qcom-cpufreq-fw.c b/drivers/cpufreq/qcom-cpufreq-fw.c new file mode 100644 index 000..67996d5 --- /dev/null +++ b/drivers/cpufreq/qcom-cpufreq-fw.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define INIT_RATE 3UL +#define XO_RATE1920UL +#define LUT_MAX_ENTRIES40U +#define CORE_COUNT_VAL(val)((val & GENMASK(18, 16)) >> 16) +#define LUT_ROW_SIZE 32 + +struct cpufreq_qcom { + struct cpufreq_frequency_table *table; + struct device *dev; + void __iomem *perf_base; + void __iomem *lut_base; + cpumask_t related_cpus; + unsigned int max_cores; +}; + +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS]; + +static int +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int index) +{ + struct cpufreq_qcom *c = policy->driver_data; + + if (index >= LUT_MAX_ENTRIES) { + dev_err(c->dev, + "Passing an index (%u) that's greater than max (%d)\n", Alignment issues here. Run checkpatch --strict. + index, LUT_MAX_ENTRIES - 1); + return -EINVAL; + } + + writel_relaxed(index, c->perf_base); + + /* Make sure the write goes through before proceeding */ + mb(); + return 0; +} + +static unsigned int qcom_cpufreq_fw_get(unsigned int cpu) +{ + struct cpufreq_qcom *c; + unsigned int index; + + c = qcom_freq_domain_map[cpu]; + if (!c) + return -ENODEV; + + index = readl_relaxed(c->perf_base); + index = min(index, LUT_MAX_ENTRIES - 1); + + return c->table[index].frequency; +} + +static int qcom_cpufreq_fw_cpu_init(struct cpufreq_policy *policy) +{ + struct cpufreq_qcom *c; + int ret; + + c = qcom_freq_domain_map[policy->cpu]; + if (!c) { + pr_err("No scaling support for CPU%d\n", policy->cpu); + return -ENODEV; + } + + cpumask_copy(policy->cpus, &c->related_cpus); + + policy->table = c->table; + policy->driver_data = c; + + return ret; +} + +static struct freq_attr *qcom_cpufreq_fw_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, + NULL +}; + +static struct cpufreq_driver cpufreq_qcom_fw_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qcom_cpufreq_fw_target_index, + .get= qcom_cpufreq_fw_get, + .init = qcom_cpufreq_fw_cpu_init, + .name
Re: [v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
On Thu, May 17, 2018 at 12:30 PM, Taniya Das wrote: > The CPUfreq FW present in some QCOM chipsets offloads the steps necessary > for hanging the frequency of CPUs. The driver implements the cpufreq driver s/hanging/changing :-) > interface for this firmware. > > Signed-off-by: Taniya Das > --- > drivers/cpufreq/Kconfig.arm | 9 ++ > drivers/cpufreq/Makefile | 1 + > drivers/cpufreq/qcom-cpufreq-fw.c | 318 > ++ > 3 files changed, 328 insertions(+) > create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c > > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm > index 96b35b8..a50aa6e 100644 > --- a/drivers/cpufreq/Kconfig.arm > +++ b/drivers/cpufreq/Kconfig.arm > @@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ > This add the CPUFreq driver support for Intel PXA2xx SOCs. > > If in doubt, say N. > + > +config ARM_QCOM_CPUFREQ_FW > + tristate "QCOM CPUFreq FW driver" > + help > +Support for the CPUFreq FW driver. > +The CPUfreq FW preset in some QCOM chipsets offloads the steps > +necessary for changing the frequency of CPUs. The driver > +implements the cpufreq driver interface for this firmware. > +Say Y if you want to support CPUFreq FW. > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile > index 8d24ade..a3edbce 100644 > --- a/drivers/cpufreq/Makefile > +++ b/drivers/cpufreq/Makefile > @@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ)+= tegra124-cpufreq.o > obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o > obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o > obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o > +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o > > > > ## > diff --git a/drivers/cpufreq/qcom-cpufreq-fw.c > b/drivers/cpufreq/qcom-cpufreq-fw.c > new file mode 100644 > index 000..67996d5 > --- /dev/null > +++ b/drivers/cpufreq/qcom-cpufreq-fw.c > @@ -0,0 +1,318 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018, The Linux Foundation. All rights reserved. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define INIT_RATE 3UL > +#define XO_RATE1920UL > +#define LUT_MAX_ENTRIES40U > +#define CORE_COUNT_VAL(val)((val & GENMASK(18, 16)) >> 16) > +#define LUT_ROW_SIZE 32 > + > +struct cpufreq_qcom { > + struct cpufreq_frequency_table *table; > + struct device *dev; > + void __iomem *perf_base; > + void __iomem *lut_base; > + cpumask_t related_cpus; > + unsigned int max_cores; > +}; > + > +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS]; > + > +static int > +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int > index) > +{ > + struct cpufreq_qcom *c = policy->driver_data; > + > + if (index >= LUT_MAX_ENTRIES) { > + dev_err(c->dev, > + "Passing an index (%u) that's greater than max (%d)\n", > + index, LUT_MAX_ENTRIES - 1); > + return -EINVAL; > + } > + > + writel_relaxed(index, c->perf_base); > + > + /* Make sure the write goes through before proceeding */ > + mb(); > + return 0; > +} > + > +static unsigned int qcom_cpufreq_fw_get(unsigned int cpu) > +{ > + struct cpufreq_qcom *c; > + unsigned int index; > + > + c = qcom_freq_domain_map[cpu]; > + if (!c) > + return -ENODEV; > + > + index = readl_relaxed(c->perf_base); > + index = min(index, LUT_MAX_ENTRIES - 1); > + > + return c->table[index].frequency; > +} > + > +static int qcom_cpufreq_fw_cpu_init(struct cpufreq_policy *policy) > +{ > + struct cpufreq_qcom *c; > + int ret; > + > + c = qcom_freq_domain_map[policy->cpu]; > + if (!c) { > + pr_err("No scaling support for CPU%d\n", policy->cpu); > + return -ENODEV; > + } > + > + cpumask_copy(policy->cpus, &c->related_cpus); > + > + policy->table = c->table; > + policy->driver_data = c; > + > + return ret; > +} > + > +static struct freq_attr *qcom_cpufreq_fw_attr[] = { > + &cpufreq_freq_attr_scaling_available_freqs, > + &cpufreq_freq_attr_scaling_boost_freqs, > + NULL > +}; > + > +static struct cpufreq_driver cpufreq_qcom_fw_driver = { > + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | > + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, > + .verify = cpufreq_generic_frequency_table_verify, > + .target_index = qcom_cpufreq_fw_target_index, > + .get= qcom_cpufreq_fw_get, > + .init = qcom_cpufr
Re: [v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
On 17-05-18, 15:00, Taniya Das wrote: > The CPUfreq FW present in some QCOM chipsets offloads the steps necessary > for hanging the frequency of CPUs. The driver implements the cpufreq driver > interface for this firmware. > > Signed-off-by: Taniya Das > --- > drivers/cpufreq/Kconfig.arm | 9 ++ > drivers/cpufreq/Makefile | 1 + > drivers/cpufreq/qcom-cpufreq-fw.c | 318 > ++ > 3 files changed, 328 insertions(+) > create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c > > diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm > index 96b35b8..a50aa6e 100644 > --- a/drivers/cpufreq/Kconfig.arm > +++ b/drivers/cpufreq/Kconfig.arm > @@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ > This add the CPUFreq driver support for Intel PXA2xx SOCs. > > If in doubt, say N. > + > +config ARM_QCOM_CPUFREQ_FW > + tristate "QCOM CPUFreq FW driver" > + help > + Support for the CPUFreq FW driver. > + The CPUfreq FW preset in some QCOM chipsets offloads the steps > + necessary for changing the frequency of CPUs. The driver > + implements the cpufreq driver interface for this firmware. > + Say Y if you want to support CPUFreq FW. > diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile > index 8d24ade..a3edbce 100644 > --- a/drivers/cpufreq/Makefile > +++ b/drivers/cpufreq/Makefile > @@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o > obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o > obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o > obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o > +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW)+= qcom-cpufreq-fw.o > > > > ## > diff --git a/drivers/cpufreq/qcom-cpufreq-fw.c > b/drivers/cpufreq/qcom-cpufreq-fw.c > new file mode 100644 > index 000..67996d5 > --- /dev/null > +++ b/drivers/cpufreq/qcom-cpufreq-fw.c > @@ -0,0 +1,318 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2018, The Linux Foundation. All rights reserved. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define INIT_RATE3UL > +#define XO_RATE 1920UL > +#define LUT_MAX_ENTRIES 40U > +#define CORE_COUNT_VAL(val) ((val & GENMASK(18, 16)) >> 16) > +#define LUT_ROW_SIZE 32 > + > +struct cpufreq_qcom { > + struct cpufreq_frequency_table *table; > + struct device *dev; > + void __iomem *perf_base; > + void __iomem *lut_base; > + cpumask_t related_cpus; > + unsigned int max_cores; > +}; > + > +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS]; > + > +static int > +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int > index) > +{ > + struct cpufreq_qcom *c = policy->driver_data; > + > + if (index >= LUT_MAX_ENTRIES) { > + dev_err(c->dev, > + "Passing an index (%u) that's greater than max (%d)\n", Alignment issues here. Run checkpatch --strict. > + index, LUT_MAX_ENTRIES - 1); > + return -EINVAL; > + } > + > + writel_relaxed(index, c->perf_base); > + > + /* Make sure the write goes through before proceeding */ > + mb(); > + return 0; > +} > + > +static unsigned int qcom_cpufreq_fw_get(unsigned int cpu) > +{ > + struct cpufreq_qcom *c; > + unsigned int index; > + > + c = qcom_freq_domain_map[cpu]; > + if (!c) > + return -ENODEV; > + > + index = readl_relaxed(c->perf_base); > + index = min(index, LUT_MAX_ENTRIES - 1); > + > + return c->table[index].frequency; > +} > + > +static int qcom_cpufreq_fw_cpu_init(struct cpufreq_policy *policy) > +{ > + struct cpufreq_qcom *c; > + int ret; > + > + c = qcom_freq_domain_map[policy->cpu]; > + if (!c) { > + pr_err("No scaling support for CPU%d\n", policy->cpu); > + return -ENODEV; > + } > + > + cpumask_copy(policy->cpus, &c->related_cpus); > + > + policy->table = c->table; > + policy->driver_data = c; > + > + return ret; > +} > + > +static struct freq_attr *qcom_cpufreq_fw_attr[] = { > + &cpufreq_freq_attr_scaling_available_freqs, > + &cpufreq_freq_attr_scaling_boost_freqs, > + NULL > +}; > + > +static struct cpufreq_driver cpufreq_qcom_fw_driver = { > + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | > + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, > + .verify = cpufreq_generic_frequency_table_verify, > + .target_index = qcom_cpufreq_fw_target_index, > + .get= qcom_cpufreq_fw_get, > + .init = qcom_cpufreq_fw_cpu_init, > + .name = "qcom-cpufreq-fw", > + .attr = qcom_cpufreq_fw_at
[v0 2/2] cpufreq: qcom-fw: Add support for QCOM cpufreq FW driver
The CPUfreq FW present in some QCOM chipsets offloads the steps necessary for hanging the frequency of CPUs. The driver implements the cpufreq driver interface for this firmware. Signed-off-by: Taniya Das --- drivers/cpufreq/Kconfig.arm | 9 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/qcom-cpufreq-fw.c | 318 ++ 3 files changed, 328 insertions(+) create mode 100644 drivers/cpufreq/qcom-cpufreq-fw.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 96b35b8..a50aa6e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -301,3 +301,12 @@ config ARM_PXA2xx_CPUFREQ This add the CPUFreq driver support for Intel PXA2xx SOCs. If in doubt, say N. + +config ARM_QCOM_CPUFREQ_FW + tristate "QCOM CPUFreq FW driver" + help +Support for the CPUFreq FW driver. +The CPUfreq FW preset in some QCOM chipsets offloads the steps +necessary for changing the frequency of CPUs. The driver +implements the cpufreq driver interface for this firmware. +Say Y if you want to support CPUFreq FW. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 8d24ade..a3edbce 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_ARM_TEGRA124_CPUFREQ)+= tegra124-cpufreq.o obj-$(CONFIG_ARM_TEGRA186_CPUFREQ) += tegra186-cpufreq.o obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_QCOM_CPUFREQ_FW) += qcom-cpufreq-fw.o ## diff --git a/drivers/cpufreq/qcom-cpufreq-fw.c b/drivers/cpufreq/qcom-cpufreq-fw.c new file mode 100644 index 000..67996d5 --- /dev/null +++ b/drivers/cpufreq/qcom-cpufreq-fw.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#define INIT_RATE 3UL +#define XO_RATE1920UL +#define LUT_MAX_ENTRIES40U +#define CORE_COUNT_VAL(val)((val & GENMASK(18, 16)) >> 16) +#define LUT_ROW_SIZE 32 + +struct cpufreq_qcom { + struct cpufreq_frequency_table *table; + struct device *dev; + void __iomem *perf_base; + void __iomem *lut_base; + cpumask_t related_cpus; + unsigned int max_cores; +}; + +static struct cpufreq_qcom *qcom_freq_domain_map[NR_CPUS]; + +static int +qcom_cpufreq_fw_target_index(struct cpufreq_policy *policy, unsigned int index) +{ + struct cpufreq_qcom *c = policy->driver_data; + + if (index >= LUT_MAX_ENTRIES) { + dev_err(c->dev, + "Passing an index (%u) that's greater than max (%d)\n", + index, LUT_MAX_ENTRIES - 1); + return -EINVAL; + } + + writel_relaxed(index, c->perf_base); + + /* Make sure the write goes through before proceeding */ + mb(); + return 0; +} + +static unsigned int qcom_cpufreq_fw_get(unsigned int cpu) +{ + struct cpufreq_qcom *c; + unsigned int index; + + c = qcom_freq_domain_map[cpu]; + if (!c) + return -ENODEV; + + index = readl_relaxed(c->perf_base); + index = min(index, LUT_MAX_ENTRIES - 1); + + return c->table[index].frequency; +} + +static int qcom_cpufreq_fw_cpu_init(struct cpufreq_policy *policy) +{ + struct cpufreq_qcom *c; + int ret; + + c = qcom_freq_domain_map[policy->cpu]; + if (!c) { + pr_err("No scaling support for CPU%d\n", policy->cpu); + return -ENODEV; + } + + cpumask_copy(policy->cpus, &c->related_cpus); + + policy->table = c->table; + policy->driver_data = c; + + return ret; +} + +static struct freq_attr *qcom_cpufreq_fw_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_scaling_boost_freqs, + NULL +}; + +static struct cpufreq_driver cpufreq_qcom_fw_driver = { + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = qcom_cpufreq_fw_target_index, + .get= qcom_cpufreq_fw_get, + .init = qcom_cpufreq_fw_cpu_init, + .name = "qcom-cpufreq-fw", + .attr = qcom_cpufreq_fw_attr, + .boost_enabled = true, +}; + +static int qcom_read_lut(struct platform_device *pdev, + struct cpufreq_qcom *c) +{ + struct device *dev = &pdev->dev; + u32 data, src, lval, i, core_count, prev_cc = 0; + + c->ta