arm: exynos: Add busfreq support for smdk5250
authorAbhilash Kesavan <a.kesavan@samsung.com>
Sun, 19 Aug 2012 11:39:54 +0000 (17:09 +0530)
committerGerrit <chrome-bot@google.com>
Tue, 28 Aug 2012 04:47:36 +0000 (21:47 -0700)
The busfreq driver controls the MIF and INT frequencies and
voltages.
Note: MIF cannot be varied for DDR3 boards and hence has been
disabled.

BUG=chrome-os-partner:11792
TEST=Measurements for variation in VDD_INT on Daisy show a reduction
of ~70-80mW at home screen.

Change-Id: I02e6cae2c1aec2b88c17b5bcccd3ebfc9d439f98
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Reviewed-on: https://gerrit.chromium.org/gerrit/28943
Reviewed-by: Sam Leffler <sleffler@chromium.org>
Reviewed-by: Bryan Freed <bfreed@chromium.org>
Tested-by: Arjun.K.V <arjun.kv@samsung.com>
13 files changed:
arch/arm/Kconfig
arch/arm/mach-exynos/Kconfig
arch/arm/mach-exynos/Makefile
arch/arm/mach-exynos/busfreq_opp_5250.c [new file with mode: 0644]
arch/arm/mach-exynos/busfreq_opp_exynos5.c [new file with mode: 0644]
arch/arm/mach-exynos/common.c
arch/arm/mach-exynos/dev.c [new file with mode: 0644]
arch/arm/mach-exynos/include/mach/busfreq_exynos5.h [new file with mode: 0644]
arch/arm/mach-exynos/include/mach/dev.h [new file with mode: 0644]
arch/arm/mach-exynos/include/mach/map.h
arch/arm/mach-exynos/include/mach/regs-clock.h
arch/arm/mach-exynos/mach-exynos5-dt.c
arch/arm/plat-samsung/include/plat/map-s5p.h

index cae6662..e5c5339 100644 (file)
@@ -904,6 +904,8 @@ config ARCH_EXYNOS
        select HAVE_S3C2410_WATCHDOG if WATCHDOG
        select NEED_MACH_MEMORY_H
        select USB_ARCH_HAS_XHCI
+       select ARCH_HAS_OPP
+       select PM_OPP if PM
        help
          Support for SAMSUNG's EXYNOS SoCs (EXYNOS4/5)
 
index a6be249..0b3c795 100644 (file)
@@ -446,4 +446,13 @@ endif
 
 endmenu
 
+menu "Busfreq Model"
+        depends on ARM_EXYNOS5250_CPUFREQ
+
+config BUSFREQ_OPP
+        bool "Busfreq with OPP"
+        depends on ARM_EXYNOS5250_CPUFREQ
+
+endmenu
+
 endif
index 5abea47..8a52763 100644 (file)
@@ -23,6 +23,11 @@ obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ)   += asv-5250.o
 
+obj-$(CONFIG_BUSFREQ_OPP)      += dev.o
+ifeq ($(CONFIG_BUSFREQ_OPP),y)
+obj-$(CONFIG_ARCH_EXYNOS5)     += busfreq_opp_exynos5.o busfreq_opp_5250.o
+endif
+
 obj-$(CONFIG_ARCH_EXYNOS)      += pmu.o
 
 obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
diff --git a/arch/arm/mach-exynos/busfreq_opp_5250.c b/arch/arm/mach-exynos/busfreq_opp_5250.c
new file mode 100644 (file)
index 0000000..318b17a
--- /dev/null
@@ -0,0 +1,1000 @@
+/* linux/arch/arm/mach-exynos/busfreq_opp_5250.c
+ *
+ * Copyright (c) 2012 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/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/opp.h>
+#include <linux/clk.h>
+#include <mach/busfreq_exynos5.h>
+
+#include <mach/regs-clock.h>
+#include <mach/dev.h>
+
+#include <plat/clock.h>
+
+#define MIF_MAX_THRESHOLD      13
+#define INT_MAX_THRESHOLD      8
+#define MIF_IDLE_THRESHOLD     3
+#define INT_IDLE_THRESHOLD     2
+
+#ifdef CONFIG_EXYNOS_DP
+#define MIF_LOCK_LCD           300160
+#else
+#define MIF_LOCK_LCD           300133
+#endif
+
+#define INT_RBB                6       /* +300mV */
+#define CDREX_BITMASK  9
+#define ASV_GROUP_10   10
+#define ASV_GROUP_12   12
+#define MIF_ASV_GROUP  3
+
+static struct clk *mout_bpll;
+static struct clk *mout_mpll;
+static struct clk *mout_mclk_cdrex;
+static struct device busfreq_for_int;
+static struct busfreq_table *exynos5_busfreq_table_mif;
+static unsigned int (*exynos5_mif_volt)[LV_MIF_END];
+static unsigned int (*clkdiv_cdrex)[CDREX_BITMASK];
+static unsigned int asv_group_index;
+static unsigned int mif_asv_group_index;
+static unsigned int old_mif_index;
+static bool init_done;
+
+static const unsigned int max_threshold[PPMU_TYPE_END] = {
+       MIF_MAX_THRESHOLD,
+       INT_MAX_THRESHOLD,
+};
+
+static const unsigned int idle_threshold[PPMU_TYPE_END] = {
+       MIF_IDLE_THRESHOLD,
+       INT_IDLE_THRESHOLD,
+};
+
+static struct busfreq_table exynos5_busfreq_table_for800[] = {
+       {LV_0, 800000, 1000000, 0, 0, 0},
+       {LV_1, 667000, 1000000, 0, 0, 0},
+       {LV_2, 400000, 1000000, 0, 0, 0},
+       {LV_3, 267000, 1000000, 0, 0, 0},
+       {LV_4, 160000, 1000000, 0, 0, 0},
+       {LV_5, 100000, 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},
+       {LV_3, 111000, 1000000, 0, 0, 0},
+       {LV_4, 111000, 1000000, 0, 0, 0},
+       {LV_5, 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},
+       {LV_3, 107000, 1000000, 0, 0, 0},
+       {LV_4, 107000, 1000000, 0, 0, 0},
+       {LV_5, 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},
+       {LV_3, 100000, 1000000, 0, 0, 0},
+       {LV_4, 100000, 1000000, 0, 0, 0},
+       {LV_5, 100000, 1000000, 0, 0, 0},
+};
+
+static unsigned int exynos5250_dmc_timing[LV_MIF_END][3] = {
+       /* timingrow, timingdata, timingpower*/
+       {0x34498692, 0x3630560C, 0x50380336},   /*for 800MHz*/
+       {0x2c48754f, 0x3630460A, 0x442F0336},   /*for 667MHz*/
+       {0x1A255309, 0x23203509, 0x281C0223},   /*for 400MHz*/
+       {0x1A255309, 0x23203509, 0x281C0223},   /*for 267MHz*/
+       {0x1A255309, 0x23203509, 0x281C0223},   /*for 160MHz*/
+       {0x1A255309, 0x23203509, 0x281C0223},   /*for 100MHz*/
+};
+
+static unsigned int
+exynos5_mif_volt_for800_orig[ASV_GROUP_10 + 1][LV_MIF_END] = {
+       /* L0        L1       L2       L3      L4      L5 */
+       {      0,       0,       0,       0,       0,      0}, /* ASV0 */
+       {1200000, 1100000, 1000000, 1000000,  950000, 950000}, /* ASV1 */
+       {1200000, 1050000, 1000000,  950000,  950000, 900000}, /* ASV2 */
+       {1200000, 1050000, 1050000, 1000000,  950000, 950000}, /* ASV3 */
+       {1150000, 1050000, 1000000,  950000,  950000, 900000}, /* ASV4 */
+       {1150000, 1050000, 1050000, 1000000, 1000000, 950000}, /* ASV5 */
+       {1150000, 1050000, 1050000,  950000,  950000, 950000}, /* ASV6 */
+       {1100000, 1050000, 1000000,  950000,  900000, 900000}, /* ASV7 */
+       {1100000, 1050000, 1000000,  950000,  900000, 900000}, /* ASV8 */
+       {1100000, 1050000, 1000000,  950000,  900000, 900000}, /* ASV9 */
+       {1100000, 1050000, 1000000,  950000,  900000, 900000}, /* ASV10 */
+};
+
+static unsigned int
+exynos5_mif_volt_for800[MIF_ASV_GROUP + 1][LV_MIF_END] = {
+       /* L0        L1       L2       L3       L4       L5 */
+       {1150000, 1050000, 1050000, 1000000, 1000000, 1000000}, /* ASV0 */
+       {1100000, 1000000,  950000,  950000,  950000,  900000}, /* ASV1 */
+       {1050000,  950000,  950000,  950000,  900000,  900000}, /* ASV2 */
+       {1000000,  900000,  900000,  900000,  900000,  900000}, /* ASV3 */
+};
+
+static unsigned int
+exynos5_mif_volt_for667[ASV_GROUP_10 + 1][LV_MIF_END] = {
+       /* L0        L1       L2       L3       L4       L5 */
+       {      0,       0,       0,       0,       0,       0}, /* ASV0 */
+       {1100000, 1000000,  950000,  950000,  950000,  950000}, /* ASV1 */
+       {1050000, 1000000,  950000,  950000,  950000,  950000}, /* ASV2 */
+       {1050000, 1050000,  950000,  950000,  950000,  950000}, /* ASV3 */
+       {1050000, 1000000,  950000,  950000,  950000,  950000}, /* ASV4 */
+       {1050000, 1050000, 1000000, 1000000, 1000000, 1000000}, /* ASV5 */
+       {1050000, 1050000,  950000,  950000,  950000,  950000}, /* ASV6 */
+       {1050000, 1000000,  900000,  900000,  900000,  900000}, /* ASV7 */
+       {1050000, 1000000,  900000,  900000,  900000,  900000}, /* ASV8 */
+       {1050000, 1000000,  900000,  900000,  900000,  900000}, /* ASV9 */
+       {1050000, 1000000,  900000,  900000,  900000,  900000}, /* ASV10 */
+};
+
+static unsigned int
+exynos5_mif_volt_for533[ASV_GROUP_10 + 1][LV_MIF_END] = {
+       /* L0        L1       L2       L3        L4      L5 */
+       {      0,       0,       0,       0,       0,       0}, /* ASV0 */
+       {1050000, 1000000,  950000,  950000,  950000,  950000}, /* ASV1 */
+       {1000000,  950000,  950000,  950000,  950000,  950000}, /* ASV2 */
+       {1050000, 1000000,  950000,  950000,  950000,  950000}, /* ASV3 */
+       {1000000,  950000,  950000,  950000,  950000,  950000}, /* ASV4 */
+       {1050000, 1000000, 1000000, 1000000, 1000000, 1000000}, /* ASV5 */
+       {1050000,  950000,  950000,  950000,  950000,  950000}, /* ASV6 */
+       {1000000,  950000,  900000,  900000,  900000,  900000}, /* ASV7 */
+       {1000000,  950000,  900000,  900000,  900000,  900000}, /* ASV8 */
+       {1000000,  950000,  900000,  900000,  900000,  900000}, /* ASV9 */
+       {1000000,  950000,  900000,  900000,  900000,  900000}, /* ASV10 */
+};
+
+static unsigned int
+exynos5_mif_volt_for400[ASV_GROUP_10 + 1][LV_MIF_END] = {
+       /* L0        L1      L2      L3      L4      L5 */
+       {      0,       0,      0,      0,      0,      0}, /* ASV0 */
+       {1000000, 1000000, 950000, 950000, 950000, 950000}, /* ASV1 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV2 */
+       {1050000, 1000000, 950000, 950000, 950000, 950000}, /* ASV3 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV4 */
+       {1050000, 1000000, 950000, 950000, 950000, 950000}, /* ASV5 */
+       {1050000,  950000, 950000, 950000, 950000, 950000}, /* ASV6 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV7 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV8 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV9 */
+       {1000000,  950000, 900000, 900000, 900000, 900000}, /* ASV10 */
+};
+
+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_12][LV_INT_END] = {
+       /* L0        L1       L2       L3 */
+       {1037500, 1000000, 975000, 950000}, /* ASV0 */
+       {1025000,  975000, 962500, 937500}, /* ASV1 */
+       {1025000,  987500, 975000, 950000}, /* ASV2 */
+       {1012500,  975000, 962500, 937500}, /* ASV3 */
+       {1000000,  962500, 950000, 925000}, /* ASV4 */
+       { 987500,  950000, 937500, 912500}, /* ASV5 */
+       { 975000,  937500, 925000, 900000}, /* ASV6 */
+       { 962500,  925000, 912500, 900000}, /* ASV7 */
+       { 962500,  925000, 912500, 900000}, /* ASV8 */
+       { 950000,  912500, 900000, 900000}, /* ASV9 */
+       { 950000,  912500, 900000, 900000}, /* ASV10 */
+       { 937500,  900000, 900000, 887500}, /* ASV11 */
+};
+
+static unsigned int
+exynos5_int_volt_orig[ASV_GROUP_10+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, 5, 1, 5, 2, 3, 2, 2},
+
+       /* ACLK_400_ISP L3 : 160MHz */
+       {4, 4, 5, 6, 1, 5, 3, 4, 5, 5},
+};
+
+/* For CMU_ACP */
+static unsigned int clkdiv_acp[LV_MIF_END][2] = {
+       /*
+        * Clock divider value for following
+        * { DIVACLK_SYSLFT, DIVPCLK_SYSLFT }
+        */
+
+       /* ACLK_SYSLFT L0 : 400MHz */
+       {1, 1},
+
+       /* ACLK_SYSLFT L1 : 400MHz */
+       {1, 1},
+
+       /* ACLK_SYSLFT L2 : 200MHz */
+       {3, 1},
+
+       /* ACLK_SYSLFT L3 : 133MHz */
+       {5, 1},
+
+       /* ACLK_SYSLFT L4 : 100MHz */
+       {7, 1},
+
+       /* ACLK_SYSLFT L5 : 100MHz */
+       {7, 1},
+};
+
+/* For CMU_CORE */
+static unsigned int clkdiv_core[LV_MIF_END][1] = {
+       /*
+        * Clock divider value for following
+        * { DIVACLK_R1BX }
+        */
+
+       /* ACLK_SYSLFT L0 : 400MHz */
+       {1},
+
+       /* ACLK_SYSLFT L1 : 400MHz */
+       {1},
+
+       /* ACLK_SYSLFT L2 : 200MHz */
+       {3},
+
+       /* ACLK_SYSLFT L3 : 133MHz */
+       {5},
+
+       /* ACLK_SYSLFT L4 : 100MHz */
+       {7},
+
+       /* ACLK_SYSLFT L5 : 100MHz */
+       {7},
+};
+
+/* For CMU_CDREX */
+static unsigned int __maybe_unused 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, 2, 1, 1, 1, 4, 1},
+
+       /* MCLK_CDREX L1: 667MHz */
+       {0, 0, 1, 2, 1, 1, 1, 4, 1},
+
+       /* MCLK_CDREX L2: 400MHz */
+       {0, 1, 1, 2, 3, 2, 1, 5, 1},
+
+       /* MCLK_CDREX L3: 267MHz */
+       {0, 2, 1, 2, 4, 2, 1, 5, 1},
+
+       /* MCLK_CDREX L4: 160MHz */
+       {0, 4, 1, 2, 5, 2, 1, 5, 1},
+
+       /* MCLK_CDREX L5: 100MHz */
+       {0, 7, 1, 2, 6, 7, 1, 15, 1},
+};
+
+static unsigned int __maybe_unused 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},
+
+       /* MCLK_CDREX L3: 111MHz */
+       {0, 5, 1, 4, 4, 5, 1, 8, 1},
+
+       /* MCLK_CDREX L4: 111MHz */
+       {0, 5, 1, 4, 4, 5, 1, 8, 1},
+
+       /* MCLK_CDREX L5: 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},
+
+       /* MCLK_CDREX L3: 107MHz */
+       {0, 4, 1, 4, 3, 5, 1, 8, 1},
+
+       /* MCLK_CDREX L4: 107MHz */
+       {0, 4, 1, 4, 3, 5, 1, 8, 1},
+
+       /* MCLK_CDREX L5: 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},
+
+       /* MCLK_CDREX L3: 100MHz */
+       {1, 7, 1, 2, 7, 7, 1, 15, 1},
+
+       /* MCLK_CDREX L4: 100MHz */
+       {1, 7, 1, 2, 7, 7, 1, 15, 1},
+
+       /* MCLK_CDREX L5: 100MHz */
+       {1, 7, 1, 2, 7, 7, 1, 15, 1},
+};
+
+static void exynos5250_set_bus_volt(void)
+{
+       unsigned int i;
+
+       asv_group_index = exynos_result_of_asv;
+       mif_asv_group_index = exynos_result_mif_asv;
+
+       if (asv_group_index == 0xff) {
+               asv_group_index = 0;
+               mif_asv_group_index = 0;
+       }
+
+       for (i = LV_0; i < LV_MIF_END; i++)
+               exynos5_busfreq_table_mif[i].volt =
+                       exynos5_mif_volt[mif_asv_group_index][i];
+
+       for (i = LV_0; i < LV_INT_END; i++) {
+               if (exynos_lot_is_nzvpu)
+                       exynos5_busfreq_table_int[i].volt = 1025000;
+               else if (exynos_lot_id)
+                       exynos5_busfreq_table_int[i].volt =
+                               exynos5_int_volt_orig[asv_group_index][i];
+               else
+                       exynos5_busfreq_table_int[i].volt =
+                               exynos5_int_volt[asv_group_index][i];
+       }
+
+       printk(KERN_INFO "VDD_INT Voltage table set with %d Group\n",
+                               asv_group_index);
+       printk(KERN_INFO "VDD_MIF Voltage table set with %d Group\n",
+                               mif_asv_group_index);
+
+       return;
+}
+
+static void exynos5250_mif_div_change(struct busfreq_data *data, int div_index)
+{
+       unsigned int tmp;
+
+       /* Change Divider - CORE */
+       tmp = __raw_readl(EXYNOS5_CLKDIV_SYSRGT);
+       tmp &= ~EXYNOS5_CLKDIV_SYSRGT_ACLK_R1BX_MASK;
+
+       tmp |= clkdiv_core[div_index][0];
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_SYSLFT);
+
+       /* Change Divider - CDREX */
+       tmp = data->cdrex_divtable[div_index];
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_CDREX);
+
+       /* Change Divider - ACP */
+       tmp = __raw_readl(EXYNOS5_CLKDIV_SYSLFT);
+
+       tmp &= ~(EXYNOS5_CLKDIV_SYSLFT_PCLK_SYSLFT_MASK |
+                EXYNOS5_CLKDIV_SYSLFT_ACLK_SYSLFT_MASK);
+
+       tmp |=
+       ((clkdiv_acp[div_index][0] << EXYNOS5_CLKDIV_SYSLFT_ACLK_SYSLFT_SHIFT) |
+       (clkdiv_acp[div_index][1] << EXYNOS5_CLKDIV_SYSLFT_PCLK_SYSLFT_SHIFT));
+
+       __raw_writel(tmp, EXYNOS5_CLKDIV_SYSLFT);
+}
+
+static void exynos5250_target_for_mif(struct busfreq_data *data, int div_index)
+{
+       /* Mux change BPLL to MPLL */
+       if (old_mif_index == LV_1) {
+               /* Change divider */
+               exynos5250_mif_div_change(data, div_index);
+               /* Change Mux BPLL to MPLL */
+               if (clk_set_parent(mout_mclk_cdrex, mout_mpll))
+                       printk(KERN_ERR "Unable to set parent %s of clock %s\n",
+                                       mout_mpll->name, mout_mclk_cdrex->name);
+       /* Mux change MPLL to BPLL */
+       } else if (div_index == LV_1) {
+               /* Change Mux MPLL to BPLL */
+               if (clk_set_parent(mout_mclk_cdrex, mout_bpll))
+                       printk(KERN_ERR "Unable to set parent %s of clock %s\n",
+                                       mout_bpll->name, mout_mclk_cdrex->name);
+               /* Change divider */
+               exynos5250_mif_div_change(data, div_index);
+       /* It is not need to change mux */
+       } else
+               /* Change divider */
+               exynos5250_mif_div_change(data, div_index);
+
+       old_mif_index = div_index;
+}
+
+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);
+       printk(KERN_INFO "INT changed to %dKHz\n",
+                       exynos5_busfreq_table_int[div_index].mem_clk);
+}
+
+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;
+}
+
+void exynos5250_prepare(int index)
+{
+       unsigned int timing0 = 0;
+       unsigned int timing1 = 0;
+       int i;
+
+       for (i = 0; i < 1; i++) {
+               timing0 = __raw_readl(S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+               if (i != 1) {
+                       timing0 |= exynos5250_dmc_timing[index][i];
+                       timing1 = exynos5250_dmc_timing[index][i];
+               } else {
+                       timing0 |=
+                       (exynos5250_dmc_timing[index][i] & 0xFFFFF000);
+                       timing1 =
+                       (exynos5250_dmc_timing[index][i] & 0xFFFFF000);
+               }
+               __raw_writel(timing0, S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+               __raw_writel(timing1, S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+       }
+}
+
+void exynos5250_post(int index)
+{
+       unsigned int timing0 = 0;
+       unsigned int timing1 = 0;
+       int i;
+
+       for (i = 0; i < 1; i++) {
+               timing0 = __raw_readl(S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+               if (i != 1) {
+                       timing0 |= exynos5250_dmc_timing[index][i];
+                       timing1 = exynos5250_dmc_timing[index][i];
+               } else {
+                       timing0 |=
+                       (exynos5250_dmc_timing[index][i] & 0xFFFFF000);
+                       timing1 =
+                       (exynos5250_dmc_timing[index][i] & 0xFFFFF000);
+               }
+               __raw_writel(timing0, S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+               __raw_writel(timing1, S5P_VA_DREXII + TIMINGROW_OFFSET + i*4);
+       }
+
+}
+
+static void exynos5250_busfreq_suspend(void)
+{
+       /* Nothing here yet */
+}
+
+static void exynos5250_busfreq_resume(void)
+{
+       /* Nothing here yet */
+}
+
+static void exynos5250_busfreq_monitor(struct busfreq_data *data,
+                       struct opp **mif_opp, struct opp **int_opp)
+{
+       int i;
+       unsigned long tmpfreq;
+       unsigned long cpufreq = 0;
+       unsigned long freq[PPMU_TYPE_END];
+       unsigned long cpu_load;
+       unsigned long ddr_c_load;
+       unsigned long right0_load;
+       unsigned long ddr_r1_load;
+       unsigned long ddr_l_load;
+       unsigned long load[PPMU_TYPE_END];
+       unsigned int cpu_load_average = 0;
+       unsigned int ddr_c_load_average = 0;
+       unsigned int ddr_l_load_average = 0;
+       unsigned int right0_load_average = 0;
+       unsigned int ddr_r1_load_average = 0;
+       unsigned int load_average[PPMU_TYPE_END];
+       struct opp *opp[PPMU_TYPE_END];
+       unsigned long lockfreq[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]);
+       right0_load =
+       div64_u64(ppmu_load[PPMU_RIGHT0_BUS] * data->curr_freq[PPMU_INT],
+                               data->max_freq[PPMU_INT]);
+       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]);
+
+       data->load_history[PPMU_CPU][data->index] = cpu_load;
+       data->load_history[PPMU_DDR_C][data->index] = ddr_c_load;
+       data->load_history[PPMU_RIGHT0_BUS][data->index] = right0_load;
+       data->load_history[PPMU_DDR_R1][data->index] = ddr_r1_load;
+       data->load_history[PPMU_DDR_L][data->index++] = ddr_l_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];
+               right0_load_average += data->load_history[PPMU_RIGHT0_BUS][i];
+               ddr_r1_load_average += data->load_history[PPMU_DDR_R1][i];
+               ddr_l_load_average += data->load_history[PPMU_DDR_L][i];
+       }
+
+       /* Calculate average Load */
+       cpu_load_average /= LOAD_HISTORY_SIZE;
+       ddr_c_load_average /= LOAD_HISTORY_SIZE;
+       right0_load_average /= LOAD_HISTORY_SIZE;
+       ddr_r1_load_average /= LOAD_HISTORY_SIZE;
+       ddr_l_load_average /= LOAD_HISTORY_SIZE;
+
+       if (ddr_c_load >= ddr_r1_load) {
+               load[PPMU_MIF] = ddr_c_load;
+               load_average[PPMU_MIF] = ddr_c_load_average;
+       } else {
+               load[PPMU_MIF] = ddr_r1_load;
+               load_average[PPMU_MIF] = ddr_r1_load_average;
+       }
+
+       if (ddr_l_load >= load[PPMU_MIF]) {
+               load[PPMU_MIF] = ddr_l_load;
+               load_average[PPMU_MIF] = ddr_l_load_average;
+       }
+
+       load[PPMU_INT] = right0_load;
+       load_average[PPMU_INT] = right0_load_average;
+
+       for (i = PPMU_MIF; i < PPMU_TYPE_END; i++) {
+               if (load[i] >= max_threshold[i]) {
+                       freq[i] = data->max_freq[i];
+               } else if (load[i] < idle_threshold[i]) {
+                       if (load_average[i] < idle_threshold[i])
+                               freq[i] = step_down(data, i, 1);
+                       else
+                               freq[i] = data->curr_freq[i];
+               } else {
+                       if (load[i] < load_average[i]) {
+                               load[i] = load_average[i];
+                               if (load[i] >= max_threshold[i])
+                                       load[i] = max_threshold[i];
+                       }
+                       freq[i] = div64_u64(data->max_freq[i] * load[i],
+                                       max_threshold[i]);
+               }
+       }
+
+       tmpfreq = dev_max_freq(data->dev[PPMU_MIF]);
+       lockfreq[PPMU_MIF] = (tmpfreq / 1000) * 1000;
+       lockfreq[PPMU_INT] = (tmpfreq % 1000) * 1000;
+
+       newfreq[PPMU_MIF] = max3(lockfreq[PPMU_MIF], freq[PPMU_MIF], cpufreq);
+       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];
+}
+
+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;
+
+       old_mif_index = 0; /* Used for MUX change checkup*/
+
+       mout_mpll = clk_get(NULL, "mout_mpll");
+       if (IS_ERR(mout_mpll)) {
+               dev_err(dev, "Failed to get mout_mpll clock");
+               ret = PTR_ERR(mout_mpll);
+               return ret;
+       }
+
+       mout_bpll = clk_get(NULL, "mout_bpll");
+       if (IS_ERR(mout_bpll)) {
+               dev_err(dev, "Failed to get mout_bpll clock");
+               ret = PTR_ERR(mout_bpll);
+               return ret;
+       }
+
+       mout_mclk_cdrex = clk_get(NULL, "clk_cdrex");
+       if (IS_ERR(mout_mclk_cdrex)) {
+               dev_err(dev, "Failed to get mout_mclk_cdrex clock");
+               ret = PTR_ERR(mout_mclk_cdrex);
+               return ret;
+       }
+       cdrexfreq = clk_get_rate(mout_mclk_cdrex) / 1000;
+       clk_put(mout_mclk_cdrex);
+
+       clk = clk_get(NULL, "aclk_266");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "Failed 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;
+               if (!exynos_lot_id)
+                       exynos5_mif_volt = exynos5_mif_volt_for800;
+               else
+                       exynos5_mif_volt = exynos5_mif_volt_for800_orig;
+       } 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, "No support for cdrexfrq %ld\n", cdrexfreq);
+               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);
+       for (i = LV_0; i < LV_MIF_END; i++) {
+               tmp &= ~(0xFFFFFFFF);
+               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;
+       }
+
+       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, "Failed to add opp entries for MIF.\n");
+                       return ret;
+               }
+       }
+
+       opp_disable(data->dev[PPMU_MIF], 107000);
+
+       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, "Failed to add opp entries for INT.\n");
+                       return ret;
+               }
+       }
+
+       data->target = exynos5250_target;
+       data->get_table_index = exynos5250_get_table_index;
+       data->monitor = exynos5250_busfreq_monitor;
+       data->busfreq_suspend = exynos5250_busfreq_suspend;
+       data->busfreq_resume = exynos5250_busfreq_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 regulator resource: 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 regulator resource: vdd_mif\n");
+               regulator_put(data->vdd_reg[PPMU_INT]);
+               return -ENODEV;
+       }
+
+       /* Request min 300MHz */
+       dev_lock(dev, dev, MIF_LOCK_LCD);
+       init_done = true;
+
+       return 0;
+}
diff --git a/arch/arm/mach-exynos/busfreq_opp_exynos5.c b/arch/arm/mach-exynos/busfreq_opp_exynos5.c
new file mode 100644 (file)
index 0000000..55a22ee
--- /dev/null
@@ -0,0 +1,493 @@
+/* linux/arch/arm/mach-exynos/busfreq_opp_exynos5.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS - BUS clock frequency scaling support with OPP
+ * Based on data from the PPMU counters monitoring the various SoC blocks
+ * such as MFC, ISP etc. the bus utiliazation is calculated and target opp
+ * is decided.
+ *
+ * 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/io.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/suspend.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/opp.h>
+
+#include <mach/dev.h>
+#include <mach/busfreq_exynos5.h>
+
+/* No support for MIF control via busfreq on DDR3 boards */
+#define SUPPORT_MIF    0
+
+#if SUPPORT_MIF
+#define TABLE_START    PPMU_MIF
+#else
+#define TABLE_START    PPMU_INT
+#endif
+
+static DEFINE_MUTEX(busfreq_lock);
+BLOCKING_NOTIFIER_HEAD(exynos_busfreq_notifier_list);
+
+static struct busfreq_control {
+       struct opp *lock[PPMU_TYPE_END];
+       struct device *dev[PPMU_TYPE_END];
+} bus_ctrl;
+
+static void update_busfreq_stat(struct busfreq_data *data,
+               enum ppmu_type type, unsigned int index)
+{
+       unsigned long long cur_time = get_jiffies_64();
+       data->time_in_state[type][index] += cur_time - data->last_time[type];
+       data->last_time[type] = cur_time;
+}
+
+static unsigned long __maybe_unused step_up(struct busfreq_data *data,
+                               enum ppmu_type type, int step)
+{
+       int i;
+       struct opp *opp;
+       unsigned long newfreq = data->curr_freq[type];
+
+       if (data->max_freq[type] == data->curr_freq[type])
+               return newfreq;
+
+       for (i = 0; i < step; i++) {
+               newfreq += 1;
+               opp = opp_find_freq_ceil(data->dev[type], &newfreq);
+
+               if (opp_get_freq(opp) == data->max_freq[type])
+                       break;
+       }
+
+       return newfreq;
+}
+
+unsigned long step_down(struct busfreq_data *data,
+                       enum ppmu_type type, int step)
+{
+       int i;
+       struct opp *opp;
+       unsigned long newfreq = data->curr_freq[type];
+
+       if (data->min_freq[type] == data->curr_freq[type])
+               return newfreq;
+
+       for (i = 0; i < step; i++) {
+               newfreq -= 1;
+               opp = opp_find_freq_floor(data->dev[type], &newfreq);
+
+               if (opp_get_freq(opp) == data->min_freq[type])
+                       break;
+       }
+
+       return newfreq;
+}
+
+static void _target(struct busfreq_data *data,
+               enum ppmu_type type, unsigned long newfreq)
+{
+       struct opp *opp;
+       unsigned int voltage;
+       int index;
+
+       opp = opp_find_freq_exact(data->dev[type], newfreq, true);
+
+       if (bus_ctrl.lock[type]) {
+               opp = bus_ctrl.lock[type];
+               newfreq = opp_get_freq(opp);
+       }
+
+       index = data->get_table_index(newfreq, type);
+
+       if (newfreq == 0 || newfreq == data->curr_freq[type] ||
+                       data->use == false) {
+               update_busfreq_stat(data, type, index);
+               return;
+       }
+
+       voltage = opp_get_voltage(opp);
+
+       /* The MIF frequency varies from 0.95 - 1.2 V */
+       if (newfreq > data->curr_freq[type]) {
+               regulator_set_voltage(data->vdd_reg[type], voltage,
+                               voltage + 25000);
+               if (type == PPMU_MIF && data->busfreq_prepare)
+                       data->busfreq_prepare(index);
+       }
+
+       data->target(data, type, index);
+
+       if (newfreq < data->curr_freq[type]) {
+               if (type == PPMU_MIF && data->busfreq_post)
+                       data->busfreq_post(index);
+               regulator_set_voltage(data->vdd_reg[type], voltage,
+                               voltage + 25000);
+       }
+       data->curr_freq[type] = newfreq;
+
+       update_busfreq_stat(data, type, index);
+}
+
+static void exynos_busfreq_timer(struct work_struct *work)
+{
+       struct delayed_work *delayed_work = to_delayed_work(work);
+       struct busfreq_data *data = container_of(delayed_work,
+                       struct busfreq_data, worker);
+       int i;
+       struct opp *opp[PPMU_TYPE_END];
+       unsigned long newfreq;
+
+       data->monitor(data, &opp[PPMU_MIF], &opp[PPMU_INT]);
+
+       ppmu_start(data->dev[PPMU_MIF]);
+
+       mutex_lock(&busfreq_lock);
+
+       for (i = TABLE_START; i < PPMU_TYPE_END; i++) {
+               newfreq = opp_get_freq(opp[i]);
+               _target(data, i, newfreq);
+       }
+
+       mutex_unlock(&busfreq_lock);
+       queue_delayed_work(system_freezable_wq, &data->worker,
+                               data->sampling_rate);
+}
+
+static int exynos_buspm_notifier_event(struct notifier_block *this,
+               unsigned long event, void *ptr)
+{
+       struct busfreq_data *data = container_of(this, struct busfreq_data,
+                       exynos_buspm_notifier);
+       int i;
+
+       switch (event) {
+       case PM_SUSPEND_PREPARE:
+               mutex_lock(&busfreq_lock);
+               for (i = TABLE_START; i < PPMU_TYPE_END; i++)
+                       _target(data, i, data->max_freq[i]);
+               mutex_unlock(&busfreq_lock);
+               data->use = false;
+               return NOTIFY_OK;
+       case PM_POST_RESTORE:
+       case PM_POST_SUSPEND:
+               data->use = true;
+               return NOTIFY_OK;
+       }
+       return NOTIFY_DONE;
+}
+
+static int exynos_busfreq_reboot_event(struct notifier_block *this,
+               unsigned long code, void *unused)
+{
+       struct busfreq_data *data = container_of(this, struct busfreq_data,
+                       exynos_reboot_notifier);
+       int i;
+       struct opp *opp;
+       unsigned int voltage[PPMU_TYPE_END];
+       for (i = TABLE_START; i < PPMU_TYPE_END; i++) {
+               opp = opp_find_freq_exact(data->dev[i],
+                               data->max_freq[i], true);
+               voltage[i] = opp_get_voltage(opp);
+
+               regulator_set_voltage(data->vdd_reg[i],
+                               voltage[i], voltage[i] + 25000);
+       }
+       data->use = false;
+
+       printk(KERN_INFO "Reboot Notifier for busfreq\n");
+       return NOTIFY_DONE;
+}
+
+/*
+ * In certain cases we want to lock the frequency of INT/MIF to a specific
+ * value; for example DP requires a minimum of 160MHz for INT. Register
+ * and use notifiers which does this via the "dev" interface provided.
+ */
+static int exynos_busfreq_request_event(struct notifier_block *this,
+               unsigned long req_newfreq, void *device)
+{
+       struct busfreq_data *data = container_of(this, struct busfreq_data,
+                       exynos_request_notifier);
+       int i;
+       struct opp *opp[PPMU_TYPE_END];
+       unsigned long newfreq[PPMU_TYPE_END];
+       unsigned long freq;
+
+       if (req_newfreq == 0 || data->use == false)
+               return -EINVAL;
+
+       mutex_lock(&busfreq_lock);
+
+       newfreq[PPMU_MIF] = (req_newfreq / 1000) * 1000;
+       newfreq[PPMU_INT] = (req_newfreq % 1000) * 1000;
+
+       for (i = TABLE_START; i < PPMU_TYPE_END; i++) {
+               opp[i] = opp_find_freq_ceil(data->dev[i], &newfreq[i]);
+               freq = opp_get_freq(opp[i]);
+               if (freq > data->curr_freq[i])
+                       _target(data, i, freq);
+       }
+
+       mutex_unlock(&busfreq_lock);
+       printk(KERN_INFO "Request Notifier for busfreq\n");
+       return NOTIFY_DONE;
+}
+
+static ssize_t show_level_lock(struct device *device,
+               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev =
+                       to_platform_device(bus_ctrl.dev[PPMU_MIF]);
+       struct busfreq_data *data =
+                       (struct busfreq_data *)platform_get_drvdata(pdev);
+       int len = 0;
+       unsigned long mif_freq, int_freq;
+       unsigned long get_mif_freq = opp_get_freq(bus_ctrl.lock[PPMU_MIF]);
+       unsigned long get_int_freq = opp_get_freq(bus_ctrl.lock[PPMU_INT]);
+
+       mif_freq = bus_ctrl.lock[PPMU_MIF] == NULL ? 0 : get_mif_freq;
+       int_freq = bus_ctrl.lock[PPMU_INT] == NULL ? 0 : get_int_freq;
+
+       len = sprintf(buf, "Current Freq(MIF/INT) : (%lu - %lu)\n",
+                       data->curr_freq[PPMU_MIF], data->curr_freq[PPMU_INT]);
+       len += sprintf(buf + len, "Current Lock Freq(MIF/INT) : (%lu - %lu\n)",
+                       mif_freq, int_freq);
+
+       return len;
+}
+
+static ssize_t store_level_lock(struct device *device,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct platform_device *pdev =
+                       to_platform_device(bus_ctrl.dev[PPMU_MIF]);
+       struct busfreq_data *data =
+                       (struct busfreq_data *)platform_get_drvdata(pdev);
+       struct opp *opp[PPMU_TYPE_END];
+       unsigned long freq[PPMU_TYPE_END];
+       int i;
+       int ret;
+
+       ret = sscanf(buf, "%lu %lu", &freq[PPMU_MIF], &freq[PPMU_INT]);
+       if (freq[PPMU_MIF] == 0 || freq[PPMU_INT] == 0 || ret != 2) {
+               pr_info("Releasing the bus level lock\n");
+               bus_ctrl.lock[PPMU_MIF] = NULL;
+               bus_ctrl.lock[PPMU_INT] = NULL;
+               return count;
+       }
+
+       for (i = TABLE_START; i < PPMU_TYPE_END; i++) {
+               if (freq[i] > data->max_freq[i])
+                       freq[i] = data->max_freq[i];
+
+               opp[i] = opp_find_freq_ceil(bus_ctrl.dev[i], &freq[i]);
+               bus_ctrl.lock[i] = opp[i];
+       }
+       return count;
+}
+
+static ssize_t show_locklist(struct device *device,
+               struct device_attribute *attr, char *buf)
+{
+       return dev_lock_list(bus_ctrl.dev[PPMU_MIF], buf);
+}
+
+static ssize_t show_time_in_state(struct device *device,
+               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *pdev =
+                       to_platform_device(bus_ctrl.dev[PPMU_MIF]);
+       struct busfreq_data *data =
+                       (struct busfreq_data *)platform_get_drvdata(pdev);
+       struct busfreq_table *table;
+       ssize_t len = 0;
+       int i;
+
+       table = data->table[PPMU_MIF];
+       len += sprintf(buf, "%s\n", "MIF stat");
+       for (i = LV_0; i < LV_MIF_END; i++)
+               len += sprintf(buf + len, "%u %llu\n", table[i].mem_clk,
+               (unsigned long long)cputime64_to_clock_t(
+                       data->time_in_state[PPMU_MIF][i]));
+
+       table = data->table[PPMU_INT];
+       len += sprintf(buf + len, "\n%s\n", "INT stat");
+       for (i = LV_0; i < LV_INT_END; i++)
+               len += sprintf(buf + len, "%u %llu\n", table[i].mem_clk,
+               (unsigned long long)cputime64_to_clock_t(
+                       data->time_in_state[PPMU_INT][i]));
+       return len;
+}
+
+static DEVICE_ATTR(curr_freq, 0666, show_level_lock, store_level_lock);
+static DEVICE_ATTR(lock_list, 0666, show_locklist, NULL);
+static DEVICE_ATTR(time_in_state, 0666, show_time_in_state, NULL);
+
+static struct attribute *busfreq_attributes[] = {
+       &dev_attr_curr_freq.attr,
+       &dev_attr_lock_list.attr,
+       &dev_attr_time_in_state.attr,
+       NULL
+};
+
+int exynos_request_register(struct notifier_block *n)
+{
+       return blocking_notifier_chain_register(
+                       &exynos_busfreq_notifier_list, n);
+}
+
+void exynos_request_apply(unsigned long freq)
+{
+       blocking_notifier_call_chain(&exynos_busfreq_notifier_list, freq, NULL);
+}
+
+static __devinit int exynos_busfreq_probe(struct platform_device *pdev)
+{
+       struct busfreq_data *data;
+
+       data = kzalloc(sizeof(struct busfreq_data), GFP_KERNEL);
+       if (!data) {
+               pr_err("Unable to create busfreq_data struct\n");
+               return -ENOMEM;
+       }
+
+       data->exynos_buspm_notifier.notifier_call =
+               exynos_buspm_notifier_event;
+       data->exynos_reboot_notifier.notifier_call =
+               exynos_busfreq_reboot_event;
+       data->busfreq_attr_group.attrs = busfreq_attributes;
+       data->exynos_request_notifier.notifier_call =
+               exynos_busfreq_request_event;
+
+       INIT_DELAYED_WORK(&data->worker, exynos_busfreq_timer);
+
+       data->init = exynos5250_init;
+       data->busfreq_prepare = exynos5250_prepare;
+       data->busfreq_post = exynos5250_post;
+
+       if (data->init(&pdev->dev, data)) {
+               pr_err("Failed to init busfreq\n");
+               goto err_busfreq;
+       }
+
+       bus_ctrl.dev[PPMU_MIF] =  data->dev[PPMU_MIF];
+       bus_ctrl.dev[PPMU_INT] =  data->dev[PPMU_INT];
+
+       data->last_time[PPMU_MIF] = get_jiffies_64();
+       data->last_time[PPMU_INT] = get_jiffies_64();
+
+       data->busfreq_kobject = kobject_create_and_add("busfreq",
+                               &cpu_subsys.dev_root->kobj);
+       if (!data->busfreq_kobject)
+               pr_err("Failed to create busfreq kobject\n");
+
+       if (sysfs_create_group(data->busfreq_kobject,
+                               &data->busfreq_attr_group))
+               pr_err("Failed to create attributes group\n");
+
+       if (register_pm_notifier(&data->exynos_buspm_notifier)) {
+               pr_err("Failed to setup bus pm notifier\n");
+               goto err_busfreq;
+       }
+
+       data->use = true;
+
+       if (register_reboot_notifier(&data->exynos_reboot_notifier))
+               pr_err("Failed to setup reboot notifier\n");
+
+       if (exynos_request_register(&data->exynos_request_notifier))
+               pr_err("Failed to setup request notifier\n");
+
+       platform_set_drvdata(pdev, data);
+
+       queue_delayed_work(system_freezable_wq, &data->worker,
+                               data->sampling_rate);
+       return 0;
+
+err_busfreq:
+       if (!IS_ERR(data->vdd_reg[PPMU_INT]))
+               regulator_put(data->vdd_reg[PPMU_INT]);
+
+       if (!IS_ERR(data->vdd_reg[PPMU_MIF]))
+               regulator_put(data->vdd_reg[PPMU_MIF]);
+
+       kfree(data);
+       return -ENODEV;
+}
+
+static __devexit int exynos_busfreq_remove(struct platform_device *pdev)
+{
+       struct busfreq_data *data = platform_get_drvdata(pdev);
+
+       unregister_pm_notifier(&data->exynos_buspm_notifier);
+       unregister_reboot_notifier(&data->exynos_reboot_notifier);
+       regulator_put(data->vdd_reg[PPMU_INT]);
+       regulator_put(data->vdd_reg[PPMU_MIF]);
+       sysfs_remove_group(data->busfreq_kobject, &data->busfreq_attr_group);
+       kfree(data);
+
+       return 0;
+}
+
+static int exynos_busfreq_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct busfreq_data *data =
+               (struct busfreq_data *)platform_get_drvdata(pdev);
+
+       if (data->busfreq_suspend)
+               data->busfreq_suspend();
+       return 0;
+}
+
+static int exynos_busfreq_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct busfreq_data *data =
+               (struct busfreq_data *)platform_get_drvdata(pdev);
+       ppmu_reset(dev);
+
+       if (data->busfreq_resume)
+               data->busfreq_resume();
+       return 0;
+}
+
+static const struct dev_pm_ops exynos_busfreq_pm = {
+       .suspend = exynos_busfreq_suspend,
+       .resume = exynos_busfreq_resume,
+};
+
+static struct platform_driver exynos_busfreq_driver = {
+       .probe  = exynos_busfreq_probe,
+       .remove = __devexit_p(exynos_busfreq_remove),
+       .driver = {
+               .name   = "exynos-busfreq",
+               .owner  = THIS_MODULE,
+               .pm     = &exynos_busfreq_pm,
+       },
+};
+
+static int __init exynos_busfreq_init(void)
+{
+       return platform_driver_register(&exynos_busfreq_driver);
+}
+late_initcall(exynos_busfreq_init);
+
+static void __exit exynos_busfreq_exit(void)
+{
+       platform_driver_unregister(&exynos_busfreq_driver);
+}
+module_exit(exynos_busfreq_exit);
index 9c52e3e..a2383e6 100644 (file)
@@ -298,6 +298,11 @@ static struct map_desc exynos5_iodesc[] __initdata = {
                .pfn            = __phys_to_pfn(EXYNOS_PA_AUDSS),
                .length         = SZ_4K,
                .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S5P_VA_DREXII,
+               .pfn            = __phys_to_pfn(EXYNOS5_PA_DREXII),
+               .length         = SZ_4K,
+               .type           = MT_DEVICE,
        },
 };
 
diff --git a/arch/arm/mach-exynos/dev.c b/arch/arm/mach-exynos/dev.c
new file mode 100644 (file)
index 0000000..ba94ce9
--- /dev/null
@@ -0,0 +1,195 @@
+/* linux/arch/arm/mach-exynos/dev.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * EXYNOS5 Device List support
+ *
+ * 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/device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <mach/dev.h>
+#include <mach/busfreq_exynos5.h>
+
+static LIST_HEAD(domains_list);
+static DEFINE_MUTEX(domains_mutex);
+
+static struct device_domain *find_device_domain(struct device *dev)
+{
+       struct device_domain *tmp_domain, *domain = ERR_PTR(-ENODEV);
+
+       mutex_lock(&domains_mutex);
+       list_for_each_entry(tmp_domain, &domains_list, node) {
+               if (tmp_domain->device == dev) {
+                       domain = tmp_domain;
+                       break;
+               }
+       }
+
+       mutex_unlock(&domains_mutex);
+       return domain;
+}
+
+int dev_add(struct device_domain *dev, struct device *device)
+{
+       if (!dev || !device)
+               return -EINVAL;
+
+       mutex_lock(&domains_mutex);
+       INIT_LIST_HEAD(&dev->domain_list);
+       dev->device = device;
+       list_add(&dev->node, &domains_list);
+       mutex_unlock(&domains_mutex);
+
+       return 0;
+}
+
+struct device *dev_get(const char *name)
+{
+       struct device_domain *domain;
+
+       mutex_lock(&domains_mutex);
+       list_for_each_entry(domain, &domains_list, node)
+               if (strcmp(name, dev_name(domain->device)) == 0)
+                       goto found;
+
+       mutex_unlock(&domains_mutex);
+       return ERR_PTR(-ENODEV);
+found:
+       mutex_unlock(&domains_mutex);
+       return domain->device;
+}
+
+void dev_put(const char *name)
+{
+       return;
+}
+
+int dev_lock(struct device *device, struct device *dev,
+               unsigned long freq)
+{
+       struct device_domain *domain;
+       struct domain_lock *lock;
+       int ret = 0;
+
+       domain = find_device_domain(device);
+
+       if (IS_ERR(domain)) {
+               dev_err(dev, "Can't find device domain.\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&domains_mutex);
+       list_for_each_entry(lock, &domain->domain_list, node) {
+               if (lock->device == dev) {
+                       /* If the lock already exist, only update the freq */
+                       lock->freq = freq;
+                       goto out;
+               }
+       }
+
+       lock = kzalloc(sizeof(struct domain_lock), GFP_KERNEL);
+       if (!lock) {
+               dev_err(device, "Unable to create domain_lock");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       lock->device = dev;
+       lock->freq = freq;
+       list_add(&lock->node, &domain->domain_list);
+
+out:
+       mutex_unlock(&domains_mutex);
+       exynos_request_apply(freq);
+       return ret;
+}
+
+int dev_unlock(struct device *device, struct device *dev)
+{
+       struct device_domain *domain;
+       struct domain_lock *lock;
+
+       domain = find_device_domain(device);
+
+       if (IS_ERR(domain)) {
+               dev_err(dev, "Can't find device domain.\n");
+               return -EINVAL;
+       }
+
+       mutex_lock(&domains_mutex);
+       list_for_each_entry(lock, &domain->domain_list, node) {
+               if (lock->device == dev) {
+                       list_del(&lock->node);
+                       kfree(lock);
+                       break;
+               }
+       }
+
+       mutex_unlock(&domains_mutex);
+
+       return 0;
+}
+
+unsigned long dev_max_freq(struct device *device)
+{
+       struct device_domain *domain;
+       struct domain_lock *lock;
+       unsigned long freq = 0;
+       unsigned long miffreq = 0;
+       unsigned long intfreq = 0;
+       unsigned long miftmp;
+       unsigned long inttmp;
+
+       domain = find_device_domain(device);
+       if (IS_ERR(domain)) {
+               dev_dbg(device, "Can't find device domain.\n");
+               return freq;
+       }
+
+       mutex_lock(&domains_mutex);
+       list_for_each_entry(lock, &domain->domain_list, node) {
+               miftmp = (lock->freq / 1000) * 1000;
+               inttmp = (lock->freq % 1000) * 1000;
+               if (miftmp > miffreq)
+                       miffreq = miftmp;
+               if (inttmp > intfreq)
+                       intfreq = inttmp;
+       }
+
+       freq = miffreq + (intfreq / 1000);
+       mutex_unlock(&domains_mutex);
+
+       return freq;
+}
+
+int dev_lock_list(struct device *device, char *buf)
+{
+       struct device_domain *domain;
+       struct domain_lock *lock;
+       int count = 0;
+
+       domain = find_device_domain(device);
+       if (IS_ERR(domain)) {
+               dev_dbg(device, "Can't find device domain.\n");
+               return 0;
+       }
+
+       mutex_lock(&domains_mutex);
+       count = sprintf(buf, "Lock List\n");
+       list_for_each_entry(lock, &domain->domain_list, node)
+               count += sprintf(buf + count, "%s : %lu\n",
+                               dev_name(lock->device), lock->freq);
+
+       mutex_unlock(&domains_mutex);
+
+       return count;
+}
diff --git a/arch/arm/mach-exynos/include/mach/busfreq_exynos5.h b/arch/arm/mach-exynos/include/mach/busfreq_exynos5.h
new file mode 100644 (file)
index 0000000..def54ad
--- /dev/null
@@ -0,0 +1,103 @@
+/* linux/arch/arm/mach-exynos/include/mach/busfreq_exynos5.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * EXYNOS5 - BUSFreq support
+ *
+ * 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.
+*/
+
+#ifndef __ASM_ARCH_BUSFREQ_H
+#define __ASM_ARCH_BUSFREQ_H __FILE__
+
+#include <linux/notifier.h>
+
+#include <mach/ppmu.h>
+
+#define MAX_LOAD               100
+#define LOAD_HISTORY_SIZE      5
+#define DIVIDING_FACTOR                10000
+
+#define TIMINGROW_OFFSET       0x34
+
+extern unsigned int exynos_result_of_asv;
+extern unsigned int exynos_result_mif_asv;
+extern bool exynos_lot_id;
+extern bool exynos_lot_is_nzvpu;
+
+enum busfreq_level_idx {
+       LV_0,
+       LV_1,
+       LV_2,
+       LV_3,
+       LV_4,
+       LV_5,
+       LV_6,
+       LV_INT_END = LV_4,
+       LV_MIF_END = LV_6,
+};
+
+struct opp;
+struct device;
+struct busfreq_table;
+
+struct busfreq_data {
+       bool use;
+       struct device *dev[PPMU_TYPE_END];
+       struct delayed_work worker;
+       unsigned long curr_freq[PPMU_TYPE_END];
+       unsigned long max_freq[PPMU_TYPE_END];
+       unsigned long min_freq[PPMU_TYPE_END];
+       struct regulator *vdd_reg[PPMU_TYPE_END];
+       unsigned int sampling_rate;
+       struct kobject *busfreq_kobject;
+       struct busfreq_table *table[PPMU_TYPE_END];
+       unsigned long long time_in_state[PPMU_TYPE_END][LV_INT_END];
+       unsigned long long last_time[PPMU_TYPE_END];
+       unsigned int load_history[PPMU_END][LOAD_HISTORY_SIZE];
+       int index;
+
+       struct notifier_block exynos_buspm_notifier;
+       struct notifier_block exynos_reboot_notifier;
+       struct notifier_block exynos_request_notifier;
+       struct attribute_group busfreq_attr_group;
+       int (*init)     (struct device *dev, struct busfreq_data *data);
+       void (*monitor) (struct busfreq_data *data, struct opp **mif_opp,
+                       struct opp **int_opp);
+       void (*target)  (struct busfreq_data *data,
+                       enum ppmu_type type, int index);
+       unsigned int (*get_int_volt) (unsigned long freq);
+       int (*get_table_index) (unsigned long freq, enum ppmu_type type);
+       void (*busfreq_prepare) (int index);
+       void (*busfreq_post) (int index);
+       void (*busfreq_suspend) (void);
+       void (*busfreq_resume) (void);
+
+       /* Dividers calculated at boot/probe-time */
+       unsigned int lex_divtable[LV_INT_END];
+       unsigned int r0x_divtable[LV_INT_END];
+       unsigned int r1x_divtable[LV_INT_END];
+       unsigned int cdrex_divtable[LV_MIF_END];
+       unsigned int cdrex2_divtable[LV_MIF_END];
+};
+
+struct busfreq_table {
+       unsigned int idx;
+       unsigned int mem_clk;
+       unsigned int volt;
+       unsigned int clk_topdiv;
+       unsigned int clk_dmc0div;
+       unsigned int clk_dmc1div;
+};
+
+void exynos_request_apply(unsigned long freq);
+unsigned long step_down(struct busfreq_data *data,
+                               enum ppmu_type type, int step);
+int exynos5250_init(struct device *dev, struct busfreq_data *data);
+void exynos5250_prepare(int index);
+void exynos5250_post(int index);
+
+#endif /* __ASM_ARCH_BUSFREQ_H */
diff --git a/arch/arm/mach-exynos/include/mach/dev.h b/arch/arm/mach-exynos/include/mach/dev.h
new file mode 100644 (file)
index 0000000..2ef6001
--- /dev/null
@@ -0,0 +1,43 @@
+/* linux/arch/arm/mach-exynos/include/mach/dev.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * EXYNOS - Device List support
+ *
+ * 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.
+*/
+
+#ifndef __ASM_ARCH_DEV_H
+#define __ASM_ARCH_DEV_H __FILE__
+
+struct device;
+
+struct domain_lock {
+       struct list_head node;
+
+       struct device_domain *domain;
+       struct device *device;
+       unsigned long freq;
+};
+
+struct device_domain {
+       struct list_head node;
+
+       struct device *device;
+       struct list_head domain_list;
+};
+
+int dev_add(struct device_domain *domain, struct device *device);
+struct device *dev_get(const char *name);
+void dev_put(const char *name);
+int dev_lock(struct device *device, struct device *dev, unsigned long freq);
+int dev_lock_fix(struct device *device, struct device *dev, unsigned long freq);
+int dev_unlock(struct device *device, struct device *dev);
+void dev_unlock_fix(struct device *device, struct device *dev);
+unsigned long dev_max_freq(struct device *device);
+int dev_lock_list(struct device *dev, char *buf);
+
+#endif /* __ASM_ARCH_DEV_H */
index 09d8c44..0541839 100644 (file)
 #define EXYNOS4_PA_SDRAM               0x40000000
 #define EXYNOS5_PA_SDRAM               0x40000000
 
+#define EXYNOS5_PA_DREXII              0x10DD0000
+
 #define EXYNOS5_PA_G3D                 0x11800000
 
 /* Compatibiltiy Defines */
index e63665e..5c04df0 100644 (file)
 
 #define EXYNOS5_BPLL_CON0                      EXYNOS_CLKREG(0x20110)
 #define EXYNOS5_CLKSRC_CDREX                   EXYNOS_CLKREG(0x20200)
+
+/* Clock Rate Control Registers */
+#define EXYNOS5_CLKDIV_SYSRGT                  EXYNOS_CLKREG(0x04508)
+#define EXYNOS5_CLKDIV_SYSLFT                  EXYNOS_CLKREG(0x08900)
+#define EXYNOS5_CLKDIV_LEX                     EXYNOS_CLKREG(0x14500)
+#define EXYNOS5_CLKDIV_R0X                     EXYNOS_CLKREG(0x18500)
+#define EXYNOS5_CLKDIV_R1X                     EXYNOS_CLKREG(0x1C500)
 #define EXYNOS5_CLKDIV_CDREX                   EXYNOS_CLKREG(0x20500)
 
+#define EXYNOS5_CLKDIV_STAT_TOP1               EXYNOS_CLKREG(0x10614)
+#define EXYNOS5_CLKDIV_STAT_LEX                        EXYNOS_CLKREG(0x14600)
+#define EXYNOS5_CLKDIV_STAT_R0X                        EXYNOS_CLKREG(0x18600)
+#define EXYNOS5_CLKDIV_STAT_R1X                        EXYNOS_CLKREG(0x1C600)
+#define EXYNOS5_CLKDIV_STAT_CDREX              EXYNOS_CLKREG(0x20600)
+
+#define EXYNOS5_CLKDIV_SYSRGT_ACLK_R1BX_SHIFT  (0)
+#define EXYNOS5_CLKDIV_SYSRGT_ACLK_R1BX_MASK   (0x7 << EXYNOS5_CLKDIV_SYSRGT_ACLK_R1BX_SHIFT)
+#define EXYNOS5_CLKDIV_SYSLFT_PCLK_SYSLFT_SHIFT        (4)
+#define EXYNOS5_CLKDIV_SYSLFT_PCLK_SYSLFT_MASK (0x7 << EXYNOS5_CLKDIV_SYSLFT_PCLK_SYSLFT_SHIFT)
+#define EXYNOS5_CLKDIV_SYSLFT_ACLK_SYSLFT_SHIFT        (0)
+#define EXYNOS5_CLKDIV_SYSLFT_ACLK_SYSLFT_MASK (0x7 << EXYNOS5_CLKDIV_SYSLFT_ACLK_SYSLFT_SHIFT)
+
+#define EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_SHIFT        (28)
+#define EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_MASK (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK300_DISP1_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK333_SHIFT      (20)
+#define EXYNOS5_CLKDIV_TOP0_ACLK333_MASK       (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK333_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK266_SHIFT      (16)
+#define EXYNOS5_CLKDIV_TOP0_ACLK266_MASK       (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK266_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK200_SHIFT      (12)
+#define EXYNOS5_CLKDIV_TOP0_ACLK200_MASK       (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK200_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK166_SHIFT      (8)
+#define EXYNOS5_CLKDIV_TOP0_ACLK166_MASK       (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK166_SHIFT)
+#define EXYNOS5_CLKDIV_TOP0_ACLK66_SHIFT       (0)
+#define EXYNOS5_CLKDIV_TOP0_ACLK66_MASK                (0x7 << EXYNOS5_CLKDIV_TOP0_ACLK66_SHIFT)
+
+#define EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_SHIFT   (24)
+#define EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_MASK    (0x7 << EXYNOS5_CLKDIV_TOP1_ACLK66_PRE_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_SHIFT  (20)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_MASK   (0x7 << EXYNOS5_CLKDIV_TOP1_ACLK400_ISP_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_SHIFT  (16)
+#define EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_MASK   (0x7 << EXYNOS5_CLKDIV_TOP1_ACLK400_IOP_SHIFT)
+#define EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_SHIFT (12)
+#define EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_MASK  (0x7 << EXYNOS5_CLKDIV_TOP1_ACLK300_GSCL_SHIFT)
+#define EXYNOS5_CLKDIV_LEX_ATCLK_LEX_SHIFT     (8)
+#define EXYNOS5_CLKDIV_LEX_ATCLK_LEX_MASK      (0x7 << EXYNOS5_CLKDIV_LEX_ATCLK_LEX_SHIFT)
+#define EXYNOS5_CLKDIV_LEX_PCLK_LEX_SHIFT      (4)
+#define EXYNOS5_CLKDIV_LEX_PCLK_LEX_MASK       (0x7 << EXYNOS5_CLKDIV_LEX_PCLK_LEX_SHIFT)
+
+#define EXYNOS5_CLKDIV_R0X_PCLK_R0X_SHIFT      (4)
+#define EXYNOS5_CLKDIV_R0X_PCLK_R0X_MASK       (0x7 << EXYNOS5_CLKDIV_R0X_PCLK_R0X_SHIFT)
+#define EXYNOS5_CLKDIV_R1X_PCLK_R1X_SHIFT      (4)
+#define EXYNOS5_CLKDIV_R1X_PCLK_R1X_MASK       (0x7 << EXYNOS5_CLKDIV_R1X_PCLK_R1X_SHIFT)
+
+#define EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_SHIFT (28)
+#define EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_MASK  (0x7 << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX2_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_SHIFT  (24)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_MASK   (0x7 << EXYNOS5_CLKDIV_CDREX_ACLK_EFCON_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_SHIFT   (20)
+#define EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_MASK    (0x7 << EXYNOS5_CLKDIV_CDREX_MCLK_DPHY_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_SHIFT  (16)
+#define EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_MASK   (0x7 << EXYNOS5_CLKDIV_CDREX_MCLK_CDREX_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_C2C200_SHIFT (12)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_C2C200_MASK  (0x7 << EXYNOS5_CLKDIV_CDREX_ACLK_C2C200_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_CLK400_SHIFT (8)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_CLK400_MASK  (0x7 << EXYNOS5_CLKDIV_CDREX_ACLK_CLK400_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_SHIFT  (4)
+#define EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_MASK   (0x7 << EXYNOS5_CLKDIV_CDREX_PCLK_CDREX_SHIFT)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_SHIFT  (0)
+#define EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_MASK   (0x7 << EXYNOS5_CLKDIV_CDREX_ACLK_CDREX_SHIFT)
+
 #define EXYNOS5_EPLL_LOCK                      EXYNOS_CLKREG(0x10030)
 #define EXYNOS5_VPLL_LOCK                      EXYNOS_CLKREG(0x10040)
 #define EXYNOS5_GPLL_LOCK                      EXYNOS_CLKREG(0x10050)
index 9d3ddb7..6f158af 100644 (file)
@@ -31,6 +31,8 @@
 #include <asm/mach/arch.h>
 #include <asm/hardware/gic.h>
 #include <mach/map.h>
+#include <mach/ppmu.h>
+#include <mach/dev.h>
 #include <mach/ohci.h>
 #include <mach/regs-pmu.h>
 #include <mach/sysmmu.h>
@@ -877,6 +879,16 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
        {},
 };
 
+#ifdef CONFIG_BUSFREQ_OPP
+/* BUSFREQ to control memory/bus*/
+static struct device_domain busfreq;
+
+static struct platform_device exynos5_busfreq = {
+       .id = -1,
+       .name = "exynos-busfreq",
+};
+#endif
+
 static struct platform_device *smdk5250_devices[] __initdata = {
        &smdk5250_lcd, /* for platform_lcd device */
        &exynos_device_md0, /* for media device framework */
@@ -891,6 +903,9 @@ static struct platform_device *smdk5250_devices[] __initdata = {
        &s3c_device_adc_ntc_thermistor1,
        &s3c_device_adc_ntc_thermistor2,
        &s3c_device_adc_ntc_thermistor3,
+#ifdef CONFIG_BUSFREQ_OPP
+       &exynos5_busfreq,
+#endif
 };
 
 static struct regulator_consumer_supply dummy_supplies[] = {
@@ -1106,6 +1121,14 @@ static void __init exynos5250_dt_machine_init(void)
        s3c_adc_ntc_init(&s3c_device_adc_ntc_thermistor2);
        s3c_adc_ntc_init(&s3c_device_adc_ntc_thermistor3);
 
+#ifdef CONFIG_BUSFREQ_OPP
+       dev_add(&busfreq, &exynos5_busfreq.dev);
+       ppmu_init(&exynos_ppmu[PPMU_CPU], &exynos5_busfreq.dev);
+       ppmu_init(&exynos_ppmu[PPMU_DDR_C], &exynos5_busfreq.dev);
+       ppmu_init(&exynos_ppmu[PPMU_DDR_R1], &exynos5_busfreq.dev);
+       ppmu_init(&exynos_ppmu[PPMU_DDR_L], &exynos5_busfreq.dev);
+       ppmu_init(&exynos_ppmu[PPMU_RIGHT0_BUS], &exynos5_busfreq.dev);
+#endif
        platform_add_devices(smdk5250_devices, ARRAY_SIZE(smdk5250_devices));
 out:
        of_node_put(srom_np);
index 3558277..b78ba5f 100644 (file)
@@ -41,6 +41,7 @@
 #define S5P_VA_GIC_DIST                S3C_ADDR(0x02820000)
 
 #define S5P_VA_AUDSS           S3C_ADDR(0x02910000)
+#define S5P_VA_DREXII          S3C_ADDR(0x02930000)
 
 #define VA_VIC(x)              (S3C_VA_IRQ + ((x) * 0x10000))
 #define VA_VIC0                        VA_VIC(0)