aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-exynos/busfreq_opp_5250.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-exynos/busfreq_opp_5250.c')
-rw-r--r--arch/arm/mach-exynos/busfreq_opp_5250.c892
1 files changed, 892 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/busfreq_opp_5250.c b/arch/arm/mach-exynos/busfreq_opp_5250.c
new file mode 100644
index 0000000..bcedb46
--- /dev/null
+++ b/arch/arm/mach-exynos/busfreq_opp_5250.c
@@ -0,0 +1,892 @@
+/* linux/arch/arm/mach-exynos/busfreq_opp_5250.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS5 - BUS clock frequency scaling support with OPP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/ktime.h>
+#include <linux/tick.h>
+#include <linux/kernel_stat.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/opp.h>
+#include <linux/clk.h>
+#include <mach/busfreq_exynos5.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/ppmu.h>
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/gpio.h>
+#include <mach/regs-mem.h>
+#include <mach/dev.h>
+#include <mach/asv.h>
+#include <mach/regs-pmu-5250.h>
+
+#include <plat/map-s5p.h>
+#include <plat/gpio-cfg.h>
+#include <plat/cpu.h>
+#include <plat/clock.h>
+
+#undef BUSFREQ_PROFILE_DEBUG
+
+#define IDLE_THRESHOLD 4
+#define MIF_R1_THRESHOLD 20
+#define MIF_MAX_THRESHOLD 20
+#define INT_MAX_THRESHOLD 20
+#define INT_RIGHT0_THRESHOLD 25
+#define INT_VIDEOPLAY_LIMIT_FREQ 200000UL
+#define INT_RBB 6 /* +300mV */
+
+static struct device busfreq_for_int;
+
+/* To save/restore DREX2_PAUSE_CTRL register */
+static unsigned int drex2_pause_ctrl;
+
+static struct busfreq_table exynos5_busfreq_table_for800[] = {
+ {LV_0, 800000, 1000000, 0, 0, 0},
+ {LV_1, 400000, 1000000, 0, 0, 0},
+ {LV_2, 160000, 1000000, 0, 0, 0},
+};
+
+static struct busfreq_table exynos5_busfreq_table_for667[] = {
+ {LV_0, 667000, 1000000, 0, 0, 0},
+ {LV_1, 334000, 1000000, 0, 0, 0},
+ {LV_2, 111000, 1000000, 0, 0, 0},
+};
+
+static struct busfreq_table exynos5_busfreq_table_for533[] = {
+ {LV_0, 533000, 1000000, 0, 0, 0},
+ {LV_1, 267000, 1000000, 0, 0, 0},
+ {LV_2, 107000, 1000000, 0, 0, 0},
+};
+
+static struct busfreq_table exynos5_busfreq_table_for400[] = {
+ {LV_0, 400000, 1000000, 0, 0, 0},
+ {LV_1, 267000, 1000000, 0, 0, 0},
+ {LV_2, 100000, 1000000, 0, 0, 0},
+};
+#define ASV_GROUP 10
+static unsigned int asv_group_index;
+
+static unsigned int exynos5_mif_volt_for800[ASV_GROUP+1][LV_MIF_END] = {
+ /* L0 L1 L2 */
+ { 0, 0, 0}, /* ASV0 */
+ {1200000, 1000000, 950000}, /* ASV1 */
+ {1200000, 1000000, 900000}, /* ASV2 */
+ {1200000, 1050000, 950000}, /* ASV3 */
+ {1150000, 1000000, 900000}, /* ASV4 */
+ {1150000, 1050000, 950000}, /* ASV5 */
+ {1150000, 1050000, 950000}, /* ASV6 */
+ {1100000, 1000000, 900000}, /* ASV7 */
+ {1100000, 1000000, 900000}, /* ASV8 */
+ {1100000, 1000000, 900000}, /* ASV9 */
+ {1100000, 1000000, 900000}, /* ASV10 */
+};
+
+static unsigned int exynos5_mif_volt_for667[ASV_GROUP+1][LV_MIF_END] = {
+ /* L0 L1 L2 */
+ { 0, 0, 0}, /* ASV0 */
+ {1100000, 1000000, 950000}, /* ASV1 */
+ {1050000, 1000000, 950000}, /* ASV2 */
+ {1050000, 1050000, 950000}, /* ASV3 */
+ {1050000, 1000000, 950000}, /* ASV4 */
+ {1050000, 1050000, 1000000}, /* ASV5 */
+ {1050000, 1050000, 950000}, /* ASV6 */
+ {1050000, 1000000, 900000}, /* ASV7 */
+ {1050000, 1000000, 900000}, /* ASV8 */
+ {1050000, 1000000, 900000}, /* ASV9 */
+ {1050000, 1000000, 900000}, /* ASV10 */
+};
+
+static unsigned int exynos5_mif_volt_for533[ASV_GROUP+1][LV_MIF_END] = {
+ /* L0 L1 L2 */
+ { 0, 0, 0}, /* ASV0 */
+ {1050000, 1000000, 950000}, /* ASV1 */
+ {1000000, 950000, 950000}, /* ASV2 */
+ {1050000, 1000000, 950000}, /* ASV3 */
+ {1000000, 950000, 950000}, /* ASV4 */
+ {1050000, 1000000, 1000000}, /* ASV5 */
+ {1050000, 950000, 950000}, /* ASV6 */
+ {1000000, 950000, 900000}, /* ASV7 */
+ {1000000, 950000, 900000}, /* ASV8 */
+ {1000000, 950000, 900000}, /* ASV9 */
+ {1000000, 950000, 900000}, /* ASV10 */
+};
+
+static unsigned int exynos5_mif_volt_for400[ASV_GROUP+1][LV_MIF_END] = {
+ /* L0 L1 L2 */
+ { 0, 0, 0}, /* ASV0 */
+ {1000000, 1000000, 950000}, /* ASV1 */
+ {1000000, 950000, 900000}, /* ASV2 */
+ {1050000, 1000000, 950000}, /* ASV3 */
+ {1000000, 950000, 900000}, /* ASV4 */
+ {1050000, 1000000, 950000}, /* ASV5 */
+ {1050000, 950000, 950000}, /* ASV6 */
+ {1000000, 950000, 900000}, /* ASV7 */
+ {1000000, 950000, 900000}, /* ASV8 */
+ {1000000, 950000, 900000}, /* ASV9 */
+ {1000000, 950000, 900000}, /* ASV10 */
+};
+
+static struct busfreq_table *exynos5_busfreq_table_mif;
+
+static unsigned int (*exynos5_mif_volt)[LV_MIF_END];
+
+static struct busfreq_table exynos5_busfreq_table_int[] = {
+ {LV_0, 267000, 1000000, 0, 0, 0},
+ {LV_1, 200000, 1000000, 0, 0, 0},
+ {LV_2, 160000, 1000000, 0, 0, 0},
+ {LV_3, 133000, 1000000, 0, 0, 0},
+};
+
+static unsigned int exynos5_int_volt[ASV_GROUP+1][LV_INT_END] = {
+ /* L0 L1 L2 L3 */
+ { 0, 0, 0, 0}, /* ASV0 */
+ {1025000, 987500, 975000, 950000}, /* ASV1 */
+ {1012500, 975000, 962500, 937500}, /* ASV2 */
+ {1012500, 987500, 975000, 950000}, /* ASV3 */
+ {1000000, 975000, 962500, 937500}, /* ASV4 */
+ {1012500, 987500, 975000, 950000}, /* ASV5 */
+ {1000000, 975000, 962500, 937500}, /* ASV6 */
+ { 987500, 962500, 950000, 925000}, /* ASV7 */
+ { 975000, 950000, 937500, 912500}, /* ASV8 */
+ { 962500, 937500, 925000, 900000}, /* ASV9 */
+ { 962500, 937500, 925000, 900000}, /* ASV10 */
+};
+
+
+/* For CMU_LEX */
+static unsigned int clkdiv_lex[LV_INT_END][2] = {
+ /*
+ * Clock divider value for following
+ * { DIVATCLK_LEX, DIVPCLK_LEX }
+ */
+
+ /* ATCLK_LEX L0 : 200MHz */
+ {0, 1},
+
+ /* ATCLK_LEX L1 : 166MHz */
+ {0, 1},
+
+ /* ATCLK_LEX L2 : 133MHz */
+ {0, 1},
+
+ /* ATCLK_LEX L3 : 114MHz */
+ {0, 1},
+};
+
+/* For CMU_R0X */
+static unsigned int clkdiv_r0x[LV_INT_END][1] = {
+ /*
+ * Clock divider value for following
+ * { DIVPCLK_R0X }
+ */
+
+ /* ACLK_PR0X L0 : 133MHz */
+ {1},
+
+ /* ACLK_DR0X L1 : 100MHz */
+ {1},
+
+ /* ACLK_PR0X L2 : 80MHz */
+ {1},
+
+ /* ACLK_PR0X L3 : 67MHz */
+ {1},
+};
+
+/* For CMU_R1X */
+static unsigned int clkdiv_r1x[LV_INT_END][1] = {
+ /*
+ * Clock divider value for following
+ * { DIVPCLK_R1X }
+ */
+
+ /* ACLK_PR1X L0 : 133MHz */
+ {1},
+
+ /* ACLK_DR1X L1 : 100MHz */
+ {1},
+
+ /* ACLK_PR1X L2 : 80MHz */
+ {1},
+
+ /* ACLK_PR1X L3 : 67MHz */
+ {1},
+};
+
+/* For CMU_TOP */
+static unsigned int clkdiv_top[LV_INT_END][10] = {
+ /*
+ * Clock divider value for following
+ * { DIVACLK400_ISP, DIVACLK400_IOP, DIVACLK266, DIVACLK_200, DIVACLK_66_PRE,
+ DIVACLK_66, DIVACLK_333, DIVACLK_166, DIVACLK_300_DISP1, DIVACLK300_GSCL }
+ */
+
+ /* ACLK_400_ISP L0 : 400MHz */
+ {1, 1, 2, 3, 1, 5, 0, 1, 2, 2},
+
+ /* ACLK_400_ISP L1 : 267MHz */
+ {2, 3, 3, 4, 1, 5, 1, 2, 2, 2},
+
+ /* ACLK_400_ISP L2 : 200MHz */
+ {3, 3, 4, 4, 1, 5, 2, 3, 2, 2},
+
+ /* ACLK_400_ISP L3 : 160MHz */
+ {4, 4, 5, 5, 1, 5, 2, 3, 5, 5},
+};
+
+/* For CMU_CDREX */
+static unsigned int clkdiv_cdrex_for800[LV_MIF_END][9] = {
+ /*
+ * Clock divider value for following
+ * { DIVMCLK_DPHY, DIVMCLK_CDREX2, DIVACLK_CDREX, DIVMCLK_CDREX,
+ DIVPCLK_CDREX, DIVC2C, DIVC2C_ACLK, DIVMCLK_EFPHY, DIVACLK_EFCON }
+ */
+
+ /* MCLK_CDREX L0: 800MHz */
+ {0, 0, 1, 0, 5, 1, 1, 4, 1},
+
+ /* MCLK_CDREX L1: 400MHz */
+ {0, 1, 1, 1, 3, 2, 1, 5, 1},
+
+ /* MCLK_CDREX L2: 100MHz */
+ {0, 4, 1, 1, 7, 7, 1, 15, 1},
+};
+
+static unsigned int clkdiv_cdrex_for667[LV_MIF_END][9] = {
+ /*
+ * Clock divider value for following
+ * { DIVMCLK_DPHY, DIVMCLK_CDREX2, DIVACLK_CDREX, DIVMCLK_CDREX,
+ DIVPCLK_CDREX, DIVC2C, DIVC2C_ACLK, DIVMCLK_EFPHY, DIVACLK_EFCON }
+ */
+
+ /* MCLK_CDREX L0: 667MHz */
+ {0, 0, 1, 0, 4, 1, 1, 4, 1},
+
+ /* MCLK_CDREX L1: 334MHz */
+ {0, 1, 1, 1, 4, 2, 1, 5, 1},
+
+ /* MCLK_CDREX L2: 111MHz */
+ {0, 5, 1, 4, 4, 5, 1, 8, 1},
+};
+
+static unsigned int clkdiv_cdrex_for533[LV_MIF_END][9] = {
+ /*
+ * Clock divider value for following
+ * { DIVMCLK_DPHY, DIVMCLK_CDREX2, DIVACLK_CDREX, DIVMCLK_CDREX,
+ DIVPCLK_CDREX, DIVC2C, DIVC2C_ACLK, DIVMCLK_EFPHY, DIVACLK_EFCON }
+ */
+
+ /* MCLK_CDREX L0: 533MHz */
+ {0, 0, 1, 0, 3, 1, 1, 4, 1},
+
+ /* MCLK_CDREX L1: 267MHz */
+ {0, 1, 1, 1, 3, 2, 1, 5, 1},
+
+ /* MCLK_CDREX L2: 107MHz */
+ {0, 4, 1, 4, 3, 5, 1, 8, 1},
+};
+
+static unsigned int __maybe_unused clkdiv_cdrex_for400[LV_MIF_END][9] = {
+ /*
+ * Clock divider value for following
+ * { DIVMCLK_DPHY, DIVMCLK_CDREX2, DIVACLK_CDREX, DIVMCLK_CDREX,
+ DIVPCLK_CDREX, DIVC2C, DIVC2C_ACLK, DIVMCLK_EFPHY, DIVACLK_EFCON }
+ */
+
+ /* MCLK_CDREX L0: 400MHz */
+ {1, 1, 1, 0, 5, 1, 1, 4, 1},
+
+ /* MCLK_CDREX L1: 267MHz */
+ {1, 2, 1, 2, 2, 2, 1, 5, 1},
+
+ /* MCLK_CDREX L2: 100MHz */
+ {1, 7, 1, 2, 7, 7, 1, 15, 1},
+};
+
+static unsigned int (*clkdiv_cdrex)[9];
+
+static void exynos5250_set_bus_volt(void)
+{
+ unsigned int i;
+
+ if (soc_is_exynos5250() && samsung_rev() < EXYNOS5250_REV_1_0)
+ asv_group_index = 0;
+ else
+ asv_group_index = exynos_result_of_asv;
+
+ if (asv_group_index == 0xff)
+ asv_group_index = 0;
+
+ printk(KERN_INFO "DVFS : VDD_INT Voltage table set with %d Group\n", asv_group_index);
+ printk(KERN_INFO "DVFS : VDD_INT Voltage of L0 level is %d \n", exynos5_mif_volt[asv_group_index][0]);
+
+ for (i = LV_0; i < LV_MIF_END; i++)
+ exynos5_busfreq_table_mif[i].volt =
+ exynos5_mif_volt[asv_group_index][i];
+
+ for (i = LV_0; i < LV_INT_END; i++)
+ exynos5_busfreq_table_int[i].volt =
+ exynos5_int_volt[asv_group_index][i];
+ return;
+}
+
+static void exynos5250_target_for_mif(struct busfreq_data *data, int div_index)
+{
+ unsigned int tmp;
+
+ /* Change Divider - CDREX */
+ tmp = data->cdrex_divtable[div_index];
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_CDREX);
+
+ if (samsung_rev() < EXYNOS5250_REV_1_0) {
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_CDREX);
+ } while (tmp & 0x11111111);
+ } else {
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_CDREX);
+ } while (tmp & 0x11110011); \
+ }
+
+ if (samsung_rev() < EXYNOS5250_REV_1_0) {
+ tmp = data->cdrex2_divtable[div_index];
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_CDREX2);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_CDREX2);
+ } while (tmp & 0x1);
+ }
+}
+
+static void exynos5250_target_for_int(struct busfreq_data *data, int div_index)
+{
+ unsigned int tmp;
+ unsigned int tmp2;
+
+ /* Change Divider - TOP */
+ tmp = __raw_readl(EXYNOS5_CLKDIV_TOP0);
+
+ tmp &= ~(EXYNOS5_CLKDIV_TOP0_ACLK266_MASK |
+ EXYNOS5_CLKDIV_TOP0_ACLK200_MASK |
+ EXYNOS5_CLKDIV_TOP0_ACLK66_MASK |
+ EXYNOS5_CLKDIV_TOP0_ACLK333_MASK |
+ EXYNOS5_CLKDIV_TOP0_ACLK166_MASK |
+ EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_MASK);
+
+ tmp |= ((clkdiv_top[div_index][2] << EXYNOS5_CLKDIV_TOP0_ACLK266_SHIFT) |
+ (clkdiv_top[div_index][3] << EXYNOS5_CLKDIV_TOP0_ACLK200_SHIFT) |
+ (clkdiv_top[div_index][5] << EXYNOS5_CLKDIV_TOP0_ACLK66_SHIFT) |
+ (clkdiv_top[div_index][6] << EXYNOS5_CLKDIV_TOP0_ACLK333_SHIFT) |
+ (clkdiv_top[div_index][7] << EXYNOS5_CLKDIV_TOP0_ACLK166_SHIFT) |
+ (clkdiv_top[div_index][8] << EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_SHIFT));
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_TOP0);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_TOP0);
+ } while (tmp & 0x151101);
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_TOP1);
+
+ tmp &= ~(EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_MASK |
+ EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_MASK |
+ EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_MASK |
+ EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_MASK);
+
+ tmp |= ((clkdiv_top[div_index][0] << EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_SHIFT) |
+ (clkdiv_top[div_index][1] << EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_SHIFT) |
+ (clkdiv_top[div_index][4] << EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_SHIFT) |
+ (clkdiv_top[div_index][9] << EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_SHIFT));
+
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_TOP1);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_TOP1);
+ tmp2 = __raw_readl(EXYNOS5_CLKDIV_STAT_TOP0);
+ } while ((tmp & 0x1110000) && (tmp2 & 0x80000));
+
+ /* Change Divider - LEX */
+ tmp = data->lex_divtable[div_index];
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_LEX);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_LEX);
+ } while (tmp & 0x110);
+
+ /* Change Divider - R0X */
+ tmp = __raw_readl(EXYNOS5_CLKDIV_R0X);
+
+ tmp &= ~EXYNOS5_CLKDIV_R0X_PCLK_R0X_MASK;
+
+ tmp |= (clkdiv_r0x[div_index][0] << EXYNOS5_CLKDIV_R0X_PCLK_R0X_SHIFT);
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_R0X);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_R0X);
+ } while (tmp & 0x10);
+
+ /* Change Divider - R1X */
+ tmp = data->r1x_divtable[div_index];
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_R1X);
+
+ do {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_R1X);
+ } while (tmp & 0x10);
+}
+
+static void exynos5250_target(struct busfreq_data *data, enum ppmu_type type,
+ int index)
+{
+ if (type == PPMU_MIF)
+ exynos5250_target_for_mif(data, index);
+ else
+ exynos5250_target_for_int(data, index);
+}
+
+static int exynos5250_get_table_index(unsigned long freq, enum ppmu_type type)
+{
+ int index;
+
+ if (type == PPMU_MIF) {
+ for (index = LV_0; index < LV_MIF_END; index++)
+ if (freq == exynos5_busfreq_table_mif[index].mem_clk)
+ return index;
+ } else {
+ for (index = LV_0; index < LV_INT_END; index++)
+ if (freq == exynos5_busfreq_table_int[index].mem_clk)
+ return index;
+ }
+ return -EINVAL;
+}
+
+static void exynos5250_suspend(void)
+{
+ /* Nothing to do */
+}
+
+static void exynos5250_resume(void)
+{
+ __raw_writel(drex2_pause_ctrl, EXYNOS5_DREX2_PAUSE);
+}
+
+static void exynos5250_monitor(struct busfreq_data *data,
+ struct opp **mif_opp, struct opp **int_opp)
+{
+ int i;
+ unsigned int cpu_load_average = 0;
+ unsigned int ddr_c_load_average = 0;
+ unsigned int ddr_l_load_average = 0;
+ unsigned int ddr_r1_load_average = 0;
+ unsigned int right0_load_average = 0;
+ unsigned int ddr_load_average;
+ unsigned long cpufreq = 0;
+ unsigned long freq_int_right0 = 0;
+ unsigned long lockfreq[PPMU_TYPE_END];
+ unsigned long freq[PPMU_TYPE_END];
+ unsigned long cpu_load;
+ unsigned long ddr_load=0;
+ unsigned long ddr_load_int=0;
+ unsigned long ddr_c_load;
+ unsigned long ddr_r1_load;
+ unsigned long ddr_l_load;
+ unsigned long right0_load;
+ struct opp *opp[PPMU_TYPE_END];
+ unsigned long newfreq[PPMU_TYPE_END];
+
+ ppmu_update(data->dev[PPMU_MIF], 3);
+
+ /* Convert from base xxx to base maxfreq */
+ cpu_load = div64_u64(ppmu_load[PPMU_CPU] * data->curr_freq[PPMU_MIF], data->max_freq[PPMU_MIF]);
+ ddr_c_load = div64_u64(ppmu_load[PPMU_DDR_C] * data->curr_freq[PPMU_MIF], data->max_freq[PPMU_MIF]);
+ ddr_r1_load = div64_u64(ppmu_load[PPMU_DDR_R1] * data->curr_freq[PPMU_MIF], data->max_freq[PPMU_MIF]);
+ ddr_l_load = div64_u64(ppmu_load[PPMU_DDR_L] * data->curr_freq[PPMU_MIF], data->max_freq[PPMU_MIF]);
+ right0_load = div64_u64(ppmu_load[PPMU_RIGHT0_BUS] * data->curr_freq[PPMU_INT], data->max_freq[PPMU_INT]);
+
+ data->load_history[PPMU_CPU][data->index] = cpu_load;
+ data->load_history[PPMU_DDR_C][data->index] = ddr_c_load;
+ data->load_history[PPMU_DDR_R1][data->index] = ddr_r1_load;
+ data->load_history[PPMU_DDR_L][data->index] = ddr_l_load;
+ data->load_history[PPMU_RIGHT0_BUS][data->index++] = right0_load;
+
+ if (data->index >= LOAD_HISTORY_SIZE)
+ data->index = 0;
+
+ for (i = 0; i < LOAD_HISTORY_SIZE; i++) {
+ cpu_load_average += data->load_history[PPMU_CPU][i];
+ ddr_c_load_average += data->load_history[PPMU_DDR_C][i];
+ ddr_r1_load_average += data->load_history[PPMU_DDR_R1][i];
+ ddr_l_load_average += data->load_history[PPMU_DDR_L][i];
+ right0_load_average += data->load_history[PPMU_RIGHT0_BUS][i];
+ }
+
+ /* Calculate average Load */
+ cpu_load_average /= LOAD_HISTORY_SIZE;
+ ddr_c_load_average /= LOAD_HISTORY_SIZE;
+ ddr_r1_load_average /= LOAD_HISTORY_SIZE;
+ ddr_l_load_average /= LOAD_HISTORY_SIZE;
+ right0_load_average /= LOAD_HISTORY_SIZE;
+
+ if (ddr_c_load >= ddr_l_load) {
+ ddr_load = ddr_c_load;
+ ddr_load_average = ddr_c_load_average;
+ } else {
+ ddr_load = ddr_l_load;
+ ddr_load_average = ddr_l_load_average;
+ }
+
+ ddr_load_int = ddr_load;
+
+ //Calculate MIF/INT frequency level
+ if (ddr_r1_load >= MIF_R1_THRESHOLD) {
+ freq[PPMU_MIF] = data->max_freq[PPMU_MIF];
+ if (right0_load >= INT_RIGHT0_THRESHOLD) {
+ freq[PPMU_INT] = data->max_freq[PPMU_INT];
+ goto go_max;
+ } else {
+ freq_int_right0 = div64_u64(data->max_freq[PPMU_INT] * right0_load, INT_RIGHT0_THRESHOLD);
+ }
+ } else {
+ // Caculate next MIF frequency
+ if (ddr_load >= MIF_MAX_THRESHOLD) {
+ freq[PPMU_MIF] = data->max_freq[PPMU_MIF];
+ } else if ( ddr_load < IDLE_THRESHOLD) {
+ if (ddr_load_average < IDLE_THRESHOLD)
+ freq[PPMU_MIF] = step_down(data, PPMU_MIF, 1);
+ else
+ freq[PPMU_MIF] = data->curr_freq[PPMU_MIF];
+ } else {
+ if (ddr_load < ddr_load_average) {
+ ddr_load = ddr_load_average;
+ if (ddr_load >= MIF_MAX_THRESHOLD)
+ ddr_load = MIF_MAX_THRESHOLD;
+ }
+ freq[PPMU_MIF] = div64_u64(data->max_freq[PPMU_MIF] * ddr_load, MIF_MAX_THRESHOLD);
+ }
+
+ freq_int_right0 = div64_u64(data->max_freq[PPMU_INT] * right0_load, INT_RIGHT0_THRESHOLD);
+ }
+
+ // Caculate next INT frequency
+ if (ddr_load_int >= INT_MAX_THRESHOLD) {
+ freq[PPMU_INT] = data->max_freq[PPMU_INT];
+ } else if ( ddr_load_int < IDLE_THRESHOLD) {
+ if (ddr_load_average < IDLE_THRESHOLD)
+ freq[PPMU_INT] = step_down(data, PPMU_INT, 1);
+ else
+ freq[PPMU_INT] = data->curr_freq[PPMU_INT];
+ } else {
+ if (ddr_load_int < ddr_load_average) {
+ ddr_load_int = ddr_load_average;
+ if (ddr_load_int >= INT_MAX_THRESHOLD)
+ ddr_load_int = INT_MAX_THRESHOLD;
+ }
+ freq[PPMU_INT] = div64_u64(data->max_freq[PPMU_INT] * ddr_load_int, INT_MAX_THRESHOLD);
+ }
+
+ freq[PPMU_INT] = max(freq[PPMU_INT], freq_int_right0);
+
+ if (freq[PPMU_INT] == data->max_freq[PPMU_INT])
+ freq[PPMU_MIF] = data->max_freq[PPMU_MIF];
+
+go_max:
+#ifdef BUSFREQ_PROFILE_DEBUG
+ printk(KERN_DEBUG "cpu[%ld] l[%ld] c[%ld] r1[%ld] rt[%ld] m_load[%ld] i_load[%ld]\n",
+ cpu_load, ddr_l_load, ddr_c_load, ddr_r1_load, right0_load, ddr_load, ddr_load_int);
+#endif
+ lockfreq[PPMU_MIF] = (dev_max_freq(data->dev[PPMU_MIF])/1000)*1000;
+ lockfreq[PPMU_INT] = (dev_max_freq(data->dev[PPMU_MIF])%1000)*1000;
+#ifdef BUSFREQ_PROFILE_DEBUG
+ printk(KERN_DEBUG "i_cf[%ld] m_cf[%ld] i_nf[%ld] m_nf[%ld] lock_Mfreq[%ld] lock_Ifreq[%ld]\n",
+ data->curr_freq[PPMU_INT],data->curr_freq[PPMU_MIF],freq[PPMU_INT], freq[PPMU_MIF], lockfreq[PPMU_MIF], lockfreq[PPMU_INT]);
+#endif
+ newfreq[PPMU_MIF] = max(lockfreq[PPMU_MIF], freq[PPMU_MIF]);
+ newfreq[PPMU_INT] = max(lockfreq[PPMU_INT], freq[PPMU_INT]);
+ opp[PPMU_MIF] = opp_find_freq_ceil(data->dev[PPMU_MIF], &newfreq[PPMU_MIF]);
+ opp[PPMU_INT] = opp_find_freq_ceil(data->dev[PPMU_INT], &newfreq[PPMU_INT]);
+
+ *mif_opp = opp[PPMU_MIF];
+ *int_opp = opp[PPMU_INT];
+}
+
+static void busfreq_early_suspend(struct early_suspend *h)
+{
+ unsigned long freq;
+ struct busfreq_data *data = container_of(h, struct busfreq_data,
+ busfreq_early_suspend_handler);
+ freq = data->min_freq[PPMU_MIF] + data->min_freq[PPMU_INT] / 1000;
+ //dev_lock(data->dev[PPMU_MIF], data->dev[PPMU_MIF], freq);
+ dev_unlock(data->dev[PPMU_MIF], data->dev[PPMU_MIF]);
+}
+
+static void busfreq_late_resume(struct early_suspend *h)
+{
+ struct busfreq_data *data = container_of(h, struct busfreq_data,
+ busfreq_early_suspend_handler);
+ /* Request min MIF/INT 300MHz */
+ dev_lock(data->dev[PPMU_MIF], data->dev[PPMU_MIF], 300150);
+}
+
+int exynos5250_init(struct device *dev, struct busfreq_data *data)
+{
+ unsigned int i, tmp;
+ unsigned long maxfreq = ULONG_MAX;
+ unsigned long minfreq = 0;
+ unsigned long cdrexfreq;
+ unsigned long lrbusfreq;
+ struct clk *clk;
+ int ret;
+
+ /* Enable pause function for DREX2 DVFS */
+ drex2_pause_ctrl = __raw_readl(EXYNOS5_DREX2_PAUSE);
+ drex2_pause_ctrl |= DMC_PAUSE_ENABLE;
+ __raw_writel(drex2_pause_ctrl, EXYNOS5_DREX2_PAUSE);
+
+ clk = clk_get(NULL, "mclk_cdrex");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Fail to get mclk_cdrex clock");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ cdrexfreq = clk_get_rate(clk) / 1000;
+ clk_put(clk);
+
+ clk = clk_get(NULL, "aclk_266");
+ if (IS_ERR(clk)) {
+ dev_err(dev, "Fail to get aclk_266 clock");
+ ret = PTR_ERR(clk);
+ return ret;
+ }
+ lrbusfreq = clk_get_rate(clk) / 1000;
+ clk_put(clk);
+
+ if (cdrexfreq == 800000) {
+ clkdiv_cdrex = clkdiv_cdrex_for800;
+ exynos5_busfreq_table_mif = exynos5_busfreq_table_for800;
+ exynos5_mif_volt = exynos5_mif_volt_for800;
+ } else if (cdrexfreq == 666857) {
+ clkdiv_cdrex = clkdiv_cdrex_for667;
+ exynos5_busfreq_table_mif = exynos5_busfreq_table_for667;
+ exynos5_mif_volt = exynos5_mif_volt_for667;
+ } else if (cdrexfreq == 533000) {
+ clkdiv_cdrex = clkdiv_cdrex_for533;
+ exynos5_busfreq_table_mif = exynos5_busfreq_table_for533;
+ exynos5_mif_volt = exynos5_mif_volt_for533;
+ } else if (cdrexfreq == 400000) {
+ clkdiv_cdrex = clkdiv_cdrex_for400;
+ exynos5_busfreq_table_mif = exynos5_busfreq_table_for400;
+ exynos5_mif_volt = exynos5_mif_volt_for400;
+ } else {
+ dev_err(dev, "Don't support cdrex table\n");
+ return -EINVAL;
+ }
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_LEX);
+
+ for (i = LV_0; i < LV_INT_END; i++) {
+ tmp &= ~(EXYNOS5_CLKDIV_LEX_ATCLK_LEX_MASK | EXYNOS5_CLKDIV_LEX_PCLK_LEX_MASK);
+
+ tmp |= ((clkdiv_lex[i][0] << EXYNOS5_CLKDIV_LEX_ATCLK_LEX_SHIFT) |
+ (clkdiv_lex[i][1] << EXYNOS5_CLKDIV_LEX_PCLK_LEX_SHIFT));
+
+ data->lex_divtable[i] = tmp;
+ }
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_R0X);
+
+ for (i = LV_0; i < LV_INT_END; i++) {
+
+ tmp &= ~EXYNOS5_CLKDIV_R0X_PCLK_R0X_MASK;
+
+ tmp |= (clkdiv_r0x[i][0] << EXYNOS5_CLKDIV_R0X_PCLK_R0X_SHIFT);
+
+ data->r0x_divtable[i] = tmp;
+ }
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_R1X);
+
+ for (i = LV_0; i < LV_INT_END; i++) {
+ tmp &= ~EXYNOS5_CLKDIV_R1X_PCLK_R1X_MASK;
+
+ tmp |= (clkdiv_r1x[i][0] << EXYNOS5_CLKDIV_R1X_PCLK_R1X_SHIFT);
+
+ data->r1x_divtable[i] = tmp;
+ }
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_CDREX);
+
+ if (samsung_rev() < EXYNOS5250_REV_1_0) {
+ for (i = LV_0; i < LV_MIF_END; i++) {
+ tmp &= ~(EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_MASK |
+ EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_CLK400_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_C2C200_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_MASK);
+
+ tmp |= ((clkdiv_cdrex[i][0] << EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_SHIFT) |
+ (clkdiv_cdrex[i][1] << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_SHIFT) |
+ (clkdiv_cdrex[i][2] << EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][3] << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][4] << EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][5] << EXYNOS5_CLKDIV_CDREX_ACLK_CLK400_SHIFT) |
+ (clkdiv_cdrex[i][6] << EXYNOS5_CLKDIV_CDREX_ACLK_C2C200_SHIFT) |
+ (clkdiv_cdrex[i][8] << EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_SHIFT));
+
+ data->cdrex_divtable[i] = tmp;
+ }
+ } else {
+ for (i = LV_0; i < LV_MIF_END; i++) {
+ tmp &= ~(EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_MASK |
+ EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_MASK |
+ EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_MASK);
+
+ tmp |= ((clkdiv_cdrex[i][0] << EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_SHIFT) |
+ (clkdiv_cdrex[i][1] << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_SHIFT) |
+ (clkdiv_cdrex[i][2] << EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][3] << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][4] << EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_SHIFT) |
+ (clkdiv_cdrex[i][8] << EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_SHIFT));
+
+ data->cdrex_divtable[i] = tmp;
+ }
+ }
+
+ if (samsung_rev() < EXYNOS5250_REV_1_0) {
+ tmp = __raw_readl(EXYNOS5_CLKDIV_CDREX2);
+
+ for (i = LV_0; i < LV_MIF_END; i++) {
+ tmp &= ~EXYNOS5_CLKDIV_CDREX2_MCLK_EFPHY_MASK;
+
+ tmp |= clkdiv_cdrex[i][7] << EXYNOS5_CLKDIV_CDREX2_MCLK_EFPHY_SHIFT;
+
+ data->cdrex2_divtable[i] = tmp;
+
+ }
+ }
+
+ exynos5250_set_bus_volt();
+
+ data->dev[PPMU_MIF] = dev;
+ data->dev[PPMU_INT] = &busfreq_for_int;
+
+ for (i = LV_0; i < LV_MIF_END; i++) {
+ ret = opp_add(data->dev[PPMU_MIF], exynos5_busfreq_table_mif[i].mem_clk,
+ exynos5_busfreq_table_mif[i].volt);
+ if (ret) {
+ dev_err(dev, "Fail to add opp entries.\n");
+ return ret;
+ }
+ }
+
+#if defined(CONFIG_DP_60HZ_P11) || defined(CONFIG_DP_60HZ_P10)
+ if (cdrexfreq == 666857) {
+ opp_disable(data->dev[PPMU_MIF], 334000);
+ opp_disable(data->dev[PPMU_MIF], 110000);
+ } else if (cdrexfreq == 533000) {
+ opp_disable(data->dev[PPMU_MIF], 267000);
+ opp_disable(data->dev[PPMU_MIF], 107000);
+ } else if (cdrexfreq == 400000) {
+ opp_disable(data->dev[PPMU_MIF], 267000);
+ opp_disable(data->dev[PPMU_MIF], 100000);
+ }
+#endif
+
+ for (i = LV_0; i < LV_INT_END; i++) {
+ ret = opp_add(data->dev[PPMU_INT], exynos5_busfreq_table_int[i].mem_clk,
+ exynos5_busfreq_table_int[i].volt);
+ if (ret) {
+ dev_err(dev, "Fail to add opp entries.\n");
+ return ret;
+ }
+ }
+
+ data->target = exynos5250_target;
+ data->get_table_index = exynos5250_get_table_index;
+ data->monitor = exynos5250_monitor;
+ data->busfreq_suspend = exynos5250_suspend;
+ data->busfreq_resume = exynos5250_resume;
+ data->sampling_rate = usecs_to_jiffies(100000);
+
+ data->table[PPMU_MIF] = exynos5_busfreq_table_mif;
+ data->table[PPMU_INT] = exynos5_busfreq_table_int;
+
+ /* Find max frequency for mif */
+ data->max_freq[PPMU_MIF] =
+ opp_get_freq(opp_find_freq_floor(data->dev[PPMU_MIF], &maxfreq));
+ data->min_freq[PPMU_MIF] =
+ opp_get_freq(opp_find_freq_ceil(data->dev[PPMU_MIF], &minfreq));
+ data->curr_freq[PPMU_MIF] =
+ opp_get_freq(opp_find_freq_ceil(data->dev[PPMU_MIF], &cdrexfreq));
+ /* Find max frequency for int */
+ maxfreq = ULONG_MAX;
+ minfreq = 0;
+ data->max_freq[PPMU_INT] =
+ opp_get_freq(opp_find_freq_floor(data->dev[PPMU_INT], &maxfreq));
+ data->min_freq[PPMU_INT] =
+ opp_get_freq(opp_find_freq_ceil(data->dev[PPMU_INT], &minfreq));
+ data->curr_freq[PPMU_INT] =
+ opp_get_freq(opp_find_freq_ceil(data->dev[PPMU_INT], &lrbusfreq));
+
+ data->vdd_reg[PPMU_INT] = regulator_get(NULL, "vdd_int");
+ if (IS_ERR(data->vdd_reg[PPMU_INT])) {
+ pr_err("failed to get resource %s\n", "vdd_int");
+ return -ENODEV;
+ }
+
+ data->vdd_reg[PPMU_MIF] = regulator_get(NULL, "vdd_mif");
+ if (IS_ERR(data->vdd_reg[PPMU_MIF])) {
+ pr_err("failed to get resource %s\n", "vdd_mif");
+ regulator_put(data->vdd_reg[PPMU_INT]);
+ return -ENODEV;
+ }
+
+ data->busfreq_early_suspend_handler.suspend = &busfreq_early_suspend;
+ data->busfreq_early_suspend_handler.resume = &busfreq_late_resume;
+
+ data->busfreq_early_suspend_handler.suspend = &busfreq_early_suspend;
+ data->busfreq_early_suspend_handler.resume = &busfreq_late_resume;
+
+ /* Request min 300MHz for MIF and 150MHz for INT*/
+ dev_lock(dev, dev, 300150);
+
+ register_early_suspend(&data->busfreq_early_suspend_handler);
+
+ tmp = __raw_readl(EXYNOS5_ABBG_INT_CONTROL);
+ tmp &= ~(0x1f | (1 << 31) | (1 << 7));
+ tmp |= ((8 + INT_RBB) | (1 << 31) | (1 << 7));
+ __raw_writel(tmp, EXYNOS5_ABBG_INT_CONTROL);
+
+ return 0;
+}