aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-sgh-i900/sgh-i900-support.patch
diff options
context:
space:
mode:
authorMartin Jansa <Martin.Jansa@gmail.com>2009-12-15 14:46:11 +0100
committerMartin Jansa <Martin.Jansa@gmail.com>2009-12-15 14:46:11 +0100
commit58884fc059504abafc9126613a7049464ba701e7 (patch)
tree61030608675a792923aff657516c48a6a2048693 /recipes/linux/linux-sgh-i900/sgh-i900-support.patch
parent75820363234db9d6335997658c6f606654d1588c (diff)
parent47d6cb217ff161738488f4f82af9dd8f8c616ce0 (diff)
downloadopenembedded-58884fc059504abafc9126613a7049464ba701e7.tar.gz
Merge commit 'origin/org.openembedded.dev' into martin_jansa/xorg-7.5
Conflicts: conf/distro/include/angstrom-2008-preferred-versions.inc
Diffstat (limited to 'recipes/linux/linux-sgh-i900/sgh-i900-support.patch')
-rw-r--r--recipes/linux/linux-sgh-i900/sgh-i900-support.patch13031
1 files changed, 13031 insertions, 0 deletions
diff --git a/recipes/linux/linux-sgh-i900/sgh-i900-support.patch b/recipes/linux/linux-sgh-i900/sgh-i900-support.patch
new file mode 100644
index 0000000000..28d65938a1
--- /dev/null
+++ b/recipes/linux/linux-sgh-i900/sgh-i900-support.patch
@@ -0,0 +1,13031 @@
+diff -ur linux-2.6.32/arch/arm/Kconfig kernel/arch/arm/Kconfig
+--- linux-2.6.32/arch/arm/Kconfig 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/Kconfig 2009-12-12 16:09:25.656278659 +0200
+@@ -1502,6 +1502,112 @@
+ config ARCH_SUSPEND_POSSIBLE
+ def_bool y
+
++config PXA_DVFM
++ bool "PXA Processor High Level DVFM support"
++ depends on PM
++ default y
++ help
++ This enables the dynamical frequency and voltage changes framework
++ for PXA Processor series.
++
++config PXA_MIPSRAM
++ bool "PXA MIPSRAM monitoring support"
++ default n
++ help
++ Enable MIPS RAM monitoring for process switching implemented in
++ the scheduler
++
++config PXA3xx_DVFM
++ bool "PXA3xx Processor DVFM support"
++ depends on PM && PXA3xx && PXA_DVFM
++# select PXA3xx_ARAVA
++# select PXA3xx_MICCO
++ default y
++ help
++ This implements the dynamical frequency and voltage changes features
++ for PXA3xx Processor particularly.
++
++config PXA3xx_DVFM_STATS
++ bool "PXA3xx/PXA930 Processor DVFM Statistics support"
++ depends on PXA3xx_DVFM
++ select RELAY
++ select DEBUG_FS
++ default y
++ help
++ This is used to collect statistics during the dynamic frequency
++ and voltage changes
++
++config PXA3xx_PMU
++ bool "PXA3xx/PXA930 Processor PMU support"
++ default y
++ help
++ PXA3xx/PXA930 provide Performance Monitor Unit to report
++ CPU statistics info.
++
++config PXA3xx_PRM
++ bool "PXA3xx Processor Profiler Resource Manager"
++ depends on PXA3xx_DVFM && PXA3xx_PMU
++ default y
++ help
++ This enables the PXA3xx Processor Profiler Resource Manager
++
++config IPM
++ bool "Marvell(R) Scalable Power Management Profiler"
++ depends on PXA3xx_PRM
++ default y
++ help
++ Support Profiler of Marvell(R) Scalable Power Management
++
++config IPMC
++ bool "Marvell(R) Scalable Power Management Userspace Daemon"
++ depends on PXA3xx_PRM
++ default n
++ help
++ Support Userspace Daemon of Marvell(R) Scalable Power Management
++
++config BPMD
++ bool "Borqs Scalable Power Management Kernel Daemon"
++ depends on PXA3xx_PRM
++ default y
++ help
++ Kernel Daemon of Borqs Scalable Power Management
++
++config TEST_BPMD
++ bool "Borqs Scalable Power Management Test Module"
++ depends on PXA3xx_PRM
++ default y
++ help
++ Test Module of Borqs Scalable Power Management
++
++config IPM_DEEPIDLE
++ bool "PXA3xx/PXA930 Processor Deep Idle support"
++ depends on IPM
++ default y
++ help
++ This enables the kernel support for PXA3xx/PXA930
++ Processor Deep Idle (D0CS Idle)
++
++config IPM_D2IDLE
++ bool "Support PXA3xx/PXA930 Processor D2 Mode as Idle"
++ depends on IPM && PXA_32KTIMER
++ default y
++ help
++ This enables kernel support PXA3xx/PXA930 D2 idle
++
++config PERIPHERAL_STATUS
++ bool "Support list peripheral status of pm"
++ depends on PM
++ default y
++ help
++ This enables kernel support peripheral status calculate
++
++config IPM_CGIDLE
++ bool "Support PXA935 Processor Clock Gated Mode as Idle"
++ depends on IPM && PXA_32KTIMER
++ default y
++ help
++ This enables kernel support PXA935 D2 idle
++
+ endmenu
+
+ source "net/Kconfig"
+diff -ur linux-2.6.32/arch/arm/mach-pxa/Kconfig kernel/arch/arm/mach-pxa/Kconfig
+--- linux-2.6.32/arch/arm/mach-pxa/Kconfig 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/mach-pxa/Kconfig 2009-12-12 16:09:26.426281936 +0200
+@@ -27,6 +27,12 @@
+ bool "PXA950 (codename Tavor-PV2)"
+ select CPU_PXA930
+
++config PXA3xx_PMIC
++ bool "PXA3xx PMIC support"
++ default y
++ help
++ PMIC support
++
+ endmenu
+
+ endif
+@@ -303,6 +309,18 @@
+ select HAVE_PWM
+ select PXA_HAVE_BOARD_IRQS
+
++config MACH_SGH_I900
++ bool "Samsung SGH-i900 (Omnia) phone"
++ select PXA3xx
++ select CPU_PXA310
++ select HAVE_PWM
++
++config MACH_SGH_I780
++ bool "Samsung SGH-i780 phone"
++ select PXA3xx
++ select CPU_PXA310
++ select HAVE_PWM
++
+ config MACH_LITTLETON
+ bool "PXA3xx Form Factor Platform (aka Littleton)"
+ select PXA3xx
+diff -ur linux-2.6.32/arch/arm/mach-pxa/Makefile kernel/arch/arm/mach-pxa/Makefile
+--- linux-2.6.32/arch/arm/mach-pxa/Makefile 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/mach-pxa/Makefile 2009-12-12 16:09:26.426281936 +0200
+@@ -5,6 +5,15 @@
+ # Common support (must be linked before board specific support)
+ obj-y += clock.o devices.o generic.o irq.o \
+ time.o reset.o
++obj-$(CONFIG_PXA_DVFM) += dvfm.o
++ifeq ($(CONFIG_PXA3xx), y)
++ obj-$(CONFIG_PXA3xx_PMIC) += pxa3xx_pmic.o
++ obj-$(CONFIG_PXA3xx_DVFM) += pxa3xx_dvfm.o pxa3xx_dvfm_ll.o
++ obj-$(CONFIG_PXA3xx_PMU) += pmu.o pmu_ll.o
++ obj-$(CONFIG_PXA3xx_PRM) += prm.o
++ obj-$(CONFIG_BPMD) += bpm.o bpm_prof.o
++endif
++
+ obj-$(CONFIG_PM) += pm.o sleep.o standby.o
+
+ ifeq ($(CONFIG_CPU_FREQ),y)
+@@ -66,6 +75,8 @@
+ obj-$(CONFIG_MACH_PALMZ72) += palmz72.o
+ obj-$(CONFIG_MACH_TREO680) += treo680.o
+ obj-$(CONFIG_ARCH_VIPER) += viper.o
++obj-$(CONFIG_MACH_SGH_I900) += sgh_i780_i900.o sgh_smd.o sgh_rpc.o
++obj-$(CONFIG_MACH_SGH_I780) += sgh_i780_i900.o sgh_smd.o sgh_rpc.o
+
+ ifeq ($(CONFIG_MACH_ZYLONITE),y)
+ obj-y += zylonite.o
+diff -ur linux-2.6.32/arch/arm/mach-pxa/bpm.c kernel/arch/arm/mach-pxa/bpm.c
+--- linux-2.6.32/arch/arm/mach-pxa/bpm.c 2009-12-13 12:57:59.831957275 +0200
++++ kernel/arch/arm/mach-pxa/bpm.c 2009-12-12 16:09:26.429614458 +0200
+@@ -0,0 +1,1814 @@
++/*
++ * linux/arch/arm/mach-pxa/bpm.c
++ *
++ * Provide bpm thread to scale system voltage & frequency dynamically.
++ *
++ * Copyright (C) 2008 Borqs Corporation.
++ *
++ * Author: Emichael Li <emichael.li@borqs.com>
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++ *
++ */
++
++#include <linux/kernel.h>
++#include <mach/prm.h>
++#include <mach/dvfm.h>
++#include <mach/mspm_prof.h>
++#include <linux/sysdev.h>
++#include <linux/delay.h>
++#include <mach/bpm.h>
++#include <mach/hardware.h>
++#include <mach/pxa3xx-regs.h>
++#include <linux/list.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++#include <linux/freezer.h>
++#include <mach/regs-ost.h>
++#ifdef CONFIG_ANDROID_POWER
++#include <linux/android_power.h>
++#endif
++
++#define DEBUG
++
++#ifdef DEBUG
++#define PM_BUG_ON(condition) \
++ do { \
++ if (unlikely(condition)) { \
++ printk(KERN_ERR "BUG: failure at %s:%d/%s()!\n", \
++ __FILE__, __LINE__, __FUNCTION__); \
++ WARN_ON(1); \
++ } \
++ } while(0)
++#define DPRINTK(fmt,args...) \
++ do { \
++ if (g_bpm_log_level) \
++ printk(KERN_ERR "%s: " fmt, __FUNCTION__ , ## args); \
++ } while (0)
++#else
++#define PM_BUG_ON(condition) \
++ do { \
++ if (unlikely(condition)) { \
++ printk(KERN_ERR "BUG: failure at %s:%d/%s()!\n", \
++ __FILE__, __LINE__, __FUNCTION__); \
++ } \
++ } while(0)
++#define DPRINTK(fmt,args...) \
++ do {} while (0)
++#endif
++
++/*****************************************************************************/
++/* */
++/* Policy variables */
++/* */
++/*****************************************************************************/
++#define REDUCE_624M_DUTYCYCLE (1)
++
++#define BPM_FREQ_POLICY_NUM (3)
++#define BPM_PROFILER_WINDOW (100)
++#define SYSTEM_BOOTUP_TIME (15000)
++#define BPM_MAX_OP_NUM (10)
++
++struct bpm_freq_bonus_arg {
++ int mips;
++ int mem_stall;
++};
++
++struct bpm_freq_policy {
++ int lower[BPM_FREQ_POLICY_NUM];
++ int higher[BPM_FREQ_POLICY_NUM];
++};
++
++#define CONSTRAINT_ID_LEN (32)
++struct bpm_cons {
++ struct list_head list;
++ char sid[CONSTRAINT_ID_LEN];
++ int count;
++ unsigned long ms;
++ unsigned long tmp_ms;
++ unsigned long tm;
++};
++
++struct bpm_cons_head {
++ struct list_head list;
++};
++
++/* manage all the ops which are supported by the hardware */
++static struct dvfm_op g_dyn_ops[BPM_MAX_OP_NUM];
++static spinlock_t g_dyn_ops_lock = SPIN_LOCK_UNLOCKED;
++
++static struct bpm_cons_head g_bpm_cons[BPM_MAX_OP_NUM];
++
++/* map the op from active ops to g_dyn_ops[] */
++static int g_active_ops_map[BPM_MAX_OP_NUM];
++static int g_active_ops_num;
++static int g_active_cur_idx = -1;
++static int g_prefer_op_idx;
++static int g_active_bonus[BPM_MAX_OP_NUM][BPM_MAX_OP_NUM * 2 - 1];
++struct bpm_freq_policy g_active_policy[BPM_MAX_OP_NUM];
++
++/*****************************************************************************/
++/* */
++/* Framework Supportted Variables */
++/* */
++/*****************************************************************************/
++
++int (*pipm_start_pmu) (void *) = NULL;
++EXPORT_SYMBOL(pipm_start_pmu);
++int (*pipm_stop_pmu)(void) = NULL;
++EXPORT_SYMBOL(pipm_stop_pmu);
++
++static int g_bpm_thread_exit;
++int g_bpm_enabled;
++static wait_queue_head_t g_bpm_enabled_waitq;
++
++static int g_profiler_window = BPM_PROFILER_WINDOW;
++static int g_bpm_log_level = 1;
++struct completion g_bpm_thread_over;
++
++extern struct sysdev_class cpu_sysdev_class;
++
++static struct bpm_event_queue g_bpm_event_queue;
++static spinlock_t g_bpm_event_queue_lock = SPIN_LOCK_UNLOCKED;
++
++#ifdef CONFIG_TEST_BPMD
++static int g_cpuload_mode;
++#endif
++
++static int dvfm_dev_idx;
++
++extern int __dvfm_enable_op(int index, int dev_idx);
++extern int __dvfm_disable_op2(int index, int dev_idx);
++extern int cur_op;
++extern struct info_head dvfm_trace_list;
++
++extern int g_dvfm_disabled;
++
++#ifdef CONFIG_MTD_NAND_HSS_FIX
++extern atomic_t nand_in_cmd;
++#endif
++/*****************************************************************************/
++/* */
++/* Blink Variables */
++/* */
++/*****************************************************************************/
++#define DVFM_BLINK_OWNER_LEN (16)
++
++struct dvfm_blink_info {
++ int time;
++ char name[DVFM_BLINK_OWNER_LEN];
++};
++
++static int g_dvfm_blink = 0;
++static struct timer_list g_dvfm_blink_timer;
++static struct dvfm_blink_info g_dvfm_binfo;
++static unsigned long g_dvfm_blink_timeout = 0;
++
++/*****************************************************************************/
++/* */
++/* android power interface */
++/* */
++/*****************************************************************************/
++static int g_android_suspended = 0;
++
++#ifdef CONFIG_ANDROID_POWER
++void bpm_android_suspend_handler(android_early_suspend_t *h)
++{
++ unsigned long flags;
++ local_irq_save(flags);
++ g_android_suspended = 1;
++ local_irq_restore(flags);
++}
++
++void bpm_android_resume_handler(android_early_suspend_t *h)
++{
++ unsigned long flags;
++ local_irq_save(flags);
++ g_android_suspended = 0;
++ local_irq_restore(flags);
++}
++
++static android_early_suspend_t bpm_early_suspend = {
++ .level = 98,
++ .suspend = bpm_android_suspend_handler,
++ .resume = bpm_android_resume_handler,
++};
++#endif
++
++static inline int is_out_d0cs(void)
++{
++#ifdef CONFIG_PXA3xx_DVFM
++ extern int out_d0cs;
++ return out_d0cs;
++#endif
++ return 0;
++}
++
++/*****************************************************************************/
++/* */
++/* BPMD Event Queue */
++/* */
++/*****************************************************************************/
++
++static int bpmq_init(void)
++{
++ g_bpm_event_queue.head = g_bpm_event_queue.tail = 0;
++ g_bpm_event_queue.len = 0;
++ init_waitqueue_head(&g_bpm_event_queue.waitq);
++ return 0;
++}
++
++static int bpmq_clear(void)
++{
++ unsigned long flag;
++
++ spin_lock_irqsave(&g_bpm_event_queue_lock, flag);
++
++ g_bpm_event_queue.head = g_bpm_event_queue.tail = 0;
++ g_bpm_event_queue.len = 0;
++
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++
++ return 0;
++}
++
++static int bpmq_get(struct bpm_event *e)
++{
++ unsigned long flag;
++
++ spin_lock_irqsave(&g_bpm_event_queue_lock, flag);
++
++ if (!g_bpm_event_queue.len) {
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++ printk(KERN_ERR "Logic error, please check bpmq_empty()\n");
++ return -1;
++ }
++ memcpy(e, g_bpm_event_queue.bpmes + g_bpm_event_queue.tail,
++ sizeof(struct bpm_event));
++ g_bpm_event_queue.len--;
++ g_bpm_event_queue.tail =
++ (g_bpm_event_queue.tail + 1) % MAX_BPM_EVENT_NUM;
++
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++
++ return 0;
++}
++
++static int bpmq_put(struct bpm_event *e)
++{
++ unsigned long flag;
++ static int err_cnt = 0;
++
++ if (unlikely(0 == g_bpm_enabled))
++ return 0;
++
++ spin_lock_irqsave(&g_bpm_event_queue_lock, flag);
++
++ if (g_bpm_event_queue.len == MAX_BPM_EVENT_NUM) {
++ if (++err_cnt > 0) {
++ printk(KERN_ERR "bpm queue over flow!\n");
++ show_state();
++ printk(KERN_ERR "send event many times instantly?");
++ dump_stack();
++ }
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++ return -1;
++ }
++ memcpy(g_bpm_event_queue.bpmes + g_bpm_event_queue.head, e,
++ sizeof(struct bpm_event));
++ g_bpm_event_queue.len++;
++ g_bpm_event_queue.head =
++ (g_bpm_event_queue.head + 1) % MAX_BPM_EVENT_NUM;
++
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++
++ wake_up_interruptible(&g_bpm_event_queue.waitq);
++
++ return 0;
++}
++
++static __inline int bpmq_empty(void)
++{
++ return (g_bpm_event_queue.len > 0) ? 0 : 1;
++}
++
++int bpm_event_notify(int type, int kind, void *info, unsigned int info_len)
++{
++ struct bpm_event event;
++ int len = 0;
++
++ if (info_len > INFO_SIZE)
++ len = INFO_SIZE;
++ else if ((info_len < INFO_SIZE) && (info_len > 0))
++ len = info_len;
++ memset(&event, 0, sizeof(struct bpm_event));
++ event.type = type;
++ event.kind = kind;
++ if ((len > 0) && (info != NULL)) {
++ memcpy(event.info, info, len);
++ }
++ if (0 != bpmq_put(&event)) {
++ len = -1;
++ }
++
++/* DPRINTK("type: %d kind: %d, len(ret): %d\n", type, kind, len); */
++ return len;
++}
++
++EXPORT_SYMBOL(bpm_event_notify);
++
++/*****************************************************************************/
++/* */
++/* BPMD PMU Interface */
++/* */
++/*****************************************************************************/
++
++static int bpm_start_pmu(void)
++{
++ int ret = -ENXIO;
++ struct ipm_profiler_arg pmu_arg;
++
++ if (pipm_start_pmu != NULL) {
++ pmu_arg.size = sizeof(struct ipm_profiler_arg);
++/* pmu_arg.flags = IPM_IDLE_PROFILER | IPM_PMU_PROFILER; */
++ pmu_arg.flags = IPM_IDLE_PROFILER;
++ pmu_arg.window_size = g_profiler_window;
++
++ pmu_arg.pmn0 = PXA3xx_EVENT_EXMEM;
++ pmu_arg.pmn1 = PXA3xx_EVENT_DMC_NOT_EMPTY;
++ pmu_arg.pmn2 = PMU_EVENT_POWER_SAVING;
++ pmu_arg.pmn3 = PMU_EVENT_POWER_SAVING;
++
++ ret = pipm_start_pmu(&pmu_arg);
++ } else {
++ printk(KERN_CRIT "No profiler\n");
++ PM_BUG_ON(1);
++ }
++
++ return ret;
++}
++
++static int bpm_stop_pmu(void)
++{
++ pipm_stop_pmu();
++ return 0;
++}
++
++/*****************************************************************************/
++/* */
++/* BPMD POLICY */
++/* */
++/*****************************************************************************/
++
++static int bpm_dump_policy(void)
++{
++#define TMP_BUF_SIZE (4096)
++ int i, j;
++ char *buf = kmalloc(TMP_BUF_SIZE, GFP_KERNEL);
++ char *s = NULL;
++
++ if (NULL == buf) {
++ printk(KERN_ERR "Can not alloc memory\n");
++ return 0;
++ }
++
++ s = buf;
++ memset(s, 0, TMP_BUF_SIZE);
++
++ s += sprintf(s, "--------------BPM DUMP POLICY BEGIN--------------\n");
++ s += sprintf(s, "dyn_boot_op = %d\n", dvfm_get_defop());
++ s += sprintf(s, "g_active_ops_maps:\n");
++
++ for (i = 0; i < BPM_MAX_OP_NUM; ++i)
++ s += sprintf(s, "%8d ", g_active_ops_map[i]);
++ s += sprintf(s, "\n");
++
++ s += sprintf(s, "g_active_ops_num: %d\n", g_active_ops_num);
++ s += sprintf(s, "g_active_cur_idx: %d\n", g_active_cur_idx);
++
++ s += sprintf(s, "g_active_policy:\n");
++ for (i = 0; i < BPM_MAX_OP_NUM; ++i) {
++ for (j = 0; j < BPM_FREQ_POLICY_NUM; ++j) {
++ s += sprintf(s, "%8d ", g_active_policy[i].lower[j]);
++ }
++
++ for (j = 0; j < BPM_FREQ_POLICY_NUM; ++j) {
++ s += sprintf(s, "%8d ", g_active_policy[i].higher[j]);
++ }
++ s += sprintf(s, "\n");
++ }
++
++ DPRINTK("%s", buf);
++
++ s = buf;
++ memset(s, 0, TMP_BUF_SIZE);
++
++ s += sprintf(s, "g_active_bonus:\n");
++ for (i = 0; i < BPM_MAX_OP_NUM; ++i) {
++ for (j = 0; j < BPM_MAX_OP_NUM * 2 - 1; ++j) {
++ s += sprintf(s, "%8d ", g_active_bonus[i][j]);
++ }
++ s += sprintf(s, "\n");
++ }
++
++ DPRINTK("%s", buf);
++
++ s = buf;
++ memset(s, 0, TMP_BUF_SIZE);
++
++ s += sprintf(s, "g_dyn_ops num: %d\n",
++ sizeof(g_dyn_ops) / sizeof(struct dvfm_op));
++
++ s += sprintf(s, "g_dyn_ops:\n");
++
++ for (i = 0; i < sizeof(g_dyn_ops) / sizeof(struct dvfm_op); ++i) {
++ s += sprintf(s, "%8d %8d %8d %s\n",
++ g_dyn_ops[i].index,
++ g_dyn_ops[i].count,
++ g_dyn_ops[i].cpu_freq, g_dyn_ops[i].name);
++ }
++ s += sprintf(s, "--------------BPM DUMP POLICY END----------------\n");
++
++ DPRINTK("%s", buf);
++
++ kfree(buf);
++ return 0;
++}
++
++static int build_active_ops(void)
++{
++ int i, j;
++ int pre_idx;
++ int cur_idx;
++ int pre_freq, cur_freq, pre_ratio;
++ int m, n;
++
++ memset(g_active_ops_map, -1, sizeof(g_active_ops_map));
++
++ for (i = 0, j = 0; i < BPM_MAX_OP_NUM; ++i) {
++ if (g_dyn_ops[i].count == 0 && g_dyn_ops[i].name != NULL
++ && !dvfm_check_active_op(g_dyn_ops[i].index))
++ g_active_ops_map[j++] = i;
++ }
++
++ g_active_ops_num = j;
++ g_active_cur_idx = -1;
++
++ memset(g_active_bonus, -1, sizeof(g_active_bonus));
++ memset(g_active_policy, -1, sizeof(g_active_policy));
++
++ for (i = 0; i < g_active_ops_num; ++i) {
++ g_active_policy[i].higher[0] = 80;
++ g_active_policy[i].higher[1] = 95;
++ g_active_policy[i].higher[2] = 100;
++
++ if (i == 0) {
++ memset(g_active_policy[i].lower, 0,
++ sizeof(g_active_policy[i].lower));
++ cur_idx = g_active_ops_map[i];
++ cur_freq = g_dyn_ops[cur_idx].cpu_freq;
++ if (cur_freq == 60) {
++ g_active_policy[i].higher[0] = 90;
++ }
++ } else {
++ pre_idx = g_active_ops_map[i - 1];
++ cur_idx = g_active_ops_map[i];
++ pre_freq = g_dyn_ops[pre_idx].cpu_freq;
++ cur_freq = g_dyn_ops[cur_idx].cpu_freq;
++ pre_ratio = g_active_policy[i - 1].higher[0];
++
++ g_active_policy[i].lower[2] = pre_freq * pre_ratio / cur_freq;
++
++ if (i > 1) {
++ pre_idx = g_active_ops_map[i - 2];
++ pre_freq = g_dyn_ops[pre_idx].cpu_freq;
++ pre_ratio = g_active_policy[i - 2].higher[0];
++
++ g_active_policy[i].lower[1] = pre_freq * pre_ratio / cur_freq;
++ } else {
++ g_active_policy[i].lower[1] = 0;
++ }
++
++ g_active_policy[i].lower[0] = 0;
++ }
++
++ for (j = 0; j < g_active_ops_num - 1 - i; ++j) {
++ g_active_bonus[i][j] = 0;
++ }
++
++ m = g_active_ops_num - 1;
++ n = 0;
++ for (j = m - i; j < 2 * g_active_ops_num - 1; ++j) {
++ g_active_bonus[i][j] = n < m ? n : m;
++ ++n;
++ }
++
++ }
++
++ g_active_policy[i - 1].higher[0] = 100;
++ g_active_policy[i - 1].higher[1] = 100;
++ g_active_policy[i - 1].higher[2] = 100;
++
++#if REDUCE_624M_DUTYCYCLE
++ cur_idx = g_active_ops_map[i - 1];
++ cur_freq = g_dyn_ops[cur_idx].cpu_freq;
++ if (cur_freq == 624) {
++ if (i > 1) {
++ g_active_policy[i - 2].higher[0] = 96;
++ g_active_policy[i - 2].higher[1] = 100;
++
++ pre_idx = g_active_ops_map[i - 2];
++ pre_freq = g_dyn_ops[pre_idx].cpu_freq;
++ pre_ratio = g_active_policy[i - 2].higher[0];
++
++ g_active_policy[i - 1].lower[2] = pre_freq * pre_ratio / cur_freq;
++ }
++ if (i > 2) {
++ g_active_policy[i - 3].higher[1] = 100;
++
++ pre_idx = g_active_ops_map[i - 3];
++ pre_freq = g_dyn_ops[pre_idx].cpu_freq;
++ pre_ratio = g_active_policy[i - 3].higher[0];
++
++ g_active_policy[i - 1].lower[1] = pre_freq * pre_ratio / cur_freq;
++ }
++ }
++#endif
++ return 0;
++}
++
++/*****************************************************************************/
++/* */
++/* Platform Related */
++/* */
++/*****************************************************************************/
++
++int get_op_power_bonus(void)
++{
++ if (0 == g_active_cur_idx)
++ return 1;
++ else
++ return 0;
++}
++
++static int build_dyn_ops(void)
++{
++ int i;
++ int ret;
++ int op_num = 0;
++ int count, x;
++
++ struct op_info *info = NULL;
++ struct op_freq freq;
++
++ op_num = dvfm_op_count();
++ PM_BUG_ON(op_num > BPM_MAX_OP_NUM);
++
++ memset(&g_dyn_ops, -1, sizeof(g_dyn_ops));
++
++ for (i = 0; i < op_num; ++i) {
++ ret = dvfm_get_opinfo(i, &info);
++
++ PM_BUG_ON(ret);
++
++ /* calculate how much bits is set in device word */
++ x = info->device;
++ for (count = 0; x; x = x & (x - 1), count++);
++
++ g_dyn_ops[i].index = i;
++ g_dyn_ops[i].count = count;
++
++ ret = dvfm_get_op_freq(i, &freq);
++ PM_BUG_ON(ret);
++
++ g_dyn_ops[i].cpu_freq = freq.cpu_freq;
++
++ g_dyn_ops[i].name = dvfm_get_op_name(i);
++
++ PM_BUG_ON(!g_dyn_ops[i].name);
++
++ INIT_LIST_HEAD(&(g_bpm_cons[i].list));
++ }
++
++ for (i = op_num; i < BPM_MAX_OP_NUM; ++i) {
++ g_dyn_ops[i].index = -1;
++ g_dyn_ops[i].count = 0;
++ g_dyn_ops[i].cpu_freq = 0;
++ g_dyn_ops[i].name = NULL;
++
++ INIT_LIST_HEAD(&(g_bpm_cons[i].list));
++ }
++
++ return 0;
++}
++
++static int get_dyn_idx(int active_idx)
++{
++ int t;
++ t = g_active_ops_map[active_idx];
++ return g_dyn_ops[t].index;
++}
++
++static int get_cur_freq(void)
++{
++ PM_BUG_ON(g_active_cur_idx == -1);
++ return g_dyn_ops[get_dyn_idx(g_active_cur_idx)].cpu_freq;
++}
++
++static int calc_new_idx(int bonus)
++{
++ int new_idx;
++
++ new_idx =
++ g_active_bonus[g_active_cur_idx][bonus + g_active_ops_num - 1];
++
++ return new_idx;
++}
++
++static int calc_bonus(struct bpm_freq_bonus_arg *parg)
++{
++ int i;
++ int bonus = 0;
++ int mem_stall = parg->mem_stall;
++ int mipsload = parg->mips * 100 / get_cur_freq();
++ int cpuload = mipsload > 100 ? 100 : mipsload;
++
++ PM_BUG_ON(cpuload > 100 || cpuload < 0);
++
++ for (i = 0; i < BPM_FREQ_POLICY_NUM; ++i) {
++ if (cpuload > g_active_policy[g_active_cur_idx].higher[i]) {
++ bonus += 1;
++// break; /* FIX ME: change the freq one by one */
++ }
++ }
++
++ for (i = BPM_FREQ_POLICY_NUM - 1; i >= 0; --i) {
++ if (cpuload < g_active_policy[g_active_cur_idx].lower[i]) {
++ bonus -= 1;
++// break; /* FIX ME: change the freq one by one */
++ }
++ }
++
++ /* memory bound */
++ if (bonus <= 0 && mem_stall > 17)
++ bonus = 1;
++
++ /* change to user_sleep policy ... */
++ if (g_android_suspended && (g_active_cur_idx <= 1))
++ bonus -= 1;
++
++ if (bonus > g_active_ops_num - 1)
++ bonus = g_active_ops_num - 1;
++ else if (bonus < 1 - g_active_ops_num)
++ bonus = 1 - g_active_ops_num;
++
++ return bonus;
++}
++
++/*****************************************************************************/
++/* */
++/* BPMD API */
++/* */
++/*****************************************************************************/
++
++static int bpm_change_op(int cur_idx, int new_idx)
++{
++ int ret;
++ struct dvfm_freqs freqs;
++ unsigned int oscr;
++
++ freqs.old = cur_idx;
++ freqs.new = new_idx;
++ oscr = OSCR;
++ ret = dvfm_set_op(&freqs, freqs.new, RELATION_STICK);
++ oscr = OSCR - oscr;
++ DPRINTK("old: %d cur: %d (tm: %d)\n", cur_idx, new_idx, oscr/325);
++/*
++ DPRINTK("ACCR: 0x%x ACSR: 0x%x AVCR: 0x%x SVCR: 0x%x CVCR: 0x%x\n",
++ ACCR, ACSR, AVCR, SVCR, CVCR);
++*/
++ return ret;
++}
++
++/* this function need to be refatored later? */
++int bpm_disable_op(int dyn_idx, int dev_idx)
++{
++ int i;
++ int ret = 0;
++ int cur_op_idx = -1, op_idx;
++ int next_op_idx = -1, next_active_idx = -1;
++
++ op_idx = g_dyn_ops[dyn_idx].index;
++
++ /* save current op information */
++ if (g_active_cur_idx != -1) {
++ cur_op_idx = get_dyn_idx(g_active_cur_idx);
++ }
++
++ if (!dvfm_check_active_op(op_idx) && g_active_ops_num == 1 &&
++ cur_op_idx == op_idx) {
++ printk(KERN_ERR "Can't disable this op %d\n", op_idx);
++ bpm_dump_policy();
++ return -1;
++ }
++
++ /*
++ * it should be at least two enabled ops here,
++ * otherwise it cannot come here if there is one enabled op.
++ */
++ if ((g_active_cur_idx != -1) && (g_active_ops_num > 1)) {
++ if (g_active_cur_idx == (g_active_ops_num - 1)) {
++ next_op_idx = get_dyn_idx(g_active_cur_idx - 1);
++ PM_BUG_ON((g_active_cur_idx - 1) < 0);
++ if ((g_active_cur_idx - 1) < 0) {
++ printk(KERN_ERR "err: %d %d\n", g_active_cur_idx, g_active_ops_num);
++ bpm_dump_policy();
++ }
++ } else {
++ next_op_idx = get_dyn_idx(g_active_cur_idx + 1);
++ PM_BUG_ON((g_active_cur_idx + 1) > (g_active_ops_num - 1));
++ if ((g_active_cur_idx + 1) > (g_active_ops_num - 1)) {
++ printk(KERN_ERR "err2: %d %d\n", g_active_cur_idx, g_active_ops_num);
++ bpm_dump_policy();
++ }
++ }
++ }
++
++ g_dyn_ops[dyn_idx].count++;
++
++ __dvfm_disable_op2(op_idx, dev_idx);
++
++ if (!dvfm_check_active_op(op_idx) && g_dyn_ops[dyn_idx].count == 1) {
++ build_active_ops();
++ }
++
++ if (cur_op_idx != -1) {
++ for (i = 0; i < g_active_ops_num; ++i) {
++ if (get_dyn_idx(i) == cur_op_idx) {
++ g_active_cur_idx = i;
++ break;
++ }
++ }
++
++ /* the disabled op is previous op, change to another op */
++ if (g_active_cur_idx == -1) {
++
++ /* find next op */
++ for (i = 0; i < g_active_ops_num; ++i) {
++ if (get_dyn_idx(i) == next_op_idx) {
++ next_active_idx = i;
++ break;
++ }
++ }
++
++ PM_BUG_ON(cur_op_idx != op_idx);
++ PM_BUG_ON(next_op_idx != get_dyn_idx(next_active_idx));
++ g_active_cur_idx = next_active_idx;
++ ret = bpm_change_op(cur_op_idx, next_op_idx);
++ PM_BUG_ON(ret);
++ }
++ }
++
++ return ret;
++}
++
++int bpm_enable_op(int dyn_idx, int dev_idx)
++{
++ int i, cur_op_idx = -1;
++
++ if (g_dyn_ops[dyn_idx].count <= 0) {
++ printk(KERN_ERR "are you disable this op before?\n");
++ return -1;
++ }
++
++ /* save current op information */
++ if (g_active_cur_idx != -1) {
++ cur_op_idx = get_dyn_idx(g_active_cur_idx);
++ }
++
++ g_dyn_ops[dyn_idx].count--;
++
++ if (g_dyn_ops[dyn_idx].count == 0)
++ build_active_ops();
++
++ __dvfm_enable_op(g_dyn_ops[dyn_idx].index, dev_idx);
++
++ if (cur_op_idx != -1) {
++ for (i = 0; i < g_active_ops_num; ++i) {
++ if (get_dyn_idx(i) == cur_op_idx) {
++ g_active_cur_idx = i;
++ break;
++ }
++ }
++ }
++
++ return 0;
++}
++
++int bpm_enable_op_name(char *name, int dev_idx, char *sid)
++{
++ unsigned long flag;
++ int ret = 0, new_idx = -1;
++ int i, found;
++ struct list_head *list = NULL;
++ struct bpm_cons *p = NULL;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ for (i = 0; i < sizeof(g_dyn_ops) / sizeof(struct dvfm_op); ++i) {
++ if (g_dyn_ops[i].name != NULL &&
++ (!strncmp(name, g_dyn_ops[i].name, sizeof(name)))) {
++ ret = bpm_enable_op(i, dev_idx);
++
++ if (!ret) {
++ found = 0;
++ list_for_each(list, &(g_bpm_cons[i].list)) {
++ p = list_entry(list, struct bpm_cons, list);
++ if (!strncmp(p->sid, sid, CONSTRAINT_ID_LEN - 1)) {
++ found = 1;
++ PM_BUG_ON(p->count <= 0);
++ p->count--;
++ if (p->tmp_ms) {
++ p->tm++;
++ p->ms += (OSCR / 3250 - p->tmp_ms);
++ }
++ break;
++ }
++ }
++ PM_BUG_ON(!found);
++ } else {
++ printk(KERN_ERR "%s use PM interface rightly!\n", sid);
++ PM_BUG_ON(1);
++ }
++ break;
++ }
++ }
++
++ if (i == sizeof(g_dyn_ops) / sizeof(struct dvfm_op)) {
++// printk(KERN_ERR "Cannot find and enable op name %s\n", name);
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ /* Change to prefrer op */
++ if (g_prefer_op_idx != cur_op && g_active_cur_idx != -1) {
++ for (i = 0; i < g_active_ops_num; ++i) {
++ if (get_dyn_idx(i) == g_prefer_op_idx) {
++ new_idx = i;
++ break;
++ }
++ }
++
++ if (new_idx != -1) {
++ ret = bpm_change_op(get_dyn_idx(g_active_cur_idx), get_dyn_idx(new_idx));
++ if (0 == ret)
++ g_active_cur_idx = new_idx;
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++ }
++ }
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return ret;
++}
++
++int bpm_disable_op_name(char *name, int dev_idx, char *sid)
++{
++ unsigned long flag;
++ int ret = -1;
++ int i;
++ int find = 0;
++ struct list_head *list = NULL;
++ struct bpm_cons *p = NULL;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ for (i = 0; i < sizeof(g_dyn_ops) / sizeof(struct dvfm_op); ++i) {
++ if (g_dyn_ops[i].name != NULL &&
++ (!strncmp(name, g_dyn_ops[i].name, sizeof(name)))) {
++ ret = bpm_disable_op(i, dev_idx);
++
++ if (!ret) {
++ list_for_each(list, &(g_bpm_cons[i].list)) {
++ p = list_entry(list, struct bpm_cons, list);
++ if (!strncmp(p->sid, sid, CONSTRAINT_ID_LEN - 1)) {
++ p->count++;
++ p->tmp_ms = OSCR / 3250;
++ find = 1;
++ break;
++ }
++ }
++
++ if (find == 0) {
++ p = (struct bpm_cons *)kzalloc(sizeof(struct bpm_cons), GFP_KERNEL);
++ strncpy(p->sid, sid, CONSTRAINT_ID_LEN - 1);
++ p->count = 1;
++ list_add_tail(&(p->list), &(g_bpm_cons[i].list));
++ }
++ }
++ break;
++ }
++ }
++
++ if (i == sizeof(g_dyn_ops) / sizeof(struct dvfm_op)) {
++// printk(KERN_ERR "Cannot find and disable op name %s\n", name);
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return ret;
++}
++
++static int handle_profiler_arg(struct bpm_freq_bonus_arg *parg)
++{
++ int bonus;
++ int new_idx;
++ unsigned long flag;
++ int cur_dyn_idx, new_dyn_idx;
++
++ if (g_dvfm_blink)
++ return 0;
++
++ /*
++ * bpm_enable_op_name() and bpm_disable_op_name() will update
++ * g_dyn_ops[] and g_active_xxx[], and then scale the op, so
++ * we need to avoid the conflict.
++ * Below code can not call schedule() indirectly.
++ */
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ if (0 == g_bpm_enabled) {
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++ return 0;
++ }
++
++ bonus = calc_bonus(parg);
++ new_idx = calc_new_idx(bonus);
++
++ cur_dyn_idx = get_dyn_idx(g_active_cur_idx);
++ new_dyn_idx = get_dyn_idx(new_idx);
++
++/*
++ DPRINTK
++ ("bonus:%d, cur_idx: %d, new_idx: %d, old_hw_idx: %d, new_hw_idx: %d\n",
++ bonus, g_active_cur_idx, new_idx, cur_dyn_idx, new_dyn_idx);
++*/
++ if (new_idx != g_active_cur_idx) {
++ if (!bpm_change_op(cur_dyn_idx, new_dyn_idx)) {
++ g_active_cur_idx = new_idx;
++ } else {
++ DPRINTK("scaling freq later!\n");
++ }
++ g_prefer_op_idx = new_dyn_idx;
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return 0;
++}
++
++static void dvfm_blink_timer_handler(unsigned long data)
++{
++ unsigned long flag;
++
++ local_irq_save(flag);
++
++ g_dvfm_blink = 0;
++ g_dvfm_blink_timeout = 0;
++ memset(&g_dvfm_binfo, 0, sizeof(struct dvfm_blink_info));
++
++ local_irq_restore(flag);
++}
++
++static int handle_blink(struct bpm_event *pevent)
++{
++ int new_idx;
++ unsigned long flag;
++ int cur_dyn_idx, new_dyn_idx;
++ struct dvfm_blink_info *pinfo = NULL;
++
++ if (0 == g_bpm_enabled)
++ return 0;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ pinfo = (struct dvfm_blink_info *)pevent->info;
++
++ DPRINTK("Blink: %d %lu %lu\n", g_dvfm_blink, g_dvfm_blink_timeout, jiffies + msecs_to_jiffies(pinfo->time));
++
++ if ((0 == g_dvfm_blink) || time_before(g_dvfm_blink_timeout, jiffies + msecs_to_jiffies(pinfo->time))) {
++
++ memcpy(&g_dvfm_binfo, pinfo, sizeof(struct dvfm_blink_info));
++
++ g_dvfm_blink_timeout = jiffies + msecs_to_jiffies(pinfo->time);
++ g_dvfm_blink = 1;
++ mod_timer(&g_dvfm_blink_timer, g_dvfm_blink_timeout);
++
++ new_idx = g_active_ops_num - 1;
++ cur_dyn_idx = get_dyn_idx(g_active_cur_idx);
++ new_dyn_idx = get_dyn_idx(new_idx);
++
++ if (new_dyn_idx > cur_dyn_idx) {
++ if (!bpm_change_op(cur_dyn_idx, new_dyn_idx)) {
++ g_active_cur_idx = new_idx;
++ g_prefer_op_idx = new_dyn_idx;
++ }
++ }
++ } else {
++ printk("Blink: %s already set and blink(%lu)\n", g_dvfm_binfo.name, g_dvfm_blink_timeout);
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return 0;
++}
++
++static int handle_profiler(struct bpm_event *pevent)
++{
++ struct ipm_profiler_result *pinfo =
++ (struct ipm_profiler_result *)pevent->info;
++ struct bpm_freq_bonus_arg bonus_arg;
++ int mips = pinfo->mips;
++ int mem_stall = 0;
++
++#ifdef CONFIG_TEST_BPMD
++ static int cpuload = 10;
++ switch (g_cpuload_mode) {
++ case 0:
++ cpuload = mips * 100 / get_cur_freq();
++ break;
++ case 1:
++ cpuload = (cpuload == 10 ? 90 : 10);
++ break;
++ case 2:
++ cpuload = OSCR % 101;
++ break;
++ case 3:
++ cpuload = (OSCR & 0x1) ? 90 : 10;
++ break;
++ case 4:
++ cpuload = OSCR % 21;
++ break;
++ case 5:
++ cpuload = 80 + OSCR % 21;
++ break;
++ }
++ mips = cpuload * get_cur_freq() / 100;
++
++// DPRINTK("orig ratio: %d new ratio: %d\n", pinfo->busy_ratio, busy);
++#endif
++ DPRINTK("time_load: %d mips_load: %d (%d)\n", pinfo->busy_ratio, mips * 100 / get_cur_freq(), get_cur_freq());
++
++ /*
++ * Get PMU Data, bla bla bla...
++ */
++ bonus_arg.mips = mips;
++ bonus_arg.mem_stall = mem_stall;
++
++ handle_profiler_arg(&bonus_arg);
++
++ bpm_start_pmu();
++ return 0;
++}
++
++static int bpm_process_event(struct bpm_event *pevent)
++{
++ switch (pevent->type) {
++ case IPM_EVENT_PROFILER:
++ handle_profiler(pevent);
++ break;
++
++ case IPM_EVENT_BLINK:
++ handle_blink(pevent);
++ break;
++
++ default:
++ PM_BUG_ON(1);
++ }
++ return 0;
++}
++
++int bpm_pre_enter_d0csidle(int* op)
++{
++ unsigned long flag;
++ int ret = 0, new_dyn_idx;;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ if (g_active_cur_idx != -1)
++ *op = get_dyn_idx(g_active_cur_idx);
++ else
++ *op = dvfm_get_defop();
++
++ new_dyn_idx = get_dyn_idx(0);
++ if (*op > new_dyn_idx) {
++ ret = bpm_change_op(*op, new_dyn_idx);
++
++ if ((0 == ret) && (-1 != g_active_cur_idx)) {
++ g_active_cur_idx = 0;
++ }
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++#ifdef CONFIG_MTD_NAND_HSS_FIX
++ if (!atomic_read(&nand_in_cmd))
++#endif
++ PM_BUG_ON(ret);
++
++ return ret;
++}
++
++int bpm_post_exit_d0csidle(int op)
++{
++ unsigned long flag;
++ int new_idx = -1;
++ int cur_dyn_op, new_dyn_op;
++ int i, ret;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ if (g_active_cur_idx != -1) {
++ for (i = 0; i < g_active_ops_num; ++i) {
++ if (get_dyn_idx(i) >= op) {
++ new_idx = i;
++ break;
++ }
++ }
++
++ PM_BUG_ON(new_idx == -1);
++
++ cur_dyn_op = get_dyn_idx(g_active_cur_idx);
++ new_dyn_op = get_dyn_idx(new_idx);
++
++ PM_BUG_ON(cur_dyn_op != cur_op);
++
++ g_active_cur_idx = new_idx;
++ } else {
++ cur_dyn_op = cur_op;
++ new_dyn_op = dvfm_get_defop();
++ PM_BUG_ON(op != new_dyn_op);
++ }
++
++ PM_BUG_ON(cur_dyn_op > new_dyn_op);
++
++ if (cur_dyn_op != new_dyn_op) {
++ ret = bpm_change_op(cur_dyn_op, new_dyn_op);
++ PM_BUG_ON(ret);
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return 0;
++}
++
++int bpm_set_active_op(const unsigned char* opname)
++{
++ int opname_idx = -1, i, cur_idx;
++ int ret = 0;
++ unsigned long flag;
++
++ if (-1 != g_active_cur_idx) {
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ for (i = 0; i < g_active_ops_num; ++i) {
++ cur_idx = g_active_ops_map[i];
++ if (!strcmp(opname, g_dyn_ops[cur_idx].name)) {
++ opname_idx = i;
++ }
++ }
++
++ if(opname_idx != -1) {
++ if (g_active_cur_idx != opname_idx) {
++ ret = bpm_change_op(get_dyn_idx(g_active_cur_idx), get_dyn_idx(opname_idx));
++ g_active_cur_idx = opname_idx;
++ g_prefer_op_idx = get_dyn_idx(opname_idx);
++ PM_BUG_ON(ret);
++ }
++ } else
++ printk(KERN_WARNING "Cannot find %s, %s is disabled?\n", opname, opname);
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++ }
++
++ return ret;
++}
++/*****************************************************************************/
++/* */
++/* BPMD Thread */
++/* */
++/*****************************************************************************/
++
++static int change_to_active_op(void)
++{
++ unsigned long flag;
++ int ret = 0;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ g_active_cur_idx = g_active_ops_num - 1;
++ ret = bpm_change_op(dvfm_get_defop(), get_dyn_idx(g_active_cur_idx));
++ g_prefer_op_idx = cur_op;
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ PM_BUG_ON(ret);
++
++ return ret;
++}
++
++static int change_to_def_op(void)
++{
++ unsigned long flag;
++ int ret = 0;
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ ret = bpm_change_op(get_dyn_idx(g_active_cur_idx), dvfm_get_defop());
++ g_prefer_op_idx = cur_op;
++
++ g_active_cur_idx = -1;
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ PM_BUG_ON(ret);
++
++ return ret;
++}
++
++static int bpm_start(void)
++{
++ int ret;
++
++ if (0 == g_bpm_enabled) {
++ bpmq_clear();
++ change_to_active_op();
++ ret = bpm_start_pmu();
++ if (ret) {
++ printk(KERN_ERR "Can't start_pmu, ret: %d\n", ret);
++ g_bpm_enabled = 0;
++ return ret;
++ }
++ g_bpm_enabled = 1;
++#ifdef DEBUG
++ bpm_dump_policy();
++#endif
++ wake_up_interruptible(&g_bpm_enabled_waitq);
++ } else {
++ printk(KERN_DEBUG "bpmd already enabled (%d)\n", g_bpm_enabled);
++ }
++
++ return 0;
++}
++
++extern int gpio_reset_work_around(void);
++static int bpm_stop(void)
++{
++ if (1 == g_bpm_enabled) {
++ bpm_stop_pmu();
++ if (machine_is_bstd())
++ gpio_reset_work_around();
++ else
++ change_to_def_op();
++ g_bpm_enabled = 0;
++ } else {
++ printk(KERN_DEBUG "bpmd already stopped (%d)\n", g_bpm_enabled);
++ }
++
++ return 0;
++}
++
++static int bpm_thread(void *data)
++{
++ int ret = 0;
++ struct bpm_event event;
++ struct task_struct *tsk = current;
++ struct sched_param param = {.sched_priority = 1 };
++
++ DEFINE_WAIT(wait);
++
++ if (g_dvfm_disabled)
++ goto thread_over;
++
++ daemonize("bpmd");
++ strcpy(tsk->comm, "bpmd");
++
++ allow_signal(SIGKILL);
++ sched_setscheduler(tsk, SCHED_FIFO, &param);
++
++ g_bpm_log_level = 0;
++
++ msleep(SYSTEM_BOOTUP_TIME);
++
++ ret = bpm_start();
++ PM_BUG_ON(ret);
++
++ DPRINTK("Begining bpm deamon thread ...\n");
++
++ while (likely(!g_bpm_thread_exit)) {
++
++ if (unlikely(signal_pending(tsk))) {
++ printk(KERN_NOTICE "BPMD is killed by SIGKILL!\n");
++ break;
++ }
++
++// DPRINTK("g_bpm_enabled = %d, bpmq_empty = %d\n",
++// g_bpm_enabled, bpmq_empty());
++
++ if (likely(g_bpm_enabled)) {
++ if (likely(bpmq_empty())) {
++ prepare_to_wait(&g_bpm_event_queue.waitq, &wait,
++ TASK_INTERRUPTIBLE);
++ schedule();
++ finish_wait(&g_bpm_event_queue.waitq, &wait);
++ }
++
++ if (likely(!bpmq_empty())) {
++ ret = bpmq_get(&event);
++ PM_BUG_ON(ret);
++
++ bpm_process_event(&event);
++ }
++ } else {
++ prepare_to_wait(&g_bpm_enabled_waitq, &wait,
++ TASK_INTERRUPTIBLE);
++ schedule();
++ finish_wait(&g_bpm_enabled_waitq, &wait);
++ }
++ }
++
++ bpm_stop();
++
++thread_over:
++ complete_and_exit(&g_bpm_thread_over, 0);
++
++ printk(KERN_WARNING "bpm daemon thread exit!\n");
++ return 0;
++}
++
++/*****************************************************************************/
++/* */
++/* BPMD SYS Interface */
++/* */
++/*****************************************************************************/
++
++static ssize_t op_show(struct sys_device *sys_dev, char *buf)
++{
++ int cur_dyn_idx, len;
++
++ if (g_active_cur_idx != -1)
++ cur_dyn_idx = get_dyn_idx(g_active_cur_idx);
++ else
++ cur_dyn_idx = dvfm_get_defop();
++
++ PM_BUG_ON(cur_dyn_idx != cur_op);
++
++ len = dvfm_dump_op(cur_dyn_idx, buf);
++
++ return len;
++}
++
++static ssize_t op_store(struct sys_device *sys_dev, const char *buf, size_t len)
++{
++ int i;
++ int dyn_idx, new_dyn_idx, cur_dyn_idx, new_active_idx = -1;
++ unsigned long flag;
++ int res = 0;
++
++ sscanf(buf, "%u", &new_dyn_idx);
++
++ spin_lock_irqsave(&g_dyn_ops_lock, flag);
++
++ for (i = 0; i < g_active_ops_num; ++i) {
++ dyn_idx = g_active_ops_map[i];
++ if (g_dyn_ops[dyn_idx].index == new_dyn_idx) {
++ new_active_idx = i;
++ break;
++ }
++ }
++
++ if (new_active_idx != -1) {
++ if (g_active_cur_idx != -1)
++ cur_dyn_idx = get_dyn_idx(g_active_cur_idx);
++ else
++ cur_dyn_idx = dvfm_get_defop();
++
++ res = bpm_change_op(cur_dyn_idx, new_dyn_idx);
++ g_prefer_op_idx = new_dyn_idx;
++
++ PM_BUG_ON(res);
++
++ g_active_cur_idx = new_active_idx;
++ } else {
++ printk(KERN_ERR "bpm is enabled, new dyn op:%d\n", new_dyn_idx);
++ printk(KERN_ERR "Cannot find new active op, please check it\n");
++ }
++
++ PM_BUG_ON((-1 != g_active_cur_idx) && (get_dyn_idx(g_active_cur_idx) != cur_op));
++
++ spin_unlock_irqrestore(&g_dyn_ops_lock, flag);
++
++ return len;
++}
++
++SYSDEV_ATTR(op, 0644, op_show, op_store);
++
++static ssize_t ops_show(struct sys_device *sys_dev, char *buf)
++{
++ int len = 0;
++ char *p = NULL;
++ int i;
++
++ for (i = 0; i < sizeof(g_dyn_ops) / sizeof(struct dvfm_op); ++i) {
++ if (g_dyn_ops[i].name != NULL) {
++ p = buf + len;
++ len += dvfm_dump_op(i, p);
++ }
++ }
++
++ return len;
++}
++
++SYSDEV_ATTR(ops, 0444, ops_show, NULL);
++
++static ssize_t enable_op_show(struct sys_device *sys_dev, char *buf)
++{
++ int len = 0;
++ char *p = NULL;
++ int i;
++
++ for (i = 0; i < sizeof(g_dyn_ops) / sizeof(struct dvfm_op); ++i) {
++ if ((!g_dyn_ops[i].count) && (g_dyn_ops[i].name != NULL)) {
++ p = buf + len;
++ len += dvfm_dump_op(i, p);
++ }
++ }
++
++ return len;
++}
++
++static ssize_t enable_op_store(struct sys_device *sys_dev, const char *buf,
++ size_t len)
++{
++ int level;
++ char name[16];
++
++ if (len >= 16) {
++ printk(KERN_ERR "invalid parameter\n");
++ return len;
++ }
++
++ memset(name, 0, sizeof(name));
++ sscanf(buf, "%s %d", name, &level);
++
++ if (level)
++ bpm_enable_op_name(name, dvfm_dev_idx, "user-echo");
++ else
++ bpm_disable_op_name(name, dvfm_dev_idx, "user-echo");
++
++ return len;
++}
++
++SYSDEV_ATTR(enable_op, 0666, enable_op_show, enable_op_store);
++
++static ssize_t profiler_window_show(struct sys_device *sys_dev, char *buf)
++{
++ char *s = buf;
++
++ s += sprintf(s, "%d\n", g_profiler_window);
++
++ return (s - buf);
++}
++
++static ssize_t profiler_window_store(struct sys_device *sys_dev,
++ const char *buf, size_t n)
++{
++ sscanf(buf, "%u", &g_profiler_window);
++
++ if (g_profiler_window < 10 || g_profiler_window > 20000)
++ printk(KERN_ERR "please input the value in (10, 20000]\n");
++
++ return n;
++}
++
++SYSDEV_ATTR(profiler_window, 0644, profiler_window_show, profiler_window_store);
++
++static ssize_t bpm_show(struct sys_device *sys_dev, char *buf)
++{
++ char *s = buf;
++
++ if (g_bpm_enabled)
++ s += sprintf(s, "%s\n", "enabled");
++ else
++ s += sprintf(s, "%s\n", "disabled");
++
++ return (s - buf);
++}
++
++static ssize_t bpm_store(struct sys_device *sys_dev, const char *buf, size_t n)
++{
++ if (n >= strlen("enable") &&
++ strncmp(buf, "enable", strlen("enable")) == 0) {
++ bpm_start();
++ return n;
++ }
++
++ if (n >= strlen("disable") &&
++ strncmp(buf, "disable", strlen("disable")) == 0) {
++ bpm_stop();
++ return n;
++ }
++
++ printk(KERN_ERR "invalid input, please try \"enable\" or \"disable\"\n");
++ return n;
++}
++
++SYSDEV_ATTR(bpm, 0644, bpm_show, bpm_store);
++
++static ssize_t blink_show(struct sys_device *sys_dev, char *buf)
++{
++ char *s = buf;
++
++ if (g_dvfm_blink)
++ s += sprintf(s, "blink: %s\n", g_dvfm_binfo.name);
++ else
++ s += sprintf(s, "blink: no\n");
++
++ return (s - buf);
++}
++
++static ssize_t blink_store(struct sys_device *sys_dev, const char *buf, size_t len)
++{
++ struct dvfm_blink_info binfo;
++
++ if (len >= (DVFM_BLINK_OWNER_LEN - 1)) {
++ printk(KERN_ERR "%s sets an invalid parameter of blink\n", current->comm);
++ return len;
++ }
++
++ memset(binfo.name, 0, sizeof(binfo.name));
++ sscanf(buf, "%s %d %*s", binfo.name, &binfo.time);
++
++ DPRINTK("blink: %s %d\n", binfo.name, binfo.time);
++
++ if (binfo.time < 0 || binfo.time > 3000) {
++ printk("%s sets an invalid time of blink\n", current->comm);
++ return len;
++ }
++
++ bpm_event_notify(IPM_EVENT_BLINK, IPM_EVENT_BLINK_SPEEDUP, &binfo,
++ sizeof(struct dvfm_blink_info));
++
++ return len;
++}
++SYSDEV_ATTR(blink, 0666, blink_show, blink_store);
++
++static ssize_t log_show(struct sys_device *sys_dev, char *buf)
++{
++ char *s = buf;
++
++ s += sprintf(s, "%d\n", g_bpm_log_level);
++
++ return (s - buf);
++}
++
++static ssize_t log_store(struct sys_device *sys_dev, const char *buf, size_t n)
++{
++ sscanf(buf, "%u", &g_bpm_log_level);
++
++ if (g_bpm_log_level < 0 || g_bpm_log_level > 7) {
++ g_bpm_log_level = 0;
++ printk(KERN_ERR "invalid command\n");
++ }
++ return n;
++}
++
++SYSDEV_ATTR(log, 0644, log_show, log_store);
++
++static ssize_t cons_show(struct sys_device *sys_dev, char *buf)
++{
++ char *s = buf;
++ struct list_head *list = NULL;
++ struct bpm_cons *p = NULL;
++ int i;
++ unsigned long avg_ms;
++
++ for (i = 0; i < BPM_MAX_OP_NUM; ++i) {
++ s += sprintf(s, "op %d: %d\n", i, g_dyn_ops[i].count);
++ list_for_each(list, &(g_bpm_cons[i].list)) {
++ p = list_entry(list, struct bpm_cons, list);
++ if (p->tm)
++ avg_ms = p->ms / p->tm;
++ else
++ avg_ms = 0;
++ s += sprintf(s, "\t%8ld %12ld %8ld %s: %d\n",
++ p->tm, p->ms, avg_ms, p->sid, p->count);
++ }
++ }
++
++ return (s - buf);
++}
++
++static ssize_t cons_store(struct sys_device *sys_dev, const char *buf, size_t n)
++{
++ struct list_head *list = NULL;
++ struct bpm_cons *p = NULL;
++ int i;
++ int cons_ctl = 0;
++
++ sscanf(buf, "%u", &cons_ctl);
++
++ if (1 == cons_ctl) {
++ for (i = 0; i < BPM_MAX_OP_NUM; ++i) {
++ list_for_each(list, &(g_bpm_cons[i].list)) {
++ p = list_entry(list, struct bpm_cons, list);
++ p->tm = 0;
++ p->ms = 0;
++ p->tmp_ms = 0;
++ }
++ }
++ }
++
++ return n;
++}
++
++SYSDEV_ATTR(cons, 0644, cons_show, cons_store);
++
++/*
++ * Dump blocked device on specified OP.
++ * And dump the device list that is tracked.
++ */
++static ssize_t trace_show(struct sys_device *sys_dev, char *buf)
++{
++ struct op_info *op_entry = NULL;
++ struct dvfm_trace_info *entry = NULL;
++ int len = 0, i;
++ unsigned int blocked_dev;
++
++ for (i = 0; i < op_nums; i++) {
++ blocked_dev = 0;
++ read_lock(&dvfm_op_list->lock);
++ /* op list shouldn't be empty because op_nums is valid */
++ list_for_each_entry(op_entry, &dvfm_op_list->list, list) {
++ if (op_entry->index == i)
++ blocked_dev = op_entry->device;
++ }
++ read_unlock(&dvfm_op_list->lock);
++ if (!blocked_dev)
++ continue;
++
++ len += sprintf(buf + len, "Blocked devices on OP%d:", i);
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ if (test_bit(entry->index, (void *)&blocked_dev))
++ len += sprintf(buf + len, "%s, ", entry->name);
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ len += sprintf(buf + len, "\n");
++ }
++ if (len == 0)
++ len += sprintf(buf + len, "None device block OP\n");
++ len += sprintf(buf + len, "Trace device list:\n");
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ len += sprintf(buf + len, "%s, ", entry->name);
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ len += sprintf(buf + len, "\n");
++ return len;
++}
++SYSDEV_ATTR(trace, 0444, trace_show, NULL);
++
++static struct attribute *bpm_attr[] = {
++ &attr_bpm.attr,
++ &attr_profiler_window.attr,
++ &attr_op.attr,
++ &attr_ops.attr,
++ &attr_enable_op.attr,
++ &attr_log.attr,
++ &attr_cons.attr,
++ &attr_blink.attr,
++ &attr_trace.attr,
++};
++
++static int bpm_add(struct sys_device *sys_dev)
++{
++ int i, n, ret;
++ n = ARRAY_SIZE(bpm_attr);
++ for (i = 0; i < n; ++i) {
++ ret = sysfs_create_file(&(sys_dev->kobj), bpm_attr[i]);
++ if (ret)
++ return ret;
++ }
++ return 0;
++}
++
++static int bpm_rm(struct sys_device *sys_dev)
++{
++ int i, n;
++ n = ARRAY_SIZE(bpm_attr);
++ for (i = 0; i < n; i++) {
++ sysfs_remove_file(&(sys_dev->kobj), bpm_attr[i]);
++ }
++ return 0;
++}
++
++static struct sysdev_driver bpm_driver = {
++ .add = bpm_add,
++ .remove = bpm_rm,
++};
++
++#ifdef CONFIG_TEST_BPMD
++#include "test_bpm.c"
++#endif
++/*****************************************************************************/
++/* */
++/* BPMD Init & Fini */
++/* */
++/*****************************************************************************/
++
++static int __init bpm_init(void)
++{
++ unsigned int ret = 0;
++ unsigned long flag;
++
++ bpmq_init();
++
++ spin_lock_irqsave(&g_bpm_event_queue_lock, flag);
++
++ build_dyn_ops();
++ build_active_ops();
++
++ spin_unlock_irqrestore(&g_bpm_event_queue_lock, flag);
++
++ g_bpm_enabled = 0;
++ init_waitqueue_head(&g_bpm_enabled_waitq);
++
++ ret = sysdev_driver_register(&cpu_sysdev_class, &bpm_driver);
++ if (ret) {
++ printk(KERN_ERR "Can't register bpm sys driver,err:%d\n", ret);
++ PM_BUG_ON(1);
++ }
++
++#ifdef CONFIG_TEST_BPMD
++ ret = sysdev_driver_register(&cpu_sysdev_class, &bpm_test_driver);
++ if (ret) {
++ printk(KERN_ERR "Can't register bpm test driver,err:%d\n", ret);
++ PM_BUG_ON(1);
++ }
++#endif
++
++ dvfm_register("user-echo", &dvfm_dev_idx);
++
++#ifdef CONFIG_ANDROID_POWER
++ android_register_early_suspend(&bpm_early_suspend);
++#endif
++ init_timer(&g_dvfm_blink_timer);
++ g_dvfm_blink_timer.function = dvfm_blink_timer_handler;
++ g_dvfm_blink_timer.data = (unsigned long)NULL;
++
++ g_bpm_thread_exit = 0;
++ init_completion(&g_bpm_thread_over);
++ ret = kernel_thread(bpm_thread, NULL, 0);
++
++ printk(KERN_NOTICE "bpm init finished (%d)\n", ret);
++ return 0;
++}
++
++static void __exit bpm_exit(void)
++{
++
++ g_bpm_thread_exit = 1;
++
++#ifdef CONFIG_ANDROID_POWER
++ android_unregister_early_suspend(&bpm_early_suspend);
++#endif
++ dvfm_unregister("user-echo", &dvfm_dev_idx);
++
++ g_bpm_enabled = 1;
++ wake_up_interruptible(&g_bpm_enabled_waitq);
++ wake_up_interruptible(&g_bpm_event_queue.waitq);
++ wait_for_completion(&g_bpm_thread_over);
++ g_bpm_enabled = 0;
++}
++
++module_init(bpm_init);
++module_exit(bpm_exit);
++
++MODULE_DESCRIPTION("BPMD");
++MODULE_LICENSE("GPL");
+diff -ur linux-2.6.32/arch/arm/mach-pxa/bpm_prof.c kernel/arch/arm/mach-pxa/bpm_prof.c
+--- linux-2.6.32/arch/arm/mach-pxa/bpm_prof.c 2009-12-13 12:58:12.232379200 +0200
++++ kernel/arch/arm/mach-pxa/bpm_prof.c 2009-12-12 16:09:26.429614458 +0200
+@@ -0,0 +1,564 @@
++/*
++ * PXA3xx IPM Profiler
++ *
++ * Copyright (C) 2008 Borqs Ltd.
++ * Emichael Li <emichael.li@borqs.com>
++ *
++ * Based on Marvell v6.5 release.
++ *
++ * Copyright (C) 2008 Marvell Corporation
++ * Haojian Zhuang <haojian.zhuang@marvell.com>
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++
++ * (C) Copyright 2008 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/tick.h>
++#include <linux/timer.h>
++#include <linux/device.h>
++#include <linux/jiffies.h>
++#include <mach/hardware.h>
++#include <mach/mspm_prof.h>
++#include <asm/arch/ipmc.h>
++#ifdef CONFIG_PXA3xx_DVFM
++#include <asm/arch/dvfm.h>
++#include <asm/arch/pxa3xx_dvfm.h>
++#endif
++
++extern int (*pipm_start_pmu)(struct ipm_profiler_arg *arg);
++extern int (*pipm_stop_pmu)(void);
++
++/* IDLE profiler tune OP with MIPS feature */
++#define MSPM_IDLE_PROF_MIPS 0
++
++#undef MAX_OP_NUM
++#define MAX_OP_NUM 10
++
++struct mspm_op_stats {
++ int op;
++ int idle;
++ unsigned int timestamp;
++ unsigned int jiffies;
++};
++
++struct mspm_mips {
++ int mips;
++ int h_thres; /* high threshold */
++ int l_thres; /* low threshold */
++};
++
++/* Store costed time in run_op_time[] & idle_op_time[] */
++static int run_op_time[MAX_OP_NUM], idle_op_time[MAX_OP_NUM];
++
++/*
++ * Store OP's MIPS in op_mips[].
++ * The lowest frequency OP is the first entry.
++ */
++static struct mspm_mips op_mips[MAX_OP_NUM];
++
++/* Store the calculated MIPS of last sample window */
++static int last_mips;
++
++/*
++ * Store the first timestamp of sample window in first_stats
++ * Store the current timestamp of sample window in cur_stats
++ */
++static struct mspm_op_stats first_stats, cur_stats;
++
++/* OP numbers used in IPM IDLE Profiler */
++static int mspm_op_num = 0;
++
++static struct timer_list idle_prof_timer;
++
++/* PMU result is stored in it */
++static struct pmu_results sum_pmu_res;
++
++static int mspm_prof_enabled = 0;
++static int window_jif = 0;
++static int mspm_pmu_id;
++
++unsigned int prof_idle_time, prof_time;
++
++static int mspm_prof_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data);
++static struct notifier_block notifier_freq_block = {
++ .notifier_call = mspm_prof_notifier_freq,
++};
++
++static unsigned int read_time(void)
++{
++#ifdef CONFIG_PXA_32KTIMER
++ return OSCR4;
++#else
++ return OSCR0;
++#endif
++}
++
++
++static int bpm_mod_timer(struct timer_list *timer, unsigned long expires)
++{
++#ifdef CONFIG_BPMD
++ extern void timer_set_deferrable(struct timer_list *timer);
++ extern void timer_clr_deferrable(struct timer_list *timer);
++ extern int get_op_power_bonus(void);
++
++ if (get_op_power_bonus())
++ timer_set_deferrable(timer);
++ else
++ timer_clr_deferrable(timer);
++#endif
++ mod_timer(timer, expires);
++
++ return 0;
++}
++
++/*
++ * Record the OP index and RUN/IDLE state.
++ */
++int mspm_add_event(int op, int cpu_idle)
++{
++ unsigned int time;
++
++ if (mspm_prof_enabled) {
++ time = read_time();
++ /* sum the current sample window */
++ if (cpu_idle == CPU_STATE_IDLE)
++ idle_op_time[cur_stats.op] +=
++ time - cur_stats.timestamp;
++ else if (cpu_idle == CPU_STATE_RUN)
++ run_op_time[cur_stats.op] +=
++ time - cur_stats.timestamp;
++ /* update start point of current sample window */
++ cur_stats.op = op;
++ cur_stats.idle = cpu_idle;
++ cur_stats.timestamp = time;
++ cur_stats.jiffies = jiffies;
++ }
++ return 0;
++}
++EXPORT_SYMBOL(mspm_add_event);
++
++/*
++ * Prepare to do a new sample.
++ * Clear the index in mspm_op_stats table.
++ */
++static int mspm_do_new_sample(void)
++{
++ /* clear previous sample window */
++ memset(&run_op_time, 0, sizeof(int) * MAX_OP_NUM);
++ memset(&idle_op_time, 0, sizeof(int) * MAX_OP_NUM);
++ /* prepare for the new sample window */
++ first_stats.op = cur_stats.op;
++ first_stats.idle = cur_stats.idle;
++ first_stats.timestamp = read_time();
++ first_stats.jiffies = jiffies;
++
++ prof_idle_time = 0;
++ prof_time = read_time();
++ return 0;
++}
++
++/*
++ * Init MIPS of all OP
++ */
++static int mspm_init_mips(void)
++{
++ struct op_info *info = NULL;
++ struct dvfm_md_opt *md_op = NULL;
++ int i, ret;
++ memset(&op_mips, 0, MAX_OP_NUM * sizeof(struct mspm_mips));
++ mspm_op_num = dvfm_op_count();
++#ifdef CONFIG_PXA3xx_DVFM
++ for (i = 0; i < mspm_op_num; i++) {
++ ret = dvfm_get_opinfo(i, &info);
++ if (ret)
++ continue;
++ md_op = (struct dvfm_md_opt *)info->op;
++ op_mips[i].mips = md_op->core;
++ if (op_mips[i].mips) {
++ op_mips[i].h_thres = DEF_HIGH_THRESHOLD;
++ if (!strcmp(md_op->name, "D0CS"))
++ op_mips[i].h_thres = 95;
++ } else {
++ mspm_op_num = i;
++ break;
++ }
++ }
++ for (i = 0; i < mspm_op_num - 1; i++)
++ op_mips[i + 1].l_thres = op_mips[i].h_thres * op_mips[i].mips
++ / op_mips[i + 1].mips;
++#endif
++ return 0;
++}
++
++/*
++ * Calculate the MIPS in sample window
++ */
++static int mspm_calc_mips(void)
++{
++ int i;
++ unsigned int sum_time = 0, sum = 0;
++
++ /* Calculate total time costed in sample window */
++ for (i = 0; i < mspm_op_num; i++) {
++ sum_time += run_op_time[i] + idle_op_time[i];
++ sum += run_op_time[i] * op_mips[i].mips;
++ }
++ if (sum_time == 0)
++ return 0;
++
++ /*
++ * Calculate MIPS in sample window
++ * Formula: run_op_time[i] / sum_time * op_mips[i].mips
++ */
++ return (sum / sum_time);
++}
++
++static int is_valid_sample_window(void)
++{
++ unsigned int time;
++ /* The sample window isn't started */
++ if (!mspm_prof_enabled)
++ goto out;
++ time = cur_stats.jiffies - first_stats.jiffies;
++ time = jiffies_to_msecs(time);
++ if (time >= MIN_SAMPLE_WINDOW)
++ return 1;
++out:
++ return 0;
++}
++
++/*
++ * When DVFM release one OP, it will invoke this func to get the prefered OP.
++ */
++static int mspm_get_mips(void)
++{
++ int ret;
++ extern int cur_op;
++
++ mspm_add_event(cur_op, CPU_STATE_RUN);
++
++ if (!is_valid_sample_window()) {
++ /* This sample window is invalide, use MIPS value of last
++ * sample window
++ */
++ ret = last_mips;
++ goto out_sample;
++ }
++ ret = mspm_calc_mips();
++ if (ret < 0)
++ goto out_calc;
++ return ret;
++out_calc:
++ printk(KERN_WARNING "Can't calculate MIPS\n");
++out_sample:
++ return ret;
++}
++
++/*
++ * Adjust to the most appropriate OP according to MIPS result of
++ * sample window
++ */
++#if MSPM_IDLE_PROF_MIPS
++int mspm_tune(void)
++{
++ int i, mips;
++ if (mspm_prof_enabled) {
++ for (i = mspm_op_num - 1; i >= 0; i--) {
++ mips = mspm_get_mips();
++ if (mips >= (op_mips[i].l_thres *
++ op_mips[i].mips / 100))
++ break;
++ }
++ dvfm_request_op(i);
++ }
++ return 0;
++}
++#else
++int mspm_tune(void) { return 0; }
++#endif
++EXPORT_SYMBOL(mspm_tune);
++
++/***************************************************************************
++ * Idle Profiler
++ ***************************************************************************
++ */
++
++static struct ipm_profiler_arg pmu_arg;
++static int mspm_start_prof(struct ipm_profiler_arg *arg)
++{
++ struct pmu_results res;
++ struct op_info *info = NULL;
++
++ memset(&sum_pmu_res, 0, sizeof(struct pmu_results));
++
++ /* pmu_arg.window_size stores the number of miliseconds.
++ * window_jif stores the number of jiffies.
++ */
++ memset(&pmu_arg, 0, sizeof(struct ipm_profiler_arg));
++ pmu_arg.flags = arg->flags;
++ if (arg->window_size > 0)
++ pmu_arg.window_size = arg->window_size;
++ else
++ pmu_arg.window_size = DEF_SAMPLE_WINDOW;
++ window_jif = msecs_to_jiffies(pmu_arg.window_size);
++ if ((mspm_pmu_id > 0) && (pmu_arg.flags & IPM_PMU_PROFILER)) {
++ pmu_arg.pmn0 = arg->pmn0;
++ pmu_arg.pmn1 = arg->pmn1;
++ pmu_arg.pmn2 = arg->pmn2;
++ pmu_arg.pmn3 = arg->pmn3;
++ /* Collect PMU information */
++ if (pmu_stop(&res))
++ printk(KERN_WARNING
++ "L:%d: pmu_stop failed!\n", __LINE__);
++ if (pmu_start(pmu_arg.pmn0, pmu_arg.pmn1, pmu_arg.pmn2,
++ pmu_arg.pmn3))
++ printk(KERN_WARNING
++ "L:%d: pmu_start failed!\n", __LINE__);
++ }
++ /* start next sample window */
++ cur_stats.op = dvfm_get_op(&info);
++ cur_stats.idle = CPU_STATE_RUN;
++ cur_stats.timestamp = read_time();
++ cur_stats.jiffies = jiffies;
++ mspm_do_new_sample();
++ bpm_mod_timer(&idle_prof_timer, jiffies + window_jif);
++ mspm_prof_enabled = 1;
++ return 0;
++}
++
++static int mspm_stop_prof(void)
++{
++ struct pmu_results res;
++ if ((mspm_pmu_id > 0) && (pmu_arg.flags & IPM_PMU_PROFILER)) {
++ if (pmu_stop(&res))
++ printk(KERN_WARNING
++ "L:%d: pmu_stop failed!\n", __LINE__);
++ }
++ del_timer(&idle_prof_timer);
++ mspm_prof_enabled = 0;
++ return 0;
++}
++
++static int calc_pmu_res(struct pmu_results *res)
++{
++ if (res == NULL)
++ return -EINVAL;
++ sum_pmu_res.ccnt += res->ccnt;
++ sum_pmu_res.pmn0 += res->pmn0;
++ sum_pmu_res.pmn1 += res->pmn1;
++ sum_pmu_res.pmn2 += res->pmn2;
++ sum_pmu_res.pmn3 += res->pmn3;
++ return 0;
++}
++
++/*
++ * Pause idle profiler when system enter Low Power mode.
++ * Continue it when system exit from Low Power mode.
++ */
++void set_idletimer(int enable)
++{
++ struct pmu_results res;
++ if (enable && mspm_prof_enabled) {
++ /*
++ * Restart the idle profiler because it's only disabled
++ * before entering low power mode.
++ * If we just continue the sample window with left jiffies,
++ * too much OS Timer wakeup exist in system.
++ * Just restart the sample window.
++ */
++ bpm_mod_timer(&idle_prof_timer, jiffies + window_jif);
++ tick_nohz_restart_sched_tick();
++
++ first_stats.jiffies = jiffies;
++ first_stats.timestamp = read_time();
++
++ if (pmu_arg.flags & IPM_PMU_PROFILER) {
++ if (pmu_start(pmu_arg.pmn0, pmu_arg.pmn1, pmu_arg.pmn2,
++ pmu_arg.pmn3)) {
++ printk(KERN_WARNING
++ "L:%d: pmu_start failed!\n", __LINE__);
++ }
++ }
++ } else if (!enable && mspm_prof_enabled) {
++ del_timer(&idle_prof_timer);
++ tick_nohz_stop_sched_tick(1);
++
++ if (pmu_arg.flags & IPM_PMU_PROFILER) {
++ if (pmu_stop(&res)) {
++ printk(KERN_WARNING
++ "L:%d: pmu_stop failed!\n", __LINE__);
++ } else
++ calc_pmu_res(&res);
++ }
++ }
++}
++EXPORT_SYMBOL(set_idletimer);
++
++/*
++ * Handler of IDLE PROFILER
++ */
++static void idle_prof_handler(unsigned long data)
++{
++ struct ipm_profiler_result out_res;
++ struct pmu_results res;
++ struct op_info *info = NULL;
++ int ret, mips, op;
++
++ if (!mspm_prof_enabled)
++ return;
++
++ ret = mspm_get_mips();
++ if (ret >= 0)
++ mips = ret;
++ else
++ mips = last_mips;
++ if ((mspm_pmu_id > 0) && (pmu_arg.flags & IPM_PMU_PROFILER)) {
++ if (pmu_stop(&res))
++ printk(KERN_WARNING "pmu_stop failed %d\n", __LINE__);
++ else
++ calc_pmu_res(&res);
++ if (pmu_start(pmu_arg.pmn0, pmu_arg.pmn1, pmu_arg.pmn2,
++ pmu_arg.pmn3))
++ printk(KERN_WARNING "pmu_start failed %d\n", __LINE__);
++ memset(&out_res, 0, sizeof(struct ipm_profiler_result));
++ out_res.pmu.ccnt = sum_pmu_res.ccnt;
++ out_res.pmu.pmn0 = sum_pmu_res.pmn0;
++ out_res.pmu.pmn1 = sum_pmu_res.pmn1;
++ out_res.pmu.pmn2 = sum_pmu_res.pmn2;
++ out_res.pmu.pmn3 = sum_pmu_res.pmn3;
++ }
++ op = dvfm_get_op(&info);
++
++#if 0
++ /* When system is running, MIPS of current OP won't be zero. */
++ out_res.busy_ratio = mips * 100 / op_mips[op].mips;
++ out_res.window_size = jiffies_to_msecs(window_jif);
++#endif
++
++ prof_time = read_time() - prof_time;
++
++ out_res.busy_ratio = 100 - 100 * prof_idle_time / prof_time;
++ out_res.window_size = 0; /* not used */
++ out_res.mips = mips;
++
++ /* send PMU result to policy maker in user space */
++ bpm_event_notify(IPM_EVENT_PROFILER, pmu_arg.flags, &out_res,
++ sizeof(struct ipm_profiler_result));
++
++#if 0
++ /* start next sample window */
++ mspm_do_new_sample();
++ bpm_mod_timer(&idle_prof_timer, jiffies + window_jif);
++ memset(&sum_pmu_res, 0, sizeof(struct pmu_results));
++#endif
++ last_mips = mips;
++}
++
++/*
++ * Pause idle profiler when system enter Low Power mode.
++ * Continue it when system exit from Low Power mode.
++ */
++static int mspm_prof_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data)
++{
++ struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
++ struct op_info *info = &(freqs->new_info);
++ struct dvfm_md_opt *md = NULL;
++ struct pmu_results res;
++
++ if (!mspm_prof_enabled)
++ return 0;
++ md = (struct dvfm_md_opt *)(info->op);
++ if (md->power_mode == POWER_MODE_D1 ||
++ md->power_mode == POWER_MODE_D2 ||
++ md->power_mode == POWER_MODE_CG) {
++ switch (val) {
++ case DVFM_FREQ_PRECHANGE:
++ del_timer(&idle_prof_timer);
++ tick_nohz_stop_sched_tick(1);
++ if (pmu_arg.flags & IPM_PMU_PROFILER) {
++ if (pmu_stop(&res))
++ printk(KERN_WARNING
++ "L:%d: pmu_stop failed!\n",
++ __LINE__);
++ else
++ calc_pmu_res(&res);
++ }
++ break;
++ case DVFM_FREQ_POSTCHANGE:
++ /* Update jiffies and touch watchdog process */
++ tick_nohz_update_jiffies();
++ /*
++ * Restart the idle profiler because it's only
++ * disabled before entering low power mode.
++ * If we just continue the sample window with
++ * left jiffies, too much OS Timer wakeup exist
++ * in system.
++ * Just restart the sample window.
++ */
++ bpm_mod_timer(&idle_prof_timer, jiffies + window_jif);
++ first_stats.jiffies = jiffies;
++ first_stats.timestamp = read_time();
++
++ if (pmu_arg.flags & IPM_PMU_PROFILER)
++ if (pmu_start(pmu_arg.pmn0, pmu_arg.pmn1,
++ pmu_arg.pmn2, pmu_arg.pmn3))
++ printk(KERN_WARNING
++ "L:%d: pmu_start failed!\n",
++ __LINE__);
++ break;
++ }
++ }
++ return 0;
++}
++
++int __init mspm_prof_init(void)
++{
++ mspm_pmu_id = pmu_claim();
++
++ memset(&pmu_arg, 0, sizeof(struct ipm_profiler_arg));
++ pmu_arg.window_size = DEF_SAMPLE_WINDOW;
++ pmu_arg.pmn0 = PMU_EVENT_POWER_SAVING;
++ pmu_arg.pmn1 = PMU_EVENT_POWER_SAVING;
++ pmu_arg.pmn2 = PMU_EVENT_POWER_SAVING;
++ pmu_arg.pmn3 = PMU_EVENT_POWER_SAVING;
++ window_jif = msecs_to_jiffies(pmu_arg.window_size);
++
++ pipm_start_pmu = mspm_start_prof;
++ pipm_stop_pmu = mspm_stop_prof;
++
++ /* It's used to trigger sample window.
++ * If system is idle, the timer could be deferred.
++ */
++ init_timer(&idle_prof_timer);
++ idle_prof_timer.function = idle_prof_handler;
++ idle_prof_timer.data = 0;
++
++ mspm_init_mips();
++
++ dvfm_register_notifier(&notifier_freq_block,
++ DVFM_FREQUENCY_NOTIFIER);
++
++ return 0;
++}
++
++void __exit mspm_prof_exit(void)
++{
++ dvfm_unregister_notifier(&notifier_freq_block,
++ DVFM_FREQUENCY_NOTIFIER);
++
++ if (mspm_pmu_id)
++ pmu_release(mspm_pmu_id);
++
++ pipm_start_pmu = NULL;
++ pipm_stop_pmu = NULL;
++}
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/devices.c kernel/arch/arm/mach-pxa/devices.c
+--- linux-2.6.32/arch/arm/mach-pxa/devices.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/mach-pxa/devices.c 2009-12-12 16:09:26.436277478 +0200
+@@ -15,6 +15,7 @@
+ #include <mach/camera.h>
+ #include <mach/audio.h>
+ #include <mach/pxa3xx_nand.h>
++#include <mach/pxa3xx_dvfm.h>
+
+ #include "devices.h"
+ #include "generic.h"
+@@ -962,6 +963,76 @@
+ },
+ };
+
++static struct resource pxa3xx_resource_freq[] = {
++ [0] = {
++ .name = "clkmgr_regs",
++ .start = 0x41340000,
++ .end = 0x41350003,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .name = "spmu_regs",
++ .start = 0x40f50000,
++ .end = 0x40f50103,
++ .flags = IORESOURCE_MEM,
++ },
++ [2] = {
++ .name = "bpmu_regs",
++ .start = 0x40f40000,
++ .end = 0x40f4003b,
++ .flags = IORESOURCE_MEM,
++ },
++ [3] = {
++ .name = "dmc_regs",
++ .start = 0x48100000,
++ .end = 0x4810012f,
++ .flags = IORESOURCE_MEM,
++ },
++ [4] = {
++ .name = "smc_regs",
++ .start = 0x4a000000,
++ .end = 0x4a00008f,
++ .flags = IORESOURCE_MEM,
++ }
++};
++
++struct platform_device pxa3xx_device_freq = {
++ .name = "pxa3xx-freq",
++ .id = 0,
++ .num_resources = ARRAY_SIZE(pxa3xx_resource_freq),
++ .resource = pxa3xx_resource_freq,
++};
++
++void __init set_pxa3xx_freq_info(struct pxa3xx_freq_mach_info *info)
++{
++ pxa_register_device(&pxa3xx_device_freq, info);
++}
++
++void __init set_pxa3xx_freq_parent(struct device *parent_dev)
++{
++ pxa3xx_device_freq.dev.parent = parent_dev;
++}
++
++static struct resource pxa3xx_pmu_resources[] = {
++ [0] = {
++ .name = "pmu_regs",
++ .start = 0x4600ff00,
++ .end = 0x4600ffff,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++struct platform_device pxa3xx_device_pmu = {
++ .name = "pxa3xx-pmu",
++ .id = 0,
++ .resource = pxa3xx_pmu_resources,
++ .num_resources = ARRAY_SIZE(pxa3xx_pmu_resources),
++};
++
++void __init pxa3xx_set_pmu_info(void *info)
++{
++ pxa_register_device(&pxa3xx_device_pmu, info);
++}
+ #endif /* CONFIG_PXA3xx */
+
+ /* pxa2xx-spi platform-device ID equals respective SSP platform-device ID + 1.
+diff -ur linux-2.6.32/arch/arm/mach-pxa/devices.h kernel/arch/arm/mach-pxa/devices.h
+--- linux-2.6.32/arch/arm/mach-pxa/devices.h 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/mach-pxa/devices.h 2009-12-12 16:09:26.436277478 +0200
+@@ -36,5 +36,6 @@
+ extern struct platform_device pxa3xx_device_i2c_power;
+
+ extern struct platform_device pxa3xx_device_gcu;
++extern struct platform_device pxa3xx_device_freq;
+
+ void __init pxa_register_device(struct platform_device *dev, void *data);
+diff -ur linux-2.6.32/arch/arm/mach-pxa/dvfm.c kernel/arch/arm/mach-pxa/dvfm.c
+--- linux-2.6.32/arch/arm/mach-pxa/dvfm.c 2009-12-13 12:58:54.725287534 +0200
++++ kernel/arch/arm/mach-pxa/dvfm.c 2009-12-12 16:09:26.439612372 +0200
+@@ -0,0 +1,922 @@
++/*
++ * DVFM Abstract Layer
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++
++ * (C) Copyright 2007 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/sysdev.h>
++#include <linux/spinlock.h>
++#include <linux/notifier.h>
++#include <linux/string.h>
++#include <linux/kobject.h>
++#include <linux/list.h>
++#include <linux/notifier.h>
++#include <asm/atomic.h>
++#include <mach/dvfm.h>
++
++#ifdef CONFIG_BPMD
++#include <mach/bpm.h>
++
++extern int bpm_enable_op(int index, int dev_idx);
++extern int bpm_disable_op(int index, int dev_idx);
++extern int bpm_enable_op_name(char *name, int dev_idx, char *sid);
++extern int bpm_disable_op_name(char *name, int dev_idx, char *sid);
++#endif
++
++#define MAX_DEVNAME_LEN 32
++/* This structure is used to dump device name list */
++struct name_list {
++ int id;
++ char name[MAX_DEVNAME_LEN];
++};
++
++static ATOMIC_NOTIFIER_HEAD(dvfm_freq_notifier_list);
++
++/* This list links log of dvfm operation */
++struct info_head dvfm_trace_list = {
++ .list = LIST_HEAD_INIT(dvfm_trace_list.list),
++ .lock = RW_LOCK_UNLOCKED,
++ .device = 0,
++};
++
++#ifndef CONFIG_BPMD
++/* This idx is used for user debug */
++static int dvfm_dev_idx;
++#endif
++
++struct dvfm_driver *dvfm_driver = NULL;
++struct info_head *dvfm_op_list = NULL;
++
++unsigned int cur_op; /* current operating point */
++unsigned int def_op; /* default operating point */
++unsigned int op_nums = 0; /* number of operating point */
++
++static atomic_t lp_count = ATOMIC_INIT(0); /* number of blocking lowpower mode */
++
++extern struct sysdev_class cpu_sysdev_class;
++
++int dvfm_find_op(int index, struct op_info **op)
++{
++ struct op_info *p = NULL;
++
++ read_lock(&dvfm_op_list->lock);
++ if (list_empty(&dvfm_op_list->list)) {
++ read_unlock(&dvfm_op_list->lock);
++ return -ENOENT;
++ }
++ list_for_each_entry(p, &dvfm_op_list->list, list) {
++ if (p->index == index) {
++ *op = p;
++ read_unlock(&dvfm_op_list->lock);
++ return 0;
++ }
++ }
++ read_unlock(&dvfm_op_list->lock);
++ return -ENOENT;
++}
++
++#ifndef CONFIG_BPMD
++/* Display current operating point */
++static ssize_t op_show(struct sys_device *sys_dev, struct sysdev_attribute *attr,char *buf)
++{
++ struct op_info *op = NULL;
++ int len = 0;
++
++ if (dvfm_driver->dump) {
++ if (!dvfm_find_op(cur_op, &op)) {
++ len = dvfm_driver->dump(dvfm_driver->priv, op, buf);
++ }
++ }
++
++ return len;
++}
++
++/* Set current operating point */
++static ssize_t op_store(struct sys_device *sys_dev, struct sysdev_attribute *attr, const char *buf,
++ size_t len)
++{
++ struct dvfm_freqs freqs;
++ int new_op;
++
++ sscanf(buf, "%u", &new_op);
++ dvfm_request_op(new_op);
++ return len;
++}
++SYSDEV_ATTR(op, 0644, op_show, op_store);
++
++/* Dump all operating point */
++static ssize_t ops_show(struct sys_device *sys_dev, struct sysdev_attribute *attr, char *buf)
++{
++ struct op_info *entry = NULL;
++ int len = 0;
++ char *p = NULL;
++
++ if (!dvfm_driver->dump)
++ return 0;
++ read_lock(&dvfm_op_list->lock);
++ if (!list_empty(&dvfm_op_list->list)) {
++ list_for_each_entry(entry, &dvfm_op_list->list, list) {
++ p = buf + len;
++ len += dvfm_driver->dump(dvfm_driver->priv, entry, p);
++ }
++ }
++ read_unlock(&dvfm_op_list->lock);
++
++ return len;
++}
++SYSDEV_ATTR(ops, 0444, ops_show, NULL);
++
++/* Dump all enabled operating point */
++static ssize_t enable_op_show(struct sys_device *sys_dev, struct sysdev_attribute *attr, char *buf)
++{
++ struct op_info *entry = NULL;
++ int len = 0;
++ char *p = NULL;
++
++ if (!dvfm_driver->dump)
++ return 0;
++ read_lock(&dvfm_op_list->lock);
++ if (!list_empty(&dvfm_op_list->list)) {
++ list_for_each_entry(entry, &dvfm_op_list->list, list) {
++ if (!entry->device) {
++ p = buf + len;
++ len += dvfm_driver->dump(dvfm_driver->priv, entry, p);
++ }
++ }
++ }
++ read_unlock(&dvfm_op_list->lock);
++
++ return len;
++}
++
++static ssize_t enable_op_store(struct sys_device *sys_dev, struct sysdev_attribute *attr, const char *buf,
++ size_t len)
++{
++ int op, level;
++
++ sscanf(buf, "%u,%u", &op, &level);
++ if (level) {
++ dvfm_enable_op(op, dvfm_dev_idx);
++ } else
++ dvfm_disable_op(op, dvfm_dev_idx);
++ return len;
++}
++SYSDEV_ATTR(enable_op, 0644, enable_op_show, enable_op_store);
++
++/*
++ * Dump blocked device on specified OP.
++ * And dump the device list that is tracked.
++ */
++static ssize_t trace_show(struct sys_device *sys_dev, struct sysdev_attribute *attr, char *buf)
++{
++ struct op_info *op_entry = NULL;
++ struct dvfm_trace_info *entry = NULL;
++ int len = 0, i;
++ unsigned int blocked_dev;
++
++ for (i = 0; i < op_nums; i++) {
++ blocked_dev = 0;
++ read_lock(&dvfm_op_list->lock);
++ /* op list shouldn't be empty because op_nums is valid */
++ list_for_each_entry(op_entry, &dvfm_op_list->list, list) {
++ if (op_entry->index == i)
++ blocked_dev = op_entry->device;
++ }
++ read_unlock(&dvfm_op_list->lock);
++ if (!blocked_dev)
++ continue;
++
++ len += sprintf(buf + len, "Blocked devices on OP%d:", i);
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ if (test_bit(entry->index, (void *)&blocked_dev))
++ len += sprintf(buf + len, "%s, ", entry->name);
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ len += sprintf(buf + len, "\n");
++ }
++ if (len == 0)
++ len += sprintf(buf + len, "None device block OP\n");
++ len += sprintf(buf + len, "Trace device list:\n");
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ len += sprintf(buf + len, "%s, ", entry->name);
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ len += sprintf(buf + len, "\n");
++ return len;
++}
++SYSDEV_ATTR(trace, 0444, trace_show, NULL);
++
++#ifdef CONFIG_CPU_PXA310
++static ssize_t freq_show(struct sys_device *sys_dev, struct sysdev_attribute *attr, char *buf)
++{
++ struct op_info *op = NULL;
++ int len = 0;
++
++ if (dvfm_driver->freq_show) {
++ if (!dvfm_find_op(cur_op, &op)) {
++ len = dvfm_driver->freq_show(dvfm_driver->priv, op, buf);
++ }
++ }
++
++ return len;
++}
++/*
++ * We can define a freq_store to set frequencies with a lot of parameters,
++ * If a new set of frequencies is inputed by that way, it will only be treated
++ * as a non-standard op, not a new op. So the freq_store function isn't defined.
++ */
++SYSDEV_ATTR(frequency, 0644, freq_show, NULL);
++#endif
++
++static struct attribute *dvfm_attr[] = {
++ &attr_op.attr,
++ &attr_ops.attr,
++ &attr_enable_op.attr,
++ &attr_trace.attr,
++#ifdef CONFIG_CPU_PXA310
++ &attr_frequency.attr,
++#endif
++};
++#endif
++
++int dvfm_op_count(void)
++{
++ int ret = -EINVAL;
++
++ if (dvfm_driver && dvfm_driver->count)
++ ret = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list);
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_op_count);
++
++int dvfm_get_op(struct op_info **p)
++{
++ if (dvfm_find_op(cur_op, p))
++ return -EINVAL;
++ return cur_op;
++}
++EXPORT_SYMBOL(dvfm_get_op);
++
++int dvfm_dump_op(int idx, char *buf)
++{
++ struct op_info *op = NULL;
++ int len = 0;
++
++ if (dvfm_driver && dvfm_driver->dump && !dvfm_find_op(idx, &op))
++ len = dvfm_driver->dump(dvfm_driver->priv, op, buf);
++
++ return len;
++}
++EXPORT_SYMBOL(dvfm_dump_op);
++
++int dvfm_get_op_freq(int idx, struct op_freq *pf)
++{
++ struct op_info *op = NULL;
++ int ret = 0;
++
++ if (dvfm_driver && dvfm_driver->get_freq && !dvfm_find_op(idx, &op))
++ ret = dvfm_driver->get_freq(dvfm_driver->priv, op, pf);
++
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_get_op_freq);
++
++int dvfm_check_active_op(int idx)
++{
++ struct op_info *op = NULL;
++ int ret = 0;
++
++ if (dvfm_driver && dvfm_driver->check_active_op && !dvfm_find_op(idx, &op))
++ ret = dvfm_driver->check_active_op(dvfm_driver->priv, op);
++
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_check_active_op);
++
++int dvfm_get_defop(void)
++{
++ return def_op;
++}
++EXPORT_SYMBOL(dvfm_get_defop);
++
++int dvfm_get_opinfo(int index, struct op_info **p)
++{
++ if (dvfm_find_op(index, p))
++ return -EINVAL;
++ return 0;
++}
++EXPORT_SYMBOL(dvfm_get_opinfo);
++
++
++const char* dvfm_get_op_name(int idx)
++{
++ struct op_info *op = NULL;
++
++ if (dvfm_driver && dvfm_driver->name && !dvfm_find_op(idx, &op))
++ return dvfm_driver->name(dvfm_driver->priv, op);
++
++ return NULL;
++}
++EXPORT_SYMBOL(dvfm_get_op_name);
++
++
++int dvfm_set_op(struct dvfm_freqs *freqs, unsigned int new,
++ unsigned int relation)
++{
++ int ret = -EINVAL;
++
++ /* check whether dvfm is enabled */
++ if (!dvfm_driver || !dvfm_driver->count)
++ return -EINVAL;
++ if (dvfm_driver->set)
++ ret = dvfm_driver->set(dvfm_driver->priv, freqs, new, relation);
++ return ret;
++}
++
++/* Request operating point. System may set higher frequency because of
++ * device constraint.
++ */
++int dvfm_request_op(int index)
++{
++ int ret = -EFAULT;
++
++ /* check whether dvfm is enabled */
++ if (!dvfm_driver || !dvfm_driver->count)
++ return -EINVAL;
++#ifdef CONFIG_BPMD
++ printk(KERN_ERR "please don't use this API\n");
++ WARN_ON(1);
++#endif
++
++ if (dvfm_driver->request_set)
++ ret = dvfm_driver->request_set(dvfm_driver->priv, index);
++
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_request_op);
++
++/*
++ * Device remove the constraint on OP.
++ */
++int __dvfm_enable_op(int index, int dev_idx)
++{
++ struct op_info *p = NULL;
++ int num;
++
++ /* check whether dvfm is enabled */
++ if (!dvfm_driver || !dvfm_driver->count)
++ return -EINVAL;
++ /* only registered device can invoke DVFM operation */
++ if ((dev_idx >= DVFM_MAX_DEVICE) || dev_idx < 0)
++ return -ENOENT;
++ num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list);
++ if (num <= index)
++ return -ENOENT;
++ if (!dvfm_find_op(index, &p)) {
++ write_lock(&dvfm_op_list->lock);
++ /* remove device ID */
++ clear_bit(dev_idx, (void *)&p->device);
++ write_unlock(&dvfm_op_list->lock);
++#ifndef CONFIG_BPMD
++ dvfm_driver->enable_op(dvfm_driver->priv, index, RELATION_LOW);
++#endif
++
++ }
++ return 0;
++}
++
++/*
++ * Device set constraint on OP
++ */
++int __dvfm_disable_op(int index, int dev_idx)
++{
++ struct op_info *p = NULL;
++ int num;
++
++ /* check whether dvfm is enabled */
++ if (!dvfm_driver || !dvfm_driver->count)
++ return -EINVAL;
++ /* only registered device can invoke DVFM operation */
++ if ((dev_idx >= DVFM_MAX_DEVICE) || dev_idx < 0)
++ return -ENOENT;
++ num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list);
++ if (num <= index)
++ return -ENOENT;
++ if (!dvfm_find_op(index, &p)) {
++ write_lock(&dvfm_op_list->lock);
++ /* set device ID */
++ set_bit(dev_idx, (void *)&p->device);
++ write_unlock(&dvfm_op_list->lock);
++ dvfm_driver->disable_op(dvfm_driver->priv, index, RELATION_LOW);
++ }
++ return 0;
++}
++
++int __dvfm_disable_op2(int index, int dev_idx)
++{
++ struct op_info *p = NULL;
++ int num;
++
++ if (!dvfm_driver || !dvfm_driver->count) {
++ return -ENOENT;
++ }
++ num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list);
++ if (num <= index)
++ return -ENOENT;
++ if (!dvfm_find_op(index, &p)) {
++ write_lock(&dvfm_op_list->lock);
++ set_bit(dev_idx, (void *)&p->device);
++ write_unlock(&dvfm_op_list->lock);
++ }
++ return 0;
++}
++
++int dvfm_enable_op(int index, int dev_idx)
++{
++#ifdef CONFIG_BPMD
++ bpm_enable_op(index, dev_idx);
++#else
++ __dvfm_enable_op(index, dev_idx);
++#endif
++ return 0;
++}
++
++int dvfm_disable_op(int index, int dev_idx)
++{
++#ifdef CONFIG_BPMD
++ bpm_disable_op(index, dev_idx);
++#else
++ __dvfm_disable_op(index, dev_idx);
++#endif
++ return 0;
++}
++
++EXPORT_SYMBOL(dvfm_enable_op);
++EXPORT_SYMBOL(dvfm_disable_op);
++
++int __dvfm_enable_op_name(char *name, int dev_idx)
++{
++ struct op_info *p = NULL;
++ int index;
++
++ if (!dvfm_driver || !dvfm_driver->name || !name)
++ return -EINVAL;
++ /* only registered device can invoke DVFM operation */
++ if ((dev_idx >= DVFM_MAX_DEVICE) || dev_idx < 0)
++ return -ENOENT;
++ list_for_each_entry(p, &dvfm_op_list->list, list) {
++ if (!strcmp(dvfm_driver->name(dvfm_driver->priv, p), name)) {
++ index = p->index;
++ write_lock(&dvfm_op_list->lock);
++ clear_bit(dev_idx, (void *)&p->device);
++ write_unlock(&dvfm_op_list->lock);
++ dvfm_driver->enable_op(dvfm_driver->priv,
++ index, RELATION_LOW);
++ break;
++ }
++ }
++ return 0;
++}
++
++int __dvfm_disable_op_name(char *name, int dev_idx)
++{
++ struct op_info *p = NULL;
++ int index;
++
++ if (!dvfm_driver || !dvfm_driver->name || !name)
++ return -EINVAL;
++ /* only registered device can invoke DVFM operation */
++ if ((dev_idx >= DVFM_MAX_DEVICE) || dev_idx < 0)
++ return -ENOENT;
++ list_for_each_entry(p, &dvfm_op_list->list, list) {
++ if (!strcmp(dvfm_driver->name(dvfm_driver->priv, p), name)) {
++ index = p->index;
++ write_lock(&dvfm_op_list->lock);
++ set_bit(dev_idx, (void *)&p->device);
++ write_unlock(&dvfm_op_list->lock);
++ dvfm_driver->disable_op(dvfm_driver->priv,
++ index, RELATION_LOW);
++ break;
++ }
++ }
++ return 0;
++}
++
++/*
++EXPORT_SYMBOL(dvfm_enable_op_name);
++EXPORT_SYMBOL(dvfm_disable_op_name);
++*/
++
++int _dvfm_enable_op_name(char *name, int dev_idx, char *sid)
++{
++ int ret;
++#ifdef CONFIG_BPMD
++ ret = bpm_enable_op_name(name, dev_idx, sid);
++#else
++ ret = __dvfm_enable_op_name(name, dev_idx);
++#endif
++ return ret;
++}
++
++int _dvfm_disable_op_name(char *name, int dev_idx, char *sid)
++{
++ int ret;
++#ifdef CONFIG_BPMD
++ ret = bpm_disable_op_name(name, dev_idx, sid);
++#else
++ ret = __dvfm_disable_op_name(name, dev_idx);
++#endif
++ return ret;
++}
++
++EXPORT_SYMBOL(_dvfm_enable_op_name);
++EXPORT_SYMBOL(_dvfm_disable_op_name);
++
++/* Only enable those safe operating point */
++int dvfm_enable(int dev_idx)
++{
++ printk(KERN_WARNING "dvfm_enable() is not preferred\n");
++ WARN_ON(1);
++ if (!dvfm_driver || !dvfm_driver->count || !dvfm_driver->enable_dvfm)
++ return -ENOENT;
++ return dvfm_driver->enable_dvfm(dvfm_driver->priv, dev_idx);
++}
++
++/* return whether the result is zero */
++int dvfm_disable(int dev_idx)
++{
++ printk(KERN_WARNING "dvfm_disable() is not preferred\n");
++ WARN_ON(1);
++ if (!dvfm_driver || !dvfm_driver->count || !dvfm_driver->disable_dvfm)
++ return -ENOENT;
++ return dvfm_driver->disable_dvfm(dvfm_driver->priv, dev_idx);
++}
++
++/* return whether the result is zero */
++int dvfm_enable_pm(void)
++{
++ return atomic_inc_and_test(&lp_count);
++}
++
++/* return whether the result is zero */
++int dvfm_disable_pm(void)
++{
++ return atomic_dec_and_test(&lp_count);
++}
++
++int dvfm_notifier_frequency(struct dvfm_freqs *freqs, unsigned int state)
++{
++ int ret;
++
++ switch (state) {
++ case DVFM_FREQ_PRECHANGE:
++ ret = atomic_notifier_call_chain(&dvfm_freq_notifier_list,
++ DVFM_FREQ_PRECHANGE, freqs);
++ if (ret != NOTIFY_DONE)
++ pr_debug("Failure in device driver before "
++ "switching frequency\n");
++ break;
++ case DVFM_FREQ_POSTCHANGE:
++ ret = atomic_notifier_call_chain(&dvfm_freq_notifier_list,
++ DVFM_FREQ_POSTCHANGE, freqs);
++ if (ret != NOTIFY_DONE)
++ pr_debug("Failure in device driver after "
++ "switching frequency\n");
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++int dvfm_register_notifier(struct notifier_block *nb, unsigned int list)
++{
++ int ret;
++
++ switch (list) {
++ case DVFM_FREQUENCY_NOTIFIER:
++ ret = atomic_notifier_chain_register(
++ &dvfm_freq_notifier_list, nb);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_register_notifier);
++
++int dvfm_unregister_notifier(struct notifier_block *nb, unsigned int list)
++{
++ int ret;
++
++ switch (list) {
++ case DVFM_FREQUENCY_NOTIFIER:
++ ret = atomic_notifier_chain_unregister(
++ &dvfm_freq_notifier_list, nb);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++EXPORT_SYMBOL(dvfm_unregister_notifier);
++
++/*
++ * add device into trace list
++ * return device index
++ */
++static int add_device(char *name)
++{
++ struct dvfm_trace_info *entry = NULL, *new = NULL;
++ int min;
++
++ min = find_first_zero_bit(&dvfm_trace_list.device, DVFM_MAX_DEVICE);
++ if (min == DVFM_MAX_DEVICE)
++ return -EINVAL;
++
++ /* If device trace table is NULL */
++ new = kzalloc(sizeof(struct dvfm_trace_info), GFP_ATOMIC);
++ if (new == NULL)
++ goto out_mem;
++ /* add new item */
++ strcpy(new->name, name);
++ new->index = min;
++ /* insert the new item in increasing order */
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ if (entry->index > min) {
++ list_add_tail(&(new->list), &(entry->list));
++ goto inserted;
++ }
++ }
++ list_add_tail(&(new->list), &(dvfm_trace_list.list));
++inserted:
++ set_bit(min, (void *)&dvfm_trace_list.device);
++
++ return min;
++out_mem:
++ return -ENOMEM;
++}
++
++/*
++ * Query the device number that registered in DVFM
++ */
++int dvfm_query_device_num(void)
++{
++ int count = 0;
++ struct dvfm_trace_info *entry = NULL;
++
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ count++;
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ return count;
++}
++EXPORT_SYMBOL(dvfm_query_device_num);
++
++/*
++ * Query all device name that registered in DVFM
++ */
++int dvfm_query_device_list(void *mem, int len)
++{
++ int count = 0, size;
++ struct dvfm_trace_info *entry = NULL;
++ struct name_list *p = (struct name_list *)mem;
++
++ count = dvfm_query_device_num();
++ size = sizeof(struct name_list);
++ if (len < count * size)
++ return -ENOMEM;
++
++ read_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(entry, &dvfm_trace_list.list, list) {
++ p->id = entry->index;
++ strcpy(p->name, entry->name);
++ p++;
++ }
++ read_unlock(&dvfm_trace_list.lock);
++ return 0;
++}
++EXPORT_SYMBOL(dvfm_query_device_list);
++
++/*
++ * Device driver register itself to DVFM before any operation.
++ * The number of registered device is limited in 32.
++ */
++int dvfm_register(char *name, int *id)
++{
++ struct dvfm_trace_info *p = NULL;
++ int len, idx;
++
++ if (name == NULL)
++ return -EINVAL;
++
++ /* device name is stricted in 32 bytes */
++ len = strlen(name);
++ if (len > DVFM_MAX_NAME)
++ len = DVFM_MAX_NAME;
++ write_lock(&dvfm_trace_list.lock);
++ list_for_each_entry(p, &dvfm_trace_list.list, list) {
++ if (!strcmp(name, p->name)) {
++ /*
++ * Find device in device trace table
++ * Skip to allocate new ID
++ */
++ *id = p->index;
++ goto out;
++ }
++ }
++ idx = add_device(name);
++ if (idx < 0)
++ goto out_num;
++ *id = idx;
++out:
++ write_unlock(&dvfm_trace_list.lock);
++ return 0;
++out_num:
++ write_unlock(&dvfm_trace_list.lock);
++ return -EINVAL;
++}
++EXPORT_SYMBOL(dvfm_register);
++
++/*
++ * Release the device and free the device index.
++ */
++int dvfm_unregister(char *name, int *id)
++{
++ struct op_info *q = NULL;
++ struct dvfm_trace_info *p = NULL;
++ int len, num, i;
++
++ if (!dvfm_driver || !dvfm_driver->count || (name == NULL))
++ return -EINVAL;
++
++ /* device name is stricted in 32 bytes */
++ len = strlen(name);
++ if (len > DVFM_MAX_NAME)
++ len = DVFM_MAX_NAME;
++
++ num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list);
++
++ write_lock(&dvfm_trace_list.lock);
++ if (list_empty(&dvfm_trace_list.list))
++ goto out;
++ list_for_each_entry(p, &dvfm_trace_list.list, list) {
++ if (!strncmp(name, p->name, len)) {
++ for (i = 0; i < num; ++i) {
++ if (!dvfm_find_op(i, &q)) {
++ write_lock(&dvfm_op_list->lock);
++ if (test_bit(p->index, (void *)&q->device)) {
++ printk(KERN_ERR "%s uses PM interface unrightly, please clean the constraint before quit!\n", name);
++ dvfm_enable_op(i, p->index);
++ }
++ write_unlock(&dvfm_op_list->lock);
++ }
++ }
++
++ /* clear the device index */
++ clear_bit(*id, (void *)&dvfm_trace_list.device);
++ *id = -1;
++ list_del(&p->list);
++ kfree(p);
++ break;
++ }
++ }
++ write_unlock(&dvfm_trace_list.lock);
++ return 0;
++out:
++ write_unlock(&dvfm_trace_list.lock);
++ return -ENOENT;
++}
++EXPORT_SYMBOL(dvfm_unregister);
++
++#ifndef CONFIG_BPMD
++static int dvfm_add(struct sys_device *sys_dev)
++{
++ int i, n;
++ int ret;
++
++ n = ARRAY_SIZE(dvfm_attr);
++ for (i = 0; i < n; i++) {
++ ret = sysfs_create_file(&(sys_dev->kobj), dvfm_attr[i]);
++ if (ret)
++ return -EIO;
++ }
++ return 0;
++}
++
++static int dvfm_rm(struct sys_device *sys_dev)
++{
++ int i, n;
++ n = ARRAY_SIZE(dvfm_attr);
++ for (i = 0; i < n; i++) {
++ sysfs_remove_file(&(sys_dev->kobj), dvfm_attr[i]);
++ }
++ return 0;
++}
++
++static int dvfm_suspend(struct sys_device *sysdev, pm_message_t pmsg)
++{
++ return 0;
++}
++
++static int dvfm_resume(struct sys_device *sysdev)
++{
++ return 0;
++}
++
++static struct sysdev_driver dvfm_sysdev_driver = {
++ .add = dvfm_add,
++ .remove = dvfm_rm,
++ .suspend = dvfm_suspend,
++ .resume = dvfm_resume,
++};
++#endif
++
++int dvfm_register_driver(struct dvfm_driver *driver_data, struct info_head *op_list)
++{
++ int ret = 0;
++ if (!driver_data || !driver_data->set)
++ return -EINVAL;
++ if (dvfm_driver)
++ return -EBUSY;
++ dvfm_driver = driver_data;
++
++ if (!op_list)
++ return -EINVAL;
++ dvfm_op_list = op_list;
++
++#ifndef CONFIG_BPMD
++ /* enable_op need to invoke dvfm operation */
++ dvfm_register("User", &dvfm_dev_idx);
++ ret = sysdev_driver_register(&cpu_sysdev_class, &dvfm_sysdev_driver);
++#endif
++ return ret;
++}
++
++int dvfm_unregister_driver(struct dvfm_driver *driver)
++{
++#ifndef CONFIG_BPMD
++ sysdev_driver_unregister(&cpu_sysdev_class, &dvfm_sysdev_driver);
++ dvfm_unregister("User", &dvfm_dev_idx);
++#endif
++ dvfm_driver = NULL;
++ return 0;
++}
++
++unsigned int NextWakeupTimeAbs;
++unsigned int AppsSyncEnabled = 0;
++
++//this function should be called form ACIPC driver when comm relenquish events occurs
++int dvfm_notify_next_comm_wakeup_time(unsigned int NextWakeupTimeRel)
++{
++ unsigned int TimeStamp;
++
++ TimeStamp = dvfm_driver->read_time();
++
++ if (NextWakeupTimeRel == 0)
++ {
++ AppsSyncEnabled = 0;
++ }
++ else
++ {
++ AppsSyncEnabled = 1;
++ }
++ //we receive the next relative comm wakeup time and add to current TS to get the absolute time of the next comm wakeup.
++ //this value is stored in a global variable for future use. this should be done every time the comm side goes to D2
++ NextWakeupTimeAbs = NextWakeupTimeRel + TimeStamp;
++ return 0;
++}
++
++//this function should be called from mspm_idle when we want to go to D2 to check when the next wakeup will occur.
++int dvfm_is_comm_wakep_near(void)
++{
++ unsigned int TimeStamp;
++ TimeStamp = dvfm_driver->read_time();
++
++ //if the feature is not enabled we should not prevent D2.
++ if (!AppsSyncEnabled)
++ return 0;
++
++ if (NextWakeupTimeAbs - TimeStamp < APPS_COMM_D2_THRESHOLD)
++ {
++ return (NextWakeupTimeAbs - TimeStamp); //preventing D2
++ }
++ else
++ {
++ return 0; //allowing D2
++ }
++}
++
++MODULE_DESCRIPTION("Basic DVFM support for Monahans");
++MODULE_LICENSE("GPL");
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/bpm.h kernel/arch/arm/mach-pxa/include/mach/bpm.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/bpm.h 2009-12-13 12:59:07.871960663 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/bpm.h 2009-12-12 16:09:26.446281263 +0200
+@@ -0,0 +1,57 @@
++/*
++ * Copyright (C) 2003-2004 Intel Corporation.
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++ *
++ *
++ * (C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ *
++ * (C) Copyright 2008 Borqs Corporation.
++ * All Rights Reserved
++ */
++
++#ifndef __BPM_H__
++#define __BPM_H__
++
++#ifdef __KERNEL__
++
++/* 10 BPM event max */
++#define MAX_BPM_EVENT_NUM 10
++#define INFO_SIZE 128
++
++struct bpm_event {
++ int type; /* What type of IPM events. */
++ int kind; /* What kind, or sub-type of events. */
++ unsigned char info[INFO_SIZE]; /* events specific data. */
++};
++
++/* IPM events queue */
++struct bpm_event_queue{
++ int head;
++ int tail;
++ int len;
++ struct bpm_event bpmes[MAX_BPM_EVENT_NUM];
++ wait_queue_head_t waitq;
++};
++
++/* IPM event types. */
++#define IPM_EVENT_PROFILER 0x7 /* Profiler events. */
++
++#define IPM_EVENT_BLINK (0xA0)
++
++/* IPM event kinds. */
++#define IPM_EVENT_IDLE_PROFILER 0x1
++#define IPM_EVENT_PERF_PROFILER 0x2
++
++#define IPM_EVENT_BLINK_SPEEDUP (0x1)
++
++/* IPM event infos, not defined yet. */
++#define IPM_EVENT_NULLINFO 0x0
++
++/* IPM functions */
++extern int bpm_event_notify(int type, int kind, void *info, unsigned int info_len);
++#endif
++
++#endif
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/dvfm.h kernel/arch/arm/mach-pxa/include/mach/dvfm.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/dvfm.h 2009-12-13 12:59:13.655291426 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/dvfm.h 2009-12-12 16:09:26.446281263 +0200
+@@ -0,0 +1,226 @@
++/*
++ * Copyright (C) 2003-2004 Intel Corporation.
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++ *
++
++ *(C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#ifndef DVFM_H
++#define DVFM_H
++
++
++#ifdef __KERNEL__
++enum {
++ FV_NOTIFIER_QUERY_SET = 1,
++ FV_NOTIFIER_PRE_SET = 2,
++ FV_NOTIFIER_POST_SET = 3,
++};
++
++
++#define MAXTOKENS 80
++#define CONSTRAINT_NAME_LEN 20
++
++#define DVFM_MAX_NAME 32
++#define DVFM_MAX_DEVICE 32
++
++#define DVFM_FREQUENCY_NOTIFIER 0
++#define DVFM_LOWPOWER_NOTIFIER 1
++
++#define DVFM_FREQ_PRECHANGE 0
++#define DVFM_FREQ_POSTCHANGE 1
++
++#define DVFM_LOWPOWER_PRECHANGE 0
++#define DVFM_LOWPOWER_POSTCHANGE 1
++#define APPS_COMM_D2_THRESHOLD 326
++
++/* set the lowest operating point that is equal or higher than specified */
++#define RELATION_LOW 0
++/* set the highest operating point that is equal or lower than specified */
++#define RELATION_HIGH 1
++/* set the specified operating point */
++#define RELATION_STICK 2
++
++/* Both of these states are used in statistical calculation */
++#define CPU_STATE_RUN 1
++#define CPU_STATE_IDLE 2
++
++/*
++ * operating point definition
++ */
++
++struct op_info {
++ void *op;
++ struct list_head list;
++ unsigned int index;
++ unsigned int device; /* store the device ID blocking OP */
++};
++
++struct dvfm_freqs {
++ unsigned int old; /* operating point index */
++ unsigned int new; /* operating point index */
++ struct op_info old_info;
++ struct op_info new_info;
++ unsigned int flags;
++};
++
++struct op_freq {
++ unsigned int cpu_freq;
++};
++
++struct dvfm_op {
++ int index;
++ int count;
++ unsigned int cpu_freq;
++ const char* name;
++};
++
++struct info_head {
++ struct list_head list;
++ rwlock_t lock;
++ unsigned int device; /* store the registerred device ID */
++};
++
++struct head_notifier {
++ spinlock_t lock;
++ struct notifier_block *head;
++};
++
++/**
++ * struct dvfm_lock - the lock struct of dvfm
++ * @lock: the spin lock struct.
++ * @flags: the flags for spin lock.
++ * @count: the count of dvfm_disable_op_name() or dvfm_enable_op_name()
++ *
++ * This struct is used for the mutex lock of dvfm_disable_op_name() and
++ * dvfm_enable_op_name(). The caller can not call dvfm_enable_op_name()
++ * without call dvfm_disable_op_name() before, so the caller of
++ * dvfm_disable_op_name() and dvfm_enable_op_name() must record the
++ * called times of these two functions.
++ */
++struct dvfm_lock {
++ spinlock_t lock;
++ unsigned long flags;
++ int dev_idx;
++ int count;
++};
++
++/*
++ * Store the dev_id and dev_name.
++ * Registered device number can't be larger than 32.
++ */
++struct dvfm_trace_info {
++ struct list_head list;
++ int index; /* index is [0,31] */
++ unsigned int dev_id; /* dev_id == 1 << index */
++ char name[DVFM_MAX_NAME];
++};
++
++
++struct dvfm_driver {
++ int (*get_opinfo)(void *driver_data, void *info);
++ int (*count)(void *driver_data, struct info_head *op_table);
++ int (*set)(void *driver_data, struct dvfm_freqs *freq, unsigned int new,
++ unsigned int relation);
++ int (*dump)(void *driver_data, struct op_info *md, char *buf);
++ char * (*name)(void *driver_data, struct op_info *md);
++ int (*request_set)(void *driver_data, int index);
++ int (*enable_dvfm)(void *driver_data, int dev_id);
++ int (*disable_dvfm)(void *driver_data, int dev_id);
++ int (*enable_op)(void *driver_data, int index, int relation);
++ int (*disable_op)(void *driver_data, int index, int relation);
++ int (*volt_show)(void *driver_data, char *buf);
++#ifdef CONFIG_CPU_PXA310
++ int (*freq_show)(void *driver_date, struct op_info *md, char *buf);
++#endif
++ unsigned int (*ticks_to_usec)(unsigned int);
++ unsigned int (*ticks_to_sec)(unsigned int);
++ unsigned int (*read_time)(void);
++ int (*get_freq)(void* driver_data, struct op_info *md, struct op_freq *freq);
++ int (*check_active_op)(void *driver_data, struct op_info *md);
++ void *priv;
++};
++
++extern struct dvfm_driver *dvfm_driver;
++extern struct info_head *dvfm_op_list;
++extern unsigned int op_nums;
++
++extern int dvfm_notifier_frequency(struct dvfm_freqs *freqs, unsigned int state);
++extern int dvfm_notifier_lowpower(struct dvfm_freqs *freqs, unsigned int state);
++extern int dvfm_register_notifier(struct notifier_block *nb, unsigned int list);
++extern int dvfm_unregister_notifier(struct notifier_block *nb, unsigned int list);
++extern int dvfm_register_driver(struct dvfm_driver *driver_data, struct info_head *op_list);
++extern int dvfm_unregister_driver(struct dvfm_driver *driver);
++extern int dvfm_register(char *name, int *);
++extern int dvfm_unregister(char *name, int *);
++extern int dvfm_query_device_num(void);
++extern int dvfm_query_device_list(void *, int);
++
++extern int dvfm_enable_op(int, int);
++extern int dvfm_disable_op(int, int);
++extern int dvfm_enable(int);
++extern int dvfm_enable_op_name(char *, int);
++extern int dvfm_disable_op_name(char *, int);
++extern int dvfm_disable(int);
++extern int dvfm_dump_op(int, char*);
++
++extern int dvfm_set_op(struct dvfm_freqs *, unsigned int, unsigned int);
++extern int dvfm_get_op(struct op_info **);
++extern int dvfm_get_op_freq(int, struct op_freq *);
++extern int dvfm_check_active_op(int);
++extern int dvfm_get_defop(void);
++extern int dvfm_get_opinfo(int, struct op_info **);
++extern int dvfm_request_op(int);
++extern int dvfm_op_count(void);
++extern int dvfm_find_op(int, struct op_info **);
++extern int dvfm_trace(char *);
++extern int dvfm_add_event(int, int, int, int);
++extern int dvfm_add_timeslot(int, int);
++extern int calc_switchtime_start(int, int, unsigned int);
++extern int calc_switchtime_end(int, int, unsigned int);
++
++//hanling comm apps sync
++extern int dvfm_notify_next_comm_wakeup_time(unsigned int NextWakeupTimeRel);
++extern int dvfm_is_comm_wakep_near(void);
++
++extern const char* dvfm_get_op_name(int);
++
++/**
++ * dvfm_disable_op_name: - disable the operating point by its name.
++ * @name: the operating point's name.
++ *
++ * Context: process and interrupt.
++ *
++ * disable the operating points by op's name, op name set includes
++ * "D0CS","156M","208M","416M","624M" and "D2".
++ *
++ * Returns zero on success, else negative errno.
++ */
++#define dvfm_disable_op_name(name, dev_idx) \
++ (_dvfm_disable_op_name(name, dev_idx, __FILE__))
++
++extern int _dvfm_disable_op_name(char *name, int dev_idx, char *sid);
++
++/**
++ * dvfm_enable_op_name: - enable the operating point by its name.
++ * @name: the operating point's name.
++ *
++ * Context: process and interrupt.
++ *
++ * enable the operating points by op's name, op name set includes
++ * "D0CS","156M","208M","416M","624M" and "D2".
++ *
++ * Returns zero on success, else negative errno.
++ */
++#define dvfm_enable_op_name(name, dev_idx) \
++ (_dvfm_enable_op_name(name, dev_idx, __FILE__))
++
++extern int _dvfm_enable_op_name(char *name, int dev_idx, char *sid);
++
++#endif
++
++#endif
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/mspm_prof.h kernel/arch/arm/mach-pxa/include/mach/mspm_prof.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/mspm_prof.h 2009-12-13 12:59:18.941953014 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/mspm_prof.h 2009-12-12 16:09:26.456281390 +0200
+@@ -0,0 +1,66 @@
++/*
++ * PXA Performance profiler and Idle profiler Routines
++ *
++ * Copyright (c) 2003 Intel Corporation.
++ *
++ * 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.
++ *
++ * (C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#ifndef MSPM_PROF_H
++#define MSPM_PROF_H
++
++#include <mach/pmu.h>
++#include <mach/xscale-pmu.h>
++
++#define IPM_IDLE_PROFILER 1
++#define IPM_PMU_PROFILER 2
++
++struct ipm_profiler_result {
++ struct pmu_results pmu;
++ unsigned int busy_ratio; /* CPU busy ratio */
++ unsigned int mips;
++ unsigned int window_size;
++};
++
++struct ipm_profiler_arg {
++ unsigned int size; /* size of ipm_profiler_arg */
++ unsigned int flags;
++ unsigned int window_size; /* in microseconds */
++ unsigned int pmn0;
++ unsigned int pmn1;
++ unsigned int pmn2;
++ unsigned int pmn3;
++};
++
++#ifdef __KERNEL__
++extern volatile int hlt_counter;
++
++#define OSCR_MASK ~(1UL)
++
++#undef MAX_OP_NUM
++#define MAX_OP_NUM 20
++
++/* The minimum sample window is 20ms, the default window is 100ms */
++#define MIN_SAMPLE_WINDOW 20
++#define DEF_SAMPLE_WINDOW 100
++
++#define DEF_HIGH_THRESHOLD 80
++#define DEF_LOW_THRESHOLD 20
++
++extern int mspm_add_event(int op, int cpu_idle);
++extern int mspm_prof_init(void);
++extern void mspm_prof_exit(void);
++
++extern int bpm_event_notify(int type, int kind, void *info,
++ unsigned int info_len);
++//extern int (*pipm_start_pmu)(struct ipm_profiler_arg *arg);
++//extern int (*pipm_stop_pmu)(void);
++#endif
++
++#endif
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/pmu.h kernel/arch/arm/mach-pxa/include/mach/pmu.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/pmu.h 2009-12-13 12:59:24.521951391 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/pmu.h 2009-12-12 16:09:26.459612243 +0200
+@@ -0,0 +1,555 @@
++/*
++ * "This software program is available to you under a choice of one of two
++ * licenses. You may choose to be licensed under either the GNU General Public
++ * License (GPL) Version 2, June 1991, available at
++ * http://www.fsf.org/copyleft/gpl.html, or the BSD License, the text of
++ * which follows:
++ *
++ * Copyright (c) 1996-2005, Intel Corporation. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this
++ * list of conditions and the following disclaimer.
++ *
++ * Redistributions in binary form must reproduce the above copyright notice, this
++ * list of conditions and the following disclaimer in the documentation and/or
++ * other materials provided with the distribution.
++ *
++ * Neither the name of the Intel Corporation ("Intel") nor the names of its
++ * contributors may be used to endorse or promote products derived from this
++ * software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
++ */
++
++/*
++ * FILENAME: pmu.h
++ *
++ * CORE STEPPING:
++ *
++ * PURPOSE: contains all PMU specific macros, typedefs, and prototypes.
++ * Declares no storage.
++ */
++
++#ifndef __PMU_H__
++#define __PMU_H__
++
++/* PMU Performance Monitor Control Register (PMNC) */
++#define PMU_ID (0x24u << 24)
++#define PMU_COUNTERS_DISALBLE (1u<<4)
++#define PMU_CLOCK_DIVIDER (1u<<3)
++#define PMU_CLOCK_RESET (1u<<2)
++#define PMU_COUNTERS_RESET (1u<<1)
++#define PMU_3_COUNTERS_ENABLE (1u<<0)
++#define PMU_COUNTERS_ENABLE (1u<<0)
++
++/* INTEN & FLAG Registers bit definition*/
++#define PMU_CLOCK_COUNT (1u<<0)
++#define PMU_COUNT_0 (1u<<1)
++#define PMU_COUNT_1 (1u<<2)
++#define PMU_COUNT_2 (1u<<3)
++#define PMU_COUNT_3 (1u<<4)
++
++/*Events combination*/
++/*!evtCount0/2:0x7(instruction count), evtCount1/3:0x0(ICache miss)*/
++#define PMU_EVTCOUNT_1 (0x0007)
++/*!evtCount0/2:0xA(DCache Access), evtCount1/3:0xB(DCache miss)*/
++#define PMU_EVTCOUNT_2 (0x0B0A)
++/*!evtCount0/2:0x1(ICache cannot deliver), evtCount1/3:0x0(ICache miss)*/
++#define PMU_EVTCOUNT_3 (0x0001)
++/*!evtCount0/2:0xB(DBufer stall duration), evtCount1/3:0x9(Dbuffer stall)*/
++#define PMU_EVTCOUNT_4 (0x090B)
++/*!evtCount0/2:0x2(data stall), evtCount1/3:0xC(DCache writeback)*/
++#define PMU_EVTCOUNT_5 (0x0C02)
++/*!evtCount0/2:0x7(instruction count), evtCount1/3:0x3(ITLB miss)*/
++#define PMU_EVTCOUNT_6 (0x0307)
++/*!evtCount0/2:0xA(DCache Access), evtCount/31:0x4(DTLB miss)*/
++#define PMU_EVTCOUNT_7 (0x040A)
++
++/* PXA3xx/PXA900 PML event selector register offset */
++#define PML_ESEL_0_OFF (0x0)
++#define PML_ESEL_1_OFF (0x4)
++#define PML_ESEL_2_OFF (0x8)
++#define PML_ESEL_3_OFF (0xC)
++#define PML_ESEL_4_OFF (0x10)
++#define PML_ESEL_5_OFF (0x14)
++#define PML_ESEL_6_OFF (0x18)
++#define PML_ESEL_7_OFF (0x1C)
++
++enum {
++ PMU_PMNC = 0,
++ PMU_CCNT,
++ PMU_PMN0,
++ PMU_PMN1,
++ PMU_PMN2,
++ PMU_PMN3,
++ PMU_INTEN,
++ PMU_FLAG,
++ PMU_EVTSEL
++};
++
++/*
++ * PMU and PML Event
++ */
++enum {
++ PMU_EVENT_INVALIDATE=0xFFFFFFFFu,
++
++ /*!< L1 Instruction cache miss requires fetch from external memory */
++ PMU_EVENT_L1_INSTRUCTION_MISS=0x0u,
++
++ /*!< L1 Instruction cache cannot deliver an instruction. this indicate
++ * an instruction cache or TLB miss. This event will occur eveyr cycle
++ * in which the condition is present
++ */
++ PMU_EVENT_L1_INSTRUCTION_NOT_DELIVER,
++
++ /*!< Stall due to a data dependency. This event will occur every cycle
++ * in which the condition is present
++ */
++ PMU_EVENT_STALL_DATA_DEPENDENCY,
++
++ /*!< Instruction TLB miss*/
++ PMU_EVENT_INSTRUCTION_TLB_MISS,
++
++ /*!< Data TLB miss*/
++ PMU_EVENT_DATA_TLB_MISS,
++
++ /*!< Branch instruction retired, branch may or many not have changed
++ * program flow. (Counts only B and BL instruction, in both ARM and
++ * Thumb mode)
++ */
++ PMU_EVENT_BRANCH_RETIRED,
++
++ /*!< Branch mispredicted. Counts only B and BL instructions, in both
++ * ARM and Thumb mode
++ */
++ PMU_EVENT_BRANCH_MISPREDICTED,
++
++ /*!< Instruction retired. This event will occur every cycle in which
++ * the condition is present
++ */
++ PMU_EVENT_INSTRUCTION_RETIRED,
++
++ /*!< L1 Data cache buffer full stall. This event will occur every
++ * cycle in which the condition is present.
++ */
++ PMU_EVENT_L1_DATA_STALL,
++
++ /*!< L1 Data cache buffer full stall. This event occur for each
++ * contiguous sequence of this type of stall
++ */
++ PMU_EVENT_L1_DATA_STALL_C,
++
++ /*!< L1 Data cache access, not including Cache Operations. All data
++ * accesses are treated as cacheable accessses and are counted here
++ * even if the cache is not enabled
++ */
++ PMU_EVENT_L1_DATA_ACCESS,
++
++ /*!< L1 Data cache miss, not including Cache Operations. All data
++ * accesses are treated as cachedable accesses and are counted as
++ * misses if the data cache is not enable
++ */
++ PMU_EVENT_L1_DATA_MISS,
++
++ /*!< L1 data cache write-back. This event occures once for each line
++ * that is written back from the cache
++ */
++ PMU_EVENT_L1_DATA_WRITE_BACK,
++
++ /*!< Software changed the PC(b bx bl blx and eor sub rsb add adc sbc
++ * rsc orr mov bic mvn ldm pop) will be counted. The count does not
++ * increment when an exception occurs and the PC changed to the
++ * exception address(e.g.. IRQ, FIR, SWI,...)
++ */
++ PMU_EVENT_SOFTWARE_CHANGED_PC,
++
++ /*!< Branch instruction retired, branch may or may noot have chanaged
++ * program flow.
++ * (Count ALL branch instructions, indirect as well as direct)
++ */
++ PMU_EVENT_BRANCH_RETIRED_ALL,
++
++ /*!< Instruction issue cycle of retired instruction. This event is a
++ * count of the number of core cycle each instruction requires to issue
++ */
++ PMU_EVENT_INSTRUCTION_CYCLE_RETIRED,
++
++ /*!< All change to the PC. (includes software changes and exceptions*/
++ PMU_EVENT_ALL_CHANGED_PC=0x18,
++
++ /*!< Pipe line flush due to branch mispredict or exception*/
++ PMU_EVENT_PIPE_FLUSH_BRANCH,
++
++ /*!< The core could not issue an instruction due to a backed stall.
++ * This event will occur every cycle in which the condition is present
++ */
++ PMU_EVENT_BACKEND_STALL,
++
++ /*!< Multiplier in use. This event will occur every cycle in which
++ * the multiplier is active
++ */
++ PMU_EVENT_MULTIPLIER,
++
++ /*!< Multiplier stalled the instruction pipelien due to resource stall.
++ * This event will occur every cycle in which the condition is present
++ */
++ PMU_EVENT_MULTIPLIER_STALL_PIPE,
++
++ /*!< Coprocessor stalled the instruction pipeline. This event will
++ * occur every cycle in which the condition is present
++ */
++ PMU_EVENT_COPROCESSOR_STALL_PIPE,
++
++ /*!< Data cache stalled the instruction pipeline. This event will
++ * occur every cycle in which the condition is present
++ */
++ PMU_EVENT_DATA_CACHE_STALL_PIPE,
++
++ /*!< Unified L2 Cache request, not including cache operations. This
++ * event includes table walks, data and instruction reqeusts
++ */
++ PMU_EVENT_L2_REQUEST=0x20,
++
++ /*!< Unified L2 cache miss, not including cache operations*/
++ PMU_EVENT_L2_MISS=0x23,
++
++ /*!< Address bus transcation*/
++ PMU_EVENT_ADDRESS_BUS=0x40,
++
++ /*!< Self initiated(Core Generated) address bus transaction*/
++ PMU_EVENT_SELF_INITIATED_ADDRESS,
++
++ /*!< Bus clock. This event occurs onece for each bus cycle*/
++ PMU_EVENT_BUS_CLOCK=0x43,
++
++ /*!< Data bus transaction. This event occurs once for
++ * each data bus cycle
++ */
++ PMU_EVENT_SELF_INITIATED_DATA=0x47,
++
++ /*!< Data bus transaction. This event occures once for
++ * each data bus cycle
++ */
++ PMU_EVENT_BUS_TRANSACTION,
++
++ PMU_EVENT_ASSP_0=0x80,
++ PMU_EVENT_ASSP_1,
++ PMU_EVENT_ASSP_2,
++ PMU_EVENT_ASSP_3,
++ PMU_EVENT_ASSP_4,
++ PMU_EVENT_ASSP_5,
++ PMU_EVENT_ASSP_6,
++ PMU_EVENT_ASSP_7,
++
++ /*!< Power Saving event. This event deactivates the corresponding
++ * PMU event counter
++ */
++ PMU_EVENT_POWER_SAVING=0xFF,
++
++ PXA3xx_EVENT_MASK=0x80000000,
++
++ /*!< Core is performing a new instruction fetch.
++ * e.g. an L2 cache miss.
++ */
++ PXA3xx_EVENT_CORE_INSTRUCTION_FETCH=PXA3xx_EVENT_MASK,
++
++ /*!< Core is performing a new data fetch*/
++ PXA3xx_EVENT_CORE_DATA_FETCH,
++
++ /*!< Core read request count*/
++ PXA3xx_EVENT_CORE_READ,
++
++ /*!< LCD read request cout*/
++ PXA3xx_EVENT_LCD_READ,
++
++ /*!< DMA read request count*/
++ PXA3xx_EVENT_DMA_READ,
++
++ /*!< Camera interface read request cout*/
++ PXA3xx_EVENT_CAMERA_READ,
++
++ /*!< USB 2.0 read request count*/
++ PXA3xx_EVENT_USB20_READ,
++
++ /*!< 2D grahpic read request count*/
++ PXA3xx_EVENT_2D_READ,
++
++ /*!< USB1.1 host read reqeust count*/
++ PXA3xx_EVENT_USB11_READ,
++
++ /*!< PX1 bus unitization. the number of cycles durring which
++ * the PX1 bus is occupied
++ */
++ PXA3xx_EVENT_PX1_UNITIZATION,
++
++ /*!< PX2(sidecar) bus unitization. the number of cycles
++ * durring which the PX2 bus is occupied
++ */
++ PXA3xx_EVENT_PX2_UNITIZATION,
++
++ /*!< Dynamic memory queue for Mandris occupied. the number of
++ * cycles when the DMC queue is not empty
++ */
++ PXA3xx_EVENT_DMC_NOT_EMPTY=PXA3xx_EVENT_MASK|14,
++
++ /*!< Dynamic memory queue for Mandris occupied by more than 1 request.
++ * the number of cycles when the DMC queue has 2 or more requests
++ */
++ PXA3xx_EVENT_DMC_2,
++
++ /*!< Dynamic memory queue for Mandris occupied by more than 2 request.
++ * the number of cycles when the DMC queue has 3 or more requests
++ */
++ PXA3xx_EVENT_DMC_3,
++
++ /*!< Dynamic memory queue for Mandris occupied by more than 3 request.
++ * the number of cycles when the DMC queue is full
++ */
++ PXA3xx_EVENT_DMC_FULL,
++
++ /*!< Static memory queue for Mandris occupied. the number of cycles
++ * when the SMC queue is not empty
++ */
++ PXA3xx_EVENT_SMC_NOT_EMPTY,
++
++ /*!< Static memory queue for Mandris occupied by more than 1 request.
++ * the number of cycles when the SMC queue has 2 or more requests
++ */
++ PXA3xx_EVENT_SMC_2,
++
++ /*!< Static memory queue for Mandris occupied by more than 2 request.
++ * the number of cycles when the SMC queue has 3 or more requests
++ */
++ PXA3xx_EVENT_SMC_3,
++
++ /*!< Static memory queue for Mandris occupied by more than 3 request.
++ * the number of cycles when the SMC queue is full
++ */
++ PXA3xx_EVENT_SMC_FULL,
++
++ /*!< Internal SRAM queue for Mandris occupied. the number of cycles
++ * when the ISRAM queue is not empty
++ */
++ PXA3xx_EVENT_ISRAM_NOT_EMPTY=PXA3xx_EVENT_MASK|26,
++
++ /*!< Internal SRAM queue for Mandris occupied by more than 1 request.
++ * the number of cycles when the ISRAM queue has 2 or more requests
++ */
++ PXA3xx_EVENT_ISRAM_2,
++
++ /*!< Internal SRAM queue for Mandris occupied by more than 2 request.
++ * the number of cycles when the ISRAM queue has 3 or more requests
++ */
++ PXA3xx_EVENT_ISRAM_3,
++
++ /*!< Internal SRAM queue for Mandris occupied by more than 3 request.
++ * the number of cycles when the ISRAM queue is full
++ */
++ PXA3xx_EVENT_ISRAM_FULL,
++
++ /*!< the number of cycles when external memory controller bus
++ * is occupied
++ */
++ PXA3xx_EVENT_EXMEM,
++
++ /*!< the number of cycles when external data flash bus is occupies */
++ PXA3xx_EVENT_DFC,
++
++ /*!< Core write request count*/
++ PXA3xx_EVENT_CORE_WRITE=PXA3xx_EVENT_MASK|36,
++
++ /*!< DMA write request count*/
++ PXA3xx_EVENT_DMA_WRITE,
++
++ /*!< Camera interface write request cout*/
++ PXA3xx_EVENT_CAMERA_WRITE,
++
++ /*!< USB 2.0 write request count*/
++ PXA3xx_EVENT_USB20_WRITE,
++
++ /*!< 2D grahpic write request count*/
++ PXA3xx_EVENT_2D_WRITE,
++
++ /*!< USB1.1 host write reqeust count*/
++ PXA3xx_EVENT_USB11_WRITE,
++
++ /*!< PX1 bus reqeust. length of time that at least one bus request
++ * is asserted on PX bus 1
++ */
++ PXA3xx_EVENT_PX1_REQUEST,
++
++ /*!< PX2 bus reqeust. length of time that at least one bus request
++ * is asserted on PX bus 2
++ */
++ PXA3xx_EVENT_PX2_REQUEST,
++
++ /*!< PX1 bus retries. number of retries on PX bus 1*/
++ PXA3xx_EVENT_PX1_RETRIES,
++
++ /*!< PX2 bus retries. number of retries on PX bus 2*/
++ PXA3xx_EVENT_PX2_RETRIES,
++
++ /*!< Temperature leve 1. time the part has spent in temperature range 1*/
++ PXA3xx_EVENT_TEMPERATURE_1,
++
++ /*!< Temperature leve 1. time the part has spent in temperature range 2*/
++ PXA3xx_EVENT_TEMPERATURE_2,
++
++ /*!< Temperature leve 1. time the part has spent in temperature range 3*/
++ PXA3xx_EVENT_TEMPERATURE_3,
++
++ /*!< Temperature leve 1. time the part has spent in temperature range 4*/
++ PXA3xx_EVENT_TEMPERATURE_4,
++
++ /*!< Core read/write latency measurement. amount of time when core
++ * have more than 1 read/write request outstanding
++ */
++ PXA3xx_EVENT_CORE_LATENCY_1,
++
++ /*!< Core read/write latency measurement. amount of time when core
++ * have more than 2 read/write request outstanding
++ */
++ PXA3xx_EVENT_CORE_LATENCY_2,
++
++ /*!< Core read/write latency measurement. amount of time when core
++ * have more than 3 read/write request outstanding
++ */
++ PXA3xx_EVENT_CORE_LATENCY_3,
++
++ /*!< Core read/write latency measurement. amount of time when core
++ * have more than 4 read/write request outstanding
++ */
++ PXA3xx_EVENT_CORE_LATENCY_4,
++
++ /*!< PX1 to IM read/write latency measurement. Amount of time when
++ * PX1 to IM has more than 1 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_IM_1,
++
++ /*!< PX1 to IM read/write latency measurement. Amount of time when
++ * PX1 to IM has more than 2 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_IM_2,
++
++ /*!< PX1 to IM read/write latency measurement. Amount of time when
++ * PX1 to IM has more than 3 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_IM_3,
++
++ /*!< PX1 to IM read/write latency measurement. Amount of time when
++ * PX1 to IM has more than 4 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_IM_4,
++
++ /*!< PX1 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX1 to DMEM/SMEM has more than 1 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_MEM_1,
++
++ /*!< PX1 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX1 to DMEM/SMEM has more than 2 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_MEM_2,
++
++ /*!< PX1 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX1 to DMEM/SMEM has more than 3 read/write requests outstanding.
++ */
++
++ PXA3xx_EVENT_PX1_MEM_3,
++ /*!< PX1 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX1 to DMEM/SMEM has more than 4 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX1_MEM_4,
++
++ /*!< PX2 to IM read/write latency measurement. Amount of time when
++ * PX2 to IM has more than 1 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_IM_1,
++
++ /*!< PX2 to IM read/write latency measurement. Amount of time when
++ * PX2 to IM has more than 2 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_IM_2,
++
++ /*!< PX2 to IM read/write latency measurement. Amount of time when
++ * PX2 to IM has more than 3 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_IM_3,
++
++ /*!< PX2 to IM read/write latency measurement. Amount of time when
++ * PX2 to IM has more than 4 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_IM_4,
++
++ /*!< PX2 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX2 to DMEM/SMEM has more than 1 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_MEM_1,
++
++ /*!< PX2 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX2 to DMEM/SMEM has more than 2 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_MEM_2,
++
++ /*!< PX2 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX2 to DMEM/SMEM has more than 3 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_MEM_3,
++
++ /*!< PX2 to DMEM/SMEM read/write latency measurement. Amount of time
++ * when PX2 to DMEM/SMEM has more than 4 read/write requests outstanding.
++ */
++ PXA3xx_EVENT_PX2_MEM_4
++};
++
++#ifdef __KERNEL__
++struct pxa3xx_pmu_info {
++ /* performance monitor unit register base */
++ unsigned char __iomem *pmu_base;
++};
++
++#ifdef __cplusplus
++extern "C"
++{
++#endif
++
++/*
++ * This routine reads the designated PMU register via CoProcessor 14
++ *
++ * @param aReg PMU register number to read define in int
++ * @return 32-bit value read from register
++ */
++extern unsigned int pmu_read_reg(unsigned int aReg);
++
++/*
++ * This routine Writes the designated PMU register via CoProcessor 14
++ *
++ * @param aReg PMU register number to read define in int
++ * aValue Value to write to PMU register
++ * @return
++ */
++extern void pmu_write_reg(unsigned int aReg, unsigned int aValue);
++
++extern int pmu_select_event(int counter, int type);
++
++extern void pxa3xx_set_pmu_info(void *info);
++
++#ifdef __cplusplus
++}
++#endif
++#endif
++
++#endif //__PMU_H__
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/prm.h kernel/arch/arm/mach-pxa/include/mach/prm.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/prm.h 2009-12-13 12:59:30.199033933 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/prm.h 2009-12-12 16:09:26.459612243 +0200
+@@ -0,0 +1,138 @@
++/*
++ * include/asm-arm/arch-pxa/prm.h
++ *
++ * Copyright (C) 2006, Intel Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef __PRM_H
++#define __PRM_H
++
++#include <linux/interrupt.h>
++#include <mach/irqs.h>
++#include <mach/pmu.h>
++#include <mach/pxa3xx_dvfm.h>
++
++#define MAX_GROUPS 2
++#define MAX_CLIENTS 16
++
++typedef enum {
++ /* tag the loweset priority*/
++ PRI_LOWEST = 0,
++ /*define the possible priorities here*/
++ PRI_IPMC = PRI_LOWEST,
++ PRI_PROFILER,
++ PRI_VTUNE,
++ /*tag the highest priority*/
++ MAX_PRIORITIES,
++ PRI_HIGHEST = MAX_PRIORITIES - 1,
++} prm_priority;
++
++struct prm_group;
++struct prm_resource;
++struct prm_resource_state;
++
++typedef enum {
++ PRM_RES_APPROPRIATED,
++ PRM_RES_READY,
++} prm_event;
++
++typedef enum {
++ PRM_CCNT = 0,
++ PRM_PMN0,
++ PRM_PMN1,
++ PRM_PMN2,
++ PRM_PMN3,
++ PRM_VCC0,
++ PRM_VCC1,
++ PRM_IDLE_PROFILER,
++ PRM_COP,
++ RESOURCE_NUM,
++} prm_resource_id;
++
++typedef void (*clientcallback)(prm_event, unsigned int, void *);
++
++/* The gourp includes a set of resources. If one of the set of resources is
++ * appropriated, the other resources will not available for access. But the
++ * resources are still allocated by the client. So the group is defined as
++ * a set of resources that all can be accessed or all can not be accessed.
++ */
++struct prm_group {
++ unsigned int id;
++ /* appropriated resources count */
++ unsigned int appropriated_cnt;
++ /* total resources count in the group */
++ unsigned int member_cnt;
++ /* list for all the resources in the group */
++ struct list_head resources;
++ struct proc_dir_entry *dir;
++};
++
++struct prm_client {
++ /* client id */
++ unsigned int id;
++ /* process id for the client */
++ unsigned int pid;
++ /* priority for the client.(LOW or HIGH) */
++ prm_priority priority;
++ /* name of the client */
++ char *name;
++ /* How many groups in the client */
++ unsigned int group_cnt;
++ /* support MAXGROUP groups, some may be NULL */
++ struct prm_group *groups[MAX_GROUPS];
++ void *client_data;
++ /* notifier for resource appropriate and ready */
++ clientcallback notify;
++ irq_handler_t handler;
++ void *dev_id;
++ struct proc_dir_entry *dir;
++};
++
++struct prm_resource_state {
++ /* which client allocate the resources. In every priority,
++ * there can be only one client allocate the resource
++ */
++ struct prm_client *allocate;
++ /* which group it belongs to */
++ struct prm_group *group;
++ int active;
++ struct prm_resource *resource;
++ /* used by prm_group->resources for link the resources into the group */
++ struct list_head entry;
++ struct proc_dir_entry *dir;
++};
++
++struct prm_resource {
++ struct prm_client *access; /* Only one client can access it */
++ prm_resource_id id;
++ struct prm_resource_state priority[MAX_PRIORITIES];
++ struct proc_dir_entry *dir;
++};
++
++int prm_open_session(prm_priority , char * , clientcallback , void * );
++int prm_close_session(unsigned int );
++int prm_allocate_resource(unsigned int , prm_resource_id , unsigned int );
++int prm_free_resources(unsigned int , unsigned int );
++int prm_commit_resources(unsigned int , unsigned int );
++int pmu_read_register(unsigned int , int , unsigned int * );
++int pmu_write_register(unsigned int , int , unsigned int );
++int pmu_set_event(unsigned int , unsigned int , int * , int );
++int pmu_enable_event_counting(unsigned int );
++int pmu_disable_event_counting(unsigned int );
++int pmu_enable_event_interrupt(unsigned int , int );
++int pmu_disable_event_interrupt(unsigned int , int );
++int pmu_register_isr(unsigned int , irq_handler_t, void * );
++int pmu_unregister_isr(unsigned int );
++int cop_get_num_of_cops(void);
++int cop_get_cop(unsigned int , unsigned int , struct pxa3xx_fv_info *);
++int cop_set_cop(unsigned int , unsigned int , int mode);
++int cop_get_def_cop(unsigned int , unsigned int *, struct pxa3xx_fv_info *);
++int cop_set_def_cop(unsigned int );
++int cop_get_cur_cop(unsigned int , unsigned int *, struct pxa3xx_fv_info *);
++
++#endif
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_dvfm.h kernel/arch/arm/mach-pxa/include/mach/pxa3xx_dvfm.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_dvfm.h 2009-12-13 12:59:37.209033179 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/pxa3xx_dvfm.h 2009-12-12 16:09:26.462949527 +0200
+@@ -0,0 +1,94 @@
++/*
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++
++ * (C) Copyright 2007 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#ifndef PXA3XX_DVFM_H
++#define PXA3XX_DVFM_H
++
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_pm.h>
++
++#define DMEMC_FREQ_HIGH 0
++#define DMEMC_FREQ_LOW 1
++#define DMEMC_D0CS_ENTER 2
++#define DMEMC_D0CS_EXIT 3
++
++#define OP_NAME_LEN 16
++
++enum {
++ POWER_MODE_D0 = 0,
++ POWER_MODE_D0CS,
++ POWER_MODE_D1,
++ POWER_MODE_D2,
++ POWER_MODE_CG,
++};
++
++enum {
++ OP_FLAG_FACTORY = 0,
++ OP_FLAG_USER_DEFINED,
++ OP_FLAG_BOOT,
++ OP_FLAG_ALL,
++};
++
++enum {
++ IDLE_D0 = 0,
++ IDLE_D0CS = 1,
++ IDLE_D1 = 2,
++ IDLE_D2 = 4,
++ IDLE_CG = 8,
++};
++
++struct dvfm_md_opt {
++ int vcc_core;
++ int vcc_sram;
++ int xl;
++ int xn;
++ int core;
++ int smcfs;
++ int sflfs;
++ int hss;
++ int dmcfs;
++ int df_clk;
++ int empi_clk;
++ int power_mode;
++ int flag;
++ int lpj;
++ char name[OP_NAME_LEN];
++};
++
++/* This structure is similar to dvfm_md_opt.
++ * Reserve this structure in order to keep compatible
++ */
++struct pxa3xx_fv_info {
++ unsigned long xl;
++ unsigned long xn;
++ unsigned int vcc_core;
++ unsigned int vcc_sram;
++ unsigned long smcfs;
++ unsigned long sflfs;
++ unsigned long hss;
++ unsigned long dmcfs;
++ unsigned long df_clk;
++ unsigned long empi_clk;
++ unsigned long d0cs;
++ /* WARNING: above fields must be consistent with PM_FV_INFO!!!*/
++
++ unsigned long lpj; /* New value for loops_per_jiffy */
++};
++
++struct pxa3xx_freq_mach_info {
++ int flags;
++};
++
++#define PXA3xx_USE_POWER_I2C (1UL << 0)
++extern void set_pxa3xx_freq_info(struct pxa3xx_freq_mach_info *info);
++extern void set_pxa3xx_freq_parent(struct device *parent_dev);
++
++extern int md2fvinfo(struct pxa3xx_fv_info *fv_info, struct dvfm_md_opt *orig);
++
++#endif
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_pm.h kernel/arch/arm/mach-pxa/include/mach/pxa3xx_pm.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_pm.h 2009-12-13 12:59:45.791952709 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/pxa3xx_pm.h 2009-12-12 16:09:26.462949527 +0200
+@@ -0,0 +1,530 @@
++/*
++ * Monahans Power Management Routines
++ *
++ * Copyright (C) 2004, Intel Corporation(chao.xie@intel.com).
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++ *
++ *(C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#ifndef __PXA3xx_PM_H__
++#define __PXA3xx_PM_H__
++
++#include <asm/types.h>
++
++/* clock manager registers */
++#define ACCR_OFF 0x00
++#define ACSR_OFF 0x04
++#define AICSR_OFF 0x08
++#define D0CKEN_A_OFF 0x0c
++#define D0CKEN_B_OFF 0x10
++#define AC97_DIV_OFF 0x14
++#define OSCC_OFF 0x10000
++
++/* service power management uinit */
++#define PSR_OFF 0x004
++#define PSPR_OFF 0x008
++#define PCFR_OFF 0x00C
++#define PWER_OFF 0x010
++#define PWSR_OFF 0x014
++#define PECR_OFF 0x018
++#define CSER_OFF 0x01C
++#define DCDCSR_OFF 0x080
++#define AVCR_OFF 0x094
++#define SVCR_OFF 0x098
++#define CVCR_OFF 0x09C
++#define PSBR_OFF 0x0A0
++#define PVCR_OFF 0x100
++#if defined(CONFIG_CPU_PXA935)
++#define SDCR_OFF 0x08C
++#endif
++
++/* slave power management unit */
++#define ASCR_OFF 0x00
++#define ARSR_OFF 0x04
++#define AD3ER_OFF 0x08
++#define AD3SR_OFF 0x0c
++#define AD2D0ER_OFF 0x10
++#define AD2D0SR_OFF 0x14
++#define AD2D1ER_OFF 0x18
++#define AD2D1SR_OFF 0x1c
++#define AD1D0ER_OFF 0x20
++#define AD1D0SR_OFF 0x24
++#define ASDCNT_OFF 0x28
++#define AGENP_OFF 0x2c
++#define AD3R_OFF 0x30
++#define AD2R_OFF 0x34
++#define AD1R_OFF 0x38
++
++/* dynamic memory controller registers */
++#define MDCNFG_OFF 0x0000
++#define MDREFR_OFF 0x0004
++#define FLYCNFG_OFF 0x0020
++#define MDMRS_OFF 0x0040
++#define DDR_SCAL_OFF 0x0050
++#define DDR_HCAL_OFF 0x0060
++#define DDR_WCAL_OFF 0x0068
++#define DMCIER_OFF 0x0070
++#define DMCISR_OFF 0x0078
++#define DMCISR2_OFF 0x007C
++#define DDR_DLS_OFF 0x0080
++#define EMPI_OFF 0x0090
++#define RCOMP_OFF 0x0100
++#define PAD_MA_OFF 0x0110
++#define PAD_MDMSB_OFF 0x0114
++#define PAD_MDLSB_OFF 0x0118
++#define PAD_SDRAM_OFF 0x011C
++#define PAD_SDCLK_OFF 0x0120
++#define PAD_SDCS_OFF 0x0124
++#define PAD_SMEM_OFF 0x0128
++#define PAD_SCLK_OFF 0x012C
++
++/* static memory controller registers */
++#define MSC0_OFF 0x0008
++#define MSC1_OFF 0x000C
++#define MECR_OFF 0x0014
++#define SXCNFG_OFF 0x001C
++#define MCMEM0_OFF 0x0028
++#define MCATT0_OFF 0x0030
++#define MCIO0_OFF 0x0038
++#define MEMCLKCFG_OFF 0x0068
++#define CSADRCFG0_OFF 0x0080
++#define CSADRCFG1_OFF 0x0084
++#define CSADRCFG2_OFF 0x0088
++#define CSADRCFG3_OFF 0x008C
++#define CSADRCFG_P_OFF 0x0090
++#define CSMSADRCFG_OFF 0x00A0
++
++/* OS Timer address space */
++#define OST_START 0x40a00000
++#define OST_END 0x40a000df
++
++/* System Bus Arbiter address space */
++#define ARB_START 0x4600fe00
++#define ARB_END 0x4600fe07
++
++/* Registers offset within ARB space */
++#define ARBCTL1_OFF 0x0000
++#define ARBCTL2_OFF 0x0004
++
++/* Dynamic memory controll address space */
++#define DMC_START 0x48100000
++#define DMC_END 0x48100fff
++
++/* static memory controll address space */
++#define SMC_START 0x4a000000
++#define SMC_END 0x4a0000ff
++
++/* Power Management Unit address space */
++#define PM_START 0x40f50000
++#define PM_END 0x40f5018f
++
++/* Bits definition for Clock Control Register */
++#define ACCR_PCCE (1 << 11)
++
++#define ACSR_XPLCK (1 << 29)
++#define ACSR_SPLCK (1 << 28)
++
++#define AICSR_PCIE (1 << 4)
++#define AICSR_TCIE (1 << 2)
++#define AICSR_FCIE (1 << 0)
++
++/* Bits definition for RTC Register */
++#define RTSR_PICE (1 << 15)
++#define RTSR_PIALE (1 << 14)
++
++/* Bits definition for Power Control Register */
++#define ASCR_RDH (1 << 31)
++#define ASCR_D1S (1 << 2)
++#define ASCR_D2S (1 << 1)
++#define ASCR_D3S (1 << 0)
++#define ASCR_MASK (ASCR_D1S | ASCR_D2S | ASCR_D3S)
++#define PSR_MASK 0x07
++#define PCFR_L1DIS (1 << 13)
++#define PCFR_L0EN (1 << 12)
++#define PECR_E1IS (1 << 31)
++#define PECR_E1IE (1 << 30)
++#define PECR_E0IS (1 << 29)
++#define PECR_E0IE (1 << 28)
++#define PECR_DIR1 (1 << 5)
++#define PECR_DIR0 (1 << 4)
++
++/* Bits definition for Oscillator Configuration Register */
++#define OSCC_GPRM (1 << 18) /* GB PLL Request Mask */
++#define OSCC_GPLS (1 << 17) /* GB PLL Lock Status */
++
++/* Bits definition for Application Subsystem General Purpose Register */
++#define AGENP_GBPLL_CTRL (1 << 29)
++#define AGENP_GBPLL_DATA (1 << 28) /* Turn on/off GB PLL */
++#define AGENP_SAVE_WK (1 << 2) /* Save wakeup */
++
++/* Registers offset within ARB space */
++#define BPB_START 0x42300000
++#define BPB_END 0x4230004B
++
++/* GPIO Wakeup Status Register */
++#define GWSR(x) ((x << 2) + 0x38)
++#define GWSR1 0x3C
++#define GWSR2 0x40
++#define GWSR3 0x44
++#define GWSR4 0x48
++
++/* bits definitions */
++#define ASCR_MTS_OFFSET 12
++#define ASCR_MTS_S_OFFSET 8
++
++#define ACSR_SPLCK_OFFSET 28
++#define ACSR_XPLCK_OFFSET 29
++
++#define ACCR_XL_OFFSET 0
++#define ACCR_XN_OFFSET 8
++#define ACCR_DMCFS_OFFSET 12
++#define ACCR_HSS_OFFSET 14
++#define ACCR_XSPCLK_OFFSET 16
++#define ACCR_SFLFS_OFFSET 18
++#define ACCR_SMCFS_OFFSET 23
++#define ACCR_D0CS_OFFSET 26
++#define ACCR_VAUFS_OFFSET 28
++#define ACCR_SPDIS_OFFSET 30
++#define ACCR_XPDIS_OFFSET 31
++
++#define ACSR_VAUFS_OFFSET 21
++
++#define ACSR_VAUFS_MASK (0x03 << ACSR_VAUFS_OFFSET)
++
++#define MEMCLKCFG_EMPI_OFFSET 0
++#define MEMCLKCFG_DF_OFFSET 16
++
++#define HCAL_HCEN_OFFSET 31
++
++#define MDCNFG_HWNOPHD_OFFSET 28
++#define MDCNFG_HWFREQ_OFFSET 29
++#define MDCNFG_DMCEN_OFFSET 30
++#define MDCNFG_DMAP_OFFSET 31
++
++/* mode save flags */
++#define PM_MODE_SAVE_FLAG_SYS 0x1
++#define PM_MODE_SAVE_FLAG_IRQ 0x2
++#define PM_MODE_SAVE_FLAG_FIQ 0x4
++#define PM_MODE_SAVE_FLAG_ABT 0x8
++#define PM_MODE_SAVE_FLAG_UND 0x10
++#define PM_MODE_SAVE_FLAG_SVC 0x20
++
++/* value for PWRMODE register */
++#define PXA3xx_PM_S2D3C4 0x06
++#define PXA3xx_PM_S0D2C2 0x03
++#define PXA3xx_PM_S3D4C4 0x07
++#define PXA3xx_PM_S0D1C2 0x02
++#define PXA3xx_PM_S0D0C1 0x01
++
++/* CPSR Processor constants */
++#define CPSR_Mode_MASK (0x0000001F)
++#define CPSR_Mode_USR (0x10)
++#define CPSR_Mode_FIQ (0x11)
++#define CPSR_Mode_IRQ (0x12)
++#define CPSR_Mode_SVC (0x13)
++#define CPSR_Mode_ABT (0x17)
++#define CPSR_Mode_UND (0x1B)
++#define CPSR_Mode_SYS (0x1F)
++#define CPSR_I_Bit (0x80)
++#define CPSR_F_Bit (0x40)
++
++
++/****************************************************************************/
++#define PXA3xx_PM_WE_EXTERNAL0 (0x1UL << 0)
++#define PXA3xx_PM_WE_EXTERNAL1 (0x1UL << 1)
++#define PXA3xx_PM_WE_GENERIC(x) (0x1UL << (x + 2))
++#define PXA3xx_PM_WE_DKEY (0x1UL << 2)
++#define PXA3xx_PM_WE_DKEY1 (0x1UL << 3)
++#define PXA3xx_PM_WE_BTUART (0x1UL << 4)
++#define PXA3xx_PM_WE_PMIC (0x1UL << 5)
++#define PXA3xx_PM_WE_NDINT (0x1UL << 6)
++#define PXA3xx_PM_WE_MMC1 (0x1UL << 7)
++#define PXA3xx_PM_WE_MMC2 (0x1UL << 8)
++#define PXA3xx_PM_WE_SSP (0x1UL << 9)
++#define PXA3xx_PM_WE_SSP4 (0x1UL << 10)
++#define PXA3xx_PM_WE_UART1 (0x1UL << 11)
++#define PXA3xx_PM_WE_CI2C (0x1UL << 12)
++#define PXA3xx_PM_WE_SSP2 (0x1UL << 13)
++#define PXA3xx_PM_WE_WDT (0x1UL << 14)
++#define PXA3xx_PM_WE_GPIO (0x1UL << 15)
++#define PXA3xx_PM_WE_OTG (0x1UL << 16)
++#define PXA3xx_PM_WE_INTC (0x1UL << 17)
++#define PXA3xx_PM_WE_MLCD (0x1UL << 18)
++#define PXA3xx_PM_WE_USIM0 (0x1UL << 19)
++#define PXA3xx_PM_WE_USIM1 (0x1UL << 20)
++#define PXA3xx_PM_WE_MKEY (0x1UL << 21)
++#define PXA3xx_PM_WE_MUX2 (0x1UL << 22)
++#define PXA3xx_PM_WE_MUX3 (0x1UL << 23)
++#define PXA3xx_PM_WE_MSL0 (0x1UL << 24)
++#define PXA3xx_PM_WE_RESERVE1 (0x1UL << 25)
++#define PXA3xx_PM_WE_USB2 (0x1UL << 26)
++#define PXA3xx_PM_WE_DMC (0x1UL << 27)
++#define PXA3xx_PM_WE_USBH (0x1UL << 28)
++#define PXA3xx_PM_WE_TSI (0x1UL << 29)
++#define PXA3xx_PM_WE_OST (0x1UL << 30)
++#define PXA3xx_PM_WE_RTC (0x1UL << 31)
++
++
++#define PWSR_EDR0 (0x1 << 0)
++#define PWSR_EDR1 (0x1 << 1)
++#define PWSR_EDF0 (0x1 << 2)
++#define PWSR_EDF1 (0x1 << 3)
++#define PWSR_EERTC (0x1 << 31)
++
++#define PWER_WER0 (0x1 << 0)
++#define PWER_WER1 (0x1 << 1)
++#define PWER_WEF0 (0x1 << 2)
++#define PWER_WEF1 (0x1 << 3)
++#define PWER_WERTC (0x1 << 31)
++
++#define WORD_SIZE 4
++
++/* the position of each data memeber */
++#define SleepState_begin 0x0
++#define SleepState_checksum 0x0
++#define SleepState_wordCount (SleepState_checksum + WORD_SIZE)
++#define SleepState_areaAddress (SleepState_wordCount + WORD_SIZE)
++#define SleepState_modeSaveFlags (SleepState_areaAddress + WORD_SIZE)
++
++/* save ARM registers */
++#define SleepState_ENTRY_REGS (SleepState_modeSaveFlags + WORD_SIZE)
++#define SleepState_ENTRY_CPSR (SleepState_ENTRY_REGS)
++#define SleepState_ENTRY_SPSR (SleepState_ENTRY_CPSR + WORD_SIZE)
++#define SleepState_ENTRY_R0 (SleepState_ENTRY_SPSR + WORD_SIZE)
++#define SleepState_ENTRY_R1 (SleepState_ENTRY_R0 + WORD_SIZE)
++#define SleepState_SYS_REGS (SleepState_ENTRY_REGS + 17*WORD_SIZE)
++#define SleepState_FIQ_REGS (SleepState_SYS_REGS + 2*WORD_SIZE)
++#define SleepState_IRQ_REGS (SleepState_FIQ_REGS + 8*WORD_SIZE)
++#define SleepState_ABT_REGS (SleepState_IRQ_REGS + 3*WORD_SIZE)
++#define SleepState_UND_REGS (SleepState_ABT_REGS + 3*WORD_SIZE)
++#define SleepState_SVC_REGS (SleepState_UND_REGS + 3*WORD_SIZE)
++
++/* save MMU settings */
++#define SleepState_Cp15_ACR_MMU (SleepState_SVC_REGS + 3*WORD_SIZE)
++#define SleepState_Cp15_AUXCR_MMU (SleepState_Cp15_ACR_MMU + WORD_SIZE)
++#define SleepState_Cp15_TTBR_MMU (SleepState_Cp15_AUXCR_MMU + WORD_SIZE)
++#define SleepState_Cp15_DACR_MMU (SleepState_Cp15_TTBR_MMU + WORD_SIZE)
++#define SleepState_Cp15_PID_MMU (SleepState_Cp15_DACR_MMU + WORD_SIZE)
++#define SleepState_Cp15_CPAR (SleepState_Cp15_PID_MMU + WORD_SIZE)
++
++#define SleepState_extendedChecksumByteCount (SleepState_Cp15_CPAR + WORD_SIZE)
++#define SleepState_psprAddress (SleepState_extendedChecksumByteCount + WORD_SIZE)
++#define SleepState_flushFunc (SleepState_psprAddress + WORD_SIZE)
++#define SleepState_end (SleepState_flushFunc + WORD_SIZE)
++#define SleepState_size (SleepState_end - SleepState_begin)
++
++#ifndef __ASSEMBLY__
++
++typedef struct {
++ unsigned long value;
++ struct {
++ unsigned ext0 : 1;
++ unsigned ext1 : 1;
++ unsigned uart1 : 1;
++ unsigned uart2 : 1;
++ unsigned uart3 : 1;
++ unsigned wifi : 1; /* wifi use UART1's pin as wakeup source */
++ unsigned mmc1_cd : 1;
++ unsigned mmc2_cd : 1;
++ unsigned mmc3_cd : 1;
++ unsigned mmc1_dat1 : 1;
++ unsigned mmc2_dat1 : 1;
++ unsigned mmc3_dat1 : 1;
++ unsigned mkey : 1;
++ unsigned usbotg : 1;
++ unsigned mlcd : 1;
++ unsigned dkey : 1;
++ unsigned usb2 : 1; /* USB 2.0 client */
++ unsigned usbh : 1; /* USB Host Port 1 */
++ unsigned msl : 1;
++ unsigned tsi : 1;
++ unsigned ost : 1;
++ unsigned rtc : 1;
++ unsigned eth : 1;
++ unsigned onkey : 1; /* pmic wakeup resources */
++ unsigned usbc : 1; /* USB client for cable and charger */
++ unsigned bat_full : 1; /* battery full */
++ unsigned bat_low : 1; /* battery low */
++ unsigned bt : 1; /* bluetooth use STRXD pin */
++ unsigned cmwdt : 1;
++ unsigned psensor : 1; /* psensor */
++ } bits;
++} pm_wakeup_src_t;
++
++
++#ifdef __KERNEL__
++struct intc_regs {
++ unsigned int iccr;
++ unsigned int ipr[32];
++ unsigned int ipr2[21];
++ unsigned int icmr;
++ unsigned int icmr2;
++ unsigned int iclr;
++ unsigned int iclr2;
++};
++
++struct clock_regs {
++ unsigned int aicsr;
++ unsigned int ckena;
++ unsigned int ckenb;
++ unsigned int oscc;
++};
++
++struct ost_regs {
++ unsigned int ossr;
++ unsigned int oier;
++ unsigned int oscr;
++ unsigned int oscr4;
++ unsigned int osmr4;
++ unsigned int omcr4;
++};
++
++struct rtc_regs {
++ unsigned int rtsr;
++ unsigned int piar;
++};
++
++struct smc_regs {
++ unsigned char __iomem *membase;
++ unsigned int msc0;
++ unsigned int msc1;
++ unsigned int mecr;
++ unsigned int sxcnfg;
++ unsigned int mcmem0;
++ unsigned int mcatt0;
++ unsigned int mcio0;
++ unsigned int memclkcfg;
++ unsigned int cscfg0;
++ unsigned int cscfg1;
++ unsigned int cscfg2;
++ unsigned int cscfg3;
++ unsigned int cscfg_p;
++ unsigned int csmscfg;
++};
++
++struct arb_regs {
++ unsigned char __iomem *membase;
++ unsigned int ctl1;
++ unsigned int ctl2;
++};
++
++struct pmu_regs {
++ unsigned int pcfr;
++ unsigned int pecr;
++ unsigned int pvcr;
++};
++
++#define MAX_MFP_PINS 419
++
++struct mfp_regs {
++ unsigned int mfp[MAX_MFP_PINS];
++};
++
++struct gpio_regs {
++ unsigned int gplr0;
++ unsigned int gplr1;
++ unsigned int gplr2;
++ unsigned int gplr3;
++ unsigned int gpdr0;
++ unsigned int gpdr1;
++ unsigned int gpdr2;
++ unsigned int gpdr3;
++ unsigned int grer0;
++ unsigned int grer1;
++ unsigned int grer2;
++ unsigned int grer3;
++ unsigned int gfer0;
++ unsigned int gfer1;
++ unsigned int gfer2;
++ unsigned int gfer3;
++};
++
++struct pm_save_data {
++ u32 checksum;
++ u32 wordCount;
++ u32 areaAddress;
++ u32 modeSaveFlags;
++ /* current mode registers cpsr, sprsr, r0-r12, lr, sp */
++ u32 ENTRY_REGS[17];
++ /* SYS mode registers:sp, lr */
++ u32 SYS_REGS[2];
++ /* FIQ mode registers:spsr, r8-r12, sp, lr */
++ u32 FIQ_REGS[8];
++ /* IRQ mode registers:spsr, sp, lr */
++ u32 IRQ_REGS[3];
++ /* ABT mode registers:spsr, sp, lr */
++ u32 ABT_REGS[3];
++ /* UND mode registers:spsr, sp, lr */
++ u32 UND_REGS[3];
++ /* SVC mode registers:spsr, sp, lr */
++ u32 SVC_REGS[3];
++ /* MMU registers */
++ u32 CP15_ACR_MMU;
++ u32 CP15_AUXCR_MMU;
++ u32 CP15_TTBR_MMU;
++ u32 CP15_DACR_MMU;
++ u32 CP15_PID_MMU;
++ u32 CP15_CPAR;
++
++ u32 extendedChecksumByteCount;
++ u32 psprAddress;
++ void (*flushFunc)(void);
++ /* the parameter is the reserved bytes from 0x5c010000 */
++ /* It returns the physical address of initlization code in SRAM */
++};
++
++struct pxa3xx_pm_regs {
++ /* It's used to save core registers. */
++ struct pm_save_data pm_data;
++ struct mfp_regs mfp;
++ struct gpio_regs gpio;
++ struct intc_regs intc;
++ struct clock_regs clock;
++ struct ost_regs ost;
++ struct rtc_regs rtc;
++ struct smc_regs smc;
++ struct arb_regs arb;
++ struct pmu_regs pmu;
++ /* It's the virtual address of ISRAM that can be accessed by kernel.
++ */
++ void *sram_map;
++ /* It's used to save ISRAM data. */
++ void *sram;
++ /* It's used to save OBM that loaded from NAND flash. */
++ void *obm;
++ /* It's the address of DDR that stores key information.
++ * Two words are used from the address.
++ */
++ void *data_pool;
++ unsigned int word0;
++ unsigned int word1;
++};
++
++extern pm_wakeup_src_t wakeup_src;
++
++struct pxa3xx_peripheral_wakeup_ops {
++ int (*init)(pm_wakeup_src_t *src);
++ int (*query)(unsigned int reg, pm_wakeup_src_t *src);
++ int (*ext)(pm_wakeup_src_t src, int enable);
++ int (*key)(pm_wakeup_src_t src, int enable);
++ int (*mmc)(pm_wakeup_src_t src, int enable);
++ int (*uart)(pm_wakeup_src_t src, int enable);
++ int (*eth)(pm_wakeup_src_t src, int enable);
++ int (*tsi)(pm_wakeup_src_t src, int enable);
++ int (*usb)(pm_wakeup_src_t src, int enable);
++ int (*cmwdt)(pm_wakeup_src_t src, int enable);
++ int (*psensor)(pm_wakeup_src_t src, int enable);
++};
++
++extern int pxa3xx_wakeup_register(struct pxa3xx_peripheral_wakeup_ops *);
++extern void pxa3xx_lock_suspend(void);
++extern void pxa3xx_unlock_suspend(void);
++extern void pxa3xx_lock_suspend_cancel(void);
++#endif
++#endif
++
++#endif
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_pmic.h kernel/arch/arm/mach-pxa/include/mach/pxa3xx_pmic.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/pxa3xx_pmic.h 2009-12-13 12:59:52.402368878 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/pxa3xx_pmic.h 2009-12-12 16:09:26.462949527 +0200
+@@ -0,0 +1,194 @@
++#ifndef __PMIC_H__
++#define __PMIC_H__
++
++#include <linux/i2c.h>
++#include <linux/interrupt.h>
++
++/* this enum should be consistent with micco_power_module[]
++ * in arch/arm/mach-pxa/xxx(platform).c */
++enum {
++ /* Set command > 0xFFFF0000 to avoid wrong
++ * parameter is used for pmic_set_voltage.
++ */
++ VCC_CORE = 0xFFFF0000,
++ VCC_SRAM,
++ VCC_MVT,
++ VCC_3V_APPS,
++ VCC_SDIO,
++ VCC_CAMERA_ANA,
++ VCC_USB,
++ VCC_LCD,
++ VCC_TSI,
++ VCC_CAMERA_IO,
++ VCC_1P8V,
++ VCC_MEM,
++ HDMI_TX,
++ TECH_3V,
++ TECH_1P8V,
++
++ /* add your command here */
++ VCC_BT,
++ VCC_JOGBALL,
++ VCC_BTWIFFSHARE,
++ VCC_LCD_IO,
++ VCC_TOUCHKEY,
++ /* max command */
++ MAX_CMD,
++};
++
++enum {
++ LED_BACKLIGHT = 0xFFFF0000,
++ LED_VIBRATOR,
++ LED_FLASH,
++ LED_GREEN,
++ LED_RED,
++ LED_BLUE,
++ LED_KEYPAD,
++ LED_MAX_CMD,
++};
++
++#define PMIC_EVENT_EXTON (1 << 0)
++#define PMIC_EVENT_VBUS (1 << 1)
++#define PMIC_EVENT_USB (PMIC_EVENT_EXTON | PMIC_EVENT_VBUS)
++
++#define PMIC_EVENT_TOUCH (1 << 2)
++
++#define PMIC_EVENT_OTGCP_IOVER (1 << 3)
++
++#define PMIC_EVENT_TBAT (1 << 4)
++#define PMIC_EVENT_REV_IOVER (1 << 5)
++#define PMIC_EVENT_IOVER (1 << 6)
++#define PMIC_EVENT_CHDET (1 << 7)
++#define PMIC_EVENT_VBATMON (1 << 8)
++#define PMIC_EVENT_ONKEY (1 << 9)
++
++#ifdef CONFIG_MICCO_HEADSET_DETECT
++#define PMIC_EVENT_HEADSET (1 << 10)
++#define PMIC_EVENT_HOOKSWITCH (1 << 11)
++#endif
++#define PMIC_EVENT_CH_CCTO (1 << 12)
++#define PMIC_EVENT_CH_TCTO (1 << 13)
++
++#define PMIC_EVENT_CHARGER (PMIC_EVENT_TBAT | \
++ PMIC_EVENT_REV_IOVER | \
++ PMIC_EVENT_IOVER | \
++ PMIC_EVENT_CHDET | \
++ PMIC_EVENT_CH_CCTO | \
++ PMIC_EVENT_CH_TCTO | \
++ PMIC_EVENT_VBATMON)
++
++
++#define PMIC_EVENT_USB_IN (1 << 0)
++#define PMIC_EVENT_AC_IN (1 << 1)
++#define PMIC_EVENT_CABLE_OUT (1 << 2)
++#define PMIC_EVENT_CABLE_DETECT (PMIC_EVENT_USB_IN | \
++ PMIC_EVENT_AC_IN | \
++ PMIC_EVENT_CABLE_OUT)
++
++struct pmic_ops {
++ int (*get_voltage)(int cmd, int *pmv);
++ int (*set_voltage)(int cmd, int mv);
++ int (*enable_voltage)(int cmd, int enable);
++ int (*check_voltage)(int cmd);
++ int (*enable_led)(int cmd, int enable);
++ int (*enable_event)(unsigned long, int enable);
++ int (*is_vbus_assert)(void);
++ int (*is_avbusvld)(void);
++ int (*is_asessvld)(void);
++ int (*is_bsessvld)(void);
++ int (*is_srp_ready)(void);
++
++ int (*set_pump)(int enable);
++ int (*set_vbus_supply)(int enable, int srp);
++ int (*set_usbotg_a_mask)(void);
++ int (*set_usbotg_b_mask)(void);
++ int (*is_usbpump_chg)(void);
++
++ int (*is_onkey_assert)(void);
++ int (*is_hookswitch_assert)(void);
++ int (*init)(struct device *dev);
++ int (*deinit)(struct device *dev);
++
++ struct list_head list; /* callback list */
++ spinlock_t cb_lock; /* spinlock for callback list */
++};
++
++struct pmic_callback {
++ unsigned long event; /* the event which callback care about */
++ void (*func)(unsigned long event); /*callback function */
++ struct list_head list;
++};
++
++struct pxa3xx_pmic_regs {
++ unsigned int data:8;
++ unsigned int hit:1;
++ unsigned int mask:1;
++ unsigned int inited:1;
++ unsigned int cacheable:1;
++};
++
++extern void start_calc_time(void);
++extern void end_calc_time(void);
++
++extern int pxa3xx_pmic_write(u8 reg, u8 val);
++extern int pxa3xx_pmic_read(u8 reg, u8 *pval);
++
++extern void pmic_set_ops(struct pmic_ops *ops);
++
++extern int pmic_callback_register(unsigned long event,
++ void (*func)(unsigned long event));
++extern int pmic_callback_unregister(unsigned long event,
++ void (*func)(unsigned long event));
++
++extern int pmic_event_handle(unsigned long event);
++
++extern int pxa3xx_pmic_get_voltage(int cmd, int *pval);
++extern int pxa3xx_pmic_set_voltage(int cmd, int val);
++
++extern int pxa3xx_pmic_check_voltage(int cmd);
++extern int pxa3xx_pmic_enable_voltage(int cmd, int enable);
++extern int pxa3xx_pmic_enable_led(int cmd, int enable);
++/* Check whether USB VBUS is asserted */
++extern int pxa3xx_pmic_is_vbus_assert(void);
++/* Check whether USB VBUS has gone above A-device VBUS valid threshold
++ * Min: 4.4V Max: N/A
++ */
++extern int pxa3xx_pmic_is_avbusvld(void);
++/* Check whether VBUS has gone above A-device Session Valid threshold
++ * Min: 0.8V Max: 2.0V
++ */
++extern int pxa3xx_pmic_is_asessvld(void);
++/* Check whether VBUS has gone above B-device Session Valid threshold
++ * Min: 0.8V Max: 4.0V
++ */
++extern int pxa3xx_pmic_is_bsessvld(void);
++/* Check whether VBUS has gone above B-device Session End threshold
++ * Min: 0.2V Max: 0.8V
++ */
++extern int pxa3xx_pmic_is_srp_ready(void);
++/* Initialize the USB PUMP */
++extern int pxa3xx_pmic_set_pump(int enable);
++/* Check the events change of PMIC */
++extern int pxa3xx_pmic_event_change(void);
++/* enable/disable VBUS supply */
++extern int pxa3xx_pmic_set_vbus_supply(int enable, int srp);
++/* Set events mask for USB A-device
++ * A-device Sessino Valid event
++ * A-device VBUS Valid event
++ */
++extern int pxa3xx_pmic_set_usbotg_a_mask(void);
++/* Set events mask for USB B-device
++ * B-device Session Valid event
++ * B-device Session end event
++ */
++extern int pxa3xx_pmic_set_usbotg_b_mask(void);
++
++extern int pxa3xx_pmic_is_onkey_assert(void);
++
++extern int pxa3xx_pmic_is_hookswitch_assert(void);
++
++extern int px3xx_pmic_event_enable(unsigned long event, int enable);
++
++
++#endif
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/sgh_msm6k.h kernel/arch/arm/mach-pxa/include/mach/sgh_msm6k.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/sgh_msm6k.h 2009-12-13 12:59:59.879036795 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/sgh_msm6k.h 2009-12-12 16:09:26.466285980 +0200
+@@ -0,0 +1,35 @@
++#ifndef __SGH_MSM6K__
++#define __SGH_MSM6K__
++
++#define CH_RPC 0
++
++enum RPC_PKT_READ_TYPE {
++ RPC_INDICATOR=1,
++ RPC_RESPONSE,
++ RPC_NOTIFICATION,
++};
++
++enum RPC_PKT_WRITE_TYPE {
++ RPC_EXECUTE=1,
++ RPC_GET,
++ RPC_SET,
++ RPC_CFRM,
++};
++
++#define RPC_GSM_CALL_INCOMING 0x0202
++#define RPC_GSM_CALL_STATUS 0x0205
++
++#define RPC_GSM_SEC_PIN_STATUS 0x0501
++#define RPC_GSM_SEC_PHONE_LOCK 0x0502
++#define RPC_GSM_SEC_LOCK_INFOMATION 0x0508
++
++#define RPC_GSM_SS_INFO 0x0c06
++
++extern void smd_init(void);
++extern int smd_read(int ch, void* buf, int len);
++extern int smd_write(int ch, void *_buf, int len);
++extern void smd_phone_power(int on);
++
++extern void rpc_init(void);
++
++#endif
+diff -ur linux-2.6.32/arch/arm/mach-pxa/include/mach/xscale-pmu.h kernel/arch/arm/mach-pxa/include/mach/xscale-pmu.h
+--- linux-2.6.32/arch/arm/mach-pxa/include/mach/xscale-pmu.h 2009-12-13 13:00:05.321944499 +0200
++++ kernel/arch/arm/mach-pxa/include/mach/xscale-pmu.h 2009-12-12 16:09:26.469613568 +0200
+@@ -0,0 +1,66 @@
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ *
++ * (C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#ifndef _XSCALE_PMU_H_
++#define _XSCALE_PMU_H_
++
++#include <linux/types.h>
++
++/*
++ * Different types of events that can be counted by the XScale PMU
++ */
++#define EVT_ICACHE_MISS 0x00
++#define EVT_ICACHE_NO_DELIVER 0x01
++#define EVT_DATA_STALL 0x02
++#define EVT_ITLB_MISS 0x03
++#define EVT_DTLB_MISS 0x04
++#define EVT_BRANCH 0x05
++#define EVT_BRANCH_MISS 0x06
++#define EVT_INSTRUCTION 0x07
++#define EVT_DCACHE_FULL_STALL 0x08
++#define EVT_DCACHE_FULL_STALL_CONTIG 0x09
++#define EVT_DCACHE_ACCESS 0x0A
++#define EVT_DCACHE_MISS 0x0B
++#define EVT_DCACE_WRITE_BACK 0x0C
++#define EVT_PC_CHANGED 0x0D
++#define EVT_BCU_REQUEST 0x10
++#define EVT_BCU_FULL 0x11
++#define EVT_BCU_DRAIN 0x12
++#define EVT_BCU_ECC_NO_ELOG 0x14
++#define EVT_BCU_1_BIT_ERR 0x15
++#define EVT_RMW 0x16
++
++struct pmu_results
++{
++ u32 ccnt_of;
++ u32 ccnt; /* Clock Counter Register */
++ u32 pmn0_of;
++ u32 pmn0; /* Performance Counter Register 0 */
++ u32 pmn1_of;
++ u32 pmn1; /* Performance Counter Register 1 */
++ u32 pmn2_of;
++ u32 pmn2; /* Performance Counter Register 2 */
++ u32 pmn3_of;
++ u32 pmn3; /* Performance Counter Register 3 */
++};
++
++#ifdef __KERNEL__
++
++extern struct pmu_results results;
++
++int pmu_claim(void); /* Claim PMU for usage */
++int pmu_start(u32, u32, u32, u32); /* Start PMU execution */
++int pmu_stop(struct pmu_results *); /* Stop perfmon unit */
++int pmu_release(int); /* Release PMU */
++#endif
++
++#endif /* _XSCALE_PMU_H_ */
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pmu.c kernel/arch/arm/mach-pxa/pmu.c
+--- linux-2.6.32/arch/arm/mach-pxa/pmu.c 2009-12-13 13:00:12.875282685 +0200
++++ kernel/arch/arm/mach-pxa/pmu.c 2009-12-12 16:09:26.479614367 +0200
+@@ -0,0 +1,183 @@
++/*
++ * "This software program is available to you under a choice of one of two
++ * licenses. You may choose to be licensed under either the GNU General Public
++ * License (GPL) Version 2, June 1991, available at
++ * http://www.fsf.org/copyleft/gpl.html, or the BSD License, the text of
++ * which follows:
++ *
++ * Copyright (c) 1996-2005, Intel Corporation. All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions are met:
++ *
++ * Redistributions of source code must retain the above copyright notice, this
++ * list of conditions and the following disclaimer.
++ *
++ * Redistributions in binary form must reproduce the above copyright notice, this
++ * list of conditions and the following disclaimer in the documentation and/or
++ * other materials provided with the distribution.
++ *
++ * Neither the name of the Intel Corporation ("Intel") nor the names of its
++ * contributors may be used to endorse or promote products derived from this
++ * software without specific prior written permission.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
++ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
++ */
++
++/*
++ * FILENAME: pmu.c
++ *
++ * CORE STEPPING:
++ *
++ * PURPOSE: contains all PMU C function.
++ *
++ * (C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <mach/pmu.h>
++#include <asm/types.h>
++#include <mach/pxa3xx-regs.h>
++#include <mach/hardware.h>
++#include <asm/io.h>
++
++static struct pxa3xx_pmu_info *pmu_info;
++
++/*
++ * Select one event including PMU and PML envent for PMU counter
++ *
++ * @par
++ * This function selects one event including Manzano and Monahans event.
++ * When type is Monahans PML Event, it is Monahans PML Event Number OR
++ * PXA3xx_EVENT_MASK. Other words, when type is Manzano event, bit31 is
++ * zero. When type is Monahans PML Event, bit31 is one.
++ * @par
++ * We only use Monahans PML first four event selectors because manzano
++ * has only 4 counters and every selector can choose all PML events.
++ * We use 1:1 map from PMU counter to PML selector. So counter 0 use
++ * PML_SEL0, counter1 use PML_SEL1 and so on.
++ * @param
++ * counter PMU Counter Number. It must be between 0 and 3
++ * type PMU And PML type
++ * @return
++ * old event type before call this function.
++ * @remarks
++ * required kernel/supervisor mode
++ */
++int pmu_select_event(int counter, int type)
++{
++ u32 oldevent, value, pmuevent, shift;
++
++ if(counter < 0 || counter > 3) {
++ return PMU_EVENT_INVALIDATE;
++ }
++ shift = counter * 8;
++
++ value = pmu_read_reg((u32)PMU_EVTSEL);
++ pmuevent = (value >> shift) & 0xFF;
++
++ if (pmuevent >= PMU_EVENT_ASSP_0 && pmuevent <= PMU_EVENT_ASSP_3) {
++ oldevent = PXA3xx_EVENT_MASK |
++ (*(pmu_info->pmu_base + (counter << 2)));
++ } else {
++ oldevent = pmuevent;
++ }
++
++ if(type & PXA3xx_EVENT_MASK) {
++ /* PML Event */
++ value &= ~(0xFF << shift);
++ value |= (PMU_EVENT_ASSP_0 + counter) << shift;
++ *(pmu_info->pmu_base + (counter << 2)) =
++ type & (~PXA3xx_EVENT_MASK);
++ } else {
++ /* PMU Event */
++ value &= ~(0xFF << shift);
++ value |= (type & 0xFF) << shift;
++ }
++ pmu_write_reg((u32)PMU_EVTSEL, value);
++
++ return oldevent;
++}
++
++#ifdef CONFIG_PM
++static int pxa3xx_pmu_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int pxa3xx_pmu_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++#else
++#define pxa3xx_pmu_suspend NULL
++#define pxa3xx_pmu_resume NULL
++#endif
++
++static int __init pxa3xx_pmu_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++
++ pmu_info = kzalloc(sizeof(struct pxa3xx_pmu_info), GFP_KERNEL);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu_regs");
++ if (!res) goto err;
++ pmu_info->pmu_base = ioremap(res->start, res->end - res->start + 1);
++ return 0;
++err:
++ printk("pxa3xx PMU init failed\n");
++ return -EIO;
++}
++
++static int pxa3xx_pmu_remove(struct platform_device *pdev)
++{
++ struct resource *res;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu_regs");
++ if (!res) goto err;
++ iounmap(pmu_info->pmu_base);
++ kfree(pmu_info);
++ return 0;
++err:
++ printk("pxa3xx PMU remove failed\n");
++ return -EIO;
++}
++
++static struct platform_driver pxa3xx_pmu_driver = {
++ .driver = {
++ .name = "pxa3xx-pmu",
++ },
++ .probe = pxa3xx_pmu_probe,
++ .remove = pxa3xx_pmu_remove,
++#ifdef CONFIG_PM
++ .suspend = pxa3xx_pmu_suspend,
++ .resume = pxa3xx_pmu_resume,
++#endif
++};
++
++static int __init pxa3xx_pmu_init(void)
++{
++ return platform_driver_register(&pxa3xx_pmu_driver);
++}
++
++static void __exit pxa3xx_pmu_exit(void)
++{
++ platform_driver_unregister(&pxa3xx_pmu_driver);
++}
++
++module_init(pxa3xx_pmu_init);
++module_exit(pxa3xx_pmu_exit);
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pmu_ll.S kernel/arch/arm/mach-pxa/pmu_ll.S
+--- linux-2.6.32/arch/arm/mach-pxa/pmu_ll.S 2009-12-13 13:00:17.648612716 +0200
++++ kernel/arch/arm/mach-pxa/pmu_ll.S 2009-12-12 16:09:26.479614367 +0200
+@@ -0,0 +1,204 @@
++@ "This software program is available to you under a choice of one of two
++@ licenses. You may choose to be licensed under either the GNU General Public
++@ License (GPL) Version 2, June 1991, available at
++@ http://www.fsf.org/copyleft/gpl.html, or the BSD License, the text of
++@ which follows:
++@
++@ Copyright (c) 1996-2005, Intel Corporation. All rights reserved.
++@
++@ Redistribution and use in source and binary forms, with or without
++@ modification, are permitted provided that the following conditions are met:
++@
++@ Redistributions of source code must retain the above copyright notice, this
++@ list of conditions and the following disclaimer.
++@
++@ Redistributions in binary form must reproduce the above copyright notice, this
++@ list of conditions and the following disclaimer in the documentation and/or
++@ other materials provided with the distribution.
++@
++@ Neither the name of the Intel Corporation ("Intel") nor the names of its
++@ contributors may be used to endorse or promote products derived from this
++@ software without specific prior written permission.
++@
++@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++@ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++@ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++@ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
++@ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
++@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
++@
++@ FILENAME: pmu_ll.S
++@
++@ PURPOSE: Provides low level PMU primitive functions written specifically for
++@ the Bulverde/Mainstone processor/platform. Specially design to fit
++@ into Intel VTUNE Architecture
++@
++@
++@ LAST MODIFIED: 10/31/02
++@******************************************************************************
++@
++@
++@ List of primitive functions in this source code include:
++@
++ .global pmu_read_reg
++ .global pmu_write_reg
++
++ .text
++
++@
++@ pmu_read_reg - Read the PMU Register
++@
++@ Description:
++@ This routine reads the designated PMU register via CoProcesser 14.
++@
++@ Input Parameters:
++@ r0 - arg1, PMU register number to read. Number between 0 to 8
++@ if r0 contains:
++@ 0 -> PMNC, PMU Control Register
++@ 1 -> CCNT, PMU Clock Counter
++@ 2 -> PMN0, PMU Count Register 0
++@ 3 -> PMN1, PMU Count Register 1
++@ 4 -> PMN2, PMU Count Register 2
++@ 5 -> PMN3, PMU Count Register 3
++@ 6 -> INTEN, PMU Interupt Enable Register
++@ 7 -> FLAG, PMU Overflow Flag Status Register
++@ 8 -> EVTSEL PMU Event Select Register
++@
++@ Returns:
++@ r0 - 32-bit value read from CoProcessor
++@
++@ Registers Modified:
++@ CoProcessor Register Modified: None
++@ General Purpose Registers Modified: r0
++@
++@ NOTE:
++@ Currently not support THUMB mode
++@ Error checking not included
++
++pmu_read_reg:
++
++ cmp r0, #8
++ addls pc, pc, r0, lsl #2
++ b RRet
++ b RdPMNC
++ b RdCCNT
++ b RdPMN0
++ b RdPMN1
++ b RdPMN2
++ b RdPMN3
++ b RdINTEN
++ b RdFLAG
++ b RdEVTSEL
++
++RdPMNC:
++ mrc p14, 0, r0, c0, c1, 0 @ Read PMNC
++ b RRet
++RdCCNT:
++ mrc p14, 0, r0, c1, c1, 0 @ Read CCNT
++ b RRet
++RdPMN0:
++ mrc p14, 0, r0, c0, c2, 0 @ Read PMN0
++ b RRet
++RdPMN1:
++ mrc p14, 0, r0, c1, c2, 0 @ Read PMN1
++ b RRet
++RdPMN2:
++ mrc p14, 0, r0, c2, c2, 0 @ Read PMN2
++ b RRet
++RdPMN3:
++ mrc p14, 0, r0, c3, c2, 0 @ Read PMN3
++ b RRet
++RdINTEN:
++ mrc p14, 0, r0, c4, c1, 0 @ Read INTEN
++ b RRet
++RdFLAG:
++ mrc p14, 0, r0, c5, c1, 0 @ Read FLAG
++ b RRet
++RdEVTSEL:
++ mrc p14, 0, r0, c8, c1, 0 @ Read EVTSEL
++
++RRet:
++ mov pc, lr @ return
++
++
++@
++@ pmu_write_reg - Writes to the PMU Register
++@
++@ Description:
++@ This routine writes to the designated PMU register via CoProcesser 14.
++@
++@ Input Parameters:
++@ r0 - arg1 - PMU register number to write
++@ r1 - arg2 - Value to write to PMU register
++@
++@ if r0 contains:
++@ 0 -> PMNC, PMU Control Register
++@ 1 -> CCNT, PMU Clock Counter
++@ 2 -> PMN0, PMU Count Register 0
++@ 3 -> PMN1, PMU Count Register 1
++@ 4 -> PMN2, PMU Count Register 2
++@ 5 -> PMN3, PMU Count Register 3
++@ 6 -> INTEN, PMU Interupt Enable Register
++@ 7 -> FLAG, PMU Overflow Flag Status Register
++@ 8 -> EVTSEL PMU Event Select Register
++@
++@ Returns:
++@ None
++@
++@ Registers Modified:
++@ CoProcessor Register Modified: PMU Register
++@ General Purpose Registers Modified: None
++@
++@NOTE:
++@ Currently not support THUMB mode
++@ Error checking not included
++
++pmu_write_reg:
++
++ cmp r0, #8
++ addls pc, pc, r0, lsl #2
++ b WRet
++ b WrPMNC
++ b WrCCNT
++ b WrPMN0
++ b WrPMN1
++ b WrPMN2
++ b WrPMN3
++ b WrINTEN
++ b WrFLAG
++ b WrEVTSEL
++
++WrPMNC:
++ mcr p14, 0, r1, c0, c1, 0 @ Write PMNC
++ b WRet
++WrCCNT:
++ mcr p14, 0, r1, c1, c1, 0 @ Write CCNT
++ b WRet
++WrPMN0:
++ mcr p14, 0, r1, c0, c2, 0 @ Write PMN0
++ b WRet
++WrPMN1:
++ mcr p14, 0, r1, c1, c2, 0 @ Write PMN1
++ b WRet
++WrPMN2:
++ mcr p14, 0, r1, c2, c2, 0 @ Write PMN2
++ b WRet
++WrPMN3:
++ mcr p14, 0, r1, c3, c2, 0 @ Write PMN3
++ b WRet
++WrINTEN:
++ mcr p14, 0, r1, c4, c1, 0 @ Write INTEN
++ b WRet
++WrFLAG:
++ mcr p14, 0, r1, c5, c1, 0 @ Write FLAG
++ b WRet
++WrEVTSEL:
++ mcr p14, 0, r1, c8, c1, 0 @ Write EVTSEL
++
++WRet:
++ mov pc, lr @ return
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/prm.c kernel/arch/arm/mach-pxa/prm.c
+--- linux-2.6.32/arch/arm/mach-pxa/prm.c 2009-12-13 13:00:22.645696759 +0200
++++ kernel/arch/arm/mach-pxa/prm.c 2009-12-12 16:09:26.479614367 +0200
+@@ -0,0 +1,1266 @@
++/*
++ * Monahans Profiler Resource Manager
++ *
++ * Copyright (C) 2004, Intel Corporation(chao.xie@intel.com).
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++ *
++ *(C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <asm/current.h>
++#include <linux/proc_fs.h>
++#include <linux/rwsem.h>
++#include <linux/interrupt.h>
++#include <mach/prm.h>
++#include <mach/pmu.h>
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_dvfm.h>
++
++/*#define DEBUG
++ */
++
++#ifdef DEBUG
++#define DPRINTK(fmt,args...) \
++ do { printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); } while (0)
++#else
++#define DPRINTK(fmt,args...) do {} while (0)
++#endif
++
++#define ASSERT_PRIORITY(pri) \
++ if ((pri) < PRI_LOWEST || (pri) > PRI_HIGHEST) \
++ return -EINVAL;
++#define ASSERT_CLIENT_ID(client) \
++ if ((client) < 0 || (client) > MAX_CLIENTS) \
++ return -EINVAL;
++#define ASSERT_RESOURCE_ID(resource) \
++ if ((resource) < 0 || (resource) > RESOURCE_NUM) \
++ return -EINVAL;
++#define ASSERT_GROUP_ID(group) \
++ if ((group) < 0 || (group) > MAX_GROUPS) \
++ return -EINVAL;
++
++#define IS_PRM_RESOURCE(reg) ((reg) >= PMU_CCNT && (reg) <= PMU_PMN3)
++#define PMU_PRM(reg) (reg - 1)
++
++#define IS_HIGHER_PRIORITY(h1, h2) ((h1) > (h2))
++#define for_each_lower_priority(index, pri) \
++ for (index = pri - 1; index >= PRI_LOWEST; index = index - 1)
++
++#define STATE_UNDEF 0x1
++#define STATE_ACTIVE 0x2
++#define STATE_APPROPRIATED 0x3
++
++static struct prm_resource prm_resources[RESOURCE_NUM];
++static struct prm_client *prm_clients[MAX_CLIENTS];
++static struct prm_client *prm_pmu_client;
++
++struct rw_semaphore prm_sem;
++
++#ifdef DEBUG
++static struct proc_dir_entry *clients_root;
++static struct proc_dir_entry *resources_root;
++static struct proc_dir_entry *prm_root;
++
++#define proc_dump_end(len, page, start, off, count, eof) \
++do { \
++ if (len <= off + count) *eof = 1; \
++ *start = page + off; \
++ len -= off; \
++ if (len > count) len = count; \
++ if (len < 0) len = 0; \
++} while (0)
++
++static int dump_group(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ char *buf = page;
++ int len;
++ struct prm_group *group = (struct prm_group *)data;
++
++ buf += sprintf(buf, "address: 0x%x\n member_cnt: %u\n"
++ " appropriated_cnt: %u\n",
++ (unsigned int)group, group->member_cnt,
++ group->appropriated_cnt);
++ len = buf - page;
++ proc_dump_end(len, page, start, off, count, eof);
++ return len;
++}
++
++static int dump_resource_state(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int client_id = -1, group_id = -1;
++ int i, len;
++ char *buf = page;
++ struct prm_resource_state *state = (struct prm_resource_state *)data;
++
++ if (state->allocate) {
++ for (i = 0; i < MAX_CLIENTS; i++) {
++ if (prm_clients[i] && prm_clients[i] == state->allocate) {
++ client_id = i;
++ break;
++ }
++ }
++ for (i = 0; i < MAX_GROUPS; i++) {
++ if (state->allocate->groups[i] &&
++ state->allocate->groups[i] == state->group) {
++ group_id = i;
++ break;
++ }
++ }
++ }
++ buf += sprintf(buf, "allocate: 0x%x(%d)\n group: 0x%x(%d)\n"
++ " active: %u\n resource: 0x%x\n",
++ (unsigned int)state->allocate, client_id,
++ (unsigned int)state->group,
++ group_id, state->active, (unsigned int)state->resource);
++ len = buf - page;
++ proc_dump_end(len, page, start, off, count, eof);
++ return len;
++}
++
++static int dump_client(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int i, len;
++ char *buf = page;
++ struct prm_client *client = (struct prm_client *)data;
++
++ buf += sprintf(buf, "address: 0x%x\n id: %u\n pid: %u\n"
++ " priority: %u\n name: %s\n group_cnt: %u\n",
++ (unsigned int)client, client->id, client->pid,
++ client->priority, client->name, client->group_cnt);
++ for (i = 0 ;i < MAX_GROUPS; i++) {
++ if (client->groups[i])
++ buf += sprintf(buf, "group%u address: 0x%x\n",
++ i, (unsigned int)client->groups[i]);
++ }
++ len = buf - page;
++ proc_dump_end(len, page, start, off, count, eof);
++ return len;
++}
++
++static int dump_resource(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int i, client_id = -1, len;
++ char *buf = page;
++ struct prm_resource *resource = (struct prm_resource *)data;
++
++ for (i = 0; i < MAX_CLIENTS; i++) {
++ if (prm_clients[i] && prm_clients[i] == resource->access) {
++ client_id = i;
++ break;
++ }
++ }
++ buf += sprintf(buf, " address:0x%x\n access: 0x%x(%d)\n id: %u\n",
++ (unsigned int)resource, (unsigned int)resource->access,
++ client_id, resource->id);
++ len = buf - page;
++ proc_dump_end(len, page, start, off, count, eof);
++ return len;
++}
++
++static void proc_add_client(struct prm_client *client)
++{
++ char buf[16];
++
++ sprintf(buf, "client%d", client->id);
++ client->dir = proc_mkdir(buf, clients_root);
++ create_proc_read_entry("info", 0, client->dir, dump_client, client);
++}
++
++static void proc_del_client(struct prm_client *client)
++{
++ char buf[16];
++
++ remove_proc_entry("info", client->dir);
++ sprintf(buf, "client%d", client->id);
++ remove_proc_entry(buf, clients_root);
++}
++
++static void proc_add_group(struct prm_client *client,
++ struct prm_group *group, unsigned int group_id)
++{
++ char buf[16];
++
++ sprintf(buf, "group%d", group_id);
++ group->dir = proc_mkdir(buf, client->dir);
++ create_proc_read_entry("info", 0, group->dir, dump_group, group);
++}
++
++static void proc_del_group(struct prm_client*client,
++ struct prm_group *group, unsigned int group_id)
++{
++ char buf[32];
++
++ remove_proc_entry("info", group->dir);
++ sprintf(buf, "group%d", group_id);
++ remove_proc_entry(buf, client->dir);
++}
++
++static void proc_add_resource(struct prm_resource *resource)
++{
++ char buf[16];
++
++ sprintf(buf, "resource%d", resource->id);
++ resource->dir = proc_mkdir(buf, resources_root);
++ create_proc_read_entry("info", 0, resource->dir,
++ dump_resource, resource);
++}
++
++static void proc_del_resource(struct prm_resource *resource)
++{
++ char buf[16];
++
++ remove_proc_entry("info", resource->dir);
++ sprintf(buf, "resource%d", resource->id);
++ remove_proc_entry(buf, resources_root);
++}
++
++static void proc_add_resource_state(struct prm_resource_state *state,
++ unsigned int priority)
++{
++ char buf[16];
++
++ sprintf(buf, "state%d", priority);
++ state->dir = proc_mkdir(buf, state->resource->dir);
++ create_proc_read_entry("info", 0, state->dir,
++ dump_resource_state, state);
++}
++
++static void proc_del_resource_state(struct prm_resource_state *state,
++ unsigned int priority)
++{
++ char buf[16];
++
++ remove_proc_entry("info", state->dir);
++ sprintf(buf, "state%d", priority);
++ remove_proc_entry(buf, state->resource->dir);
++}
++
++static void proc_commit_resource(struct prm_resource *resource)
++{
++ char buf[32];
++
++ remove_proc_entry("access", resource->dir);
++ sprintf(buf, "/proc/prm/clients/%s", resource->access->dir->name);
++ proc_symlink("access", resource->dir, buf);
++}
++
++static void proc_allocate_resource(struct prm_resource_state *state)
++{
++ char buf[32], path[64];
++
++ remove_proc_entry("allocate", state->dir);
++
++ sprintf(path, "/proc/prm/clients/%s/%s",
++ state->allocate->dir->name, state->group->dir->name);
++ proc_symlink("group", state->dir, path);
++ sprintf(buf, "resource%d_state%d",
++ state->resource->id, state->allocate->priority);
++ sprintf(path, "/proc/prm/resources/%s/%s",
++ state->resource->dir->name, state->dir->name);
++ proc_symlink(buf, state->group->dir, path);
++}
++
++static void proc_free_resource(struct prm_resource_state *state)
++{
++ char buf[32];
++
++ sprintf(buf, "resource%d_state%d",
++ state->resource->id, state->allocate->priority);
++ remove_proc_entry("access", state->resource->dir);
++ remove_proc_entry("allocate", state->dir);
++ remove_proc_entry("group", state->dir);
++ remove_proc_entry(buf, state->group->dir);
++}
++
++static void proc_prm_init(void)
++{
++ prm_root = proc_mkdir("prm", NULL);
++ clients_root = proc_mkdir("clients", prm_root);
++ resources_root = proc_mkdir("resources", prm_root);
++}
++
++static void proc_prm_exit(void)
++{
++ remove_proc_entry("clients", prm_root);
++ remove_proc_entry("resources", prm_root);
++ remove_proc_entry("prm", NULL);
++}
++#else
++static void proc_add_client(struct prm_client *client) {}
++static void proc_del_client(struct prm_client *client) {}
++static void proc_add_group(struct prm_client *client,
++ struct prm_group *group, unsigned int group_id) {}
++static void proc_del_group(struct prm_client*client,
++ struct prm_group *group, unsigned int group_id) {}
++static void proc_add_resource(struct prm_resource *resource) {}
++static void proc_del_resource(struct prm_resource *resource) {}
++static void proc_add_resource_state(struct prm_resource_state *state,
++ unsigned int priority) {}
++static void proc_del_resource_state(struct prm_resource_state *state,
++ unsigned int priority) {}
++static void proc_commit_resource(struct prm_resource *resource) {}
++static void proc_allocate_resource(struct prm_resource_state *state) {}
++static void proc_free_resource(struct prm_resource_state *state) {}
++static void proc_prm_init(void) {}
++static void proc_prm_exit(void) {}
++#endif
++
++/*****************************************************************************/
++/* */
++/* Profiler Resource Manager */
++/* */
++/*****************************************************************************/
++
++static void clear_state(struct prm_resource_state *state)
++{
++ state->allocate = NULL;
++ state->group = NULL;
++ state->active = STATE_UNDEF;
++ /* the state has been deleted from the group resource list */
++ INIT_LIST_HEAD(&(state->entry));
++}
++
++static int group_commited(struct prm_client *client,
++ struct prm_group *group)
++{
++ struct prm_resource_state *state;
++ struct prm_resource *resource;
++ struct list_head *pos;
++
++ list_for_each(pos, &(group->resources)) {
++ state = list_entry(pos, struct prm_resource_state, entry);
++ resource = state->resource;
++ if (resource->access != client) {
++ return 0;
++ }
++ }
++ return 1;
++}
++
++static int try_to_access_group(struct prm_client *client,
++ struct prm_group *group, int set_state)
++{
++ struct prm_resource_state *state;
++ struct prm_resource *resource;
++ int ret = 0;
++ struct list_head *pos;
++
++ DPRINTK("client <%d> try to access group <%d>, set_state as <%d>\n",
++ (unsigned int)client->id, (unsigned int)group->id, set_state);
++ list_for_each(pos, &(group->resources)) {
++ state = list_entry(pos, struct prm_resource_state, entry);
++ resource = state->resource;
++ if (resource->access != NULL && resource->access != client &&
++ IS_HIGHER_PRIORITY(resource->access->priority,
++ client->priority)) {
++ if (set_state) {
++ state->active = STATE_APPROPRIATED;
++ group->appropriated_cnt++;
++ }
++ ret++;
++ }
++ }
++ DPRINTK("try_to_access() return :%d\n", ret);
++ return ret;
++}
++
++static struct prm_client * get_resource_access(struct prm_resource *resource)
++{
++ if (resource)
++ return resource->access;
++ else /*for access the isr and control regs of PMU */
++ return (struct prm_client *)(
++ (unsigned long)prm_resources[PRM_CCNT].access &
++ (unsigned long)prm_resources[PRM_PMN0].access &
++ (unsigned long)prm_resources[PRM_PMN1].access &
++ (unsigned long)prm_resources[PRM_PMN2].access &
++ (unsigned long)prm_resources[PRM_PMN3].access
++ );
++}
++
++static void unload_isr(struct prm_client *client)
++{
++ if (prm_pmu_client == client || get_resource_access(NULL) == client)
++ prm_pmu_client = NULL;
++}
++
++static void load_isr(struct prm_client *client)
++{
++ if (prm_pmu_client != client && get_resource_access(NULL) == client)
++ prm_pmu_client = client;
++}
++
++/* this function will be invoked with locked */
++static int set_resource_access(struct prm_resource *resource,
++ struct prm_client *client)
++{
++ struct prm_resource_state *state, *owner_state;
++ struct prm_group *group, *owner_group;
++ struct prm_client *owner;
++ int ret = 0;
++
++ if (client == NULL) {
++ /* The client will free the committed resources to the appropriated
++ * lower client. And notification will be sent so as to give the lower
++ * priority client a chance to commit resources if:
++ * 1. all the resources of the lower priority group that resource
++ * belongs to are committable
++ * 2. lower priority client hasn't committed above group resources
++ * Note: the notified client is unnessarily the appropriated client.
++ */
++ int index;
++
++ DPRINTK("client <%d> give up resource <%d>\n",
++ (unsigned int)resource->access->id, (unsigned int)resource->id);
++ unload_isr(resource->access);
++ owner = resource->access;
++ resource->access = NULL;
++ resource->priority[owner->priority].active = STATE_UNDEF;
++ for_each_lower_priority(index, owner->priority) {
++ state = &(resource->priority[index]);
++ DPRINTK(" its state of lower priority <%d> is <%d>\n",
++ index, state->active);
++ if (state->active == STATE_APPROPRIATED) {
++ DPRINTK("client <%d> return resource <%d>"
++ " to client <%d>\n", owner->id,
++ resource->id, state->allocate->id);
++ group = state->group;
++ group->appropriated_cnt--;
++ DPRINTK("resource group <%d> of client <%d>"
++ " has <%d> resources appropriated\n",
++ group->id, state->allocate->id,
++ group->appropriated_cnt);
++ }
++ if (state->group &&
++ state->group->appropriated_cnt == 0 &&
++ state->allocate &&
++ !group_commited(state->allocate, state->group)) {
++ ret = try_to_access_group(state->allocate,
++ state->group, 1);
++ if (ret < 0)
++ return ret;
++ else if (ret == 0) {
++ /* state->active = STATE_UNDEF;
++ */
++ /* ISR will not reload, because now the
++ * group has not be commited again.
++ * The isr should be loaded when commit
++ */
++ if (state->allocate->notify) {
++ up_write(&prm_sem);
++ DPRINTK("client <%d> notified"
++ " with PRM_RES_READY\n",
++ (unsigned int)state->allocate->id);
++ state->allocate->notify(PRM_RES_READY,
++ state->group->id,
++ state->allocate->client_data);
++ down_write(&prm_sem);
++ }
++ break;
++ }
++ }
++ }
++ }
++ else {
++ struct prm_resource *group_resource;
++ struct list_head *pos;
++
++ owner = resource->access;
++
++ if (owner == client){
++ DPRINTK("client <%d> commits resource <%d>:"
++ " already commited\n",
++ (unsigned int)client->id,
++ (unsigned int)resource->id);
++ return 0;
++ }
++ if (!owner)
++ unload_isr(owner);
++ resource->access = client;
++ resource->priority[client->priority].active = STATE_ACTIVE;
++ load_isr(client);
++ if (owner == NULL) {
++ DPRINTK("client <%d> commits resource <%d>:"
++ " from free list\n",
++ (unsigned int)client->id,
++ (unsigned int)resource->id);
++ return 0;
++ } else {
++ DPRINTK("client <%d> commits resource <%d>:"
++ " from client <%d>\n\n",
++ (unsigned int)client->id,
++ (unsigned int)resource->id, owner->id);
++ }
++
++ owner_state = &(resource->priority[owner->priority]);
++ owner_state->active = STATE_APPROPRIATED;
++ owner_group = owner_state->group;
++ if (owner_group->appropriated_cnt++ == 0) {
++ list_for_each(pos, &(owner_group->resources)) {
++ state = list_entry(pos,
++ struct prm_resource_state, entry);
++ group_resource = state->resource;
++ if (group_resource->access == owner)
++ ret = set_resource_access(
++ group_resource, NULL);
++ if (ret)
++ return ret;
++ }
++ if (owner->notify) {
++ up_write(&prm_sem);
++ DPRINTK("client <%d> notified with"
++ " PRM_RES_APPROPRIATED\n",
++ (unsigned int)owner->id);
++ owner->notify(PRM_RES_APPROPRIATED,
++ owner_group->id, owner->client_data);
++ down_write(&prm_sem);
++ }
++ }
++ }
++ return 0;
++}
++
++int prm_open_session(prm_priority priority, char *name,
++ clientcallback callback, void *data)
++{
++ struct prm_client * client;
++ unsigned int name_len;
++ int i = 0;
++
++ ASSERT_PRIORITY(priority);
++ if (!name) {
++ return -EINVAL;
++ }
++ /* protect for read */
++ down_read(&prm_sem);
++ for (i = 0;i < MAX_CLIENTS;i++) {
++ if (prm_clients[i] == NULL)
++ break;
++ }
++ up_read(&prm_sem);
++
++ if (i == MAX_CLIENTS)
++ return -ENOENT;
++
++ name_len = strlen(name);
++ client = (struct prm_client *)
++ kmalloc(sizeof(struct prm_client) + name_len + 1, GFP_KERNEL);
++ if (!client)
++ return -ENOMEM;
++ memset(client, 0x0, sizeof(struct prm_client));
++ client->id = i;
++ client->pid = current->pid;
++ client->priority = priority;
++ client->notify = callback;
++ client->client_data = data;
++ client->name = (char *)(client + 1);
++ strncpy(client->name, name, name_len);
++ client->name[name_len] = '\0';
++
++ for(i = 0;i < MAX_GROUPS;i++)
++ client->groups[i] = NULL;
++
++ down_write(&prm_sem);
++ if (prm_clients[client->id] != NULL) {
++ up_write(&prm_sem);
++ kfree(client);
++ return -ENOENT;
++ }
++ prm_clients[client->id] = client;
++ up_write(&prm_sem);
++ proc_add_client(client);
++
++ DPRINTK("client<%d>(%s) open a session with priority <%d>\n",
++ client->id, name, priority);
++
++ return client->id;
++}
++
++int prm_close_session(unsigned int client_id)
++{
++ struct prm_client *client;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_write(&prm_sem);
++ client = prm_clients[client_id];
++ /* resources should be freed before close seesion */
++ if (client->group_cnt) {
++ up_write(&prm_sem);
++ return -EPERM;
++ }
++ prm_clients[client_id] = NULL;
++ up_write(&prm_sem);
++ proc_del_client(client);
++ kfree(client);
++
++ DPRINTK("client<%d> closed its session\n", client_id);
++
++ return 0;
++}
++
++/* allocate resource, but can not access it now */
++int prm_allocate_resource(unsigned int client_id,
++ prm_resource_id res_id, unsigned int group_id)
++{
++ struct prm_client *client;
++ struct prm_resource *resource;
++ struct prm_resource_state *state;
++ struct prm_group *group;
++
++ ASSERT_CLIENT_ID(client_id);
++ ASSERT_RESOURCE_ID(res_id);
++ ASSERT_GROUP_ID(group_id);
++
++ DPRINTK("allocate resource for client <%d> with resource <%d>"
++ " for group <%d>\n", client_id, res_id, group_id);
++ down_write(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_write(&prm_sem);
++ return -EINVAL;
++ }
++ resource = &(prm_resources[res_id]);
++ state = &(resource->priority[client->priority]);
++
++ /* The resource in the client->priority has been reserved */
++ if (state->allocate) {
++ up_write(&prm_sem);
++ return -EPERM;
++ }
++ else
++ state->allocate = client;
++ group = client->groups[group_id];
++ up_write(&prm_sem);
++
++ if (group == NULL) {
++ group = (struct prm_group *)
++ kmalloc(sizeof(struct prm_group), GFP_KERNEL);
++ if (group == NULL)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&(group->resources));
++ group->id = group_id;
++ group->appropriated_cnt = 0;
++ group->member_cnt = 1;
++ proc_add_group(client, group, group_id);
++
++ down_write(&prm_sem);
++ if (client->groups[group_id]) {
++ up_write(&prm_sem);
++ kfree(group);
++ down_write(&prm_sem);
++ group = client->groups[group_id];
++ }
++ else {
++ client->groups[group_id] = group;
++ client->group_cnt++;
++ }
++ }
++ else {
++ down_write(&prm_sem);
++ client->groups[group_id]->member_cnt++;
++ }
++ list_add(&(state->entry), &(group->resources));
++ state->group = group;
++ state->active = STATE_UNDEF;
++ up_write(&prm_sem);
++ proc_allocate_resource(state);
++
++ return 0;
++}
++
++int prm_free_resources(unsigned int client_id, unsigned int group_id)
++{
++ struct prm_client *client;
++ struct prm_resource *resource;
++ struct prm_group *group;
++ struct prm_resource_state *state;
++ int ret = -EINVAL;
++ struct list_head *pos, *n;
++
++ ASSERT_CLIENT_ID(client_id);
++ ASSERT_GROUP_ID(group_id);
++
++ down_write(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_write(&prm_sem);
++ return -EINVAL;
++ }
++ group = client->groups[group_id];
++ if (!group) {
++ up_write(&prm_sem);
++ return -EINVAL;
++ }
++
++ list_for_each_safe(pos, n, &(group->resources)) {
++ state = list_entry(pos, struct prm_resource_state, entry);
++ resource = state->resource;
++ if (get_resource_access(resource) == client) {
++ ret = set_resource_access(resource, NULL);
++ if (ret) {
++ up_write(&prm_sem);
++ return ret;
++ }
++ }
++#if 0
++ else if (state->active == STATE_APPROPRIATED)
++ group->appropriated_cnt--;
++#endif
++ proc_free_resource(state);
++ list_del(pos);
++ clear_state(state);
++ }
++ client->group_cnt--;
++ client->groups[group_id] = NULL;
++ up_write(&prm_sem);
++ proc_del_group(client, group, group_id);
++ kfree(group);
++
++ return 0;
++}
++
++int prm_commit_resources(unsigned int client_id, unsigned int group_id)
++{
++ struct prm_client *client;
++ struct prm_group *group;
++ struct prm_resource_state *state;
++ struct prm_resource *resource;
++ struct list_head *pos;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++ ASSERT_GROUP_ID(group_id);
++
++ DPRINTK("client <%d> commit resource group <%d>\n",
++ client_id, group_id);
++ down_write(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_write(&prm_sem);
++ return -EINVAL;
++ }
++ group = client->groups[group_id];
++ if (!group) {
++ up_write(&prm_sem);
++ return -EINVAL;
++ }
++
++ ret = try_to_access_group(client, group, 0);
++ if (ret) {
++ up_write(&prm_sem);
++ return ret;
++ }
++
++ list_for_each(pos, &(group->resources)) {
++ state = list_entry(pos, struct prm_resource_state, entry);
++ resource = state->resource;
++ ret = set_resource_access(resource, client);
++ if (ret) {
++ up_write(&prm_sem);
++ return ret;
++ }
++ proc_commit_resource(resource);
++ }
++ up_write(&prm_sem);
++ return 0;
++}
++
++int prm_get_cpuid(void)
++{
++ int cpu_id;
++
++ asm("mrc p15, 0, %0, c0, c0" : "=r" (cpu_id));
++ cpu_id &= 0xfffff000;
++
++ return cpu_id;
++}
++
++static irqreturn_t prm_pmu_handler(int irq, void *dev_id)
++{
++ /*DPRINTK("PMU interrupt generated!\n");
++ */
++ if (prm_pmu_client)
++ prm_pmu_client->handler(irq, prm_pmu_client->dev_id);
++ return IRQ_HANDLED;
++}
++
++EXPORT_SYMBOL(prm_open_session);
++EXPORT_SYMBOL(prm_close_session);
++EXPORT_SYMBOL(prm_allocate_resource);
++EXPORT_SYMBOL(prm_free_resources);
++EXPORT_SYMBOL(prm_commit_resources);
++EXPORT_SYMBOL(prm_get_cpuid);
++
++/*****************************************************************************/
++/* */
++/* PMU API */
++/* */
++/*****************************************************************************/
++
++int pmu_read_register(unsigned int client_id, int reg, unsigned int *pval)
++{
++ struct prm_resource *resource;
++ struct prm_client *client;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ resource = IS_PRM_RESOURCE(reg)? &(prm_resources[PMU_PRM(reg)]):NULL;
++ ret = (get_resource_access(resource) == client);
++ up_read(&prm_sem);
++
++ if (ret)
++ *pval = pmu_read_reg(reg);
++ else
++ return -EACCES;
++
++ return 0;
++}
++
++int pmu_write_register(unsigned int client_id, int reg, unsigned int val)
++{
++ struct prm_resource *resource;
++ struct prm_client *client;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ resource = IS_PRM_RESOURCE(reg)? &(prm_resources[PMU_PRM(reg)]):NULL;
++ ret = (get_resource_access(resource) == client);
++ up_read(&prm_sem);
++
++ if (ret)
++ pmu_write_reg(reg, val);
++ else
++ return -EACCES;
++
++ return 0;
++}
++
++int pmu_set_event(unsigned int client_id, unsigned int counter,
++ int *pre_type, int type)
++{
++ struct prm_client *client;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ ret = (client == get_resource_access(NULL)) ;
++ up_read(&prm_sem);
++
++ if (ret) {
++ *pre_type = pmu_select_event(counter, type);
++ if (*pre_type == PMU_EVENT_INVALIDATE)
++ return -EINVAL;
++ }
++ else
++ return -EACCES;
++ return 0;
++}
++
++int pmu_enable_event_counting(unsigned int client_id)
++{
++ struct prm_client *client;
++ unsigned long val;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ ret = (client == get_resource_access(NULL));
++ up_read(&prm_sem);
++
++ if (ret) {
++ /* enable and reset all counters,
++ * CCNT counts every clock cycle
++ */
++ val = 0x07;
++ pmu_write_reg(PMU_PMNC, val);
++ }
++ else
++ return -EACCES;
++ return 0;
++}
++
++int pmu_disable_event_counting(unsigned int client_id)
++{
++ struct prm_client *client;
++ unsigned long val;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ ret = (client == get_resource_access(NULL));
++ up_read(&prm_sem);
++
++ if (ret) {
++ /* disable all counters */
++ val = 0x10;
++ pmu_write_reg(PMU_PMNC, val);
++ }
++ else
++ return -EACCES;
++ return 0;
++}
++
++int pmu_enable_event_interrupt(unsigned int client_id, int reg)
++{
++ struct prm_client *client;
++ unsigned long val;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ if (IS_PRM_RESOURCE(reg)) {
++ ret = (get_resource_access(&(prm_resources[PMU_PRM(reg)])) == client);
++ up_read(&prm_sem);
++ if (ret) {
++ val = pmu_read_reg(PMU_INTEN);
++ val |= (0x1 << reg);
++ pmu_write_reg(PMU_INTEN, val);
++ }
++ else
++ return -EACCES;
++ }
++ else {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++int pmu_disable_event_interrupt(unsigned int client_id, int reg)
++{
++ struct prm_client *client;
++ unsigned long val;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ if (IS_PRM_RESOURCE(reg)) {
++ ret = (get_resource_access(&(prm_resources[PMU_PRM(reg)])) == client);
++ up_read(&prm_sem);
++ if (ret) {
++ val = pmu_read_reg(PMU_INTEN);
++ val &= ~(0x1 << reg);
++ pmu_write_reg(PMU_INTEN, val);
++ }
++ else
++ return -EACCES;
++ }
++ else {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++int pmu_register_isr(unsigned int client_id,
++ irq_handler_t handler, void *dev_id)
++{
++ struct prm_client *client;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ client->handler = handler;
++ client->dev_id = dev_id;
++ load_isr(client);
++ up_read(&prm_sem);
++ return 0;
++}
++
++int pmu_unregister_isr(unsigned int client_id)
++{
++ struct prm_client *client;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ unload_isr(client);
++ client->handler = NULL;
++ client->dev_id = NULL;
++ up_read(&prm_sem);
++ return 0;
++}
++
++EXPORT_SYMBOL(pmu_read_reg);
++EXPORT_SYMBOL(pmu_write_reg);
++EXPORT_SYMBOL(pmu_read_register);
++EXPORT_SYMBOL(pmu_write_register);
++EXPORT_SYMBOL(pmu_set_event);
++EXPORT_SYMBOL(pmu_enable_event_counting);
++EXPORT_SYMBOL(pmu_disable_event_counting);
++EXPORT_SYMBOL(pmu_enable_event_interrupt);
++EXPORT_SYMBOL(pmu_disable_event_interrupt);
++EXPORT_SYMBOL(pmu_register_isr);
++EXPORT_SYMBOL(pmu_unregister_isr);
++
++/*****************************************************************************/
++/* */
++/* COP API */
++/* */
++/*****************************************************************************/
++
++int cop_get_num_of_cops(void)
++{
++ return dvfm_op_count();
++}
++
++int cop_get_cop(unsigned int client_id, unsigned int n,
++ struct pxa3xx_fv_info *param)
++{
++ struct op_info *info = NULL;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ ret = dvfm_get_opinfo(n, &info);
++ if (ret == 0) {
++ md2fvinfo(param, (struct dvfm_md_opt *)info->op);
++ }
++ return ret;
++}
++
++int cop_set_cop(unsigned int client_id, unsigned int n, int mode)
++{
++ struct prm_resource *resource;
++ struct prm_client *client;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ resource = &prm_resources[PRM_COP];
++ ret = (get_resource_access(resource) == client);
++ up_read(&prm_sem);
++
++ if (ret)
++ return dvfm_request_op(n);
++ return -EACCES;
++}
++
++int cop_get_def_cop(unsigned int client_id, unsigned int *n,
++ struct pxa3xx_fv_info *param)
++{
++ struct op_info *info = NULL;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ *n = dvfm_get_defop();
++ ret = dvfm_get_opinfo(*n, &info);
++ if (ret == 0) {
++ md2fvinfo(param, (struct dvfm_md_opt *)info->op);
++ }
++ return ret;
++}
++
++int cop_set_def_cop(unsigned int client_id)
++{
++ struct prm_resource *resource;
++ struct prm_client *client;
++ unsigned int def_op;
++ int ret;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ down_read(&prm_sem);
++ client = prm_clients[client_id];
++ if (!client) {
++ up_read(&prm_sem);
++ return -EINVAL;
++ }
++
++ resource = &prm_resources[PRM_COP];
++ ret = (get_resource_access(resource) == client);
++ up_read(&prm_sem);
++
++ def_op = dvfm_get_defop();
++ if (ret)
++ return dvfm_request_op(def_op);
++ return -EACCES;
++}
++
++int cop_get_cur_cop(unsigned int client_id, unsigned int *n,
++ struct pxa3xx_fv_info *param)
++{
++ struct op_info *info = NULL;
++
++ ASSERT_CLIENT_ID(client_id);
++
++ *n = dvfm_get_op(&info);
++ md2fvinfo(param, (struct dvfm_md_opt *)info->op);
++
++ return 0;
++}
++
++EXPORT_SYMBOL(cop_get_num_of_cops);
++EXPORT_SYMBOL(cop_get_cop);
++EXPORT_SYMBOL(cop_set_cop);
++EXPORT_SYMBOL(cop_get_def_cop);
++EXPORT_SYMBOL(cop_set_def_cop);
++EXPORT_SYMBOL(cop_get_cur_cop);
++
++/*****************************************************************************/
++/* */
++/* Module Init/Exit */
++/* */
++/*****************************************************************************/
++
++static int __init prm_init(void)
++{
++ int ret, i , j;
++
++ proc_prm_init();
++ init_rwsem(&prm_sem);
++ /*prm_sem.debug = 1;
++ */
++ for (i = 0; i < RESOURCE_NUM; i++) {
++ prm_resources[i].access = NULL;
++ prm_resources[i].id = i;
++ proc_add_resource(&prm_resources[i]);
++ for (j = 0; j < MAX_PRIORITIES;j++) {
++ prm_resources[i].priority[j].resource = &prm_resources[i];
++ prm_resources[i].priority[j].allocate = NULL;
++ prm_resources[i].priority[j].active = STATE_UNDEF;
++ INIT_LIST_HEAD(&(prm_resources[i].priority[j].entry));
++ proc_add_resource_state(&prm_resources[i].priority[j], j);
++ }
++ }
++
++ for (i = 0; i < MAX_CLIENTS; i++) {
++ prm_clients[i] = NULL;
++ }
++ prm_pmu_client = NULL;
++
++ ret = request_irq(IRQ_PMU, prm_pmu_handler, 0, "PMU", NULL);
++ if (ret < 0) {
++ DPRINTK("PMU interrupt handler registeration: failed!\n");
++ return ret;
++ } else {
++ DPRINTK("PMU interrupt handler registeration: OK!\n");
++ }
++
++ DPRINTK("CPU_ID = 0x%08x\n", prm_get_cpuid());
++
++ return 0;
++}
++
++static void __exit prm_exit(void)
++{
++ int i, j;
++
++ for (i = 0; i < RESOURCE_NUM; i++) {
++ for(j = 0; j < MAX_PRIORITIES;j++) {
++ proc_del_resource_state(&prm_resources[i].priority[j], j);
++ }
++ proc_del_resource(&prm_resources[i]);
++ memset(&(prm_resources[i]), 0x0, sizeof(struct prm_resource));
++ }
++
++ for (i = 0; i < MAX_CLIENTS; i++) {
++ if (prm_clients[i]) {
++ if (prm_clients[i]->group_cnt) {
++ for (j = 0; j < MAX_GROUPS; j++) {
++ if (prm_clients[i]->groups[j]) {
++ proc_del_group(prm_clients[i],
++ prm_clients[i]->groups[j], j);
++ kfree(prm_clients[i]->groups[j]);
++ }
++ }
++ }
++ proc_del_client(prm_clients[i]);
++ kfree(prm_clients[i]);
++ }
++ }
++ prm_pmu_client = NULL;
++ free_irq(IRQ_PMU, NULL);
++ proc_prm_exit();
++}
++
++module_init(prm_init);
++module_exit(prm_exit);
++
++MODULE_DESCRIPTION("Performance Resources Management");
++MODULE_LICENSE("GPL");
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pxa3xx.c kernel/arch/arm/mach-pxa/pxa3xx.c
+--- linux-2.6.32/arch/arm/mach-pxa/pxa3xx.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/mach-pxa/pxa3xx.c 2009-12-12 16:09:26.482948915 +0200
+@@ -613,3 +613,4 @@
+ }
+
+ postcore_initcall(pxa3xx_init);
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pxa3xx_dvfm.c kernel/arch/arm/mach-pxa/pxa3xx_dvfm.c
+--- linux-2.6.32/arch/arm/mach-pxa/pxa3xx_dvfm.c 2009-12-13 13:00:35.598610849 +0200
++++ kernel/arch/arm/mach-pxa/pxa3xx_dvfm.c 2009-12-12 16:09:26.482948915 +0200
+@@ -0,0 +1,2319 @@
++/*
++ * PXA3xx DVFM Driver
++ *
++ * Copyright (C) 2007 Marvell Corporation
++ * Haojian Zhuang <haojian.zhuang@marvell.com>
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++
++ * (C) Copyright 2007 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#define DEBUG
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sysdev.h>
++#include <linux/miscdevice.h>
++#include <linux/fs.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++#include <linux/list.h>
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <asm/uaccess.h>
++//#include <asm/arch/pxa-regs.h>
++#include <mach/pxa3xx-regs.h>
++#include <mach/pxa3xx_pmic.h>
++#include <mach/hardware.h>
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_dvfm.h>
++#include <asm/io.h>
++//#include <asm/arch/pxa_ispt.h>
++//#include <asm/arch/mspm_prof.h>
++
++#include "devices.h"
++
++#ifdef CONFIG_CPU_PXA310
++#define FREQ_CORE(xl, xn) ((xl)*(xn)*13)
++
++#define FREQ_SRAM(sflfs) (((sflfs) == 0x0)?104: \
++ ((sflfs) == 0x1)?156: \
++ ((sflfs) == 0x2)?208:312)
++
++#define FREQ_STMM(smcfs) (((smcfs) == 0x0)?78: \
++ ((smcfs) == 0x2)?104: \
++ ((smcfs) == 0x5)?208:0)
++
++#define FREQ_DDR(dmcfs) (((dmcfs) == 0x0)?26: \
++ ((dmcfs) == 0x2)?208: \
++ ((dmcfs) == 0x3)?260:0)
++
++#define FREQ_HSS(hss) (((hss) == 0x0)?104: \
++ ((hss) == 0x1)?156: \
++ ((hss) == 0x2)?208:0)
++
++#define FREQ_DFCLK(smcfs, df_clkdiv) \
++ (((df_clkdiv) == 0x1)?FREQ_STMM((smcfs)): \
++ ((df_clkdiv) == 0x2)?FREQ_STMM((smcfs))/2: \
++ ((df_clkdiv) == 0x3)?FREQ_STMM((smcfs))/4:0)
++
++#define FREQ_EMPICLK(smcfs, empi_clkdiv) \
++ (((empi_clkdiv) == 0x1)?FREQ_STMM((smcfs)): \
++ ((empi_clkdiv) == 0x2)?FREQ_STMM((smcfs))/2: \
++ ((empi_clkdiv) == 0x3)?FREQ_STMM((smcfs))/4:0)
++
++#define LPJ_PER_MHZ 4988
++#endif
++
++/* Enter D2 before exiting D0CS */
++#define DVFM_LP_SAFE
++
++struct pxa3xx_dvfm_info {
++ /* flags */
++ uint32_t flags;
++
++ /* CPU ID */
++ uint32_t cpuid;
++
++ /* LCD clock */
++ struct clk *lcd_clk;
++
++ /* clock manager register base */
++ unsigned char __iomem *clkmgr_base;
++
++ /* service power management unit */
++ unsigned char __iomem *spmu_base;
++
++ /* slave power management unit */
++ unsigned char __iomem *bpmu_base;
++
++ /* dynamic memory controller register base */
++ unsigned char __iomem *dmc_base;
++
++ /* static memory controller register base */
++ unsigned char __iomem *smc_base;
++};
++
++#define MIN_SAFE_FREQUENCY 624
++
++struct info_head pxa3xx_dvfm_op_list = {
++ .list = LIST_HEAD_INIT(pxa3xx_dvfm_op_list.list),
++ .lock = RW_LOCK_UNLOCKED,
++};
++
++#ifdef CONFIG_PXA3xx_DVFM_STATS
++
++static unsigned int switch_lowpower_before, switch_lowpower_after;
++
++static int pxa3xx_stats_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data);
++static struct notifier_block notifier_freq_block = {
++ .notifier_call = pxa3xx_stats_notifier_freq,
++};
++#endif
++
++/* the operating point preferred by policy maker or user */
++static int preferred_op;
++static int current_op;
++
++extern unsigned int cur_op; /* current operating point */
++extern unsigned int def_op; /* default operating point */
++
++extern int enter_d0cs_a(volatile u32 *, volatile u32 *);
++extern int exit_d0cs_a(volatile u32 *, volatile u32 *);
++extern int md2fvinfo(struct pxa3xx_fv_info *, struct dvfm_md_opt *);
++extern void set_idle_op(int, int);
++
++#ifdef CONFIG_FB_PXA
++extern void pxafb_set_pcd(void);
++#else
++static void pxafb_set_pcd(void) {}
++#endif
++
++static int dvfm_dev_id;
++#define LPJ_D0CS (293888 * 100 / HZ)
++#define LPJ_104M (517120 * 100 / HZ)
++#define LPJ_156M (778128 * 100 / HZ)
++#define LPJ_208M (1036288 * 100 / HZ)
++#define LPJ_416M (2076672 * 100 / HZ)
++#define LPJ_624M (3112960 * 100 / HZ)
++#define LPJ_806M (4020906 * 100 / HZ)
++
++static int d0cs_lpj = LPJ_D0CS;
++
++static int boot_core_freq = 0;
++
++int out_d0cs = 0;
++
++/* define the operating point of S0D0 and S0D0CS mode */
++static struct dvfm_md_opt pxa300_op_array[] = {
++ /* 60MHz -- ring oscillator */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 0,
++ .xn = 0,
++ .smcfs = 15,
++ .sflfs = 60,
++ .hss = 60,
++ .dmcfs = 30, /* will be 60MHZ for PXA310 A2 and PXA935/PXA940 */
++ .df_clk = 15,
++ .empi_clk = 15,
++ .power_mode = POWER_MODE_D0CS,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 293888*100/HZ,
++ .name = "D0CS",
++ },
++ /* 104MHz */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 8,
++ .xn = 1,
++ .smcfs = 78,
++ .sflfs = 104,
++ .hss = 104,
++ .dmcfs = 260,
++ /* Actually it's 19.5, not 19 */
++ .df_clk = 19,
++ .empi_clk = 19,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 517120*100/HZ,
++ .name = "104M",
++ },
++ /* 208MHz */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 16,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 1036288*100/HZ,
++ .name = "208M",
++ },
++ /* 416MHz */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .xl = 16,
++ .xn = 2,
++ .smcfs = 104,
++ .sflfs = 208,
++ .hss = 156,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 2076672*100/HZ,
++ .name = "416M",
++ },
++ /* 624MHz */
++ {
++ .vcc_core = 1375,
++ .vcc_sram = 1400,
++ .xl = 24,
++ .xn = 2,
++ .smcfs = 208,
++ .sflfs = 312,
++ .hss = 208,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 3112960*100/HZ,
++ .name = "624M",
++ },
++ /* D1 mode */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .power_mode = POWER_MODE_D1,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D1",
++ },
++ /* D2 mode */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .power_mode = POWER_MODE_D2,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D2",
++ },
++};
++
++static struct dvfm_md_opt pxa320_op_array[] = {
++ /* 60MHz -- ring oscillator */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 0,
++ .xn = 0,
++ .smcfs = 15,
++ .sflfs = 60,
++ .hss = 60,
++ .dmcfs = 30,
++ .df_clk = 15,
++ .empi_clk = 15,
++ .power_mode = POWER_MODE_D0CS,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 293888*100/HZ,
++ .name = "D0CS",
++ },
++ /* 104MHz */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 8,
++ .xn = 1,
++ .smcfs = 78,
++ .sflfs = 104,
++ .hss = 104,
++ .dmcfs = 260,
++ /* Actually it's 19.5, not 19 */
++ .df_clk = 19,
++ .empi_clk = 19,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 517120*100/HZ,
++ .name = "104M",
++ },
++ /* 208MHz */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 16,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 1036288*100/HZ,
++ .name = "208M",
++ },
++ /* 416MHz */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .xl = 16,
++ .xn = 2,
++ .smcfs = 104,
++ .sflfs = 208,
++ .hss = 156,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 2076672*100/HZ,
++ .name = "416M",
++ },
++ /* 624MHz */
++ {
++ .vcc_core = 1375,
++ .vcc_sram = 1400,
++ .xl = 24,
++ .xn = 2,
++ .smcfs = 208,
++ .sflfs = 312,
++ .hss = 208,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 3112960*100/HZ,
++ .name = "624M",
++ },
++ /* 806MHz */
++ {
++ .vcc_core = 1400,
++ .vcc_sram = 1400,
++ .xl = 31,
++ .xn = 2,
++ .smcfs = 208,
++ .sflfs = 312,
++ .hss = 208,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 4020906*100/HZ,
++ .name = "806M",
++ },
++#if 0
++ /* D1 mode */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .power_mode = POWER_MODE_D1,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D1",
++ },
++#endif
++ /* D2 mode */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .power_mode = POWER_MODE_D2,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D2",
++ },
++};
++
++static struct dvfm_md_opt pxa930_op_array[] = {
++ /* 60MHz -- ring oscillator */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 0,
++ .xn = 0,
++ .smcfs = 15,
++ .sflfs = 60,
++ .hss = 60,
++ .dmcfs = 30,
++ .df_clk = 15,
++ .empi_clk = 15,
++ .power_mode = POWER_MODE_D0CS,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 293888*100/HZ,
++ .name = "D0CS",
++ },
++ /* 156MHz -- single PLL mode */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 12,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 208,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 778128*100/HZ,
++ .name = "156M",
++ },
++ /* 208MHz */
++ {
++ .vcc_core = 1000,
++ .vcc_sram = 1100,
++ .xl = 16,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 1036288*100/HZ,
++ .name = "208M",
++ },
++ /* 416MHz */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .xl = 16,
++ .xn = 2,
++ .smcfs = 104,
++ .sflfs = 208,
++ .hss = 156,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 2076672*100/HZ,
++ .name = "416M",
++ },
++ /* 624MHz */
++ {
++ .vcc_core = 1375,
++ .vcc_sram = 1400,
++ .xl = 24,
++ .xn = 2,
++ .smcfs = 208,
++ .sflfs = 312,
++ .hss = 208,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 3112960*100/HZ,
++ .name = "624M",
++ },
++ /* D2 mode */
++ {
++ .vcc_core = 1100,
++ .vcc_sram = 1200,
++ .power_mode = POWER_MODE_D2,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D2",
++ },
++};
++
++static struct dvfm_md_opt pxa935_op_array[] = {
++ /* 60MHz -- ring oscillator */
++ {
++ .vcc_core = 1250,
++ .xl = 0,
++ .xn = 0,
++ .smcfs = 15,
++ .sflfs = 60,
++ .hss = 60,
++ .dmcfs = 30,
++ .df_clk = 15,
++ .empi_clk = 15,
++ .power_mode = POWER_MODE_D0CS,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 293888*100/HZ,
++ .name = "D0CS",
++ },
++ /* 156MHz -- single PLL mode */
++ {
++ .vcc_core = 1250,
++ .xl = 12,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 208,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 778128*100/HZ,
++ .name = "156M",
++ },
++ /* 208MHz */
++ {
++ .vcc_core = 1250,
++ .xl = 16,
++ .xn = 1,
++ .smcfs = 104,
++ .sflfs = 156,
++ .hss = 104,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 1036288*100/HZ,
++ .name = "208M",
++ },
++ /* 416MHz */
++ {
++ .vcc_core = 1250,
++ .xl = 16,
++ .xn = 2,
++ .smcfs = 104,
++ .sflfs = 208,
++ .hss = 156,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 2076672*100/HZ,
++ .name = "416M",
++ },
++ /* 624MHz */
++ {
++ .vcc_core = 1250,
++ .xl = 24,
++ .xn = 2,
++ .smcfs = 208,
++ .sflfs = 312,
++ .hss = 208,
++ .dmcfs = 260,
++ .df_clk = 52,
++ .empi_clk = 52,
++ .power_mode = POWER_MODE_D0,
++ .flag = OP_FLAG_FACTORY,
++ .lpj = 3112960*100/HZ,
++ .name = "624M",
++ },
++#if 0
++ /* D1 mode */
++ {
++ .vcc_core = 1250,
++ .power_mode = POWER_MODE_D1,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D1",
++ },
++#endif
++ /* D2 mode */
++ {
++ .vcc_core = 1250,
++ .power_mode = POWER_MODE_D2,
++ .flag = OP_FLAG_FACTORY,
++ .name = "D2",
++ },
++ /* CG (clock gated) mode */
++ {
++ .vcc_core = 1250,
++ .power_mode = POWER_MODE_CG,
++ .flag = OP_FLAG_FACTORY,
++ .name = "CG",
++ },
++
++};
++
++struct proc_op_array {
++ unsigned int cpuid;
++ char *cpu_name;
++ struct dvfm_md_opt *op_array;
++ unsigned int nr_op;
++};
++
++#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
++static struct proc_op_array proc_op_arrays[] = {
++ {0x6880, "PXA300", ARRAY_AND_SIZE(pxa300_op_array)},
++ {0x6890, "PXA310", ARRAY_AND_SIZE(pxa300_op_array)},
++ {0x6820, "PXA320", ARRAY_AND_SIZE(pxa320_op_array)},
++ {0x6830, "PXA930", ARRAY_AND_SIZE(pxa930_op_array)},
++ {0x6930, "PXA935/PXA940", ARRAY_AND_SIZE(pxa935_op_array)},
++};
++
++extern void pxa_clkcfg_write(unsigned int);
++
++static int prepare_dmc(void *driver_data, int flag);
++static int polling_dmc(void *driver_data);
++
++#ifdef CONFIG_ISPT
++static int ispt_dvfm_op(int old, int new)
++{
++ return ispt_dvfm_msg(old, new);
++}
++
++static int ispt_block_dvfm(int enable, int dev_id)
++{
++ int ret;
++ if (enable)
++ ret = ispt_driver_msg(CT_P_DVFM_BLOCK_REQ, dev_id);
++ else
++ ret = ispt_driver_msg(CT_P_DVFM_BLOCK_REL, dev_id);
++ return ret;
++}
++
++static int ispt_power_state_d2(void)
++{
++ return ispt_power_msg(CT_P_PWR_STATE_ENTRY_D2);
++}
++#else
++static int ispt_dvfm_op(int old, int new) { return 0; }
++static int ispt_block_dvfm(int enable, int dev_id) { return 0; }
++static int ispt_power_state_d2(void) { return 0; }
++#endif
++
++unsigned int pxa3xx_clk_to_lpj(unsigned int clk)
++{
++ if (clk == 624000000)
++ return LPJ_624M;
++ if (clk == 416000000)
++ return LPJ_416M;
++ if (clk == 208000000)
++ return LPJ_208M;
++ if (clk == 156000000)
++ return LPJ_156M;
++ if (clk == 104000000)
++ return LPJ_104M;
++ if (clk == 60000000)
++ return LPJ_D0CS;
++
++ printk(KERN_CRIT "%s does not support clk (%d MHz)\n",
++ __FILE__, clk/1000000);
++
++ return 0;
++}
++
++/* #####################Debug Function######################## */
++static int dump_op(void *driver_data, struct op_info *p, char *buf)
++{
++ int len, count, x;
++ struct dvfm_md_opt *q = (struct dvfm_md_opt *)p->op;
++
++ if (q == NULL)
++ len = sprintf(buf, "Can't dump the op info\n");
++ else {
++ /* calculate how much bits is set in device word */
++ x = p->device;
++ for (count = 0; x; x = x & (x - 1), count++);
++ len = sprintf(buf, "OP:%d name:%s [%s, %d]\n",
++ p->index, q->name, (count)?"Disabled"
++ :"Enabled", count);
++ len += sprintf(buf + len, "vcore:%d vsram:%d xl:%d xn:%d "
++ "smcfs:%d sflfs:%d hss:%d dmcfs:%d df_clk:%d "
++ "power_mode:%d flag:%d\n",
++ q->vcc_core, q->vcc_sram, q->xl, q->xn,
++ q->smcfs, q->sflfs, q->hss, q->dmcfs,
++ q->df_clk, q->power_mode, q->flag);
++ }
++ return len;
++}
++
++static int dump_op_list(void *driver_data, struct info_head *op_table, int flag)
++{
++ struct op_info *p = NULL;
++ struct dvfm_md_opt *q = NULL;
++ struct list_head *list = NULL;
++ struct pxa3xx_dvfm_info *info = driver_data;
++ char buf[256];
++
++ if (!op_table || list_empty(&op_table->list)) {
++ printk(KERN_WARNING "op list is null\n");
++ return -EINVAL;
++ }
++ memset(buf, 0, 256);
++ list_for_each(list, &op_table->list) {
++ p = list_entry(list, struct op_info, list);
++ q = (struct dvfm_md_opt *)p->op;
++ if (q->flag <= flag) {
++ dump_op(info, p, buf);
++ pr_debug("%s", buf);
++ }
++ }
++ return 0;
++}
++
++/* ########################################################## */
++static int freq2reg(struct pxa3xx_fv_info *fv_info, struct dvfm_md_opt *orig)
++{
++ int res = -EFAULT, tmp;
++
++ if (orig && fv_info) {
++ fv_info->vcc_core = orig->vcc_core;
++ fv_info->vcc_sram = orig->vcc_sram;
++ if (orig->power_mode == POWER_MODE_D0) {
++ res = 0;
++ fv_info->xl = orig->xl;
++ fv_info->xn = orig->xn;
++ fv_info->d0cs = 0;
++ if (orig->smcfs == 78)
++ fv_info->smcfs = 0;
++ else if (orig->smcfs == 104)
++ fv_info->smcfs = 2;
++ else if (orig->smcfs == 208)
++ fv_info->smcfs = 5;
++ else
++ res = -EINVAL;
++ if (orig->sflfs == 104)
++ fv_info->sflfs = 0;
++ else if (orig->sflfs == 156)
++ fv_info->sflfs = 1;
++ else if (orig->sflfs == 208)
++ fv_info->sflfs = 2;
++ else if (orig->sflfs == 312)
++ fv_info->sflfs = 3;
++ else
++ res = -EINVAL;
++ if (orig->hss == 104)
++ fv_info->hss = 0;
++ else if (orig->hss == 156)
++ fv_info->hss = 1;
++ else if (orig->hss == 208)
++ fv_info->hss = 2;
++ else
++ res = -EINVAL;
++ if (orig->dmcfs == 26)
++ fv_info->dmcfs = 0;
++ else if (orig->dmcfs == 208)
++ fv_info->dmcfs = 2;
++ else if (orig->dmcfs == 260)
++ fv_info->dmcfs = 3;
++ else
++ res = -EINVAL;
++ tmp = orig->smcfs / orig->df_clk;
++ if (tmp == 2)
++ fv_info->df_clk = 2;
++ else if (tmp == 4)
++ fv_info->df_clk = 3;
++ fv_info->empi_clk = fv_info->df_clk;
++ } else if (orig->power_mode == POWER_MODE_D0CS) {
++ fv_info->d0cs = 1;
++ res = 0;
++ }
++ }
++ return res;
++}
++
++int md2fvinfo(struct pxa3xx_fv_info *fv_info, struct dvfm_md_opt *orig)
++{
++ return freq2reg(fv_info, orig);
++}
++
++static int reg2freq(void *driver_data, struct dvfm_md_opt *fv_info)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ int res = -EFAULT, tmp;
++ uint32_t accr;
++
++ if (fv_info) {
++ res = 0;
++ if (fv_info->power_mode == POWER_MODE_D0CS) {
++ /* set S0D0CS operating pointer */
++ fv_info->power_mode = POWER_MODE_D0CS;
++ fv_info->xl = 0;
++ fv_info->xn = 0;
++ fv_info->smcfs = 15;
++ fv_info->sflfs = 60;
++ fv_info->hss = 60;
++ /* PXA310 A2 or PXA935/PXA940 */
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ if (accr & 0x80)
++ fv_info->dmcfs = 60;
++ else
++ fv_info->dmcfs = 30;
++ fv_info->df_clk = 15;
++ fv_info->empi_clk = 15;
++ } else {
++ /* set S0D0 operating pointer */
++ fv_info->power_mode = POWER_MODE_D0;
++ tmp = fv_info->smcfs;
++ if (tmp == 0)
++ fv_info->smcfs = 78;
++ else if (tmp == 2)
++ fv_info->smcfs = 104;
++ else if (tmp == 5)
++ fv_info->smcfs = 208;
++ else
++ res = -EINVAL;
++ tmp = fv_info->sflfs;
++ if (tmp == 0)
++ fv_info->sflfs = 104;
++ else if (tmp == 1)
++ fv_info->sflfs = 156;
++ else if (tmp == 2)
++ fv_info->sflfs = 208;
++ else if (tmp == 3)
++ fv_info->sflfs = 312;
++ tmp = fv_info->hss;
++ if (tmp == 0)
++ fv_info->hss = 104;
++ else if (tmp == 1)
++ fv_info->hss = 156;
++ else if (tmp == 2)
++ fv_info->hss = 208;
++ else
++ res = -EINVAL;
++ tmp = fv_info->dmcfs;
++ if (tmp == 0)
++ fv_info->dmcfs = 26;
++ else if (tmp == 2)
++ fv_info->dmcfs = 208;
++ else if (tmp == 3)
++ fv_info->dmcfs = 260;
++ else
++ res = -EINVAL;
++ tmp = fv_info->df_clk;
++ if (tmp == 1)
++ fv_info->df_clk = fv_info->smcfs;
++ else if (tmp == 2)
++ fv_info->df_clk = fv_info->smcfs / 2;
++ else if (tmp == 3)
++ fv_info->df_clk = fv_info->smcfs / 4;
++ fv_info->empi_clk = fv_info->df_clk;
++ }
++ }
++ return res;
++}
++
++/* Get current setting, and record it in fv_info structure
++ */
++static int capture_op_info(void *driver_data, struct dvfm_md_opt *fv_info)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ int res = -EFAULT;
++ uint32_t acsr, memclkcfg;
++
++ if (fv_info) {
++ memset(fv_info, 0, sizeof(struct dvfm_md_opt));
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ fv_info->xl = (acsr >> ACCR_XL_OFFSET) & 0x1F;
++ fv_info->xn = (acsr >> ACCR_XN_OFFSET) & 0x07;
++ fv_info->smcfs = (acsr >> ACCR_SMCFS_OFFSET) & 0x07;
++ fv_info->sflfs = (acsr >> ACCR_SFLFS_OFFSET) & 0x03;
++ fv_info->hss = (acsr >> ACCR_HSS_OFFSET) & 0x03;
++ fv_info->dmcfs = (acsr >> ACCR_DMCFS_OFFSET) & 0x03;
++ fv_info->power_mode = (acsr >> ACCR_D0CS_OFFSET) & 0x01;
++ memclkcfg = __raw_readl(info->smc_base + MEMCLKCFG_OFF);
++ fv_info->df_clk = (memclkcfg >> MEMCLKCFG_DF_OFFSET) & 0x07;
++ fv_info->empi_clk = (memclkcfg >> MEMCLKCFG_EMPI_OFFSET) & 0x07;
++ res = reg2freq(info, fv_info);
++ pxa3xx_pmic_get_voltage(VCC_CORE, &fv_info->vcc_core);
++ if ((info->cpuid & 0xFFF0) == 0x6930) {
++ /* PXA935/PXA940 doesn't have VCC_SRAM */
++ fv_info->vcc_sram = 0;
++ } else {
++ pxa3xx_pmic_get_voltage(VCC_SRAM, &fv_info->vcc_sram);
++ }
++ /* TODO: mix up the usage of struct dvfm_md_opt and struct pxa3xx_fv_info
++ * better to define reg2freq(struct dvfm_md_opt *md_info,
++ * struct pxa3xx_fv_info *fv_info)
++ */
++ }
++ return res;
++}
++
++/* return all op including user defined op, and boot op */
++static int get_op_num(void *driver_data, struct info_head *op_table)
++{
++ struct list_head *entry = NULL;
++ int num = 0;
++
++ if (!op_table)
++ goto out;
++ read_lock(&op_table->lock);
++ if (list_empty(&op_table->list)) {
++ read_unlock(&op_table->lock);
++ goto out;
++ }
++ list_for_each(entry, &op_table->list) {
++ num++;
++ }
++ read_unlock(&op_table->lock);
++out:
++ return num;
++}
++
++/* return op name. */
++static char *get_op_name(void *driver_data, struct op_info *p)
++{
++ struct dvfm_md_opt *q = NULL;
++ if (p == NULL)
++ return NULL;
++ q = (struct dvfm_md_opt *)p->op;
++ return q->name;
++}
++
++static int update_voltage(void *driver_data, struct dvfm_md_opt *old, struct dvfm_md_opt *new)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++
++ if (!(info->flags & PXA3xx_USE_POWER_I2C)) {
++ pxa3xx_pmic_set_voltage(VCC_CORE, new->vcc_core);
++ pxa3xx_pmic_set_voltage(VCC_SRAM, new->vcc_sram);
++ }
++ return 0;
++}
++
++static void pxa3xx_enter_d0cs(void *driver_data)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++
++ unsigned int reg, spll = 0;
++ uint32_t accr, mdrefr;
++
++ reg = (12 << ACCR_XL_OFFSET) | (1 << ACCR_XN_OFFSET);
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ if (reg == (accr & (ACCR_XN_MASK | ACCR_XL_MASK))) {
++ spll = 1;
++ }
++ /* clk_disable(info->lcd_clk);*/
++ enter_d0cs_a((volatile u32 *)info->clkmgr_base, (volatile u32 *)info->dmc_base);
++ pxafb_set_pcd();
++ /* clk_enable(info->lcd_clk);*/
++ /* update to D0CS LPJ, it must be updated before udelay() */
++ loops_per_jiffy = d0cs_lpj;
++ if (cpu_is_pxa930())
++ udelay(200);
++ else
++ udelay(100);
++
++ /* disable PLL */
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ if (spll) {
++ /* single PLL mode only disable System PLL */
++ accr |= (1 << ACCR_SPDIS_OFFSET);
++ } else {
++ /* Disable both System PLL and Core PLL */
++ accr |= (1 << ACCR_XPDIS_OFFSET) | (1 << ACCR_SPDIS_OFFSET);
++ }
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++
++ mdrefr = __raw_readl(info->dmc_base + MDREFR_OFF);
++ __raw_writel(mdrefr, info->dmc_base + MDREFR_OFF);
++}
++
++static void pxa3xx_exit_d0cs(void *driver_data)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ unsigned int spll = 0;
++ uint32_t reg, accr, acsr, mdrefr;
++
++ reg = (12 << ACCR_XL_OFFSET) | (1 << ACCR_XN_OFFSET);
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ if (reg == (accr & (ACCR_XN_MASK | ACCR_XL_MASK))) {
++ spll = 1;
++ }
++ /* enable PLL */
++ if (spll) {
++ /* single PLL mode only enable System PLL */
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ accr &= ~(1 << ACCR_SPDIS_OFFSET);
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ do {
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ } while (acsr & (1 << ACCR_SPDIS_OFFSET));
++ } else {
++ /* enable both System PLL and Core PLL */
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ accr &= ~((1 << ACCR_XPDIS_OFFSET) |
++ (1 << ACCR_SPDIS_OFFSET));
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ do {
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ } while (acsr & (1 << ACCR_XPDIS_OFFSET)
++ || acsr & (1 << ACCR_SPDIS_OFFSET));
++ }
++
++ /* clk_disable(info->lcd_clk);*/
++ exit_d0cs_a((volatile u32 *)info->clkmgr_base, (volatile u32 *)info->dmc_base);
++ mdrefr = __raw_readl(info->dmc_base + MDREFR_OFF);
++ __raw_writel(mdrefr, info->dmc_base + MDREFR_OFF);
++ pxafb_set_pcd();
++ /* clk_enable(info->lcd_clk);*/
++}
++
++/* Return 1 if Grayback PLL is on. */
++static int check_grayback_pll(void *driver_data)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++
++ return (__raw_readl(info->clkmgr_base + OSCC_OFF) & (1 << 17));
++}
++
++static int set_grayback_pll(void *driver_data, int lev)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ int timeout = 100, turnoff;
++ uint32_t oscc, agenp;
++
++ if ((info->cpuid & 0xFFF0) != 0x6830 && (info->cpuid & 0xFFF0) != 0x6930) {
++ /* It's not PXA930/PXA935/PXA940*/
++ return 0;
++ }
++ if (lev) {
++ /* turn on grayback PLL */
++ for (;;){
++ timeout = 100;
++ /* clear OSCC[GPRM] */
++ oscc = __raw_readl(info->clkmgr_base + OSCC_OFF);
++ oscc &= ~(1 << 18);
++ __raw_writel(oscc, info->clkmgr_base + OSCC_OFF);
++
++ /* set AGENP[GBPLL_CTRL] and AGENP[GBPLL_ST] */
++ agenp = __raw_readl(info->bpmu_base + AGENP_OFF);
++ agenp |= (3 << 28);
++ __raw_writel(agenp, info->bpmu_base + AGENP_OFF);
++
++ /* check OSCC[GPRL] */
++ do {
++ oscc = __raw_readl(info->clkmgr_base + OSCC_OFF);
++ if (--timeout == 0)
++ break;
++ } while (!(oscc & (1 << 17)));
++
++ if (timeout)
++ break;
++ }
++ } else {
++ /* turn off Grayback PLL */
++ for (;;){
++ timeout = 100;
++ /* clear AGENP[GBPLL_CTRL] and AGENP[GBPLL_ST] */
++ agenp = __raw_readl(info->bpmu_base + AGENP_OFF);
++ if (agenp & (1 << 28)) {
++ turnoff = 1;
++ agenp &= ~(3 << 28);
++ agenp |= (2 << 28);
++ __raw_writel(agenp, info->bpmu_base + AGENP_OFF);
++
++ /* check OSCC[GPRL] */
++ do {
++ oscc = __raw_readl(info->clkmgr_base + OSCC_OFF);
++ if (--timeout == 0)
++ break;
++ } while ((oscc & (1 << 17)));
++ }
++
++ if (timeout)
++ break;
++ }
++ if (turnoff) {
++ /* set OSCC[GPRM] */
++ oscc = __raw_readl(info->clkmgr_base + OSCC_OFF);
++ oscc |= (1 << 18);
++ __raw_writel(oscc, info->clkmgr_base + OSCC_OFF);
++ }
++ }
++ return 0;
++}
++
++/*
++ * Return 2 if MTS should be changed to 2.
++ * Return 1 if MTS should be changed to 1.
++ * Return 0 if MTS won't be changed.
++ * In this function, the maxium MTS is 2.
++ */
++static int check_mts(struct dvfm_md_opt *old, struct dvfm_md_opt *new)
++{
++ int ret = 0;
++ if ((old->xn == 1) && (new->xn == 2))
++ ret = 2;
++ if ((old->xn == 2) && (new->xn == 1))
++ ret = 1;
++ return ret;
++}
++
++static int set_mts(void *driver_data, int mts)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ unsigned int ascr;
++
++ ascr = __raw_readl(info->bpmu_base + ASCR_OFF);
++ ascr &= ~(3 << ASCR_MTS_OFFSET);
++ ascr |= (mts << ASCR_MTS_OFFSET);
++ __raw_writel(ascr, info->bpmu_base + ASCR_OFF);
++
++ /* wait MTS is set */
++ do {
++ ascr = __raw_readl(info->bpmu_base + ASCR_OFF);
++ }while (((ascr >> ASCR_MTS_OFFSET) & 0x3)
++ != ((ascr >> ASCR_MTS_S_OFFSET) & 0x3));
++
++ return 0;
++}
++
++static int prepare_dmc(void *driver_data, int flag)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ int pll;
++ uint32_t mdcnfg, ddr_hcal;
++
++ if (flag == DMEMC_D0CS_ENTER) {
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++ mdcnfg |= (1 << MDCNFG_HWFREQ_OFFSET);
++ __raw_writel(mdcnfg, info->dmc_base + MDCNFG_OFF);
++
++ ddr_hcal = __raw_readl(info->dmc_base + DDR_HCAL_OFF);
++ ddr_hcal &= ~(1 << HCAL_HCEN_OFFSET);
++ __raw_writel(ddr_hcal, info->dmc_base + DDR_HCAL_OFF);
++
++ return 0;
++ } else if (flag == DMEMC_D0CS_EXIT) {
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++ mdcnfg |= (1 << MDCNFG_HWFREQ_OFFSET);
++ __raw_writel(mdcnfg, info->dmc_base + MDCNFG_OFF);
++
++ ddr_hcal = __raw_readl(info->dmc_base + DDR_HCAL_OFF);
++ ddr_hcal |= (1 << HCAL_HCEN_OFFSET);
++ __raw_writel(ddr_hcal, info->dmc_base + DDR_HCAL_OFF);
++
++ return 0;
++ } else if (flag == DMEMC_FREQ_LOW) {
++ pll = 3;
++ } else {
++ pll = 2;
++ }
++
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++ mdcnfg &= ~(3 << 28);
++ mdcnfg |= (pll << 28);
++ __raw_writel(mdcnfg, info->dmc_base + MDCNFG_OFF);
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++
++ ddr_hcal = __raw_readl(info->dmc_base + DDR_HCAL_OFF);
++ ddr_hcal |= (1 << HCAL_HCEN_OFFSET);
++ __raw_writel(ddr_hcal, info->dmc_base + DDR_HCAL_OFF);
++ ddr_hcal = __raw_readl(info->dmc_base + DDR_HCAL_OFF);
++
++ do {
++ /*pr_debug("polling MDCNFG:0x%x\n", MDCNFG);*/
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++ } while (((mdcnfg >> 28) & 0x3) != pll);
++
++ return 0;
++}
++
++static int set_dmc60(void *driver_data, int flag)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ uint32_t accr, reg;
++
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ if (flag)
++ accr |= 0x80;
++ else
++ accr &= ~0x80;
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ /* polling ACCR */
++ do {
++ reg = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ } while ((accr & 0x80) != (reg & 0x80));
++
++ return 0;
++}
++
++/* set DF and EMPI divider */
++/* TODO: why did not we see DF/EMPI clock as input here? If we want to set DFI_clock or
++ * EMPI clock as other frequecy than 52, how can we do?
++ */
++static int set_df(void *driver_data, int smc)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ uint32_t memclkcfg;
++ int fix_empi;
++
++ if (((info->cpuid > 0x6880) && (info->cpuid <= 0x6881))
++ || ((info->cpuid >= 0x6890) && (info->cpuid <= 0x6892)))
++ /* It's PXA300 or PXA310 */
++ fix_empi = 1;
++ else
++ fix_empi = 0;
++
++ memclkcfg = __raw_readl(info->smc_base + MEMCLKCFG_OFF);
++ memclkcfg &= ~((7 << MEMCLKCFG_DF_OFFSET) | (7 << MEMCLKCFG_EMPI_OFFSET));
++ if (fix_empi) {
++ memclkcfg |= (3 << MEMCLKCFG_EMPI_OFFSET);
++ switch (smc) {
++ case 208:
++ /* divider -- 4 */
++ memclkcfg |= (3 << MEMCLKCFG_DF_OFFSET);
++ break;
++ case 104:
++ /* divider -- 2 */
++ memclkcfg |= (2 << MEMCLKCFG_DF_OFFSET);
++ break;
++ case 78:
++ /* divider -- 4 */
++ memclkcfg |= (3 << MEMCLKCFG_DF_OFFSET);
++ break;
++ }
++ } else {
++ switch (smc) {
++ case 208:
++ /* divider -- 4 */
++ memclkcfg |= (3 << MEMCLKCFG_DF_OFFSET);
++ memclkcfg |= (3 << MEMCLKCFG_EMPI_OFFSET);
++ break;
++ case 104:
++ /* divider -- 2 */
++ memclkcfg |= (2 << MEMCLKCFG_DF_OFFSET);
++ memclkcfg |= (2 << MEMCLKCFG_EMPI_OFFSET);
++ break;
++ case 78:
++ /* divider -- 4 */
++ memclkcfg |= (3 << MEMCLKCFG_DF_OFFSET);
++ memclkcfg |= (3 << MEMCLKCFG_EMPI_OFFSET);
++ break;
++ }
++ }
++ __raw_writel(memclkcfg, info->smc_base + MEMCLKCFG_OFF);
++ memclkcfg = __raw_readl(info->smc_base + MEMCLKCFG_OFF);
++
++ return 0;
++}
++
++/* TODO: sugguest to differentiate the operating point definition from
++ * register info.And we can remove *reg_new here, and convert dvfm_md_opt to
++ * it in the routine. That will make it much more clear.
++ */
++static int update_hss(void *driver_data, struct dvfm_md_opt *old, struct dvfm_md_opt *new,
++ struct pxa3xx_fv_info *fv_info)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ unsigned int accr, acsr;
++
++ if (old->hss != new->hss) {
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ accr &= ~ACCR_HSS_MASK;
++ accr |= (fv_info->hss << ACCR_HSS_OFFSET);
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ /* wait until ACSR is changed */
++ do {
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF) ;
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF) ;
++ }while ((accr & ACCR_HSS_MASK) != (acsr & ACCR_HSS_MASK));
++ /* clk_disable(info->lcd_clk);*/
++ /* set PCD just after HSS updated */
++ pxafb_set_pcd();
++ /* clk_enable(info->lcd_clk);*/
++ }
++
++ return 0;
++}
++
++static int update_bus_freq(void *driver_data, struct dvfm_md_opt *old, struct dvfm_md_opt *new)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ struct pxa3xx_fv_info fv_info;
++ uint32_t accr, acsr, mdcnfg, mask;
++ int timeout, dmcflag = 1;
++
++ freq2reg(&fv_info, new);
++ if (old->dmcfs < new->dmcfs)
++ prepare_dmc(info, DMEMC_FREQ_HIGH);
++ else if (old->dmcfs > new->dmcfs)
++ prepare_dmc(info, DMEMC_FREQ_LOW);
++ else
++ dmcflag = 0;
++ if (new->smcfs == 208 || new->smcfs == 78)
++ set_df(info, new->smcfs);
++
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ mask = 0;
++ if (old->smcfs != new->smcfs) {
++ accr &= ~ACCR_SMCFS_MASK;
++ accr |= (fv_info.smcfs << ACCR_SMCFS_OFFSET);
++ mask |= ACCR_SMCFS_MASK;
++ }
++ if (old->sflfs != new->sflfs) {
++ accr &= ~ACCR_SFLFS_MASK;
++ accr |= (fv_info.sflfs << ACCR_SFLFS_OFFSET);
++ mask |= ACCR_SFLFS_MASK;
++ }
++ if (old->dmcfs != new->dmcfs) {
++ accr &= ~ACCR_DMCFS_MASK;
++ accr |= (fv_info.dmcfs << ACCR_DMCFS_OFFSET);
++ mask |= ACCR_DMCFS_MASK;
++ }
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++
++ /* wait until ACSR is changed */
++ do {
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ } while ((accr & mask) != (acsr & mask));
++
++ if (dmcflag) {
++ timeout = 10;
++ do {
++ mdcnfg = __raw_readl(info->dmc_base + MDCNFG_OFF);
++ udelay(1);
++ if (--timeout == 0) {
++ printk(KERN_WARNING "MDCNFG[29:28] isn't zero\n");
++ break;
++ }
++ } while (mdcnfg & ( 3 << 28));
++ }
++
++ if (new->smcfs == 104) {
++ set_df(info, new->smcfs);
++ }
++
++ update_hss(info, old, new, &fv_info);
++
++ return 0;
++}
++
++static int set_freq(void *driver_data, struct dvfm_md_opt *old, struct dvfm_md_opt *new)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ int spll;
++ uint32_t accr, acsr;
++
++ /* check whether new OP is single PLL mode */
++ if ((new->xl == 0x0c) && (new->xn == 0x1))
++ spll = 1;
++ else
++ spll = 0;
++
++ /* turn on Grayback PLL */
++ if (!spll & !check_grayback_pll(info))
++ set_grayback_pll(info ,1);
++ if (check_mts(old, new) == 2)
++ set_mts(info, 2);
++
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ accr &= ~(ACCR_XL_MASK | ACCR_XN_MASK | ACCR_XSPCLK_MASK);
++ accr |= ((new->xl << ACCR_XL_OFFSET) | (new->xn << ACCR_XN_OFFSET)
++ | (3 << ACCR_XSPCLK_OFFSET));
++ __raw_writel(accr, info->clkmgr_base + ACCR_OFF);
++ /* delay 2 cycles of 13MHz clock */
++ udelay(1);
++
++ if (check_mts(old, new) == 1)
++ set_mts(info, 1);
++
++ if ((new->xl == old->xl) && (new->xn != old->xn))
++ /* set T bit */
++ pxa_clkcfg_write(1);
++ else
++ /* set F bit */
++ pxa_clkcfg_write(2);
++ do {
++ accr = __raw_readl(info->clkmgr_base + ACCR_OFF);
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ } while ((accr & (ACCR_XL_MASK | ACCR_XN_MASK))
++ != (acsr & (ACCR_XL_MASK | ACCR_XN_MASK)));
++
++ udelay(1);
++ update_bus_freq(info, old, new);
++
++ /* turn off Grayback PLL */
++ if (spll)
++ set_grayback_pll(info, 0);
++ return 0;
++}
++
++static int update_freq(void *driver_data, struct dvfm_freqs *freqs)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ static struct dvfm_md_opt before_d0cs;
++ struct dvfm_md_opt old, new;
++ struct op_info *p = NULL;
++ unsigned long flags;
++ int found = 0, new_op = cur_op;
++
++ memset(&old, 0, sizeof(struct dvfm_md_opt));
++ memset(&new, 0, sizeof(struct dvfm_md_opt));
++ write_lock_irqsave(&pxa3xx_dvfm_op_list.lock, flags);
++ if (!list_empty(&pxa3xx_dvfm_op_list.list)) {
++ list_for_each_entry(p, &pxa3xx_dvfm_op_list.list, list) {
++ if (p->index == freqs->old) {
++ found++;
++ memcpy(&old, (struct dvfm_md_opt *)p->op,
++ sizeof(struct dvfm_md_opt));
++ }
++ if (p->index == freqs->new) {
++ found++;
++ memcpy(&new, (struct dvfm_md_opt *)p->op,
++ sizeof(struct dvfm_md_opt));
++ new_op = p->index;
++ }
++ if (found == 2)
++ break;
++ }
++ }
++ write_unlock_irqrestore(&pxa3xx_dvfm_op_list.lock, flags);
++ if (found != 2)
++ return -EINVAL;
++
++ if ((old.power_mode == POWER_MODE_D0)
++ && (new.power_mode == POWER_MODE_D0CS)) {
++ memcpy(&before_d0cs, &old, sizeof(struct dvfm_md_opt));
++
++ pxa3xx_enter_d0cs(info);
++ update_voltage(info, &old, &new);
++ cur_op = new_op;
++ loops_per_jiffy = new.lpj;
++ return 0;
++ } else if ((old.power_mode == POWER_MODE_D0CS)
++ && (new.power_mode == POWER_MODE_D0)) {
++ if (memcmp(&before_d0cs, &new, sizeof(struct dvfm_md_opt))) {
++ /* exit d0cs and set new operating point */
++ if ((before_d0cs.vcc_core < new.vcc_core) ||
++ (before_d0cs.vcc_sram < new.vcc_sram)) {
++ update_voltage(info, &old, &new);
++ } else {
++ update_voltage(info, &old, &before_d0cs);
++ }
++ pxa3xx_exit_d0cs(info);
++ set_freq(info, &before_d0cs, &new);
++
++ if ((before_d0cs.vcc_core > new.vcc_core) ||
++ (before_d0cs.vcc_sram > new.vcc_sram))
++ update_voltage(info, &before_d0cs, &new);
++ } else {
++ update_voltage(info, &old, &new);
++ /* exit d0cs */
++ pxa3xx_exit_d0cs(info);
++ }
++ cur_op = new_op;
++ loops_per_jiffy = new.lpj;
++ return 0;
++ } else if ((old.power_mode == POWER_MODE_D0CS)
++ && (new.power_mode == POWER_MODE_D0CS)) {
++ cur_op = new_op;
++ return 0;
++ }
++
++ if (old.core < new.core) {
++ update_voltage(info, &old, &new);
++ }
++ set_freq(info, &old, &new);
++ if (old.core > new.core) {
++ update_voltage(info, &old, &new);
++ }
++ cur_op = new_op;
++ if ((new.power_mode == POWER_MODE_D0)
++ || (new.power_mode == POWER_MODE_D0CS))
++ loops_per_jiffy = new.lpj;
++ return 0;
++}
++
++/* function of entering low power mode */
++extern void enter_lowpower_mode(int state);
++
++static void do_freq_notify(void *driver_data, struct dvfm_freqs *freqs)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++
++ dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE);
++ update_freq(info, freqs);
++ dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE);
++ ispt_dvfm_op(freqs->old, freqs->new);
++
++ printk("-- dvfm: cur_op=%d\n",cur_op);
++}
++
++static void do_lowpower_notify(void *driver_data, struct dvfm_freqs *freqs, unsigned int state)
++{
++ dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE);
++ //enter_lowpower_mode(state);
++ dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE);
++ ispt_power_state_d2();
++}
++
++static int check_op(void *driver_data, struct dvfm_freqs *freqs, unsigned int new,
++ unsigned int relation)
++{
++ struct op_info *p = NULL;
++ struct dvfm_md_opt *q = NULL;
++ int core, tmp_core = -1, found = 0;
++ int first_op = 0;
++
++ freqs->new = -1;
++ if (!dvfm_find_op(new, &p)) {
++ q = (struct dvfm_md_opt *)p->op;
++ core = q->core;
++ } else
++ return -EINVAL;
++ /*
++ pr_debug("%s, old:%d, new:%d, core:%d\n", __FUNCTION__, freqs->old,
++ new, core);
++ */
++ read_lock(&pxa3xx_dvfm_op_list.lock);
++ if (relation == RELATION_LOW) {
++ /* Set the lowest frequency that is higher than specifed one */
++ list_for_each_entry(p, &pxa3xx_dvfm_op_list.list, list) {
++ q = (struct dvfm_md_opt *)p->op;
++ if (core == 0) {
++ /* Lowpower mode */
++ if ((q->power_mode == POWER_MODE_D1)
++ || (q->power_mode == POWER_MODE_D2)
++ || (q->power_mode == POWER_MODE_CG)) {
++ if (!p->device && (new == p->index)) {
++ freqs->new = p->index;
++ /*
++ pr_debug("%s, found op%d\n",
++ __FUNCTION__, p->index);
++ */
++ break;
++ }
++ }
++ continue;
++ }
++
++ if (!p->device && (q->core >= core)) {
++ if (tmp_core == -1 || (tmp_core >= q->core)) {
++ /*
++ pr_debug("%s, found op%d, core:%d\n",
++ __FUNCTION__, p->index,
++ q->core);
++ */
++ if (first_op == 0)
++ first_op = p->index;
++ freqs->new = p->index;
++ tmp_core = q->core;
++ found = 1;
++ }
++ if (found && (new == p->index))
++ break;
++ }
++ }
++ if (found && (first_op == 1) && (new != p->index))
++ freqs->new = first_op;
++ } else if (relation == RELATION_HIGH) {
++ /* Set the highest frequency that is lower than specified one */
++ list_for_each_entry(p, &pxa3xx_dvfm_op_list.list, list) {
++ q = (struct dvfm_md_opt *)p->op;
++ if (!p->device && (q->core <= core)) {
++ if (tmp_core == -1 || tmp_core < q->core) {
++ freqs->new = p->index;
++ tmp_core = q->core;
++ }
++ }
++ }
++ } else if (relation == RELATION_STICK) {
++ /* Set the specified frequency */
++ list_for_each_entry(p, &pxa3xx_dvfm_op_list.list, list) {
++ if (!p->device && (p->index == new)) {
++ freqs->new = p->index;
++ break;
++ }
++ }
++ }
++ read_unlock(&pxa3xx_dvfm_op_list.lock);
++ if (freqs->new == -1) {
++ /*
++ pr_debug("%s, Can't find op\n", __FUNCTION__);
++ pr_debug("%s, old:%d, new:%d, core:%d\n", __FUNCTION__,
++ freqs->old, new, core);
++ */
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int pxa3xx_get_freq(void *driver_data, struct op_info *p, struct op_freq *freq)
++{
++ struct dvfm_md_opt *q = (struct dvfm_md_opt *)p->op;
++ freq->cpu_freq = q->core;
++ return 0;
++}
++
++static int pxa3xx_check_active_op(void *driver_data, struct op_info *p)
++{
++ struct dvfm_md_opt *q = (struct dvfm_md_opt *)p->op;
++
++ if ((!strcmp(q->name, "D0CS")) && (boot_core_freq >= q->core))
++ return 0;
++
++ if ((!strcmp(q->name, "104M")) && (boot_core_freq >= q->core))
++ return 0;
++
++ if ((!strcmp(q->name, "156M")) && (boot_core_freq >= q->core))
++ return 0;
++
++ if ((!strcmp(q->name, "208M")) && (boot_core_freq >= q->core))
++ return 0;
++
++ if ((!strcmp(q->name, "416M")) && (boot_core_freq >= q->core))
++ return 0;
++
++ if ((!strcmp(q->name, "624M")) && (boot_core_freq >= q->core))
++ return 0;
++
++ return -EINVAL;
++}
++
++
++static int pxa3xx_set_op(void *driver_data, struct dvfm_freqs *freqs, unsigned int new,
++ unsigned int relation)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ struct dvfm_md_opt *md = NULL, *old_md = NULL;
++ struct op_info *p = NULL;
++ unsigned long flags;
++ int ret;
++ out_d0cs = 0;
++
++ local_fiq_disable();
++ local_irq_save(flags);
++ ret = dvfm_find_op(freqs->old, &p);
++ if (ret) {
++ printk("---- pxa3xx_set_op1 check_op failed to %d\n",new);
++ goto out;
++ }
++
++ memcpy(&freqs->old_info, p, sizeof(struct op_info));
++ ret = check_op(info, freqs, new, relation);
++ if (ret) {
++ printk("---- pxa3xx_set_op2 check_op failed to %d\n",new);
++ goto out;
++ }
++
++ if (!dvfm_find_op(freqs->new, &p)) {
++ memcpy(&(freqs->new_info), p, sizeof(struct op_info));
++ /* If find old op and new op is same, skip it.
++ * At here, ret should be zero.
++ */
++ if (freqs->old_info.index == freqs->new_info.index)
++ goto out;
++#ifdef DVFM_LP_SAFE
++ md = (struct dvfm_md_opt *)(freqs->new_info.op);
++ old_md = (struct dvfm_md_opt *)(freqs->old_info.op);
++ if ((old_md->power_mode == POWER_MODE_D0CS)
++ && ((md->power_mode == POWER_MODE_D1)
++ || (md->power_mode == POWER_MODE_D2))) {
++ dvfm_disable_op_name("D0CS", dvfm_dev_id);
++ out_d0cs = 1;
++ }
++
++ md = (struct dvfm_md_opt *)p->op;
++ switch (md->power_mode) {
++ case POWER_MODE_D0:
++ case POWER_MODE_D0CS:
++ do_freq_notify(info, freqs);
++ break;
++ case POWER_MODE_D1:
++ case POWER_MODE_D2:
++ case POWER_MODE_CG:
++ do_lowpower_notify(info, freqs, md->power_mode);
++ break;
++ }
++ local_irq_restore(flags);
++ local_fiq_enable();
++
++ if (out_d0cs) {
++ dvfm_enable_op_name("D0CS", dvfm_dev_id);
++ }
++#else
++ md = (struct dvfm_md_opt *)p->op;
++ switch (md->power_mode) {
++ case POWER_MODE_D0:
++ case POWER_MODE_D0CS:
++ do_freq_notify(info, freqs);
++ break;
++ case POWER_MODE_D1:
++ case POWER_MODE_D2:
++ case POWER_MODE_CG:
++ do_lowpower_notify(info, freqs, md->power_mode);
++ break;
++ }
++ local_irq_restore(flags);
++ local_fiq_enable();
++#endif
++ }
++ return 0;
++out:
++ local_irq_restore(flags);
++ local_fiq_enable();
++ return ret;
++}
++
++static int pxa3xx_request_op(void *driver_data, int index)
++{
++ struct dvfm_freqs freqs;
++ struct op_info *info = NULL;
++ struct dvfm_md_opt *md = NULL;
++ int relation, ret;
++ ret = dvfm_find_op(index, &info);
++ if (ret)
++ goto out;
++ freqs.old = cur_op;
++ freqs.new = index;
++ md = (struct dvfm_md_opt *)(info->op);
++ switch (md->power_mode) {
++ case POWER_MODE_D1:
++ case POWER_MODE_D2:
++ case POWER_MODE_CG:
++ relation = RELATION_STICK;
++ ret = pxa3xx_set_op(driver_data, &freqs, index, relation);
++ break;
++ default:
++ relation = RELATION_LOW;
++ /* only use non-low power mode as preferred op */
++ ret = pxa3xx_set_op(driver_data, &freqs, index, relation);
++ if (!ret)
++ preferred_op = index;
++ break;
++ }
++out:
++ return ret;
++}
++
++static int is_d0cs(void *driver_data)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ unsigned int acsr;
++ /* read ACSR */
++ acsr = __raw_readl(info->clkmgr_base + ACSR_OFF);
++ /* Check ring oscillator status */
++ if (acsr & (1 << 26))
++ return 1;
++ return 0;
++}
++
++/* Produce a operating point table */
++static int op_init(void *driver_data, struct info_head *op_table)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ unsigned long flags;
++ int i, index;
++ struct op_info *p = NULL, *q = NULL;
++ struct dvfm_md_opt *md = NULL, *smd = NULL;
++ struct proc_op_array *proc = NULL;
++
++ write_lock_irqsave(&op_table->lock, flags);
++ for (i = 0; i < ARRAY_SIZE(proc_op_arrays); i++){
++ if (proc_op_arrays[i].cpuid == (info->cpuid & 0xfff0)) {
++ proc = &proc_op_arrays[i];
++ break;
++ }
++ }
++ if (proc == NULL) {
++ printk(KERN_ERR "Failed to find op tables for cpu_id 0x%08x", info->cpuid);
++ write_unlock_irqrestore(&op_table->lock, flags);
++ return -EIO;
++ } else {
++ printk("initializing op table for %s\n", proc->cpu_name);
++ }
++ for (i = 0, index = 0; i < proc->nr_op; i++) {
++ /* PXA310 A2 or PXA935/PXA940, dmcfs 60MHz in S0D0CS mode */
++ if ((proc->op_array[i].power_mode == POWER_MODE_D0CS)
++ && (info->cpuid == 0x6892 || (info->cpuid & 0xFFF0) == 0x6930)) {
++ set_dmc60(info, 1);
++ proc->op_array[i].dmcfs = 60;
++ }
++
++ /* Set index of operating point used in idle */
++ if (proc->op_array[i].power_mode != POWER_MODE_D0) {
++ //set_idle_op(index, proc->op_array[i].power_mode);
++ }
++
++ md = (struct dvfm_md_opt *)kzalloc(sizeof(struct dvfm_md_opt),
++ GFP_KERNEL);
++ p = (struct op_info *)kzalloc(sizeof(struct op_info),
++ GFP_KERNEL);
++ p->op = (void *)md;
++ memcpy(p->op, &proc->op_array[i], sizeof(struct dvfm_md_opt));
++ md->core = 13 * md->xl * md->xn;
++ if (md->power_mode == POWER_MODE_D0CS)
++ md->core = 60;
++ p->index = index++;
++ list_add_tail(&(p->list), &(op_table->list));
++ }
++ md = (struct dvfm_md_opt *)kzalloc(sizeof(struct dvfm_md_opt),
++ GFP_KERNEL);
++ p = (struct op_info *)kzalloc(sizeof(struct op_info), GFP_KERNEL);
++ p->op = (void *)md;
++ if (capture_op_info(info, md)) {
++ printk(KERN_WARNING "Failed to get current op setting\n");
++ } else {
++ def_op = 0x5a5a; /* magic number */
++ list_for_each_entry(q, &(op_table->list), list) {
++ smd = (struct dvfm_md_opt *)q->op;
++ md->flag = smd->flag;
++ md->lpj = smd->lpj;
++ md->core = smd->core;
++ if (memcmp(md, smd, sizeof(struct dvfm_md_opt)) == 0) {
++ def_op = q->index;
++ break;
++ }
++ }
++ }
++ if (is_d0cs(driver_data))
++ md->core = 60;
++ else
++ md->core = 13 * md->xl * md->xn;
++ md->lpj = loops_per_jiffy;
++ md->flag = OP_FLAG_BOOT;
++ sprintf(md->name, "BOOT OP");
++
++ boot_core_freq = md->core;
++
++#if 0 /* disable CUSTOM OP for borq platfrom */
++ smd = (struct dvfm_md_opt *)kzalloc(sizeof(struct dvfm_md_opt),
++ GFP_KERNEL);
++ q = (struct op_info *)kzalloc(sizeof(struct op_info), GFP_KERNEL);
++ memcpy(q, p, sizeof(struct op_info));
++ memcpy(smd, md, sizeof(struct dvfm_md_opt));
++ smd->core = md->core;
++ smd->lpj = md->lpj;
++ smd->flag = OP_FLAG_USER_DEFINED;
++ sprintf(smd->name, "CUSTOM OP");
++ q->op = (void *)smd;
++ /* Add CUSTOM OP into op list */
++ q->index = index++;
++ list_add_tail(&q->list, &op_table->list);
++#endif
++ /* Add BOOT OP into op list */
++ p->index = index++;
++ preferred_op = p->index;
++ list_add_tail(&p->list, &op_table->list);
++ /* BOOT op */
++ if (def_op == 0x5a5a) {
++ cur_op = p->index;
++ def_op = p->index;
++ } else
++ cur_op = def_op;
++ pr_debug("%s, def_op:%d, cur_op:%d\n", __FUNCTION__, def_op, cur_op);
++
++ op_nums = proc->nr_op + 2; /* set the operating point number */
++
++ pr_debug("Current Operating Point is %d\n", cur_op);
++ dump_op_list(info, op_table, OP_FLAG_ALL);
++ write_unlock_irqrestore(&op_table->lock, flags);
++
++ return 0;
++}
++
++/*
++ * The machine operation of dvfm_enable
++ */
++static int pxa3xx_enable_dvfm(void *driver_data, int dev_id)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ struct dvfm_md_opt *md = NULL;
++ struct op_info *p = NULL;
++ int i, num;
++ num = get_op_num(info, &pxa3xx_dvfm_op_list);
++ for (i = 0; i < num; i++) {
++ if (!dvfm_find_op(i, &p)) {
++ md = (struct dvfm_md_opt *)p->op;
++ if (md->core < boot_core_freq)
++ dvfm_enable_op_name(md->name, dev_id);
++ }
++ }
++ ispt_block_dvfm(0, dev_id);
++ return 0;
++}
++
++/*
++ * The mach operation of dvfm_disable
++ */
++static int pxa3xx_disable_dvfm(void *driver_data, int dev_id)
++{
++ struct pxa3xx_dvfm_info *info = driver_data;
++ struct dvfm_md_opt *md = NULL;
++ struct op_info *p = NULL;
++ int i, num;
++ num = get_op_num(info, &pxa3xx_dvfm_op_list);
++ for (i = 0; i < num; i++) {
++ if (!dvfm_find_op(i, &p)) {
++ md = (struct dvfm_md_opt *)p->op;
++ if (md->core < boot_core_freq)
++ dvfm_disable_op_name(md->name, dev_id);
++ }
++ }
++ ispt_block_dvfm(1, dev_id);
++ return 0;
++}
++
++static int pxa3xx_enable_op(void *driver_data, int index, int relation)
++{
++ /*
++ * Restore preferred_op. Because this op is sugguested by policy maker
++ * or user.
++ */
++ return pxa3xx_request_op(driver_data, preferred_op);
++}
++
++static int pxa3xx_disable_op(void *driver_data, int index, int relation)
++{
++ struct dvfm_freqs freqs;
++ if (cur_op == index) {
++ freqs.old = index;
++ freqs.new = -1;
++ dvfm_set_op(&freqs, freqs.old, relation);
++ }
++ return 0;
++}
++
++static int pxa3xx_volt_show(void *driver_data, char *buf)
++{
++ struct dvfm_md_opt new;
++ int len = 0;
++
++ memset(&new, 0, sizeof(struct dvfm_md_opt));
++ pxa3xx_pmic_get_voltage(VCC_CORE, &new.vcc_core);
++ pxa3xx_pmic_get_voltage(VCC_SRAM, &new.vcc_sram);
++ len = sprintf(buf, "core voltage:%dmv, sram voltage:%dmv\n",
++ new.vcc_core, new.vcc_sram);
++ return len;
++}
++
++#ifdef CONFIG_CPU_PXA310
++static int pxa3xx_freq_show(void *driver_data, struct op_info *p, char *buf)
++{
++ struct dvfm_md_opt *q = (struct dvfm_md_opt *)p->op;
++ struct pxa3xx_fv_info info;
++
++ if (q == NULL)
++ return sprintf(buf, "unable to get frequency info\n");
++ else {
++ freq2reg(&info, q);
++ if (!info.d0cs){
++ return sprintf(buf, "current frequency is %luMhz"
++ " (XL: %lu, XN: %lu, %s) with\n"
++ " SMEM: %lu (%dMhz)\n"
++ " SRAM: %lu (%dMhz)\n"
++ " HSS: %lu (%dMhz)\n"
++ " DDR: %lu (%dMhz)\n"
++ " DFCLK: %lu (%dMhz)\n"
++ " EMPICLK: %lu (%dMhz)\n"
++ " D0CKEN_A: 0x%08x\n"
++ " D0CKEN_B: 0x%08x\n"
++ " ACCR: 0x%08x\n"
++ " ACSR: 0x%08x\n"
++ " OSCC: 0x%08x\n",
++ FREQ_CORE(info.xl, info.xn), info.xl, info.xn,
++ (info.xn != 0x1)? "Turbo Mode" : "Run Mode",
++ info.smcfs, FREQ_STMM(info.smcfs),
++ info.sflfs, FREQ_SRAM(info.sflfs),
++ info.hss, FREQ_HSS(info.hss),
++ info.dmcfs, FREQ_DDR(info.dmcfs),
++ info.df_clk, FREQ_DFCLK(info.smcfs, info.df_clk),
++ info.empi_clk, FREQ_EMPICLK(info.smcfs, info.empi_clk),
++ CKENA, CKENB, ACCR, ACSR, OSCC);
++ } else {
++ return sprintf(buf, "current frequency is 60Mhz"
++ " (ring oscillator mode) with\n"
++ " SMEM:15Mhz\n"
++ " SRAM:60Mhz\n"
++ " HSS:60Mhz\n"
++ " DDR:30Mhz\n"
++ " DFCLK:%sMhz\n"
++ " EMPICLK:%sMhz\n"
++ " D0CKEN_A: 0x%08x\n"
++ " D0CKEN_B: 0x%08x\n"
++ " ACCR: 0x%08x\n"
++ " ACSR: 0x%08x\n"
++ " OSCC: 0x%08x\n",
++ (info.df_clk == 1)?"15":
++ (info.df_clk == 2)?"7.5":
++ (info.df_clk == 3)?"3.75":"0",
++ (info.empi_clk == 1)?"15":
++ (info.empi_clk == 2)?"7.5":
++ (info.empi_clk == 3)?"3.75":"0",
++ CKENA, CKENB, ACCR, ACSR, OSCC);
++ }
++
++
++ }
++}
++#endif
++
++#ifdef CONFIG_PXA3xx_DVFM_STATS
++/* Convert ticks from 32K timer to microseconds */
++static unsigned int pxa3xx_ticks_to_usec(unsigned int ticks)
++{
++ return (ticks * 5 * 5 * 5 * 5 * 5 * 5) >> 9;
++}
++
++static unsigned int pxa3xx_ticks_to_sec(unsigned int ticks)
++{
++ return (ticks >> 15);
++}
++
++static unsigned int pxa3xx_read_time(void)
++{
++ return OSCR4;
++}
++
++/* It's invoked by PM functions.
++ * PM functions can store the accurate time of entering/exiting low power
++ * mode.
++ */
++int calc_switchtime(unsigned int end, unsigned int start)
++{
++ switch_lowpower_before = end;
++ switch_lowpower_after = start;
++ return 0;
++}
++
++static int pxa3xx_stats_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data)
++{
++ struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
++ struct op_info *info = &(freqs->new_info);
++ struct dvfm_md_opt *md = NULL;
++ unsigned int ticks;
++
++ ticks = pxa3xx_read_time();
++ md = (struct dvfm_md_opt *)(info->op);
++ if (md->power_mode == POWER_MODE_D0 ||
++ md->power_mode == POWER_MODE_D0CS) {
++ switch (val) {
++ case DVFM_FREQ_PRECHANGE:
++ calc_switchtime_start(freqs->old, freqs->new, ticks);
++ break;
++ case DVFM_FREQ_POSTCHANGE:
++ /* Calculate the costed time on switching frequency */
++ calc_switchtime_end(freqs->old, freqs->new, ticks);
++ dvfm_add_event(freqs->old, CPU_STATE_RUN,
++ freqs->new, CPU_STATE_RUN);
++ dvfm_add_timeslot(freqs->old, CPU_STATE_RUN);
++ mspm_add_event(freqs->old, CPU_STATE_RUN);
++ break;
++ }
++ } else if (md->power_mode == POWER_MODE_D1 ||
++ md->power_mode == POWER_MODE_D2 ||
++ md->power_mode == POWER_MODE_CG) {
++ switch (val) {
++ case DVFM_FREQ_PRECHANGE:
++ calc_switchtime_start(freqs->old, freqs->new, ticks);
++ /* Consider lowpower mode as idle mode */
++ dvfm_add_event(freqs->old, CPU_STATE_RUN,
++ freqs->new, CPU_STATE_IDLE);
++ dvfm_add_timeslot(freqs->old, CPU_STATE_RUN);
++ mspm_add_event(freqs->old, CPU_STATE_RUN);
++ break;
++ case DVFM_FREQ_POSTCHANGE:
++ /* switch_lowpower_start before switch_lowpower_after
++ * is updated in calc_switchtime().
++ * It's invoked in pm function.
++ */
++ calc_switchtime_end(freqs->old, freqs->new,
++ switch_lowpower_before);
++ calc_switchtime_start(freqs->new, freqs->old,
++ switch_lowpower_after);
++ calc_switchtime_end(freqs->new, freqs->old,
++ ticks);
++ dvfm_add_event(freqs->new, CPU_STATE_IDLE,
++ freqs->old, CPU_STATE_RUN);
++ dvfm_add_timeslot(freqs->new, CPU_STATE_IDLE);
++ mspm_add_event(freqs->new, CPU_STATE_IDLE);
++ break;
++ }
++ }
++ return 0;
++}
++#else
++#define pxa3xx_ticks_to_usec NULL
++#define pxa3xx_ticks_to_sec NULL
++#define pxa3xx_read_time NULL
++#endif
++
++static struct dvfm_driver pxa3xx_driver = {
++ .count = get_op_num,
++ .set = pxa3xx_set_op,
++ .dump = dump_op,
++ .name = get_op_name,
++ .request_set = pxa3xx_request_op,
++ .enable_dvfm = pxa3xx_enable_dvfm,
++ .disable_dvfm = pxa3xx_disable_dvfm,
++ .enable_op = pxa3xx_enable_op,
++ .disable_op = pxa3xx_disable_op,
++ .volt_show = pxa3xx_volt_show,
++#ifdef CONFIG_CPU_PXA310
++ .freq_show = pxa3xx_freq_show,
++#endif
++ .ticks_to_usec = pxa3xx_ticks_to_usec,
++ .ticks_to_sec = pxa3xx_ticks_to_sec,
++ .read_time = pxa3xx_read_time,
++ .get_freq = pxa3xx_get_freq,
++ .check_active_op = pxa3xx_check_active_op,
++};
++
++#ifdef CONFIG_PM
++static int pxa3xx_freq_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ current_op = cur_op;
++ dvfm_request_op(1);
++ return 0;
++}
++
++static int pxa3xx_freq_resume(struct platform_device *pdev)
++{
++ dvfm_request_op(current_op);
++ return 0;
++}
++#else
++#define pxa3xx_freq_suspend NULL
++#define pxa3xx_freq_resume NULL
++#endif
++
++static void pxa3xx_poweri2c_init(struct pxa3xx_dvfm_info *info)
++{
++ uint32_t avcr, svcr, cvcr, pcfr, pvcr;
++
++ if ((info->flags & PXA3xx_USE_POWER_I2C) &&
++ ((info->cpuid & 0xfff0) == 0x6930)) {
++ /* set AVCR for PXA935/PXA940:
++ * level 0: 1250mv, 0x15
++ * level 1: 1250mv, 0x15
++ * level 2: 1250mv, 0x15
++ * level 3: 1250mv, 0x15
++ */
++ avcr = __raw_readl(info->spmu_base + AVCR_OFF);
++ avcr &= 0xE0E0E0E0;
++ avcr |= (0x15 << 24) | (0x15 << 16) | (0x15 << 8) | 0x15;
++ __raw_writel(avcr, info->spmu_base + AVCR_OFF);
++ avcr = __raw_readl(info->spmu_base + AVCR_OFF);
++
++ /* set delay */
++ pcfr = __raw_readl(info->spmu_base + PCFR_OFF);
++ pcfr &= 0x000FFFFF;
++ pcfr |= 0xCCF00000;
++ /* Disable pullup/pulldown in PWR_SCL and PWR_SDA */
++ pcfr |= 0x04;
++ __raw_writel(pcfr, info->spmu_base + PCFR_OFF);
++ pcfr = __raw_readl(info->spmu_base + PCFR_OFF);
++
++ /* enable FVE,PVE,TVE bit */
++ __raw_writel(0xe0500034, info->spmu_base + PVCR_OFF);
++ } else if (info->flags & PXA3xx_USE_POWER_I2C) {
++ /* set AVCR for PXA300/PXA310/PXA320/PXA930
++ * level 0: 1000mv, 0x0b
++ * level 1: 1100mv, 0x0f
++ * level 2: 1375mv, 0x1a
++ * level 3: 1400mv, 0x1b
++ */
++ avcr = __raw_readl(info->spmu_base + AVCR_OFF);
++ avcr &= 0xE0E0E0E0;
++ /* PXA930 B0(cpuid 0x6835) requires special setting */
++ if (info->cpuid == 0x6835)
++ avcr |= (0x1b << 24) | (0x1a << 16) | (0x0f << 8) | 0xb;
++ else
++ avcr |= (0x0f << 24) | (0x1a << 16) | (0x0f << 8) | 0xb;
++ __raw_writel(avcr, info->spmu_base + AVCR_OFF);
++ avcr = __raw_readl(info->spmu_base + AVCR_OFF);
++ /* set SVCR:
++ * level 0: 1100mv, 0x0f
++ * level 1: 1200mv, 0x13
++ * level 2: 1400mv, 0x1b
++ * level 3: 1400mv, 0x1b
++ */
++ svcr = __raw_readl(info->spmu_base + SVCR_OFF);
++ svcr &= 0xE0E0E0E0;
++ if (info->cpuid == 0x6835)
++ svcr |= (0x1b << 24) | (0x1b << 16) | (0x13 << 8) | 0xf;
++ else
++ svcr |= (0x0f << 24) | (0x1b << 16) | (0x13 << 8) | 0xf;
++ __raw_writel(svcr, info->spmu_base + SVCR_OFF);
++ svcr = __raw_readl(info->spmu_base + SVCR_OFF);
++ /* set CVCR:
++ * level 0: 925mv, 0x08
++ * level 1: 1250mv, 0x15
++ * level 2: 1375mv, 0x1a
++ * level 3: 1400mv, 0x1b
++ */
++ cvcr = __raw_readl(info->spmu_base + CVCR_OFF);
++ cvcr &= 0xE0E0E0E0;
++ if (info->cpuid == 0x6835)
++ cvcr |= (0x1b << 24) | (0x1a << 16) | (0x15 << 8) | 0x08;
++ else
++ cvcr |= (0x0f << 24) | (0x1a << 16) | (0x15 << 8) | 0x08;
++ __raw_writel(cvcr, info->spmu_base + CVCR_OFF);
++ cvcr = __raw_readl(info->spmu_base + CVCR_OFF);
++
++ /* set delay */
++ pcfr = __raw_readl(info->spmu_base + PCFR_OFF);
++ pcfr &= 0x000FFFFF;
++ pcfr |= 0xCCF00000;
++ /* Disable pullup/pulldown in PWR_SCL and PWR_SDA */
++ pcfr |= 0x04;
++ __raw_writel(pcfr, info->spmu_base + PCFR_OFF);
++ pcfr = __raw_readl(info->spmu_base + PCFR_OFF);
++
++ /* enable FVE,PVE,TVE bit */
++ __raw_writel(0xe0500034, info->spmu_base + PVCR_OFF);
++ } else {
++ /* disable FVE,PVE,TVE,FVC bit */
++ pvcr = __raw_readl(info->spmu_base + PVCR_OFF);
++ pvcr &= 0x0fffffff;
++ __raw_writel(pvcr, info->spmu_base + PVCR_OFF);
++ }
++}
++
++int gpio_reset_work_around(void)
++{
++ dvfm_disable_op_name("624M", dvfm_dev_id);
++ dvfm_disable_op_name("416M", dvfm_dev_id);
++ dvfm_disable_op_name("208M", dvfm_dev_id);
++ return 0;
++}
++
++static int pxa3xx_freq_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ struct pxa3xx_freq_mach_info *pdata;
++ struct pxa3xx_dvfm_info *info;
++ int rc;
++
++ /* initialize the information necessary to frequency/voltage change operation */
++ pdata = pdev->dev.platform_data;
++ info = kzalloc(sizeof(struct pxa3xx_dvfm_info), GFP_KERNEL);
++ info->flags = pdata->flags;
++ info->cpuid = read_cpuid(0) & 0xFFFF;
++
++ //info->lcd_clk = clk_get(&pxa_device_fb.dev, "LCDCLK");
++ //if (IS_ERR(info->lcd_clk)) goto err;
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clkmgr_regs");
++ if (!res) goto err;
++ info->clkmgr_base = ioremap(res->start, res->end - res->start + 1);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spmu_regs");
++ if (!res) goto err;
++ info->spmu_base = ioremap(res->start, res->end - res->start + 1);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bpmu_regs");
++ if (!res) goto err;
++ info->bpmu_base = ioremap(res->start, res->end - res->start + 1);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc_regs");
++ if (!res) goto err;
++ info->dmc_base = ioremap(res->start, res->end - res->start + 1);
++
++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc_regs");
++ if (!res) goto err;
++ info->smc_base = ioremap(res->start, res->end - res->start + 1);
++
++ pxa3xx_driver.priv = info;
++
++ pxa3xx_poweri2c_init(info);
++ op_init(info, &pxa3xx_dvfm_op_list);
++
++ return dvfm_register_driver(&pxa3xx_driver, &pxa3xx_dvfm_op_list);
++err:
++ printk("pxa3xx_dvfm init failed\n");
++ return -EIO;
++}
++
++static int pxa3xx_freq_remove(struct platform_device *pdev)
++{
++ kfree(pxa3xx_driver.priv);
++ return dvfm_unregister_driver(&pxa3xx_driver);
++}
++
++static struct platform_driver pxa3xx_freq_driver = {
++ .driver = {
++ .name = "pxa3xx-freq",
++ },
++ .probe = pxa3xx_freq_probe,
++ .remove = pxa3xx_freq_remove,
++#ifdef CONFIG_PM
++ //.suspend = pxa3xx_freq_suspend,
++ //.resume = pxa3xx_freq_resume,
++#endif
++};
++
++
++static int __init pxa3xx_freq_init(void)
++{
++ int ret;
++ ret = platform_driver_register(&pxa3xx_freq_driver);
++ if (ret)
++ goto out;
++#ifdef CONFIG_PXA3xx_DVFM_STATS
++ ret = dvfm_register_notifier(&notifier_freq_block,
++ DVFM_FREQUENCY_NOTIFIER);
++#endif
++ ret = dvfm_register("DVFM", &dvfm_dev_id);
++out:
++ return ret;
++}
++
++static void __exit pxa3xx_freq_exit(void)
++{
++#ifdef CONFIG_PXA3xx_DVFM_STATS
++ dvfm_unregister_notifier(&notifier_freq_block,
++ DVFM_FREQUENCY_NOTIFIER);
++#endif
++ dvfm_unregister("DVFM", &dvfm_dev_id);
++ platform_driver_unregister(&pxa3xx_freq_driver);
++}
++
++module_init(pxa3xx_freq_init);
++module_exit(pxa3xx_freq_exit);
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pxa3xx_dvfm_ll.S kernel/arch/arm/mach-pxa/pxa3xx_dvfm_ll.S
+--- linux-2.6.32/arch/arm/mach-pxa/pxa3xx_dvfm_ll.S 2009-12-13 13:00:42.108609192 +0200
++++ kernel/arch/arm/mach-pxa/pxa3xx_dvfm_ll.S 2009-12-12 16:09:26.482948915 +0200
+@@ -0,0 +1,261 @@
++@
++@ This program is free software; you can redistribute it and/or modify
++@ it under the terms of the GNU General Public License as published by
++@ the Free Software Foundation; either version 2 of the License, or
++@ (at your option) any later version.
++@
++@ This program is distributed in the hope that it will be useful,
++@ but WITHOUT ANY WARRANTY; without even the implied warranty of
++@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++@ GNU General Public License for more details.
++@
++@ You should have received a copy of the GNU General Public License
++@ along with this program; if not, write to the Free Software
++@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++@
++@
++@ FILENAME: pxa3xx_dvfm_ll.S
++@
++@ PURPOSE: Provides low level DVFM primitive functions written specifically
++@ for the Monahans/Zylonite processor/platform.
++@
++@******************************************************************************
++
++
++@
++@ List of primitive functions in this module:
++@
++ .global enter_d0cs_a
++ .global exit_d0cs_a
++ .global pxa_clkcfg_read
++ .global pxa_clkcfg_write
++
++.equ CLKMGR_ACCR_OFFSET,0x0000
++.equ CLKMGR_ACSR_OFFSET,0x0004
++
++.equ DMEMC_MDCNFG_OFFSET, 0x0000
++.equ DMEMC_DDRHCAL_OFFSET,0x0060
++
++ .text
++
++@
++@
++@ UINT32 enter_d0cs_a
++@
++@
++@ Description:
++@ put system into D0CS mode.
++@
++@ Input Parameters:
++@ r0 - arg1, the address of Clock Manager Controller
++@ r1 - arg2, the address of Dynamic Memory controller
++@ Returns:
++@ r0 - success (0) or failure(1)
++@
++@ Registers Modified:
++@ ACCR, MDCNFG, DDR_HCAL
++@ General Purpose Registers Modified: r3, r4
++@
++@ NOTE:
++@
++
++enter_d0cs_a:
++ stmfd sp!, {r3, r4, lr}
++ @
++ @ return directly if current mode is D0CS already
++ @
++ ldr r3, [r0, #CLKMGR_ACSR_OFFSET] @ load ACSR
++ tst r3, #0x04000000
++ movne r0, #0
++ bne 6f
++0:
++ @
++ @ set DMEMC.MDCFG[29]
++ @
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET] @ get MDCNFG
++ orr r3, r3, #0x20000000 @ Set DMEMC.MDCNFG[29].
++ str r3, [r1, #DMEMC_MDCNFG_OFFSET] @ load MDCNFG
++1:
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET] @ ensure DMEMC.MDCNFG[29] bit is written
++ tst r3, #0x20000000
++ beq 1b
++
++ @
++ @ clear DMEMC.DDR_HCAL[31]
++ @
++ ldr r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ get DDR_HCAL
++ bic r3, r3, #0x80000000 @ Insure DDR_HCAL[31] is clear
++ str r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ load DDR_HCAL
++2:
++ ldr r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ Insure DDR_HCAL[31] is clear
++ tst r3, #0x80000000
++ bne 2b
++
++ @
++ @ set ACCR[D0CS] bit
++ @
++ ldr r3, [r0, #CLKMGR_ACCR_OFFSET] @ get ACCR
++ orr r3, r3, #0x04000000 @ set D0CS bit in ACCR
++ str r3, [r0, #CLKMGR_ACCR_OFFSET] @ load ACCR
++3:
++ ldr r3, [r0, #CLKMGR_ACCR_OFFSET] @ ensure D0CS bit is written
++ tst r3, #0x04000000
++ beq 3b
++
++ @
++ @ enter D0CS mode
++ @
++ mov r4, #5 @ r4: power mode
++ b enterd0cs @ skip the garbage before .align 5
++ .align 5
++enterd0cs:
++ mcr p14, 0, r4, c7, c0, 0 @ enter D0CS mode
++4: @ wait for system to enter D0CS really
++ ldr r3, [r0, #CLKMGR_ACSR_OFFSET] @ load ACSR
++ tst r3, #0x04000000
++ beq 4b
++5: @ wait for DMEMC.MDCNFG[29] clear
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET]
++ tst r3, #0x20000000
++ bne 5b
++
++6:
++ @
++ @ return
++ @
++ mov r0, #0
++ ldmfd sp!, {r3, r4, pc} @ return
++
++@
++@
++@ UINT32 exit_d0cs_a
++@
++@
++@ Description:
++@ let system exit D0CS mode.
++@
++@ r0 - arg1, the address of Clock Manager Controller
++@ r1 - arg2, the address of Dynamic Memory controller
++@ Returns:
++@ r0 - success (0) or failure(1)
++@
++@ Registers Modified:
++@ ACCR, MDCNFG, DDR_HCAL
++@ General Purpose Registers Modified: r3, r4
++@
++@ NOTE:
++@
++
++exit_d0cs_a:
++ stmfd sp!, {r3,r4,lr}
++ @
++ @ return directly if current mode is not D0CS
++ @
++ ldr r3, [r0, #CLKMGR_ACSR_OFFSET] @ load ACSR
++ tst r3, #0x04000000
++ beq 6f
++0:
++ @
++ @ set DMEMC.MDCFG[29]
++ @
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET] @ get MDCNFG
++ orr r3, r3, #0x20000000 @ Set DMEMC.MDCNFG[29].
++ str r3, [r1, #DMEMC_MDCNFG_OFFSET] @ load MDCNFG
++1:
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET] @ ensure DMEMC.MDCNFG[29] bit is written
++ tst r3, #0x20000000
++ beq 1b
++
++ @
++ @ set DMEMC.DDR_HCAL[31]
++ @
++ ldr r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ get DDR_HCAL
++ orr r3, r3, #0x80000000 @ Insure DDR_HCAL[31] is set
++ str r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ load DDR_HCAL
++2:
++ ldr r3, [r1, #DMEMC_DDRHCAL_OFFSET] @ Insure DDR_HCAL[31] is set
++ tst r3, #0x80000000
++ beq 2b
++
++ @
++ @ clear ACCR[D0CS] bit
++ @
++ ldr r3, [r0, #CLKMGR_ACCR_OFFSET] @ get ACCR
++ bic r3, r3, #0x04000000 @ clear D0CS bit in ACCR
++ str r3, [r0, #CLKMGR_ACCR_OFFSET] @ load ACCR
++3:
++ ldr r3, [r0, #CLKMGR_ACCR_OFFSET] @ ensure D0CS bit is clear
++ tst r3, #0x04000000
++ bne 3b
++
++ @
++ @ exit D0CS mode
++ @
++ mov r4, #5 @ r4: power mode
++ b exitd0cs @ skip the garbage before .align 5
++ .align 5
++exitd0cs:
++ mcr p14, 0, r4, c7, c0, 0 @ exit D0CS mode
++4: @ wait for system to exit D0CS really
++ ldr r3, [r0, #CLKMGR_ACSR_OFFSET] @ load ACSR
++ tst r3, #0x04000000
++ bne 4b
++5: @ wait for DMEMC.MDCNFG[29] clear
++ ldr r3, [r1, #DMEMC_MDCNFG_OFFSET]
++ tst r3, #0x20000000
++ bne 5b
++6:
++ @
++ @ return
++ @
++ mov r0, #0
++ ldmfd sp!, {r3,r4,pc} @ return
++
++@
++@ UINT32 pxa_clkcfg_read
++@
++@ Description:
++@ This routine reads the designated PMU register via CoProcesser 14.
++@
++@ Input Parameters:
++@
++@ Returns:
++@ r0 - clkcfg value
++@
++@ Registers Modified:
++@ CoProcessor Register Modified: None
++@ General Purpose Registers Modified: None
++@
++@
++
++pxa_clkcfg_read:
++ mrc p14, 0, r0, c6, c0, 0 @ Read clkcfg
++ bx lr @ return
++
++
++
++@
++@ void pxa_clkcfg_write
++@
++@ Description:
++@ This routine writes to the designated ClkCFG register via CoProcesser 14.
++@
++@ Input Parameters:
++@ r0 - arg1 - Value to write to ClkCFG register
++@
++
++@ Returns:
++@ None
++@
++@ Registers Modified:
++@ CoProcessor Register Modified: ClkCFG Register
++@ General Purpose Registers Modified: None
++@
++@ NOTE
++@ Error checking not included
++@
++
++pxa_clkcfg_write:
++ mcr p14, 0, r0, c6, c0, 0 @ Write ClkCFG
++ bx lr @ return
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/pxa3xx_pmic.c kernel/arch/arm/mach-pxa/pxa3xx_pmic.c
+--- linux-2.6.32/arch/arm/mach-pxa/pxa3xx_pmic.c 2009-12-13 13:00:47.651947246 +0200
++++ kernel/arch/arm/mach-pxa/pxa3xx_pmic.c 2009-12-12 16:09:26.482948915 +0200
+@@ -0,0 +1,394 @@
++/*
++ * Monahans PMIC abstrction layer
++ *
++ * This software program is licensed subject to the GNU General Public License
++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
++
++ * (C) Copyright 2007 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#include <mach/pxa3xx_pmic.h>
++
++#include <mach/mfp.h>
++static struct pmic_ops *pxa3xx_pmic_ops;
++
++#ifdef DEBUG
++/* calculate the elapsed time on operating PMIC */
++static unsigned int start_time, end_time;
++void start_calc_time(void)
++{
++ start_time = OSCR;
++}
++
++void end_calc_time(void)
++{
++ unsigned int time;
++ end_time = OSCR
++ time = (end_time - start_time) * 100 / 325;
++
++ pr_debug("\n%s:\t:%dus\n", __func__, time);
++}
++#else
++void start_calc_time(void) {}
++void end_calc_time(void) {}
++#endif
++
++void pmic_set_ops(struct pmic_ops *ops)
++{
++ printk("pmic_set_ops:%x\n", ops);
++ if (pxa3xx_pmic_ops != NULL) {
++ printk(KERN_ERR "set pmic_ops when pmic_ops is not NULL\n");
++ return;
++ }
++ pxa3xx_pmic_ops = ops;
++ INIT_LIST_HEAD(&pxa3xx_pmic_ops->list);
++ spin_lock_init(&pxa3xx_pmic_ops->cb_lock);
++}
++
++/*****************************************************************************
++ * Operation of PMIC *
++ *****************************************************************************/
++int check_pmic_ops(void)
++{
++ if (!pxa3xx_pmic_ops) {
++ printk(KERN_WARNING "No pmic_ops registered!\n");
++ return -EINVAL;
++ } else
++ return 0;
++}
++
++int pxa3xx_pmic_get_voltage(int cmd, int *pval)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->get_voltage)
++ return pxa3xx_pmic_ops->get_voltage(cmd, pval);
++ else
++ return -EINVAL;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_get_voltage);
++
++int pxa3xx_pmic_set_voltage(int cmd, int val)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->set_voltage)
++ return pxa3xx_pmic_ops->set_voltage(cmd, val);
++ else
++ return -EINVAL;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_set_voltage);
++
++int pxa3xx_pmic_check_voltage(int cmd)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->check_voltage)
++ return pxa3xx_pmic_ops->check_voltage(cmd);
++ else
++ return -EINVAL;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_check_voltage);
++
++int pxa3xx_pmic_enable_voltage(int cmd, int enable)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->enable_voltage)
++ return pxa3xx_pmic_ops->enable_voltage(cmd, enable);
++ else
++ return -EINVAL;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_enable_voltage);
++
++int pxa3xx_pmic_enable_led(int cmd, int enable)
++{
++ int ret;
++
++ ret=check_pmic_ops();
++ if (ret > 0)
++ return ret;
++
++ if(pxa3xx_pmic_ops->enable_led)
++ return pxa3xx_pmic_ops->enable_led(cmd, enable);
++ else
++ return -EINVAL;
++}
++
++EXPORT_SYMBOL(pxa3xx_pmic_enable_led);
++
++int pxa3xx_pmic_is_vbus_assert(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0) /* If illegal pmic_ops, always no vbus activity */
++ return 0;
++
++ if (pxa3xx_pmic_ops->is_vbus_assert)
++ return pxa3xx_pmic_ops->is_vbus_assert();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_vbus_assert);
++
++int pxa3xx_pmic_is_avbusvld(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0) /* If illegal pmic_ops, always no A vbus valid */
++ return 0;
++
++ if (pxa3xx_pmic_ops->is_avbusvld)
++ return pxa3xx_pmic_ops->is_avbusvld();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_avbusvld);
++
++int pxa3xx_pmic_is_asessvld(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0) /* If illegal pmic_ops, always no A assert valid */
++ return 0;
++
++ if (pxa3xx_pmic_ops->is_asessvld)
++ return pxa3xx_pmic_ops->is_asessvld();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_asessvld);
++
++int pxa3xx_pmic_is_bsessvld(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0) /* If illegal pmic_ops, always no B assert valid */
++ return 0;
++
++ if (pxa3xx_pmic_ops->is_bsessvld)
++ return pxa3xx_pmic_ops->is_bsessvld();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_bsessvld);
++
++int pxa3xx_pmic_is_srp_ready(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0) /* If illegal pmic_ops, always no SRP detect */
++ return 0;
++
++ if (pxa3xx_pmic_ops->is_srp_ready)
++ return pxa3xx_pmic_ops->is_srp_ready();
++
++ return 0;
++
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_srp_ready);
++
++int pxa3xx_pmic_set_pump(int enable)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->set_pump)
++ return pxa3xx_pmic_ops->set_pump(enable);
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_set_pump);
++
++int pxa3xx_pmic_set_vbus_supply(int enable, int srp)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->set_vbus_supply)
++ return pxa3xx_pmic_ops->set_vbus_supply(enable, srp);
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_set_vbus_supply);
++
++int pxa3xx_pmic_set_usbotg_a_mask(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->set_usbotg_a_mask)
++ return pxa3xx_pmic_ops->set_usbotg_a_mask();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_set_usbotg_a_mask);
++
++int pxa3xx_pmic_set_usbotg_b_mask(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->set_usbotg_b_mask)
++ return pxa3xx_pmic_ops->set_usbotg_b_mask();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_set_usbotg_b_mask);
++
++int pxa3xx_pmic_is_onkey_assert(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->is_onkey_assert)
++ return pxa3xx_pmic_ops->is_onkey_assert();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_onkey_assert);
++
++/* Register pmic callback */
++int pmic_callback_register(unsigned long event,
++ void (*func)(unsigned long event))
++{
++ int ret;
++ unsigned long flags;
++ struct pmic_callback *pmic_cb;
++
++ might_sleep();
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ pmic_cb = kzalloc(sizeof(*pmic_cb), GFP_KERNEL);
++ if (!pmic_cb)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&pmic_cb->list);
++ pmic_cb->event = event;
++ pmic_cb->func = func;
++
++ spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags);
++ list_add(&pmic_cb->list, &pxa3xx_pmic_ops->list);
++ spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags);
++
++ return 0;
++}
++EXPORT_SYMBOL(pmic_callback_register);
++
++/* Unregister pmic callback */
++int pmic_callback_unregister(unsigned long event,
++ void (*func)(unsigned long event))
++{
++ unsigned long flags;
++ struct pmic_callback *pmic_cb, *next;
++
++ spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags);
++ list_for_each_entry_safe(pmic_cb, next, &pxa3xx_pmic_ops->list, list) {
++ if ((pmic_cb->event == event) && (pmic_cb->func == func)) {
++ list_del_init(&pmic_cb->list);
++ kfree(pmic_cb);
++ }
++ }
++ spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags);
++ return 0;
++}
++EXPORT_SYMBOL(pmic_callback_unregister);
++
++int pmic_event_handle(unsigned long event)
++{
++ int ret;
++ unsigned long flags;
++ struct pmic_callback *pmic_cb;
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags);
++ list_for_each_entry(pmic_cb, &pxa3xx_pmic_ops->list, list) {
++ spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags);
++ /* event is bit-wise parameter, need bit AND here as filter */
++ if ((pmic_cb->event & event) && (pmic_cb->func))
++ pmic_cb->func(event);
++ spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags);
++ }
++ spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags);
++ return 0;
++}
++EXPORT_SYMBOL(pmic_event_handle);
++
++
++int px3xx_pmic_event_enable(unsigned long event, int enable)
++{
++ int ret;
++ u8 val;
++ unsigned long flags;
++ struct pmic_callback *pmic_cb;
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++ printk("pxa pmic event enable 11\n");
++ if(pxa3xx_pmic_ops->enable_event)
++ {
++ printk("pxa pmic event enable 22\n");
++ return pxa3xx_pmic_ops->enable_event(event, enable);
++ }
++ else
++ return -EINVAL;
++}
++EXPORT_SYMBOL(px3xx_pmic_event_enable);
++
++int pxa3xx_pmic_is_hookswitch_assert(void)
++{
++ int ret;
++
++ ret = check_pmic_ops();
++ if (ret < 0)
++ return ret;
++
++ if (pxa3xx_pmic_ops->is_hookswitch_assert)
++ return pxa3xx_pmic_ops->is_hookswitch_assert();
++
++ return 0;
++}
++EXPORT_SYMBOL(pxa3xx_pmic_is_hookswitch_assert);
++
+diff -ur linux-2.6.32/arch/arm/mach-pxa/sgh_i780_i900.c kernel/arch/arm/mach-pxa/sgh_i780_i900.c
+--- linux-2.6.32/arch/arm/mach-pxa/sgh_i780_i900.c 2009-12-13 13:00:53.329024629 +0200
++++ kernel/arch/arm/mach-pxa/sgh_i780_i900.c 2009-12-12 16:09:26.486282481 +0200
+@@ -0,0 +1,618 @@
++/**
++ * Support for the PXA311 and PXA312 based Samsung SGH devices
++ * m480, i780, i900, i904, i908, i910
++ *
++ * Copyright (C) 2009 Sacha Refshauge <xsacha@gmail.com>
++ * Copyright (C) 2009 Stefan Schmidt <stefan@datenfreihafen.org>
++ * Copyright (C) 2009 Mustafa Ozsakalli <ozsakalli@hotmail.com>
++ *
++ * Based on zylonite.c Copyright (C) 2006 Marvell International Ltd.
++ *
++ * 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/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/gpio.h>
++#include <linux/pwm_backlight.h>
++#include <linux/power_supply.h>
++#include <linux/pda_power.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/libertas_spi.h>
++#include <../drivers/staging/android/timed_gpio.h>
++
++#include <plat/i2c.h>
++#include <asm/mach-types.h>
++#include <asm/mach/arch.h>
++#include <mach/hardware.h>
++#include <mach/pxafb.h>
++#include <mach/audio.h>
++#include <mach/mmc.h>
++#include <mach/udc.h>
++#include <mach/ohci.h>
++#include <mach/pxa27x-udc.h>
++#include <mach/pxa27x_keypad.h>
++#include <mach/pxa2xx_spi.h>
++#include <mach/pxa3xx-regs.h>
++#include <mach/mfp-pxa300.h>
++#if defined(CONFIG_PXA_DVFM)
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_dvfm.h>
++#include <mach/pmu.h>
++#endif
++
++#include <mach/sgh_msm6k.h>
++
++#include "devices.h"
++#include "generic.h"
++
++#define SGH_BATT_I2C_SLAVE_ADDRESS 0x34
++
++#define GPIO09_SGH_LED_GREEN 9
++#define GPIO23_SGH_TOUCHSCREEN 23
++#define GPIO71_SGH_LED_BLUE 71
++#define GPIO79_SGH_LED_VIBRATE 79
++#define GPIO88_SGH_BATT_CHARGE 88
++#define GPIO104_SGH_WIFI_CMD 104
++#define GPIO105_SGH_CARD_DETECT 105
++
++#define GPIO18_SGH_I780_WIFI_CMD 11
++#define GPIO19_SGH_I780_SPK_AUDIO 19
++#define GPIO48_SGH_I780_LED_BACKLIGHT 48
++#define GPIO75_SGH_I780_LED_RED 75
++#define GPIO94_SGH_I780_WIFI_POWER 94
++
++#define GPIO03_SGH_I900_WIFI_POWER 3
++#define GPIO17_SGH_I900_SPK_AUDIO 17
++#define GPIO48_SGH_I900_LED_RED 48
++#define GPIO76_SGH_I900_BT_POWER 76
++#define GPIO118_SGH_I900_WIFI_CMD 118
++
++#define GPIO16_SGH_SPI_CHIP_SEL 16
++
++#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
++static struct gpio_led sgh_leds[] = {
++ [0] = {
++ .name = "red",
++ },
++ [1] = {
++ .name = "green",
++ .default_trigger = "mmc0",
++ .gpio = GPIO09_SGH_LED_GREEN,
++ },
++ [2] = {
++ .name = "blue",
++ .default_trigger = "mmc0",
++ .gpio = GPIO71_SGH_LED_BLUE,
++ },
++ [3] = {
++ .name = "keyboard",
++ },
++};
++
++static struct gpio_led_platform_data sgh_leds_info = {
++ .leds = sgh_leds,
++ .num_leds = ARRAY_SIZE(sgh_leds),
++};
++
++static struct platform_device sgh_device_leds = {
++ .name = "leds-gpio",
++ .id = -1,
++ .dev = {
++ .platform_data = &sgh_leds_info,
++ }
++};
++
++static struct timed_gpio sgh_vibrator = {
++ .name = "vibrator",
++ .gpio = GPIO79_SGH_LED_VIBRATE,
++ .max_timeout = 1000,
++};
++
++static struct timed_gpio_platform_data sgh_vibrator_info = {
++ .gpios = &sgh_vibrator,
++ .num_gpios = 1,
++};
++
++static struct platform_device sgh_device_vibrator = {
++ .name = "timed-gpio",
++ .id = -1,
++ .dev = {
++ .platform_data = &sgh_vibrator_info,
++ }
++};
++
++static void __init sgh_init_leds(void)
++{
++
++ sgh_leds[0].gpio = (machine_is_sgh_i780()) ? GPIO75_SGH_I780_LED_RED : GPIO48_SGH_I900_LED_RED;
++ if(machine_is_sgh_i780())
++ sgh_leds[3].gpio = GPIO48_SGH_I780_LED_BACKLIGHT;
++
++ platform_device_register(&sgh_device_leds);
++ //timed_gpio doesnt request gpio
++ gpio_request(GPIO79_SGH_LED_VIBRATE, "SGH-VIBRATOR");
++ platform_device_register(&sgh_device_vibrator);
++
++}
++#else
++static inline void sgh_init_leds(void) {}
++#endif
++
++#if defined(CONFIG_FB_PXA) || defined(CONFIG_FB_PXA_MODULE)
++static struct platform_pwm_backlight_data sgh_backlight_data = {
++ .pwm_id = 3,
++ .max_brightness = 100,
++ .dft_brightness = 100,
++ .pwm_period_ns = 10000,
++};
++
++static struct platform_device sgh_backlight_device = {
++ .name = "backlight",
++ .dev = {
++ .parent = &pxa27x_device_pwm1.dev,
++ .platform_data = &sgh_backlight_data,
++ },
++};
++/* Pixclock Calculation
++ Calculated from reviewing HaRET source: http://xanadux.cvs.sourceforge.net/viewvc/xanadux/haret/haret-gnu/src/script.cpp?view=markup
++ pixclock = K * 8MHz / CLK ; where CLK is 312MHz and K is last 8 bits of lccr3
++
++ New: pixclock = (K * 200000000) / 15600
++*/
++static struct pxafb_mode_info sgh_i780_mode = {
++ .pixclock = 243600, // K = 19
++ .xres = 320, // HACK: Android does not like square resolutions
++ .yres = 319,
++ .bpp = 16,
++ .hsync_len = 16,
++ .left_margin = 24,
++ .right_margin = 24,
++ .vsync_len = 2,
++ .upper_margin = 3,
++ .lower_margin = 0,
++ .sync = 0,
++};
++static struct pxafb_mode_info sgh_i900_mode = {
++ .pixclock = 256500, // K = 20
++ .xres = 240,
++ .yres = 400,
++ .bpp = 16,
++ .hsync_len = 8,
++ .left_margin = 8,
++ .right_margin = 8,
++ .vsync_len = 4,
++ .upper_margin = 38,
++ .lower_margin = 38,
++ .sync = 0, //FB_SYNC_VERT_HIGH_ACT,
++};
++
++static struct pxafb_mach_info sgh_lcd_info = {
++ .num_modes = 1,
++ .lcd_conn = LCD_COLOR_TFT_16BPP | LCD_PCLK_EDGE_FALL,
++};
++
++static void __init sgh_init_lcd(void)
++{
++ platform_device_register(&sgh_backlight_device);
++ sgh_lcd_info.modes = (machine_is_sgh_i780()) ? &sgh_i780_mode : &sgh_i900_mode;
++ set_pxa_fb_info(&sgh_lcd_info);
++}
++#else
++static inline void sgh_init_lcd(void) {}
++#endif
++
++/****************************
++* Keypad *
++****************************/
++
++/*Android (i.e. non-linux) keys:
++Name: defined as: function:
++KEY_SEND 231 Send key
++KEY_END 107 End key
++KEY_BACK 158 Go back a page
++KEY_MENU 139 Open a special menu
++KEY_HOME 102 Return to the home screen
++KEY_SEARCH 217 Open the Android search
++KEY_VOLUMEUP 115 Increase volume
++KEY_VOLUMEDOWN 114 Decrease volume
++KEY_CAMERA 212 Opens camera
++KEY_CAMERAFOCUS 211 Focuses camera (Omnia only, replaces KEY_HP in kernel/include/linux/input.h)
++*/
++
++#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULE)
++/* KEY(row, col, key_code) */
++static unsigned int sgh_i780_matrix_key_map[] = {
++/* QWERTY Keyboard */
++/* 1st row */
++KEY(0, 0, KEY_Q), KEY(7, 1, KEY_W), KEY(2, 0, KEY_E), KEY(3, 0, KEY_R), KEY(4, 0, KEY_T),
++KEY(0, 4, KEY_Y), KEY(1, 4, KEY_U), KEY(2, 4, KEY_I), KEY(3, 4, KEY_O), KEY(4, 4, KEY_P),
++/* 2nd row */
++KEY(0, 1, KEY_A), KEY(7, 2, KEY_S), KEY(2, 1, KEY_D), KEY(3, 1, KEY_F), KEY(4, 1, KEY_G),
++KEY(0, 5, KEY_H), KEY(1, 5, KEY_J), KEY(2, 5, KEY_K), KEY(3, 5, KEY_L), KEY(4, 5, KEY_BACKSPACE),
++/* 3rd row */
++KEY(0, 2, KEY_LEFTALT), KEY(1, 2, KEY_Z), KEY(2, 2, KEY_X), KEY(3, 2, KEY_C), KEY(4, 2, KEY_V),
++KEY(0, 6, KEY_B), KEY(1, 6, KEY_N), KEY(2, 6, KEY_M), KEY(3, 6, KEY_DOT), KEY(4, 6, KEY_ENTER),
++/* 4th row */
++KEY(0, 3, KEY_LEFTSHIFT), KEY(1, 3, KEY_RIGHTALT), KEY(2, 3, KEY_0), KEY(3, 3, KEY_SPACE),
++KEY(4, 3, KEY_COMMA), KEY(7, 6, KEY_SLASH), /* Message */ KEY(5, 1, KEY_TAB), /* GPS */
++
++/* Volume Keys */
++KEY(1, 0, KEY_VOLUMEUP),
++KEY(1, 1, KEY_VOLUMEDOWN),
++
++/* Left Softkey */ /* Windows Key */ /* OK */ /* Right Softkey */
++KEY(5, 4, KEY_MINUS), KEY(5, 2, KEY_MENU), KEY(5, 3, KEY_EXIT), KEY(5, 6, KEY_F2),
++KEY(5, 5, KEY_SEND), KEY(6, 4, KEY_REPLY), KEY(7, 0, KEY_END),
++/* Green Key */ /* Center */ /* Red Key */
++
++/* Camera */
++KEY(7, 3, KEY_CAMERA),
++};
++
++static unsigned int sgh_i900_matrix_key_map[] = {
++ /* KEY(row, col, key_code) */
++ KEY(0, 0, KEY_CAMERAFOCUS), //Camera half-press
++ KEY(0, 1, KEY_CAMERA), //Camera full-press
++ KEY(0, 2, KEY_ENTER), //Center optical dpad button
++ KEY(1, 0, KEY_VOLUMEUP), //Volume up
++ KEY(1, 1, KEY_VOLUMEDOWN), //Volume down
++ KEY(1, 2, KEY_SEND), //Send key
++ KEY(2, 0, KEY_MENU), //Top right key (Main Menu button)
++ KEY(2, 1, KEY_END), //???
++ KEY(2, 2, KEY_BACK), //End key (Back button)
++
++};
++
++static struct pxa27x_keypad_platform_data sgh_keypad_info = {
++ .enable_rotary0 = 0,
++
++ .debounce_interval = 30,
++};
++
++static void __init sgh_init_keypad(void)
++{
++ if(machine_is_sgh_i780())
++ {
++ sgh_keypad_info.matrix_key_rows = 8;
++ sgh_keypad_info.matrix_key_cols = 7;
++ sgh_keypad_info.matrix_key_map = sgh_i780_matrix_key_map;
++ sgh_keypad_info.matrix_key_map_size = ARRAY_SIZE(sgh_i780_matrix_key_map);
++ }
++ else
++ {
++ sgh_keypad_info.matrix_key_rows = 3;
++ sgh_keypad_info.matrix_key_cols = 3;
++ sgh_keypad_info.matrix_key_map = sgh_i900_matrix_key_map;
++ sgh_keypad_info.matrix_key_map_size = ARRAY_SIZE(sgh_i900_matrix_key_map);
++ }
++
++ pxa_set_keypad_info(&sgh_keypad_info);
++}
++#else
++static inline void sgh_init_keypad(void) {}
++#endif
++
++#if defined(CONFIG_MMC)
++static int sgh_mci_sdcard_init(struct device *dev,
++ irq_handler_t sgh_detect_int,
++ void *data)
++{
++ int err, cd_irq;
++ int gpio_cd = GPIO105_SGH_CARD_DETECT;
++
++ cd_irq = gpio_to_irq(gpio_cd);
++
++ /*
++ * setup GPIO for MMC controller
++ */
++ err = gpio_request(gpio_cd, "microSD card detect");
++ if (err)
++ goto err_request_cd;
++ gpio_direction_input(gpio_cd);
++
++ err = request_irq(cd_irq, sgh_detect_int,
++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "microSD card detect", data);
++ if (err) {
++ printk(KERN_ERR "%s: MicroSD: "
++ "can't request card detect IRQ\n", __func__);
++ goto err_request_cd;
++ }
++
++ return 0;
++
++err_request_cd:
++ return err;
++}
++
++static void sgh_mci_sdcard_exit(struct device *dev, void *data)
++{
++ int cd_irq, gpio_cd;
++
++ cd_irq = gpio_to_irq(105);
++ gpio_cd = 105;
++
++ free_irq(cd_irq, data);
++ gpio_free(gpio_cd);
++}
++
++static struct pxamci_platform_data sgh_mci_sdcard_platform_data = {
++ .detect_delay = 20,
++ .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
++ .init = sgh_mci_sdcard_init,
++ .exit = sgh_mci_sdcard_exit,
++ .gpio_card_detect = -1,
++ .gpio_card_ro = -1,
++ .gpio_power = -1,
++};
++
++
++static void __init sgh_init_mmc(void)
++{
++ pxa_set_mci_info(&sgh_mci_sdcard_platform_data); // External MicroSD
++ if(machine_is_sgh_i900())
++ pxa3xx_set_mci2_info(&sgh_mci_sdcard_platform_data); // Internal MicroSD
++}
++#else
++static inline void sgh_init_mmc(void) {}
++#endif
++static void sgh_udc_command(int cmd)
++{
++ switch (cmd) {
++ case PXA2XX_UDC_CMD_CONNECT:
++ //UP2OCR |= UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE;
++ UP2OCR |= 0xf024; // USB Port 2 Output Control Register
++ break;
++ case PXA2XX_UDC_CMD_DISCONNECT:
++ //UP2OCR &= ~(UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE);
++ UP2OCR &= 0xf024;
++ break;
++ }
++}
++static struct pxa2xx_udc_mach_info sgh_udc_info __initdata = {
++ .udc_command = sgh_udc_command,
++};
++ /* WinMo: UHCHR_SSEP2 | UHCHR_SSEP1 | UHCHR_SSE | UHCHR_CGR | UHCHR_FHR
++ Set the Power Control Polarity Low */
++/* UHCHR = (UHCHR | UHCHR_PCPL) &
++ ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE);
++*/
++static int sgh_init_udc(void)
++{
++ pxa_set_udc_info(&sgh_udc_info);
++ return 0;
++}
++
++#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
++static int sgh_ohci_init(struct device *dev)
++{
++ return 0;
++}
++static struct pxaohci_platform_data sgh_ohci_platform_data = {
++ .port_mode = PMM_PERPORT_MODE,
++ .init = sgh_ohci_init
++};
++
++static void __init sgh_init_ohci(void)
++{
++ pxa_set_ohci_info(&sgh_ohci_platform_data);
++}
++#else
++static inline void sgh_init_ohci(void) {}
++#endif /* CONFIG_USB_OHCI_HCD || CONFIG_USB_OHCI_HCD_MODULE */
++
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++static struct i2c_board_info __initdata sgh_i2c_board_info[] = {
++ { /* PM6558 Battery */
++ .type = "sgh_battery",
++ .addr = SGH_BATT_I2C_SLAVE_ADDRESS,
++ },
++};
++static void __init sgh_init_i2c(void)
++{
++ i2c_register_board_info(0, sgh_i2c_board_info,
++ ARRAY_SIZE(sgh_i2c_board_info));
++ pxa_set_i2c_info(NULL);
++}
++#else
++static inline void sgh_init_i2c(void) {}
++#endif
++
++#if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MASTER)
++static void sgh_spi_wifi_cs(u32 command)
++{
++ gpio_set_value(GPIO16_SGH_SPI_CHIP_SEL, !(command == PXA2XX_CS_ASSERT));
++}
++
++static int sgh_libertas_setup(struct spi_device *spi)
++{
++ int WifiPwr = 0;
++ int WifiCmd = 0;
++ if(machine_is_sgh_i780())
++ {
++ WifiPwr = GPIO94_SGH_I780_WIFI_POWER;
++ WifiCmd = GPIO18_SGH_I780_WIFI_CMD;
++ }
++ else if(machine_is_sgh_i900())
++ {
++ WifiPwr = GPIO03_SGH_I900_WIFI_POWER;
++ WifiCmd = GPIO118_SGH_I900_WIFI_CMD;
++ }
++ gpio_request(WifiPwr,"WLAN");
++ gpio_request(0x10,"WLAN");
++ gpio_request(0x68,"WLAN");
++ gpio_request(WifiCmd,"WLAN");
++
++ //pxa_init_hw
++ gpio_direction_output(0x68,1);
++ gpio_direction_output(WifiCmd,1);
++ gpio_direction_output(WifiPwr,1);
++ gpio_direction_output(0x10,1);
++ mdelay(60);
++
++ gpio_set_value(WifiPwr,1);
++ mdelay(60);
++ gpio_set_value(WifiCmd,1);
++ gpio_set_value(0x10,1);
++ gpio_set_value(0x68,1);
++ mdelay(60);
++
++
++ //gspx_power_up
++ gpio_set_value(WifiPwr,1);
++ mdelay(60);
++ gpio_set_value(WifiCmd,1);
++ gpio_set_value(0x68,1);
++ mdelay(150);
++
++ //gspx_reset_module
++ gpio_set_value(0x68,1);
++ mdelay(60);
++ gpio_set_value(0x68,0);
++ mdelay(60);
++ gpio_set_value(0x68,1);
++ mdelay(100);
++
++ spi->bits_per_word = 16;
++ spi_setup(spi);
++
++ return 0;
++}
++
++static struct pxa2xx_spi_chip sgh_wifi_chip = {
++ .rx_threshold = 8,
++ .tx_threshold = 8,
++ .timeout = 235,
++ .dma_burst_size = 16,
++ .cs_control = sgh_spi_wifi_cs,
++};
++
++static struct pxa2xx_spi_master sgh_spi_info = {
++ .clock_enable = CKEN_SSP1,
++ .num_chipselect = 1,
++ .enable_dma = 1,
++};
++
++struct libertas_spi_platform_data sgh_wifi_pdata = {
++ .use_dummy_writes = 0,
++ .setup = sgh_libertas_setup,
++};
++
++static struct spi_board_info sgh_spi_devices[] __initdata = {
++ { //wireless
++ .modalias = "libertas_spi",
++ .max_speed_hz = 13000000,
++ .bus_num = 1,
++ .irq = IRQ_GPIO(8),
++ .chip_select = 0,
++ .controller_data = &sgh_wifi_chip,
++ .platform_data = &sgh_wifi_pdata,
++ },
++};
++
++static void __init sgh_init_spi(void)
++{
++ sgh_spi_devices[0].irq = IRQ_GPIO(machine_is_sgh_i780() ? 11 : 8);
++ pxa2xx_set_spi_info(1, &sgh_spi_info);
++ spi_register_board_info(ARRAY_AND_SIZE(sgh_spi_devices));
++}
++#else
++static inline void sgh_init_spi(void){}
++#endif
++
++#if defined(CONFIG_PXA_DVFM)
++struct pxa3xx_freq_mach_info sgh_freq_mach_info = {
++ .flags = 0,
++};
++
++static void __init sgh_init_dvfm() {
++ set_pxa3xx_freq_info(&sgh_freq_mach_info);
++ pxa3xx_set_pmu_info(NULL);
++}
++#else
++static inline void sgh_init_dvfm(void){}
++#endif
++
++static mfp_cfg_t sgh_mfp_cfg[] __initdata = {
++ /* AC97 */
++ //GPIO23_AC97_nACRESET,
++ GPIO25_AC97_SDATA_IN_0,
++ GPIO27_AC97_SDATA_OUT,
++ GPIO28_AC97_SYNC,
++ GPIO29_AC97_BITCLK,
++
++ /* KEYPAD */
++ GPIO115_KP_MKIN_0 | MFP_LPM_EDGE_BOTH,
++ GPIO116_KP_MKIN_1 | MFP_LPM_EDGE_BOTH,
++ GPIO117_KP_MKIN_2 | MFP_LPM_EDGE_BOTH,
++ GPIO121_KP_MKOUT_0,
++ GPIO122_KP_MKOUT_1,
++ GPIO123_KP_MKOUT_2,
++ GPIO124_KP_MKOUT_3,
++
++};
++
++static struct platform_device sgh_audio = {
++ .name = "sgh-asoc",
++ .id = -1,
++};
++
++static struct platform_device *devices[] __initdata = {
++ &sgh_audio,
++};
++
++
++static void __init sgh_init(void)
++{
++ static int dvfm = 0;
++
++ pxa3xx_mfp_config(ARRAY_AND_SIZE(sgh_mfp_cfg));
++ sgh_init_dvfm();
++
++ rpc_init();
++
++ sgh_init_lcd();
++ sgh_init_mmc();
++ sgh_init_leds();
++ sgh_init_keypad();
++
++ pxa_set_ac97_info(NULL);
++ platform_add_devices(devices, ARRAY_SIZE(devices));
++
++ sgh_init_ohci();
++ sgh_init_udc();
++ sgh_init_i2c();
++ sgh_init_spi();
++ /*
++ dvfm_register("Test", &dvfm);
++ dvfm_disable_op_name("D1", dvfm);
++ dvfm_disable_op_name("D2", dvfm);
++ */
++}
++
++MACHINE_START(SGH_I780, "Samsung SGH-i780 (Mirage) phone")
++ .phys_io = 0x40000000,
++ .boot_params = 0xa0000100,
++ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
++ .map_io = pxa_map_io,
++ .init_irq = pxa3xx_init_irq,
++ .timer = &pxa_timer,
++ .init_machine = sgh_init,
++MACHINE_END
++
++MACHINE_START(SGH_I900, "Samsung SGH-i900 (Omnia) phone")
++ .phys_io = 0x40000000,
++ .boot_params = 0xa0000100,
++ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
++ .map_io = pxa_map_io,
++ .init_irq = pxa3xx_init_irq,
++ .timer = &pxa_timer,
++ .init_machine = sgh_init,
++MACHINE_END
+diff -ur linux-2.6.32/arch/arm/mach-pxa/sgh_rpc.c kernel/arch/arm/mach-pxa/sgh_rpc.c
+--- linux-2.6.32/arch/arm/mach-pxa/sgh_rpc.c 2009-12-13 13:00:59.168618858 +0200
++++ kernel/arch/arm/mach-pxa/sgh_rpc.c 2009-12-12 16:09:26.486282481 +0200
+@@ -0,0 +1,333 @@
++/**
++ * Samsung SGH I900 RPC Driver for MSM6K
++ *
++ * Copyright (C) 2009 Mustafa Ozsakalli <ozsakalli@hotmail.com>
++ *
++ * 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/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/miscdevice.h>
++#include <linux/io.h>
++#include <linux/platform_device.h>
++#include <linux/gpio.h>
++#include <linux/delay.h>
++#include <linux/workqueue.h>
++#include <linux/completion.h>
++#include <linux/list.h>
++#include <linux/fs.h>
++#include <linux/cdev.h>
++
++#include <asm/uaccess.h>
++
++#include <mach/hardware.h>
++#include <mach/sgh_msm6k.h>
++
++
++#include "devices.h"
++
++static DEFINE_SPINLOCK(msgs_lock);
++static DEFINE_SPINLOCK(crc_lock);
++
++static void do_read_data(struct work_struct *work);
++static DECLARE_WORK(work_read, do_read_data);
++static struct workqueue_struct *workqueue;
++
++struct class *sgh_rpc_class;
++dev_t sgh_rpc_devno;
++
++static struct cdev rpc_cdev;
++static struct device *rpc_device;
++
++struct __rpc_msg {
++ int command;
++ int type;
++ int index;
++ int len;
++ int crc;
++ char *data;
++
++ struct __rpc_msg *next;
++};
++
++static struct __rpc_msg *msgs_head = NULL;
++static struct __rpc_msg *msgs_tail = NULL;
++
++static void *rpc_malloc(unsigned sz) {
++ void *ptr = kmalloc(sz, GFP_KERNEL);
++
++ if(ptr)
++ return ptr;
++
++ printk(KERN_ERR "sgh_rpc: kmalloc of %d failed, retrying...\n", sz);
++
++ do {
++ ptr = kmalloc(sz, GFP_KERNEL);
++ } while (!ptr);
++
++ return ptr;
++}
++
++static void do_read_data(struct work_struct *work) {
++ struct __rpc_msg *msg;
++ int pktlen, rpclen;
++ unsigned char end_flag;
++ char buf[11];
++ unsigned long flags=0;
++
++ msg = (struct __rpc_msg *)rpc_malloc(sizeof(struct __rpc_msg));
++ msg->data = NULL;
++
++ if(smd_read(CH_RPC, buf,11) == 0) {
++ if(buf[0] != 0x7f) {
++ goto cleanup;
++ }
++
++ pktlen = (buf[2]<<8)|(buf[1]);
++ msg->crc = buf[3];
++ rpclen = (buf[5]<<8)|(buf[4]);
++ msg->len = rpclen - 7;
++ msg->index = (buf[7]<<8)|(buf[6]);
++ msg->command = (buf[8]<<8)|(buf[9]);
++ msg->type = buf[10];
++
++ if(msg->len > 0) {
++ msg->data = (char *)rpc_malloc(msg->len);
++ if(smd_read(CH_RPC, msg->data, msg->len) != 0) {
++ goto cleanup;
++ }
++ }
++
++ if(smd_read(CH_RPC, &end_flag,1)!=0 || end_flag!=0x7e){
++ goto cleanup;
++ }
++
++ spin_lock_irqsave(&msgs_lock, flags);
++ if(msgs_tail != NULL)
++ msgs_tail->next = msg;
++ msgs_tail = msg;
++ msgs_tail->next = NULL;
++ if(msgs_head == NULL)
++ msgs_head = msg;
++ spin_unlock_irqrestore(&msgs_lock, flags);
++
++ goto success;
++
++ }
++
++cleanup:
++ if(msg->data)
++ kfree(msg->data);
++
++ kfree(msg);
++
++success:
++ queue_work(workqueue, &work_read);
++}
++
++static int write_index = 0xff00;
++
++static char __crc;
++
++static char calc_crc() {
++ int64_t m;
++ int u, rc;
++
++ m = (__crc+1) * -2130574327; //0x81020409
++ u = m>>32;
++ u += __crc+1;
++ u >>= 6;
++
++ u += ((unsigned)u>>31);
++ u += ((unsigned)u<<7);
++ u = __crc+1 - u;
++
++ rc = __crc;
++ __crc = u;
++
++ return rc;
++}
++
++static int rpc_write(unsigned cmd, unsigned type,void *data, unsigned len) {
++ char *pkt;
++ char *p;
++ int crc;
++ unsigned long flags=0;
++ unsigned n;
++
++ pkt = rpc_malloc(len+11);
++ p = pkt;
++
++ n = len + 10;
++
++ *p++ = 0x7f;
++ *p++ = n & 0xff;
++ *p++ = (n>>8) & 0xff;
++ spin_lock_irqsave(&crc_lock, flags);
++ crc = calc_crc();
++ spin_unlock_irqrestore(&crc_lock, flags);
++ *p++ = crc;
++
++ n = len + 7;
++ *p++ = (n) & 0xff;
++ *p++ = (n>>8) & 0xff;
++ *p++ = write_index++ & 0xff; //index
++ *p++ = 0xff;
++ *p++ = (cmd>>8) & 0xff;
++ *p++ = cmd & 0xff;
++ *p++ = type & 0xff;
++
++ if(len > 0 && data != NULL) {
++ copy_from_user(p, data, len);
++ p += len;
++ }
++
++ *p++ = 0x7e;
++
++ smd_write(CH_RPC, pkt, (unsigned)(p - pkt));
++
++ kfree(pkt);
++
++ return len;
++}
++
++
++static int rpc_ops_open(struct inode *inode, struct file *filp) {
++ int rc;
++
++ rc = nonseekable_open(inode, filp);
++ if (rc < 0)
++ return rc;
++
++ return 0;
++}
++
++static int rpc_ops_release(struct inode *inode, struct file *filp) {
++ return 0;
++}
++
++static ssize_t rpc_ops_read(struct file *filp, char __user *buf,size_t count, loff_t *ppos) {
++ unsigned long flags = 0;
++ struct __rpc_msg *msg = NULL;
++ int len = 0;
++
++
++ msg = msgs_head;
++ if(msg == NULL) return -EIO;
++
++ spin_lock_irqsave(&msgs_lock, flags);
++ msgs_head = msgs_head->next;
++ if(msgs_head == NULL)
++ msgs_tail = NULL;
++ spin_unlock_irqrestore(&msgs_lock, flags);
++
++ if(msg->data != NULL && msg->len > 0) {
++ len = count > msg->len ? msg->len : count;
++ if(copy_to_user(buf, msg->data, len)!=0)
++ len = -EIO;
++ }
++ if(msg->data != NULL)
++ kfree(msg->data);
++ kfree(msg);
++
++ return len;
++}
++
++static ssize_t rpc_ops_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos) {
++ char h[6];
++ short *sh;
++
++ if(copy_from_user(h, buf, 6))
++ return 0;
++
++ buf += 6;
++
++ sh = (short *)h;
++
++ return rpc_write(sh[0], sh[1], sh[2]>0 ? buf : NULL, sh[2]);
++}
++
++static unsigned int rpc_ops_poll(struct file *filp, struct poll_table_struct *wait) {
++ unsigned mask = 0;
++
++ return mask;
++}
++
++static long rpc_ops_ioctl(struct file *filp, unsigned int cmd,unsigned long arg) {
++ struct __rpc_msg *msg;
++
++ msg = msgs_head;
++ if(msg == NULL)
++ return -EIO;
++
++ return copy_to_user((void *)arg, msg, 20);
++}
++
++static struct file_operations rpc_fops = {
++ .owner = THIS_MODULE,
++ .open = rpc_ops_open,
++ .release = rpc_ops_release,
++ .read = rpc_ops_read,
++ .write = rpc_ops_write,
++ //.poll = rpc_ops_poll,
++ .unlocked_ioctl = rpc_ops_ioctl,
++
++};
++
++
++void rpc_init(void) {
++ int rc;
++ int major;
++
++ smd_init();
++
++ /* Create the device nodes */
++ sgh_rpc_class = class_create(THIS_MODULE, "sghrpc");
++ if (IS_ERR(sgh_rpc_class)) {
++ rc = -ENOMEM;
++ printk(KERN_ERR
++ "sgh_rpc: failed to create sghrpc class\n");
++ return;
++ }
++
++ rc = alloc_chrdev_region(&sgh_rpc_devno, 0, 1, "sghrpc");
++ if (rc < 0) {
++ printk(KERN_ERR
++ "rpcrouter: Failed to alloc chardev region (%d)\n", rc);
++ goto fail_destroy_class;
++ }
++
++ major = MAJOR(sgh_rpc_devno);
++ rpc_device = device_create(sgh_rpc_class, NULL,
++ sgh_rpc_devno, NULL, "sghrpc%d:%d",
++ 0, 0);
++ if (IS_ERR(rpc_device)) {
++ rc = -ENOMEM;
++ goto fail_unregister_cdev_region;
++ }
++
++ cdev_init(&rpc_cdev, &rpc_fops);
++ rpc_cdev.owner = THIS_MODULE;
++
++ rc = cdev_add(&rpc_cdev, sgh_rpc_devno, 1);
++ if (rc < 0)
++ goto fail_destroy_device;
++
++ workqueue = create_singlethread_workqueue("sgh-rpc");
++ queue_work(workqueue, &work_read);
++
++ return;
++
++fail_destroy_device:
++ device_destroy(sgh_rpc_class, sgh_rpc_devno);
++fail_unregister_cdev_region:
++ unregister_chrdev_region(sgh_rpc_devno, 1);
++fail_destroy_class:
++ class_destroy(sgh_rpc_class);
++}
+diff -ur linux-2.6.32/arch/arm/mach-pxa/sgh_smd.c kernel/arch/arm/mach-pxa/sgh_smd.c
+--- linux-2.6.32/arch/arm/mach-pxa/sgh_smd.c 2009-12-13 13:01:03.799036125 +0200
++++ kernel/arch/arm/mach-pxa/sgh_smd.c 2009-12-12 16:09:26.486282481 +0200
+@@ -0,0 +1,458 @@
++/**
++ * Support for Samsung SGH I900 MSM6K Shared Memory
++ *
++ * Copyright (C) 2009 Mustafa Ozsakalli <ozsakalli@hotmail.com>
++ *
++ * 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/platform_device.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/miscdevice.h>
++#include <linux/io.h>
++#include <linux/gpio.h>
++#include <linux/delay.h>
++#include <linux/freezer.h>
++#include <linux/wait.h>
++#include <linux/workqueue.h>
++#include <linux/completion.h>
++
++#include <mach/hardware.h>
++#include <mach/sgh_msm6k.h>
++
++#include "devices.h"
++
++static unsigned mmio;
++static int smd_initialized;
++
++static DEFINE_SPINLOCK(smd_lock);
++
++#define MEMP(x) (void *)(mmio + x)
++#define MEMW(x) *((unsigned short *)(mmio + x))
++#define MEML(x) *((unsigned long *)(mmio + x))
++
++#define HEAD(c) MEMW(c.head)
++#define TAIL(c) MEMW(c.tail)
++#define HEADPTR(c) MEMP(c.base + HEAD(c))
++#define TAILPTR(c) MEMP(c.base + TAIL(c))
++#define SETTAIL(c,t) TAIL(c)=t; TAIL(c)=t; TAIL(c)=t
++#define SETHEAD(c,h) HEAD(c)=h; HEAD(c)=h; HEAD(c)=h
++#define AVAIL(h,t,s) t<=h ? h-t : s - (t-h)
++
++struct smd_half_channel {
++ unsigned head;
++ unsigned tail;
++ unsigned base;
++ unsigned size;
++};
++
++struct smd_channel {
++ struct smd_half_channel send;
++ struct smd_half_channel recv;
++ unsigned head_mask;
++ unsigned tail_mask;
++ unsigned data_mask;
++ unsigned ex_mask;
++ wait_queue_head_t wait_recv;
++ wait_queue_head_t wait_send;
++};
++
++static struct smd_channel smd_channels[] = {
++ //msm6k rpc channel
++ {
++
++ .send = {
++ .head = 0x4,
++ .tail = 0x6,
++ .base = 0x8,
++ .size = 0x3fc,
++ },
++
++ .recv = {
++ .head = 0x1298,
++ .tail = 0x129a,
++ .base = 0x129c,
++ .size = 0x3fc,
++ },
++
++ .head_mask = 0x2,
++ .tail_mask = 0x8,
++ .data_mask = 0x20,
++
++ },
++
++ {
++
++ .send = {
++ .head = 0x404,
++ .tail = 0x406,
++ .base = 0x408,
++ .size = 0xe90,
++ },
++
++ .recv = {
++ .head = 0x1698,
++ .tail = 0x169a,
++ .base = 0x169c,
++ .size = 0x2950,
++ },
++
++ .head_mask = 0x1,
++ .tail_mask = 0x4,
++ .data_mask = 0x10,
++
++ },
++
++};
++
++void smd_phone_power(int on) {
++ if(on){
++ gpio_set_value(0x66,1);
++ gpio_set_value(0x51,1);
++ mdelay(500);
++ gpio_set_value(0x51,0);
++ } else {
++ gpio_set_value(0x66,0);
++ mdelay(500);
++ gpio_set_value(0x66,1);
++
++ }
++}
++
++
++void smd_init_mem(void)
++{
++ int i;
++
++ if(smd_initialized)
++ return;
++
++ MEML(0x20) = 0;
++ MEMW(0x3ffe) = 0x00C1;
++ MEML(0x20) = 0;
++
++ MEMW(0x2) = 0;
++ MEMW(0x4) = 0;
++ MEMW(0x6) = 0;
++
++ for(i = 8; i < 0x404; i += 2)
++ MEMW(i) = 0x1111;
++
++ MEMW(0x404) = 0;
++ MEMW(0x406) = 0;
++
++ for(i = 0x408; i < 0x1298; i += 2)
++ MEMW(i) = 0x2222;
++
++ MEMW(0x1298) = 0;
++ MEMW(0x129A) = 0;
++
++ for(i = 0x129C; i < 0x1698; i += 2)
++ MEMW(i) = 0x3333;
++
++ MEMW(0x1698) = 0;
++ MEMW(0x169A) = 0;
++
++ for(i = 0x169C; i < 0x3FEC; i += 2)
++ MEMW(i) = 0x4444;
++
++ if(MEML(0x18) == 0) {
++ MEMW(0) = 0x00AA;
++ MEMW(2) = 0x0001;
++ }
++
++ MEMW(0x3ffe) = 0x00C2;
++ MEML(0x20) = 0;
++
++ smd_initialized = 1;
++
++ printk("SMD: Initialize Completed\n");
++}
++
++static int smd_write_and_check(unsigned adr, void* data, int len) {
++ int try;
++
++ for(try=0; try<3; try++){
++ memcpy(MEMP(adr), data, len);
++ if(memcmp(MEMP(adr), data, len)==0) break;
++ }
++
++ if(adr == 0x3FFE)
++ MEML(0x20) = 0;
++
++ return try<3 ? 1 : 0;
++
++}
++
++static int smd_read_and_check(unsigned adr, void *data, int len) {
++ int try;
++
++
++ for(try=0;try<3;try++){
++ memcpy(data,MEMP(adr),len);
++ if(memcmp(data,MEMP(adr),len)==0) break;
++ }
++
++ if(try > 2 || adr == 0x3ffc)
++ MEML(0x20) = 0;
++
++ //synch problem
++ if(try>2) return 0;
++
++ return 1;
++
++}
++
++static int smd_get_mask() {
++ unsigned short mask;
++
++ smd_read_and_check(0x3ffc, &mask, 2);
++
++ return mask;
++}
++
++static void smd_set_mask(short mask) {
++ smd_write_and_check(0x3ffe, &mask, 2);
++}
++
++irqreturn_t smd_irq_handler(int irq, void *dev_id){
++ unsigned long flags;
++ int mask,i;
++
++ //printk("SMD: IRQ fired\n");
++
++ spin_lock_irqsave(&smd_lock, flags);
++
++ mask = smd_get_mask();
++
++ if(!(mask&0x80)) goto done;
++
++ switch(mask & ~0x80){
++ case 0x48 : //initialize
++ smd_init_mem();
++ break;
++
++ case 0x4A :
++ printk("SMD: Phone Deep Sleep??\n");
++ /*fire PhoneDeepSleepEvent?*/
++ break;
++ }
++
++
++ for(i=0;i<2;i++){
++ struct smd_channel *c = &smd_channels[i];
++ if(HEAD(c->recv) != TAIL(c->recv))
++ mask |= c->head_mask;
++ }
++
++ if((mask & 0x2a) != 0)
++ smd_channels[0].ex_mask = mask;
++
++ if((mask & 0x15) != 0)
++ smd_channels[1].ex_mask = mask;
++
++
++ for(i=0;i<2;i++){
++ struct smd_channel *c = &smd_channels[i];
++
++ if(HEAD(c->recv) != TAIL(c->recv)){
++ wake_up(&c->wait_recv);
++ }
++
++ if((mask & (0x80 | c->tail_mask)) != 0)
++ wake_up(&c->wait_send);
++ }
++
++done:
++ spin_unlock_irqrestore(&smd_lock, flags);
++
++ return IRQ_HANDLED;
++}
++
++int smd_read_avail(struct smd_channel *c) {
++ unsigned head, tail;
++
++ if(!smd_initialized)
++ return 0;
++
++ head = HEAD(c->recv);
++ tail = TAIL(c->recv);
++
++ return AVAIL(head,tail,c->recv.size);
++}
++
++int ch_read(struct smd_channel *c, void *_buf, int len) {
++ int n;
++ int head, tail;
++ int orig_len = len;
++ unsigned char *buf = _buf;
++
++ if(!smd_initialized) return 0;
++
++
++ while(len > 0) {
++ head = HEAD(c->recv);
++ tail = TAIL(c->recv);
++
++ n = tail<=head ? head - tail : c->recv.size - tail;
++ if(n==0) break;
++
++ if(n > len) n = len;
++
++ memcpy(buf, TAILPTR(c->recv), n);
++
++ buf += n;
++ len -= n;
++
++ tail = (tail + n) % (c->recv.size);
++ SETTAIL(c->recv,tail);
++ }
++
++ if(orig_len!=len || HEAD(c->recv)==TAIL(c->recv)) {
++ int mask = c->ex_mask;
++ mask &= c->data_mask;
++ if(mask != 0)
++ smd_set_mask(0x80 | c->tail_mask);
++ }
++
++ return orig_len - len;
++}
++
++int ch_write(struct smd_channel *c, void *buf, int len) {
++ unsigned head, tail ,mask;
++ int n;
++
++ head = HEAD(c->send);
++ tail = TAIL(c->send);
++
++ n = (head < tail) ? tail - head :
++ c->send.size - head;
++
++
++ mask = 0x80;
++
++ if(n > len) n = len;
++
++ if(n > 0) {
++ memcpy(HEADPTR(c->send), buf, n);
++ head = (head + n) % c->send.size;
++ SETHEAD(c->send, head);
++ mask |= c->head_mask;
++ }
++ head = HEAD(c->send);
++ tail = TAIL(c->send);
++
++ if(n < len)
++ mask |= c->data_mask;
++
++ smd_set_mask(mask);
++
++ return n;
++}
++
++struct smd_channel *smd_get_channel(int c) {
++ return &smd_channels[c];
++}
++
++int smd_read(int ch, void *buf, int len) {
++ struct smd_channel *c;
++ unsigned long flags;
++ int rc;
++
++ c = smd_get_channel(ch);
++ for(;;) {
++ spin_lock_irqsave(&smd_lock, flags);
++ if(smd_read_avail(c) >= len) {
++ rc = ch_read(c, buf, len);
++ spin_unlock_irqrestore(&smd_lock, flags);
++ if(rc == len)
++ return 0;
++ else
++ return -EIO;
++ }
++
++ spin_unlock_irqrestore(&smd_lock, flags);
++ wait_event(c->wait_recv, smd_read_avail(c) >= len);
++ }
++
++ return 0;
++}
++
++int smd_write(int ch, void *_buf, int len) {
++ struct smd_channel *c;
++ unsigned long flags;
++ int n;
++ char *buf = _buf;
++
++ c = smd_get_channel(ch);
++ while(len > 0) {
++ spin_lock_irqsave(&smd_lock, flags);
++ n = ch_write(c, buf, len);
++ spin_unlock_irqrestore(&smd_lock, flags);
++
++ len -= n;
++ buf += n;
++
++ wait_event(c->wait_send, len <= 0);
++ }
++
++ return 0;
++}
++
++
++void smd_init(void) {
++ struct resource *r;
++ unsigned short *ram;
++ int rc, i;
++
++ gpio_request(0x6b,"dpram");
++ gpio_request(0x46,"dpram");
++ gpio_request(0x6e,"dpram");
++ gpio_request(0x51,"dpram");
++ gpio_request(0x66,"dpram");
++
++ gpio_direction_output(0x6b,0);
++ gpio_direction_output(0x46,0);
++ gpio_direction_output(0x6e,0);
++ gpio_direction_output(0x51,1);
++ gpio_direction_output(0x66,1);
++
++ smd_phone_power(0);
++
++ gpio_set_value(0x46,0);
++ gpio_set_value(0x6b,0);
++
++ r = request_mem_region(0,0x4000,"dpram");
++ if(r==NULL){
++ printk("SMD: Can't get memory region!\n");
++ return;
++ }
++
++ mmio = (unsigned long)ioremap(r->start,r->end-r->start+1);
++
++ for(i=0;i<2;i++) {
++ init_waitqueue_head(&smd_channels[i].wait_recv);
++ init_waitqueue_head(&smd_channels[i].wait_send);
++ }
++
++ ram = ((unsigned short *)(mmio));
++ //check dpram
++ for(i=0;i<0x2000;i++)
++ ram[i] = 0;
++
++ ram[0] = 0xaa;
++ ram[1] = 1;
++
++ rc = request_irq(IRQ_GPIO(0x46), smd_irq_handler,
++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ "SMD-17", NULL);
++
++ smd_phone_power(0);
++ smd_phone_power(0);
++}
+diff -ur linux-2.6.32/arch/arm/tools/mach-types kernel/arch/arm/tools/mach-types
+--- linux-2.6.32/arch/arm/tools/mach-types 2009-12-03 05:51:21.000000000 +0200
++++ kernel/arch/arm/tools/mach-types 2009-12-12 16:09:26.746279722 +0200
+@@ -2249,7 +2249,7 @@
+ darwin MACH_DARWIN DARWIN 2262
+ oratiscomu MACH_ORATISCOMU ORATISCOMU 2263
+ rtsbc20 MACH_RTSBC20 RTSBC20 2264
+-sgh_i780 MACH_I780 I780 2265
++sgh_i780 MACH_SGH_I780 SGH_I780 2265
+ gemini324 MACH_GEMINI324 GEMINI324 2266
+ oratislan MACH_ORATISLAN ORATISLAN 2267
+ oratisalog MACH_ORATISALOG ORATISALOG 2268
+diff -ur linux-2.6.32/drivers/i2c/busses/i2c-pxa.c kernel/drivers/i2c/busses/i2c-pxa.c
+--- linux-2.6.32/drivers/i2c/busses/i2c-pxa.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/i2c/busses/i2c-pxa.c 2009-12-12 16:09:31.776280278 +0200
+@@ -1173,7 +1173,7 @@
+ .owner = THIS_MODULE,
+ .pm = I2C_PXA_DEV_PM_OPS,
+ },
+- .id_table = i2c_pxa_id_table,
++ .id_table = &i2c_pxa_id_table,
+ };
+
+ static int __init i2c_adap_pxa_init(void)
+diff -ur linux-2.6.32/drivers/input/keyboard/pxa27x_keypad.c kernel/drivers/input/keyboard/pxa27x_keypad.c
+--- linux-2.6.32/drivers/input/keyboard/pxa27x_keypad.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/input/keyboard/pxa27x_keypad.c 2009-12-12 16:09:32.012943837 +0200
+@@ -32,6 +32,16 @@
+
+ #include <mach/hardware.h>
+ #include <mach/pxa27x_keypad.h>
++#if defined(CONFIG_PXA3xx_DVFM)
++#include <linux/notifier.h>
++#include <linux/timer.h>
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_dvfm.h>
++#endif
++#ifdef CONFIG_ANDROID_POWER
++#include <linux/android_power.h>
++static android_suspend_lock_t pxa27x_keypad_suspend_lock;
++#endif
+ /*
+ * Keypad Controller registers
+ */
+@@ -98,6 +108,25 @@
+ #define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
+ #define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM)
+
++#if defined(CONFIG_PXA3xx_DVFM)
++#define D2_STABLE_JIFFIES 6
++
++static int keyevent_enable = 0;
++static int keypad_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data);
++static struct notifier_block notifier_freq_block = {
++ .notifier_call = keypad_notifier_freq,
++};
++
++static struct dvfm_lock dvfm_lock = {
++ .lock = SPIN_LOCK_UNLOCKED,
++ .dev_idx = -1,
++ .count = 0,
++};
++
++static struct timer_list kp_timer;
++#endif
++
+ struct pxa27x_keypad {
+ struct pxa27x_keypad_platform_data *pdata;
+
+@@ -334,6 +363,8 @@
+ struct pxa27x_keypad *keypad = dev_id;
+ unsigned long kpc = keypad_readl(KPC);
+
++ printk("-- irq handled --\n");
++
+ if (kpc & KPC_DI)
+ pxa27x_keypad_scan_direct(keypad);
+
+@@ -402,6 +433,87 @@
+ clk_disable(keypad->clk);
+ }
+
++#if defined(CONFIG_PXA3xx_DVFM)
++static void set_dvfm_constraint(void)
++{
++ spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags);
++ if (dvfm_lock.count++ == 0) {
++ /* Disable lowpower mode */
++ dvfm_disable_op_name("D1", dvfm_lock.dev_idx);
++ dvfm_disable_op_name("D2", dvfm_lock.dev_idx);
++ if (cpu_is_pxa935())
++ dvfm_disable_op_name("CG", dvfm_lock.dev_idx);
++ }
++ spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags);
++}
++
++static void unset_dvfm_constraint(void)
++{
++ spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags);
++ if (dvfm_lock.count == 0) {
++ printk(KERN_WARNING "Keypad constraint has been removed.\n");
++ } else if (--dvfm_lock.count == 0) {
++ /* Enable lowpower mode */
++ dvfm_enable_op_name("D1", dvfm_lock.dev_idx);
++ dvfm_enable_op_name("D2", dvfm_lock.dev_idx);
++ if (cpu_is_pxa935())
++ dvfm_enable_op_name("CG", dvfm_lock.dev_idx);
++ }
++ spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags);
++}
++
++/*
++ * FIXME: Here a timer is used to disable entering D1/D2 for a while.
++ * Because keypad event wakeup system from D1/D2 mode. But keypad device
++ * can't detect the interrupt since it's in standby state.
++ * Keypad device need time to detect it again. So we use a timer here.
++ * D1/D2 idle is determined by idle time. It's better to comine these
++ * timers together.
++ */
++static void keypad_timer_handler(unsigned long data)
++{
++ unset_dvfm_constraint();
++}
++
++extern void get_wakeup_source(pm_wakeup_src_t *);
++
++static int keypad_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data)
++{
++ struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
++ struct op_info *new = NULL;
++ struct dvfm_md_opt *op;
++ pm_wakeup_src_t src;
++
++ if (freqs)
++ new = &freqs->new_info;
++ else
++ return 0;
++
++ op = (struct dvfm_md_opt *)new->op;
++ if (val == DVFM_FREQ_POSTCHANGE) {
++ if ((op->power_mode == POWER_MODE_D1) ||
++ (op->power_mode == POWER_MODE_D2) ||
++ (op->power_mode == POWER_MODE_CG)) {
++ //get_wakeup_source(&src);
++ //if (src.bits.mkey || src.bits.dkey) {
++ /* If keypad event happens and wake system
++ * from D1/D2. Disable D1/D2 to make keypad
++ * work for a while.
++ */
++ kp_timer.expires = jiffies + D2_STABLE_JIFFIES;
++ add_timer(&kp_timer);
++ set_dvfm_constraint();
++ #ifdef CONFIG_ANDROID_POWER
++ android_lock_suspend_auto_expire(&pxa27x_keypad_suspend_lock, D2_STABLE_JIFFIES);
++ #endif
++ //}
++ }
++ }
++ return 0;
++}
++#endif
++
+ #ifdef CONFIG_PM
+ static int pxa27x_keypad_suspend(struct device *dev)
+ {
+@@ -410,8 +522,10 @@
+
+ clk_disable(keypad->clk);
+
+- if (device_may_wakeup(&pdev->dev))
++ if (device_may_wakeup(&pdev->dev)) {
++ printk("-- keypad wake set %d\n",keypad->irq);
+ enable_irq_wake(keypad->irq);
++ }
+
+ return 0;
+ }
+@@ -495,6 +609,15 @@
+ goto failed_free_mem;
+ }
+
++#if defined(CONFIG_PXA3xx_DVFM)
++ dvfm_register("Keypad", &dvfm_lock.dev_idx);
++ dvfm_register_notifier(&notifier_freq_block,
++ DVFM_FREQUENCY_NOTIFIER);
++ init_timer(&kp_timer);
++ kp_timer.function = keypad_timer_handler;
++ kp_timer.data = 0;
++#endif
++
+ keypad->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "failed to get keypad clock\n");
+@@ -596,11 +719,18 @@
+
+ static int __init pxa27x_keypad_init(void)
+ {
++#ifdef CONFIG_ANDROID_POWER
++ pxa27x_keypad_suspend_lock.name = "pxa27x_keypad";
++ android_init_suspend_lock(&pxa27x_keypad_suspend_lock);
++#endif
+ return platform_driver_register(&pxa27x_keypad_driver);
+ }
+
+ static void __exit pxa27x_keypad_exit(void)
+ {
++#ifdef CONFIG_ANDROID_POWER
++ android_uninit_suspend_lock(&pxa27x_keypad_suspend_lock);
++#endif
+ platform_driver_unregister(&pxa27x_keypad_driver);
+ }
+
+diff -ur linux-2.6.32/drivers/mmc/host/pxamci.c kernel/drivers/mmc/host/pxamci.c
+--- linux-2.6.32/drivers/mmc/host/pxamci.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/mmc/host/pxamci.c 2009-12-12 16:09:33.709612828 +0200
+@@ -127,9 +127,10 @@
+ break;
+ udelay(1);
+ } while (timeout--);
+-
++ /*
+ if (v & STAT_CLK_EN)
+ dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
++ */
+ }
+ }
+
+diff -ur linux-2.6.32/drivers/net/wireless/libertas/if_spi.c kernel/drivers/net/wireless/libertas/if_spi.c
+--- linux-2.6.32/drivers/net/wireless/libertas/if_spi.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/net/wireless/libertas/if_spi.c 2009-12-12 16:09:35.289611714 +0200
+@@ -1020,9 +1020,9 @@
+ lbs_pr_err("Unsupported chip_id: 0x%02x\n", card_id);
+ return -EAFNOSUPPORT;
+ }
+- snprintf(helper_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d_hlp.bin",
++ snprintf(helper_fw, IF_SPI_FW_NAME_MAX, "gspi%d_hlp.bin",
+ chip_id_to_device_name[i].name);
+- snprintf(main_fw, IF_SPI_FW_NAME_MAX, "libertas/gspi%d.bin",
++ snprintf(main_fw, IF_SPI_FW_NAME_MAX, "gspi%d.bin",
+ chip_id_to_device_name[i].name);
+ return 0;
+ }
+diff -ur linux-2.6.32/drivers/power/Kconfig kernel/drivers/power/Kconfig
+--- linux-2.6.32/drivers/power/Kconfig 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/power/Kconfig 2009-12-12 16:09:35.736280931 +0200
+@@ -110,4 +110,10 @@
+ help
+ Say Y to include support for NXP PCF50633 Main Battery Charger.
+
++config BATTERY_SGH
++ tristate "SGH battery driver"
++ depends on I2C
++ help
++ Say Y here to enable support for PM6558(I2C) chip used on Samsung I780/I900.
++
+ endif # POWER_SUPPLY
+diff -ur linux-2.6.32/drivers/power/Makefile kernel/drivers/power/Makefile
+--- linux-2.6.32/drivers/power/Makefile 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/power/Makefile 2009-12-12 16:09:35.736280931 +0200
+@@ -29,3 +29,5 @@
+ obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
+ obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
+ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
++obj-$(CONFIG_BATTERY_SGH) += sgh_battery.o
++
+diff -ur linux-2.6.32/drivers/power/sgh_battery.c kernel/drivers/power/sgh_battery.c
+--- linux-2.6.32/drivers/power/sgh_battery.c 2009-12-13 13:03:35.181931149 +0200
++++ kernel/drivers/power/sgh_battery.c 2009-12-12 16:09:35.746280509 +0200
+@@ -0,0 +1,474 @@
++/*
++ * Samsung I780/I900 battery driver
++ *
++ * Copyright (C) 2009 Sacha Refshauge <xsacha@gmail.com>
++ *
++ * Based on DQ27x00 battery driver:
++ * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
++ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
++ *
++ * which was based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
++ *
++ * This package 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.
++ *
++ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
++ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
++ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
++ *
++ */
++#include <linux/module.h>
++#include <linux/param.h>
++#include <linux/jiffies.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/power_supply.h>
++#include <linux/idr.h>
++#include <linux/i2c.h>
++#include <linux/gpio.h>
++
++#define DRIVER_VERSION "1.0.1"
++
++#define SGH_CHARGE_GPIO 88
++
++#define SGH_BATT_REG_TEMP 0x25
++#define SGH_BATT_REG_VDIFF 0x23
++#define SGH_BATT_REG_VOLT 0x21
++
++/* Reg Table
++This is what is known of the register banks in PM6558 by
++observation. Assumed that all registers are WORDs, so
++address increases by 2. Also assumed that all registers
++are 12-bit right justified (& 0xFFF).
++
++Register Task Value
++0x21 Voltage The voltage
++0x23 Charging True if charging, False if not charging
++0x25 Temperature The temperature (which is used to determine charge)
++0xC2 Shutdown Write-only regisiter
++
++*/
++
++struct sgh_batt_device_info;
++struct sgh_batt_access_methods {
++ int (*read)(u8 reg, int *rt_value, int b_single,
++ struct sgh_batt_device_info *di);
++};
++struct sgh_batt_device_info {
++ struct device *dev;
++ int id;
++ int voltage_uV;
++ int current_uA;
++ int temp_C;
++ int charge_rsoc;
++ struct sgh_batt_access_methods *bus;
++ struct power_supply bat;
++ struct power_supply bat_ac;
++ struct power_supply bat_usb;
++
++ struct i2c_client *client;
++
++ struct delayed_work work;
++ struct workqueue_struct *wqueue;
++
++ int voltage;
++ int voltage_sum;
++ int voltage_count;
++ int capacity;
++ int charging_status;
++ int poll_count;
++};
++
++
++static enum power_supply_property sgh_batt_battery_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_HEALTH,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_PRESENT,
++ POWER_SUPPLY_PROP_BATT_VOL,
++ POWER_SUPPLY_PROP_CAPACITY,
++ POWER_SUPPLY_PROP_BATT_TEMP,
++};
++
++static enum power_supply_property sgh_batt_power_props[] = {
++ POWER_SUPPLY_PROP_ONLINE,
++};
++
++static int sgh_batt_read(u8 reg, int *rt_value, struct sgh_batt_device_info *di)
++{
++ struct i2c_client *client = di->client;
++
++ *rt_value = be16_to_cpu(i2c_smbus_read_word_data(client, reg));
++ *rt_value = *rt_value & 0xFFF;
++
++ return 0;
++}
++
++/*
++ * Return the battery voltage in millivolts
++ *
++ */
++static int sgh_batt_get_voltage(struct sgh_batt_device_info *di)
++{
++ int i;
++ int voltages[5];
++ int voltage = 0, largest = 0, smallest = 0;
++
++ for(i = 0; i < 5; i++)
++ {
++ sgh_batt_read(SGH_BATT_REG_VOLT, &voltages[i], di);
++ if (voltages[i] > voltages[largest])
++ largest = i;
++
++ if (voltages[i] < voltages[smallest])
++ smallest = i;
++ }
++ for(i = 0; i < 5; i++)
++ {
++ if (i != smallest && i != largest)
++ voltage += voltages[i];
++ }
++
++ voltage /= 3;
++
++ if(di->voltage_count < 10) {
++ di->voltage_sum += voltage;
++ di->voltage_count++;
++ voltage = di->voltage_sum / di->voltage_count;
++ } else {
++ di->voltage_sum = di->voltage_sum - di->voltage + voltage;
++ voltage = di->voltage_sum / 10;
++ }
++
++ return voltage;
++}
++
++/*
++ * Return the battery temperature in (10x) Celcius degrees.
++ *
++ * From Windows Mobile:
++ * Temp Sample [ Min: 0x21B, 0x368, 0x89e : Max]
++ * 539 872 2206
++ */
++static int sgh_batt_get_temp(struct sgh_batt_device_info *di)
++{
++ int temp = 0;
++
++ sgh_batt_read(SGH_BATT_REG_TEMP, &temp, di);
++
++ return temp >> 2;
++}
++
++/*
++ * Return the battery charge in percentage.
++ */
++static int sgh_batt_get_charge(struct sgh_batt_device_info *di)
++{
++ int volt = di->voltage;
++ int i, k = 0, d = 10;
++ int ndist, tdist;
++ int vsamp[] = {0xe38, 0xdb6, 0xd66, 0xd25, 0xce4, 0xc94, 0xb79};
++ // Charging applies a greater voltage. USB: ~0x30 AC: ~0x60
++ // volt -= be16_to_cpu(i2c_smbus_read_word_data(di->client, SGH_BATT_REG_VDIFF)) & 0xFFF; // FIXME
++
++ /* Use voltage to work out charge.
++ Closer to 100%, the voltage has less impact on gradient (linear).
++ Whereas closer to 0%, it is purely the gradient.
++ */
++ for (i = 6; i >= 0; i--)
++ {
++ if (volt < vsamp[i])
++ {
++ switch (i) {
++ case 0:
++ k = k + 1;
++ case 1:
++ k = k + 1;
++ case 2:
++ k = k + 1;
++ case 3:
++ d = d >> 1;
++ case 4:
++ k = k + 1;
++ case 5:
++ ndist = 100 * (volt - vsamp[i+1]);
++ tdist = (vsamp[i] - vsamp[i+1]);
++ volt = (k * 100) + (ndist / tdist);
++ return volt / d;
++ default:
++ return 0;
++ }
++ }
++ }
++ return 100;
++}
++
++#define to_sgh_batt_device_info(x) container_of((x), \
++ struct sgh_batt_device_info, bat);
++
++static int sgh_batt_battery_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct sgh_batt_device_info *di = to_sgh_batt_device_info(psy);
++ switch (psp) {
++
++ case POWER_SUPPLY_PROP_BATT_VOL:
++ val->intval = sgh_batt_get_voltage(di);
++ break;
++
++ case POWER_SUPPLY_PROP_PRESENT:
++ val->intval = true; // Device can't run without it
++ break;
++ case POWER_SUPPLY_PROP_CAPACITY:
++ val->intval = di->capacity;
++ break;
++ case POWER_SUPPLY_PROP_BATT_TEMP:
++ val->intval = sgh_batt_get_temp(di);
++ break;
++
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = di->charging_status ? POWER_SUPPLY_STATUS_CHARGING : POWER_SUPPLY_STATUS_NOT_CHARGING;
++ break;
++ case POWER_SUPPLY_PROP_TECHNOLOGY:
++ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
++ break;
++ case POWER_SUPPLY_PROP_HEALTH:
++ val->intval = POWER_SUPPLY_HEALTH_GOOD;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int sgh_batt_power_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct sgh_batt_device_info *di = to_sgh_batt_device_info(psy);
++ switch (psp) {
++ case POWER_SUPPLY_PROP_ONLINE:
++ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
++ val->intval = (di->charging_status == 1) ? 1 : 0;
++ else if (psy->type == POWER_SUPPLY_TYPE_USB)
++ val->intval = (di->charging_status == 2) ? 1 : 0;
++ else val->intval = 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void sgh_batt_battery_update(struct power_supply *psy)
++{
++ int charging_status;
++ struct sgh_batt_device_info *di = to_sgh_batt_device_info(psy);
++ charging_status = di->charging_status;
++
++ di->poll_count++;
++ if(gpio_get_value(SGH_CHARGE_GPIO)) {
++ di->charging_status = 0; //not charging
++ } else {
++ di->charging_status = 1; //ac
++ //TODO: Detect usb
++ }
++
++ if(di->charging_status != charging_status || di->poll_count >= 5) {
++ if(di->charging_status != charging_status)
++ di->voltage_sum = di->voltage_count = 0;
++
++ di->voltage = sgh_batt_get_voltage(di);
++ di->capacity = sgh_batt_get_charge(di);
++
++ printk("pwr: V:%x C:%d\n",di->voltage,di->capacity);
++
++ power_supply_changed(psy);
++ di->poll_count = 0;
++ }
++
++ /*
++ di->charging_status = gpio_get_value(SGH_CHARGE_GPIO) ?
++ POWER_SUPPLY_STATUS_NOT_CHARGING :
++ POWER_SUPPLY_STATUS_CHARGING;
++ */
++/*
++ if (di->charging_status != charging_status)
++ {
++ di->reset_avg = 1;
++ di->poll_count = 0xff;
++ }
++
++ if(di->poll_count >= 10) {
++ di->poll_count = 0;
++ power_supply_changed(psy);
++ }
++*/
++}
++
++static void sgh_batt_battery_work(struct work_struct *work)
++{
++ struct sgh_batt_device_info *di = container_of(work, struct sgh_batt_device_info, work.work);
++
++ sgh_batt_battery_update(&di->bat);
++ queue_delayed_work(di->wqueue, &di->work, HZ*5);
++}
++
++static char *supply_list[] = {
++ "battery",
++};
++
++static void sgh_powersupply_init(struct sgh_batt_device_info *di) {
++ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
++ di->bat.properties = sgh_batt_battery_props;
++ di->bat.num_properties = ARRAY_SIZE(sgh_batt_battery_props);
++ di->bat.get_property = sgh_batt_battery_get_property;
++ di->bat.external_power_changed = NULL;
++}
++
++static void sgh_powersupply_power_init(struct power_supply *bat,int is_usb) {
++ bat->name = is_usb ? "usb" : "ac";
++ bat->type = is_usb ? POWER_SUPPLY_TYPE_USB : POWER_SUPPLY_TYPE_MAINS;
++ bat->supplied_to = supply_list;
++ bat->num_supplicants = ARRAY_SIZE(supply_list);
++ bat->properties = sgh_batt_power_props;
++ bat->num_properties = ARRAY_SIZE(sgh_batt_power_props);
++ bat->get_property = sgh_batt_power_get_property;
++}
++
++static int sgh_batt_battery_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct sgh_batt_device_info *di;
++ struct sgh_batt_access_methods *bus;
++ int retval = 0;
++
++ retval = gpio_request(SGH_CHARGE_GPIO, "BATT CHRG");
++ if (retval)
++ goto batt_failed_0;
++
++ di = kzalloc(sizeof(*di), GFP_KERNEL);
++ if (!di) {
++ dev_err(&client->dev, "failed to allocate device info data\n");
++ retval = -ENOMEM;
++ return retval;
++ }
++
++ bus = kzalloc(sizeof(*bus), GFP_KERNEL);
++ if (!bus) {
++ dev_err(&client->dev, "failed to allocate access method "
++ "data\n");
++ retval = -ENOMEM;
++ goto batt_failed_1;
++ }
++
++ i2c_set_clientdata(client, di);
++ di->dev = &client->dev;
++ di->bat.name = "battery"; // Android only looks for this
++ di->bus = bus;
++ di->client = client;
++ di->poll_count = 0;
++ sgh_powersupply_init(di);
++ retval = power_supply_register(&client->dev, &di->bat);
++ if (retval) {
++ dev_err(&client->dev, "failed to register battery\n");
++ goto batt_failed_2;
++ }
++
++ sgh_powersupply_power_init(&di->bat_ac,0);
++ retval = power_supply_register(&client->dev, &di->bat_ac);
++ if (retval) {
++ dev_err(&client->dev, "failed to register battery (ac)\n");
++ goto batt_failed_2;
++ }
++
++ sgh_powersupply_power_init(&di->bat_usb,1);
++ retval = power_supply_register(&client->dev, &di->bat_usb);
++ if (retval) {
++ dev_err(&client->dev, "failed to register battery (usb)\n");
++ goto batt_failed_2;
++ }
++
++ INIT_DELAYED_WORK(&di->work, sgh_batt_battery_work);
++ di->wqueue = create_singlethread_workqueue("battery");
++ queue_delayed_work(di->wqueue, &di->work, 1);
++
++ dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
++
++ return 0;
++
++batt_failed_2:
++ kfree(bus);
++batt_failed_1:
++ kfree(di);
++batt_failed_0:
++
++ return retval;
++}
++
++static int sgh_batt_battery_remove(struct i2c_client *client)
++{
++ struct sgh_batt_device_info *di = i2c_get_clientdata(client);
++
++ cancel_rearming_delayed_workqueue(di->wqueue,
++ &di->work);
++ destroy_workqueue(di->wqueue);
++
++ gpio_free(SGH_CHARGE_GPIO);
++
++ power_supply_unregister(&di->bat);
++
++ kfree(di->bat.name);
++
++ kfree(di);
++
++ return 0;
++}
++
++
++/*
++ * Module stuff
++ */
++
++static const struct i2c_device_id sgh_batt_id[] = {
++ { "sgh_battery", 0 },
++ {},
++};
++
++static struct i2c_driver sgh_batt_battery_driver = {
++ .driver = {
++ .name = "battery",
++ },
++ .probe = sgh_batt_battery_probe,
++ .remove = sgh_batt_battery_remove,
++ .suspend = NULL,
++ .resume = NULL, //todo: power management
++ .id_table = sgh_batt_id,
++};
++
++static int __init sgh_batt_battery_init(void)
++{
++ int ret;
++
++ ret = i2c_add_driver(&sgh_batt_battery_driver);
++ if (ret)
++ printk(KERN_ERR "Unable to register Samsung I780/I900 driver\n");
++
++ return ret;
++}
++module_init(sgh_batt_battery_init);
++
++static void __exit sgh_batt_battery_exit(void)
++{
++ i2c_del_driver(&sgh_batt_battery_driver);
++}
++module_exit(sgh_batt_battery_exit);
++
++MODULE_AUTHOR("Sacha Refshauge <xsacha@gmail.com>");
++MODULE_DESCRIPTION("Samsung I780/I900 battery monitor driver");
++MODULE_LICENSE("GPL");
+diff -ur linux-2.6.32/drivers/video/pxafb.c kernel/drivers/video/pxafb.c
+--- linux-2.6.32/drivers/video/pxafb.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/drivers/video/pxafb.c 2009-12-13 14:40:18.638427304 +0200
+@@ -62,6 +62,11 @@
+ #include <mach/bitfield.h>
+ #include <mach/pxafb.h>
+
++#ifdef CONFIG_PXA3xx_DVFM
++#include <mach/dvfm.h>
++#include <mach/pxa3xx_dvfm.h>
++#endif
++
+ /*
+ * Complain if VAR is out of range.
+ */
+@@ -86,6 +91,19 @@
+
+ static unsigned long video_mem_size = 0;
+
++#ifdef CONFIG_PXA3xx_DVFM
++static int fb_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data);
++static struct notifier_block notifier_freq_block = {
++ .notifier_call = fb_notifier_freq,
++};
++
++static void *dev_id = NULL;
++
++static int hss = 0;
++static int pxafb_adjust_pcd(struct pxafb_info *fbi, int hss);
++#endif
++
+ static inline unsigned long
+ lcd_readl(struct pxafb_info *fbi, unsigned int off)
+ {
+@@ -1468,6 +1486,11 @@
+ lcd_writel(fbi, LCSR1, lcsr1);
+ }
+ #endif
++ if( lcsr & (LCSR_BS|LCSR_SOF))
++ {
++ wake_up(&fbi->ctrlr_wait);
++ }
++
+ return IRQ_HANDLED;
+ }
+
+@@ -1642,7 +1665,7 @@
+ {
+ struct pxafb_info *fbi = dev_get_drvdata(dev);
+
+- set_ctrlr_state(fbi, C_DISABLE_PM);
++ //set_ctrlr_state(fbi, C_DISABLE_PM);
+ return 0;
+ }
+
+@@ -1650,7 +1673,7 @@
+ {
+ struct pxafb_info *fbi = dev_get_drvdata(dev);
+
+- set_ctrlr_state(fbi, C_ENABLE_PM);
++ //set_ctrlr_state(fbi, C_ENABLE_PM);
+ return 0;
+ }
+
+@@ -1660,6 +1683,87 @@
+ };
+ #endif
+
++#ifdef CONFIG_PXA3xx_DVFM
++static int dvfm_dev_idx;
++static void set_dvfm_constraint(void)
++{
++ /* Disable Lowpower mode */
++ /* Remove D0CS constraint since LCCR3_STALL is set */
++// dvfm_disable_op_name("D0CS", dvfm_dev_idx);
++ dvfm_disable_op_name("D1", dvfm_dev_idx);
++ dvfm_disable_op_name("D2", dvfm_dev_idx);
++ if (cpu_is_pxa935())
++ dvfm_disable_op_name("CG", dvfm_dev_idx);
++}
++
++static void unset_dvfm_constraint(void)
++{
++ /* Enable Lowpower mode */
++ /* Remove D0CS constraint since LCCR3_STALL is set */
++// dvfm_enable_op_name("D0CS", dvfm_dev_idx);
++ dvfm_enable_op_name("D1", dvfm_dev_idx);
++ dvfm_enable_op_name("D2", dvfm_dev_idx);
++ if (cpu_is_pxa935())
++ dvfm_enable_op_name("CG", dvfm_dev_idx);
++}
++
++static int fb_notifier_freq(struct notifier_block *nb,
++ unsigned long val, void *data)
++{
++ struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
++ struct op_info *new = NULL;
++ struct dvfm_md_opt *op;
++/*
++ if (freqs) {
++ new = &freqs->new_info;
++ } else
++ return 0;
++
++ op = (struct dvfm_md_opt *)new->op;
++ switch (val) {
++ case DVFM_FREQ_PRECHANGE:
++ if ((op->power_mode == POWER_MODE_D0) ||
++ (op->power_mode == POWER_MODE_D0CS))
++ hss = op->hss;
++ else if ((op->power_mode == POWER_MODE_D1) ||
++ (op->power_mode == POWER_MODE_D2) ||
++ (op->power_mode == POWER_MODE_CG))
++ lcd_update = 0;
++ break;
++ case DVFM_FREQ_POSTCHANGE:
++ if ((op->power_mode == POWER_MODE_D1) ||
++ (op->power_mode == POWER_MODE_D2) ||
++ (op->power_mode == POWER_MODE_CG))
++ lcd_update = 1;
++ break;
++ }
++*/
++ return 0;
++}
++
++static int pxafb_adjust_pcd(struct pxafb_info *fbi, int hss)
++{
++
++ return 0;
++}
++
++void pxafb_set_pcd(void)
++{
++/*
++ struct pxafb_info *fbi = (struct pxafb_info *)dev_id;
++
++ if (fbi)
++ pxafb_adjust_pcd(fbi, hss);
++*/
++ return;
++}
++
++EXPORT_SYMBOL(pxafb_set_pcd);
++#else
++static void set_dvfm_constraint(void) {}
++static void unset_dvfm_constraint(void) {}
++#endif
++
+ static int __devinit pxafb_init_video_memory(struct pxafb_info *fbi)
+ {
+ int size = PAGE_ALIGN(fbi->video_mem_size);
+
+diff -ur linux-2.6.32/include/linux/input.h kernel/include/linux/input.h
+--- linux-2.6.32/include/linux/input.h 2009-12-03 05:51:21.000000000 +0200
++++ kernel/include/linux/input.h 2009-12-12 16:09:40.056274324 +0200
+@@ -333,6 +333,7 @@
+ #define KEY_BASSBOOST 209
+ #define KEY_PRINT 210 /* AC Print */
+ #define KEY_HP 211
++#define KEY_CAMERAFOCUS 211
+ #define KEY_CAMERA 212
+ #define KEY_SOUND 213
+ #define KEY_QUESTION 214
+diff -ur linux-2.6.32/include/linux/power_supply.h kernel/include/linux/power_supply.h
+--- linux-2.6.32/include/linux/power_supply.h 2009-12-03 05:51:21.000000000 +0200
++++ kernel/include/linux/power_supply.h 2009-12-12 16:09:40.166275516 +0200
+@@ -113,6 +113,8 @@
+ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
++ POWER_SUPPLY_PROP_BATT_VOL,
++ POWER_SUPPLY_PROP_BATT_TEMP,
+ /* Properties of type `const char *' */
+ POWER_SUPPLY_PROP_MODEL_NAME,
+ POWER_SUPPLY_PROP_MANUFACTURER,
+diff -ur linux-2.6.32/include/linux/time.h kernel/include/linux/time.h
+--- linux-2.6.32/include/linux/time.h 2009-12-03 05:51:21.000000000 +0200
++++ kernel/include/linux/time.h 2009-12-12 16:09:40.246280545 +0200
+@@ -107,6 +107,7 @@
+ extern int no_sync_cmos_clock __read_mostly;
+ void timekeeping_init(void);
+ extern int timekeeping_suspended;
++extern void update_sleep_time(struct timespec ts);
+
+ unsigned long get_seconds(void);
+ struct timespec current_kernel_time(void);
+diff -ur linux-2.6.32/kernel/printk.c kernel/kernel/printk.c
+--- linux-2.6.32/kernel/printk.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/kernel/printk.c 2009-12-12 16:09:40.512534939 +0200
+@@ -257,6 +257,53 @@
+ #endif
+
+ /*
++ * Return the number of unread characters in the log buffer.
++ */
++static int log_buf_get_len(void)
++{
++ return logged_chars;
++}
++
++/*
++ * Clears the ring-buffer
++ */
++void log_buf_clear(void)
++{
++ logged_chars = 0;
++}
++
++/*
++ * Copy a range of characters from the log buffer.
++ */
++int log_buf_copy(char *dest, int idx, int len)
++{
++ int ret, max;
++ bool took_lock = false;
++
++ if (!oops_in_progress) {
++ spin_lock_irq(&logbuf_lock);
++ took_lock = true;
++ }
++
++ max = log_buf_get_len();
++ if (idx < 0 || idx >= max) {
++ ret = -1;
++ } else {
++ if (len > max - idx)
++ len = max - idx;
++ ret = len;
++ idx += (log_end - max);
++ while (len-- > 0)
++ dest[len] = LOG_BUF(idx + len);
++ }
++
++ if (took_lock)
++ spin_unlock_irq(&logbuf_lock);
++
++ return ret;
++}
++
++/*
+ * Commands to do_syslog:
+ *
+ * 0 -- Close the log. Currently a NOP.
+@@ -1405,3 +1452,4 @@
+ }
+ EXPORT_SYMBOL(printk_timed_ratelimit);
+ #endif
++
+diff -ur linux-2.6.32/kernel/time/timekeeping.c kernel/kernel/time/timekeeping.c
+--- linux-2.6.32/kernel/time/timekeeping.c 2009-12-03 05:51:21.000000000 +0200
++++ kernel/kernel/time/timekeeping.c 2009-12-12 16:09:40.559199813 +0200
+@@ -886,3 +886,14 @@
+ now.tv_nsec + mono.tv_nsec);
+ return now;
+ }
++
++void update_sleep_time(struct timespec ts)
++{
++ long wtm_sec, wtm_nsec;
++ wtm_sec = wall_to_monotonic.tv_sec - ts.tv_sec;
++ wtm_nsec = wall_to_monotonic.tv_nsec - ts.tv_nsec;
++ set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
++ set_normalized_timespec(&total_sleep_time,
++ (ts.tv_sec + total_sleep_time.tv_sec),
++ (ts.tv_nsec + total_sleep_time.tv_nsec));
++}
+diff -ur linux-2.6.32/sound/soc/pxa/Kconfig kernel/sound/soc/pxa/Kconfig
+--- linux-2.6.32/sound/soc/pxa/Kconfig 2009-12-03 05:51:21.000000000 +0200
++++ kernel/sound/soc/pxa/Kconfig 2009-12-12 16:09:41.692528097 +0200
+@@ -144,3 +144,14 @@
+ help
+ Say Y if you want to add support for SoC audio on the
+ IMote 2.
++
++config SND_SOC_SGH
++ tristate "SoC Audio support for Samsung SGH I900"
++ depends on SND_PXA2XX_SOC && MACH_SGH_I900
++ select SND_PXA2XX_SOC_AC97
++ select SND_PXA_SOC_SSP
++ select SND_SOC_WM9713
++ help
++ Say Y if you want to add support for SoC audio on the
++ Samsung SGH I900 mobile phone.
++
+diff -ur linux-2.6.32/sound/soc/pxa/Makefile kernel/sound/soc/pxa/Makefile
+--- linux-2.6.32/sound/soc/pxa/Makefile 2009-12-03 05:51:21.000000000 +0200
++++ kernel/sound/soc/pxa/Makefile 2009-12-12 16:09:41.692528097 +0200
+@@ -23,6 +23,7 @@
+ snd-soc-magician-objs := magician.o
+ snd-soc-mioa701-objs := mioa701_wm9713.o
+ snd-soc-imote2-objs := imote2.o
++snd-soc-sgh-objs := sgh.o
+
+ obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
+ obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
+@@ -37,3 +38,4 @@
+ obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
+ obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
+ obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
++obj-$(CONFIG_SND_SOC_SGH) += snd-soc-sgh.o
+diff -ur linux-2.6.32/sound/soc/pxa/sgh.c kernel/sound/soc/pxa/sgh.c
+--- linux-2.6.32/sound/soc/pxa/sgh.c 2009-12-13 13:07:09.965238502 +0200
++++ kernel/sound/soc/pxa/sgh.c 2009-12-12 16:09:41.695861483 +0200
+@@ -0,0 +1,310 @@
++/*
++ * Handles the Samsung I780-I900 SoC system
++ *
++ * Copyright (C) 2009 Mustafa Ozsakalli
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation in version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/platform_device.h>
++#include <linux/gpio.h>
++#include <linux/delay.h>
++#include <linux/irq.h>
++
++#include <asm/mach-types.h>
++#include <mach/audio.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++#include <sound/ac97_codec.h>
++
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++#include "../codecs/wm9713.h"
++#include "pxa-ssp.h"
++
++#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
++
++#define SGH_I780_AUDIO_GPIO 0x13
++#define SGH_I900_AUDIO_GPIO 0x11
++
++static const struct snd_soc_dapm_widget sgh_dapm_widgets[] = {
++ SND_SOC_DAPM_SPK("Front Speaker", NULL),
++ SND_SOC_DAPM_HP("Headset", NULL),
++ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
++ SND_SOC_DAPM_LINE("GSM Line In", NULL),
++ SND_SOC_DAPM_LINE("Radio Line Out", NULL),
++ SND_SOC_DAPM_MIC("Front Mic", NULL),
++};
++
++static const struct snd_soc_dapm_route audio_map[] = {
++ /* Microphone */
++ {"MIC1", NULL, "Front Mic"},
++
++ /* Speaker */
++ {"Front Speaker", NULL, "SPKL"},
++ {"Front Speaker", NULL, "SPKR"},
++
++ /* Earpiece */
++ {"Headset", NULL, "HPL"},
++ {"Headset", NULL, "HPR"},
++
++ /* GSM Module */
++ {"MONOIN", NULL, "GSM Line Out"},
++ {"PCBEEP", NULL, "GSM Line Out"},
++ {"GSM Line In", NULL, "MONO"},
++
++ /* FM Radio Module */
++ {"LINEL", NULL, "Radio Line Out"},
++ {"LINER", NULL, "Radio Line Out"},
++};
++
++static int sgh_wm9713_init(struct snd_soc_codec *codec)
++{
++ unsigned short reg;
++
++ snd_soc_dapm_new_controls(codec, ARRAY_AND_SIZE(sgh_dapm_widgets));
++ snd_soc_dapm_add_routes(codec, ARRAY_AND_SIZE(audio_map));
++
++ snd_soc_dapm_enable_pin(codec, "Front Speaker");
++
++ snd_soc_dapm_sync(codec);
++
++
++ return 0;
++}
++
++static int sgh_hifi_startup(struct snd_pcm_substream *substream){
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ cpu_dai->playback.channels_min = 2;
++ cpu_dai->playback.channels_max = 2;
++
++ return 0;
++}
++
++static int sgh_hifi_prepare(struct snd_pcm_substream *substream) {
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->card->codec;
++ struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
++ u16 reg;
++ int gpio = machine_is_sgh_i780() ? SGH_I780_AUDIO_GPIO : SGH_I900_AUDIO_GPIO;
++
++ codec->write(codec, AC97_POWERDOWN, 0);
++ mdelay(1);
++ codec_dai->ops->set_pll(codec_dai, 0, 4096000, 0);
++ schedule_timeout_interruptible(msecs_to_jiffies(10));
++ codec->write(codec, AC97_HANDSET_RATE, 0x0000);
++ schedule_timeout_interruptible(msecs_to_jiffies(10));
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ reg = AC97_PCM_FRONT_DAC_RATE;
++ else
++ reg = AC97_PCM_LR_ADC_RATE;
++ codec->write(codec, AC97_EXTENDED_STATUS, 0x1);
++ codec->write(codec, reg, substream->runtime->rate);
++
++ //Turn on external speaker
++ //TODO: Headset detection
++ gpio_set_value(gpio, 1);
++
++ return 0;
++}
++
++static void sgh_hifi_shutdown(struct snd_pcm_substream *substream) {
++ int gpio = machine_is_sgh_i780() ? SGH_I780_AUDIO_GPIO : SGH_I900_AUDIO_GPIO;
++ gpio_direction_output(gpio, 1);
++ gpio_set_value(gpio, 0);
++}
++
++static int sgh_voice_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ cpu_dai->playback.channels_min = 1;
++ cpu_dai->playback.channels_max = 1;
++
++ return 0;
++};
++
++static int sgh_voice_prepare(struct snd_pcm_substream *substream)
++{
++#define WM9713_DR_8000 0x1F40 /* 8000 samples/sec */
++#define WM9713_DR_11025 0x2B11 /* 11025 samples/sec */
++#define WM9713_DR_12000 0x2EE0 /* 12000 samples/sec */
++#define WM9713_DR_16000 0x3E80 /* 16000 samples/sec */
++#define WM9713_DR_22050 0x5622 /* 22050 samples/sec */
++#define WM9713_DR_24000 0x5DC0 /* 24000 samples/sec */
++#define WM9713_DR_32000 0x7D00 /* 32000 samples/sec */
++#define WM9713_DR_44100 0xAC44 /* 44100 samples/sec */
++#define WM9713_DR_48000 0xBB80 /* 48000 samples/sec */
++
++ return 0;
++};
++
++static void sgh_voice_shutdown(struct snd_pcm_substream *substream)
++{
++
++};
++
++static struct snd_soc_ops sgh_ops[] = {
++{
++ .startup = sgh_hifi_startup,
++ .prepare = sgh_hifi_prepare,
++ .shutdown = sgh_hifi_shutdown,
++},
++{
++ .startup = sgh_voice_startup,
++ .prepare = sgh_voice_prepare,
++ .shutdown = sgh_voice_shutdown,
++},
++};
++
++static struct snd_soc_dai_link sgh_dai[] = {
++ {
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
++ .init = sgh_wm9713_init,
++ .ops = &sgh_ops[0],
++ },
++ {
++ .name = "AC97 Aux",
++ .stream_name = "AC97 Aux",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
++ },
++ {
++ .name = "WM9713 Voice",
++ .stream_name = "WM9713 Voice",
++ .cpu_dai = &pxa_ssp_dai[PXA_DAI_SSP3],
++ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
++ .ops = &sgh_ops[1],
++ },
++};
++
++static struct snd_soc_card sgh = {
++ .name = "SGHAudio",
++ .platform = &pxa2xx_soc_platform,
++ .dai_link = sgh_dai,
++ .num_links = ARRAY_SIZE(sgh_dai),
++};
++
++static struct snd_soc_device sgh_snd_devdata = {
++ .card = &sgh,
++ .codec_dev = &soc_codec_dev_wm9713,
++};
++
++static struct platform_device *sgh_snd_device;
++
++static int sgh_wm9713_probe(struct platform_device *pdev)
++{
++ int ret;
++ int gpio = machine_is_sgh_i780() ? SGH_I780_AUDIO_GPIO : SGH_I900_AUDIO_GPIO;
++
++ gpio_request(0x64, "WM9713 Power");
++ gpio_direction_output(0x64, 1);
++ gpio_set_value(0x64, 0);
++ mdelay(10);
++ gpio_set_value(0x64, 1);
++
++ gpio_request(gpio, "Speaker");
++ gpio_direction_output(gpio, 1);
++ gpio_set_value(gpio, 0);
++
++ sgh_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!sgh_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(sgh_snd_device, &sgh_snd_devdata);
++ sgh_snd_devdata.dev = &sgh_snd_device->dev;
++
++ ret = platform_device_add(sgh_snd_device);
++ if (ret != 0)
++ platform_device_put(sgh_snd_device);
++
++ return ret;
++}
++
++static int __devexit sgh_wm9713_remove(struct platform_device *pdev)
++{
++ platform_device_unregister(sgh_snd_device);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static int sgh_wm9713_suspend(struct platform_device *pdev,
++ pm_message_t state)
++{
++ //struct snd_soc_card *card = platform_get_drvdata(pdev);
++ return 0;
++ //return snd_soc_card_suspend_pcms(card, state);
++}
++
++static int sgh_wm9713_resume(struct platform_device *pdev)
++{
++ //struct snd_soc_card *card = platform_get_drvdata(pdev);
++ return 0;
++ //return snd_soc_card_resume_pcms(card);
++}
++
++#else
++#define sgh_wm9713_suspend NULL
++#define sgh_wm9713_resume NULL
++#define sgh_wm9713_suspend_late NULL
++#define sgh_wm9713_resume_early NULL
++#endif
++
++static struct platform_driver sgh_wm9713_driver = {
++ .probe = sgh_wm9713_probe,
++ .remove = __devexit_p(sgh_wm9713_remove),
++ .suspend = sgh_wm9713_suspend,
++ .resume = sgh_wm9713_resume,
++ .driver = {
++ .name = "sgh-asoc",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init sgh_asoc_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&sgh_wm9713_driver);
++
++ return ret;
++}
++
++static void __exit sgh_asoc_exit(void)
++{
++ platform_driver_unregister(&sgh_wm9713_driver);
++}
++
++module_init(sgh_asoc_init);
++module_exit(sgh_asoc_exit);
++
++/* Module information */
++MODULE_AUTHOR("Mustafa Ozsakalli (ozsakalli@hotmail.com)");
++MODULE_DESCRIPTION("ALSA SoC WM9713 Samsung SGH I780/I900");
++MODULE_LICENSE("GPL");