Index: linux-2.6.12/arch/arm/mach-pxa/sharpsl.h
===================================================================
--- linux-2.6.12.orig/arch/arm/mach-pxa/sharpsl.h	2005-08-26 08:45:26.000000000 +0100
+++ linux-2.6.12/arch/arm/mach-pxa/sharpsl.h	2005-08-26 08:45:28.000000000 +0100
@@ -14,3 +14,73 @@
 
 void corgi_ssp_set_machinfo(struct corgissp_machinfo *machinfo);
 
+/* 
+ * SharpSL Battery/PM Driver 
+ */
+
+struct sharpsl_charger_machinfo {
+	void (*init)(void);
+	int gpio_batlock;
+	int gpio_acin;
+	int gpio_batfull;
+	int gpio_batlow;
+	int (*status_acin)(void);
+	void (*discharge)(int);
+	void (*charge)(int);
+	void (*chargeled)(int);
+	void (*measure_temp)(int);
+	void (*presuspend)(void);
+	void (*postsuspend)(void);
+	unsigned long (*charger_wakeup)(void);
+	int (*should_wakeup)(unsigned int resume_on_alarm);
+	int bat_levels;
+	struct battery_thresh *bat_levels_noac;
+	struct battery_thresh *bat_levels_acin;
+};
+
+struct battery_thresh {
+	int voltage;
+	int percentage;
+	int status;
+};
+
+struct battery_stat {
+	int ac_status;         /* APM AC Present/Not Present */
+	int mainbat_status;    /* APM Main Battery Status */
+	int mainbat_percent;   /* Main Battery Percentage Charge */
+	int mainbat_voltage;   /* Main Battery Voltage */
+};
+
+struct sharpsl_pm_status {
+
+	int charge_mode;
+#define CHRG_ERROR    (-1)
+#define CHRG_OFF      (0)
+#define CHRG_ON       (1)
+#define CHRG_DONE     (2)
+
+	int charging;
+	int suspended;
+	int full_count;
+	int offline_charge_activate;
+	unsigned long charge_start_time;
+	struct sharpsl_charger_machinfo *machinfo;
+	struct battery_stat battstat;
+};
+
+extern struct sharpsl_pm_status sharpsl_pm;
+extern struct battery_thresh spitz_battery_levels_acin[];
+extern struct battery_thresh spitz_battery_levels_noac[];
+
+#define READ_GPIO_BIT(x)    (GPLR(x) & GPIO_bit(x))
+
+#define CHARGE_ON()         sharpsl_pm.machinfo->charge(1)
+#define CHARGE_OFF()        sharpsl_pm.machinfo->charge(0)
+#define CHARGE_LED_ON()     ({ printk("Charge LED On\n"); sharpsl_pm.machinfo->chargeled(1); })
+#define CHARGE_LED_OFF()    ({ printk("Charge LED Off\n"); sharpsl_pm.machinfo->chargeled(0); })
+#define CHARGE_LED_ERR()    printk("Charge LED Error\n");
+#define DISCHARGE_ON()      sharpsl_pm.machinfo->discharge(1)
+#define DISCHARGE_OFF()     sharpsl_pm.machinfo->discharge(0)
+#define STATUS_AC_IN        sharpsl_pm.machinfo->status_acin()
+#define STATUS_BATT_LOW     READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlow)
+#define STATUS_CHRG_FULL    READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull)
Index: linux-2.6.12/arch/arm/mach-pxa/sharpsl_pm.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.12/arch/arm/mach-pxa/sharpsl_pm.c	2005-08-26 11:00:54.000000000 +0100
@@ -0,0 +1,1036 @@
+/*
+ * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00 
+ * series of PDAs
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on code written by Sharp for 2.4 kernels
+ *
+ * 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/poll.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/apm_bios.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/scoop.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+#include <asm/apm.h>
+
+#include <asm/arch/sharpsl.h>
+#include <asm/arch/pxa-regs.h>
+#include "sharpsl.h"
+
+static struct {
+	u32 ffier, fflcr, ffmcr, ffspr, ffisr, ffdll, ffdlh;
+} sys_ctx;
+
+
+//#define DEBUG 1
+//#define BATTERY_CHECK_TIME              60*1       /* 1 min */
+#ifdef DEBUG
+#define DPRINTK(x, args...) printk(x,##args)
+#else
+#define DPRINTK(x, args...)  if (msglevel > 1 ) printk(x,##args);
+#endif
+
+#define SHARPSL_WAIT_CO_TIME            15          /* 15 Sec */
+#define SHARPSL_BATCHK_TIME             (5*HZ)      /* 5 sec */
+#define SHARPSL_CHARGE_ON_TIME_INTERVAL (1*60*HZ)   /* 1 min */
+#define SHARPSL_CHARGE_FINISH_TIME      (10*60*HZ)  /* 10 min */
+#define BATTERY_CHECK_TIME              60*10       /* 10 min */
+
+/* Time to wait after enabling battery discharge */
+#define SHARPSL_WAIT_DISCHARGE_ON              100 /* 100msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP   10  /* 10msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT   10  /* 10msec */
+#define SHARPSL_CHECK_BATTERY_WAIT_TIME_JKVAD  10  /* 10msec */
+#define SHARPSL_CHARGE_WAIT_TIME               15  /* 15 msec */
+#define SHARPSL_CHARGE_CO_CHECK_TIME           5   /* 5 msec */
+#define SHARPSL_CHARGE_RETRY_CNT               1   /* eqv. 10 min */
+
+#define SHARPSL_CHARGE_ON_VOLT         0x99  /* 2.9V */
+#define SHARPSL_CHARGE_ON_TEMP         0xe0  /* 2.9V */
+#define SHARPSL_CHARGE_ON_JKVAD_HIGH   0x9b  /* 6V */
+#define SHARPSL_CHARGE_ON_JKVAD_LOW    0x34  /* 2V */
+#define SHARPSL_FATAL_ACIN_VOLT        182   /* 3.45V */
+#define SHARPSL_FATAL_NOACIN_VOLT      170   /* 3.40V */
+
+/*
+SPITZ FIXME
+#define SHARPSL_SPITZ_FATAL_ACIN_VOLT_TEMP	187	// 3.56V
+#define SHARPSL_SPITZ_FATAL_NOACIN_VOLT_TEMP	185	// 3.51V
+#define SHARPSL_CNV_VALUE_NUM	5
+*/
+
+/*
+ * Prototypes
+ */
+static int sharpsl_read_MainBattery(void);
+static int sharpsl_off_charge_battery(void);
+static int GetLevelIndex(struct battery_thresh *thresh, int Volt);
+static int Get_DAC_Value(int);
+static int sharpsl_check_battery(int mode);
+static int sharpsl_ac_check(void);
+static int chkFatalBatt(void);
+static void sharpsl_average_clear(void);
+static int sharpsl_average_value(int ad);
+static void sharpsl_charge_on(void);
+static void sharpsl_charge_off(void);
+static void sharpsl_charge_error(void);
+static void sharpsl_charge_toggle(void *private_);
+static void sharpsl_battery_thread(void *private_);
+
+extern void corgibl_limit_intensity(int limit);
+extern struct pm_ops pxa_pm_ops;
+
+
+/*
+ * Variables 
+ */
+struct sharpsl_pm_status sharpsl_pm;
+static int (*pxa_pm_enter_orig)(suspend_state_t state);
+static unsigned long charge_alarm_active;
+static int msglevel;       /* Debug message level */
+static int corgipwr_bl_limit;   /* Are we limiting the Backlight Intensity */
+static int apm_event_queued;
+
+static struct timer_list ac_kick_timer;
+DECLARE_WORK(toggle_charger, sharpsl_charge_toggle, NULL);
+DECLARE_WORK(sharpsl_bat, sharpsl_battery_thread, NULL);
+
+
+/* 
+ * MAX1111 Command Definitions
+ */
+#define MAXCTRL_PD0      1u << 0
+#define MAXCTRL_PD1      1u << 1
+#define MAXCTRL_SGL      1u << 2
+#define MAXCTRL_UNI      1u << 3
+#define MAXCTRL_SEL_SH   4
+#define MAXCTRL_STR      1u << 7
+
+/* 
+ * MAX1111 Channel Definitions
+ */
+#define BATT_AD    4u
+#define BATT_THM   2u
+#define JK_VAD     6u
+
+#if defined(CONFIG_MACH_SPITZ) || defined(CONFIG_MACH_AKITA)
+void spitz_mailled(int on);
+#else
+#define spitz_mailled(x)   
+#endif
+
+struct battery_thresh spitz_battery_levels_acin[] = {
+	{ 213, 100, APM_BATTERY_STATUS_HIGH},
+	{ 212,  98, APM_BATTERY_STATUS_HIGH},
+	{ 211,  95, APM_BATTERY_STATUS_HIGH},
+	{ 210,  93, APM_BATTERY_STATUS_HIGH},
+	{ 209,  90, APM_BATTERY_STATUS_HIGH},
+	{ 208,  88, APM_BATTERY_STATUS_HIGH},
+	{ 207,  85, APM_BATTERY_STATUS_HIGH},
+	{ 206,  83, APM_BATTERY_STATUS_HIGH},
+	{ 205,  80, APM_BATTERY_STATUS_HIGH},
+	{ 204,  78, APM_BATTERY_STATUS_HIGH},
+	{ 203,  75, APM_BATTERY_STATUS_HIGH},
+	{ 202,  73, APM_BATTERY_STATUS_HIGH},
+	{ 201,  70, APM_BATTERY_STATUS_HIGH},
+	{ 200,  68, APM_BATTERY_STATUS_HIGH},
+	{ 199,  65, APM_BATTERY_STATUS_HIGH},
+	{ 198,  63, APM_BATTERY_STATUS_HIGH},
+	{ 197,  60, APM_BATTERY_STATUS_HIGH},
+	{ 196,  58, APM_BATTERY_STATUS_HIGH},
+	{ 195,  55, APM_BATTERY_STATUS_HIGH},
+	{ 194,  53, APM_BATTERY_STATUS_HIGH},
+	{ 193,  50, APM_BATTERY_STATUS_HIGH},
+	{ 192,  48, APM_BATTERY_STATUS_HIGH},
+	{ 192,  45, APM_BATTERY_STATUS_HIGH},
+	{ 191,  43, APM_BATTERY_STATUS_HIGH},
+	{ 191,  40, APM_BATTERY_STATUS_HIGH},
+	{ 190,  38, APM_BATTERY_STATUS_HIGH},
+	{ 190,  35, APM_BATTERY_STATUS_HIGH},
+	{ 189,  33, APM_BATTERY_STATUS_HIGH},
+	{ 188,  30, APM_BATTERY_STATUS_HIGH},
+	{ 187,  28, APM_BATTERY_STATUS_LOW},
+	{ 186,  25, APM_BATTERY_STATUS_LOW},
+	{ 185,  23, APM_BATTERY_STATUS_LOW},
+	{ 184,  20, APM_BATTERY_STATUS_LOW},
+	{ 183,  18, APM_BATTERY_STATUS_LOW},
+	{ 182,  15, APM_BATTERY_STATUS_LOW},
+	{ 181,  13, APM_BATTERY_STATUS_LOW},
+	{ 180,  10, APM_BATTERY_STATUS_LOW},
+	{ 179,   8, APM_BATTERY_STATUS_LOW},
+	{ 178,   5, APM_BATTERY_STATUS_LOW},
+	{   0,   0, APM_BATTERY_STATUS_CRITICAL},
+};
+
+struct battery_thresh  spitz_battery_levels_noac[] = {
+	{ 213, 100, APM_BATTERY_STATUS_HIGH},
+	{ 212,  98, APM_BATTERY_STATUS_HIGH},
+	{ 211,  95, APM_BATTERY_STATUS_HIGH},
+	{ 210,  93, APM_BATTERY_STATUS_HIGH},
+	{ 209,  90, APM_BATTERY_STATUS_HIGH},
+	{ 208,  88, APM_BATTERY_STATUS_HIGH},
+	{ 207,  85, APM_BATTERY_STATUS_HIGH},
+	{ 206,  83, APM_BATTERY_STATUS_HIGH},
+	{ 205,  80, APM_BATTERY_STATUS_HIGH},
+	{ 204,  78, APM_BATTERY_STATUS_HIGH},
+	{ 203,  75, APM_BATTERY_STATUS_HIGH},
+	{ 202,  73, APM_BATTERY_STATUS_HIGH},
+	{ 201,  70, APM_BATTERY_STATUS_HIGH},
+	{ 200,  68, APM_BATTERY_STATUS_HIGH},
+	{ 199,  65, APM_BATTERY_STATUS_HIGH},
+	{ 198,  63, APM_BATTERY_STATUS_HIGH},
+	{ 197,  60, APM_BATTERY_STATUS_HIGH},
+	{ 196,  58, APM_BATTERY_STATUS_HIGH},
+	{ 195,  55, APM_BATTERY_STATUS_HIGH},
+	{ 194,  53, APM_BATTERY_STATUS_HIGH},
+	{ 193,  50, APM_BATTERY_STATUS_HIGH},
+	{ 192,  48, APM_BATTERY_STATUS_HIGH},
+	{ 191,  45, APM_BATTERY_STATUS_HIGH},
+	{ 190,  43, APM_BATTERY_STATUS_HIGH},
+	{ 189,  40, APM_BATTERY_STATUS_HIGH},
+	{ 188,  38, APM_BATTERY_STATUS_HIGH},
+	{ 187,  35, APM_BATTERY_STATUS_HIGH},
+	{ 186,  33, APM_BATTERY_STATUS_HIGH},
+	{ 185,  30, APM_BATTERY_STATUS_HIGH},
+	{ 184,  28, APM_BATTERY_STATUS_LOW},
+	{ 183,  25, APM_BATTERY_STATUS_LOW},
+	{ 182,  23, APM_BATTERY_STATUS_LOW},
+	{ 181,  20, APM_BATTERY_STATUS_LOW},
+	{ 180,  18, APM_BATTERY_STATUS_LOW},
+	{ 179,  15, APM_BATTERY_STATUS_LOW},
+	{ 178,  13, APM_BATTERY_STATUS_LOW},
+	{ 177,  10, APM_BATTERY_STATUS_LOW},
+	{ 176,   8, APM_BATTERY_STATUS_LOW},
+	{ 175,   5, APM_BATTERY_STATUS_LOW},
+	{   0,   0, APM_BATTERY_STATUS_CRITICAL},
+};
+
+void corgipwr_kick_battery(void)
+{
+	schedule_delayed_work(&sharpsl_bat, HZ/8);
+}
+EXPORT_SYMBOL(corgipwr_kick_battery);
+
+
+static void sharpsl_battery_thread(void *private_)
+{
+	int voltage, percent, i = 0;
+	struct battery_thresh *thresh;
+
+	if (!sharpsl_pm.machinfo)
+		return;
+
+	/* Get ac status */
+	sharpsl_pm.battstat.ac_status = (!(STATUS_AC_IN) ? APM_AC_OFFLINE : APM_AC_ONLINE);
+
+	/* Corgi cannot confirm when battery fully charged so periodically kick! */
+	if (machine_is_corgi() && (sharpsl_pm.charge_mode == CHRG_ON) 
+			&& time_after(jiffies, sharpsl_pm.charge_start_time +  SHARPSL_CHARGE_ON_TIME_INTERVAL))
+		schedule_work(&toggle_charger);
+
+	while(1) {
+		voltage = sharpsl_read_MainBattery();
+		if (voltage > 0) break;
+		if (i++ > 5) {
+			voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage;
+			printk("Warning: Cannot read main battery!\n");
+			break;
+		}
+	}
+
+	voltage = sharpsl_average_value(voltage);
+		
+	if (sharpsl_pm.charge_mode == CHRG_ON) 
+		thresh=sharpsl_pm.machinfo->bat_levels_acin;
+	else
+		thresh=sharpsl_pm.machinfo->bat_levels_noac;
+	
+	i=GetLevelIndex(thresh, voltage);
+	percent=thresh[i].percentage;
+
+	/* If there's no AC, the battery can't charge! */
+	if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) ||  percent <= sharpsl_pm.battstat.mainbat_percent) {
+		sharpsl_pm.battstat.mainbat_voltage = voltage;
+		sharpsl_pm.battstat.mainbat_status = thresh[i].status;
+		sharpsl_pm.battstat.mainbat_percent = percent;
+	}
+
+	DPRINTK("Battery Voltage: %d\n", voltage);
+	DPRINTK("        Status: %d\n", sharpsl_pm.battstat.mainbat_status);
+	DPRINTK("        Percentage: %d (at time %lx)\n", sharpsl_pm.battstat.mainbat_percent, jiffies);
+
+	/* If battery is low. limit backlight intensity to save power. */
+	if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 
+			&& ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW) ||
+			(sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) {
+		if (corgipwr_bl_limit==0) {
+			corgibl_limit_intensity(1);
+			corgipwr_bl_limit=1;
+		}
+	} else if (corgipwr_bl_limit==1) {
+		corgibl_limit_intensity(0);
+		corgipwr_bl_limit=0;
+	}
+
+	/* Suspend if critical battery level */
+	if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 
+			&& (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL) 
+			&& !apm_event_queued) {
+		apm_event_queued = 1;
+		printk("Fatal Off\n");
+		//FIXME
+		//apm_queue_event(APM_CRITICAL_SUSPEND);
+	} 
+
+	schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME);
+}
+
+
+
+static void sharpsl_charge_on(void)
+{
+	DPRINTK("Turning Charger On\n");
+
+	sharpsl_pm.full_count = 0;
+	sharpsl_pm.charge_mode = CHRG_ON;
+	schedule_delayed_work(&toggle_charger, HZ/4);
+	schedule_delayed_work(&sharpsl_bat, HZ/2);
+}
+
+static void sharpsl_charge_toggle(void *private_)
+{
+	DPRINTK("Toggling Charger at time: %lx\n", jiffies);
+
+	/* Can't charge if no ac... */
+	if (STATUS_AC_IN == 0) {
+		sharpsl_charge_off();
+		return;
+	} else if ((sharpsl_check_battery(1) < 0) || (sharpsl_ac_check() < 0)) {
+		sharpsl_charge_error();
+		return;
+	}
+
+	CHARGE_LED_ON();
+	CHARGE_OFF();
+	mdelay(SHARPSL_CHARGE_WAIT_TIME);
+	CHARGE_ON();
+
+	sharpsl_pm.charge_start_time = jiffies;
+}
+
+
+static void sharpsl_charge_off(void)
+{
+	DPRINTK("Turning Charger Off\n");
+
+	CHARGE_OFF();
+	CHARGE_LED_OFF();
+	sharpsl_pm.charge_mode = CHRG_OFF;
+
+	schedule_work(&sharpsl_bat);
+}
+
+static void sharpsl_charge_error(void)
+{
+	CHARGE_LED_ERR();
+	CHARGE_OFF();
+	sharpsl_pm.charge_mode = CHRG_ERROR;
+}
+
+static void sharpsl_ac_kick_timer(unsigned long data)
+{
+	int acin = STATUS_AC_IN;
+
+	DPRINTK("AC Status: %d\n",acin);
+
+	sharpsl_average_clear();
+	if (acin && (sharpsl_pm.charge_mode != CHRG_ON)) {
+		sharpsl_charge_on();
+	} else if (sharpsl_pm.charge_mode == CHRG_ON) {
+		sharpsl_charge_off();
+	}
+
+	schedule_work(&sharpsl_bat);
+}
+
+
+static irqreturn_t sharpsl_ac_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+	/* wait delay */
+	mod_timer(&ac_kick_timer, jiffies + HZ / 4);
+
+	return IRQ_HANDLED;
+}
+
+/* Charging Finished Interrupt */
+/* Not present on Corgi */
+static irqreturn_t sharpsl_chrg_full_interrupt(int irq, void *dev_id, struct pt_regs *fp)
+{
+	DPRINTK("Charge Full at time: %lx\n", jiffies);
+
+	if (sharpsl_pm.suspended)
+		return IRQ_HANDLED;
+
+	sharpsl_pm.full_count++;
+
+	if (STATUS_AC_IN == 0) {
+		DPRINTK("Charge Full: AC removed - stop charging!\n");
+		sharpsl_charge_off();
+	} else if (sharpsl_pm.full_count < 2) {
+		DPRINTK("Charge Full: Count too low\n");
+		schedule_work(&toggle_charger);
+	} else if (time_after(jiffies, sharpsl_pm.charge_start_time +  SHARPSL_CHARGE_FINISH_TIME)) {
+		DPRINTK("Charge Full: Interrupt generated too slowly - retry.\n");
+		schedule_work(&toggle_charger);
+	} else {
+		sharpsl_charge_off();
+		sharpsl_pm.charge_mode = CHRG_DONE;
+		DPRINTK("Charge Full: Charging Finished\n");
+	} 
+	return IRQ_HANDLED;
+}
+
+
+/*
+ * Maintain an average of the last 10 readings
+ */
+#define SHARPSL_CNV_VALUE_NUM    10
+static int sharpsl_ad_index;
+
+static void sharpsl_average_clear(void)
+{
+	sharpsl_ad_index = 0;
+}
+
+static int sharpsl_average_value(int ad)
+{
+	int i, ad_val = 0;
+	static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1];
+
+	if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) {
+		sharpsl_ad_index = 0;
+		return ad;
+	}
+
+	sharpsl_ad[sharpsl_ad_index] = ad;
+	sharpsl_ad_index++;
+	if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) {
+		for(i=0;i<(SHARPSL_CNV_VALUE_NUM-1);i++)
+			sharpsl_ad[i] = sharpsl_ad[i+1];
+		sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM -1;
+	}
+	for(i=0;i<sharpsl_ad_index;i++)
+		ad_val += sharpsl_ad[i];
+
+	return (ad_val / sharpsl_ad_index);
+}
+
+
+
+static int GetLevelIndex(struct battery_thresh *thresh, int Volt)
+{
+	int i = sharpsl_pm.machinfo->bat_levels-1;
+
+	while (i > 0 && (Volt > thresh[i].voltage))
+		i--;
+		
+	return i;
+}
+
+/* 
+ * Translate Analog signal to Digital data
+ */
+static int Get_DAC_Value(int channel)
+{
+	return corgi_ssp_max1111_get((channel << MAXCTRL_SEL_SH) | MAXCTRL_PD0 | MAXCTRL_PD1 
+			| MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR);
+}
+
+static int sharpsl_read_MainBattery(void)
+{
+	return Get_DAC_Value(BATT_AD);
+}
+
+static int sharpsl_read_Temp(void)
+{
+	int temp;
+
+	sharpsl_pm.machinfo->measure_temp(1);
+
+	mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
+	temp = Get_DAC_Value(BATT_THM);
+
+	sharpsl_pm.machinfo->measure_temp(0);
+
+	return temp;
+}
+
+static int sharpsl_read_jkvad(void)
+{
+	return Get_DAC_Value(JK_VAD);
+}
+
+/*
+ * Take an array of 5 integers, remove the maximum and minimum values
+ * and return the average.
+ */
+static int get_select_val(int *val)
+{
+	int i,j,k,temp,sum = 0;
+
+	for(i=0;i<5;i++) {
+		DPRINTK("val %d = %d\n",i,val[i]);
+	}
+
+	/* Get MAX */
+	temp = val[0];
+	j=0;
+	for(i=1;i<5;i++) {
+		if (temp < val[i]) { temp = val[i]; j = i; }
+	}
+
+	/* Get MIN */
+	temp = val[4];
+	k=4;
+	for(i=3;i>=0;i--) {
+		if (temp > val[i]) { temp = val[i]; k = i; }
+	}
+
+	for(i=0;i<5;i++) {
+		if (i == j || i == k ) continue;
+		sum += val[i];
+	}
+	DPRINTK("val = %d(%d)\n",sum,sum/3);
+
+	return ( sum / 3 );
+}
+
+/*
+ * Check: mode=0 - Check Temperature and Voltage
+ *        mode=1 - Check Temperature Only
+ */
+static int sharpsl_check_battery(int mode)
+{
+	int val, i, buff[5];
+
+	/* Check Temp : check inserting battery ? */
+	for(i=0;i<5;i++) {
+		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
+		buff[i] = sharpsl_read_Temp();
+	}
+	
+	val = get_select_val(buff);
+
+	DPRINTK("Temperature: %d\n",val);
+	if (val > SHARPSL_CHARGE_ON_TEMP) 
+		return -1;
+	if (mode == 1) 
+		return 0;
+
+	/* disable charge, enable discharge */
+	CHARGE_OFF();
+	DISCHARGE_ON();
+	mdelay(SHARPSL_WAIT_DISCHARGE_ON);
+
+	spitz_mailled(1);
+
+	/* Check Voltage : check full charging */
+	for(i=0;i<5;i++) {
+		buff[i] = sharpsl_read_MainBattery();
+		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
+	}
+
+	spitz_mailled(0);
+
+	DISCHARGE_OFF();;
+
+	val = get_select_val(buff);
+	DPRINTK("Voltage: %d\n",val);
+
+	if (val < SHARPSL_CHARGE_ON_VOLT) 
+		return -1;
+
+	return 0;
+}
+
+static int sharpsl_ac_check(void)
+{
+	int temp, i, buff[5];
+
+	/* Check JK_VAD */
+	for(i=0;i<5;i++) {
+		buff[i] = sharpsl_read_jkvad();
+		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_JKVAD);
+	}
+	
+	temp = get_select_val(buff);
+	DPRINTK("jkvad: %d\n",temp);
+
+	/* error occured ! */
+	if ((temp > SHARPSL_CHARGE_ON_JKVAD_HIGH) || (temp < SHARPSL_CHARGE_ON_JKVAD_LOW)) {
+		printk("AC Error!\n");
+		if (!apm_event_queued && !sharpsl_pm.suspended) {
+			apm_event_queued = 1;
+			apm_queue_event(APM_CRITICAL_SUSPEND);
+		}
+		CHARGE_LED_ERR();
+		return -1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int corgi_batt_suspend(struct device *dev, pm_message_t state, uint32_t level)
+{
+	if (level == SUSPEND_POWER_DOWN) {
+
+		DPRINTK("SharpSL Battery Suspending \n");
+		sharpsl_pm.suspended=1;
+
+		flush_scheduled_work();
+		
+		if (sharpsl_pm.charge_mode == CHRG_ON) {
+			sharpsl_pm.offline_charge_activate = 1;
+		} else {
+			sharpsl_pm.offline_charge_activate = 0;
+		}
+	}
+	return 0;
+}	
+		
+static int corgi_batt_resume(struct device *dev, uint32_t level)
+{
+	if (level == RESUME_POWER_ON) {
+
+		DPRINTK("SharpSL Battery Resuming \n");
+	
+		/* Clear the reset source indicators as they break the bootloader upon reboot */
+		RCSR=0x0f;
+		apm_event_queued = 0;
+		sharpsl_average_clear();
+
+		sharpsl_pm.suspended=0;
+	}
+	return 0;
+}
+
+static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
+{
+	DPRINTK("Time is: %8x\n",RCNR);
+	
+	DPRINTK("sharpsl_pm.offline_charge_activate = %d\n",sharpsl_pm.offline_charge_activate);
+	/* not charging and AC-IN! */
+
+	if (sharpsl_pm.offline_charge_activate && (STATUS_AC_IN != 0)) {
+		DPRINTK("Activating Charger...\n");
+		sharpsl_pm.charge_mode = CHRG_OFF;
+		sharpsl_pm.offline_charge_activate = 0;
+		sharpsl_off_charge_battery();
+	}
+
+	sharpsl_pm.machinfo->presuspend();
+
+	PEDR = 0xffffffff; /* clear it */
+
+	charge_alarm_active=0;
+	if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (BATTERY_CHECK_TIME + 30))) || !alarm_enable)) {
+		RTSR &= RTSR_ALE;
+		RTAR = RCNR + BATTERY_CHECK_TIME;
+		DPRINTK("Charging alarm at: %8x\n",RTAR);			
+		charge_alarm_active=1;
+	} else if (alarm_enable) {
+		RTSR &= RTSR_ALE;
+		RTAR = alarm_time;
+		DPRINTK("User alarm at: %8x\n",RTAR);
+	} else {
+		DPRINTK("No alarms set.\n");
+	}
+
+	sys_ctx.ffier = FFIER;
+	sys_ctx.fflcr = FFLCR;
+	sys_ctx.ffmcr = FFMCR;
+	sys_ctx.ffspr = FFSPR;
+	sys_ctx.ffisr = FFISR;
+	FFLCR |= 0x80;
+	sys_ctx.ffdll = FFDLL;
+	sys_ctx.ffdlh = FFDLH;
+	FFLCR &= 0xef;
+
+	while (!(FFLSR & LSR_TEMT)) {
+		msleep(10);
+	}
+
+	pxa_pm_enter_orig(state);
+
+	sharpsl_pm.machinfo->postsuspend();
+
+	FFMCR = sys_ctx.ffmcr;
+	FFSPR = sys_ctx.ffspr;
+	FFLCR = sys_ctx.fflcr;
+	FFLCR |= 0x80;
+	FFDLH = sys_ctx.ffdlh;
+	FFDLL = sys_ctx.ffdll;
+	FFLCR = sys_ctx.fflcr;
+	FFISR = sys_ctx.ffisr;
+	FFLCR = 0x07;
+	FFIER = sys_ctx.ffier;
+
+	DPRINTK("Corgi woken up from suspend: %x\n",PEDR);
+}
+
+static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
+{
+	if (!sharpsl_pm.machinfo->should_wakeup(!charge_alarm_active && alarm_enable) ) 
+	{
+		if (!charge_alarm_active) {
+			DPRINTK("No user triggered wakeup events and not charging. Strange. Suspend...\n");
+			corgi_goto_sleep(alarm_time, alarm_enable, state);
+			return 1;
+		}
+		if(sharpsl_off_charge_battery()) {
+			DPRINTK("Charging. Suspend...\n");
+			corgi_goto_sleep(alarm_time, alarm_enable, state);
+			return 1;
+		} 
+		DPRINTK("User triggered wakeup in Charging Code...\n");
+	}
+
+	if ((STATUS_BATT_LOW == 0) || (chkFatalBatt() < 0) ) 
+	{
+		DPRINTK("Fatal condition. Suspend...\n");
+		//FIXME
+		//corgi_goto_sleep(alarm_time, alarm_enable, state);
+		//return 1;
+	}
+
+	return 0;
+}
+
+static int corgi_pxa_pm_enter(suspend_state_t state)
+{
+	unsigned long alarm_time=RTAR;
+	unsigned int alarm_status=((RTSR & RTSR_ALE) != 0);
+
+	DPRINTK("Corgi suspending for first time!\n");
+
+	corgi_goto_sleep(alarm_time, alarm_status, state);
+	
+	while (corgi_enter_suspend(alarm_time,alarm_status,state)) 
+		{}
+
+	DPRINTK("Device resuming...\n");
+
+	return 0;
+}
+
+#endif
+
+
+/*
+ * Check for fatal battery errors
+ * Fatal returns -1 
+ */
+static int chkFatalBatt(void)
+{
+	int buff[5],temp,i,acin;
+
+	DPRINTK("Check fatal batt\n");
+
+	/* Check AC-Adapter */
+	acin=STATUS_AC_IN;
+
+	if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
+		printk("Disabling charger and turning discharge on\n");
+		CHARGE_OFF();
+		udelay(100);
+		DISCHARGE_ON();	/* enable discharge */
+		mdelay(SHARPSL_WAIT_DISCHARGE_ON);
+	}
+	
+	spitz_mailled(1);
+
+	/* Check battery : check inserting battery ? */
+	for(i=0;i<5;i++) {
+		buff[i] = sharpsl_read_MainBattery();
+		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
+	}
+
+	spitz_mailled(0);
+
+	temp = get_select_val(buff);
+	DPRINTK("chkFatalBatt Voltage: %d\n",temp);
+
+	if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
+		printk("Reenabling charger and turning discharge off\n");
+		udelay(100);
+		CHARGE_ON();
+		DISCHARGE_OFF();
+	}
+
+	DPRINTK("chkFatalBatt Voltage: ac %d, noac %d\n",SHARPSL_FATAL_ACIN_VOLT,SHARPSL_FATAL_NOACIN_VOLT);
+
+	printk("Val10: %d\n", sharpsl_read_MainBattery());
+
+	if ((acin && (temp < SHARPSL_FATAL_ACIN_VOLT)) ||
+			(!acin && (temp < SHARPSL_FATAL_NOACIN_VOLT))) 
+			return -1;
+	return 0;
+}
+
+static int sharpsl_off_charge_error(void)
+{
+	DPRINTK("STEP ERROR\n");
+	CHARGE_OFF();
+	CHARGE_LED_ERR();
+	sharpsl_pm.charge_mode = CHRG_ERROR;
+	return 1;
+}
+
+/*
+ * Charging Control while suspended
+ * Return 1 - go straight to sleep
+ * Return 0 - sleep or wakeup depending on other factors
+ */
+static int sharpsl_off_charge_battery(void)
+{
+	int time;
+	
+	DPRINTK("Charge Mode: %d\n", sharpsl_pm.charge_mode);
+
+	if (sharpsl_pm.charge_mode == CHRG_OFF) {
+		DPRINTK("STEP 1\n");
+	
+		/* AC Check */
+		if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery(1) < 0)) 
+			return sharpsl_off_charge_error();
+
+		DPRINTK("STEP 2\n");
+
+		/* Start Charging */
+		CHARGE_LED_ON();
+		CHARGE_OFF();
+		mdelay(SHARPSL_CHARGE_WAIT_TIME);
+		CHARGE_ON();
+
+		sharpsl_pm.charge_mode = CHRG_ON;
+		sharpsl_pm.full_count = 0;
+   			
+		return 1;
+	} else if (sharpsl_pm.charge_mode != CHRG_ON) {
+		return 1;
+	}
+
+	if (sharpsl_pm.full_count == 0) {
+		int time;
+		
+		DPRINTK("STEP 3\n");
+    			
+		if (sharpsl_check_battery(0) < 0) 
+			return sharpsl_off_charge_error();
+
+		CHARGE_OFF();
+		mdelay(SHARPSL_CHARGE_WAIT_TIME);
+		CHARGE_ON();		/* Charge ON */
+		sharpsl_pm.charge_mode = CHRG_ON;
+		DPRINTK("STEP 4\n");
+    		
+		mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
+
+		time = RCNR;
+		while(1) {
+			/* Check if any wakeup event had occured */
+			if (sharpsl_pm.machinfo->charger_wakeup() != 0) 
+				return 0;
+			/* Check for timeout */
+			if ((RCNR - time) > SHARPSL_WAIT_CO_TIME) 
+				return 1;
+			if (STATUS_CHRG_FULL) {
+				DPRINTK("Charge full occured\n");
+	   			sharpsl_pm.full_count++;
+				/* retry charge */
+				CHARGE_OFF();
+				mdelay(SHARPSL_CHARGE_WAIT_TIME);
+				CHARGE_ON();  /* Charge ON */
+				return 1;
+			}
+		}
+	}
+
+	DPRINTK("STEP 6\n");
+    			
+	mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
+    			
+	time = RCNR;
+	while(1) {
+		/* Check if any wakeup event had occured */
+		if (sharpsl_pm.machinfo->charger_wakeup() != 0) 
+			return 0;
+		/* Check for timeout */
+		if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) {
+			if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) {
+				DPRINTK("Not charged sufficiently. Retry...\n");
+				sharpsl_pm.full_count = 0;
+			} 
+			sharpsl_pm.full_count++;
+			return 1;
+		}
+		if (STATUS_CHRG_FULL) {
+			DPRINTK("Charging complete\n");
+			CHARGE_LED_OFF();
+			CHARGE_OFF();
+			sharpsl_pm.charge_mode = CHRG_DONE;
+			return 1;
+		}
+	}
+}
+
+
+/* 
+ * Sysfs functions 
+ */
+
+static ssize_t msglevel_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n",msglevel);
+}
+
+static ssize_t msglevel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+	msglevel = simple_strtoul(buf, NULL, 16);
+	return count;
+}
+
+static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_percent);
+}
+
+static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_voltage);
+}
+
+static DEVICE_ATTR(msglevel, 0644, msglevel_show, msglevel_store);
+static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL);
+static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL);
+
+/*** Config & Setup **********************************************************/
+
+extern void (*apm_get_power_status)(struct apm_power_info *);
+
+
+static void sharpsl_apm_get_power_status(struct apm_power_info *info)
+{
+	info->ac_line_status = sharpsl_pm.battstat.ac_status;
+
+	if (sharpsl_pm.charge_mode == CHRG_ON)
+		info->battery_status = APM_BATTERY_STATUS_CHARGING;
+	else 
+		info->battery_status = sharpsl_pm.battstat.mainbat_status;
+
+	info->battery_flag = (1 << info->battery_status);
+	info->battery_life = sharpsl_pm.battstat.mainbat_percent;
+}
+
+static int __init corgi_batt_probe(struct device *dev)
+{
+	if (!dev->platform_data)
+		return -EINVAL;
+
+	sharpsl_pm.machinfo=dev->platform_data;
+	sharpsl_pm.charge_mode = CHRG_OFF;
+	sharpsl_pm.suspended=0;
+
+	sharpsl_pm.machinfo->init();
+
+	init_timer(&ac_kick_timer);
+	ac_kick_timer.function = sharpsl_ac_kick_timer;
+
+	pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN);
+	pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batfull | GPIO_IN);
+	pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batlow | GPIO_IN);
+
+	/* Register interrupt handler. */
+	if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_interrupt, SA_INTERRUPT, "AC Input Detect", sharpsl_ac_interrupt)) {
+		printk("Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin));
+	}
+	else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin),IRQT_BOTHEDGE); /* AC IN */
+
+	if (!machine_is_corgi()) 
+	{
+		/* Register interrupt handler. */
+		if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_interrupt, SA_INTERRUPT, "CO", sharpsl_chrg_full_interrupt)) {
+			printk("Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull));
+		}
+		else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull),IRQT_RISING); /* CHRG FULL */
+	}
+	
+	device_create_file(dev, &dev_attr_msglevel);
+	device_create_file(dev, &dev_attr_battery_percentage);
+	device_create_file(dev, &dev_attr_battery_voltage);
+
+	apm_get_power_status = sharpsl_apm_get_power_status;
+
+	pxa_pm_enter_orig=pxa_pm_ops.enter;
+	pxa_pm_ops.enter=corgi_pxa_pm_enter;
+	
+	mod_timer(&ac_kick_timer, jiffies + HZ / 4);
+
+	return 0;
+}
+
+static int corgi_batt_remove(struct device *dev)
+{
+	free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_interrupt);
+	if (!machine_is_corgi()) 
+		free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_interrupt);
+
+	device_remove_file(dev, &dev_attr_msglevel);
+	device_remove_file(dev, &dev_attr_battery_percentage);
+	device_remove_file(dev, &dev_attr_battery_voltage);
+
+	return 0;
+}
+
+static struct device_driver corgi_batt_driver = {
+	.name		= "corgi-pm",
+	.bus		= &platform_bus_type,
+	.probe		= corgi_batt_probe,
+	.remove		= corgi_batt_remove,
+	.suspend	= corgi_batt_suspend,
+	.resume		= corgi_batt_resume,
+};
+
+static int __devinit corgi_batt_init(void)
+{
+	return driver_register(&corgi_batt_driver);
+}
+
+static void corgi_batt_exit(void)
+{
+ 	driver_unregister(&corgi_batt_driver);
+}
+
+module_init(corgi_batt_init);
+module_exit(corgi_batt_exit);

