summaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq/qcom-cpufreq-hw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c')
-rw-r--r--drivers/cpufreq/qcom-cpufreq-hw.c53
1 files changed, 42 insertions, 11 deletions
diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c
index d83939a1b3d4..4b0b50403901 100644
--- a/drivers/cpufreq/qcom-cpufreq-hw.c
+++ b/drivers/cpufreq/qcom-cpufreq-hw.c
@@ -10,18 +10,21 @@
10#include <linux/module.h> 10#include <linux/module.h>
11#include <linux/of_address.h> 11#include <linux/of_address.h>
12#include <linux/of_platform.h> 12#include <linux/of_platform.h>
13#include <linux/pm_opp.h>
13#include <linux/slab.h> 14#include <linux/slab.h>
14 15
15#define LUT_MAX_ENTRIES 40U 16#define LUT_MAX_ENTRIES 40U
16#define LUT_SRC GENMASK(31, 30) 17#define LUT_SRC GENMASK(31, 30)
17#define LUT_L_VAL GENMASK(7, 0) 18#define LUT_L_VAL GENMASK(7, 0)
18#define LUT_CORE_COUNT GENMASK(18, 16) 19#define LUT_CORE_COUNT GENMASK(18, 16)
20#define LUT_VOLT GENMASK(11, 0)
19#define LUT_ROW_SIZE 32 21#define LUT_ROW_SIZE 32
20#define CLK_HW_DIV 2 22#define CLK_HW_DIV 2
21 23
22/* Register offsets */ 24/* Register offsets */
23#define REG_ENABLE 0x0 25#define REG_ENABLE 0x0
24#define REG_LUT_TABLE 0x110 26#define REG_FREQ_LUT 0x110
27#define REG_VOLT_LUT 0x114
25#define REG_PERF_STATE 0x920 28#define REG_PERF_STATE 0x920
26 29
27static unsigned long cpu_hw_rate, xo_rate; 30static unsigned long cpu_hw_rate, xo_rate;
@@ -70,11 +73,12 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
70 return policy->freq_table[index].frequency; 73 return policy->freq_table[index].frequency;
71} 74}
72 75
73static int qcom_cpufreq_hw_read_lut(struct device *dev, 76static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
74 struct cpufreq_policy *policy, 77 struct cpufreq_policy *policy,
75 void __iomem *base) 78 void __iomem *base)
76{ 79{
77 u32 data, src, lval, i, core_count, prev_cc = 0, prev_freq = 0, freq; 80 u32 data, src, lval, i, core_count, prev_cc = 0, prev_freq = 0, freq;
81 u32 volt;
78 unsigned int max_cores = cpumask_weight(policy->cpus); 82 unsigned int max_cores = cpumask_weight(policy->cpus);
79 struct cpufreq_frequency_table *table; 83 struct cpufreq_frequency_table *table;
80 84
@@ -83,23 +87,28 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
83 return -ENOMEM; 87 return -ENOMEM;
84 88
85 for (i = 0; i < LUT_MAX_ENTRIES; i++) { 89 for (i = 0; i < LUT_MAX_ENTRIES; i++) {
86 data = readl_relaxed(base + REG_LUT_TABLE + i * LUT_ROW_SIZE); 90 data = readl_relaxed(base + REG_FREQ_LUT +
91 i * LUT_ROW_SIZE);
87 src = FIELD_GET(LUT_SRC, data); 92 src = FIELD_GET(LUT_SRC, data);
88 lval = FIELD_GET(LUT_L_VAL, data); 93 lval = FIELD_GET(LUT_L_VAL, data);
89 core_count = FIELD_GET(LUT_CORE_COUNT, data); 94 core_count = FIELD_GET(LUT_CORE_COUNT, data);
90 95
96 data = readl_relaxed(base + REG_VOLT_LUT +
97 i * LUT_ROW_SIZE);
98 volt = FIELD_GET(LUT_VOLT, data) * 1000;
99
91 if (src) 100 if (src)
92 freq = xo_rate * lval / 1000; 101 freq = xo_rate * lval / 1000;
93 else 102 else
94 freq = cpu_hw_rate / 1000; 103 freq = cpu_hw_rate / 1000;
95 104
96 /* Ignore boosts in the middle of the table */ 105 if (freq != prev_freq && core_count == max_cores) {
97 if (core_count != max_cores) {
98 table[i].frequency = CPUFREQ_ENTRY_INVALID;
99 } else {
100 table[i].frequency = freq; 106 table[i].frequency = freq;
101 dev_dbg(dev, "index=%d freq=%d, core_count %d\n", i, 107 dev_pm_opp_add(cpu_dev, freq * 1000, volt);
108 dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
102 freq, core_count); 109 freq, core_count);
110 } else {
111 table[i].frequency = CPUFREQ_ENTRY_INVALID;
103 } 112 }
104 113
105 /* 114 /*
@@ -116,6 +125,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
116 if (prev_cc != max_cores) { 125 if (prev_cc != max_cores) {
117 prev->frequency = prev_freq; 126 prev->frequency = prev_freq;
118 prev->flags = CPUFREQ_BOOST_FREQ; 127 prev->flags = CPUFREQ_BOOST_FREQ;
128 dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt);
119 } 129 }
120 130
121 break; 131 break;
@@ -127,6 +137,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
127 137
128 table[i].frequency = CPUFREQ_TABLE_END; 138 table[i].frequency = CPUFREQ_TABLE_END;
129 policy->freq_table = table; 139 policy->freq_table = table;
140 dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
130 141
131 return 0; 142 return 0;
132} 143}
@@ -159,10 +170,18 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
159 struct device *dev = &global_pdev->dev; 170 struct device *dev = &global_pdev->dev;
160 struct of_phandle_args args; 171 struct of_phandle_args args;
161 struct device_node *cpu_np; 172 struct device_node *cpu_np;
173 struct device *cpu_dev;
162 struct resource *res; 174 struct resource *res;
163 void __iomem *base; 175 void __iomem *base;
164 int ret, index; 176 int ret, index;
165 177
178 cpu_dev = get_cpu_device(policy->cpu);
179 if (!cpu_dev) {
180 pr_err("%s: failed to get cpu%d device\n", __func__,
181 policy->cpu);
182 return -ENODEV;
183 }
184
166 cpu_np = of_cpu_device_node_get(policy->cpu); 185 cpu_np = of_cpu_device_node_get(policy->cpu);
167 if (!cpu_np) 186 if (!cpu_np)
168 return -EINVAL; 187 return -EINVAL;
@@ -199,12 +218,21 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
199 218
200 policy->driver_data = base + REG_PERF_STATE; 219 policy->driver_data = base + REG_PERF_STATE;
201 220
202 ret = qcom_cpufreq_hw_read_lut(dev, policy, base); 221 ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy, base);
203 if (ret) { 222 if (ret) {
204 dev_err(dev, "Domain-%d failed to read LUT\n", index); 223 dev_err(dev, "Domain-%d failed to read LUT\n", index);
205 goto error; 224 goto error;
206 } 225 }
207 226
227 ret = dev_pm_opp_get_opp_count(cpu_dev);
228 if (ret <= 0) {
229 dev_err(cpu_dev, "Failed to add OPPs\n");
230 ret = -ENODEV;
231 goto error;
232 }
233
234 dev_pm_opp_of_register_em(policy->cpus);
235
208 policy->fast_switch_possible = true; 236 policy->fast_switch_possible = true;
209 237
210 return 0; 238 return 0;
@@ -215,8 +243,10 @@ error:
215 243
216static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) 244static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
217{ 245{
246 struct device *cpu_dev = get_cpu_device(policy->cpu);
218 void __iomem *base = policy->driver_data - REG_PERF_STATE; 247 void __iomem *base = policy->driver_data - REG_PERF_STATE;
219 248
249 dev_pm_opp_remove_all_dynamic(cpu_dev);
220 kfree(policy->freq_table); 250 kfree(policy->freq_table);
221 devm_iounmap(&global_pdev->dev, base); 251 devm_iounmap(&global_pdev->dev, base);
222 252
@@ -231,7 +261,8 @@ static struct freq_attr *qcom_cpufreq_hw_attr[] = {
231 261
232static struct cpufreq_driver cpufreq_qcom_hw_driver = { 262static struct cpufreq_driver cpufreq_qcom_hw_driver = {
233 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK | 263 .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
234 CPUFREQ_HAVE_GOVERNOR_PER_POLICY, 264 CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
265 CPUFREQ_IS_COOLING_DEV,
235 .verify = cpufreq_generic_frequency_table_verify, 266 .verify = cpufreq_generic_frequency_table_verify,
236 .target_index = qcom_cpufreq_hw_target_index, 267 .target_index = qcom_cpufreq_hw_target_index,
237 .get = qcom_cpufreq_hw_get, 268 .get = qcom_cpufreq_hw_get,
@@ -296,7 +327,7 @@ static int __init qcom_cpufreq_hw_init(void)
296{ 327{
297 return platform_driver_register(&qcom_cpufreq_hw_driver); 328 return platform_driver_register(&qcom_cpufreq_hw_driver);
298} 329}
299subsys_initcall(qcom_cpufreq_hw_init); 330device_initcall(qcom_cpufreq_hw_init);
300 331
301static void __exit qcom_cpufreq_hw_exit(void) 332static void __exit qcom_cpufreq_hw_exit(void)
302{ 333{