diff options
Diffstat (limited to 'drivers/cpufreq/qcom-cpufreq-hw.c')
-rw-r--r-- | drivers/cpufreq/qcom-cpufreq-hw.c | 53 |
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 | ||
27 | static unsigned long cpu_hw_rate, xo_rate; | 30 | static 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 | ||
73 | static int qcom_cpufreq_hw_read_lut(struct device *dev, | 76 | static 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 | ||
216 | static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) | 244 | static 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 | ||
232 | static struct cpufreq_driver cpufreq_qcom_hw_driver = { | 262 | static 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 | } |
299 | subsys_initcall(qcom_cpufreq_hw_init); | 330 | device_initcall(qcom_cpufreq_hw_init); |
300 | 331 | ||
301 | static void __exit qcom_cpufreq_hw_exit(void) | 332 | static void __exit qcom_cpufreq_hw_exit(void) |
302 | { | 333 | { |