blob: 4ac1408ed47b275faf541def0a66771f3ce6726c [file] [log] [blame]
Patrick Daly985c14b2012-12-03 17:12:37 -08001/*
2 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/delay.h>
21#include <linux/mutex.h>
22#include <linux/spinlock.h>
23#include <linux/errno.h>
24#include <linux/cpufreq.h>
25#include <linux/clk.h>
26#include <linux/platform_device.h>
27#include <linux/iopoll.h>
28
29#include <mach/board.h>
30#include <mach/msm_iomap.h>
31#include <mach/msm_bus.h>
32#include <mach/msm_bus_board.h>
33#include <mach/rpm-regulator.h>
34#include <mach/clk-provider.h>
35#include <mach/rpm-regulator-smd.h>
36
37#include "acpuclock.h"
38#include "acpuclock-cortex.h"
39
40#define POLL_INTERVAL_US 1
41#define APCS_RCG_UPDATE_TIMEOUT_US 20
42
43static struct acpuclk_drv_data *acpuclk_init_data;
44static uint32_t bus_perf_client;
45
46/* Update the bus bandwidth request. */
47static void set_bus_bw(unsigned int bw)
48{
49 int ret;
50
51 if (bw >= acpuclk_init_data->bus_scale->num_usecases) {
52 pr_err("invalid bandwidth request (%d)\n", bw);
53 return;
54 }
55
56 /* Update bandwidth if request has changed. This may sleep. */
57 ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
58 if (ret)
59 pr_err("bandwidth request failed (%d)\n", ret);
60
61 return;
62}
63
64/* Apply any voltage increases. */
65static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
66{
67 int rc = 0;
68
69 /* Increase vdd_mem before vdd_cpu. vdd_mem should be >= vdd_cpu. */
70 rc = regulator_set_voltage(acpuclk_init_data->vdd_mem, vdd_mem,
71 acpuclk_init_data->vdd_max_mem);
72 if (rc) {
73 pr_err("vdd_mem increase failed (%d)\n", rc);
74 return rc;
75 }
76
77 rc = regulator_set_voltage(acpuclk_init_data->vdd_cpu, vdd_cpu,
78 acpuclk_init_data->vdd_max_cpu);
79 if (rc)
80 pr_err("vdd_cpu increase failed (%d)\n", rc);
81
82 return rc;
83}
84
85/* Apply any per-cpu voltage decreases. */
86static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
87{
88 int ret;
89
90 /* Update CPU voltage. */
91 ret = regulator_set_voltage(acpuclk_init_data->vdd_cpu, vdd_cpu,
92 acpuclk_init_data->vdd_max_cpu);
93 if (ret) {
94 pr_err("vdd_cpu decrease failed (%d)\n", ret);
95 return;
96 }
97
98 /* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
99 ret = regulator_set_voltage(acpuclk_init_data->vdd_mem, vdd_mem,
100 acpuclk_init_data->vdd_max_mem);
101 if (ret)
102 pr_err("vdd_mem decrease failed (%d)\n", ret);
103}
104
105static void select_clk_source_div(struct acpuclk_drv_data *drv_data,
106 struct clkctl_acpu_speed *s)
107{
108 u32 regval, rc, src_div;
109 void __iomem *apcs_rcg_config = drv_data->apcs_rcg_config;
110 void __iomem *apcs_rcg_cmd = drv_data->apcs_rcg_cmd;
111 struct acpuclk_reg_data *r = &drv_data->reg_data;
112
113 src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
114
115 regval = readl_relaxed(apcs_rcg_config);
116 regval &= ~r->cfg_src_mask;
117 regval |= s->src_sel << r->cfg_src_shift;
118 regval &= ~r->cfg_div_mask;
119 regval |= src_div << r->cfg_div_shift;
120 writel_relaxed(regval, apcs_rcg_config);
121
122 /* Update the configuration */
123 regval = readl_relaxed(apcs_rcg_cmd);
124 regval |= r->update_mask;
125 writel_relaxed(regval, apcs_rcg_cmd);
126
127 /* Wait for the update to take effect */
128 rc = readl_poll_timeout(apcs_rcg_cmd, regval,
129 !(regval & r->poll_mask),
130 POLL_INTERVAL_US,
131 APCS_RCG_UPDATE_TIMEOUT_US);
132 if (rc)
133 pr_warn("acpu rcg didn't update its configuration\n");
134}
135
136static int set_speed(struct clkctl_acpu_speed *tgt_s)
137{
138 int rc = 0;
139 unsigned int tgt_freq_hz = tgt_s->khz * 1000;
140 struct clkctl_acpu_speed *strt_s = acpuclk_init_data->current_speed;
141 struct clkctl_acpu_speed *cxo_s = &acpuclk_init_data->freq_tbl[0];
142 struct clk *strt = acpuclk_init_data->src_clocks[strt_s->src].clk;
143 struct clk *tgt = acpuclk_init_data->src_clocks[tgt_s->src].clk;
144
145 if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
146 /* Switch to another always on src */
147 select_clk_source_div(acpuclk_init_data, cxo_s);
148
149 /* Re-program acpu pll */
150 clk_disable(tgt);
151 rc = clk_set_rate(tgt, tgt_freq_hz);
152 if (rc)
153 pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
154 BUG_ON(clk_enable(tgt));
155
156 /* Switch back to acpu pll */
157 select_clk_source_div(acpuclk_init_data, tgt_s);
158
159 } else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
160 rc = clk_set_rate(tgt, tgt_freq_hz);
161 if (rc) {
162 pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
163 return rc;
164 }
165
166 rc = clk_enable(tgt);
167 if (rc) {
168 pr_err("ACPU PLL enable failed\n");
169 return rc;
170 }
171
172 select_clk_source_div(acpuclk_init_data, tgt_s);
173
174 clk_disable(strt);
175 } else {
176 rc = clk_enable(tgt);
177 if (rc) {
178 pr_err("%s enable failed\n",
179 acpuclk_init_data->src_clocks[tgt_s->src].name);
180 return rc;
181 }
182
183 select_clk_source_div(acpuclk_init_data, tgt_s);
184
185 clk_disable(strt);
186 }
187
188 return rc;
189}
190
191static int acpuclk_cortex_set_rate(int cpu, unsigned long rate,
192 enum setrate_reason reason)
193{
194 struct clkctl_acpu_speed *tgt_s, *strt_s;
195 int rc = 0;
196
197 if (reason == SETRATE_CPUFREQ)
198 mutex_lock(&acpuclk_init_data->lock);
199
200 strt_s = acpuclk_init_data->current_speed;
201
202 /* Return early if rate didn't change */
203 if (rate == strt_s->khz)
204 goto out;
205
206 /* Find target frequency */
207 for (tgt_s = acpuclk_init_data->freq_tbl; tgt_s->khz != 0; tgt_s++)
208 if (tgt_s->khz == rate)
209 break;
210 if (tgt_s->khz == 0) {
211 rc = -EINVAL;
212 goto out;
213 }
214
215 /* Increase VDD levels if needed */
216 if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
217 && (tgt_s->khz > strt_s->khz)) {
218 rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
219 if (rc)
220 goto out;
221 }
222
223 pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
224 strt_s->khz, tgt_s->khz);
225
226 /* Switch CPU speed. */
227 rc = set_speed(tgt_s);
228 if (rc)
229 goto out;
230
231 acpuclk_init_data->current_speed = tgt_s;
232 pr_debug("CPU speed change complete\n");
233
234 /* Nothing else to do for SWFI or power-collapse. */
235 if (reason == SETRATE_SWFI || reason == SETRATE_PC)
236 goto out;
237
238 /* Update bus bandwith request */
239 set_bus_bw(tgt_s->bw_level);
240
241 /* Drop VDD levels if we can. */
242 if (tgt_s->khz < strt_s->khz)
243 decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
244
245out:
246 if (reason == SETRATE_CPUFREQ)
247 mutex_unlock(&acpuclk_init_data->lock);
248 return rc;
249}
250
251static unsigned long acpuclk_cortex_get_rate(int cpu)
252{
253 return acpuclk_init_data->current_speed->khz;
254}
255
256#ifdef CONFIG_CPU_FREQ_MSM
257static struct cpufreq_frequency_table freq_table[30];
258
259static void __init cpufreq_table_init(void)
260{
261 int i, freq_cnt = 0;
262
263 /* Construct the freq_table tables from acpuclk_init_data->freq_tbl. */
264 for (i = 0; acpuclk_init_data->freq_tbl[i].khz != 0
265 && freq_cnt < ARRAY_SIZE(freq_table); i++) {
266 if (!acpuclk_init_data->freq_tbl[i].use_for_scaling)
267 continue;
268 freq_table[freq_cnt].index = freq_cnt;
269 freq_table[freq_cnt].frequency =
270 acpuclk_init_data->freq_tbl[i].khz;
271 freq_cnt++;
272 }
273 /* freq_table not big enough to store all usable freqs. */
274 BUG_ON(acpuclk_init_data->freq_tbl[i].khz != 0);
275
276 freq_table[freq_cnt].index = freq_cnt;
277 freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
278
279 pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
280
281 /* Register table with CPUFreq. */
282 for_each_possible_cpu(i)
283 cpufreq_frequency_table_get_attr(freq_table, i);
284}
285#else
286static void __init cpufreq_table_init(void) {}
287#endif
288
289static struct acpuclk_data acpuclk_cortex_data = {
290 .set_rate = acpuclk_cortex_set_rate,
291 .get_rate = acpuclk_cortex_get_rate,
292 .power_collapse_khz = 19200,
293 .wait_for_irq_khz = 19200,
294};
295
296int __init acpuclk_cortex_init(struct platform_device *pdev,
297 struct acpuclk_drv_data *data)
298{
299 unsigned long max_cpu_khz = 0;
300 int i, rc;
301
302 acpuclk_init_data = data;
303 mutex_init(&acpuclk_init_data->lock);
304
305 bus_perf_client = msm_bus_scale_register_client(
306 acpuclk_init_data->bus_scale);
307 if (!bus_perf_client) {
308 pr_err("Unable to register bus client\n");
309 BUG();
310 }
311
312 for (i = 0; i < NUM_SRC; i++) {
313 if (!acpuclk_init_data->src_clocks[i].name)
314 continue;
315 acpuclk_init_data->src_clocks[i].clk =
316 clk_get(&pdev->dev,
317 acpuclk_init_data->src_clocks[i].name);
318 BUG_ON(IS_ERR(acpuclk_init_data->src_clocks[i].clk));
319 /*
320 * Prepare the PLLs because we enable/disable them
321 * in atomic context during power collapse/restore.
322 */
323 BUG_ON(clk_prepare(acpuclk_init_data->src_clocks[i].clk));
324 }
325
326 /* Improve boot time by ramping up CPU immediately */
Patrick Daly71839d42013-02-25 13:05:05 -0800327 for (i = 0; acpuclk_init_data->freq_tbl[i].khz != 0; i++)
328 if (acpuclk_init_data->freq_tbl[i].use_for_scaling)
329 max_cpu_khz = acpuclk_init_data->freq_tbl[i].khz;
Patrick Daly985c14b2012-12-03 17:12:37 -0800330
331 /* Initialize regulators */
332 rc = increase_vdd(acpuclk_init_data->freq_tbl[i].vdd_cpu,
333 acpuclk_init_data->freq_tbl[i].vdd_mem);
334 if (rc)
335 goto err_vdd;
336
337 rc = regulator_enable(acpuclk_init_data->vdd_mem);
338 if (rc) {
339 dev_err(&pdev->dev, "regulator_enable for mem failed\n");
340 goto err_vdd;
341 }
342
343 rc = regulator_enable(acpuclk_init_data->vdd_cpu);
344 if (rc) {
345 dev_err(&pdev->dev, "regulator_enable for cpu failed\n");
346 goto err_vdd_cpu;
347 }
348
Patrick Daly71839d42013-02-25 13:05:05 -0800349 /*
350 * Select a state which is always a valid transition to align SW with
351 * the HW configuration set by the bootloaders.
352 */
353 acpuclk_cortex_set_rate(0, acpuclk_cortex_data.power_collapse_khz,
354 SETRATE_INIT);
Patrick Daly985c14b2012-12-03 17:12:37 -0800355 acpuclk_cortex_set_rate(0, max_cpu_khz, SETRATE_INIT);
356
357 acpuclk_register(&acpuclk_cortex_data);
358 cpufreq_table_init();
359
360 return 0;
361
362err_vdd_cpu:
363 regulator_disable(acpuclk_init_data->vdd_mem);
364err_vdd:
365 regulator_put(acpuclk_init_data->vdd_mem);
366 regulator_put(acpuclk_init_data->vdd_cpu);
367
368 for (i = 0; i < NUM_SRC; i++) {
369 if (!acpuclk_init_data->src_clocks[i].name)
370 continue;
371 clk_unprepare(acpuclk_init_data->src_clocks[i].clk);
372 clk_put(acpuclk_init_data->src_clocks[i].clk);
373 }
374 return rc;
375}