162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00
462306a36Sopenharmony_ci * series of PDAs
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2004-2005 Richard Purdie
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on code written by Sharp for 2.4 kernels
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#undef DEBUG
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/apm-emulation.h>
1862306a36Sopenharmony_ci#include <linux/timer.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/leds.h>
2162306a36Sopenharmony_ci#include <linux/suspend.h>
2262306a36Sopenharmony_ci#include <linux/gpio.h>
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/mach-types.h>
2662306a36Sopenharmony_ci#include "pm.h"
2762306a36Sopenharmony_ci#include "pxa2xx-regs.h"
2862306a36Sopenharmony_ci#include "regs-rtc.h"
2962306a36Sopenharmony_ci#include "sharpsl_pm.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * Constants
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci#define SHARPSL_CHARGE_ON_TIME_INTERVAL        (msecs_to_jiffies(1*60*1000))  /* 1 min */
3562306a36Sopenharmony_ci#define SHARPSL_CHARGE_FINISH_TIME             (msecs_to_jiffies(10*60*1000)) /* 10 min */
3662306a36Sopenharmony_ci#define SHARPSL_BATCHK_TIME                    (msecs_to_jiffies(15*1000))    /* 15 sec */
3762306a36Sopenharmony_ci#define SHARPSL_BATCHK_TIME_SUSPEND            (60*10)                        /* 10 min */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define SHARPSL_WAIT_CO_TIME                   15  /* 15 sec */
4062306a36Sopenharmony_ci#define SHARPSL_WAIT_DISCHARGE_ON              100 /* 100 msec */
4162306a36Sopenharmony_ci#define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP   10  /* 10 msec */
4262306a36Sopenharmony_ci#define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT   10  /* 10 msec */
4362306a36Sopenharmony_ci#define SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN   10  /* 10 msec */
4462306a36Sopenharmony_ci#define SHARPSL_CHARGE_WAIT_TIME               15  /* 15 msec */
4562306a36Sopenharmony_ci#define SHARPSL_CHARGE_CO_CHECK_TIME           5   /* 5 msec */
4662306a36Sopenharmony_ci#define SHARPSL_CHARGE_RETRY_CNT               1   /* eqv. 10 min */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * Prototypes
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_ci#ifdef CONFIG_PM
5262306a36Sopenharmony_cistatic int sharpsl_off_charge_battery(void);
5362306a36Sopenharmony_cistatic int sharpsl_check_battery_voltage(void);
5462306a36Sopenharmony_ci#endif
5562306a36Sopenharmony_cistatic int sharpsl_check_battery_temp(void);
5662306a36Sopenharmony_cistatic int sharpsl_ac_check(void);
5762306a36Sopenharmony_cistatic int sharpsl_average_value(int ad);
5862306a36Sopenharmony_cistatic void sharpsl_average_clear(void);
5962306a36Sopenharmony_cistatic void sharpsl_charge_toggle(struct work_struct *private_);
6062306a36Sopenharmony_cistatic void sharpsl_battery_thread(struct work_struct *private_);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * Variables
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_cistruct sharpsl_pm_status sharpsl_pm;
6762306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(toggle_charger, sharpsl_charge_toggle);
6862306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(sharpsl_bat, sharpsl_battery_thread);
6962306a36Sopenharmony_ciDEFINE_LED_TRIGGER(sharpsl_charge_led_trigger);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct battery_thresh sharpsl_battery_levels_acin[] = {
7462306a36Sopenharmony_ci	{ 213, 100},
7562306a36Sopenharmony_ci	{ 212,  98},
7662306a36Sopenharmony_ci	{ 211,  95},
7762306a36Sopenharmony_ci	{ 210,  93},
7862306a36Sopenharmony_ci	{ 209,  90},
7962306a36Sopenharmony_ci	{ 208,  88},
8062306a36Sopenharmony_ci	{ 207,  85},
8162306a36Sopenharmony_ci	{ 206,  83},
8262306a36Sopenharmony_ci	{ 205,  80},
8362306a36Sopenharmony_ci	{ 204,  78},
8462306a36Sopenharmony_ci	{ 203,  75},
8562306a36Sopenharmony_ci	{ 202,  73},
8662306a36Sopenharmony_ci	{ 201,  70},
8762306a36Sopenharmony_ci	{ 200,  68},
8862306a36Sopenharmony_ci	{ 199,  65},
8962306a36Sopenharmony_ci	{ 198,  63},
9062306a36Sopenharmony_ci	{ 197,  60},
9162306a36Sopenharmony_ci	{ 196,  58},
9262306a36Sopenharmony_ci	{ 195,  55},
9362306a36Sopenharmony_ci	{ 194,  53},
9462306a36Sopenharmony_ci	{ 193,  50},
9562306a36Sopenharmony_ci	{ 192,  48},
9662306a36Sopenharmony_ci	{ 192,  45},
9762306a36Sopenharmony_ci	{ 191,  43},
9862306a36Sopenharmony_ci	{ 191,  40},
9962306a36Sopenharmony_ci	{ 190,  38},
10062306a36Sopenharmony_ci	{ 190,  35},
10162306a36Sopenharmony_ci	{ 189,  33},
10262306a36Sopenharmony_ci	{ 188,  30},
10362306a36Sopenharmony_ci	{ 187,  28},
10462306a36Sopenharmony_ci	{ 186,  25},
10562306a36Sopenharmony_ci	{ 185,  23},
10662306a36Sopenharmony_ci	{ 184,  20},
10762306a36Sopenharmony_ci	{ 183,  18},
10862306a36Sopenharmony_ci	{ 182,  15},
10962306a36Sopenharmony_ci	{ 181,  13},
11062306a36Sopenharmony_ci	{ 180,  10},
11162306a36Sopenharmony_ci	{ 179,   8},
11262306a36Sopenharmony_ci	{ 178,   5},
11362306a36Sopenharmony_ci	{   0,   0},
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistruct battery_thresh sharpsl_battery_levels_noac[] = {
11762306a36Sopenharmony_ci	{ 213, 100},
11862306a36Sopenharmony_ci	{ 212,  98},
11962306a36Sopenharmony_ci	{ 211,  95},
12062306a36Sopenharmony_ci	{ 210,  93},
12162306a36Sopenharmony_ci	{ 209,  90},
12262306a36Sopenharmony_ci	{ 208,  88},
12362306a36Sopenharmony_ci	{ 207,  85},
12462306a36Sopenharmony_ci	{ 206,  83},
12562306a36Sopenharmony_ci	{ 205,  80},
12662306a36Sopenharmony_ci	{ 204,  78},
12762306a36Sopenharmony_ci	{ 203,  75},
12862306a36Sopenharmony_ci	{ 202,  73},
12962306a36Sopenharmony_ci	{ 201,  70},
13062306a36Sopenharmony_ci	{ 200,  68},
13162306a36Sopenharmony_ci	{ 199,  65},
13262306a36Sopenharmony_ci	{ 198,  63},
13362306a36Sopenharmony_ci	{ 197,  60},
13462306a36Sopenharmony_ci	{ 196,  58},
13562306a36Sopenharmony_ci	{ 195,  55},
13662306a36Sopenharmony_ci	{ 194,  53},
13762306a36Sopenharmony_ci	{ 193,  50},
13862306a36Sopenharmony_ci	{ 192,  48},
13962306a36Sopenharmony_ci	{ 191,  45},
14062306a36Sopenharmony_ci	{ 190,  43},
14162306a36Sopenharmony_ci	{ 189,  40},
14262306a36Sopenharmony_ci	{ 188,  38},
14362306a36Sopenharmony_ci	{ 187,  35},
14462306a36Sopenharmony_ci	{ 186,  33},
14562306a36Sopenharmony_ci	{ 185,  30},
14662306a36Sopenharmony_ci	{ 184,  28},
14762306a36Sopenharmony_ci	{ 183,  25},
14862306a36Sopenharmony_ci	{ 182,  23},
14962306a36Sopenharmony_ci	{ 181,  20},
15062306a36Sopenharmony_ci	{ 180,  18},
15162306a36Sopenharmony_ci	{ 179,  15},
15262306a36Sopenharmony_ci	{ 178,  13},
15362306a36Sopenharmony_ci	{ 177,  10},
15462306a36Sopenharmony_ci	{ 176,   8},
15562306a36Sopenharmony_ci	{ 175,   5},
15662306a36Sopenharmony_ci	{   0,   0},
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* MAX1111 Commands */
16062306a36Sopenharmony_ci#define MAXCTRL_PD0      (1u << 0)
16162306a36Sopenharmony_ci#define MAXCTRL_PD1      (1u << 1)
16262306a36Sopenharmony_ci#define MAXCTRL_SGL      (1u << 2)
16362306a36Sopenharmony_ci#define MAXCTRL_UNI      (1u << 3)
16462306a36Sopenharmony_ci#define MAXCTRL_SEL_SH   4
16562306a36Sopenharmony_ci#define MAXCTRL_STR      (1u << 7)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciextern int max1111_read_channel(int);
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * Read MAX1111 ADC
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ciint sharpsl_pm_pxa_read_max1111(int channel)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	/* max1111 accepts channels from 0-3, however,
17462306a36Sopenharmony_ci	 * it is encoded from 0-7 here in the code.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	return max1111_read_channel(channel >> 1);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int get_percentage(int voltage)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int i = sharpsl_pm.machinfo->bat_levels - 1;
18262306a36Sopenharmony_ci	int bl_status = sharpsl_pm.machinfo->backlight_get_status ? sharpsl_pm.machinfo->backlight_get_status() : 0;
18362306a36Sopenharmony_ci	struct battery_thresh *thresh;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (sharpsl_pm.charge_mode == CHRG_ON)
18662306a36Sopenharmony_ci		thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_acin_bl : sharpsl_pm.machinfo->bat_levels_acin;
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_noac_bl : sharpsl_pm.machinfo->bat_levels_noac;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	while (i > 0 && (voltage > thresh[i].voltage))
19162306a36Sopenharmony_ci		i--;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return thresh[i].percentage;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int get_apm_status(int voltage)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	int low_thresh, high_thresh;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (sharpsl_pm.charge_mode == CHRG_ON) {
20162306a36Sopenharmony_ci		high_thresh = sharpsl_pm.machinfo->status_high_acin;
20262306a36Sopenharmony_ci		low_thresh = sharpsl_pm.machinfo->status_low_acin;
20362306a36Sopenharmony_ci	} else {
20462306a36Sopenharmony_ci		high_thresh = sharpsl_pm.machinfo->status_high_noac;
20562306a36Sopenharmony_ci		low_thresh = sharpsl_pm.machinfo->status_low_noac;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (voltage >= high_thresh)
20962306a36Sopenharmony_ci		return APM_BATTERY_STATUS_HIGH;
21062306a36Sopenharmony_ci	if (voltage >= low_thresh)
21162306a36Sopenharmony_ci		return APM_BATTERY_STATUS_LOW;
21262306a36Sopenharmony_ci	return APM_BATTERY_STATUS_CRITICAL;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_civoid sharpsl_battery_kick(void)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125));
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void sharpsl_battery_thread(struct work_struct *private_)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	int voltage, percent, apm_status, i;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo)
22562306a36Sopenharmony_ci		return;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	sharpsl_pm.battstat.ac_status = (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN) ? APM_AC_ONLINE : APM_AC_OFFLINE);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Corgi cannot confirm when battery fully charged so periodically kick! */
23062306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->batfull_irq && (sharpsl_pm.charge_mode == CHRG_ON)
23162306a36Sopenharmony_ci			&& time_after(jiffies, sharpsl_pm.charge_start_time +  SHARPSL_CHARGE_ON_TIME_INTERVAL))
23262306a36Sopenharmony_ci		schedule_delayed_work(&toggle_charger, 0);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
23562306a36Sopenharmony_ci		voltage = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT);
23662306a36Sopenharmony_ci		if (voltage > 0)
23762306a36Sopenharmony_ci			break;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	if (voltage <= 0) {
24062306a36Sopenharmony_ci		voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage;
24162306a36Sopenharmony_ci		dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n");
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	voltage = sharpsl_average_value(voltage);
24562306a36Sopenharmony_ci	apm_status = get_apm_status(voltage);
24662306a36Sopenharmony_ci	percent = get_percentage(voltage);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* At low battery voltages, the voltage has a tendency to start
24962306a36Sopenharmony_ci	   creeping back up so we try to avoid this here */
25062306a36Sopenharmony_ci	if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE)
25162306a36Sopenharmony_ci	    || (apm_status == APM_BATTERY_STATUS_HIGH)
25262306a36Sopenharmony_ci	    || percent <= sharpsl_pm.battstat.mainbat_percent) {
25362306a36Sopenharmony_ci		sharpsl_pm.battstat.mainbat_voltage = voltage;
25462306a36Sopenharmony_ci		sharpsl_pm.battstat.mainbat_status = apm_status;
25562306a36Sopenharmony_ci		sharpsl_pm.battstat.mainbat_percent = percent;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage,
25962306a36Sopenharmony_ci			sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Suspend if critical battery level */
26262306a36Sopenharmony_ci	if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
26362306a36Sopenharmony_ci	     && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL)
26462306a36Sopenharmony_ci	     && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) {
26562306a36Sopenharmony_ci		sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
26662306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Fatal Off\n");
26762306a36Sopenharmony_ci		apm_queue_event(APM_CRITICAL_SUSPEND);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid sharpsl_pm_led(int val)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	if (val == SHARPSL_LED_ERROR) {
27662306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Charging Error!\n");
27762306a36Sopenharmony_ci	} else if (val == SHARPSL_LED_ON) {
27862306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge LED On\n");
27962306a36Sopenharmony_ci		led_trigger_event(sharpsl_charge_led_trigger, LED_FULL);
28062306a36Sopenharmony_ci	} else {
28162306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge LED Off\n");
28262306a36Sopenharmony_ci		led_trigger_event(sharpsl_charge_led_trigger, LED_OFF);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void sharpsl_charge_on(void)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Turning Charger On\n");
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	sharpsl_pm.full_count = 0;
29162306a36Sopenharmony_ci	sharpsl_pm.charge_mode = CHRG_ON;
29262306a36Sopenharmony_ci	schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250));
29362306a36Sopenharmony_ci	schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500));
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void sharpsl_charge_off(void)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n");
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(0);
30162306a36Sopenharmony_ci	sharpsl_pm_led(SHARPSL_LED_OFF);
30262306a36Sopenharmony_ci	sharpsl_pm.charge_mode = CHRG_OFF;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	schedule_delayed_work(&sharpsl_bat, 0);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void sharpsl_charge_error(void)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	sharpsl_pm_led(SHARPSL_LED_ERROR);
31062306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(0);
31162306a36Sopenharmony_ci	sharpsl_pm.charge_mode = CHRG_ERROR;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void sharpsl_charge_toggle(struct work_struct *private_)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Toggling Charger at time: %lx\n", jiffies);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
31962306a36Sopenharmony_ci		sharpsl_charge_off();
32062306a36Sopenharmony_ci		return;
32162306a36Sopenharmony_ci	} else if ((sharpsl_check_battery_temp() < 0) || (sharpsl_ac_check() < 0)) {
32262306a36Sopenharmony_ci		sharpsl_charge_error();
32362306a36Sopenharmony_ci		return;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	sharpsl_pm_led(SHARPSL_LED_ON);
32762306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(0);
32862306a36Sopenharmony_ci	mdelay(SHARPSL_CHARGE_WAIT_TIME);
32962306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(1);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	sharpsl_pm.charge_start_time = jiffies;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void sharpsl_ac_timer(struct timer_list *unused)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "AC Status: %d\n", acin);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	sharpsl_average_clear();
34162306a36Sopenharmony_ci	if (acin && (sharpsl_pm.charge_mode != CHRG_ON))
34262306a36Sopenharmony_ci		sharpsl_charge_on();
34362306a36Sopenharmony_ci	else if (sharpsl_pm.charge_mode == CHRG_ON)
34462306a36Sopenharmony_ci		sharpsl_charge_off();
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	schedule_delayed_work(&sharpsl_bat, 0);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic irqreturn_t sharpsl_ac_isr(int irq, void *dev_id)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	/* Delay the event slightly to debounce */
35362306a36Sopenharmony_ci	/* Must be a smaller delay than the chrg_full_isr below */
35462306a36Sopenharmony_ci	mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	return IRQ_HANDLED;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void sharpsl_chrg_full_timer(struct timer_list *unused)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	sharpsl_pm.full_count++;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) {
36662306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n");
36762306a36Sopenharmony_ci		if (sharpsl_pm.charge_mode == CHRG_ON)
36862306a36Sopenharmony_ci			sharpsl_charge_off();
36962306a36Sopenharmony_ci	} else if (sharpsl_pm.full_count < 2) {
37062306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n");
37162306a36Sopenharmony_ci		schedule_delayed_work(&toggle_charger, 0);
37262306a36Sopenharmony_ci	} else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) {
37362306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n");
37462306a36Sopenharmony_ci		schedule_delayed_work(&toggle_charger, 0);
37562306a36Sopenharmony_ci	} else {
37662306a36Sopenharmony_ci		sharpsl_charge_off();
37762306a36Sopenharmony_ci		sharpsl_pm.charge_mode = CHRG_DONE;
37862306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n");
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci/* Charging Finished Interrupt (Not present on Corgi) */
38362306a36Sopenharmony_ci/* Can trigger at the same time as an AC status change so
38462306a36Sopenharmony_ci   delay until after that has been processed */
38562306a36Sopenharmony_cistatic irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	if (sharpsl_pm.flags & SHARPSL_SUSPENDED)
38862306a36Sopenharmony_ci		return IRQ_HANDLED;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	/* delay until after any ac interrupt */
39162306a36Sopenharmony_ci	mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500));
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return IRQ_HANDLED;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	int is_fatal = 0;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) {
40162306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n");
40262306a36Sopenharmony_ci		is_fatal = 1;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)) {
40662306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n");
40762306a36Sopenharmony_ci		is_fatal = 1;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) {
41162306a36Sopenharmony_ci		sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
41262306a36Sopenharmony_ci		apm_queue_event(APM_CRITICAL_SUSPEND);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return IRQ_HANDLED;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci * Maintain an average of the last 10 readings
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_ci#define SHARPSL_CNV_VALUE_NUM    10
42262306a36Sopenharmony_cistatic int sharpsl_ad_index;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic void sharpsl_average_clear(void)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	sharpsl_ad_index = 0;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int sharpsl_average_value(int ad)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	int i, ad_val = 0;
43262306a36Sopenharmony_ci	static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1];
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) {
43562306a36Sopenharmony_ci		sharpsl_ad_index = 0;
43662306a36Sopenharmony_ci		return ad;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	sharpsl_ad[sharpsl_ad_index] = ad;
44062306a36Sopenharmony_ci	sharpsl_ad_index++;
44162306a36Sopenharmony_ci	if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) {
44262306a36Sopenharmony_ci		for (i = 0; i < (SHARPSL_CNV_VALUE_NUM-1); i++)
44362306a36Sopenharmony_ci			sharpsl_ad[i] = sharpsl_ad[i+1];
44462306a36Sopenharmony_ci		sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci	for (i = 0; i < sharpsl_ad_index; i++)
44762306a36Sopenharmony_ci		ad_val += sharpsl_ad[i];
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return ad_val / sharpsl_ad_index;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/*
45362306a36Sopenharmony_ci * Take an array of 5 integers, remove the maximum and minimum values
45462306a36Sopenharmony_ci * and return the average.
45562306a36Sopenharmony_ci */
45662306a36Sopenharmony_cistatic int get_select_val(int *val)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	int i, j, k, temp, sum = 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* Find MAX val */
46162306a36Sopenharmony_ci	temp = val[0];
46262306a36Sopenharmony_ci	j = 0;
46362306a36Sopenharmony_ci	for (i = 1; i < 5; i++) {
46462306a36Sopenharmony_ci		if (temp < val[i]) {
46562306a36Sopenharmony_ci			temp = val[i];
46662306a36Sopenharmony_ci			j = i;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Find MIN val */
47162306a36Sopenharmony_ci	temp = val[4];
47262306a36Sopenharmony_ci	k = 4;
47362306a36Sopenharmony_ci	for (i = 3; i >= 0; i--) {
47462306a36Sopenharmony_ci		if (temp > val[i]) {
47562306a36Sopenharmony_ci			temp = val[i];
47662306a36Sopenharmony_ci			k = i;
47762306a36Sopenharmony_ci		}
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	for (i = 0; i < 5; i++)
48162306a36Sopenharmony_ci		if (i != j && i != k)
48262306a36Sopenharmony_ci			sum += val[i];
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Average: %d from values: %d, %d, %d, %d, %d\n", sum/3, val[0], val[1], val[2], val[3], val[4]);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return sum/3;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int sharpsl_check_battery_temp(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	int val, i, buff[5];
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/* Check battery temperature */
49462306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
49562306a36Sopenharmony_ci		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
49662306a36Sopenharmony_ci		sharpsl_pm.machinfo->measure_temp(1);
49762306a36Sopenharmony_ci		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
49862306a36Sopenharmony_ci		buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_TEMP);
49962306a36Sopenharmony_ci		sharpsl_pm.machinfo->measure_temp(0);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	val = get_select_val(buff);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val);
50562306a36Sopenharmony_ci	if (val > sharpsl_pm.machinfo->charge_on_temp) {
50662306a36Sopenharmony_ci		printk(KERN_WARNING "Not charging: temperature out of limits.\n");
50762306a36Sopenharmony_ci		return -1;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci#ifdef CONFIG_PM
51462306a36Sopenharmony_cistatic int sharpsl_check_battery_voltage(void)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	int val, i, buff[5];
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* disable charge, enable discharge */
51962306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(0);
52062306a36Sopenharmony_ci	sharpsl_pm.machinfo->discharge(1);
52162306a36Sopenharmony_ci	mdelay(SHARPSL_WAIT_DISCHARGE_ON);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->discharge1)
52462306a36Sopenharmony_ci		sharpsl_pm.machinfo->discharge1(1);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Check battery voltage */
52762306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
52862306a36Sopenharmony_ci		buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT);
52962306a36Sopenharmony_ci		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->discharge1)
53362306a36Sopenharmony_ci		sharpsl_pm.machinfo->discharge1(0);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	sharpsl_pm.machinfo->discharge(0);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	val = get_select_val(buff);
53862306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	if (val < sharpsl_pm.machinfo->charge_on_volt)
54162306a36Sopenharmony_ci		return -1;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	return 0;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci#endif
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int sharpsl_ac_check(void)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	int temp, i, buff[5];
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	for (i = 0; i < 5; i++) {
55262306a36Sopenharmony_ci		buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_ACIN_VOLT);
55362306a36Sopenharmony_ci		mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	temp = get_select_val(buff);
55762306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n", temp);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if ((temp > sharpsl_pm.machinfo->charge_acin_high) || (temp < sharpsl_pm.machinfo->charge_acin_low)) {
56062306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Error: AC check failed: voltage %d.\n", temp);
56162306a36Sopenharmony_ci		return -1;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return 0;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci#ifdef CONFIG_PM
56862306a36Sopenharmony_cistatic int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	sharpsl_pm.flags |= SHARPSL_SUSPENDED;
57162306a36Sopenharmony_ci	flush_delayed_work(&toggle_charger);
57262306a36Sopenharmony_ci	flush_delayed_work(&sharpsl_bat);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (sharpsl_pm.charge_mode == CHRG_ON)
57562306a36Sopenharmony_ci		sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
57662306a36Sopenharmony_ci	else
57762306a36Sopenharmony_ci		sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int sharpsl_pm_resume(struct platform_device *pdev)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	/* Clear the reset source indicators as they break the bootloader upon reboot */
58562306a36Sopenharmony_ci	RCSR = 0x0f;
58662306a36Sopenharmony_ci	sharpsl_average_clear();
58762306a36Sopenharmony_ci	sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED;
58862306a36Sopenharmony_ci	sharpsl_pm.flags &= ~SHARPSL_SUSPENDED;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Time is: %08x\n", RCNR);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n", sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG);
59862306a36Sopenharmony_ci	/* not charging and AC-IN! */
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN))) {
60162306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n");
60262306a36Sopenharmony_ci		sharpsl_pm.charge_mode = CHRG_OFF;
60362306a36Sopenharmony_ci		sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
60462306a36Sopenharmony_ci		sharpsl_off_charge_battery();
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	sharpsl_pm.machinfo->presuspend();
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	PEDR = 0xffffffff; /* clear it */
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE;
61262306a36Sopenharmony_ci	if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) {
61362306a36Sopenharmony_ci		RTSR &= RTSR_ALE;
61462306a36Sopenharmony_ci		RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND;
61562306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n", RTAR);
61662306a36Sopenharmony_ci		sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE;
61762306a36Sopenharmony_ci	} else if (alarm_enable) {
61862306a36Sopenharmony_ci		RTSR &= RTSR_ALE;
61962306a36Sopenharmony_ci		RTAR = alarm_time;
62062306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n", RTAR);
62162306a36Sopenharmony_ci	} else {
62262306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "No alarms set.\n");
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	pxa_pm_enter(state);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	sharpsl_pm.machinfo->postsuspend();
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n", PEDR);
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable)) {
63562306a36Sopenharmony_ci		if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) {
63662306a36Sopenharmony_ci			dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n");
63762306a36Sopenharmony_ci			corgi_goto_sleep(alarm_time, alarm_enable, state);
63862306a36Sopenharmony_ci			return 1;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci		if (sharpsl_off_charge_battery()) {
64162306a36Sopenharmony_ci			dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n");
64262306a36Sopenharmony_ci			corgi_goto_sleep(alarm_time, alarm_enable, state);
64362306a36Sopenharmony_ci			return 1;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n");
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) ||
64962306a36Sopenharmony_ci	    (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)))	{
65062306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n");
65162306a36Sopenharmony_ci		corgi_goto_sleep(alarm_time, alarm_enable, state);
65262306a36Sopenharmony_ci		return 1;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic int corgi_pxa_pm_enter(suspend_state_t state)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	unsigned long alarm_time = RTAR;
66162306a36Sopenharmony_ci	unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n");
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	corgi_goto_sleep(alarm_time, alarm_status, state);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	while (corgi_enter_suspend(alarm_time, alarm_status, state))
66862306a36Sopenharmony_ci		{}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->earlyresume)
67162306a36Sopenharmony_ci		sharpsl_pm.machinfo->earlyresume();
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	return 0;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic int sharpsl_off_charge_error(void)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	dev_err(sharpsl_pm.dev, "Offline Charger: Error occurred.\n");
68162306a36Sopenharmony_ci	sharpsl_pm.machinfo->charge(0);
68262306a36Sopenharmony_ci	sharpsl_pm_led(SHARPSL_LED_ERROR);
68362306a36Sopenharmony_ci	sharpsl_pm.charge_mode = CHRG_ERROR;
68462306a36Sopenharmony_ci	return 1;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/*
68862306a36Sopenharmony_ci * Charging Control while suspended
68962306a36Sopenharmony_ci * Return 1 - go straight to sleep
69062306a36Sopenharmony_ci * Return 0 - sleep or wakeup depending on other factors
69162306a36Sopenharmony_ci */
69262306a36Sopenharmony_cistatic int sharpsl_off_charge_battery(void)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	int time;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (sharpsl_pm.charge_mode == CHRG_OFF) {
69962306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n");
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		/* AC Check */
70262306a36Sopenharmony_ci		if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery_temp() < 0))
70362306a36Sopenharmony_ci			return sharpsl_off_charge_error();
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		/* Start Charging */
70662306a36Sopenharmony_ci		sharpsl_pm_led(SHARPSL_LED_ON);
70762306a36Sopenharmony_ci		sharpsl_pm.machinfo->charge(0);
70862306a36Sopenharmony_ci		mdelay(SHARPSL_CHARGE_WAIT_TIME);
70962306a36Sopenharmony_ci		sharpsl_pm.machinfo->charge(1);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci		sharpsl_pm.charge_mode = CHRG_ON;
71262306a36Sopenharmony_ci		sharpsl_pm.full_count = 0;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		return 1;
71562306a36Sopenharmony_ci	} else if (sharpsl_pm.charge_mode != CHRG_ON) {
71662306a36Sopenharmony_ci		return 1;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (sharpsl_pm.full_count == 0) {
72062306a36Sopenharmony_ci		int time;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n");
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci		if ((sharpsl_check_battery_temp() < 0) || (sharpsl_check_battery_voltage() < 0))
72562306a36Sopenharmony_ci			return sharpsl_off_charge_error();
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci		sharpsl_pm.machinfo->charge(0);
72862306a36Sopenharmony_ci		mdelay(SHARPSL_CHARGE_WAIT_TIME);
72962306a36Sopenharmony_ci		sharpsl_pm.machinfo->charge(1);
73062306a36Sopenharmony_ci		sharpsl_pm.charge_mode = CHRG_ON;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		time = RCNR;
73562306a36Sopenharmony_ci		while (1) {
73662306a36Sopenharmony_ci			/* Check if any wakeup event had occurred */
73762306a36Sopenharmony_ci			if (sharpsl_pm.machinfo->charger_wakeup())
73862306a36Sopenharmony_ci				return 0;
73962306a36Sopenharmony_ci			/* Check for timeout */
74062306a36Sopenharmony_ci			if ((RCNR - time) > SHARPSL_WAIT_CO_TIME)
74162306a36Sopenharmony_ci				return 1;
74262306a36Sopenharmony_ci			if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) {
74362306a36Sopenharmony_ci				dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occurred. Retrying to check\n");
74462306a36Sopenharmony_ci				sharpsl_pm.full_count++;
74562306a36Sopenharmony_ci				sharpsl_pm.machinfo->charge(0);
74662306a36Sopenharmony_ci				mdelay(SHARPSL_CHARGE_WAIT_TIME);
74762306a36Sopenharmony_ci				sharpsl_pm.machinfo->charge(1);
74862306a36Sopenharmony_ci				return 1;
74962306a36Sopenharmony_ci			}
75062306a36Sopenharmony_ci		}
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n");
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	time = RCNR;
75862306a36Sopenharmony_ci	while (1) {
75962306a36Sopenharmony_ci		/* Check if any wakeup event had occurred */
76062306a36Sopenharmony_ci		if (sharpsl_pm.machinfo->charger_wakeup())
76162306a36Sopenharmony_ci			return 0;
76262306a36Sopenharmony_ci		/* Check for timeout */
76362306a36Sopenharmony_ci		if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) {
76462306a36Sopenharmony_ci			if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) {
76562306a36Sopenharmony_ci				dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n");
76662306a36Sopenharmony_ci				sharpsl_pm.full_count = 0;
76762306a36Sopenharmony_ci			}
76862306a36Sopenharmony_ci			sharpsl_pm.full_count++;
76962306a36Sopenharmony_ci			return 1;
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci		if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) {
77262306a36Sopenharmony_ci			dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n");
77362306a36Sopenharmony_ci			sharpsl_pm_led(SHARPSL_LED_OFF);
77462306a36Sopenharmony_ci			sharpsl_pm.machinfo->charge(0);
77562306a36Sopenharmony_ci			sharpsl_pm.charge_mode = CHRG_DONE;
77662306a36Sopenharmony_ci			return 1;
77762306a36Sopenharmony_ci		}
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci#else
78162306a36Sopenharmony_ci#define sharpsl_pm_suspend	NULL
78262306a36Sopenharmony_ci#define sharpsl_pm_resume	NULL
78362306a36Sopenharmony_ci#endif
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	return sprintf(buf, "%d\n", sharpsl_pm.battstat.mainbat_percent);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", sharpsl_pm.battstat.mainbat_voltage);
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(battery_percentage);
79662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(battery_voltage);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ciextern void (*apm_get_power_status)(struct apm_power_info *);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic void sharpsl_apm_get_power_status(struct apm_power_info *info)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	info->ac_line_status = sharpsl_pm.battstat.ac_status;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (sharpsl_pm.charge_mode == CHRG_ON)
80562306a36Sopenharmony_ci		info->battery_status = APM_BATTERY_STATUS_CHARGING;
80662306a36Sopenharmony_ci	else
80762306a36Sopenharmony_ci		info->battery_status = sharpsl_pm.battstat.mainbat_status;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	info->battery_flag = (1 << info->battery_status);
81062306a36Sopenharmony_ci	info->battery_life = sharpsl_pm.battstat.mainbat_percent;
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci#ifdef CONFIG_PM
81462306a36Sopenharmony_cistatic const struct platform_suspend_ops sharpsl_pm_ops = {
81562306a36Sopenharmony_ci	.prepare	= pxa_pm_prepare,
81662306a36Sopenharmony_ci	.finish		= pxa_pm_finish,
81762306a36Sopenharmony_ci	.enter		= corgi_pxa_pm_enter,
81862306a36Sopenharmony_ci	.valid		= suspend_valid_only_mem,
81962306a36Sopenharmony_ci};
82062306a36Sopenharmony_ci#endif
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int sharpsl_pm_probe(struct platform_device *pdev)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	int ret, irq;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!pdev->dev.platform_data)
82762306a36Sopenharmony_ci		return -EINVAL;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	sharpsl_pm.dev = &pdev->dev;
83062306a36Sopenharmony_ci	sharpsl_pm.machinfo = pdev->dev.platform_data;
83162306a36Sopenharmony_ci	sharpsl_pm.charge_mode = CHRG_OFF;
83262306a36Sopenharmony_ci	sharpsl_pm.flags = 0;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	timer_setup(&sharpsl_pm.ac_timer, sharpsl_ac_timer, 0);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	timer_setup(&sharpsl_pm.chrg_full_timer, sharpsl_chrg_full_timer, 0);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	sharpsl_pm.machinfo->init();
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	gpio_request(sharpsl_pm.machinfo->gpio_acin, "AC IN");
84362306a36Sopenharmony_ci	gpio_direction_input(sharpsl_pm.machinfo->gpio_acin);
84462306a36Sopenharmony_ci	gpio_request(sharpsl_pm.machinfo->gpio_batfull, "Battery Full");
84562306a36Sopenharmony_ci	gpio_direction_input(sharpsl_pm.machinfo->gpio_batfull);
84662306a36Sopenharmony_ci	gpio_request(sharpsl_pm.machinfo->gpio_batlock, "Battery Lock");
84762306a36Sopenharmony_ci	gpio_direction_input(sharpsl_pm.machinfo->gpio_batlock);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/* Register interrupt handlers */
85062306a36Sopenharmony_ci	irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_acin);
85162306a36Sopenharmony_ci	if (request_irq(irq, sharpsl_ac_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "AC Input Detect", sharpsl_ac_isr)) {
85262306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_batlock);
85662306a36Sopenharmony_ci	if (request_irq(irq, sharpsl_fatal_isr, IRQF_TRIGGER_FALLING, "Battery Cover", sharpsl_fatal_isr)) {
85762306a36Sopenharmony_ci		dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq);
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->gpio_fatal) {
86162306a36Sopenharmony_ci		irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_fatal);
86262306a36Sopenharmony_ci		if (request_irq(irq, sharpsl_fatal_isr, IRQF_TRIGGER_FALLING, "Fatal Battery", sharpsl_fatal_isr)) {
86362306a36Sopenharmony_ci			dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq);
86462306a36Sopenharmony_ci		}
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->batfull_irq) {
86862306a36Sopenharmony_ci		/* Register interrupt handler. */
86962306a36Sopenharmony_ci		irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_batfull);
87062306a36Sopenharmony_ci		if (request_irq(irq, sharpsl_chrg_full_isr, IRQF_TRIGGER_RISING, "CO", sharpsl_chrg_full_isr)) {
87162306a36Sopenharmony_ci			dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq);
87262306a36Sopenharmony_ci		}
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ret = device_create_file(&pdev->dev, &dev_attr_battery_percentage);
87662306a36Sopenharmony_ci	ret |= device_create_file(&pdev->dev, &dev_attr_battery_voltage);
87762306a36Sopenharmony_ci	if (ret != 0)
87862306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Failed to register attributes (%d)\n", ret);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	apm_get_power_status = sharpsl_apm_get_power_status;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci#ifdef CONFIG_PM
88362306a36Sopenharmony_ci	suspend_set_ops(&sharpsl_pm_ops);
88462306a36Sopenharmony_ci#endif
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic void sharpsl_pm_remove(struct platform_device *pdev)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	suspend_set_ops(NULL);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_battery_percentage);
89662306a36Sopenharmony_ci	device_remove_file(&pdev->dev, &dev_attr_battery_voltage);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	led_trigger_unregister_simple(sharpsl_charge_led_trigger);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr);
90162306a36Sopenharmony_ci	free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->gpio_fatal)
90462306a36Sopenharmony_ci		free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->batfull_irq)
90762306a36Sopenharmony_ci		free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	gpio_free(sharpsl_pm.machinfo->gpio_batlock);
91062306a36Sopenharmony_ci	gpio_free(sharpsl_pm.machinfo->gpio_batfull);
91162306a36Sopenharmony_ci	gpio_free(sharpsl_pm.machinfo->gpio_acin);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	if (sharpsl_pm.machinfo->exit)
91462306a36Sopenharmony_ci		sharpsl_pm.machinfo->exit();
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	del_timer_sync(&sharpsl_pm.chrg_full_timer);
91762306a36Sopenharmony_ci	del_timer_sync(&sharpsl_pm.ac_timer);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic struct platform_driver sharpsl_pm_driver = {
92162306a36Sopenharmony_ci	.probe		= sharpsl_pm_probe,
92262306a36Sopenharmony_ci	.remove_new	= sharpsl_pm_remove,
92362306a36Sopenharmony_ci	.suspend	= sharpsl_pm_suspend,
92462306a36Sopenharmony_ci	.resume		= sharpsl_pm_resume,
92562306a36Sopenharmony_ci	.driver		= {
92662306a36Sopenharmony_ci		.name		= "sharpsl-pm",
92762306a36Sopenharmony_ci	},
92862306a36Sopenharmony_ci};
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic int sharpsl_pm_init(void)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	return platform_driver_register(&sharpsl_pm_driver);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void sharpsl_pm_exit(void)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	platform_driver_unregister(&sharpsl_pm_driver);
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cilate_initcall(sharpsl_pm_init);
94162306a36Sopenharmony_cimodule_exit(sharpsl_pm_exit);
942