/* linux/arch/arm/mach-s5pv310/cpufreq.c
 *
 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com/
 *
 * S5PV310 - CPU frequency scaling 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/clk.h>
#include <linux/io.h>
#include <linux/suspend.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
#include <linux/cpufreq.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

#define CPUMON 0

#ifdef CONFIG_S5PV310_BUSFREQ
#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>
#endif

#include <plat/pm.h>

#include <plat/s5pv310.h>

#include <mach/cpufreq.h>
#include <mach/dmc.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/pm-core.h>

int exp_UV_mV[6] = { 
	1275000, // 1200Mhz
	1175000, // 1000Mhz
	1075000, // 800Mhz
	1000000,  // 500Mhz
	975000,  // 200Mhz
	950000,  // 100Mhz
};

static struct clk *arm_clk;
static struct clk *moutcore;
static struct clk *mout_mpll;
static struct clk *mout_apll;
static struct clk *sclk_dmc;

#ifdef CONFIG_REGULATOR
static struct regulator *arm_regulator;
static struct regulator *int_regulator;
#endif

static struct cpufreq_freqs freqs;
static int s5pv310_dvs_locking;
static bool s5pv310_cpufreq_init_done;
static DEFINE_MUTEX(set_cpu_freq_change);
static DEFINE_MUTEX(set_cpu_freq_lock);

#undef HAVE_DAC

#ifdef HAVE_DAC
void __iomem *dac_base;
#endif

/* temperary define for additional symantics for relation */
#define DISABLE_FURTHER_CPUFREQ         0x10
#define ENABLE_FURTHER_CPUFREQ          0x20
#define MASK_FURTHER_CPUFREQ            0x30
#define MASK_ONLY_SET_CPUFREQ		0x40
#define SET_CPU_FREQ_SAMPLING_RATE      100000

#define printk(args...)
//#define SMOOTH_SCALING
#define ARIGHI_SMOOTH_SCALING

#define ARMCLOCK_1200MHZ		1200000
#define ARMCLOCK_1000MHZ		1000000
#define ARMCLOCK_500MHZ			 500000

#define CHANGE_S_MASK ((0xFFFF<<16)|(0xFF))

static int s5pv310_max_armclk;

enum s5pv310_memory_type{
	DDR2 = 0x4,
	LPDDR2 = 0x5,
	DDR3 = 0x6,
};

#ifdef CONFIG_CPU_S5PV310_EVT1
enum cpufreq_level_index{
	L0, L1, L2, L3, L4, L5, CPUFREQ_LEVEL_END,
};
#else
enum cpufreq_level_index{
	L0, L1, L2, L3, CPUFREQ_LEVEL_END,
};
#endif

//don't ask me why we have another table. this is just an easy solution
static unsigned int siyah_freq_table[] = {
	1200,1000,800,500,200,100
};

static unsigned int freq_trans_table[CPUFREQ_LEVEL_END][CPUFREQ_LEVEL_END] = {
	/* This indicates what to do when cpufreq is changed.
	 * i.e. s-value change in apll changing.
	 *      arm voltage up in freq changing btn 500MHz and 200MHz.
	 * The line & column of below array means new & old frequency.
	 * the conents of array means types to do when frequency is changed.
	 *  @type 1 ---> changing only s-value in apll is changed.
	 *  @type 2 ---> increasing frequency
	 *  @type 4 ---> decreasing frequency
	 *  @type 8 ---> changing frequecy btn 500MMhz & 200MHz,
	 *    and temporaily set voltage @ 800MHz
	 *  The value 5 means to be set both type1 and type4.
	 *
	 * (for example)
	 * from\to 1200/1000/800/500/200 (old_idex, new_index)
	 * 1200
	 * 1000
	 *  800
	 *  500
	 *  200
	 */
	{ 0, 4, 4, 4, 4, 4 },
	{ 2, 0, 4, 4, 4, 4 },
	{ 2, 2, 0, 4, 4, 4 },
	{ 2, 2, 2, 0, 4, 4 },
	{ 2, 2, 3, 2, 0, 12 },
	{ 2, 2, 2, 3, 10, 0 },
};

static struct cpufreq_frequency_table s5pv310_freq_table[] = {
	{L0, 1200*1000},
	{L1, 1000*1000},
	{L2, 800*1000},
	{L3, 500*1000},
	{L4, 200*1000},
	{L5, 100*1000},
	{0, CPUFREQ_TABLE_END},
};

#ifdef CONFIG_S5PV310_BUSFREQ
#undef SYSFS_DEBUG_BUSFREQ

#define MAX_LOAD	100
#define UP_THRESHOLD_DEFAULT	23

static unsigned int up_threshold;
static struct s5pv310_dmc_ppmu_hw dmc[2];
static struct s5pv310_cpu_ppmu_hw cpu;
static unsigned int bus_utilization[2];

static unsigned int busfreq_fix;
static unsigned int fix_busfreq_level;
static unsigned int pre_fix_busfreq_level;

static unsigned int calc_bus_utilization(struct s5pv310_dmc_ppmu_hw *ppmu);
static void busfreq_target(void);

static DEFINE_MUTEX(set_bus_freq_change);
static DEFINE_MUTEX(set_bus_freq_lock);

enum busfreq_level_idx {
	LV_0,
	LV_1,
	LV_2,
	LV_END
};

#ifdef SYSFS_DEBUG_BUSFREQ
static unsigned int time_in_state[LV_END];
unsigned long prejiffies;
unsigned long curjiffies;
#endif

static unsigned int p_idx;

struct busfreq_table {
	unsigned int idx;
	unsigned int mem_clk;
	unsigned int volt;
};
static struct busfreq_table s5pv310_busfreq_table[] = {
	{LV_0, 400000, 1100000},
	{LV_1, 267000, 1000000},
#ifdef CONFIG_BUSFREQ_L2_160M
	{LV_2, 160000, 1000000},
#else
	{LV_2, 133000, 1000000},
#endif
	{0, 0, 0},
};
#endif

/* This defines are for cpufreq lock */
#define CPUFREQ_MIN_LEVEL	(CPUFREQ_LEVEL_END - 1)

unsigned int g_cpufreq_lock_id;
unsigned int g_cpufreq_lock_val[DVFS_LOCK_ID_END];
unsigned int g_cpufreq_lock_level = CPUFREQ_MIN_LEVEL;

#define CPUFREQ_LIMIT_LEVEL	L0

unsigned int g_cpufreq_limit_id;
unsigned int g_cpufreq_limit_val[DVFS_LOCK_ID_END];
unsigned int g_cpufreq_limit_level = CPUFREQ_LIMIT_LEVEL;

#define BUSFREQ_MIN_LEVEL	(LV_END - 1)

unsigned int g_busfreq_lock_id;
unsigned int g_busfreq_lock_val[DVFS_LOCK_ID_END];
unsigned int g_busfreq_lock_level = BUSFREQ_MIN_LEVEL;



/* ASV table to work 1.2GHz in DVFS has 6 asv level. */
static unsigned int s5pv310_asv_cpu_volt_table[6][CPUFREQ_LEVEL_END] = {
	{ 1300000, 1200000, 1100000, 1000000,  975000,  975000 },	/* 0 */
	{ 1275000, 1175000, 1075000,  975000,  950000,  950000 },	/* 1 */
	{ 1250000, 1150000, 1050000,  975000,  950000,  950000 },	/* 2 */
	{ 1250000, 1150000, 1050000,  975000,  925000,  925000 },	/* 3 */
	{ 1200000, 1100000, 1000000,  975000,  925000,  925000 },	/* 4 */
	{ 1175000, 1075000,  975000,  950000,  925000,  925000 },	/* 5 */
};

static unsigned int asv_int_volt_table[6][LV_END] = {
	{ 1125000, 1025000, 1025000 },	/* 0 */
	{ 1100000, 1000000, 1000000 },	/* 1 */
	{ 1100000, 1000000, 1000000 },	/* 2 */
	{ 1075000, 975000, 975000 },	/* 3 */
	{ 1075000, 975000, 975000 },	/* 4 */
	{ 1050000, 950000, 950000 },	/* 5 */
};

static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = {
	/*
	 * Clock divider value for following
	 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
	 *		DIVATB, DIVPCLK_DBG, DIVAPLL }
	 */	
	/* ARM L0: 1200MHz */
	{ 0, 3, 7, 3, 4, 1, 7 },

	/* ARM L1: 1000MHz */
	{ 0, 3, 7, 3, 4, 1, 7 },

	/* ARM L2: 800MHz */
	{ 0, 3, 7, 3, 3, 1, 7 },

	/* ARM L3: 500MHz */
	{ 0, 3, 7, 3, 3, 1, 7 },

	/* ARM L4: 200MHz */
	{ 0, 1, 3, 1, 3, 1, 7 },

	/* ARM L5: 100MHz */
	{ 0, 1, 3, 1, 3, 1, 7 },
};

static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = {
	/* Clock divider value for following
	 * { DIVCOPY, DIVHPM }
	 */
	/* ARM L0: 1200MHz */
	{ 5, 0 },

	/* ARM L1: 1000MHz */
	{ 4, 0 },

	/* ARM L2: 800MHz */
	{ 3, 0 },

	/* ARM L3: 500MHz */
	{ 3, 0 },

	/* ARM L4: 200MHz */
	{ 3, 0 },

	/* ARM L5: 100MHz */
	{ 3, 0 },
};

#ifdef CONFIG_CPU_S5PV310_EVT1
#ifndef CONFIG_S5PV310_BUSFREQ
static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
	/*
	 * Clock divider value for following
	 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
	 *		DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
	 */

	/* DMC L0: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L1: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L2: 266.7MHz */
	{ 4, 1, 1, 2, 1, 1, 3, 1 },

	/* DMC L3: 133MHz */
	{ 5, 1, 1, 5, 1, 1, 3, 1 },
};


static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
	/*
	 * Clock divider value for following
	 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
	 */

	/* ACLK200 L0: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L1: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L2: 160MHz */
	{ 4, 7, 5, 6, 1 },

	/* ACLK200 L3: 133MHz */
	{ 5, 7, 7, 7, 1 },
};

static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
	/*
	 * Clock divider value for following
	 * { DIVGDL/R, DIVGPL/R }
	 */

	/* ACLK_GDL/R L0: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L1: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L2: 160MHz */
	{ 4, 1 },

	/* ACLK_GDL/R L3: 133MHz */
	{ 5, 1 },
};

static unsigned int clkdiv_ip_bus[CPUFREQ_LEVEL_END][3] = {
	/*
	 * Clock divider value for following
	 * { DIV_MFC, DIV_G2D, DIV_FIMC }
	 */

	/* L0: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L1: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L2: MFC/G2D 160MHz FIMC 133MHz */
	/* { 4, 4, 5 },*/
	{ 3, 4, 5 },

	/* L3: MFC/G2D 133MHz FIMC 100MHz */
	/* { 5, 5, 7 },*/
	{ 3, 5, 7 },
};

#else
static unsigned int clkdiv_dmc0[LV_END][8] = {
	/*
	 * Clock divider value for following
	 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
	 *		DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
	 */

	/* DMC L0: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L1: 266.7MHz */
	{ 4, 1, 1, 2, 1, 1, 3, 1 },

#ifdef CONFIG_BUSFREQ_L2_160M
	/* DMC L2: 160MHz */
	{ 5, 1, 1, 4, 1, 1, 3, 1 },
#else
	/* DMC L2: 133MHz */
	{ 5, 1, 1, 5, 1, 1, 3, 1 },
#endif
};


static unsigned int clkdiv_top[LV_END][5] = {
	/*
	 * Clock divider value for following
	 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
	 */

	/* ACLK200 L1: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L2: 160MHz */
	{ 4, 7, 5, 6, 1 },

	/* ACLK200 L3: 133MHz */
	{ 5, 7, 7, 7, 1 },
};

static unsigned int clkdiv_lr_bus[LV_END][2] = {
	/*
	 * Clock divider value for following
	 * { DIVGDL/R, DIVGPL/R }
	 */

	/* ACLK_GDL/R L1: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L2: 160MHz */
	{ 4, 1 },

	/* ACLK_GDL/R L3: 133MHz */
	{ 5, 1 },
};

static unsigned int clkdiv_ip_bus[LV_END][3] = {
	/*
	 * Clock divider value for following
	 * { DIV_MFC, DIV_G2D, DIV_FIMC }
	 */

	/* L0: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L1: MFC/G2D 160MHz FIMC 133MHz */
	/* { 4, 4, 5 },*/
	{ 3, 4, 5 },

	/* L2: MFC/G2D 133MHz FIMC 100MHz */
	/* { 5, 5, 7 },*/
	{ 3, 5, 7 },
};

#endif
#else
#ifndef CONFIG_S5PV310_BUSFREQ
static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
	/*
	 * Clock divider value for following
	 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
	 *		DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
	 */

	/* DMC L0: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L1: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L2: 266.7MHz */
	{ 7, 1, 1, 2, 1, 1, 3, 1 },

	/* DMC L3: 200MHz */
	{ 7, 1, 1, 3, 1, 1, 3, 1 },
};


static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
	/*
	 * Clock divider value for following
	 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
	 */

	/* ACLK200 L0: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L1: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L2: 160MHz */
	{ 4, 7, 5, 7, 1 },

	/* ACLK200 L3: 133.3MHz */
	{ 5, 7, 7, 7, 1 },
};

static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
	/*
	 * Clock divider value for following
	 * { DIVGDL/R, DIVGPL/R }
	 */

	/* ACLK_GDL/R L0: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L1: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L2: 160MHz */
	{ 4, 1 },

	/* ACLK_GDL/R L3: 133.3MHz */
	{ 5, 1 },
};

static unsigned int clkdiv_ip_bus[CPUFREQ_LEVEL_END][3] = {
	/*
	 * Clock divider value for following
	 * { DIV_MFC, DIV_G2D, DIV_FIMC }
	 */

	/* L0: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L1: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L2: MFC/G2D 160MHz FIMC 133MHz */
	/* { 4, 4, 5 },*/
	{ 3, 4, 5 },

	/* L3: MFC/G2D 133MHz FIMC 100MHz */
	/* { 5, 5, 7 },*/
	{ 3, 5, 7 },
};

#else
static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = {
	/*
	 * Clock divider value for following
	 * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD
	 *		DIVDMCP, DIVCOPY2, DIVCORE_TIMERS }
	 */

	/* DMC L0: 400MHz */
	{ 3, 2, 1, 1, 1, 1, 3, 1 },

	/* DMC L1: 266.7MHz */
	{ 7, 1, 1, 2, 1, 1, 3, 1 },

	/* DMC L2: 200MHz */
	{ 7, 1, 1, 3, 1, 1, 3, 1 },
};

static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = {
	/*
	 * Clock divider value for following
	 * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND }
	 */

	/* ACLK200 L0: 200MHz */
	{ 3, 7, 4, 5, 1 },

	/* ACLK200 L1: 160MHz */
	{ 4, 7, 5, 7, 1 },

	/* ACLK200 L2: 133.3MHz */
	{ 5, 7, 7, 7, 1 },
};

static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = {
	/*
	 * Clock divider value for following
	 * { DIVGDL/R, DIVGPL/R }
	 */

	/* ACLK_GDL/R L0: 200MHz */
	{ 3, 1 },

	/* ACLK_GDL/R L1: 160MHz */
	{ 4, 1 },

	/* ACLK_GDL/R L2: 133.3MHz */
	{ 5, 1 },
};

static unsigned int clkdiv_ip_bus[LV_END][3] = {
	/*
	 * Clock divider value for following
	 * { DIV_MFC, DIV_G2D, DIV_FIMC }
	 */

	/* L0: MFC 200MHz G2D 266MHz FIMC 160MHz */
	{ 3, 2, 4 },

	/* L1: MFC/G2D 160MHz FIMC 133MHz */
	/* { 4, 4, 5 },*/
	{ 3, 4, 5 },

	/* L2: MFC/G2D 133MHz FIMC 100MHz */
	/* { 5, 5, 7 }, */
	{ 3, 5, 7 },
};

#endif
#endif

struct cpufreq_voltage_table {
	unsigned int	index;		/* any */
	unsigned int	arm_volt;	/* uV */
	unsigned int	int_volt;
};

/* Using lookup table to support 1200MHz/1000MHz by reading chip id */
static struct cpufreq_voltage_table s5pv310_lookup_volt_table[] = {
	{
		.index		= L0,
		.arm_volt	= 1300000,
		.int_volt	= 1100000,
	}, {
		.index		= L1,
		.arm_volt	= 1200000,
		.int_volt	= 1100000,
	}, {
		.index		= L2,
		.arm_volt	= 1100000,
		.int_volt	= 1100000,
	}, {
		.index		= L3,
		.arm_volt	= 1000000,
		.int_volt	= 1000000,
	}, {
		.index		= L4,
		.arm_volt	= 975000,
		.int_volt	= 1000000,
	}, {
		.index		= L5,
		.arm_volt	= 950000,
		.int_volt	= 1000000,
	},
};

static unsigned int s5pv310_lookup_apll_pms_table[CPUFREQ_LEVEL_END] = {
	/* APLL FOUT L0: 1200MHz */
	((150<<16)|(3<<8)|(0x1)),

	/* APLL FOUT L1: 1000MHz */
	((250<<16)|(6<<8)|(0x1)),

	/* APLL FOUT L2: 800MHz */
	((200<<16)|(6<<8)|(0x1)),

	/* APLL FOUT L3: 500MHz */
	((250<<16)|(6<<8)|(0x2)),

	/* APLL FOUT L4: 200MHz */
	((200<<16)|(6<<8)|(0x3)),
	
	/* APLL FOUT L5: 100MHz */
	((200<<16)|(6<<8)|(0x3)),
};

#ifdef CONFIG_S5PV310_ASV
static struct cpufreq_voltage_table s5pv310_volt_table[CPUFREQ_LEVEL_END] = {
	{
		.index		= L0,
		.arm_volt	= 1275000,
		.int_volt	= 1100000,
	}, {
		.index		= L1,
		.arm_volt	= 1175000,
		.int_volt	= 1100000,
	}, {
		.index		= L2,
		.arm_volt	= 1075000,
		.int_volt	= 1100000,
	}, {
		.index		= L3,
		.arm_volt	= 1000000,
		.int_volt	= 1000000,
	}, {
		.index		= L4,
		.arm_volt	= 975000,
		.int_volt	= 1000000,
	}, {
		.index		= L5,
		.arm_volt	= 950000,
		.int_volt	= 1000000,
	},
};
#endif
static unsigned int s5pv310_apll_pms_table[CPUFREQ_LEVEL_END] = {
	/* APLL FOUT L0: 1200MHz */
	((150<<16)|(3<<8)|(0x1)),

	/* APLL FOUT L1 1000MHz */
	((250<<16)|(6<<8)|(0x1)),

	/* APLL FOUT L2: 800MHz */
	((200<<16)|(6<<8)|(0x1)),

	/* APLL FOUT L3: 500MHz */
	((250<<16)|(6<<8)|(0x2)),

	/* APLL FOUT L4: 200MHz */
	((200<<16)|(6<<8)|(0x3)),

	/* APLL FOUT L5: 100MHz */
	((200<<16)|(6<<8)|(0x4)),
};

int s5pv310_verify_policy(struct cpufreq_policy *policy)
{
	return cpufreq_frequency_table_verify(policy, s5pv310_freq_table);
}

unsigned int s5pv310_getspeed(unsigned int cpu)
{
	unsigned long rate;

	rate = clk_get_rate(arm_clk) / 1000;

	return rate;
}

static unsigned int s5pv310_getspeed_dmc(unsigned int dmc)
{
	unsigned long rate;

	rate = clk_get_rate(sclk_dmc) / 1000;

	return rate;
}

void s5pv310_set_busfreq(unsigned int div_index)
{
	unsigned int tmp, val;

	/* Change Divider - DMC0 */
	tmp = __raw_readl(S5P_CLKDIV_DMC0);

	tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK |
		S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK |
		S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK |
		S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK);

	tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) |
		(clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
		(clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) |
		(clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) |
		(clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) |
		(clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) |
		(clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) |
		(clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT));

	__raw_writel(tmp, S5P_CLKDIV_DMC0);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
	} while (tmp & 0x11111111);


	/* Change Divider - TOP */
	tmp = __raw_readl(S5P_CLKDIV_TOP);

	tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK |
		S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK |
		S5P_CLKDIV_TOP_ONENAND_MASK);

	tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) |
		(clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) |
		(clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) |
		(clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) |
		(clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT));

	__raw_writel(tmp, S5P_CLKDIV_TOP);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
	} while (tmp & 0x11111);

	/* Change Divider - LEFTBUS */
	tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);

	tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);

	tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
		(clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));

	__raw_writel(tmp, S5P_CLKDIV_LEFTBUS);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
	} while (tmp & 0x11);

	/* Change Divider - RIGHTBUS */
	tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);

	tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);

	tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) |
		(clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT));

	__raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
	} while (tmp & 0x11);

	/* Change Divider - SCLK_MFC */
	tmp = __raw_readl(S5P_CLKDIV_MFC);

	tmp &= ~S5P_CLKDIV_MFC_MASK;

	tmp |= (clkdiv_ip_bus[div_index][0] << S5P_CLKDIV_MFC_SHIFT);

	__raw_writel(tmp, S5P_CLKDIV_MFC);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_MFC);
	} while (tmp & 0x1);

	/* Change Divider - SCLK_G2D */
	tmp = __raw_readl(S5P_CLKDIV_IMAGE);

	tmp &= ~S5P_CLKDIV_IMAGE_MASK;

	tmp |= (clkdiv_ip_bus[div_index][1] << S5P_CLKDIV_IMAGE_SHIFT);

	__raw_writel(tmp, S5P_CLKDIV_IMAGE);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_IMAGE);
	} while (tmp & 0x1);

	/* Change Divider - SCLK_FIMC */
	tmp = __raw_readl(S5P_CLKDIV_CAM);

	tmp &= ~S5P_CLKDIV_CAM_MASK;

	val = clkdiv_ip_bus[div_index][2];
	tmp |= ((val << 0) | (val << 4) | (val << 8) | (val << 12));

	__raw_writel(tmp, S5P_CLKDIV_CAM);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STAT_CAM);
	} while (tmp & 0x1111);
}

void s5pv310_set_clkdiv(unsigned int div_index)
{
	unsigned int tmp;

	/* Change Divider - CPU0 */
	tmp = __raw_readl(S5P_CLKDIV_CPU);

	tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK |
		S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK |
		S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK |
		S5P_CLKDIV_CPU0_APLL_MASK);

	tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) |
		(clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) |
		(clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) |
		(clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) |
		(clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) |
		(clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) |
		(clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT));

	__raw_writel(tmp, S5P_CLKDIV_CPU);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STATCPU);
	} while (tmp & 0x1111111);

	/* Change Divider - CPU1 */
	tmp = __raw_readl(S5P_CLKDIV_CPU1);

	tmp &= ~((0x7 << 4) | (0x7));

	tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
		(clkdiv_cpu1[div_index][1] << 0));

	__raw_writel(tmp, S5P_CLKDIV_CPU1);

	do {
		tmp = __raw_readl(S5P_CLKDIV_STATCPU1);
	} while (tmp & 0x11);

#ifndef CONFIG_S5PV310_BUSFREQ
	s5pv310_set_busfreq(div_index);
#endif
}

void s5pv310_set_apll(unsigned int index)
{
	unsigned int tmp;
	unsigned int save_val;

	/* 1. MUX_CORE_SEL = MPLL,
	 * Reduce the CLKDIVCPU value for using MPLL */
	save_val = __raw_readl(S5P_CLKDIV_CPU);
	tmp = save_val;
	tmp &= ~S5P_CLKDIV_CPU0_CORE_MASK;
	tmp |= (((save_val & 0xf) + 1) << S5P_CLKDIV_CPU0_CORE_SHIFT);
	__raw_writel(tmp, S5P_CLKDIV_CPU);
	do {
		tmp = __raw_readl(S5P_CLKDIV_STATCPU);
	} while (tmp & 0x1);

	/* ARMCLK uses MPLL for lock time */
	clk_set_parent(moutcore, mout_mpll);

	do {
		tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
			>> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
		tmp &= 0x7;
	} while (tmp != 0x2);

	/* 2. Set APLL Lock time */
	__raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);

	/* 3. Change PLL PMS values */
	tmp = __raw_readl(S5P_APLL_CON0);
	tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
	tmp |= s5pv310_apll_pms_table[index];
	__raw_writel(tmp, S5P_APLL_CON0);

	/* 4. wait_lock_time */
	do {
		tmp = __raw_readl(S5P_APLL_CON0);
	} while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));

	/* 5. MUX_CORE_SEL = APLL */
	clk_set_parent(moutcore, mout_apll);

	do {
		tmp = __raw_readl(S5P_CLKMUX_STATCPU);
		tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
	} while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));

	/* Restore the CLKDIVCPU value for APLL */
	__raw_writel(save_val, S5P_CLKDIV_CPU);
	do {
		tmp = __raw_readl(S5P_CLKDIV_STATCPU);
	} while (tmp & 0x1);

}

void s5pv310_set_frequency(unsigned int old_index, unsigned int new_index)
{
	unsigned int tmp;
	unsigned int is_curfreq_table = 0;
	unsigned int change_s_value = 0;

	if (freqs.old == s5pv310_freq_table[old_index].frequency)
		is_curfreq_table = 1;

	if (freqs.old < freqs.new) {
#ifdef CONFIG_CPU_S5PV310_EVT1
		/* 500->1000 & 200->800 change require to only change s value */
		if (is_curfreq_table &&
			(s5pv310_apll_pms_table[old_index]&CHANGE_S_MASK == s5pv310_apll_pms_table[new_index]&CHANGE_S_MASK)) {
			/* 1. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);

			/* 2. Change just s value in apll m,p,s value */
			tmp = __raw_readl(S5P_APLL_CON0);
			tmp &= ~(0x7 << 0);
			tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
			__raw_writel(tmp, S5P_APLL_CON0);

		} else {
			/* Clock Configuration Procedure */

			/* 1. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);

			/* 2. Change the apll m,p,s value */
			if (freqs.new == ARMCLOCK_500MHZ) {
				regulator_set_voltage(arm_regulator,
					s5pv310_volt_table[3].arm_volt,
					s5pv310_volt_table[3].arm_volt);
			}
			s5pv310_set_apll(new_index);
		}
#else
		/* The frequency change to L0 needs to change apll */
		if (is_curfreq_table &&
			(freqs.new == s5pv310_freq_table[L0].frequency)) {
			/* Clock Configuration Procedure */

			/* 1. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);

			/* 2. Change the apll m,p,s value */
			s5pv310_set_apll(new_index);
		} else {
			/* 1. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);

			/* 2. Change just s value in apll m,p,s value */
			tmp = __raw_readl(S5P_APLL_CON0);
			tmp &= ~(0x7 << 0);
			tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
			__raw_writel(tmp, S5P_APLL_CON0);
		}
#endif
	} else if (freqs.old > freqs.new) {
#ifdef CONFIG_CPU_S5PV310_EVT1
			/* L1/L3, L2/L4 Level change require to only change s value */
		if (is_curfreq_table &&
			(s5pv310_apll_pms_table[old_index]&CHANGE_S_MASK == s5pv310_apll_pms_table[new_index]&CHANGE_S_MASK)) {					change_s_value = 1;

			/* 1. Change just s value in apll m,p,s value */
			tmp = __raw_readl(S5P_APLL_CON0);
			tmp &= ~(0x7 << 0);
			tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
			__raw_writel(tmp, S5P_APLL_CON0);

			/* 2. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);
		} else {
			/* Clock Configuration Procedure */
			if (freqs.old == ARMCLOCK_500MHZ) {
				regulator_set_voltage(arm_regulator,
					s5pv310_volt_table[3].arm_volt,
					s5pv310_volt_table[3].arm_volt);
			}

			/* 1. Change the apll m,p,s value */
			s5pv310_set_apll(new_index);

			/* 2. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);
		}
#else
		/* The frequency change from L0 needs to change apll */
		if (is_cpufreq_table &&
			(freqs.old == s5pv310_freq_table[L0].frequency)) {
			/* Clock Configuration Procedure */

			/* 1. Change the apll m,p,s value */
			s5pv310_set_apll(new_index);

			/* 2. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);
		} else {
			/* 1. Change just s value in apll m,p,s value */
			tmp = __raw_readl(S5P_APLL_CON0);
			tmp &= ~(0x7 << 0);
			tmp |= (s5pv310_apll_pms_table[new_index] & 0x7);
			__raw_writel(tmp, S5P_APLL_CON0);

			/* 2. Change the system clock divider values */
			s5pv310_set_clkdiv(new_index);
		}
#endif
	}
}

#ifdef ARIGHI_SMOOTH_SCALING
unsigned int target_freq_smooth;

static void do_smooth_freq(struct work_struct *work)
{
	unsigned int target_freq;

	mutex_lock(&set_cpu_freq_change);
	target_freq = target_freq_smooth;
	mutex_unlock(&set_cpu_freq_change);

	if (likely(target_freq)) {
		struct cpufreq_policy *policy = cpufreq_cpu_get(0);
		int ret;

		printk(KERN_INFO "%s: cpu%d: freq set to %u\n",
			__func__, policy->cpu, target_freq_smooth);
		ret = cpufreq_driver_target(policy,
				target_freq_smooth, CPUFREQ_RELATION_H);
		WARN_ON(ret < 0);
	}
}

static DECLARE_DELAYED_WORK(smooth_freq_work, do_smooth_freq);
#endif

int smooth_target = L0;
int smooth_offset = 2;
int smooth_step = 1;

static int s5pv310_target(struct cpufreq_policy *policy,
		unsigned int target_freq,
		unsigned int relation)
{
	int ret = 0;
	unsigned int index, old_index, target_index;
	unsigned int arm_volt;
#ifndef CONFIG_S5PV310_BUSFREQ
	unsigned int int_volt;
#endif

	unsigned int check_gov = 0;

	mutex_lock(&set_cpu_freq_change);

	if ((relation & ENABLE_FURTHER_CPUFREQ) &&
		(relation & DISABLE_FURTHER_CPUFREQ)) {
		/* Invalidate both if both marked */
		relation &= ~ENABLE_FURTHER_CPUFREQ;
		relation &= ~DISABLE_FURTHER_CPUFREQ;
		printk(KERN_ERR "%s:%d denied marking \"FURTHER_CPUFREQ\""
				" as both marked.\n",
				__FILE__, __LINE__);
	}

//string compare here?? you gotta be kidding.. - gm
	if(policy->governor->disableScalingDuringSuspend) {
		check_gov = 1;
		if (relation & ENABLE_FURTHER_CPUFREQ)
			s5pv310_dvs_locking = 0;

		if (s5pv310_dvs_locking == 1)
			goto cpufreq_out;

		if (relation & DISABLE_FURTHER_CPUFREQ)
			s5pv310_dvs_locking = 1;

		relation &= ~(MASK_FURTHER_CPUFREQ | MASK_ONLY_SET_CPUFREQ);
	} else {  //disable this part later, maybe... - gm
		if ((relation & ENABLE_FURTHER_CPUFREQ) ||
			(relation & DISABLE_FURTHER_CPUFREQ) ||
			(relation & MASK_ONLY_SET_CPUFREQ))
			goto cpufreq_out;
	}

	freqs.old = s5pv310_getspeed(policy->cpu);

	if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
				freqs.old, relation, &old_index)) {
		ret = -EINVAL;
		goto cpufreq_out;
	}

	if (cpufreq_frequency_table_target(policy, s5pv310_freq_table,
				target_freq, relation, &index)) {
		ret = -EINVAL;
		goto cpufreq_out;
	}

	if ((index > g_cpufreq_lock_level) && check_gov)
		index = g_cpufreq_lock_level;

	if ((index < g_cpufreq_limit_level) && check_gov)
		index = g_cpufreq_limit_level;

#ifdef ARIGHI_SMOOTH_SCALING
	target_index = index;
#endif

#ifdef SMOOTH_SCALING
#ifdef CONFIG_FREQ_STEP_UP_L2_L0
		/* change L2 -> L0 */
		if ((index <= L0) && (old_index > L2))
			index = L2;
#else
		/* change L2 -> L1 and change L1 -> L0 */
		if (index <= L2) {
			if (old_index > L3)
				index = L3;

			if (old_index > L4)
				index = L4;
		}
#endif
#else
//reach 1200MHz step by step starting from 800MHz -gm
if(policy->governor->enableSmoothScaling && index <= smooth_target)
  if(index < old_index) index = min(smooth_target + smooth_offset, old_index - smooth_step);
#endif

/* prevent freqs going above max policy - netarchy */
//	if (s5pv310_freq_table[index].frequency > policy->max) { //redundant - gm
		while (s5pv310_freq_table[index].frequency > policy->max) {
			index += 1;
		}
//	}

	freqs.new = s5pv310_freq_table[index].frequency;
	freqs.cpu = policy->cpu;
#ifdef ARIGHI_SMOOTH_SCALING
	if (index != target_index) {
		target_freq_smooth = target_freq;
		schedule_delayed_work_on(0, &smooth_freq_work, HZ >> 1);
	} else
		target_freq_smooth = 0;
#endif
	/* If the new frequency is same with previous frequency, skip */
	if (freqs.new == freqs.old)
		goto bus_freq;

#if CPUMON
	printk(KERN_ERR "CPUMON F %d\n", freqs.new);
#endif

	/* get the voltage value */
//	arm_volt = s5pv310_volt_table[index].arm_volt;
	arm_volt = exp_UV_mV[index];
#ifndef CONFIG_S5PV310_BUSFREQ
	int_volt = s5pv310_volt_table[index].int_volt;
#endif
	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

	/* When the new frequency is higher than current frequency */
	if (freqs.new > freqs.old) {
		/* Firstly, voltage up to increase frequency */
#if defined(CONFIG_REGULATOR)
		regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
#ifndef CONFIG_S5PV310_BUSFREQ
		regulator_set_voltage(int_regulator, int_volt, int_volt);
#endif
#endif
	}

	s5pv310_set_frequency(old_index, index);

	/* When the new frequency is lower than current frequency */
	if (freqs.new < freqs.old) {
		/* down the voltage after frequency change */
#if defined(CONFIG_REGULATOR)
		regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
#ifndef CONFIG_S5PV310_BUSFREQ
		regulator_set_voltage(int_regulator, int_volt, int_volt);
#endif
#endif
	}

	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

#ifdef HAVE_DAC
	switch (index) {
	case L0:
		__raw_writeb(0xff, dac_base);
		break;
	case L1:
		__raw_writeb(0xaa, dac_base);
		break;
	case L2:
		__raw_writeb(0x55, dac_base);
		break;
	case L3:
		__raw_writeb(0x00, dac_base);
		break;
	}
#endif

bus_freq:
	mutex_unlock(&set_cpu_freq_change);
#ifdef CONFIG_S5PV310_BUSFREQ
	busfreq_target();
#endif
	return ret;

cpufreq_out:
	mutex_unlock(&set_cpu_freq_change);
	return ret;
}

#ifdef CONFIG_S5PV310_BUSFREQ
static int busfreq_ppmu_init(void)
{
	unsigned int i;

	for (i = 0; i < 2; i++) {
		s5pv310_dmc_ppmu_reset(&dmc[i]);
		s5pv310_dmc_ppmu_setevent(&dmc[i], 0x6);
		s5pv310_dmc_ppmu_start(&dmc[i]);
	}

	return 0;
}

static int cpu_ppmu_init(void)
{
	s5pv310_cpu_ppmu_reset(&cpu);
	s5pv310_cpu_ppmu_setevent(&cpu, 0x5, 0);
	s5pv310_cpu_ppmu_setevent(&cpu, 0x6, 1);
	s5pv310_cpu_ppmu_start(&cpu);

	return 0;
}

static unsigned int calc_bus_utilization(struct s5pv310_dmc_ppmu_hw *ppmu)
{
	unsigned int bus_usage;

	if (ppmu->ccnt == 0) {
		printk(KERN_DEBUG "%s: 0 value is not permitted\n", __func__);
		return MAX_LOAD;
	}

	if (!(ppmu->ccnt >> 7))
		bus_usage = (ppmu->count[0] * 100) / ppmu->ccnt;
	else
		bus_usage = ((ppmu->count[0] >> 7) * 100) / (ppmu->ccnt >> 7);

	return bus_usage;
}

static unsigned int calc_cpu_bus_utilization(struct s5pv310_cpu_ppmu_hw *cpu_ppmu)
{
	unsigned int cpu_bus_usage;

	if (cpu.ccnt == 0)
		cpu.ccnt = MAX_LOAD;

	cpu_bus_usage = (cpu.count[0] + cpu.count[1])*100 / cpu.ccnt;

	return cpu_bus_usage;
}

static unsigned int get_cpu_ppmu_load(void)
{
	unsigned int cpu_bus_load;

	s5pv310_cpu_ppmu_stop(&cpu);
	s5pv310_cpu_ppmu_update(&cpu);

	cpu_bus_load = calc_cpu_bus_utilization(&cpu);

	return cpu_bus_load;
}

static unsigned int get_ppc_load(void)
{
	int i;
	unsigned int bus_load;

	for (i = 0; i < 2; i++) {
		s5pv310_dmc_ppmu_stop(&dmc[i]);
		s5pv310_dmc_ppmu_update(&dmc[i]);
		bus_utilization[i] = calc_bus_utilization(&dmc[i]);
	}

	bus_load = max(bus_utilization[0], bus_utilization[1]);

	return bus_load;
}

static unsigned int is_busfreq_static = 0;
int busfreq_static_level[6] = { 
	0, //1200Mhz
	0, //1000Mhz
	1, // 800Mhz
	2, // 500Mhz
	2,  // 200Mhz
	2,  // 100Mhz
};

static int busload_observor(struct busfreq_table *freq_table,
			unsigned int bus_load,
			unsigned int cpu_bus_load,
			unsigned int pre_idx,
			unsigned int *index)
{
	unsigned int i, target_freq, idx = 0;

	if(is_busfreq_static)
	{
		//find the step
		for(i=0;freqs.new < s5pv310_freq_table[i].frequency;i++);
		idx = busfreq_static_level[i];
		goto observerout;
	}
	
	if ((freqs.new >= s5pv310_freq_table[L0].frequency)) {
		*index = LV_0;
		return 0;
	}

	if (bus_load > MAX_LOAD)
		return -EINVAL;

	/*
	* Maximum bus_load of S5PV310 is about 50%.
	* Default Up Threshold is 27%.
	*/
	if (bus_load > 50) {
		printk(KERN_DEBUG "BUSLOAD is larger than 50(%d)\n", bus_load);
		bus_load = 50;
	}

	if (bus_load >= up_threshold || cpu_bus_load > 10) {
		target_freq = freq_table[0].mem_clk;
		idx = 0;
	} else if (bus_load < (up_threshold - 2)) {
		target_freq = (bus_load * freq_table[pre_idx].mem_clk) /
							(up_threshold - 2);

		if (target_freq >= freq_table[pre_idx].mem_clk) {
			for (i = 0; (freq_table[i].mem_clk != 0); i++) {
				unsigned int freq = freq_table[i].mem_clk;
				if (freq <= target_freq) {
					idx = i;
					break;
				}
			}
		} else {
			for (i = 0; (freq_table[i].mem_clk != 0); i++) {
				unsigned int freq = freq_table[i].mem_clk;
				if (freq >= target_freq) {
					idx = i;
					continue;
				}
				if (freq < target_freq)
					break;
			}
		}
	} else {
		idx = pre_idx;
	}

	if ((freqs.new == s5pv310_freq_table[L0].frequency) && (bus_load == 0))
		idx = pre_idx;

	if ((idx > LV_1) && (cpu_bus_load > 5))
		idx = LV_1;
observerout:
	if (idx > g_busfreq_lock_level)
		idx = g_busfreq_lock_level;

	*index = idx;

	return 0;
}

static void busfreq_target(void)
{
	unsigned int i, index = 0, ret, voltage;
	unsigned int bus_load, cpu_bus_load;
#ifdef SYSFS_DEBUG_BUSFREQ
	unsigned long level_state_jiffies;
#endif

	mutex_lock(&set_bus_freq_change);

	if (busfreq_fix) {
		for (i = 0; i < 2; i++)
			s5pv310_dmc_ppmu_stop(&dmc[i]);

		s5pv310_cpu_ppmu_stop(&cpu);
		goto fix_out;
	}

	cpu_bus_load = get_cpu_ppmu_load();

	if (cpu_bus_load > 10) {
		if (p_idx != LV_0) {
			voltage = s5pv310_busfreq_table[LV_0].volt;
#if defined(CONFIG_REGULATOR)
			regulator_set_voltage(int_regulator, voltage, voltage);
#endif
			s5pv310_set_busfreq(LV_0);
		}
	  p_idx = LV_0;

	  goto out;
	}

	bus_load = get_ppc_load();

	/* Change bus frequency */
	ret = busload_observor(s5pv310_busfreq_table,
				bus_load, cpu_bus_load, p_idx, &index);
	if (ret < 0)
		printk(KERN_ERR "%s:fail to check load (%d)\n", __func__, ret);

#ifdef SYSFS_DEBUG_BUSFREQ
	curjiffies = jiffies;
	if (prejiffies != 0)
		level_state_jiffies = curjiffies - prejiffies;
	else
		level_state_jiffies = 0;

	prejiffies = jiffies;

	switch (p_idx) {
	case LV_0:
		time_in_state[LV_0] += level_state_jiffies;
		break;
	case LV_1:
		time_in_state[LV_1] += level_state_jiffies;
		break;
	case LV_2:
		time_in_state[LV_2] += level_state_jiffies;
		break;
	default:
		break;
	}
#endif

	if (p_idx != index) {
		voltage = s5pv310_busfreq_table[index].volt;
		if (p_idx > index) {
#if defined(CONFIG_REGULATOR)
			regulator_set_voltage(int_regulator, voltage, voltage);
#endif
		}

		s5pv310_set_busfreq(index);

		if (p_idx < index) {
#if defined(CONFIG_REGULATOR)
			regulator_set_voltage(int_regulator, voltage, voltage);
#endif
		}
		smp_mb();
		p_idx = index;
	}
out:
	busfreq_ppmu_init();
	cpu_ppmu_init();
fix_out:
	mutex_unlock(&set_bus_freq_change);

}
#endif

int s5pv310_cpufreq_lock(unsigned int nId,
			enum cpufreq_level_request cpufreq_level)
{
	int ret = 0, cpu = 0;
	unsigned int cur_freq;

	if (!s5pv310_cpufreq_init_done)
		return 0;
#if 0
	if (s5pv310_max_armclk != ARMCLOCK_1200MHZ) {
		if (cpufreq_level != CPU_L0) {
			cpufreq_level -= 1;
		} else {
			printk(KERN_WARNING
				"[CPUFREQ]cpufreq lock to 1GHz in place of 1.2GHz\n");
		}
	}
#endif
	if (g_cpufreq_lock_id & (1 << nId)) {
		printk(KERN_ERR
		"[CPUFREQ]This device [%d] already locked cpufreq\n", nId);
		return 0;
	}
	mutex_lock(&set_cpu_freq_lock);
	g_cpufreq_lock_id |= (1 << nId);
	g_cpufreq_lock_val[nId] = cpufreq_level;

	/* If the requested cpufreq is higher than current min frequency */
	if (cpufreq_level < g_cpufreq_lock_level)
		g_cpufreq_lock_level = cpufreq_level;
	mutex_unlock(&set_cpu_freq_lock);

	/* If current frequency is lower than requested freq, need to update */
	cur_freq = s5pv310_getspeed(cpu);
	if (cur_freq < s5pv310_freq_table[cpufreq_level].frequency) {
		ret = cpufreq_driver_target(cpufreq_cpu_get(cpu),
				s5pv310_freq_table[cpufreq_level].frequency,
				MASK_ONLY_SET_CPUFREQ);
	}

	return ret;
}

void s5pv310_cpufreq_lock_free(unsigned int nId)
{
	unsigned int i;

	if (!s5pv310_cpufreq_init_done)
		return;

	mutex_lock(&set_cpu_freq_lock);

	g_cpufreq_lock_id &= ~(1 << nId);
	g_cpufreq_lock_val[nId] = CPUFREQ_MIN_LEVEL;
	g_cpufreq_lock_level = CPUFREQ_MIN_LEVEL;
	if (g_cpufreq_lock_id) {
		for (i = 0; i < DVFS_LOCK_ID_END; i++) {
			if (g_cpufreq_lock_val[i] < g_cpufreq_lock_level)
				g_cpufreq_lock_level = g_cpufreq_lock_val[i];
		}
	}

	mutex_unlock(&set_cpu_freq_lock);
}

int s5pv310_cpufreq_upper_limit(unsigned int nId, enum cpufreq_level_request cpufreq_level)
{
	int ret = 0, cpu = 0;
	unsigned int cur_freq;

	if (!s5pv310_cpufreq_init_done)
		return 0;
#if 0
	if (s5pv310_max_armclk != ARMCLOCK_1200MHZ) {
		if (cpufreq_level != CPU_L0) {
			cpufreq_level -= 1;
		} else {
			printk(KERN_DEBUG
				"[CPUFREQ]cpufreq lock to 1GHz in place of 1.2GHz\n");
		}
	}
#endif
	if (g_cpufreq_limit_id & (1 << nId)) {
		printk(KERN_ERR "[CPUFREQ]This device [%d] already limited cpufreq\n", nId);
		return 0;
	}

	mutex_lock(&set_cpu_freq_lock);
	g_cpufreq_limit_id |= (1 << nId);
	g_cpufreq_limit_val[nId] = cpufreq_level;

	/* If the requested limit level is lower than current value */
	if (cpufreq_level > g_cpufreq_limit_level)
		g_cpufreq_limit_level = cpufreq_level;

	mutex_unlock(&set_cpu_freq_lock);

	/* If cur frequency is higher than limit freq, it needs to update */
	cur_freq = s5pv310_getspeed(cpu);
	if (cur_freq > s5pv310_freq_table[cpufreq_level].frequency) {
		ret = cpufreq_driver_target(cpufreq_cpu_get(cpu),
				s5pv310_freq_table[cpufreq_level].frequency,
				MASK_ONLY_SET_CPUFREQ);
	}

	return ret;
}

void s5pv310_cpufreq_upper_limit_free(unsigned int nId)
{
	unsigned int i;

	if (!s5pv310_cpufreq_init_done)
		return;

	mutex_lock(&set_cpu_freq_lock);
	g_cpufreq_limit_id &= ~(1 << nId);
	g_cpufreq_limit_val[nId] = CPUFREQ_LIMIT_LEVEL;
	g_cpufreq_limit_level = CPUFREQ_LIMIT_LEVEL;
	if (g_cpufreq_limit_id) {
		for (i = 0; i < DVFS_LOCK_ID_END; i++) {
			if (g_cpufreq_limit_val[i] > g_cpufreq_limit_level)
				g_cpufreq_limit_level = g_cpufreq_limit_val[i];
		}
	}
	mutex_unlock(&set_cpu_freq_lock);
}

#ifdef CONFIG_S5PV310_BUSFREQ
int s5pv310_busfreq_lock(unsigned int nId,
			enum busfreq_level_request busfreq_level)
{
	if (g_busfreq_lock_id & (1 << nId)) {
		printk(KERN_ERR
		"[BUSFREQ] This device [%d] already locked busfreq\n", nId);
		return 0;
	}
	mutex_lock(&set_bus_freq_lock);
	g_busfreq_lock_id |= (1 << nId);
	g_busfreq_lock_val[nId] = busfreq_level;

	/* If the requested cpufreq is higher than current min frequency */
	if (busfreq_level < g_busfreq_lock_level) {
		g_busfreq_lock_level = busfreq_level;
		mutex_unlock(&set_bus_freq_lock);
		busfreq_target();
	} else
		mutex_unlock(&set_bus_freq_lock);

	return 0;
}

void s5pv310_busfreq_lock_free(unsigned int nId)
{
	unsigned int i;

	mutex_lock(&set_bus_freq_lock);

	g_busfreq_lock_id &= ~(1 << nId);
	g_busfreq_lock_val[nId] = BUSFREQ_MIN_LEVEL;
	g_busfreq_lock_level = BUSFREQ_MIN_LEVEL;

	if (g_busfreq_lock_id) {
		for (i = 0; i < DVFS_LOCK_ID_END; i++) {
			if (g_busfreq_lock_val[i] < g_busfreq_lock_level)
				g_busfreq_lock_level = g_busfreq_lock_val[i];
		}
	}

	mutex_unlock(&set_bus_freq_lock);
}
#endif

#ifdef CONFIG_PM
static int s5pv310_cpufreq_suspend(struct cpufreq_policy *policy,
			pm_message_t pmsg)
{
	int ret = 0;

	return ret;
}

static int s5pv310_cpufreq_resume(struct cpufreq_policy *policy)
{
	int ret = 0;

	return ret;
}
#endif

ssize_t set_freq_table(unsigned char step, unsigned int freq);
static DEFINE_MUTEX(suspend_mutex);
int deepsleep_cpulevel = L2;
int deepsleep_buslevel = BUS_L0;

static int s5pv310_cpufreq_notifier_event(struct notifier_block *this,
		unsigned long event, void *ptr)
{
        static int max, min;
	struct cpufreq_policy *policy = cpufreq_cpu_get(0);
	int ret = 0;

	switch (event) {
	case PM_SUSPEND_PREPARE:
		max = policy->max;
		min = policy->min;
		policy->max = policy->min = s5pv310_freq_table[deepsleep_cpulevel].frequency;
		ret = cpufreq_driver_target(policy,
		s5pv310_freq_table[deepsleep_cpulevel].frequency, DISABLE_FURTHER_CPUFREQ);
		if (WARN_ON(ret < 0))
			return NOTIFY_BAD;
#ifdef CONFIG_S5PV310_BUSFREQ
		s5pv310_busfreq_lock(DVFS_LOCK_ID_PM, deepsleep_buslevel);
#endif
		printk(KERN_DEBUG "PM_SUSPEND_PREPARE for CPUFREQ\n");
		return NOTIFY_OK;
	case PM_POST_RESTORE:
	case PM_POST_SUSPEND:
		printk(KERN_DEBUG "PM_POST_SUSPEND for CPUFREQ: %d\n", ret);
		ret = cpufreq_driver_target(policy,
		s5pv310_freq_table[deepsleep_cpulevel].frequency, ENABLE_FURTHER_CPUFREQ);
		policy->max = max;
		policy->min = min;
#ifdef CONFIG_S5PV310_BUSFREQ
		s5pv310_busfreq_lock_free(DVFS_LOCK_ID_PM);
#endif
		return NOTIFY_OK;
	}
	return NOTIFY_DONE;
}

static struct notifier_block s5pv310_cpufreq_notifier = {
	.notifier_call = s5pv310_cpufreq_notifier_event,
#ifdef ARIGHI_SMOOTH_SCALING
	.priority = INT_MIN, /* done last */
#endif
};

static int s5pv310_cpufreq_reboot_notifier_call(struct notifier_block *this,
				   unsigned long code, void *_cmd)
{
	unsigned int cpu = 0;
	int ret = 0;

	ret = cpufreq_driver_target(cpufreq_cpu_get(cpu),
		s5pv310_freq_table[L0].frequency, DISABLE_FURTHER_CPUFREQ);
	if (WARN_ON(ret < 0))
		return NOTIFY_BAD;
#ifdef CONFIG_S5PV310_BUSFREQ
	s5pv310_busfreq_lock(DVFS_LOCK_ID_PM, BUS_L0);
#endif
	printk(KERN_ERR "C1 REBOOT Notifier for CPUFREQ\n");

	return NOTIFY_DONE;
}

static struct notifier_block s5pv310_cpufreq_reboot_notifier = {
	.notifier_call = s5pv310_cpufreq_reboot_notifier_call,
};

static int s5pv310_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
	printk(KERN_DEBUG "++ %s\n", __func__);

	policy->cur = policy->min = policy->max = s5pv310_getspeed(policy->cpu);

	cpufreq_frequency_table_get_attr(s5pv310_freq_table, policy->cpu);

	/* set the transition latency value
	 */
	policy->cpuinfo.transition_latency = SET_CPU_FREQ_SAMPLING_RATE;

	/* S5PV310 multi-core processors has 2 cores
	 * that the frequency cannot be set independently.
	 * Each cpu is bound to the same speed.
	 * So the affected cpu is all of the cpus.
	 */
	if (!cpu_online(1)) {
		cpumask_copy(policy->related_cpus, cpu_possible_mask);
		cpumask_copy(policy->cpus, cpu_online_mask);
	} else {
		cpumask_setall(policy->cpus);
	}

	cpufreq_frequency_table_cpuinfo(policy, s5pv310_freq_table);
	/* set safe default min and max speeds - netarchy */
	policy->max = 1200 * 1000;
	policy->min = 200 * 1000;
	return 0;
}

/* Make sure we have the scaling_available_freqs sysfs file */
static struct freq_attr *s5pv310_cpufreq_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
        NULL,
}; 

static struct cpufreq_driver s5pv310_driver = {
	.flags = CPUFREQ_STICKY,
	.verify = s5pv310_verify_policy,
	.target = s5pv310_target,
	.get = s5pv310_getspeed,
	.init = s5pv310_cpufreq_cpu_init,
	.name = "s5pv310_cpufreq",
	.attr = s5pv310_cpufreq_attr,
#ifdef CONFIG_PM
	.suspend = s5pv310_cpufreq_suspend,
	.resume = s5pv310_cpufreq_resume,
#endif
};

#ifdef CONFIG_S5PV310_ASV

#include <mach/regs-iem.h>
#include <mach/asv.h>

#define IDS_OFFSET	24
#define IDS_MASK	0xFF

#define IDS_SS		4
#define IDS_A1		8
#define IDS_A2		12
#define IDS_B1		17
#define IDS_B2		27
#define IDS_C1		45
#define IDS_C2		55
#define IDS_D1		56

#define HPM_SS		8
#define HPM_A1		11
#define HPM_A2		14
#define HPM_B1		18
#define HPM_B2		21
#define HPM_C1		23
#define HPM_C2		25
#define HPM_D1		26

#define INT_LEVEL_END	3

#define LOOP_CNT	50

struct s5pv310_asv_info asv_info = {
	.asv_num = 0,
	.asv_init_done = 0
};
EXPORT_SYMBOL(asv_info);

static int iem_clock_init(void)
{
	struct clk *clk_hpm;
	struct clk *clk_copy;
	struct clk *clk_parent;

	/* PWI clock setting */
	clk_copy = clk_get(NULL, "sclk_pwi");
	if (IS_ERR(clk_copy)) {
		printk(KERN_ERR"ASV : SCLK_PWI clock get error\n");
		return -EINVAL;
	} else {
		clk_parent = clk_get(NULL, "xusbxti");
		if (IS_ERR(clk_parent)) {
			printk(KERN_ERR"ASV : MOUT_APLL clock get error\n");
			return -EINVAL;
		}
		clk_set_parent(clk_copy, clk_parent);

		clk_put(clk_parent);
	}

	clk_set_rate(clk_copy, 4800000);

	clk_put(clk_copy);

	/* HPM clock setting */
	clk_copy = clk_get(NULL, "dout_copy");

	if (IS_ERR(clk_copy)) {
		printk(KERN_ERR"ASV : DOUT_COPY clock get error\n");
		return -EINVAL;
	} else {
		clk_parent = clk_get(NULL, "mout_apll");
		if (IS_ERR(clk_parent)) {
			printk(KERN_ERR"ASV : MOUT_APLL clock get error\n");
			return -EINVAL;
		}
		clk_set_parent(clk_copy, clk_parent);

		clk_put(clk_parent);
	}

	clk_set_rate(clk_copy, 1000000000);

	clk_put(clk_copy);

	clk_hpm = clk_get(NULL, "sclk_hpm");
	if (IS_ERR(clk_hpm))
		return -EINVAL;

	clk_set_rate(clk_hpm, (210 * 1000 * 1000));

	clk_put(clk_hpm);

	return 0;
}

void iem_clock_set(void)
{
	/* APLL_CON0 level register */
	__raw_writel(0x80FA0601, S5P_APLL_CON0L8);
	__raw_writel(0x80C80601, S5P_APLL_CON0L7);
	__raw_writel(0x80C80602, S5P_APLL_CON0L6);
	__raw_writel(0x80C80604, S5P_APLL_CON0L5);
	__raw_writel(0x80C80601, S5P_APLL_CON0L4);
	__raw_writel(0x80C80601, S5P_APLL_CON0L3);
	__raw_writel(0x80C80601, S5P_APLL_CON0L2);
	__raw_writel(0x80C80601, S5P_APLL_CON0L1);

	/* IEM Divider register */
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L8);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L7);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L6);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L5);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L4);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L3);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L2);
	__raw_writel(0x00500000, S5P_CLKDIV_IEM_L1);
}
static int s5pv310_asv_init(void)
{
	unsigned int i;
	unsigned long sum_result = 0;
	unsigned int tmp;
	unsigned int hpm[LOOP_CNT];
	static void __iomem *iem_base;
	struct clk *clk_iec;
	struct clk *clk_apc;
	struct clk *clk_hpm;

	iem_base = ioremap(S5PV310_PA_IEM, (128 * 1024));
	if (iem_base == NULL) {
		printk(KERN_ERR "faile to ioremap\n");
		goto out;
	}
	/* IEC clock gate enable */
	clk_iec = clk_get(NULL, "iem-iec");
	if (IS_ERR(clk_iec)) {
		printk(KERN_ERR"ASV : IEM IEC clock get error\n");
		return -EINVAL;
	}
	clk_enable(clk_iec);

	/* APC clock gate enable */
	clk_apc = clk_get(NULL, "iem-apc");
	if (IS_ERR(clk_apc)) {
		printk(KERN_ERR"ASV : IEM APC clock get error\n");
		return -EINVAL;
	}
	clk_enable(clk_apc);

	/* hpm clock gate enalbe */
	clk_hpm = clk_get(NULL, "hpm");
	if (IS_ERR(clk_hpm)) {
		printk(KERN_ERR"ASV : HPM clock get error\n");
		return -EINVAL;
	}
	clk_enable(clk_hpm);


	if (iem_clock_init()) {
		printk(KERN_ERR "ASV driver clock_init fail\n");
		goto out;
	} else {
		/* HPM enable  */
		tmp = __raw_readl(iem_base + S5PV310_APC_CONTROL);
		tmp |= APC_HPM_EN;
		__raw_writel(tmp, (iem_base + S5PV310_APC_CONTROL));

		iem_clock_set();

		/* IEM enable */
		tmp = __raw_readl(iem_base + S5PV310_IECDPCCR);
		tmp |= IEC_EN;
		__raw_writel(tmp, (iem_base + S5PV310_IECDPCCR));
	}

	for (i = 0; i < LOOP_CNT; i++) {
		tmp = __raw_readb(iem_base + S5PV310_APC_DBG_DLYCODE);
		sum_result += tmp;
		hpm[i] = tmp;
	}

	for (i = 0; i < LOOP_CNT; i++)
		printk(KERN_INFO "ASV : hpm[%d] = %d value\n", i, hpm[i]);

	sum_result /= LOOP_CNT;
	printk(KERN_INFO "ASV : sum average value : %ld\n", sum_result);
	sum_result -= 1;
	printk(KERN_INFO "ASV : hpm value %ld\n", sum_result);

	/* hpm clock gate disable */
	clk_disable(clk_hpm);
	clk_put(clk_hpm);

	/* IEC clock gate disable */
	clk_disable(clk_iec);
	clk_put(clk_iec);

	/* APC clock gate disable */
	clk_disable(clk_apc);
	clk_put(clk_apc);

	iounmap(iem_base);

	return sum_result;

out:
	return -EINVAL;
}

unsigned int ids_arm, hpm_code;

static int s5pv310_asv_table_update(void)
{
	unsigned int i;
	unsigned int tmp;
	unsigned int hpm_group = 0xff, ids_group = 0xff;
	unsigned int asv_group;
	struct clk *clk_chipid;
	unsigned int last_level = 0;

	/* chip id clock gate enable*/
	clk_chipid = clk_get(NULL, "chipid");
	if (IS_ERR(clk_chipid)) {
		printk(KERN_ERR "ASV : chipid clock get error\n");
		return -EINVAL;
	}
	clk_enable(clk_chipid);

	tmp = __raw_readl(S5P_VA_CHIPID + 0x4);

	/* get the ids_arm */
	ids_arm = ((tmp >> IDS_OFFSET) & IDS_MASK);
	if (!ids_arm) {
		printk(KERN_ERR "S5PV310 : Cannot read IDS\n");
		return -EINVAL;
	}

	/* ids grouping */
	if ((ids_arm > 0) && (ids_arm <= IDS_SS))
		ids_group = 0;
	else if ((ids_arm > IDS_SS) && (ids_arm <= IDS_A1))
		ids_group = 1;
	else if ((ids_arm > IDS_A1) && (ids_arm <= IDS_A2))
		ids_group = 2;
	else if ((ids_arm > IDS_A2) && (ids_arm <= IDS_B1))
		ids_group = 3;
	else if ((ids_arm > IDS_B1) && (ids_arm <= IDS_B2))
		ids_group = 4;
	else if ((ids_arm > IDS_B2) && (ids_arm <= IDS_C1))
		ids_group = 5;
	else if ((ids_arm > IDS_C1) && (ids_arm <= IDS_C2))
		ids_group = 6;
	else if (ids_arm >= IDS_D1)
		ids_group = 7;

	/* Change Divider - CPU1 */
	tmp = __raw_readl(S5P_CLKDIV_CPU1);
	tmp &= ~((0x7 << S5P_CLKDIV_CPU1_HPM_SHIFT) |
		(0x7 << S5P_CLKDIV_CPU1_COPY_SHIFT));
	tmp |= ((0x0 << S5P_CLKDIV_CPU1_HPM_SHIFT) |
		(0x3 << S5P_CLKDIV_CPU1_COPY_SHIFT));
	__raw_writel(tmp, S5P_CLKDIV_CPU1);

	/* HPM SCLKMPLL */
	tmp = __raw_readl(S5P_CLKSRC_CPU);
	tmp &= ~(0x1 << S5P_CLKSRC_CPU_MUXHPM_SHIFT);
	tmp |= 0x1 << S5P_CLKSRC_CPU_MUXHPM_SHIFT;
	__raw_writel(tmp, S5P_CLKSRC_CPU);

	hpm_code = s5pv310_asv_init();

	/* HPM SCLKAPLL */
	tmp = __raw_readl(S5P_CLKSRC_CPU);
	tmp &= ~(0x1 << S5P_CLKSRC_CPU_MUXHPM_SHIFT);
	tmp |= 0x0 << S5P_CLKSRC_CPU_MUXHPM_SHIFT;
	__raw_writel(tmp, S5P_CLKSRC_CPU);

	/* hpm grouping */
	if ((hpm_code > 0) && (hpm_code <= HPM_SS))
		hpm_group = 0;
	else if ((hpm_code > HPM_SS) && (hpm_code <= HPM_A1))
		hpm_group = 1;
	else if ((hpm_code > HPM_A1) && (hpm_code <= HPM_A2))
		hpm_group = 2;
	else if ((hpm_code > HPM_A2) && (hpm_code <= HPM_B1))
		hpm_group = 3;
	else if ((hpm_code > HPM_B1) && (hpm_code <= HPM_B2))
		hpm_group = 4;
	else if ((hpm_code > HPM_B2) && (hpm_code <= HPM_C1))
		hpm_group = 5;
	else if ((hpm_code > HPM_C1) && (hpm_code <= HPM_C2))
		hpm_group = 6;
	else if (hpm_code >= HPM_D1)
		hpm_group = 7;

	printk(KERN_INFO "******************************ASV *********************\n");
	printk(KERN_INFO "ASV ids_arm = %d hpm_code = %d\n", ids_arm, hpm_code);

	/* decide asv group */
	if (ids_group > hpm_group) {
		if (ids_group - hpm_group >= 3)
			asv_group = ids_group - 3;
		else
			asv_group = hpm_group;
	} else {
		if (hpm_group - ids_group >= 3)
			asv_group = hpm_group - 3;
		else
			asv_group = ids_group;
	}

	/* set asv infomation */
	asv_info.asv_num = asv_group;
	asv_info.asv_init_done = 1;

	printk(KERN_INFO "******************************ASV *********************\n");
	printk(KERN_INFO "ASV asv_info.asv_num = %d, asv_info.asv_init_done = %d\n",
		asv_info.asv_num, asv_info.asv_init_done);
	printk(KERN_INFO "ASV ids_group = %d hpm_group = %d asv_group = %d\n",
		ids_group, hpm_group, asv_group);

	if (true) //(s5pv310_max_armclk == ARMCLOCK_1200MHZ)
		last_level = CPUFREQ_LEVEL_END - 1;
	else
		last_level = CPUFREQ_LEVEL_END - 2;

	/* VDD_ARM level except the last level  */
	for (i = 0; i < last_level; i++) {
		switch (asv_group) {
		case 0:
			s5pv310_volt_table[i].arm_volt += (100*1000);
			break;
		case 1:
			s5pv310_volt_table[i].arm_volt += (50*1000);
			break;
		case 2:
			s5pv310_volt_table[i].arm_volt += (0*1000);
			break;
		case 3:
			s5pv310_volt_table[i].arm_volt -= (25*1000);
			break;
		case 4:
			if (s5pv310_max_armclk == ARMCLOCK_1200MHZ) {
				if (i == 3)
					s5pv310_volt_table[i].arm_volt -= (25*1000);
				else
					s5pv310_volt_table[i].arm_volt -= (50*1000);
			} else {
				if (i == 2)
					s5pv310_volt_table[i].arm_volt -= (25*1000);
				else
					s5pv310_volt_table[i].arm_volt -= (50*1000);
			}
			break;
		case 5:
			if (true) { //s5pv310_max_armclk == ARMCLOCK_1200MHZ) {
				if (i == 3)
					s5pv310_volt_table[i].arm_volt -= (25*1000);
				else
					s5pv310_volt_table[i].arm_volt -= (50*1000);
			} else {
				if (i == 2)
					s5pv310_volt_table[i].arm_volt -= (25*1000);
				else
					s5pv310_volt_table[i].arm_volt -= (50*1000);
			}
			break;
		case 6:
			s5pv310_volt_table[i].arm_volt -= (100*1000);
			break;
		case 7:
			s5pv310_volt_table[i].arm_volt -= (125*1000);
			break;
		}
		/* Maximum Voltage */
		if (s5pv310_volt_table[i].arm_volt > CPU_UV_MV_MAX)
			s5pv310_volt_table[i].arm_volt = CPU_UV_MV_MAX;

		/* Minimum Voltage */
		if (s5pv310_volt_table[i].arm_volt < CPU_UV_MV_MIN)
			s5pv310_volt_table[i].arm_volt = CPU_UV_MV_MIN;

		printk(KERN_INFO "ASV voltage_table[%d].arm_volt = %d\n",
				i, s5pv310_volt_table[i].arm_volt);
	}

	/* The last level of VDD_ARM */
	switch (asv_group) {
	case 0:
		s5pv310_volt_table[last_level].arm_volt += (75*1000);
		break;
	case 1:
		s5pv310_volt_table[last_level].arm_volt += (25*1000);
		break;
	case 2:
		s5pv310_volt_table[last_level].arm_volt += (0*1000);
		break;
	case 3:
	case 4:
		s5pv310_volt_table[last_level].arm_volt -= (25*1000);
		break;
	case 5:
	case 6:
	case 7:
		s5pv310_volt_table[last_level].arm_volt -= (50*1000);
		break;
	}
	printk(KERN_INFO "ASV voltage_table[%d].arm_volt = %d\n",
		last_level, s5pv310_volt_table[last_level].arm_volt);

	//this one is to correct 200MHz voltage
	s5pv310_volt_table[last_level-1].arm_volt =
				s5pv310_volt_table[last_level].arm_volt;
	
	for(i=0;i<CPUFREQ_LEVEL_END;i++) {
		exp_UV_mV[i] = s5pv310_volt_table[i].arm_volt;
	}


	/* VDD_INT ASV */
	for (i = 0; i < INT_LEVEL_END; i++) {
		switch (asv_group) {
		case 0:
			s5pv310_busfreq_table[i].volt += (50*1000);
			break;
		case 1:
		case 2:
			s5pv310_busfreq_table[i].volt += (25*1000);
			break;
		case 3:
		case 4:
			s5pv310_busfreq_table[i].volt -= (0*1000);
			break;
		case 5:
		case 6:
			s5pv310_busfreq_table[i].volt -= (25*1000);
			break;
		case 7:
			s5pv310_busfreq_table[i].volt -= (50*1000);
			break;
		}

		if (s5pv310_busfreq_table[i].volt < 950000)
			s5pv310_busfreq_table[i].volt = 950000;

		printk(KERN_INFO "ASV busfreq_table[%d].volt = %d\n",
			i, s5pv310_busfreq_table[i].volt);
	}

	/* Disable chipid clock */
	clk_disable(clk_chipid);

	return 0;
}

static void s5pv310_asv_set_voltage(void)
{
	unsigned int asv_arm_index = 0, asv_int_index = 0;
	unsigned int asv_arm_volt, asv_int_volt;
	unsigned int rate;

	/* get current ARM level */
	mutex_lock(&set_cpu_freq_change);

	freqs.old = s5pv310_getspeed(0);

#if 0
	switch (freqs.old) {
	case 1200000:
		asv_arm_index = 0;
		break;
	case 1000000:
		asv_arm_index = 1;
		break;
	case 800000:
		asv_arm_index = 2;
		break;
	case 500000:
		asv_arm_index = 3;
		break;
	case 200000:
		asv_arm_index = 4;
		break;
	case 100000:
		asv_arm_index = 5;
		break;
	default:
		for(asv_arm_index=5;asv_arm_index>0;asv_arm_index--)
			if(freqs.old == s5pv310_freq_table[asv_arm_index].frequency) break;
		break;

	}

	if (s5pv310_max_armclk != ARMCLOCK_1200MHZ)
		asv_arm_index -= 1;

	asv_arm_volt = s5pv310_volt_table[asv_arm_index].arm_volt;
#endif

	asv_arm_volt = exp_UV_mV[asv_arm_index];

#if defined(CONFIG_REGULATOR)
	regulator_set_voltage(arm_regulator, asv_arm_volt, asv_arm_volt);
#endif
	mutex_unlock(&set_cpu_freq_change);

	/* get current INT level */
	mutex_lock(&set_bus_freq_change);

	rate = s5pv310_getspeed_dmc(0);

	switch (rate) {
	case 400000:
		asv_int_index = 0;
		break;
	case 266666:
	case 267000:
		asv_int_index = 1;
		break;
	case 133000:
	case 133333:
		asv_int_index = 2;
		break;
	default:
		asv_int_index = 0;
		break;
	}
	asv_int_volt = s5pv310_busfreq_table[asv_int_index].volt;
#if defined(CONFIG_REGULATOR)
	regulator_set_voltage(int_regulator, asv_int_volt, asv_int_volt);
#endif
	mutex_unlock(&set_bus_freq_change);

	printk(KERN_INFO "******************************ASV *********************\n");
	printk(KERN_INFO "ASV**** arm_index %d, arm_volt %d\n",
			asv_arm_index, asv_arm_volt);
	printk(KERN_INFO "ASV**** int_index %d, int_volt %d\n",
			asv_int_index, asv_int_volt);
}

#endif

static int s5pv310_update_dvfs_table(void)
{
	unsigned int i, j;
	int ret = 0;

	/* Get the maximum arm clock */
	s5pv310_max_armclk = s5pv310_freq_table[0].frequency;
	printk(KERN_INFO "armclk set max %d \n", s5pv310_max_armclk);

	if (s5pv310_max_armclk < 0) {
		printk(KERN_ERR "Fail to get max armclk infomatioin.\n");
		s5pv310_max_armclk  = 0; /* 1000MHz as default value */
		ret = -EINVAL;
	}
#if 0
	switch (s5pv310_max_armclk) {
	case 1200000:
		printk(KERN_INFO "armclk set max 1200MHz as default@@@@@\n");
		break;

	case 0:
	case 1000000:
	default:
		s5pv310_max_armclk = ARMCLOCK_1000MHZ;
		printk(KERN_INFO "@@@@@ armclk set max 1000MHz @@@@@\n");
		/*
		 *  Prepare to dvfs table to work maximum 1000MHz
		 *
		 *  Copy freq_table, volt_table, apll_pms_table, clk_div0_table,
		 * and clk_div1_table from lists of lookup table.
		*/
		for (i = 1; i < CPUFREQ_LEVEL_END; i++) {
			s5pv310_freq_table[i-1].index = s5pv310_lookup_freq_table[i].index - 1;
			s5pv310_freq_table[i-1].frequency = s5pv310_lookup_freq_table[i].frequency;
			printk(KERN_INFO "index = %d, frequency = %d\n",
				s5pv310_freq_table[i-1].index, s5pv310_freq_table[i-1].frequency);
		}

		for (i = 1; i < CPUFREQ_LEVEL_END; i++) {
			s5pv310_volt_table[i-1].index = s5pv310_lookup_volt_table[i].index - 1;
			s5pv310_volt_table[i-1].arm_volt = s5pv310_lookup_volt_table[i].arm_volt;
			printk(KERN_INFO "index = %d, arm_volt = %d\n",
				 s5pv310_volt_table[i-1].index, s5pv310_volt_table[i-1].arm_volt);
		}

		for (i = 1; i < CPUFREQ_LEVEL_END; i++) {
			s5pv310_apll_pms_table[i-1] = s5pv310_lookup_apll_pms_table[i];
			printk(KERN_INFO "apll pms_table = 0x%08x\n", s5pv310_apll_pms_table[i-1]);
		}

		for (i = 1; i < CPUFREQ_LEVEL_END; i++) {
			for (j = 0; j < 7; j++) {
				clkdiv_cpu0[i-1][j] = clkdiv_cpu0_lookup[i][j];
				printk("%d, ", clkdiv_cpu0[i-1][j]);
			}
			printk("\n");
		}

		for (i = 1; i < CPUFREQ_LEVEL_END; i++) {
			for (j = 0; j < 2; j++) {
				clkdiv_cpu1[i-1][j] = clkdiv_cpu1_lookup[i][j];
				printk("%d, ", clkdiv_cpu1[i-1][j]);
			}
			printk("\n");
		}
		printk(KERN_INFO "@@@@@ updated dvfs table @@@@@@\n");
		break;
	}
#endif
	return ret;
}

static int __init s5pv310_cpufreq_init(void)
{
	int i;

	printk(KERN_INFO "++ %s\n", __func__);

	arm_clk = clk_get(NULL, "armclk");
	if (IS_ERR(arm_clk))
		return PTR_ERR(arm_clk);

	moutcore = clk_get(NULL, "moutcore");
	if (IS_ERR(moutcore))
		goto out;

	mout_mpll = clk_get(NULL, "mout_mpll");
	if (IS_ERR(mout_mpll))
		goto out;

	mout_apll = clk_get(NULL, "mout_apll");
	if (IS_ERR(mout_apll))
		goto out;

	sclk_dmc = clk_get(NULL, "sclk_dmc");
	if (IS_ERR(sclk_dmc))
		goto out;

#if defined(CONFIG_REGULATOR)
	arm_regulator = regulator_get(NULL, "vdd_arm");
	if (IS_ERR(arm_regulator)) {
		printk(KERN_ERR "failed to get resource %s\n", "vdd_arm");
		goto out;
	}
	int_regulator = regulator_get(NULL, "vdd_int");
	if (IS_ERR(int_regulator)) {
		printk(KERN_ERR "failed to get resource %s\n", "vdd_int");
		goto out;
	}
	s5pv310_dvs_locking = 0;
#ifdef HAVE_DAC
	s5pv310_dac_init();
#endif
#endif

#ifdef CONFIG_CPU_S5PV310_EVT1
	/*
	 * By getting chip information from pkg_id & pro_id register,
	 * adujst dvfs level, update clk divider, voltage table,
	 * apll pms value of dvfs table.
	*/
	if (s5pv310_update_dvfs_table() < 0)
		printk(KERN_INFO "arm clock limited to maximum 1000MHz.\n");
#endif

#ifdef CONFIG_S5PV310_BUSFREQ
	up_threshold = UP_THRESHOLD_DEFAULT;
	cpu.cpu_hw_base = S5PV310_VA_PPMU_CPU;
	dmc[DMC0].dmc_hw_base = S5P_VA_DMC0;
	dmc[DMC1].dmc_hw_base = S5P_VA_DMC1;

	busfreq_ppmu_init();
	cpu_ppmu_init();

	for (i = 0; i < DVFS_LOCK_ID_END; i++)
		g_busfreq_lock_val[i] = BUSFREQ_MIN_LEVEL;

#endif
	for (i = 0; i < DVFS_LOCK_ID_END; i++)
		g_cpufreq_lock_val[i] = CPUFREQ_MIN_LEVEL;

	register_pm_notifier(&s5pv310_cpufreq_notifier);
	register_reboot_notifier(&s5pv310_cpufreq_reboot_notifier);

	s5pv310_cpufreq_init_done = true;

#ifdef CONFIG_S5PV310_ASV
	asv_info.asv_init_done = 0;
	if (s5pv310_asv_table_update())
		return -EINVAL;

	s5pv310_asv_set_voltage();
#endif
	printk(KERN_INFO "-- %s\n", __func__);
	return cpufreq_register_driver(&s5pv310_driver);

out:
	if (!IS_ERR(arm_clk))
		clk_put(arm_clk);

	if (!IS_ERR(moutcore))
		clk_put(moutcore);

	if (!IS_ERR(mout_mpll))
		clk_put(mout_mpll);

	if (!IS_ERR(mout_apll))
		clk_put(mout_apll);

#ifdef CONFIG_REGULATOR
	if (!IS_ERR(arm_regulator))
		regulator_put(arm_regulator);

	if (!IS_ERR(int_regulator))
		regulator_put(int_regulator);
#endif
	printk(KERN_ERR "%s: failed initialization\n", __func__);
	return -EINVAL;
}

late_initcall(s5pv310_cpufreq_init);

static ssize_t show_busfreq_fix(struct device *dev,
				struct device_attribute *attr,
				char *buf)
{
	if (!busfreq_fix)
		return sprintf(buf, "Busfreq is NOT fixed\n");
	else
		return sprintf(buf, "Busfreq is Fixed\n");
}

static ssize_t store_busfreq_fix(struct device *dev,
				struct device_attribute *attr,
				const char *buf,
				size_t count)
{
	int ret;
	ret = sscanf(buf, "%u", &busfreq_fix);

	if (ret != 1)
		return -EINVAL;

	return count;
}

static DEVICE_ATTR(busfreq_fix, 0644, show_busfreq_fix, store_busfreq_fix);

static ssize_t show_fix_busfreq_level(struct device *dev,
					struct device_attribute *attr,
					char *buf)
{
	if (!busfreq_fix)
		return sprintf(buf,
		"busfreq level fix is only available in busfreq_fix state\n");
	else
		return sprintf(buf, "BusFreq Level L%u\n", fix_busfreq_level);
}

static ssize_t store_fix_busfreq_level(struct device *dev,
					struct device_attribute *attr,
					const char *buf,
					size_t count)
{
	int ret;
	if (!busfreq_fix) {
		printk(KERN_ERR
		"busfreq level fix is only avaliable in Busfreq Fix state\n");
		return count;
	} else {
		ret = sscanf(buf, "%u", &fix_busfreq_level);
		if (ret != 1)
			return -EINVAL;
		if ((fix_busfreq_level < 0) ||
				(fix_busfreq_level >= BUS_LEVEL_END)) {
			printk(KERN_INFO "Fixing Busfreq level is invalid\n");
			return count;
		}
		if (pre_fix_busfreq_level >= fix_busfreq_level)
#if defined(CONFIG_REGULATOR)
			regulator_set_voltage(int_regulator,
				s5pv310_busfreq_table[fix_busfreq_level].volt,
				s5pv310_busfreq_table[fix_busfreq_level].volt);
#endif
		s5pv310_set_busfreq(fix_busfreq_level);

		if (pre_fix_busfreq_level < fix_busfreq_level)
#if defined(CONFIG_REGULATOR)
			regulator_set_voltage(int_regulator,
				s5pv310_busfreq_table[fix_busfreq_level].volt,
				s5pv310_busfreq_table[fix_busfreq_level].volt);
#endif
		pre_fix_busfreq_level = fix_busfreq_level;

		return count;
	}
}

static DEVICE_ATTR(fix_busfreq_level, 0644, show_fix_busfreq_level,
				store_fix_busfreq_level);

#ifdef SYSFS_DEBUG_BUSFREQ
static ssize_t show_time_in_state(struct device *dev,
				struct device_attribute *attr,
				char *buf)
{
	ssize_t len = 0;
	int i;

	for (i = 0; i < LV_END; i++)
		len += sprintf(buf + len, "%u: %u\n",
			s5pv310_busfreq_table[i].mem_clk, time_in_state[i]);

	return len;
}

static ssize_t store_time_in_state(struct device *dev,
				struct device_attribute *attr,
				const char *buf,
				size_t count)
{

	return count;
}

static DEVICE_ATTR(time_in_state, 0644, show_time_in_state,
				store_time_in_state);

static ssize_t show_up_threshold(struct device *dev,
				struct device_attribute *attr,
				char *buf)
{
	return sprintf(buf, "%u\n", up_threshold);
}

static ssize_t store_up_threshold(struct device *dev,
				struct device_attribute *attr,
				const char *buf,
				size_t count)
{
	int ret;

	ret = sscanf(buf, "%u", &up_threshold);
	if (ret != 1)
		return -EINVAL;
	printk(KERN_ERR "** Up_Threshold is changed to %u **\n", up_threshold);
	return count;
}

static DEVICE_ATTR(up_threshold, 0644, show_up_threshold, store_up_threshold);
#endif

static int sysfs_busfreq_create(struct device *dev)
{
	int ret;

	ret = device_create_file(dev, &dev_attr_busfreq_fix);

	if (ret)
		return ret;

	ret = device_create_file(dev, &dev_attr_fix_busfreq_level);

	if (ret)
		goto failed;

#ifdef SYSFS_DEBUG_BUSFREQ
	ret = device_create_file(dev, &dev_attr_up_threshold);

	if (ret)
		goto failed_fix_busfreq_level;

	ret = device_create_file(dev, &dev_attr_time_in_state);

	if (ret)
		goto failed_up_threshold;

    return ret;

failed_up_threshold:
	device_remove_file(dev, &dev_attr_up_threshold);
failed_fix_busfreq_level:
	device_remove_file(dev, &dev_attr_fix_busfreq_level);
#else
    return ret;
#endif
failed:
	device_remove_file(dev, &dev_attr_busfreq_fix);

	return ret;
}

static struct platform_device s5pv310_busfreq_device = {
	.name	= "s5pv310-busfreq",
	.id	= -1,
};

static int __init s5pv310_busfreq_device_init(void)
{
	int ret;

	ret = platform_device_register(&s5pv310_busfreq_device);

	if (ret) {
		printk(KERN_ERR "failed at(%d)\n", __LINE__);
		return ret;
	}

	ret = sysfs_busfreq_create(&s5pv310_busfreq_device.dev);

	if (ret) {
		printk(KERN_ERR "failed at(%d)\n", __LINE__);
		goto sysfs_err;
	}

	printk(KERN_INFO "s5pv310_busfreq_device_init: %d\n", ret);

	return ret;
sysfs_err:

	platform_device_unregister(&s5pv310_busfreq_device);
	return ret;
}

late_initcall(s5pv310_busfreq_device_init);

ssize_t set_freq_table(unsigned char step, unsigned int freq)
{
	int pll;
	//TODO: do not change freq during transition
	switch(step)
	{
		case 0: //1200
			if(freq >= 1452 || freq <= 1000) return -EINVAL;
			pll = (int)(freq / 8);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(3<<8)|(0x1));
			s5pv310_freq_table[step].frequency = pll * 8 * 1000;	
			break;
		case 1: //1000
			if(freq >= 1200 || freq <=  800) return -EINVAL;
			pll = (int)(freq / 4);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(6<<8)|(0x1));
			s5pv310_freq_table[step].frequency = pll * 4 * 1000;	
			break;
		case 2: //800
			if(freq >= 1000 || freq <  500) return -EINVAL;
			pll = (int)(freq / 4);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(6<<8)|(0x1));
			s5pv310_freq_table[step].frequency = pll * 4 * 1000;	
			break;
		case 3: //500
			if(freq >=  800 || freq <=  200) return -EINVAL;
			pll = (int)(freq / 2);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(6<<8)|(0x2));
			s5pv310_freq_table[step].frequency = pll * 2 * 1000;	
			break;
		case 4: //200
			if(freq >=  500 || freq <=  100) return -EINVAL;
			pll = (int)(freq / 1);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(6<<8)|(0x3));
			s5pv310_freq_table[step].frequency = pll * 1 * 1000;	
			break;
		case 5: //100
			if(freq >   200 || freq <   25) return -EINVAL;
			pll = (int)(freq * 2);
			s5pv310_apll_pms_table[step] =	((pll<<16)|(6<<8)|(0x4));
			s5pv310_freq_table[step].frequency = pll * 500;	
			break;
		default:
			return -EINVAL;
	}
	return 1;
}

ssize_t show_freq_table(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d %d %d %d %d %d", 
		s5pv310_freq_table[0].frequency/1000,s5pv310_freq_table[1].frequency/1000,
		s5pv310_freq_table[2].frequency/1000,s5pv310_freq_table[3].frequency/1000,
		s5pv310_freq_table[4].frequency/1000,s5pv310_freq_table[5].frequency/1000);
}


ssize_t store_freq_table(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {

	unsigned int ret = -EINVAL;
	int i = 0,max,min,u[6];
	ret = sscanf(buf, "%d %d %d %d %d %d", 
		&u[0], &u[1], &u[2], &u[3], &u[4], &u[5]);
	if(ret != 6)
		return -EINVAL;
	for(i=0;i<5;i++)
		if(u[i]<=u[i+1]) return -EINVAL;
/*
	((250<<16)|(6<<8)|(0x1)),
	((200<<16)|(6<<8)|(0x1)),
	((250<<16)|(6<<8)|(0x2)),
	((200<<16)|(6<<8)|(0x3)),
	((100<<16)|(6<<8)|(0x3)),*/
	for(i=0;i<CPUFREQ_LEVEL_END;i++) if(s5pv310_freq_table[i].frequency==policy->max) break;
	max = i;
	for(i=0;i<CPUFREQ_LEVEL_END;i++) if(s5pv310_freq_table[i].frequency==policy->min) break;
	min = i;
	policy->max = policy->min = s5pv310_freq_table[L2].frequency;

	for(i=0;i<8;i++) {
		if(set_freq_table(i, u[i])!=-EINVAL) 
			siyah_freq_table[i] = u[i];
	}

	policy->max = s5pv310_freq_table[max].frequency;
	policy->min = s5pv310_freq_table[min].frequency;
	policy->cpuinfo.max_freq = s5pv310_freq_table[0].frequency;
	policy->cpuinfo.min_freq = s5pv310_freq_table[5].frequency;

	return count;
}

ssize_t show_UV_mV_table(struct cpufreq_policy *policy, char *buf) {

      return sprintf(buf, 
"%dmhz: %d mV\n%dmhz: %d mV\n%dmhz: %d mV\n%dmhz: %d mV\n\
%dmhz: %d mV\n%dmhz: %d mV\n", 
		s5pv310_freq_table[0].frequency/1000,exp_UV_mV[0]/1000,
		s5pv310_freq_table[1].frequency/1000,exp_UV_mV[1]/1000,
		s5pv310_freq_table[2].frequency/1000,exp_UV_mV[2]/1000,
		s5pv310_freq_table[3].frequency/1000,exp_UV_mV[3]/1000,
		s5pv310_freq_table[4].frequency/1000,exp_UV_mV[4]/1000,
		s5pv310_freq_table[5].frequency/1000,exp_UV_mV[5]/1000);
}

#define VREF_SEL     1	/* 0: 0.625V (50mV step), 1: 0.3125V (25mV step). */
#define V_STEP       (25 * (2 - VREF_SEL)) /* Minimum voltage step size. */
#define VREG_DATA    (VREG_CONFIG | (VREF_SEL << 5))
#define VREG_CONFIG  (BIT(7) | BIT(6)) /* Enable VREG, pull-down if disabled. */
/* Cause a compile error if the voltage is not a multiple of the step size. */
#define MV(mv)      ((mv) / (!((mv) % V_STEP)))

ssize_t acpuclk_get_vdd_levels_str(char *buf)
{
	int i, len = 0;
	
	if (buf)
	{
		for (i = 0; i<CPUFREQ_LEVEL_END; i++)
		{
			len += sprintf(buf + len, "%8u: %4d\n", s5pv310_freq_table[i].frequency, exp_UV_mV[i]);
		}
	}

	return len;
}

void acpuclk_set_vdd(unsigned int khz, int vdd)
{
	int i;
	unsigned int new_vdd;
	vdd = vdd / V_STEP * V_STEP;
	
	for (i = 0; i<CPUFREQ_LEVEL_END; i++)
	{
		if (khz == 0)
			new_vdd = min(max((exp_UV_mV[i] + vdd), CPU_UV_MV_MIN), CPU_UV_MV_MAX);
		else if (s5pv310_freq_table[i].frequency == khz)
			new_vdd = min(max((unsigned int)vdd, CPU_UV_MV_MIN), CPU_UV_MV_MAX);
		else continue;
		
		exp_UV_mV[i] = new_vdd;
	}
}

ssize_t store_UV_mV_table(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {

      unsigned int ret = -EINVAL;
      int i = 0;
	  int u[6];
      ret = sscanf(buf, "%d %d %d %d %d %d", &u[0], &u[1], &u[2], &u[3], &u[4], &u[5]);
	  if(ret != 6) {
	      ret = sscanf(buf, "%d %d %d %d %d", &u[0], &u[1], &u[2], &u[3], &u[4]);
		  if(ret != 5) {
		      ret = sscanf(buf, "%d %d %d %d", &u[1], &u[2], &u[3], &u[4]);
			  if( ret != 4) return -EINVAL;
		  }
	  }
		for( i = 0; i < CPUFREQ_LEVEL_END; i++ )
		{
			if (u[i] > CPU_UV_MV_MAX / 1000)
			{
				u[i] = CPU_UV_MV_MAX / 1000;
			}
			else if (u[i] < CPU_UV_MV_MIN / 1000)
			{
				u[i] = CPU_UV_MV_MIN / 1000;
			}
		}
		if(ret >= 5) exp_UV_mV[0] = u[0] * 1000;
		exp_UV_mV[1] = u[1] * 1000; 
		exp_UV_mV[2] = u[2] * 1000;
		exp_UV_mV[3] = u[3] * 1000; 
		exp_UV_mV[4] = u[4] * 1000;
		if(ret == 6) exp_UV_mV[5] = u[5] * 1000;
		return count;
}

ssize_t show_cpu_class(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "ids_arm: %d hpm_class: %d",ids_arm, hpm_code);
}

ssize_t show_busfreq_static(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, 
"%dmhz: %d\n%dmhz: %d\n%dmhz: %d\n%dmhz: %d\n\
%dmhz: %d\n%dmhz: %d\n\n\
bus frequency static mode: %s\n\
You can echo the frequency step values to this file or 'enable'/'disable' status.\n\
Valid frequency step values are: 0 (400MHz), 1 (266MHz), 2 (133MHz)", 
		s5pv310_freq_table[0].frequency/1000,busfreq_static_level[0],
		s5pv310_freq_table[1].frequency/1000,busfreq_static_level[1],
		s5pv310_freq_table[2].frequency/1000,busfreq_static_level[2],
		s5pv310_freq_table[3].frequency/1000,busfreq_static_level[3],
		s5pv310_freq_table[4].frequency/1000,busfreq_static_level[4],
		s5pv310_freq_table[5].frequency/1000,busfreq_static_level[5],
		(is_busfreq_static ? "enabled" : "disabled")
		);
}

ssize_t store_busfreq_static(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {

	unsigned int ret = -EINVAL;
	int i = 0,u[6];
	
	if(!strncmp(buf,"enabled",5)) {
		is_busfreq_static = 1;
		return count;
	}
	if(!strncmp(buf,"disabled",6)) {
		is_busfreq_static = 0;
		return count;
	}
	
	ret = sscanf(buf, "%d %d %d %d %d %d", 
		&u[0], &u[1], &u[2], &u[3], &u[4], &u[5]);
	if(ret != 6)
		return -EINVAL;
	for(i=0;i<6;i++) {
		if(u[i]>2 || u[i]<0) return -EINVAL;
	}
	for(i=0;i<6;i++) {
		busfreq_static_level[i] = u[i];
	}
	return count;
}

extern int deepsleep_cpulevel;
ssize_t show_deepsleep_cpulevel(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d\n", deepsleep_cpulevel);
}

ssize_t store_deepsleep_cpulevel(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {

	unsigned int ret = -EINVAL, level;

	ret = sscanf(buf, "%d", &level);
	if(ret!=1) return -EINVAL;
	if(level <L2 || level>L5) return -EINVAL;
	deepsleep_cpulevel = level;
	return count;
}
extern int deepsleep_buslevel;
ssize_t show_deepsleep_buslevel(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d\n", deepsleep_buslevel);
}
ssize_t store_deepsleep_buslevel(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {
	unsigned int ret = -EINVAL, level;
	ret = sscanf(buf, "%d", &level);
	if(ret!=1) return -EINVAL;
	if(level<0 || level>2) return -EINVAL;
	deepsleep_buslevel = level;
	return count;
}

extern int smooth_step;
ssize_t show_smooth_step(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d\n", smooth_step);
}
ssize_t store_smooth_step(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {
	unsigned int ret = -EINVAL, level;
	ret = sscanf(buf, "%d", &level);
	if(ret!=1) return -EINVAL;
	if(level<0 || level>4) return -EINVAL;
	smooth_step = level;
	return count;
}
extern int smooth_target;
ssize_t show_smooth_target(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d\n", smooth_target);
}
ssize_t store_smooth_target(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {
	unsigned int ret = -EINVAL, level;
	ret = sscanf(buf, "%d", &level);
	if(ret!=1) return -EINVAL;
	if(level<0 || level>5) return -EINVAL;
	smooth_target = level;
	return count;
}
extern int smooth_offset;
ssize_t show_smooth_offset(struct cpufreq_policy *policy, char *buf) {
      return sprintf(buf, "%d\n", smooth_offset);
}
ssize_t store_smooth_offset(struct cpufreq_policy *policy,
                                      const char *buf, size_t count) {
	unsigned int ret = -EINVAL, level;
	ret = sscanf(buf, "%d", &level);
	if(ret!=1) return -EINVAL;
	if(level<0 || level>4) return -EINVAL;
	smooth_offset = level;
	return count;
}