diff options
Diffstat (limited to 'arch/arm/mach-exynos/busfreq_opp_4x12.c')
-rw-r--r-- | arch/arm/mach-exynos/busfreq_opp_4x12.c | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/arch/arm/mach-exynos/busfreq_opp_4x12.c b/arch/arm/mach-exynos/busfreq_opp_4x12.c new file mode 100644 index 0000000..54837b4 --- /dev/null +++ b/arch/arm/mach-exynos/busfreq_opp_4x12.c @@ -0,0 +1,1076 @@ +/* linux/arch/arm/mach-exynos/busfreq_opp_4x12.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4 - BUS clock frequency scaling support with OPP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <linux/platform_device.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/cpu.h> +#include <linux/ktime.h> +#include <linux/tick.h> +#include <linux/kernel_stat.h> +#include <linux/reboot.h> +#include <linux/slab.h> +#include <linux/opp.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/pm_qos_params.h> + +#include <mach/busfreq_exynos4.h> + +#include <asm/mach-types.h> + +#include <mach/ppmu.h> +#include <mach/map.h> +#include <mach/regs-clock.h> +#include <mach/gpio.h> +#include <mach/regs-mem.h> +#include <mach/dev.h> +#include <mach/asv.h> +#include <mach/smc.h> + +#include <plat/map-s5p.h> +#include <plat/gpio-cfg.h> +#include <plat/cpu.h> + +#define UP_THRESHOLD 30 +#define IDLE_THRESHOLD 4 +#define UP_CPU_THRESHOLD 11 +#define MAX_CPU_THRESHOLD 20 +#define CPU_SLOPE_SIZE 7 +#define PPMU_THRESHOLD 5 + +unsigned int up_threshold = UP_THRESHOLD; +unsigned int ppmu_threshold = PPMU_THRESHOLD; +unsigned int idle_threshold = IDLE_THRESHOLD; +unsigned int up_cpu_threshold = UP_CPU_THRESHOLD; +unsigned int max_cpu_threshold = MAX_CPU_THRESHOLD; +unsigned int cpu_slope_size = CPU_SLOPE_SIZE; +unsigned int dmc_max_threshold; +unsigned int load_history_size = LOAD_HISTORY_SIZE; + +static bool mif_locking; +static bool int_locking; + +/* To save/restore DMC_PAUSE_CTRL register */ +static unsigned int dmc_pause_ctrl; + +enum busfreq_level_idx { + LV_0, + LV_1, + LV_2, + LV_3, + LV_4, + LV_5, + LV_6, + LV_END +}; + +static struct busfreq_table *exynos4_busfreq_table; + +static struct busfreq_table exynos4_busfreq_table_orig[] = { + {LV_0, 400266, 1100000, 0, 0, 0}, /* MIF : 400MHz INT : 200MHz */ + {LV_1, 400200, 1100000, 0, 0, 0}, /* MIF : 400MHz INT : 200MHz */ + {LV_2, 267200, 1000000, 0, 0, 0}, /* MIF : 267MHz INT : 200MHz */ + {LV_3, 267160, 1000000, 0, 0, 0}, /* MIF : 267MHz INT : 160MHz */ + {LV_4, 160160, 950000, 0, 0, 0}, /* MIF : 160MHz INT : 160MHz */ + {LV_5, 133133, 950000, 0, 0, 0}, /* MIF : 133MHz INT : 133MHz */ + {LV_6, 100100, 950000, 0, 0, 0}, /* MIF : 100MHz INT : 100MHz */ +}; + +static struct busfreq_table exynos4_busfreq_table_rev2[] = { + {LV_0, 440293, 1100000, 0, 0, 0}, /* MIF : 440MHz INT : 220MHz */ + {LV_1, 440220, 1100000, 0, 0, 0}, /* MIF : 440MHz INT : 220MHz */ + {LV_2, 293220, 1000000, 0, 0, 0}, /* MIF : 293MHz INT : 220MHz */ + {LV_3, 293176, 1000000, 0, 0, 0}, /* MIF : 293MHz INT : 176MHz */ + {LV_4, 176176, 950000, 0, 0, 0}, /* MIF : 176MHz INT : 176MHz */ + {LV_5, 147147, 950000, 0, 0, 0}, /* MIF : 147MHz INT : 147MHz */ + {LV_6, 110110, 950000, 0, 0, 0}, /* MIF : 110MHz INT : 110MHz */ +}; + +enum busfreq_qos_target { + BUS_QOS_0, + BUS_QOS_1, + BUS_QOS_MAX, +}; + +static enum busfreq_qos_target busfreq_qos = BUS_QOS_0; +static unsigned int (*exynos4_qos_value)[LV_END][4]; + +#if defined(CONFIG_BUSFREQ_QOS_1280X800) /* P4NOTE */ +static unsigned int exynos4_qos_value_orig[BUS_QOS_MAX][LV_END][4] = { + { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x06, 0x03, 0x06, 0x0e}, + {0x06, 0x03, 0x06, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x02, 0x0B, 0x00, 0x00}, + }, + { + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x03, 0x06, 0x0e}, + {0x04, 0x03, 0x04, 0x0e}, + {0x02, 0x0b, 0x00, 0x00}, + }, +}; + +static unsigned int exynos4_qos_value_rev2[BUS_QOS_MAX][LV_END][4] = { + { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x06, 0x03, 0x06, 0x0e}, + {0x06, 0x03, 0x06, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x02, 0x0B, 0x00, 0x00}, + }, + { + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x03, 0x06, 0x0e}, + {0x04, 0x03, 0x04, 0x0e}, + {0x02, 0x0b, 0x00, 0x00}, + }, +}; +#else +static unsigned int exynos4_qos_value_orig[BUS_QOS_MAX][LV_END][4] = { + { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x06, 0x03, 0x06, 0x0e}, + {0x04, 0x03, 0x04, 0x0e}, + {0x03, 0x0B, 0x00, 0x00}, + }, + { + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x03, 0x06, 0x0e}, + {0x04, 0x03, 0x04, 0x0e}, + {0x03, 0x0b, 0x00, 0x00}, + }, +}; + +static unsigned int exynos4_qos_value_rev2[BUS_QOS_MAX][LV_END][4] = { + { + {0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00}, + {0x06, 0x03, 0x06, 0x0e}, + {0x06, 0x03, 0x06, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x03, 0x03, 0x03, 0x0e}, + {0x02, 0x03, 0x02, 0x0e}, + }, + { + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x0b, 0x06, 0x0f}, + {0x06, 0x03, 0x06, 0x0e}, + {0x04, 0x03, 0x04, 0x0e}, + {0x03, 0x0b, 0x00, 0x00}, + }, +}; +#endif + +#define ASV_GROUP 12 +#define PRIME_ASV_GROUP 13 +static unsigned int asv_group_index; + +static unsigned int (*exynos4_mif_volt)[LV_END]; +static unsigned int (*exynos4_int_volt)[LV_END]; + +static unsigned int exynos4212_mif_volt[ASV_GROUP][LV_END] = { + /* 400 400 267 267 160 133 100 */ + {1012500, 1012500, 962500, 962500, 912500, 912500, 912500}, /* RESERVED */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV1 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV2 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV3 */ + {1050000, 1050000, 1000000, 1000000, 900000, 900000, 900000}, /* ASV4 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV5 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV6 */ + {950000, 950000, 900000, 900000, 900000, 900000, 900000}, /* ASV7 */ + {950000, 950000, 900000, 900000, 900000, 900000, 850000}, /* ASV8 */ + {950000, 950000, 900000, 900000, 900000, 900000, 850000}, /* ASV9 */ + {950000, 950000, 900000, 900000, 900000, 850000, 850000}, /* ASV10 */ + {937500, 937500, 887500, 887500, 887500, 850000, 850000}, /* RESERVED */ +}; + +static unsigned int exynos4212_int_volt[ASV_GROUP][LV_END] = { + /* 266 200 200 160 160 133 100 */ + {1300000, 1250000, 1250000, 950000, 950000, 912500, 887500}, /* RESERVED */ + {1062500, 1012500, 1012500, 937500, 937500, 900000, 875000}, /* ASV1 */ + {1050000, 1000000, 1000000, 925000, 925000, 887500, 875000}, /* ASV2 */ + {1050000, 1000000, 1000000, 912500, 912500, 887500, 875000}, /* ASV3 */ + {1062500, 1012500, 1012500, 925000, 925000, 900000, 875000}, /* ASV4 */ + {1050000, 1000000, 1000000, 925000, 925000, 887500, 875000}, /* ASV5 */ + {1050000, 1000000, 1000000, 912500, 912500, 887500, 875000}, /* ASV6 */ + {1037500, 987500, 987500, 912500, 912500, 875000, 875000}, /* ASV7 */ + {1037500, 987500, 987500, 900000, 900000, 875000, 875000}, /* ASV8 */ + {1037500, 987500, 987500, 900000, 900000, 875000, 875000}, /* ASV9 */ + {1037500, 987500, 987500, 900000, 900000, 862500, 850000}, /* ASV10 */ + {1035000, 975000, 975000, 887500, 887500, 850000, 850000}, /* RESERVED */ +}; + +static unsigned int exynos4412_mif_volt[ASV_GROUP][LV_END] = { + /* 400 400 267 267 160 133 100 */ + {1100000, 1100000, 1000000, 1000000, 950000, 950000, 950000}, /* RESERVED */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* RESERVED */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV2 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV3 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV4 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV5 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV6 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV7 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV8 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 850000}, /* ASV9 */ + {1000000, 1000000, 900000, 900000, 900000, 900000, 850000}, /* ASV10 */ + {1000000, 1000000, 900000, 900000, 900000, 900000, 850000}, /* RESERVED */ +}; + +static unsigned int exynos4412_int_volt[ASV_GROUP][LV_END] = { + /* GDR : 266 200 200 160 160 133 100 */ + {1112500, 1062500, 1062500, 975000, 975000, 937500, 900000}, /* RESERVED */ + {1100000, 1050000, 1050000, 962500, 962500, 925000, 887500}, /* RESERVED */ + {1075000, 1025000, 1025000, 937500, 937500, 912500, 875000}, /* ASV2 */ + {1062500, 1012500, 1012500, 937500, 937500, 900000, 862500}, /* ASV3 */ + {1062500, 1012500, 1012500, 925000, 925000, 900000, 862500}, /* ASV4 */ + {1050000, 1000000, 1000000, 925000, 925000, 887500, 850000}, /* ASV5 */ + {1050000, 1000000, 1000000, 912500, 912500, 875000, 850000}, /* ASV6 */ + {1037500, 987500, 987500, 912500, 912500, 862500, 850000}, /* ASV7 */ + {1037500, 987500, 987500, 900000, 900000, 862500, 850000}, /* ASV8 */ + {1037500, 987500, 987500, 900000, 900000, 862500, 850000}, /* ASV9 */ + {1037500, 987500, 987500, 900000, 900000, 862500, 850000}, /* ASV10 */ + {1025000, 975000, 975000, 887500, 887500, 850000, 850000}, /* RESERVED */ +}; + + +/* 20120822 DVFS table for pega prime */ +/* Because buck1 of pmic can be set to 50mV step size, 50mV table is used */ +static unsigned int exynos4412_mif_volt_rev2[PRIME_ASV_GROUP][LV_END] = { + /* 440 440 293 293 176 147 110 */ + {1100000, 1100000, 1000000, 1000000, 950000, 950000, 950000}, /* RESERVED */ + {1100000, 1100000, 1000000, 1000000, 950000, 950000, 950000}, /* ASV1 */ + {1100000, 1100000, 1000000, 1000000, 950000, 950000, 900000}, /* ASV2 */ + {1100000, 1100000, 1000000, 1000000, 950000, 900000, 900000}, /* ASV3 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV4 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV5 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV6 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 850000}, /* ASV7 */ + {1050000, 1050000, 950000, 950000, 900000, 850000, 850000}, /* ASV8 */ + {1000000, 1000000, 900000, 900000, 850000, 850000, 850000}, /* ASV9 */ + {1000000, 1000000, 900000, 900000, 850000, 850000, 850000}, /* ASV10 */ + {1000000, 1000000, 900000, 900000, 850000, 850000, 850000}, /* ASV11 */ + { 950000, 950000, 850000, 850000, 850000, 850000, 850000}, /* ASV12 */ +}; + +/* 20120822 DVFS table for pega prime */ +static unsigned int exynos4412_int_volt_rev2[PRIME_ASV_GROUP][LV_END] = { + /* GDR : 293 220 220 176 176 147 110 */ + {1087500, 1062500, 1062500, 1000000, 1000000, 962500, 950000}, /* RESERVED */ + {1075000, 1050000, 1050000, 987500, 987500, 950000, 937500}, /* ASV1 */ + {1062500, 1037500, 1037500, 975000, 975000, 937500, 912500}, /* ASV2 */ + {1050000, 1037500, 1037500, 975000, 975000, 937500, 900000}, /* ASV3 */ + {1037500, 1025000, 1025000, 962500, 962500, 925000, 887500}, /* ASV4 */ + {1025000, 1012500, 1012500, 950000, 950000, 912500, 887500}, /* ASV5 */ + {1012500, 1000000, 1000000, 937500, 937500, 900000, 887500}, /* ASV6 */ + {1000000, 987500, 987500, 925000, 925000, 887500, 875000}, /* ASV7 */ + {1037500, 975000, 975000, 912500, 912500, 875000, 875000}, /* ASV8 */ + {1025000, 962500, 962500, 900000, 900000, 875000, 875000}, /* ASV9 */ + {1012500, 937500, 937500, 875000, 875000, 850000, 850000}, /* ASV10 */ + {1000000, 925000, 925000, 862500, 862500, 850000, 850000}, /* ASV11 */ + {1000000, 912500, 912500, 850000, 850000, 850000, 850000}, /* ASV12 */ +}; + +static unsigned int exynos4412_1ghz_mif_volt[ASV_GROUP][LV_END] = { + /* 400 400 267 267 160 133 100 */ + {1100000, 1100000, 1000000, 1000000, 950000, 950000, 950000}, /* RESERVED */ + {1050000, 1050000, 1000000, 1000000, 950000, 950000, 950000}, /* RESERVED */ + {1050000, 1050000, 1000000, 1000000, 950000, 950000, 950000}, /* ASV2 */ + {1050000, 1050000, 1000000, 1000000, 950000, 950000, 950000}, /* ASV3 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV4 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV5 */ + {1050000, 1050000, 950000, 950000, 900000, 900000, 900000}, /* ASV6 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV7 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV8 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV9 */ + {1000000, 1000000, 950000, 950000, 900000, 900000, 900000}, /* ASV10 */ + {1025000, 1025000, 925000, 925000, 925000, 925000, 875000}, /* RESERVED */ +}; + +static unsigned int exynos4412_1ghz_int_volt[ASV_GROUP][LV_END] = { +/* GDR : 266 200 200 160 160 133 100 */ + {0, 1087500, 1087500, 1000000, 1000000, 975000, 950000}, /* RESERVED */ + {0, 1050000, 1050000, 1000000, 1000000, 975000, 950000}, /* RESERVED */ + {0, 1050000, 1050000, 1000000, 1000000, 975000, 950000}, /* ASV2 */ + {0, 1050000, 1050000, 1000000, 1000000, 975000, 950000}, /* ASV3 */ + {0, 1037500, 1037500, 950000, 950000, 925000, 900000}, /* ASV4 */ + {0, 1037500, 1037500, 950000, 950000, 925000, 900000}, /* ASV5 */ + {0, 1037500, 1037500, 950000, 950000, 925000, 900000}, /* ASV6 */ + {0, 1012500, 1012500, 937500, 937500, 887500, 875000}, /* ASV7 */ + {0, 1012500, 1012500, 937500, 937500, 887500, 875000}, /* ASV8 */ + {0, 1012500, 1012500, 937500, 937500, 887500, 875000}, /* ASV9 */ + {0, 1012500, 1012500, 937500, 937500, 887500, 875000}, /* ASV10 */ + {0, 1000000, 1000000, 912500, 912500, 875000, 875000}, /* RESERVED */ +}; + +static unsigned int exynos4x12_timingrow[LV_END] = { + 0x34498691, 0x34498691, 0x24488490, 0x24488490, 0x154882D0, 0x154882D0, 0x0D488210 +}; + +static unsigned int clkdiv_dmc0[LV_END][6] = { + /* + * Clock divider value for following + * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD + * DIVDMCP} + */ + + /* DMC L0: 400MHz */ + {3, 1, 1, 1, 1, 1}, + + /* DMC L1: 400MHz */ + {3, 1, 1, 1, 1, 1}, + + /* DMC L2: 266.7MHz */ + {4, 1, 1, 2, 1, 1}, + + /* DMC L3: 266.7MHz */ + {4, 1, 1, 2, 1, 1}, + + /* DMC L4: 160MHz */ + {5, 1, 1, 4, 1, 1}, + + /* DMC L5: 133MHz */ + {5, 1, 1, 5, 1, 1}, + + /* DMC L6: 100MHz */ + {7, 1, 1, 7, 1, 1}, +}; + +static unsigned int clkdiv_dmc1[LV_END][6] = { + /* + * Clock divider value for following + * { G2DACP, DIVC2C, DIVC2C_ACLK } + */ + + /* DMC L0: 400MHz */ + {3, 1, 1}, + + /* DMC L1: 400MHz */ + {3, 1, 1}, + + /* DMC L2: 266.7MHz */ + {4, 2, 1}, + + /* DMC L3: 266.7MHz */ + {4, 2, 1}, + + /* DMC L4: 160MHz */ + {5, 4, 1}, + + /* DMC L5: 133MHz */ + {5, 5, 1}, + + /* DMC L6: 100MHz */ + {7, 7, 1}, +}; + +static unsigned int clkdiv_top[LV_END][5] = { + /* + * Clock divider value for following + * { DIVACLK266_GPS, DIVACLK100, DIVACLK160, + DIVACLK133, DIVONENAND } + */ + + /* ACLK_GDL/R L0: 266MHz */ + {2, 7, 4, 5, 1}, + + /* ACLK_GDL/R L1: 200MHz */ + {2, 7, 4, 5, 1}, + + /* ACLK_GDL/R L2: 200MHz */ + {2, 7, 4, 5, 1}, + + /* ACLK_GDL/R L3: 160MHz */ + {4, 7, 5, 7, 1}, + + /* ACLK_GDL/R L4: 160MHz */ + {4, 7, 5, 7, 1}, + + /* ACLK_GDL/R L5: 133MHz */ + {5, 7, 5, 7, 1}, + + /* ACLK_GDL/R L6: 100MHz */ + {7, 7, 7, 7, 1}, +}; + +static unsigned int clkdiv_l_bus[LV_END][2] = { + /* + * Clock divider value for following + * { DIVGDL, DIVGPL } + */ + + /* ACLK_GDL L0: 200MHz */ + {3, 1}, + + /* ACLK_GDL L1: 200MHz */ + {3, 1}, + + /* ACLK_GDL L2: 200MHz */ + {3, 1}, + + /* ACLK_GDL L3: 160MHz */ + {4, 1}, + + /* ACLK_GDL L4: 160MHz */ + {4, 1}, + + /* ACLK_GDL L5: 133MHz */ + {5, 1}, + + /* ACLK_GDL L6: 100MHz */ + {7, 1}, +}; + +static unsigned int clkdiv_r_bus[LV_END][2] = { + /* + * Clock divider value for following + * { DIVGDR, DIVGPR } + */ + + /* ACLK_GDR L0: 266MHz */ + {2, 1}, + + /* ACLK_GDR L1: 200MHz */ + {3, 1}, + + /* ACLK_GDR L2: 200MHz */ + {3, 1}, + + /* ACLK_GDR L3: 160MHz */ + {4, 1}, + + /* ACLK_GDR L4: 160MHz */ + {4, 1}, + + /* ACLK_GDR L5: 133MHz */ + {5, 1}, + + /* ACLK_GDR L6: 100MHz */ + {7, 1}, +}; + +static unsigned int clkdiv_sclkip[LV_END][3] = { + /* + * Clock divider value for following + * { DIVMFC, DIVJPEG, DIVFIMC0~3} + */ + + /* SCLK_MFC: 200MHz */ + {3, 3, 4}, + + /* SCLK_MFC: 200MHz */ + {3, 3, 4}, + + /* SCLK_MFC: 200MHz */ + {3, 3, 4}, + + /* SCLK_MFC: 160MHz */ + {4, 4, 5}, + + /* SCLK_MFC: 160MHz */ + {4, 4, 5}, + + /* SCLK_MFC: 133MHz */ + {5, 5, 5}, + + /* SCLK_MFC: 100MHz */ + {7, 7, 7}, +}; + +static void exynos4x12_set_bus_volt(void) +{ + unsigned int i; + + asv_group_index = exynos_result_of_asv; + + if (asv_group_index == 0xff) + asv_group_index = 0; + + if ((is_special_flag() >> MIF_LOCK_FLAG) & 0x1) + mif_locking = true; + + if ((is_special_flag() >> INT_LOCK_FLAG) & 0x1) + int_locking = true; + + printk(KERN_INFO "DVFS : VDD_INT Voltage table set with %d Group\n", asv_group_index); + + for (i = 0 ; i < LV_END ; i++) { + exynos4_busfreq_table[i].volt = + exynos4_mif_volt[asv_group_index][i]; + + if (mif_locking) + exynos4_busfreq_table[i].volt += 50000; + + if (int_locking) + exynos4_int_volt[asv_group_index][i] += 25000; + } + + return; +} + +void exynos4x12_target(int index) +{ + unsigned int tmp; + + /* Change Divider - DMC0 */ + tmp = exynos4_busfreq_table[index].clk_dmc0div; + + __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0); + } while (tmp & 0x111111); + + /* Change Divider - DMC1 */ + tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1); + + tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK | + EXYNOS4_CLKDIV_DMC1_C2C_MASK | + EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK); + + tmp |= ((clkdiv_dmc1[index][0] << EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) | + (clkdiv_dmc1[index][1] << EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) | + (clkdiv_dmc1[index][2] << EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1); + } while (tmp & 0x1011); + + /* Change Divider - TOP */ + tmp = __raw_readl(EXYNOS4_CLKDIV_TOP); + + tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK | + EXYNOS4_CLKDIV_TOP_ACLK100_MASK | + EXYNOS4_CLKDIV_TOP_ACLK160_MASK | + EXYNOS4_CLKDIV_TOP_ACLK133_MASK | + EXYNOS4_CLKDIV_TOP_ONENAND_MASK); + + tmp |= ((clkdiv_top[index][0] << EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) | + (clkdiv_top[index][1] << EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) | + (clkdiv_top[index][2] << EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) | + (clkdiv_top[index][3] << EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) | + (clkdiv_top[index][4] << EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_TOP); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP); + } while (tmp & 0x11111); + + /* Change Divider - LEFTBUS */ + tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS); + + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); + + tmp |= ((clkdiv_l_bus[index][0] << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | + (clkdiv_l_bus[index][1] << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS); + } while (tmp & 0x11); + + /* Change Divider - RIGHTBUS */ + tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS); + + tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK); + + tmp |= ((clkdiv_r_bus[index][0] << EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) | + (clkdiv_r_bus[index][1] << EXYNOS4_CLKDIV_BUS_GPLR_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS); + } while (tmp & 0x11); + + /* Change Divider - MFC */ + tmp = __raw_readl(EXYNOS4_CLKDIV_MFC); + + tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK); + + tmp |= ((clkdiv_sclkip[index][0] << EXYNOS4_CLKDIV_MFC_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_MFC); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC); + } while (tmp & 0x1); + + /* Change Divider - JPEG */ + tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1); + + tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK); + + tmp |= ((clkdiv_sclkip[index][1] << EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); + } while (tmp & 0x1); + + /* Change Divider - FIMC0~3 */ + tmp = __raw_readl(EXYNOS4_CLKDIV_CAM); + + tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK | + EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK); + + tmp |= ((clkdiv_sclkip[index][2] << EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) | + (clkdiv_sclkip[index][2] << EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) | + (clkdiv_sclkip[index][2] << EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) | + (clkdiv_sclkip[index][2] << EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT)); + + __raw_writel(tmp, EXYNOS4_CLKDIV_CAM); + + do { + tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1); + } while (tmp & 0x1111); + + /* if pega-prime, ABB value is not changed */ + if (samsung_rev() >= EXYNOS4412_REV_2_0) + return; + + /* ABB value is changed in below case */ + if (soc_is_exynos4412() && (exynos_result_of_asv > 3)) { + if (index == LV_6) { /* MIF:100 / INT:100 */ + exynos4x12_set_abb_member(ABB_INT, ABB_MODE_100V); + exynos4x12_set_abb_member(ABB_MIF, ABB_MODE_100V); + } else { + exynos4x12_set_abb_member(ABB_INT, ABB_MODE_130V); + exynos4x12_set_abb_member(ABB_MIF, ABB_MODE_130V); + } + } +} + +unsigned int exynos4x12_get_table_index(struct opp *opp) +{ + unsigned int index; + + for (index = LV_0; index < LV_END; index++) + if (opp_get_freq(opp) == exynos4_busfreq_table[index].mem_clk) + break; + + return index; +} + +void exynos4x12_prepare(unsigned int index) +{ + unsigned int timing0 = 0; + +#ifdef CONFIG_ARM_TRUSTZONE + exynos_smc_readsfr(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET, &timing0); + timing0 |= exynos4x12_timingrow[index]; + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET), + timing0, 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET), + exynos4x12_timingrow[index], 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC1_4212 + TIMINGROW_OFFSET), + timing0, 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC1_4212 + TIMINGROW_OFFSET), + exynos4x12_timingrow[index], 0); +#else + timing0 = __raw_readl(S5P_VA_DMC0 + TIMINGROW_OFFSET); + timing0 |= exynos4x12_timingrow[index]; + __raw_writel(timing0, S5P_VA_DMC0 + TIMINGROW_OFFSET); + __raw_writel(exynos4x12_timingrow[index], S5P_VA_DMC0 + TIMINGROW_OFFSET); + __raw_writel(timing0, S5P_VA_DMC1 + TIMINGROW_OFFSET); + __raw_writel(exynos4x12_timingrow[index], S5P_VA_DMC1 + TIMINGROW_OFFSET); +#endif +} + +void exynos4x12_post(unsigned int index) +{ + unsigned int timing0 = 0; + +#ifdef CONFIG_ARM_TRUSTZONE + exynos_smc_readsfr(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET, &timing0); + timing0 |= exynos4x12_timingrow[index]; + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET), + timing0, 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC0_4212 + TIMINGROW_OFFSET), + exynos4x12_timingrow[index], 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC1_4212 + TIMINGROW_OFFSET), + timing0, 0); + exynos_smc(SMC_CMD_REG, SMC_REG_ID_SFR_W(EXYNOS4_PA_DMC1_4212 + TIMINGROW_OFFSET), + exynos4x12_timingrow[index], 0); +#else + timing0 = __raw_readl(S5P_VA_DMC0 + TIMINGROW_OFFSET); + timing0 |= exynos4x12_timingrow[index]; + __raw_writel(timing0, S5P_VA_DMC0 + TIMINGROW_OFFSET); + __raw_writel(exynos4x12_timingrow[index], S5P_VA_DMC0 + TIMINGROW_OFFSET); + __raw_writel(timing0, S5P_VA_DMC1 + TIMINGROW_OFFSET); + __raw_writel(exynos4x12_timingrow[index], S5P_VA_DMC1 + TIMINGROW_OFFSET); +#endif +} + +void exynos4x12_set_qos(unsigned int index) +{ + __raw_writel(exynos4_qos_value[busfreq_qos][index][0], S5P_VA_GDL + 0x400); + __raw_writel(exynos4_qos_value[busfreq_qos][index][1], S5P_VA_GDL + 0x404); + __raw_writel(exynos4_qos_value[busfreq_qos][index][2], S5P_VA_GDR + 0x400); + __raw_writel(exynos4_qos_value[busfreq_qos][index][3], S5P_VA_GDR + 0x404); +} + +void exynos4x12_suspend(void) +{ + /* Nothing to do */ +} + +void exynos4x12_resume(void) +{ + __raw_writel(dmc_pause_ctrl, EXYNOS4_DMC_PAUSE_CTRL); +} + +/** + * exynos4x12_find_busfreq_by_volt - find busfreq by requested + * voltage. + * + * This function finds the busfreq to set for voltage above req_volt + * and return its value. + */ +int exynos4x12_find_busfreq_by_volt(unsigned int req_volt, unsigned int *freq) +{ + unsigned int volt_cmp; + int i; + + /* check if req_volt has value or not */ + if (!req_volt) { + pr_err("%s: req_volt has no value.\n", __func__); + return -EINVAL; + } + + /* find busfreq level in busfreq_table */ + for (i = LV_END - 1; i >= 0; i--) { + volt_cmp = min(exynos4_int_volt[asv_group_index][i], + exynos4_mif_volt[asv_group_index][i]); + + if (volt_cmp >= req_volt) { + *freq = exynos4_busfreq_table[i].mem_clk; + return 0; + } + } + pr_err("%s: %u volt can't support\n", __func__, req_volt); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(exynos4x12_find_busfreq_by_volt); + +unsigned int exynos4x12_get_int_volt(unsigned long index) +{ + return exynos4_int_volt[asv_group_index][index]; +} + +struct opp *exynos4x12_monitor(struct busfreq_data *data) +{ + struct opp *opp = data->curr_opp; + int i; + unsigned int cpu_load_average = 0; + unsigned int dmc0_load_average = 0; + unsigned int dmc1_load_average = 0; + unsigned int dmc_load_average; + unsigned long cpufreq = 0; + unsigned long lockfreq; + unsigned long dmcfreq; + unsigned long newfreq; + unsigned long currfreq = opp_get_freq(data->curr_opp) / 1000; + unsigned long maxfreq = opp_get_freq(data->max_opp) / 1000; + unsigned long cpu_load; + unsigned long dmc0_load; + unsigned long dmc1_load; + unsigned long dmc_load; + int cpu_load_slope; + + ppmu_update(data->dev, 3); + + /* Convert from base xxx to base maxfreq */ + cpu_load = ppmu_load[PPMU_CPU]; + dmc0_load = div64_u64(ppmu_load[PPMU_DMC0] * currfreq, maxfreq); + dmc1_load = div64_u64(ppmu_load[PPMU_DMC1] * currfreq, maxfreq); + + cpu_load_slope = cpu_load - + data->load_history[PPMU_CPU] + [data->index ? data->index - 1 : load_history_size - 1]; + + data->load_history[PPMU_CPU][data->index] = cpu_load; + data->load_history[PPMU_DMC0][data->index] = dmc0_load; + data->load_history[PPMU_DMC1][data->index++] = dmc1_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]; + dmc0_load_average += data->load_history[PPMU_DMC0][i]; + dmc1_load_average += data->load_history[PPMU_DMC1][i]; + } + + /* Calculate average Load */ + cpu_load_average /= load_history_size; + dmc0_load_average /= load_history_size; + dmc1_load_average /= load_history_size; + + if (dmc0_load >= dmc1_load) { + dmc_load = dmc0_load; + dmc_load_average = dmc0_load_average; + } else { + dmc_load = dmc1_load; + dmc_load_average = dmc1_load_average; + } + + if (cpu_load >= up_cpu_threshold) { + cpufreq = opp_get_freq(data->max_opp); + if (cpu_load < max_cpu_threshold) { + opp = data->curr_opp; + if (cpu_load_slope > cpu_slope_size) { + cpufreq--; + opp = opp_find_freq_floor(data->dev, &cpufreq); + } + cpufreq = opp_get_freq(opp); + } + } + + if (dmc_load >= dmc_max_threshold) { + dmcfreq = opp_get_freq(data->max_opp); + } else if (dmc_load < idle_threshold) { + if (dmc_load_average < idle_threshold) + opp = step_down(data, 1); + else + opp = data->curr_opp; + dmcfreq = opp_get_freq(opp); + } else { + if (dmc_load < dmc_load_average) { + dmc_load = dmc_load_average; + if (dmc_load >= dmc_max_threshold) + dmc_load = dmc_max_threshold; + } + dmcfreq = div64_u64(maxfreq * dmc_load * 1000, dmc_max_threshold); + } + + lockfreq = dev_max_freq(data->dev); + + newfreq = max3(lockfreq, dmcfreq, cpufreq); + + if (samsung_rev() < EXYNOS4412_REV_1_0) + newfreq = opp_get_freq(data->max_opp); + + pr_debug("curfreq %ld, newfreq %ld, dmc0_load %ld, dmc1_load %ld, cpu_load %ld\n", + currfreq, newfreq, dmc0_load, dmc1_load, cpu_load); + + opp = opp_find_freq_ceil(data->dev, &newfreq); + if (IS_ERR(opp)) + opp = data->max_opp; + + return opp; +} + +static int exynos4x12_bus_qos_notifiy(struct notifier_block *nb, + unsigned long l, void *v) +{ + struct busfreq_data *bus_data = container_of(nb, struct busfreq_data, + exynos_busqos_notifier); + + busfreq_qos = (int)l; + exynos4x12_set_qos(bus_data->get_table_index(bus_data->curr_opp)); + + return NOTIFY_OK; +} + +static inline void exynos4x12_bus_qos_notifier_init(struct notifier_block *n) +{ + pm_qos_add_notifier(PM_QOS_BUS_QOS, n); +} + +#define ARM_INT_CORRECTION 160160 + +static int exynos4x12_busfreq_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + struct busfreq_data *bus_data = container_of(nb, struct busfreq_data, + exynos_cpufreq_notifier); + + switch (val) { + case CPUFREQ_PRECHANGE: + if (freqs->new > 900000 && freqs->old < 1000000) + dev_lock(bus_data->dev, bus_data->dev, ARM_INT_CORRECTION); + break; + case CPUFREQ_POSTCHANGE: + if (freqs->old > 900000 && freqs->new < 1000000) + dev_unlock(bus_data->dev, bus_data->dev); + break; + } + return NOTIFY_DONE; +} + +int exynos4x12_init(struct device *dev, struct busfreq_data *data) +{ + unsigned int i; + unsigned int tmp; + unsigned long maxfreq = 400200; + unsigned long minfreq = 0; + unsigned long freq; + struct clk *sclk_dmc; + int ret; + + if (soc_is_exynos4412() && samsung_rev() >= EXYNOS4412_REV_2_0) { + exynos4_busfreq_table = exynos4_busfreq_table_rev2; + exynos4_qos_value = exynos4_qos_value_rev2; + } else { + exynos4_busfreq_table = exynos4_busfreq_table_orig; + exynos4_qos_value = exynos4_qos_value_orig; + } + + if (soc_is_exynos4212()) { + exynos4_mif_volt = exynos4212_mif_volt; + exynos4_int_volt = exynos4212_int_volt; + dmc_max_threshold = EXYNOS4212_DMC_MAX_THRESHOLD; + } else if (soc_is_exynos4412()) { +#ifdef CONFIG_EXYNOS4X12_1000MHZ_SUPPORT + exynos4_mif_volt = exynos4412_1ghz_mif_volt; + exynos4_int_volt = exynos4412_1ghz_int_volt; +#else + if (samsung_rev() >= EXYNOS4412_REV_2_0) { + exynos4_mif_volt = exynos4412_mif_volt_rev2; + exynos4_int_volt = exynos4412_int_volt_rev2; + dmc_max_threshold = PRIME_DMC_MAX_THRESHOLD; + } else if (exynos_armclk_max == 1000000) { + exynos4_mif_volt = exynos4412_1ghz_mif_volt; + exynos4_int_volt = exynos4412_1ghz_int_volt; + dmc_max_threshold = EXYNOS4412_DMC_MAX_THRESHOLD; + } else { + exynos4_mif_volt = exynos4412_mif_volt; + exynos4_int_volt = exynos4412_int_volt; + dmc_max_threshold = EXYNOS4412_DMC_MAX_THRESHOLD; + } +#endif + } else { + pr_err("Unsupported model.\n"); + return -EINVAL; + } + + /* Enable pause function for DREX2 DVFS */ + dmc_pause_ctrl = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL); + dmc_pause_ctrl |= DMC_PAUSE_ENABLE; + __raw_writel(dmc_pause_ctrl, EXYNOS4_DMC_PAUSE_CTRL); + + tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0); + + for (i = 0; i < LV_END; i++) { + tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK | + EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK | + EXYNOS4_CLKDIV_DMC0_DPHY_MASK | + EXYNOS4_CLKDIV_DMC0_DMC_MASK | + EXYNOS4_CLKDIV_DMC0_DMCD_MASK | + EXYNOS4_CLKDIV_DMC0_DMCP_MASK); + + tmp |= ((clkdiv_dmc0[i][0] << EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) | + (clkdiv_dmc0[i][1] << EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) | + (clkdiv_dmc0[i][2] << EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) | + (clkdiv_dmc0[i][3] << EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) | + (clkdiv_dmc0[i][4] << EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) | + (clkdiv_dmc0[i][5] << EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT)); + + exynos4_busfreq_table[i].clk_dmc0div = tmp; + } + + exynos4x12_set_bus_volt(); + + for (i = 0; i < LV_END; i++) { + ret = opp_add(dev, exynos4_busfreq_table[i].mem_clk, + exynos4_busfreq_table[i].volt); + if (ret) { + dev_err(dev, "Fail to add opp entries.\n"); + return ret; + } + } + + if (samsung_rev() >= EXYNOS4412_REV_2_0) { + opp_disable(dev, 440293); + maxfreq = 440220; + } else { + /* opp_disable(dev, 267200); */ + } + + data->table = exynos4_busfreq_table; + data->table_size = LV_END; + + /* Find max frequency */ + data->max_opp = opp_find_freq_floor(dev, &maxfreq); + data->min_opp = opp_find_freq_ceil(dev, &minfreq); + + sclk_dmc = clk_get(NULL, "sclk_dmc"); + + if (IS_ERR(sclk_dmc)) { + pr_err("Failed to get sclk_dmc.!\n"); + data->curr_opp = data->max_opp; + } else { + freq = clk_get_rate(sclk_dmc) / 1000; + clk_put(sclk_dmc); + data->curr_opp = opp_find_freq_ceil(dev, &freq); + } + + data->vdd_int = regulator_get(NULL, "vdd_int"); + if (IS_ERR(data->vdd_int)) { + pr_err("failed to get resource %s\n", "vdd_int"); + return -ENODEV; + } + + data->vdd_mif = regulator_get(NULL, "vdd_mif"); + if (IS_ERR(data->vdd_mif)) { + pr_err("failed to get resource %s\n", "vdd_mif"); + regulator_put(data->vdd_int); + return -ENODEV; + } + + data->exynos_cpufreq_notifier.notifier_call = + exynos4x12_busfreq_cpufreq_transition; + + if (cpufreq_register_notifier(&data->exynos_cpufreq_notifier, + CPUFREQ_TRANSITION_NOTIFIER)) + pr_err("Falied to register cpufreq notifier\n"); + + data->exynos_busqos_notifier.notifier_call = exynos4x12_bus_qos_notifiy; + exynos4x12_bus_qos_notifier_init(&data->exynos_busqos_notifier); + + return 0; +} |