-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathacpuclock-scorpion.c
577 lines (478 loc) · 14.9 KB
/
acpuclock-scorpion.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
/*
* Copyright (c) 2009 Google, Inc.
* Copyright (c) 2008 QUALCOMM Incorporated.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Note: Added AVS Support (Ivan Dimkovic)
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/regulator/consumer.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include "avs.h"
#include "acpuclock.h"
#include "proc_comm.h"
/* idimkovic: remove this if you wish no AVS support */
#define CONFIG_MSM_CPU_AVS 1
/* idimkovic: remove this if you wish no overclocking */
#define USE_OVERCLOCKING 1
#if 0
#define DEBUG(x...) pr_info(x)
#else
#define DEBUG(x...) do {} while (0)
#endif
#define SHOT_SWITCH 4
#define HOP_SWITCH 5
#define SIMPLE_SLEW 6
#define COMPLEX_SLEW 7
#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100)
#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104)
/* Scorpion PLL registers */
#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4)
#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18)
#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10)
struct clkctl_acpu_speed {
unsigned acpu_khz;
unsigned clk_cfg;
unsigned clk_sel;
unsigned sc_l_value;
unsigned lpj;
int vdd;
};
/* clock sources */
#define CLK_TCXO 0 /* 19.2 MHz */
#define CLK_GLOBAL_PLL 1 /* 768 MHz */
#define CLK_MODEM_PLL 4 /* 245 MHz (UMTS) or 235.93 MHz (CDMA) */
#define CCTL(src, div) (((src) << 4) | (div - 1))
/* core sources */
#define SRC_RAW 0 /* clock from SPSS_CLK_CNTL */
#define SRC_SCPLL 1 /* output of scpll 128-998 MHZ */
#define SRC_AXI 2 /* 128 MHz */
#define SRC_PLL1 3 /* 768 MHz */
struct clkctl_acpu_speed acpu_freq_tbl[] = {
{ 19200, CCTL(CLK_TCXO, 1), SRC_RAW, 0, 0, 1000 },
{ 128000, CCTL(CLK_TCXO, 1), SRC_AXI, 0, 0, 1000 },
{ 245000, CCTL(CLK_MODEM_PLL, 1), SRC_RAW, 0, 0, 1000 },
{ 256000, CCTL(CLK_GLOBAL_PLL, 3), SRC_RAW, 0, 0, 1000 },
{ 384000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0A, 0, 1000 },
{ 422400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0B, 0, 1000 },
{ 460800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0C, 0, 1000 },
{ 499200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0D, 0, 1025 },
{ 537600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0E, 0, 1050},
{ 576000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x0F, 0, 1050 },
{ 614400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x10, 0, 1075 },
{ 652800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x11, 0, 1100 },
{ 691200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x12, 0, 1125 },
{ 729600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x13, 0, 1150 },
{ 768000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x14, 0, 1150 },
{ 806400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x15, 0, 1175 },
{ 844800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x16, 0, 1200 },
{ 883200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x17, 0, 1225 },
{ 921600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x18, 0, 1250 },
{ 960000, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x19, 0, 1250 },
{ 998400, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1A, 0, 1250 },
#ifdef USE_OVERCLOCKING
/* idimkovic: overclocked frequencies */
{ 1036800, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1B, 0, 1250 },
{ 1075200, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1C, 0, 1250 },
{ 1113600, CCTL(CLK_TCXO, 1), SRC_SCPLL, 0x1D, 0, 1275 },
#endif
{ 0 },
};
/* select the standby clock that is used when switching scpll
* frequencies
*
* Currently: MPLL
*/
struct clkctl_acpu_speed *acpu_stby = &acpu_freq_tbl[2];
#define IS_ACPU_STANDBY(x) (((x)->clk_cfg == acpu_stby->clk_cfg) && \
((x)->clk_sel == acpu_stby->clk_sel))
struct clkctl_acpu_speed *acpu_mpll = &acpu_freq_tbl[2];
#ifdef CONFIG_CPU_FREQ_TABLE
static struct cpufreq_frequency_table freq_table[ARRAY_SIZE(acpu_freq_tbl)];
static void __init acpuclk_init_cpufreq_table(void)
{
int i;
int vdd;
for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) {
freq_table[i].index = i;
freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
/* Skip speeds we don't want */
if ( acpu_freq_tbl[i].acpu_khz == 19200 ||
acpu_freq_tbl[i].acpu_khz == 128000 ||
acpu_freq_tbl[i].acpu_khz == 256000)
continue;
vdd = acpu_freq_tbl[i].vdd;
/* Allow mpll and the first scpll speeds */
if (acpu_freq_tbl[i].acpu_khz == acpu_mpll->acpu_khz ||
acpu_freq_tbl[i].acpu_khz == 384000) {
freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
continue;
}
/* Add to the table */
//if (vdd != acpu_freq_tbl[i + 1].vdd)
freq_table[i].frequency = acpu_freq_tbl[i].acpu_khz;
}
freq_table[i].index = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
cpufreq_frequency_table_get_attr(freq_table, smp_processor_id());
}
#else
#define acpuclk_init_cpufreq_table() do {} while (0);
#endif
struct clock_state {
struct clkctl_acpu_speed *current_speed;
struct mutex lock;
uint32_t acpu_switch_time_us;
uint32_t max_speed_delta_khz;
uint32_t vdd_switch_time_us;
unsigned long power_collapse_khz;
unsigned long wait_for_irq_khz;
struct regulator *regulator;
int (*acpu_set_vdd) (int mvolts);
};
static struct clock_state drv_state = { 0 };
static DEFINE_SPINLOCK(acpu_lock);
#define PLLMODE_POWERDOWN 0
#define PLLMODE_BYPASS 1
#define PLLMODE_STANDBY 2
#define PLLMODE_FULL_CAL 4
#define PLLMODE_HALF_CAL 5
#define PLLMODE_STEP_CAL 6
#define PLLMODE_NORMAL 7
#define PLLMODE_MASK 7
#ifdef CONFIG_MSM_CPU_AVS
static int __init acpu_avs_init(int (*set_vdd) (int), int khz)
{
int i;
int freq_count = 0;
int freq_index = -1;
for (i = 0; acpu_freq_tbl[i].acpu_khz; i++) {
freq_count++;
if (acpu_freq_tbl[i].acpu_khz == khz)
freq_index = i;
}
return avs_init(set_vdd, freq_count, freq_index);
}
#endif
static void scpll_power_down(void)
{
uint32_t val;
/* Wait for any frequency switches to finish. */
while (readl(SCPLL_STATUS_ADDR) & 0x1)
;
/* put the pll in standby mode */
val = readl(SCPLL_CTL_ADDR);
val = (val & (~PLLMODE_MASK)) | PLLMODE_STANDBY;
writel(val, SCPLL_CTL_ADDR);
dmb();
/* wait to stabilize in standby mode */
udelay(10);
val = (val & (~PLLMODE_MASK)) | PLLMODE_POWERDOWN;
writel(val, SCPLL_CTL_ADDR);
dmb();
}
static void scpll_set_freq(uint32_t lval)
{
uint32_t val, ctl;
if (lval > 33)
lval = 33;
if (lval < 10)
lval = 10;
/* wait for any calibrations or frequency switches to finish */
while (readl(SCPLL_STATUS_ADDR) & 0x3)
;
ctl = readl(SCPLL_CTL_ADDR);
if ((ctl & PLLMODE_MASK) != PLLMODE_NORMAL) {
/* put the pll in standby mode */
writel((ctl & (~PLLMODE_MASK)) | PLLMODE_STANDBY, SCPLL_CTL_ADDR);
dmb();
/* wait to stabilize in standby mode */
udelay(10);
/* switch to 384 MHz */
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
val = (val & (~0x1FF)) | (0x0A << 3) | SHOT_SWITCH;
writel(val, SCPLL_FSM_CTL_EXT_ADDR);
dmb();
ctl = readl(SCPLL_CTL_ADDR);
writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
dmb();
/* wait for frequency switch to finish */
while (readl(SCPLL_STATUS_ADDR) & 0x1)
;
/* completion bit is not reliable for SHOT switch */
udelay(15);
}
/* write the new L val and switch mode */
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
val = (val & (~0x1FF)) | (lval << 3) | HOP_SWITCH;
writel(val, SCPLL_FSM_CTL_EXT_ADDR);
dmb();
ctl = readl(SCPLL_CTL_ADDR);
writel(ctl | PLLMODE_NORMAL, SCPLL_CTL_ADDR);
dmb();
/* wait for frequency switch to finish */
while (readl(SCPLL_STATUS_ADDR) & 0x1)
;
}
/* this is still a bit weird... */
static void select_clock(unsigned src, unsigned config)
{
uint32_t val;
if (src == SRC_RAW) {
uint32_t sel = readl(SPSS_CLK_SEL_ADDR);
unsigned shift = (sel & 1) ? 8 : 0;
/* set other clock source to the new configuration */
val = readl(SPSS_CLK_CNTL_ADDR);
val = (val & (~(0x7F << shift))) | (config << shift);
writel(val, SPSS_CLK_CNTL_ADDR);
/* switch to other clock source */
writel(sel ^ 1, SPSS_CLK_SEL_ADDR);
dmb(); /* necessary? */
}
/* switch to new source */
val = readl(SPSS_CLK_SEL_ADDR) & (~6);
writel(val | ((src & 3) << 1), SPSS_CLK_SEL_ADDR);
}
static int acpuclk_set_vdd_level(int vdd)
{
if (drv_state.acpu_set_vdd)
return drv_state.acpu_set_vdd(vdd);
else {
/* Assume that the PMIC supports scaling the processor
* to its maximum frequency at its default voltage.
*/
return 0;
}
}
static int acpu_set_vdd(int vdd)
{
if (!drv_state.regulator || IS_ERR(drv_state.regulator)) {
drv_state.regulator = regulator_get(NULL, "acpu_vcore");
if (IS_ERR(drv_state.regulator)) {
pr_info("acpuclk_set_vdd_level %d no regulator\n", vdd);
/* Assume that the PMIC supports scaling the processor
* to its maximum frequency at its default voltage.
*/
return 0;
}
pr_info("acpuclk_set_vdd_level got regulator\n");
}
vdd *= 1000; /* mV -> uV */
return regulator_set_voltage(drv_state.regulator, vdd, vdd);
}
int acpuclk_set_rate(unsigned long rate, int for_power_collapse)
{
struct clkctl_acpu_speed *cur, *next;
unsigned long flags;
int freq_index = 0;
cur = drv_state.current_speed;
/* convert to KHz */
rate /= 1000;
DEBUG("acpuclk_set_rate(%d,%d)\n", (int) rate, for_power_collapse);
if (rate == 0 || rate == cur->acpu_khz)
return 0;
next = acpu_freq_tbl;
for (;;) {
if (next->acpu_khz == rate)
break;
if (next->acpu_khz == 0)
return -EINVAL;
next++;
freq_index++;
}
if (!for_power_collapse) {
mutex_lock(&drv_state.lock);
#ifdef CONFIG_MSM_CPU_AVS
/* Notify avs before changing frequency */
if (avs_adjust_freq(freq_index, 1)) {
pr_err( "acpuclock: Unable to increase ACPU vdd.\n");
mutex_unlock(&drv_state.lock);
return -EINVAL;
}
#endif
/* Increase VDD if needed. */
if (next->vdd > cur->vdd) {
if (acpuclk_set_vdd_level(next->vdd)) {
pr_err("acpuclock: Unable to increase ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int)rate);
mutex_unlock(&drv_state.lock);
return -EINVAL;
}
}
}
spin_lock_irqsave(&acpu_lock, flags);
DEBUG("sel=%d cfg=%02x lv=%02x -> sel=%d, cfg=%02x lv=%02x\n",
cur->clk_sel, cur->clk_cfg, cur->sc_l_value,
next->clk_sel, next->clk_cfg, next->sc_l_value);
if (next->clk_sel == SRC_SCPLL) {
if (!IS_ACPU_STANDBY(cur))
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
loops_per_jiffy = next->lpj;
scpll_set_freq(next->sc_l_value);
select_clock(SRC_SCPLL, 0);
} else {
loops_per_jiffy = next->lpj;
if (cur->clk_sel == SRC_SCPLL) {
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
select_clock(next->clk_sel, next->clk_cfg);
scpll_power_down();
} else {
select_clock(next->clk_sel, next->clk_cfg);
}
}
drv_state.current_speed = next;
spin_unlock_irqrestore(&acpu_lock, flags);
if (!for_power_collapse) {
#ifdef CONFIG_MSM_CPU_AVS
/* notify avs after changing frequency */
if (avs_adjust_freq(freq_index, 0)) {
pr_err("acpuclock: Unable to drop ACPU vdd.\n");
mutex_unlock(&drv_state.lock);
return -EINVAL;
}
#endif
/* Drop VDD level if we can. */
if (next->vdd < cur->vdd) {
if (acpuclk_set_vdd_level(next->vdd))
pr_err("acpuclock: Unable to drop ACPU VDD from %d to %d setting rate to %d.\n", cur->vdd, next->vdd, (int) rate);
}
mutex_unlock(&drv_state.lock);
}
return 0;
}
static unsigned __init acpuclk_find_speed(void)
{
uint32_t sel, val;
sel = readl(SPSS_CLK_SEL_ADDR);
switch ((sel & 6) >> 1) {
case 1:
val = readl(SCPLL_FSM_CTL_EXT_ADDR);
val = (val >> 3) & 0x3f;
return val * 38400;
case 2:
return 128000;
default:
pr_err("acpu_find_speed: failed\n");
BUG();
return 0;
}
}
#define PCOM_MODEM_PLL 0
static int pll_request(unsigned id, unsigned on)
{
on = !!on;
return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
}
static void __init acpuclk_init(void)
{
struct clkctl_acpu_speed *speed;
unsigned init_khz;
init_khz = acpuclk_find_speed();
/* request the modem pll, and then drop it. We don't want to keep a
* ref to it, but we do want to make sure that it is initialized at
* this point. The ARM9 will ensure that the MPLL is always on
* once it is fully booted, but it may not be up by the time we get
* to here. So, our pll_request for it will block until the mpll is
* actually up. We want it up because we will want to use it as a
* temporary step during frequency scaling. */
pll_request(PCOM_MODEM_PLL, 1);
pll_request(PCOM_MODEM_PLL, 0);
if (!(readl(MSM_CLK_CTL_BASE + 0x300) & 1)) {
pr_err("%s: MPLL IS NOT ON!!! RUN AWAY!!\n", __func__);
BUG();
}
/* Move to 883MHz for boot, which is a safe frequency
* for all versions of Scorpion at the moment.
*/
speed = acpu_freq_tbl;
for (;;) {
if (speed->acpu_khz == 883200)
break;
if (speed->acpu_khz == 0) {
pr_err("acpuclk_init: cannot find 883MHz\n");
BUG();
}
speed++;
}
if (init_khz != speed->acpu_khz) {
/* Bootloader needs to have SCPLL operating, but we're
* going to step over to the standby clock and make sure
* we select the right frequency on SCPLL and then
* step back to it, to make sure we're sane here.
*/
select_clock(acpu_stby->clk_sel, acpu_stby->clk_cfg);
scpll_power_down();
scpll_set_freq(speed->sc_l_value);
select_clock(SRC_SCPLL, 0);
}
drv_state.current_speed = speed;
for (speed = acpu_freq_tbl; speed->acpu_khz; speed++)
speed->lpj = cpufreq_scale(loops_per_jiffy,
init_khz, speed->acpu_khz);
loops_per_jiffy = drv_state.current_speed->lpj;
}
unsigned long acpuclk_get_rate(void)
{
return drv_state.current_speed->acpu_khz;
}
uint32_t acpuclk_get_switch_time(void)
{
return drv_state.acpu_switch_time_us;
}
unsigned long acpuclk_power_collapse(void)
{
int ret = acpuclk_get_rate();
if (ret > drv_state.power_collapse_khz)
acpuclk_set_rate(drv_state.power_collapse_khz * 1000, 1);
return ret * 1000;
}
unsigned long acpuclk_get_wfi_rate(void)
{
return drv_state.wait_for_irq_khz * 1000;
}
unsigned long acpuclk_wait_for_irq(void)
{
int ret = acpuclk_get_rate();
if (ret > drv_state.wait_for_irq_khz)
acpuclk_set_rate(drv_state.wait_for_irq_khz * 1000, 1);
return ret * 1000;
}
void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata)
{
spin_lock_init(&acpu_lock);
mutex_init(&drv_state.lock);
drv_state.acpu_set_vdd = acpu_set_vdd;
drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us;
drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz;
drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us;
drv_state.power_collapse_khz = clkdata->power_collapse_khz;
drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz;
if (clkdata->mpll_khz)
acpu_mpll->acpu_khz = clkdata->mpll_khz;
acpuclk_init();
acpuclk_init_cpufreq_table();
#ifdef CONFIG_MSM_CPU_AVS
if (!acpu_avs_init(drv_state.acpu_set_vdd,
drv_state.current_speed->acpu_khz)) {
/* avs init successful. avs will handle voltage changes */
drv_state.acpu_set_vdd = NULL;
} else {
pr_err("acpuclock: Unable to initialize AVS!\n");
}
#endif
}