blob: 924a46abc2ee011bf69c82fe7a1c803c8f443335 [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>
30
31#include "acpuclock.h"
32
33#define REG_CLKSEL_0 (MSM_APCS_GLB_BASE + 0x08)
34#define REG_CLKDIV_0 (MSM_APCS_GLB_BASE + 0x0C)
35#define REG_CLKSEL_1 (MSM_APCS_GLB_BASE + 0x10)
36#define REG_CLKDIV_1 (MSM_APCS_GLB_BASE + 0x14)
37#define REG_CLKOUTSEL (MSM_APCS_GLB_BASE + 0x18)
38
39enum clk_src {
40 SRC_CXO,
41 SRC_PLL0,
42 SRC_PLL8,
43 SRC_PLL9,
44 NUM_SRC,
45};
46
47struct src_clock {
48 struct clk *clk;
49 const char *name;
50};
51
52static struct src_clock clocks[NUM_SRC] = {
53 [SRC_CXO].name = "cxo",
54 [SRC_PLL0].name = "pll0",
55 [SRC_PLL8].name = "pll8",
56 [SRC_PLL9].name = "pll9",
57};
58
59struct clkctl_acpu_speed {
60 bool use_for_scaling;
61 unsigned int khz;
62 int src;
63 unsigned int src_sel;
64 unsigned int src_div;
65};
66
67struct acpuclk_state {
68 struct mutex lock;
69 struct clkctl_acpu_speed *current_speed;
70};
71
72static struct acpuclk_state drv_state = {
73 .current_speed = &(struct clkctl_acpu_speed){ 0 },
74};
75
76static struct clkctl_acpu_speed acpu_freq_tbl[] = {
77 { 0, 19200, SRC_CXO, 0, 0 },
78 { 1, 138000, SRC_PLL0, 6, 1 },
79 { 1, 276000, SRC_PLL0, 6, 0 },
80 { 1, 384000, SRC_PLL8, 3, 0 },
81 { 1, 440000, SRC_PLL9, 2, 0 },
82 { 0 }
83};
84
85static void select_clk_source_div(struct clkctl_acpu_speed *s)
86{
87 static void * __iomem const sel_reg[] = {REG_CLKSEL_0, REG_CLKSEL_1};
88 static void * __iomem const div_reg[] = {REG_CLKDIV_0, REG_CLKDIV_1};
89 uint32_t next_bank;
90
91 next_bank = !(readl_relaxed(REG_CLKOUTSEL) & 1);
92 writel_relaxed(s->src_sel, sel_reg[next_bank]);
93 writel_relaxed(s->src_div, div_reg[next_bank]);
94 writel_relaxed(next_bank, REG_CLKOUTSEL);
95
96 /* Wait for switch to complete. */
97 mb();
98 udelay(1);
99}
100
101static int acpuclk_9615_set_rate(int cpu, unsigned long rate,
102 enum setrate_reason reason)
103{
104 struct clkctl_acpu_speed *tgt_s, *strt_s;
105 int rc = 0;
106
107 if (reason == SETRATE_CPUFREQ)
108 mutex_lock(&drv_state.lock);
109
110 strt_s = drv_state.current_speed;
111
112 /* Return early if rate didn't change. */
113 if (rate == strt_s->khz)
114 goto out;
115
116 /* Find target frequency. */
117 for (tgt_s = acpu_freq_tbl; tgt_s->khz != 0; tgt_s++)
118 if (tgt_s->khz == rate)
119 break;
120 if (tgt_s->khz == 0) {
121 rc = -EINVAL;
122 goto out;
123 }
124
Matt Wagantallf5d64072011-10-13 14:15:19 -0700125 pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
Matt Wagantall44f672e2011-09-07 20:31:16 -0700126 strt_s->khz, tgt_s->khz);
127
128 /* Switch CPU speed. */
129 clk_enable(clocks[tgt_s->src].clk);
130 select_clk_source_div(tgt_s);
131 clk_disable(clocks[strt_s->src].clk);
132
133 drv_state.current_speed = tgt_s;
Matt Wagantallf5d64072011-10-13 14:15:19 -0700134 pr_debug("CPU speed change complete\n");
Matt Wagantall44f672e2011-09-07 20:31:16 -0700135
136out:
137 if (reason == SETRATE_CPUFREQ)
138 mutex_unlock(&drv_state.lock);
139 return rc;
140}
141
142static unsigned long acpuclk_9615_get_rate(int cpu)
143{
144 return drv_state.current_speed->khz;
145}
146
147#ifdef CONFIG_CPU_FREQ_MSM
148static struct cpufreq_frequency_table freq_table[30];
149
150static void __init cpufreq_table_init(void)
151{
152 int i, freq_cnt = 0;
153
154 /* Construct the freq_table tables from acpu_freq_tbl. */
155 for (i = 0; acpu_freq_tbl[i].khz != 0
156 && freq_cnt < ARRAY_SIZE(freq_table); i++) {
157 if (acpu_freq_tbl[i].use_for_scaling) {
158 freq_table[freq_cnt].index = freq_cnt;
159 freq_table[freq_cnt].frequency
160 = acpu_freq_tbl[i].khz;
161 freq_cnt++;
162 }
163 }
164 /* freq_table not big enough to store all usable freqs. */
165 BUG_ON(acpu_freq_tbl[i].khz != 0);
166
167 freq_table[freq_cnt].index = freq_cnt;
168 freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
169
170 pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
171
172 /* Register table with CPUFreq. */
173 cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
174}
175#else
176static void __init cpufreq_table_init(void) {}
177#endif
178
179static struct acpuclk_data acpuclk_9615_data = {
180 .set_rate = acpuclk_9615_set_rate,
181 .get_rate = acpuclk_9615_get_rate,
182 .power_collapse_khz = 19200,
183 .wait_for_irq_khz = 19200,
184};
185
186static int __init acpuclk_9615_init(struct acpuclk_soc_data *soc_data)
187{
188 unsigned long max_cpu_khz = 0;
189 int i;
190
191 mutex_init(&drv_state.lock);
192 for (i = 0; i < NUM_SRC; i++) {
193 if (clocks[i].name) {
194 clocks[i].clk = clk_get_sys(NULL, clocks[i].name);
195 BUG_ON(IS_ERR(clocks[i].clk));
196 }
197 }
198
199 /* Improve boot time by ramping up CPU immediately. */
200 for (i = 0; acpu_freq_tbl[i].khz != 0; i++)
201 max_cpu_khz = acpu_freq_tbl[i].khz;
202 acpuclk_9615_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT);
203
204 acpuclk_register(&acpuclk_9615_data);
205 cpufreq_table_init();
206
207 return 0;
208}
209
210struct acpuclk_soc_data acpuclk_9615_soc_data __initdata = {
211 .init = acpuclk_9615_init,
212};