select HAVE_S3C_RTC if RTC_CLASS
select NEED_MACH_GPIO_H
select NEED_MACH_MEMORY_H
+ select ARCH_HAS_OPP
+ select PM_OPP if PM
help
Support for SAMSUNG's EXYNOS SoCs (EXYNOS4/5)
};
buck1_reg: BUCK1 {
- regulator-name = "VDD_MIF_1.2V";
+ regulator-name = "vdd_mif";
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1200000>;
regulator-always-on;
};
buck3_reg: BUCK3 {
- regulator-name = "VDD_INT_1.0V";
+ regulator-name = "vdd_int";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1200000>;
regulator-always-on;
endmenu
+menu "Busfreq Model"
+ depends on ARM_EXYNOS5250_CPUFREQ
+
+config BUSFREQ_OPP
+ bool "Busfreq with OPP"
+ depends on ARM_EXYNOS5250_CPUFREQ
+
+endmenu
+
endif
obj-$(CONFIG_ARCH_EXYNOS) += common.o
obj-$(CONFIG_ARCH_EXYNOS4) += clock-exynos4.o
-obj-$(CONFIG_ARCH_EXYNOS5) += clock-exynos5.o
+obj-$(CONFIG_ARCH_EXYNOS5) += clock-exynos5.o ppmu.o
obj-$(CONFIG_CPU_EXYNOS4210) += clock-exynos4210.o
obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o
obj-$(CONFIG_PM) += pm.o
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
--- /dev/null
+/* linux/arch/arm/mach-exynos/asv-5250.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS5250 - ASV(Adaptive Supply Voltage) driver
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <mach/map.h>
+
+#include <plat/cpu.h>
+
+/* ASV function for Fused Chip */
+#define IDS_ARM_OFFSET 24
+#define IDS_ARM_MASK 0xFF
+#define HPM_OFFSET 12
+#define HPM_MASK 0x1F
+
+#define FUSED_SG_OFFSET 3
+#define ORIG_SG_OFFSET 17
+#define ORIG_SG_MASK 0xF
+#define MOD_SG_OFFSET 21
+#define MOD_SG_MASK 0x7
+
+#define DEFAULT_ASV_GROUP 1
+#define DEFAULT_MIF_ASV_GROUP 0
+
+#define CHIP_ID_REG (S5P_VA_CHIPID + 0x4)
+#define LOT_ID_REG (S5P_VA_CHIPID + 0x14)
+
+/* ASV choice table based on HPM and IDS values */
+struct asv_table {
+ unsigned int hpm_limit; /* HPM value to decide target group */
+ unsigned int ids_limit; /* IDS value to decide target group */
+};
+
+struct samsung_asv {
+ unsigned int package_id; /* fused value for pakage */
+ unsigned int hpm_result; /* hpm value of chip */
+ unsigned int ids_result; /* ids value of chip */
+ /* returns IDS value */
+ int (*get_ids)(struct samsung_asv *asv_info);
+ /* returns HPM value */
+ int (*get_hpm)(struct samsung_asv *asv_info);
+ /* store asv result for later use */
+ int (*store_result)(struct samsung_asv *asv_info);
+};
+
+unsigned int exynos_result_of_asv;
+unsigned int exynos_result_mif_asv;
+bool exynos_lot_id;
+bool exynos_lot_is_nzvpu;
+
+/* ASV group voltage table */
+#define CPUFREQ_LEVEL_END (16)
+static const unsigned int asv_voltage_special[CPUFREQ_LEVEL_END][11] = {
+ /* ASV0 does not exist */
+ /* ASV1, ASV2, ASV3, ASV4, ASV5, ASV6, ASV7, ASV8, ASV9, ASV10 */
+ { 0, 1300000, 1275000, 1287500, 1275000, 1275000, 1262500, 1250000, 1237500, 1225000, 1225000 }, /* L0 */
+ { 0, 1250000, 1237500, 1250000, 1237500, 1250000, 1237500, 1225000, 1212500, 1200000, 1200000 }, /* L1 */
+ { 0, 1225000, 1200000, 1212500, 1200000, 1212500, 1200000, 1187500, 1175000, 1175000, 1150000 }, /* L2 */
+ { 0, 1200000, 1175000, 1200000, 1175000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000 }, /* L3 */
+ { 0, 1150000, 1125000, 1150000, 1125000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000 }, /* L4 */
+ { 0, 1125000, 1112500, 1125000, 1112500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, /* L5 */
+ { 0, 1100000, 1075000, 1100000, 1087500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500 }, /* L6 */
+ { 0, 1075000, 1050000, 1062500, 1050000, 1062500, 1050000, 1050000, 1037500, 1025000, 1012500 }, /* L7 */
+ { 0, 1050000, 1025000, 1050000, 1037500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500 }, /* L8 */
+ { 0, 1025000, 1012500, 1025000, 1012500, 1025000, 1012500, 1000000, 1000000, 987500, 975000 }, /* L9 */
+ { 0, 1012500, 1000000, 1012500, 1000000, 1012500, 1000000, 987500, 975000, 975000, 962500 }, /* L10 */
+ { 0, 1000000, 975000, 1000000, 975000, 1000000, 987500, 975000, 962500, 962500, 950000 }, /* L11 */
+ { 0, 975000, 962500, 975000, 962500, 975000, 962500, 950000, 937500, 925000, 925000 }, /* L12 */
+ { 0, 950000, 937500, 950000, 937500, 950000, 937500, 925000, 925000, 925000, 912500 }, /* L13 */
+ { 0, 937500, 925000, 937500, 925000, 937500, 925000, 912500, 912500, 900000, 900000 }, /* L14 */
+ { 0, 925000, 912500, 925000, 912500, 925000, 912500, 900000, 900000, 887500, 887500 }, /* L15 */
+};
+
+static const unsigned int asv_voltage[CPUFREQ_LEVEL_END][12] = {
+ { 1300000, 1275000, 1275000, 1262500, 1250000, 1225000, 1212500, 1200000, 1187500, 1175000, 1150000, 1125000 }, /* L0 */
+ { 1250000, 1225000, 1225000, 1212500, 1200000, 1187500, 1175000, 1162500, 1150000, 1137500, 1112500, 1100000 }, /* L1 */
+ { 1225000, 1187500, 1175000, 1162500, 1150000, 1137500, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500 }, /* L2 */
+ { 1200000, 1125000, 1125000, 1125000, 1112500, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000 }, /* L3 */
+ { 1150000, 1100000, 1100000, 1100000, 1087500, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000 }, /* L4 */
+ { 1125000, 1075000, 1075000, 1062500, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, 975000 }, /* L5 */
+ { 1100000, 1050000, 1050000, 1037500, 1025000, 1012500, 1000000, 987500, 975000, 962500, 950000, 925000 }, /* L6 */
+ { 1075000, 1037500, 1037500, 1012500, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, 912500 }, /* L7 */
+ { 1050000, 1025000, 1012500, 987500, 975000, 962500, 950000, 937500, 925000, 912500, 912500, 900000 }, /* L8 */
+ { 1025000, 1000000, 987500, 975000, 962500, 950000, 937500, 925000, 912500, 900000, 900000, 900000 }, /* L9 */
+ { 1012500, 975000, 962500, 950000, 937500, 925000, 912500, 900000, 900000, 900000, 900000, 900000 }, /* L10 */
+ { 1000000, 962500, 950000, 937500, 925000, 912500, 900000, 900000, 900000, 900000, 900000, 900000 }, /* L11 */
+ { 975000, 950000, 937500, 925000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, 887500 }, /* L12 */
+ { 950000, 937500, 925000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, 887500, 887500 }, /* L13 */
+ { 937500, 925000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, 887500, 887500, 875000 }, /* L14 */
+ { 925000, 912500, 900000, 900000, 900000, 900000, 900000, 900000, 887500, 887500, 875000, 875000 }, /* L15 */
+};
+
+/* Original ASV table had 10 levels */
+static struct asv_table exynos5250_limit_orig[] = {
+ /* HPM, IDS */
+ { 0, 0}, /* Reserved Group */
+ { 9, 7},
+ { 10, 9},
+ { 12, 11},
+ { 14, 14},
+ { 15, 17},
+ { 16, 20},
+ { 17, 23},
+ { 18, 27},
+ { 19, 30},
+ { 100, 100},
+ { 999, 999}, /* Reserved Group */
+};
+
+/* New ASV table has 12 levels */
+static struct asv_table exynos5250_limit[] = {
+ /* HPM, IDS */
+ { 6, 7},
+ { 8, 9},
+ { 9, 10},
+ { 10, 11},
+ { 12, 13},
+ { 13, 15},
+ { 14, 17},
+ { 16, 21},
+ { 17, 25},
+ { 19, 32},
+ { 20, 39},
+ { 100, 100},
+ { 999, 999}, /* Reserved Group */
+};
+
+/* For MIF busfreq support */
+static struct asv_table exynos5250_mif_limit[] = {
+ /* HPM, LOCK */
+ { 0, 0}, /* Reserved Group */
+ { 12, 100},
+ { 15, 112},
+ { 100, 512},
+};
+
+const unsigned int exynos5250_cpufreq_get_asv(unsigned int index)
+{
+ unsigned int asv_group = exynos_result_of_asv;
+ if (exynos_lot_id)
+ return asv_voltage_special[index][asv_group];
+ else
+ return asv_voltage[index][asv_group];
+}
+
+char *special_lot_id_list[] = {
+ "NZVPU",
+ "NZVR7",
+};
+
+/*
+ * If lot id is "NZVPU" the ARM_IDS value needs to be modified
+ */
+static int exynos5250_check_lot_id(struct samsung_asv *asv_info)
+{
+ unsigned int lid_reg = 0;
+ unsigned int rev_lid = 0;
+ unsigned int i;
+ unsigned int tmp;
+ unsigned int wno;
+ char lot_id[5];
+
+ lid_reg = __raw_readl(LOT_ID_REG);
+
+ for (i = 0; i < 32; i++) {
+ tmp = (lid_reg >> i) & 0x1;
+ rev_lid += tmp << (31 - i);
+ }
+
+ lot_id[0] = 'N';
+ lid_reg = (rev_lid >> 11) & 0x1FFFFF;
+
+ for (i = 4; i >= 1; i--) {
+ tmp = lid_reg % 36;
+ lid_reg /= 36;
+ lot_id[i] = (tmp < 10) ? (tmp + '0') : ((tmp - 10) + 'A');
+ }
+
+ wno = (rev_lid >> 6) & 0x1f;
+
+ /* NZVPU lot has incorrect IDS value */
+ if ((!strncmp(lot_id, "NZVPU", ARRAY_SIZE(lot_id)))) {
+ exynos_lot_is_nzvpu = true;
+ printk(KERN_INFO "Exynos5250: Lot ID is %s and wafer number is %d\n",
+ lot_id, wno);
+ if (wno >= 2 && wno <= 6)
+ asv_info->ids_result -= 16;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(special_lot_id_list); i++) {
+ if (!strncmp(lot_id, special_lot_id_list[i],
+ ARRAY_SIZE(lot_id))) {
+ printk(KERN_INFO "Exynos5250: Lot ID is %s\n", lot_id);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int exynos5250_asv_store_result(struct samsung_asv *asv_info)
+{
+ unsigned int i;
+
+ if (!exynos5250_check_lot_id(asv_info))
+ exynos_lot_id = true;
+
+ /* New ASV table */
+ if (!exynos_lot_id) {
+ for (i = 0; i < ARRAY_SIZE(exynos5250_limit); i++) {
+ if ((asv_info->ids_result <=
+ exynos5250_limit[i].ids_limit) || \
+ (asv_info->hpm_result <=
+ exynos5250_limit[i].hpm_limit)) {
+ exynos_result_of_asv = i;
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(exynos5250_mif_limit); i++) {
+ if (asv_info->hpm_result <=
+ exynos5250_mif_limit[i].hpm_limit) {
+ exynos_result_mif_asv = i;
+ break;
+ }
+ }
+ /* Original ASV table */
+ } else {
+ for (i = 0; i < ARRAY_SIZE(exynos5250_limit_orig); i++) {
+ if ((asv_info->ids_result <=
+ exynos5250_limit_orig[i].ids_limit) || \
+ (asv_info->hpm_result <=
+ exynos5250_limit_orig[i].hpm_limit)) {
+ exynos_result_of_asv = i;
+ exynos_result_mif_asv = i;
+ break;
+ }
+ }
+ }
+ /*
+ * If ASV result value is lower than the default value then choose
+ * the default group (1)
+ */
+ if (exynos_result_of_asv < DEFAULT_ASV_GROUP)
+ exynos_result_of_asv = DEFAULT_ASV_GROUP;
+
+ printk(KERN_INFO "EXYNOS5250: IDS:%d HPM:%d RESULT:%d MIF:%d\n",
+ asv_info->ids_result, asv_info->hpm_result,
+ exynos_result_of_asv, exynos_result_mif_asv);
+
+ return 0;
+}
+
+static int exynos5250_asv_init(void)
+{
+ unsigned int tmp;
+ unsigned int exynos_orig_sp;
+ unsigned int exynos_mod_sp;
+ int exynos_cal_asv;
+ struct samsung_asv *exynos_asv;
+
+ printk(KERN_INFO "EXYNOS5250: Adaptive Support Voltage init\n");
+
+ exynos_asv = kzalloc(sizeof(struct samsung_asv), GFP_KERNEL);
+ if (!exynos_asv)
+ return -ENOMEM;
+
+ tmp = __raw_readl(CHIP_ID_REG);
+ exynos_asv->package_id = tmp; /* Store PKG_ID */
+
+ /* If Speed group is fused then retrieve it from there */
+ if ((tmp >> FUSED_SG_OFFSET) & 0x1) {
+ exynos_orig_sp = (tmp >> ORIG_SG_OFFSET) & ORIG_SG_MASK;
+ exynos_mod_sp = (tmp >> MOD_SG_OFFSET) & MOD_SG_MASK;
+ exynos_cal_asv = exynos_orig_sp - exynos_mod_sp;
+
+ /*
+ * If There is no origin speed group,
+ * store 1 asv group into exynos_result_of_asv.
+ */
+ if (!exynos_orig_sp) {
+ printk(KERN_INFO "EXYNOS5250: No Origin speed Group\n");
+ exynos_result_of_asv = DEFAULT_ASV_GROUP;
+ } else {
+ if (exynos_cal_asv < DEFAULT_ASV_GROUP) {
+ exynos_result_of_asv = DEFAULT_ASV_GROUP;
+ exynos_result_mif_asv = DEFAULT_ASV_GROUP;
+ } else {
+ exynos_result_of_asv = exynos_cal_asv;
+ exynos_result_mif_asv = DEFAULT_MIF_ASV_GROUP;
+ }
+ }
+
+ printk(KERN_INFO "EXYNOS5250: ORIG: %d MOD: %d RESULT: %d\n",
+ exynos_orig_sp, exynos_mod_sp, exynos_result_of_asv);
+
+ return 0;
+ }
+
+ exynos_asv->ids_result = (exynos_asv->package_id >> IDS_ARM_OFFSET) & IDS_ARM_MASK;
+ exynos_asv->hpm_result = (exynos_asv->package_id >> HPM_OFFSET) & HPM_MASK;
+ exynos5250_asv_store_result(exynos_asv);
+
+ return 0;
+}
+device_initcall_sync(exynos5250_asv_init);
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
.pfn = __phys_to_pfn(EXYNOS5_PA_USB_PHY),
.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,
},
};
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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 */
#define EXYNOS5_PA_SPI1 0x12D30000
#define EXYNOS5_PA_SPI2 0x12D40000
+#define EXYNOS5_PA_PPMU_DDR_C 0x10C40000
+#define EXYNOS5_PA_PPMU_DDR_R1 0x10C50000
+#define EXYNOS5_PA_PPMU_CPU 0x10C60000
+#define EXYNOS5_PA_PPMU_DDR_L 0x10CB0000
+#define EXYNOS5_PA_PPMU_RIGHT0_BUS 0x13660000
+
#define EXYNOS4_PA_GPIO1 0x11400000
#define EXYNOS4_PA_GPIO2 0x11000000
#define EXYNOS4_PA_GPIO3 0x03860000
#define EXYNOS4_PA_SDRAM 0x40000000
#define EXYNOS5_PA_SDRAM 0x40000000
+#define EXYNOS5_PA_DREXII 0x10DD0000
+
/* Compatibiltiy Defines */
#define S3C_PA_HSMMC0 EXYNOS4_PA_HSMMC(0)
--- /dev/null
+/* linux/arch/arm/mach-exynos/include/mach/ppmu.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS PPMU Header file
+ *
+ * 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_PPMU_H
+#define __ASM_ARCH_PPMU_H __FILE__
+
+#define PPMU_NR_COUNTERS 4
+
+#define PPMU_CNTENS 0x10
+#define PPMU_CNTENC 0x20
+#define PPMU_INTENS 0x30
+#define PPMU_INTENC 0x40
+#define PPMU_FLAG 0x50
+
+#define PPMU_CCNT 0x100
+#define PPMU_PMCNT0 0x110
+#define PPMU_PMCNT_OFFSET 0x10
+
+#define PPMU_BEVT0SEL 0x1000
+#define PPMU_BEVTSEL_OFFSET 0x100
+#define PPMU_CNT_RESET 0x1800
+
+#define DEVT0_SEL 0x1000
+#define DEVT0_ID 0x1010
+#define DEVT0_IDMSK 0x1014
+#define DEVT_ID_OFFSET 0x100
+
+#define DEFAULT_WEIGHT 1
+#define MAX_CCNT 100
+#define RDWR_DATA_COUNT 0x7
+
+#define PMCNT_OFFSET(i) (PPMU_PMCNT0 + (PPMU_PMCNT_OFFSET * i))
+#define PPMU_BEVTSEL(i) (PPMU_BEVT0SEL + (PPMU_BEVTSEL_OFFSET * i))
+
+enum ppmu_type {
+ PPMU_MIF,
+ PPMU_INT,
+ PPMU_TYPE_END,
+};
+
+enum exynos_ppmu {
+ PPMU_CPU,
+ PPMU_DDR_C,
+ PPMU_DDR_R1,
+ PPMU_DDR_L,
+ PPMU_RIGHT0_BUS,
+ PPMU_END,
+};
+
+extern unsigned long long ppmu_load[PPMU_END];
+
+struct exynos_ppmu_hw {
+ struct list_head node;
+ void __iomem *hw_base;
+ unsigned int base_addr;
+ unsigned int ccnt;
+ unsigned int event[PPMU_NR_COUNTERS];
+ unsigned int weight;
+ int usage;
+ int id;
+ struct device *dev;
+ unsigned int count[PPMU_NR_COUNTERS];
+};
+
+void exynos_ppmu_reset(struct exynos_ppmu_hw *ppmu);
+void exynos_ppmu_start(struct exynos_ppmu_hw *ppmu);
+void exynos_ppmu_stop(struct exynos_ppmu_hw *ppmu);
+void exynos_ppmu_setevent(struct exynos_ppmu_hw *ppmu,
+ unsigned int evt_num);
+unsigned long long exynos_ppmu_update(struct exynos_ppmu_hw *ppmu, int ch);
+
+void ppmu_init(struct exynos_ppmu_hw *ppmu, struct device *dev);
+void ppmu_start(struct device *dev);
+void ppmu_update(struct device *dev, int ch);
+void ppmu_reset(struct device *dev);
+
+extern struct exynos_ppmu_hw exynos_ppmu[];
+
+#endif /* __ASM_ARCH_PPMU_H */
+
#define EXYNOS4_CLKDIV_STAT_RIGHTBUS EXYNOS_CLKREG(0x08600)
#define EXYNOS4_CLKGATE_IP_RIGHTBUS EXYNOS_CLKREG(0x08800)
+#define EXYNOS5_CLKDIV_STAT_TOP0 EXYNOS_CLKREG(0x10610)
#define EXYNOS4_EPLL_LOCK EXYNOS_CLKREG(0x0C010)
#define EXYNOS4_VPLL_LOCK EXYNOS_CLKREG(0x0C020)
#define EXYNOS5_CLKSRC_CDREX EXYNOS_CLKREG(0x20200)
#define EXYNOS5_CLKDIV_CDREX EXYNOS_CLKREG(0x20500)
+
+/* 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_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_PLL_DIV2_SEL EXYNOS_CLKREG(0x20A24)
#define EXYNOS5_EPLL_LOCK EXYNOS_CLKREG(0x10030)
#include <linux/platform_data/usb-exynos.h>
#include <linux/platform_data/samsung-usbphy.h>
+#include <mach/ppmu.h>
+#include <mach/dev.h>
+
#include <plat/cpu.h>
#include <plat/regs-serial.h>
#include <plat/regs-srom.h>
#include "common.h"
+#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",
+};
+static struct platform_device *smdk5250_devices[] __initdata = {
+ &exynos5_busfreq,
+};
+#endif
+
+
static struct samsung_usbphy_data exynos5_usbphy_pdata = {
.pmu_isolation = s5p_usb_phy_pmu_isolation,
.phy_cfg_sel = s5p_usb_phy_cfg_sel,
exynos5_i2c_setup();
of_platform_populate(NULL, of_default_bus_match_table,
exynos5250_auxdata_lookup, NULL);
+#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);
+ platform_add_devices(smdk5250_devices, ARRAY_SIZE(smdk5250_devices));
+#endif
+
}
static char const *exynos5250_dt_compat[] __initdata = {
--- /dev/null
+/* linux/arch/arm/mach-exynos/ppmu.c
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS5 - CPU PPMU 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/types.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/math64.h>
+
+#include <plat/cpu.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/ppmu.h>
+
+static LIST_HEAD(ppmu_list);
+
+unsigned long long ppmu_load[PPMU_END];
+
+void exynos_ppmu_reset(struct exynos_ppmu_hw *ppmu)
+{
+ writel(0x3 << 1, ppmu->hw_base);
+ writel(0x8000000f, ppmu->hw_base + PPMU_CNTENS);
+}
+
+void exynos_ppmu_setevent(struct exynos_ppmu_hw *ppmu,
+ unsigned int evt_num)
+{
+ writel(ppmu->event[evt_num],
+ ppmu->hw_base + PPMU_BEVTSEL(evt_num));
+}
+
+void exynos_ppmu_start(struct exynos_ppmu_hw *ppmu)
+{
+ writel(0x1, ppmu->hw_base);
+}
+
+void exynos_ppmu_stop(struct exynos_ppmu_hw *ppmu)
+{
+ writel(0x0, ppmu->hw_base);
+}
+
+unsigned long long exynos_ppmu_update(struct exynos_ppmu_hw *ppmu, int ch)
+{
+ unsigned long long total = 0;
+
+ ppmu->ccnt = readl(ppmu->hw_base + PPMU_CCNT);
+
+ if (ppmu->ccnt == 0)
+ ppmu->ccnt = MAX_CCNT;
+
+ if (ch >= PPMU_NR_COUNTERS || ppmu->event[ch] == 0)
+ return 0;
+
+ if (ch == 3)
+ total = (((u64)readl(ppmu->hw_base +
+ PMCNT_OFFSET(ch)) << 8) | readl(ppmu->hw_base +
+ PMCNT_OFFSET(ch + 1)));
+ else
+ total = readl(ppmu->hw_base + PMCNT_OFFSET(ch));
+
+ if (total > ppmu->ccnt)
+ total = ppmu->ccnt;
+
+ return div64_u64((total * ppmu->weight * 100), ppmu->ccnt);
+}
+
+void ppmu_start(struct device *dev)
+{
+ struct exynos_ppmu_hw *ppmu;
+
+ list_for_each_entry(ppmu, &ppmu_list, node)
+ if (ppmu->dev == dev)
+ exynos_ppmu_start(ppmu);
+}
+
+void ppmu_update(struct device *dev, int ch)
+{
+ struct exynos_ppmu_hw *ppmu;
+
+ list_for_each_entry(ppmu, &ppmu_list, node)
+ if (ppmu->dev == dev) {
+ exynos_ppmu_stop(ppmu);
+ ppmu_load[ppmu->id] = exynos_ppmu_update(ppmu, ch);
+ exynos_ppmu_reset(ppmu);
+ }
+}
+
+void ppmu_reset(struct device *dev)
+{
+ struct exynos_ppmu_hw *ppmu;
+ int i;
+
+ list_for_each_entry(ppmu, &ppmu_list, node) {
+ if (ppmu->dev == dev) {
+ exynos_ppmu_stop(ppmu);
+ for (i = 0; i < PPMU_NR_COUNTERS; i++)
+ if (ppmu->event[i] != 0)
+ exynos_ppmu_setevent(ppmu, i);
+ exynos_ppmu_reset(ppmu);
+ }
+ }
+}
+
+void ppmu_init(struct exynos_ppmu_hw *ppmu, struct device *dev)
+{
+ int i;
+
+ ppmu->hw_base = ioremap(ppmu->base_addr, SZ_8K);
+ if (ppmu->hw_base == NULL) {
+ printk(KERN_ERR "failed ioremap for ppmu\n");
+ return;
+ }
+
+ ppmu->dev = dev;
+ list_add(&ppmu->node, &ppmu_list);
+
+ for (i = 0; i < PPMU_NR_COUNTERS; i++)
+ if (ppmu->event[i] != 0)
+ exynos_ppmu_setevent(ppmu, i);
+}
+
+struct exynos_ppmu_hw exynos_ppmu[] = {
+ [PPMU_CPU] = {
+ .id = PPMU_CPU,
+ .base_addr = EXYNOS5_PA_PPMU_CPU,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DDR_C] = {
+ .id = PPMU_DDR_C,
+ .base_addr = EXYNOS5_PA_PPMU_DDR_C,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DDR_R1] = {
+ .id = PPMU_DDR_R1,
+ .base_addr = EXYNOS5_PA_PPMU_DDR_R1,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_DDR_L] = {
+ .id = PPMU_DDR_L,
+ .base_addr = EXYNOS5_PA_PPMU_DDR_L,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+ [PPMU_RIGHT0_BUS] = {
+ .id = PPMU_RIGHT0_BUS,
+ .base_addr = EXYNOS5_PA_PPMU_RIGHT0_BUS,
+ .event[3] = RDWR_DATA_COUNT,
+ .weight = DEFAULT_WEIGHT,
+ },
+};
#define S5P_VA_GIC_CPU S3C_ADDR(0x02810000)
#define S5P_VA_GIC_DIST S3C_ADDR(0x02820000)
+#define S5P_VA_DREXII S3C_ADDR(0x02930000)
+
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0 VA_VIC(0)
#define VA_VIC1 VA_VIC(1)