1 /* linux/arch/arm/mach-s5pv310/busfreq.c
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
6 * S5PV310 - BUS clock frequency scaling support
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/init.h>
14 #include <linux/interrupt.h>
15 #include <linux/types.h>
16 #include <linux/err.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/sysfs.h>
20 #include <linux/platform_device.h>
21 #include <linux/device.h>
22 #include <linux/module.h>
23 #include <linux/cpu.h>
24 #include <linux/ktime.h>
25 #include <linux/tick.h>
26 #include <linux/kernel_stat.h>
27 #include <linux/cpufreq.h>
28 #include <linux/suspend.h>
30 #include <mach/ppmu.h>
32 #include <mach/regs-clock.h>
33 #include <plat/map-s5p.h>
34 #include <mach/gpio.h>
35 #include <mach/regs-mem.h>
36 #include <mach/cpufreq.h>
37 #include <plat/gpio-cfg.h>
38 #include <asm/mach-types.h>
42 #define DIVIDING_FACTOR 10000
43 #define UP_THRESHOLD_DEFAULT 17
45 #define BUS_SATURATION 40
47 static bool init_complete;
48 static unsigned up_threshold;
49 static struct regulator *int_regulator;
50 static struct s5pv310_ppmu_hw dmc[2];
51 static struct s5pv310_ppmu_hw cpu;
52 static unsigned int bus_utilization[2];
54 static bool halt_busfreq;
56 static unsigned int g_busfreq_lock_id;
57 static unsigned int g_busfreq_lock_val[DVFS_LOCK_ID_END];
58 static unsigned int g_busfreq_lock_level;
60 static DEFINE_MUTEX(set_bus_freq_lock);
62 enum busfreq_level_idx {
69 static unsigned int p_idx;
70 static unsigned int curr_idx;
71 static unsigned int minFreq;
73 struct busfreq_table {
79 struct busfreq_clkdiv {
84 static struct busfreq_table s5pv310_busfreq_table[] = {
85 {LV_0, 400000, 1100000},
86 {LV_1, 267000, 1000000},
87 {LV_2, 133000, 950000},
92 static unsigned int s5pv310_asv_volt[ASV_GROUP][LV_END] = {
93 {1150000, 1050000, 1025000},
94 {1125000, 1025000, 1000000},
95 {1100000, 1000000, 975000},
96 {1075000, 975000, 950000},
97 {1050000, 950000, 950000},
100 static unsigned int clkdiv_dmc0[LV_END][8] = {
102 * Clock divider value for following
103 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
104 * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
108 { 3, 1, 1, 1, 1, 1, 3, 1 },
110 /* DMC L1: 266.7MHz */
111 { 4, 1, 1, 2, 1, 1, 3, 1 },
114 { 5, 1, 1, 5, 1, 1, 3, 1 },
117 static unsigned int clkdiv_top[LV_END][5] = {
119 * Clock divider value for following
120 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
123 /* ACLK200 L0: 200MHz */
126 /* ACLK200 L1: 160MHz */
129 /* ACLK200 L2: 133MHz */
133 static unsigned int clkdiv_lr_bus[LV_END][2] = {
135 * Clock divider value for following
136 * { DIVGDL/R, DIVGPL/R }
139 /* ACLK_GDL/R L1: 200MHz */
142 /* ACLK_GDL/R L2: 160MHz */
145 /* ACLK_GDL/R L3: 133MHz */
149 static struct busfreq_clkdiv busfreq_dmc_divtable[] = {
155 static struct busfreq_clkdiv busfreq_top_divtable[] = {
161 static void s5pv310_set_busfreq(unsigned int div_index)
165 /* Change Divider - DMC0 */
166 tmp = busfreq_dmc_divtable[div_index].clkdiv;
168 __raw_writel(tmp, S5P_CLKDIV_DMC0);
171 tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
172 } while (tmp & 0x11111111);
174 /* Change Divider - TOP */
175 tmp = busfreq_top_divtable[div_index].clkdiv;
177 __raw_writel(tmp, S5P_CLKDIV_TOP);
180 tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
181 } while (tmp & 0x11111);
183 /* Change Divider - LEFTBUS */
184 tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);
186 tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
188 tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
189 (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
191 __raw_writel(tmp, S5P_CLKDIV_LEFTBUS);
194 tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
195 } while (tmp & 0x11);
197 /* Change Divider - RIGHTBUS */
198 tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);
200 tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
202 tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
203 (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));
205 __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);
208 tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
209 } while (tmp & 0x11);
213 static unsigned int calc_bus_utilization(struct s5pv310_ppmu_hw *ppmu)
215 if (ppmu->ccnt == 0) {
216 pr_err("%s: 0 value is not permitted\n", __func__);
220 if (!(ppmu->ccnt >> 7))
221 return (ppmu->count[0] * 100) / ppmu->ccnt;
223 return ((ppmu->count[0] >> 7) * 100) / (ppmu->ccnt >> 7);
227 static int busfreq_target(struct busfreq_table *freq_table,
228 unsigned int ppc_load,
229 unsigned int ppmu_load,
230 unsigned int pre_idx,
233 unsigned int i, target_freq, idx = 0;
236 pr_debug("Busfreq: Bus Load is larger than 40(%d)\n", ppc_load);
240 if (pre_idx >= LV_END) {
241 WARN(true, "Errornous value entered: %d\n", pre_idx);
242 pre_idx = LV_END - 1;
245 target_freq = ppc_load * freq_table[pre_idx].mem_clk * 100
246 / up_threshold / BUS_SATURATION;
248 for (i = 0; (freq_table[i].mem_clk != 0); i++) {
249 if (freq_table[i].mem_clk >= target_freq)
255 if (idx > g_busfreq_lock_level) {
256 pr_debug("LOCKED BUSFREQ %d->%d\n", idx, g_busfreq_lock_level);
257 idx = g_busfreq_lock_level;
261 WARN(true, "Errornous value entered: %d\n", idx);
270 static void busfreq_mon_reset(void)
274 for (i = 0; i < 2; i++) {
275 s5pv310_ppc_reset(&dmc[i]);
276 s5pv310_ppc_setevent(&dmc[i], 0x6);
277 s5pv310_ppc_start(&dmc[i]);
280 s5pv310_ppmu_reset(&cpu);
281 s5pv310_ppmu_setevent(&cpu, 0x5, 0);
282 s5pv310_ppmu_setevent(&cpu, 0x6, 1);
283 s5pv310_ppmu_start(&cpu);
287 static unsigned int busfreq_monitor(void)
289 unsigned int i, index = 0, ppcload, ppmuload, ret;
291 for (i = 0; i < 2; i++) {
292 s5pv310_ppc_stop(&dmc[i]);
293 s5pv310_ppc_update(&dmc[i]);
294 bus_utilization[i] = calc_bus_utilization(&dmc[i]);
297 s5pv310_ppmu_stop(&cpu);
298 s5pv310_ppmu_update(&cpu);
304 ppcload = max(bus_utilization[0], bus_utilization[1]);
305 ppmuload = (cpu.count[0] + cpu.count[1])*100 / cpu.ccnt;
308 /* Change bus frequency */
309 ret = busfreq_target(s5pv310_busfreq_table, ppcload,
310 ppmuload, p_idx, &index);
312 pr_err("%s: (%d)\n", __func__, ret);
314 pr_debug("Bus freq(%d-%d) / ppmu %u, ppc %u\n", p_idx, index, ppmuload, ppcload);
321 void s5pv310_busfreq_force_notify(struct cpufreq_freqs *freq)
323 unsigned int voltage;
331 /* Skip reevaluating at idling */
332 if (freq->new <= 200000 && p_idx == LV_2)
335 mutex_lock(&set_bus_freq_lock);
337 curr_idx = busfreq_monitor();
338 voltage = s5pv310_busfreq_table[curr_idx].volt;
339 if (p_idx > curr_idx) {
340 regulator_set_voltage(int_regulator, voltage, voltage);
343 if (p_idx != curr_idx)
344 s5pv310_set_busfreq(curr_idx);
346 if (p_idx < curr_idx) {
347 regulator_set_voltage(int_regulator, voltage, voltage);
351 mutex_unlock(&set_bus_freq_lock);
354 static int s5pv310_busfreq_notifier_event(struct notifier_block *this,
355 unsigned long event, void *ptr)
357 unsigned int voltage;
366 case CPUFREQ_PRECHANGE:
367 mutex_lock(&set_bus_freq_lock);
368 curr_idx = busfreq_monitor();
369 mutex_unlock(&set_bus_freq_lock);
371 case CPUFREQ_POSTCHANGE:
372 mutex_lock(&set_bus_freq_lock);
373 voltage = s5pv310_busfreq_table[curr_idx].volt;
374 if (p_idx > curr_idx) {
375 regulator_set_voltage(int_regulator, voltage,
379 if (p_idx != curr_idx)
380 s5pv310_set_busfreq(curr_idx);
382 if (p_idx < curr_idx) {
383 regulator_set_voltage(int_regulator, voltage,
387 mutex_unlock(&set_bus_freq_lock);
389 case CPUFREQ_RESUMECHANGE:
399 static struct notifier_block s5pv310_busfreq_notifier = {
400 .notifier_call = s5pv310_busfreq_notifier_event,
403 static int s5pv310_buspm_notifier_event(struct notifier_block *this,
404 unsigned long event, void *ptr)
407 case PM_RESTORE_PREPARE:
408 case PM_SUSPEND_PREPARE:
409 case PM_HIBERNATION_PREPARE:
410 s5pv310_busfreq_lock(DVFS_LOCK_ID_PM, BUS_L0);
413 case PM_POST_RESTORE:
414 case PM_POST_SUSPEND:
415 case PM_POST_HIBERNATION:
416 halt_busfreq = false;
418 s5pv310_busfreq_lock_free(DVFS_LOCK_ID_PM);
424 static struct notifier_block s5pv310_buspm_notifier = {
425 .notifier_call = s5pv310_buspm_notifier_event,
428 int s5pv310_busfreq_lock(unsigned int nId,
429 enum busfreq_level_request busfreq_level)
431 unsigned int int_volt;
433 mutex_lock(&set_bus_freq_lock);
434 if (g_busfreq_lock_id & (1 << nId)) {
435 pr_err("This device [%d] already locked busfreq\n", nId);
438 g_busfreq_lock_id |= (1 << nId);
439 g_busfreq_lock_val[nId] = busfreq_level;
444 /* If the requested cpufreq is higher than current min frequency */
445 if (busfreq_level < g_busfreq_lock_level) {
446 g_busfreq_lock_level = busfreq_level;
448 /* Change now if the wanted freq is higher */
449 if (busfreq_level < p_idx) {
450 /* get the voltage value */
451 int_volt = s5pv310_busfreq_table[busfreq_level].volt;
452 regulator_set_voltage(int_regulator, int_volt,
454 s5pv310_set_busfreq(busfreq_level);
458 mutex_unlock(&set_bus_freq_lock);
462 void s5pv310_busfreq_lock_free(unsigned int nId)
466 mutex_lock(&set_bus_freq_lock);
467 g_busfreq_lock_id &= ~(1 << nId);
468 g_busfreq_lock_val[nId] = minFreq;
469 g_busfreq_lock_level = minFreq;
471 if (g_busfreq_lock_id) {
472 for (i = 0; i < DVFS_LOCK_ID_END; i++) {
473 if (g_busfreq_lock_val[i] < g_busfreq_lock_level)
474 g_busfreq_lock_level = g_busfreq_lock_val[i];
477 mutex_unlock(&set_bus_freq_lock);
480 static void __init s5pv310_set_bus_volt(void)
482 unsigned int asv_group;
485 asv_group = exynos4_asv_group;
487 printk(KERN_INFO "DVFS : VDD_INT Voltage table set with %d Group\n", asv_group);
489 for (i = 0 ; i < LV_END ; i++) {
492 s5pv310_busfreq_table[i].volt =
493 s5pv310_asv_volt[0][i];
497 s5pv310_busfreq_table[i].volt =
498 s5pv310_asv_volt[1][i];
502 s5pv310_busfreq_table[i].volt =
503 s5pv310_asv_volt[2][i];
507 s5pv310_busfreq_table[i].volt =
508 s5pv310_asv_volt[3][i];
511 s5pv310_busfreq_table[i].volt =
512 s5pv310_asv_volt[4][i];
520 static int __init busfreq_mon_init(void)
524 struct cpufreq_frequency_table *table;
526 if (machine_is_smdkv310())
529 table = cpufreq_frequency_get_table(0);
531 if (IS_ERR_OR_NULL(table))
534 for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
535 /* Donthing to do currently */
538 minFreq = table[i-1].frequency ;
539 g_busfreq_lock_level = minFreq;
541 cpu.hw_base = S5P_VA_PPMU_CPU;
543 dmc[DMC0].hw_base = S5P_VA_DMC0;
544 dmc[DMC1].hw_base = S5P_VA_DMC1;
547 up_threshold = UP_THRESHOLD_DEFAULT;
549 tmp = __raw_readl(S5P_CLKDIV_DMC0);
551 for (i = 0; i < LV_END; i++) {
552 tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK |
553 S5P_CLKDIV_DMC0_ACPPCLK_MASK |
554 S5P_CLKDIV_DMC0_DPHY_MASK |
555 S5P_CLKDIV_DMC0_DMC_MASK |
556 S5P_CLKDIV_DMC0_DMCD_MASK |
557 S5P_CLKDIV_DMC0_DMCP_MASK |
558 S5P_CLKDIV_DMC0_COPY2_MASK |
559 S5P_CLKDIV_DMC0_CORETI_MASK);
561 tmp |= ((clkdiv_dmc0[i][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) |
562 (clkdiv_dmc0[i][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
563 (clkdiv_dmc0[i][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) |
564 (clkdiv_dmc0[i][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) |
565 (clkdiv_dmc0[i][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) |
566 (clkdiv_dmc0[i][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) |
567 (clkdiv_dmc0[i][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) |
568 (clkdiv_dmc0[i][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT));
570 busfreq_dmc_divtable[i].clkdiv = tmp;
573 tmp = __raw_readl(S5P_CLKDIV_TOP);
575 for (i = 0; i < LV_END; i++) {
576 tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK |
577 S5P_CLKDIV_TOP_ACLK100_MASK |
578 S5P_CLKDIV_TOP_ACLK160_MASK |
579 S5P_CLKDIV_TOP_ACLK133_MASK |
580 S5P_CLKDIV_TOP_ONENAND_MASK);
582 tmp |= ((clkdiv_top[i][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) |
583 (clkdiv_top[i][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) |
584 (clkdiv_top[i][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) |
585 (clkdiv_top[i][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) |
586 (clkdiv_top[i][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT));
588 busfreq_top_divtable[i].clkdiv = tmp;
591 s5pv310_set_bus_volt();
593 int_regulator = regulator_get(NULL, "vdd_int");
594 if (IS_ERR(int_regulator)) {
595 pr_err("failed to get resource %s\n", "vdd_int");
600 if (cpufreq_register_notifier(&s5pv310_busfreq_notifier,
601 CPUFREQ_TRANSITION_NOTIFIER)) {
602 pr_err("Failed to setup cpufreq notifier\n");
606 if (register_pm_notifier(&s5pv310_buspm_notifier)) {
607 pr_err("Failed to setup buspm notifier\n");
611 init_complete = true;
614 cpufreq_unregister_notifier(&s5pv310_busfreq_notifier,
615 CPUFREQ_TRANSITION_NOTIFIER);
617 if (!IS_ERR(int_regulator))
618 regulator_put(int_regulator);
621 late_initcall(busfreq_mon_init);