blob: ebc7f1b725b3a7ab9bde56505d8d5c2aba4d8847 [file] [log] [blame]
Matt Wagantall44f672e2011-09-07 20:31:16 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. 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/init.h>
18#include <linux/io.h>
19#include <linux/delay.h>
20#include <linux/mutex.h>
21#include <linux/spinlock.h>
22#include <linux/errno.h>
23#include <linux/cpufreq.h>
24#include <linux/clk.h>
25
26#include <asm/cpu.h>
27
28#include <mach/board.h>
29#include <mach/msm_iomap.h>
Matt Wagantallb1be5662011-10-18 13:28:58 -070030#include <mach/rpm-regulator.h>
Matt Wagantall44f672e2011-09-07 20:31:16 -070031
32#include "acpuclock.h"
33
34#define REG_CLKSEL_0 (MSM_APCS_GLB_BASE + 0x08)
35#define REG_CLKDIV_0 (MSM_APCS_GLB_BASE + 0x0C)
36#define REG_CLKSEL_1 (MSM_APCS_GLB_BASE + 0x10)
37#define REG_CLKDIV_1 (MSM_APCS_GLB_BASE + 0x14)
38#define REG_CLKOUTSEL (MSM_APCS_GLB_BASE + 0x18)
39
Matt Wagantallb1be5662011-10-18 13:28:58 -070040#define MAX_VDD_CPU 1150000
41#define MAX_VDD_MEM 1150000
42
Matt Wagantall44f672e2011-09-07 20:31:16 -070043enum clk_src {
44 SRC_CXO,
45 SRC_PLL0,
46 SRC_PLL8,
47 SRC_PLL9,
48 NUM_SRC,
49};
50
51struct src_clock {
52 struct clk *clk;
53 const char *name;
54};
55
56static struct src_clock clocks[NUM_SRC] = {
57 [SRC_CXO].name = "cxo",
58 [SRC_PLL0].name = "pll0",
59 [SRC_PLL8].name = "pll8",
60 [SRC_PLL9].name = "pll9",
61};
62
63struct clkctl_acpu_speed {
64 bool use_for_scaling;
65 unsigned int khz;
66 int src;
67 unsigned int src_sel;
68 unsigned int src_div;
Matt Wagantallb1be5662011-10-18 13:28:58 -070069 unsigned int vdd_cpu;
70 unsigned int vdd_mem;
Matt Wagantall44f672e2011-09-07 20:31:16 -070071};
72
73struct acpuclk_state {
74 struct mutex lock;
75 struct clkctl_acpu_speed *current_speed;
76};
77
78static struct acpuclk_state drv_state = {
79 .current_speed = &(struct clkctl_acpu_speed){ 0 },
80};
81
82static struct clkctl_acpu_speed acpu_freq_tbl[] = {
Matt Wagantallb1be5662011-10-18 13:28:58 -070083 { 0, 19200, SRC_CXO, 0, 0, 950000, 1050000 },
84 { 1, 138000, SRC_PLL0, 6, 1, 950000, 1050000 },
85 { 1, 276000, SRC_PLL0, 6, 0, 1050000, 1050000 },
86 { 1, 384000, SRC_PLL8, 3, 0, 1150000, 1150000 },
87 { 1, 440000, SRC_PLL9, 2, 0, 1150000, 1150000 },
Matt Wagantall44f672e2011-09-07 20:31:16 -070088 { 0 }
89};
90
91static void select_clk_source_div(struct clkctl_acpu_speed *s)
92{
93 static void * __iomem const sel_reg[] = {REG_CLKSEL_0, REG_CLKSEL_1};
94 static void * __iomem const div_reg[] = {REG_CLKDIV_0, REG_CLKDIV_1};
95 uint32_t next_bank;
96
97 next_bank = !(readl_relaxed(REG_CLKOUTSEL) & 1);
98 writel_relaxed(s->src_sel, sel_reg[next_bank]);
99 writel_relaxed(s->src_div, div_reg[next_bank]);
100 writel_relaxed(next_bank, REG_CLKOUTSEL);
101
102 /* Wait for switch to complete. */
103 mb();
104 udelay(1);
105}
106
Matt Wagantallb1be5662011-10-18 13:28:58 -0700107/* Apply any per-cpu voltage increases. */
108static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
109{
110 int rc = 0;
111
112 /*
113 * Increase vdd_mem active-set before vdd_cpu.
114 * vdd_mem should be >= vdd_cpu.
115 */
116 rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1,
117 vdd_mem, MAX_VDD_MEM, 0);
118 if (rc) {
119 pr_err("vdd_mem increase failed (%d)\n", rc);
120 return rc;
121 }
122
123 rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1,
124 vdd_cpu, MAX_VDD_CPU, 0);
125 if (rc)
126 pr_err("vdd_cpu increase failed (%d)\n", rc);
127
128 return rc;
129}
130
131/* Apply any per-cpu voltage decreases. */
132static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
133{
134 int ret;
135
136 /* Update CPU voltage. */
137 ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_S1, RPM_VREG_VOTER1,
138 vdd_cpu, MAX_VDD_CPU, 0);
139 if (ret) {
140 pr_err("vdd_cpu decrease failed (%d)\n", ret);
141 return;
142 }
143
144 /*
145 * Decrease vdd_mem active-set after vdd_cpu.
146 * vdd_mem should be >= vdd_cpu.
147 */
148 ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1,
149 vdd_mem, MAX_VDD_MEM, 0);
150 if (ret)
151 pr_err("vdd_mem decrease failed (%d)\n", ret);
152}
153
Matt Wagantall44f672e2011-09-07 20:31:16 -0700154static int acpuclk_9615_set_rate(int cpu, unsigned long rate,
155 enum setrate_reason reason)
156{
157 struct clkctl_acpu_speed *tgt_s, *strt_s;
158 int rc = 0;
159
160 if (reason == SETRATE_CPUFREQ)
161 mutex_lock(&drv_state.lock);
162
163 strt_s = drv_state.current_speed;
164
165 /* Return early if rate didn't change. */
166 if (rate == strt_s->khz)
167 goto out;
168
169 /* Find target frequency. */
170 for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++)
171 if (tgt_s->khz == rate)
172 break;
173 if (tgt_s->khz == 0) {
174 rc = -EINVAL;
175 goto out;
176 }
177
Matt Wagantallb1be5662011-10-18 13:28:58 -0700178 /* Increase VDD levels if needed. */
179 if ((reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
180 && (tgt_s->khz > strt_s->khz)) {
181 rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
182 if (rc)
183 goto out;
184 }
185
Matt Wagantallf5d64072011-10-13 14:15:19 -0700186 pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
Matt Wagantall44f672e2011-09-07 20:31:16 -0700187 strt_s->khz, tgt_s->khz);
188
189 /* Switch CPU speed. */
190 clk_enable(clocks[tgt_s->src].clk);
191 select_clk_source_div(tgt_s);
192 clk_disable(clocks[strt_s->src].clk);
193
194 drv_state.current_speed = tgt_s;
Matt Wagantallf5d64072011-10-13 14:15:19 -0700195 pr_debug("CPU speed change complete\n");
Matt Wagantall44f672e2011-09-07 20:31:16 -0700196
Matt Wagantallb1be5662011-10-18 13:28:58 -0700197 /* Nothing else to do for SWFI or power-collapse. */
198 if (reason == SETRATE_SWFI || reason == SETRATE_PC)
199 goto out;
200
201 /* Drop VDD levels if we can. */
202 if (tgt_s->khz < strt_s->khz)
203 decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
204
Matt Wagantall44f672e2011-09-07 20:31:16 -0700205out:
206 if (reason == SETRATE_CPUFREQ)
207 mutex_unlock(&drv_state.lock);
208 return rc;
209}
210
211static unsigned long acpuclk_9615_get_rate(int cpu)
212{
213 return drv_state.current_speed->khz;
214}
215
216#ifdef CONFIG_CPU_FREQ_MSM
217static struct cpufreq_frequency_table freq_table[30];
218
219static void __init cpufreq_table_init(void)
220{
221 int i, freq_cnt = 0;
222
223 /* Construct the freq_table tables from acpu_freq_tbl. */
224 for (i = 0; acpu_freq_tbl[i].khz != 0
225 && freq_cnt < ARRAY_SIZE(freq_table); i++) {
226 if (acpu_freq_tbl[i].use_for_scaling) {
227 freq_table[freq_cnt].index = freq_cnt;
228 freq_table[freq_cnt].frequency
229 = acpu_freq_tbl[i].khz;
230 freq_cnt++;
231 }
232 }
233 /* freq_table not big enough to store all usable freqs. */
234 BUG_ON(acpu_freq_tbl[i].khz != 0);
235
236 freq_table[freq_cnt].index = freq_cnt;
237 freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
238
239 pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
240
241 /* Register table with CPUFreq. */
242 cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
243}
244#else
245static void __init cpufreq_table_init(void) {}
246#endif
247
248static struct acpuclk_data acpuclk_9615_data = {
249 .set_rate = acpuclk_9615_set_rate,
250 .get_rate = acpuclk_9615_get_rate,
251 .power_collapse_khz = 19200,
252 .wait_for_irq_khz = 19200,
253};
254
255static int __init acpuclk_9615_init(struct acpuclk_soc_data *soc_data)
256{
257 unsigned long max_cpu_khz = 0;
258 int i;
259
260 mutex_init(&drv_state.lock);
261 for (i = 0; i < NUM_SRC; i++) {
262 if (clocks[i].name) {
263 clocks[i].clk = clk_get_sys(NULL, clocks[i].name);
264 BUG_ON(IS_ERR(clocks[i].clk));
265 }
266 }
267
268 /* Improve boot time by ramping up CPU immediately. */
269 for (i = 0; acpu_freq_tbl[i].khz != 0; i++)
270 max_cpu_khz = acpu_freq_tbl[i].khz;
271 acpuclk_9615_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
272
273 acpuclk_register(&acpuclk_9615_data);
274 cpufreq_table_init();
275
276 return 0;
277}
278
279struct acpuclk_soc_data acpuclk_9615_soc_data __initdata = {
280 .init = acpuclk_9615_init,
281};