blob: ca7fc2b0cedfe142ce87973b7234d91c964e3085 [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
Patrick Daly9196ed42013-03-13 15:59:03 -070043static struct acpuclk_drv_data *priv;
Patrick Daly985c14b2012-12-03 17:12:37 -080044static 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
Patrick Daly9196ed42013-03-13 15:59:03 -070051 if (bw >= priv->bus_scale->num_usecases) {
Patrick Daly985c14b2012-12-03 17:12:37 -080052 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. */
Patrick Daly9196ed42013-03-13 15:59:03 -070070 rc = regulator_set_voltage(priv->vdd_mem, vdd_mem, priv->vdd_max_mem);
Patrick Daly985c14b2012-12-03 17:12:37 -080071 if (rc) {
72 pr_err("vdd_mem increase failed (%d)\n", rc);
73 return rc;
74 }
75
Patrick Daly9196ed42013-03-13 15:59:03 -070076 rc = regulator_set_voltage(priv->vdd_cpu, vdd_cpu, priv->vdd_max_cpu);
Patrick Daly985c14b2012-12-03 17:12:37 -080077 if (rc)
78 pr_err("vdd_cpu increase failed (%d)\n", rc);
79
80 return rc;
81}
82
83/* Apply any per-cpu voltage decreases. */
84static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
85{
86 int ret;
87
88 /* Update CPU voltage. */
Patrick Daly9196ed42013-03-13 15:59:03 -070089 ret = regulator_set_voltage(priv->vdd_cpu, vdd_cpu, priv->vdd_max_cpu);
Patrick Daly985c14b2012-12-03 17:12:37 -080090 if (ret) {
91 pr_err("vdd_cpu decrease failed (%d)\n", ret);
92 return;
93 }
94
95 /* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
Patrick Daly9196ed42013-03-13 15:59:03 -070096 ret = regulator_set_voltage(priv->vdd_mem, vdd_mem, priv->vdd_max_mem);
Patrick Daly985c14b2012-12-03 17:12:37 -080097 if (ret)
98 pr_err("vdd_mem decrease failed (%d)\n", ret);
99}
100
101static void select_clk_source_div(struct acpuclk_drv_data *drv_data,
102 struct clkctl_acpu_speed *s)
103{
104 u32 regval, rc, src_div;
105 void __iomem *apcs_rcg_config = drv_data->apcs_rcg_config;
106 void __iomem *apcs_rcg_cmd = drv_data->apcs_rcg_cmd;
107 struct acpuclk_reg_data *r = &drv_data->reg_data;
108
109 src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
110
111 regval = readl_relaxed(apcs_rcg_config);
112 regval &= ~r->cfg_src_mask;
113 regval |= s->src_sel << r->cfg_src_shift;
114 regval &= ~r->cfg_div_mask;
115 regval |= src_div << r->cfg_div_shift;
116 writel_relaxed(regval, apcs_rcg_config);
117
118 /* Update the configuration */
119 regval = readl_relaxed(apcs_rcg_cmd);
120 regval |= r->update_mask;
121 writel_relaxed(regval, apcs_rcg_cmd);
122
123 /* Wait for the update to take effect */
124 rc = readl_poll_timeout(apcs_rcg_cmd, regval,
125 !(regval & r->poll_mask),
126 POLL_INTERVAL_US,
127 APCS_RCG_UPDATE_TIMEOUT_US);
128 if (rc)
129 pr_warn("acpu rcg didn't update its configuration\n");
130}
131
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800132/*
133 * This function can be called in both atomic and nonatomic context.
134 * Since regulator APIS can sleep, we cannot always use the clk prepare
135 * unprepare API.
136 */
137static int set_speed(struct clkctl_acpu_speed *tgt_s, bool atomic)
Patrick Daly985c14b2012-12-03 17:12:37 -0800138{
139 int rc = 0;
140 unsigned int tgt_freq_hz = tgt_s->khz * 1000;
Patrick Daly9196ed42013-03-13 15:59:03 -0700141 struct clkctl_acpu_speed *strt_s = priv->current_speed;
142 struct clkctl_acpu_speed *cxo_s = &priv->freq_tbl[0];
143 struct clk *strt = priv->src_clocks[strt_s->src].clk;
144 struct clk *tgt = priv->src_clocks[tgt_s->src].clk;
Patrick Daly985c14b2012-12-03 17:12:37 -0800145
146 if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
147 /* Switch to another always on src */
Patrick Daly9196ed42013-03-13 15:59:03 -0700148 select_clk_source_div(priv, cxo_s);
Patrick Daly985c14b2012-12-03 17:12:37 -0800149
150 /* Re-program acpu pll */
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800151 if (atomic)
152 clk_disable(tgt);
153 else
154 clk_disable_unprepare(tgt);
155
Patrick Daly985c14b2012-12-03 17:12:37 -0800156 rc = clk_set_rate(tgt, tgt_freq_hz);
157 if (rc)
158 pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800159
160 if (atomic)
161 BUG_ON(clk_enable(tgt));
162 else
163 BUG_ON(clk_prepare_enable(tgt));
Patrick Daly985c14b2012-12-03 17:12:37 -0800164
165 /* Switch back to acpu pll */
Patrick Daly9196ed42013-03-13 15:59:03 -0700166 select_clk_source_div(priv, tgt_s);
Patrick Daly985c14b2012-12-03 17:12:37 -0800167
168 } else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
169 rc = clk_set_rate(tgt, tgt_freq_hz);
170 if (rc) {
171 pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
172 return rc;
173 }
174
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800175 if (atomic)
Vikram Mulukutla4f8ffeb2013-03-15 14:00:49 -0700176 rc = clk_enable(tgt);
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800177 else
Vikram Mulukutla4f8ffeb2013-03-15 14:00:49 -0700178 rc = clk_prepare_enable(tgt);
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800179
Patrick Daly985c14b2012-12-03 17:12:37 -0800180 if (rc) {
181 pr_err("ACPU PLL enable failed\n");
182 return rc;
183 }
184
Patrick Daly9196ed42013-03-13 15:59:03 -0700185 select_clk_source_div(priv, tgt_s);
Patrick Daly985c14b2012-12-03 17:12:37 -0800186
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800187 if (atomic)
188 clk_disable(strt);
189 else
190 clk_disable_unprepare(strt);
191
Patrick Daly985c14b2012-12-03 17:12:37 -0800192 } else {
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800193 if (atomic)
Vikram Mulukutla4f8ffeb2013-03-15 14:00:49 -0700194 rc = clk_enable(tgt);
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800195 else
Vikram Mulukutla4f8ffeb2013-03-15 14:00:49 -0700196 rc = clk_prepare_enable(tgt);
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800197
Patrick Daly985c14b2012-12-03 17:12:37 -0800198 if (rc) {
199 pr_err("%s enable failed\n",
Patrick Daly9196ed42013-03-13 15:59:03 -0700200 priv->src_clocks[tgt_s->src].name);
Patrick Daly985c14b2012-12-03 17:12:37 -0800201 return rc;
202 }
203
Patrick Daly9196ed42013-03-13 15:59:03 -0700204 select_clk_source_div(priv, tgt_s);
Patrick Daly985c14b2012-12-03 17:12:37 -0800205
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800206 if (atomic)
207 clk_disable(strt);
208 else
209 clk_disable_unprepare(strt);
210
Patrick Daly985c14b2012-12-03 17:12:37 -0800211 }
212
213 return rc;
214}
215
216static int acpuclk_cortex_set_rate(int cpu, unsigned long rate,
217 enum setrate_reason reason)
218{
219 struct clkctl_acpu_speed *tgt_s, *strt_s;
220 int rc = 0;
221
222 if (reason == SETRATE_CPUFREQ)
Patrick Daly9196ed42013-03-13 15:59:03 -0700223 mutex_lock(&priv->lock);
Patrick Daly985c14b2012-12-03 17:12:37 -0800224
Patrick Daly9196ed42013-03-13 15:59:03 -0700225 strt_s = priv->current_speed;
Patrick Daly985c14b2012-12-03 17:12:37 -0800226
227 /* Return early if rate didn't change */
228 if (rate == strt_s->khz)
229 goto out;
230
231 /* Find target frequency */
Patrick Daly9196ed42013-03-13 15:59:03 -0700232 for (tgt_s = priv->freq_tbl; tgt_s->khz != 0; tgt_s++)
Patrick Daly985c14b2012-12-03 17:12:37 -0800233 if (tgt_s->khz == rate)
234 break;
235 if (tgt_s->khz == 0) {
236 rc = -EINVAL;
237 goto out;
238 }
239
240 /* Increase VDD levels if needed */
241 if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
242 && (tgt_s->khz > strt_s->khz)) {
243 rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
244 if (rc)
245 goto out;
246 }
247
248 pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
249 strt_s->khz, tgt_s->khz);
250
Patrick Dalye85c6bc2013-01-31 14:14:28 -0800251 /* Switch CPU speed. Flag indicates atomic context */
252 if (reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
253 rc = set_speed(tgt_s, false);
254 else
255 rc = set_speed(tgt_s, true);
256
Patrick Daly985c14b2012-12-03 17:12:37 -0800257 if (rc)
258 goto out;
259
Patrick Daly9196ed42013-03-13 15:59:03 -0700260 priv->current_speed = tgt_s;
Patrick Daly985c14b2012-12-03 17:12:37 -0800261 pr_debug("CPU speed change complete\n");
262
263 /* Nothing else to do for SWFI or power-collapse. */
264 if (reason == SETRATE_SWFI || reason == SETRATE_PC)
265 goto out;
266
267 /* Update bus bandwith request */
268 set_bus_bw(tgt_s->bw_level);
269
270 /* Drop VDD levels if we can. */
271 if (tgt_s->khz < strt_s->khz)
272 decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
273
274out:
275 if (reason == SETRATE_CPUFREQ)
Patrick Daly9196ed42013-03-13 15:59:03 -0700276 mutex_unlock(&priv->lock);
Patrick Daly985c14b2012-12-03 17:12:37 -0800277 return rc;
278}
279
280static unsigned long acpuclk_cortex_get_rate(int cpu)
281{
Patrick Daly9196ed42013-03-13 15:59:03 -0700282 return priv->current_speed->khz;
Patrick Daly985c14b2012-12-03 17:12:37 -0800283}
284
285#ifdef CONFIG_CPU_FREQ_MSM
286static struct cpufreq_frequency_table freq_table[30];
287
288static void __init cpufreq_table_init(void)
289{
290 int i, freq_cnt = 0;
291
Patrick Daly9196ed42013-03-13 15:59:03 -0700292 /* Construct the freq_table tables from priv->freq_tbl. */
293 for (i = 0; priv->freq_tbl[i].khz != 0
Patrick Daly985c14b2012-12-03 17:12:37 -0800294 && freq_cnt < ARRAY_SIZE(freq_table); i++) {
Patrick Daly9196ed42013-03-13 15:59:03 -0700295 if (!priv->freq_tbl[i].use_for_scaling)
Patrick Daly985c14b2012-12-03 17:12:37 -0800296 continue;
297 freq_table[freq_cnt].index = freq_cnt;
Patrick Daly9196ed42013-03-13 15:59:03 -0700298 freq_table[freq_cnt].frequency = priv->freq_tbl[i].khz;
Patrick Daly985c14b2012-12-03 17:12:37 -0800299 freq_cnt++;
300 }
301 /* freq_table not big enough to store all usable freqs. */
Patrick Daly9196ed42013-03-13 15:59:03 -0700302 BUG_ON(priv->freq_tbl[i].khz != 0);
Patrick Daly985c14b2012-12-03 17:12:37 -0800303
304 freq_table[freq_cnt].index = freq_cnt;
305 freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
306
307 pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
308
309 /* Register table with CPUFreq. */
310 for_each_possible_cpu(i)
311 cpufreq_frequency_table_get_attr(freq_table, i);
312}
313#else
314static void __init cpufreq_table_init(void) {}
315#endif
316
317static struct acpuclk_data acpuclk_cortex_data = {
318 .set_rate = acpuclk_cortex_set_rate,
319 .get_rate = acpuclk_cortex_get_rate,
Patrick Daly985c14b2012-12-03 17:12:37 -0800320};
321
322int __init acpuclk_cortex_init(struct platform_device *pdev,
323 struct acpuclk_drv_data *data)
324{
325 unsigned long max_cpu_khz = 0;
326 int i, rc;
327
Patrick Daly9196ed42013-03-13 15:59:03 -0700328 priv = data;
329 mutex_init(&priv->lock);
Patrick Daly985c14b2012-12-03 17:12:37 -0800330
Patrick Dalyaf8808e2013-03-20 12:57:00 -0700331 acpuclk_cortex_data.power_collapse_khz = priv->wait_for_irq_khz;
332 acpuclk_cortex_data.wait_for_irq_khz = priv->wait_for_irq_khz;
333
Patrick Daly9196ed42013-03-13 15:59:03 -0700334 bus_perf_client = msm_bus_scale_register_client(priv->bus_scale);
Patrick Daly985c14b2012-12-03 17:12:37 -0800335 if (!bus_perf_client) {
336 pr_err("Unable to register bus client\n");
337 BUG();
338 }
339
Patrick Daly985c14b2012-12-03 17:12:37 -0800340 /* Improve boot time by ramping up CPU immediately */
Patrick Daly9196ed42013-03-13 15:59:03 -0700341 for (i = 0; priv->freq_tbl[i].khz != 0; i++)
342 if (priv->freq_tbl[i].use_for_scaling)
343 max_cpu_khz = priv->freq_tbl[i].khz;
Patrick Daly985c14b2012-12-03 17:12:37 -0800344
345 /* Initialize regulators */
Patrick Daly9196ed42013-03-13 15:59:03 -0700346 rc = increase_vdd(priv->vdd_max_cpu, priv->vdd_max_mem);
Patrick Daly985c14b2012-12-03 17:12:37 -0800347 if (rc)
348 goto err_vdd;
349
Patrick Daly9196ed42013-03-13 15:59:03 -0700350 rc = regulator_enable(priv->vdd_mem);
Patrick Daly985c14b2012-12-03 17:12:37 -0800351 if (rc) {
352 dev_err(&pdev->dev, "regulator_enable for mem failed\n");
353 goto err_vdd;
354 }
355
Patrick Daly9196ed42013-03-13 15:59:03 -0700356 rc = regulator_enable(priv->vdd_cpu);
Patrick Daly985c14b2012-12-03 17:12:37 -0800357 if (rc) {
358 dev_err(&pdev->dev, "regulator_enable for cpu failed\n");
359 goto err_vdd_cpu;
360 }
361
Patrick Daly71839d42013-02-25 13:05:05 -0800362 /*
363 * Select a state which is always a valid transition to align SW with
364 * the HW configuration set by the bootloaders.
365 */
366 acpuclk_cortex_set_rate(0, acpuclk_cortex_data.power_collapse_khz,
367 SETRATE_INIT);
Patrick Daly985c14b2012-12-03 17:12:37 -0800368 acpuclk_cortex_set_rate(0, max_cpu_khz, SETRATE_INIT);
369
370 acpuclk_register(&acpuclk_cortex_data);
371 cpufreq_table_init();
372
373 return 0;
374
375err_vdd_cpu:
Patrick Daly9196ed42013-03-13 15:59:03 -0700376 regulator_disable(priv->vdd_mem);
Patrick Daly985c14b2012-12-03 17:12:37 -0800377err_vdd:
Patrick Daly985c14b2012-12-03 17:12:37 -0800378 return rc;
379}