Skip to content

Commit 46e2856

Browse files
aff-tarrafaeljw
authored andcommitted
cpufreq: Add Kryo CPU scaling driver
In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors, the CPU frequency subset and voltage value of each OPP varies 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 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. Signed-off-by: Ilia Lin <ilialin@codeaurora.org> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org> Tested-by: Amit Kucheria <amit.kucheria@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent cc85de3 commit 46e2856

File tree

5 files changed

+234
-0
lines changed

5 files changed

+234
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11653,6 +11653,13 @@ F: Documentation/devicetree/bindings/media/qcom,camss.txt
1165311653
F: Documentation/media/v4l-drivers/qcom_camss.rst
1165411654
F: drivers/media/platform/qcom/camss-8x16/
1165511655

11656+
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
11657+
M: Ilia Lin <ilia.lin@gmail.com>
11658+
L: linux-pm@vger.kernel.org
11659+
S: Maintained
11660+
F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
11661+
F: drivers/cpufreq/qcom-cpufreq-kryo.c
11662+
1165611663
QUALCOMM EMAC GIGABIT ETHERNET DRIVER
1165711664
M: Timur Tabi <timur@codeaurora.org>
1165811665
L: netdev@vger.kernel.org

drivers/cpufreq/Kconfig.arm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
124124
depends on ARCH_OMAP2PLUS
125125
default ARCH_OMAP2PLUS
126126

127+
config ARM_QCOM_CPUFREQ_KRYO
128+
bool "Qualcomm Kryo based CPUFreq"
129+
depends on ARM64
130+
depends on QCOM_QFPROM
131+
depends on QCOM_SMEM
132+
select PM_OPP
133+
help
134+
This adds the CPUFreq driver for Qualcomm Kryo SoC based boards.
135+
136+
If in doubt, say N.
137+
127138
config ARM_S3C_CPUFREQ
128139
bool
129140
help

drivers/cpufreq/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
6565
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
6666
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
6767
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
68+
obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
6869
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
6970
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
7071
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o

drivers/cpufreq/cpufreq-dt-platdev.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ static const struct of_device_id blacklist[] __initconst = {
116116

117117
{ .compatible = "nvidia,tegra124", },
118118

119+
{ .compatible = "qcom,apq8096", },
120+
{ .compatible = "qcom,msm8996", },
121+
119122
{ .compatible = "st,stih407", },
120123
{ .compatible = "st,stih410", },
121124

drivers/cpufreq/qcom-cpufreq-kryo.c

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
4+
*/
5+
6+
/*
7+
* In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
8+
* the CPU frequency subset and voltage value of each OPP varies
9+
* based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
10+
* defines the voltage and frequency value based on the msm-id in SMEM
11+
* and speedbin blown in the efuse combination.
12+
* The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
13+
* to provide the OPP framework with required information.
14+
* This is used to determine the voltage and frequency value for each OPP of
15+
* operating-points-v2 table when it is parsed by the OPP framework.
16+
*/
17+
18+
#include <linux/cpu.h>
19+
#include <linux/err.h>
20+
#include <linux/init.h>
21+
#include <linux/kernel.h>
22+
#include <linux/module.h>
23+
#include <linux/nvmem-consumer.h>
24+
#include <linux/of.h>
25+
#include <linux/platform_device.h>
26+
#include <linux/pm_opp.h>
27+
#include <linux/slab.h>
28+
#include <linux/soc/qcom/smem.h>
29+
30+
#define MSM_ID_SMEM 137
31+
32+
enum _msm_id {
33+
MSM8996V3 = 0xF6ul,
34+
APQ8096V3 = 0x123ul,
35+
MSM8996SG = 0x131ul,
36+
APQ8096SG = 0x138ul,
37+
};
38+
39+
enum _msm8996_version {
40+
MSM8996_V3,
41+
MSM8996_SG,
42+
NUM_OF_MSM8996_VERSIONS,
43+
};
44+
45+
static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void)
46+
{
47+
size_t len;
48+
u32 *msm_id;
49+
enum _msm8996_version version;
50+
51+
msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
52+
if (IS_ERR(msm_id))
53+
return NUM_OF_MSM8996_VERSIONS;
54+
55+
/* The first 4 bytes are format, next to them is the actual msm-id */
56+
msm_id++;
57+
58+
switch ((enum _msm_id)*msm_id) {
59+
case MSM8996V3:
60+
case APQ8096V3:
61+
version = MSM8996_V3;
62+
break;
63+
case MSM8996SG:
64+
case APQ8096SG:
65+
version = MSM8996_SG;
66+
break;
67+
default:
68+
version = NUM_OF_MSM8996_VERSIONS;
69+
}
70+
71+
return version;
72+
}
73+
74+
static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
75+
{
76+
struct opp_table *opp_tables[NR_CPUS] = {0};
77+
struct platform_device *cpufreq_dt_pdev;
78+
enum _msm8996_version msm8996_version;
79+
struct nvmem_cell *speedbin_nvmem;
80+
struct device_node *np;
81+
struct device *cpu_dev;
82+
unsigned cpu;
83+
u8 *speedbin;
84+
u32 versions;
85+
size_t len;
86+
int ret;
87+
88+
cpu_dev = get_cpu_device(0);
89+
if (NULL == cpu_dev)
90+
ret = -ENODEV;
91+
92+
msm8996_version = qcom_cpufreq_kryo_get_msm_id();
93+
if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
94+
dev_err(cpu_dev, "Not Snapdragon 820/821!");
95+
return -ENODEV;
96+
}
97+
98+
np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
99+
if (IS_ERR(np))
100+
return PTR_ERR(np);
101+
102+
ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
103+
if (!ret) {
104+
of_node_put(np);
105+
return -ENOENT;
106+
}
107+
108+
speedbin_nvmem = of_nvmem_cell_get(np, NULL);
109+
of_node_put(np);
110+
if (IS_ERR(speedbin_nvmem)) {
111+
dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
112+
PTR_ERR(speedbin_nvmem));
113+
return PTR_ERR(speedbin_nvmem);
114+
}
115+
116+
speedbin = nvmem_cell_read(speedbin_nvmem, &len);
117+
nvmem_cell_put(speedbin_nvmem);
118+
119+
switch (msm8996_version) {
120+
case MSM8996_V3:
121+
versions = 1 << (unsigned int)(*speedbin);
122+
break;
123+
case MSM8996_SG:
124+
versions = 1 << ((unsigned int)(*speedbin) + 4);
125+
break;
126+
default:
127+
BUG();
128+
break;
129+
}
130+
131+
for_each_possible_cpu(cpu) {
132+
cpu_dev = get_cpu_device(cpu);
133+
if (NULL == cpu_dev) {
134+
ret = -ENODEV;
135+
goto free_opp;
136+
}
137+
138+
opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
139+
&versions, 1);
140+
if (IS_ERR(opp_tables[cpu])) {
141+
ret = PTR_ERR(opp_tables[cpu]);
142+
dev_err(cpu_dev, "Failed to set supported hardware\n");
143+
goto free_opp;
144+
}
145+
}
146+
147+
cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
148+
NULL, 0);
149+
if (!IS_ERR(cpufreq_dt_pdev))
150+
return 0;
151+
152+
ret = PTR_ERR(cpufreq_dt_pdev);
153+
dev_err(cpu_dev, "Failed to register platform device\n");
154+
155+
free_opp:
156+
for_each_possible_cpu(cpu) {
157+
if (IS_ERR_OR_NULL(opp_tables[cpu]))
158+
break;
159+
dev_pm_opp_put_supported_hw(opp_tables[cpu]);
160+
}
161+
162+
return ret;
163+
}
164+
165+
static struct platform_driver qcom_cpufreq_kryo_driver = {
166+
.probe = qcom_cpufreq_kryo_probe,
167+
.driver = {
168+
.name = "qcom-cpufreq-kryo",
169+
},
170+
};
171+
172+
static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
173+
{ .compatible = "qcom,apq8096", },
174+
{ .compatible = "qcom,msm8996", },
175+
};
176+
177+
/*
178+
* Since the driver depends on smem and nvmem drivers, which may
179+
* return EPROBE_DEFER, all the real activity is done in the probe,
180+
* which may be defered as well. The init here is only registering
181+
* the driver and the platform device.
182+
*/
183+
static int __init qcom_cpufreq_kryo_init(void)
184+
{
185+
struct device_node *np = of_find_node_by_path("/");
186+
const struct of_device_id *match;
187+
int ret;
188+
189+
if (!np)
190+
return -ENODEV;
191+
192+
match = of_match_node(qcom_cpufreq_kryo_match_list, np);
193+
of_node_put(np);
194+
if (!match)
195+
return -ENODEV;
196+
197+
ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
198+
if (unlikely(ret < 0))
199+
return ret;
200+
201+
ret = PTR_ERR_OR_ZERO(platform_device_register_simple(
202+
"qcom-cpufreq-kryo", -1, NULL, 0));
203+
if (0 == ret)
204+
return 0;
205+
206+
platform_driver_unregister(&qcom_cpufreq_kryo_driver);
207+
return ret;
208+
}
209+
module_init(qcom_cpufreq_kryo_init);
210+
211+
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
212+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)