1078abcf9SRichard Purdie /* 2078abcf9SRichard Purdie * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00 3078abcf9SRichard Purdie * series of PDAs 4078abcf9SRichard Purdie * 5078abcf9SRichard Purdie * Copyright (c) 2004-2005 Richard Purdie 6078abcf9SRichard Purdie * 7078abcf9SRichard Purdie * Based on code written by Sharp for 2.4 kernels 8078abcf9SRichard Purdie * 9078abcf9SRichard Purdie * This program is free software; you can redistribute it and/or modify 10078abcf9SRichard Purdie * it under the terms of the GNU General Public License version 2 as 11078abcf9SRichard Purdie * published by the Free Software Foundation. 12078abcf9SRichard Purdie * 13078abcf9SRichard Purdie */ 14078abcf9SRichard Purdie 15078abcf9SRichard Purdie #undef DEBUG 16078abcf9SRichard Purdie 17078abcf9SRichard Purdie #include <linux/module.h> 18078abcf9SRichard Purdie #include <linux/kernel.h> 19078abcf9SRichard Purdie #include <linux/interrupt.h> 201623dee8SThomas Gleixner #include <linux/irq.h> 21c5e1ae97SRichard Purdie #include <linux/platform_device.h> 2261fde514SRussell King #include <linux/apm-emulation.h> 2378731d33SDmitry Eremin-Solenikov #include <linux/timer.h> 2478731d33SDmitry Eremin-Solenikov #include <linux/delay.h> 2578731d33SDmitry Eremin-Solenikov #include <linux/leds.h> 2678731d33SDmitry Eremin-Solenikov #include <linux/suspend.h> 27078abcf9SRichard Purdie 28078abcf9SRichard Purdie #include <asm/mach-types.h> 29a09e64fbSRussell King #include <mach/pm.h> 3078731d33SDmitry Eremin-Solenikov #include <mach/pxa2xx-regs.h> 31a09e64fbSRussell King #include <mach/pxa2xx-gpio.h> 3278731d33SDmitry Eremin-Solenikov #include <mach/regs-rtc.h> 33a09e64fbSRussell King #include <mach/sharpsl.h> 3478731d33SDmitry Eremin-Solenikov #include <mach/sharpsl_pm.h> 3578731d33SDmitry Eremin-Solenikov 36d48898a3SDmitry Eremin-Solenikov #include "sharpsl.h" 37d48898a3SDmitry Eremin-Solenikov 3878731d33SDmitry Eremin-Solenikov /* 3978731d33SDmitry Eremin-Solenikov * Constants 4078731d33SDmitry Eremin-Solenikov */ 4178731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_ON_TIME_INTERVAL (msecs_to_jiffies(1*60*1000)) /* 1 min */ 4278731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_FINISH_TIME (msecs_to_jiffies(10*60*1000)) /* 10 min */ 4378731d33SDmitry Eremin-Solenikov #define SHARPSL_BATCHK_TIME (msecs_to_jiffies(15*1000)) /* 15 sec */ 4478731d33SDmitry Eremin-Solenikov #define SHARPSL_BATCHK_TIME_SUSPEND (60*10) /* 10 min */ 4578731d33SDmitry Eremin-Solenikov 4678731d33SDmitry Eremin-Solenikov #define SHARPSL_WAIT_CO_TIME 15 /* 15 sec */ 4778731d33SDmitry Eremin-Solenikov #define SHARPSL_WAIT_DISCHARGE_ON 100 /* 100 msec */ 4878731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP 10 /* 10 msec */ 4978731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT 10 /* 10 msec */ 5078731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN 10 /* 10 msec */ 5178731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_WAIT_TIME 15 /* 15 msec */ 5278731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_CO_CHECK_TIME 5 /* 5 msec */ 5378731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_RETRY_CNT 1 /* eqv. 10 min */ 5478731d33SDmitry Eremin-Solenikov 5578731d33SDmitry Eremin-Solenikov /* 5678731d33SDmitry Eremin-Solenikov * Prototypes 5778731d33SDmitry Eremin-Solenikov */ 5878731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 5978731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_battery(void); 6078731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_voltage(void); 6178731d33SDmitry Eremin-Solenikov static int sharpsl_fatal_check(void); 6278731d33SDmitry Eremin-Solenikov #endif 6378731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_temp(void); 6478731d33SDmitry Eremin-Solenikov static int sharpsl_ac_check(void); 6578731d33SDmitry Eremin-Solenikov static int sharpsl_average_value(int ad); 6678731d33SDmitry Eremin-Solenikov static void sharpsl_average_clear(void); 6778731d33SDmitry Eremin-Solenikov static void sharpsl_charge_toggle(struct work_struct *private_); 6878731d33SDmitry Eremin-Solenikov static void sharpsl_battery_thread(struct work_struct *private_); 6978731d33SDmitry Eremin-Solenikov 7078731d33SDmitry Eremin-Solenikov 7178731d33SDmitry Eremin-Solenikov /* 7278731d33SDmitry Eremin-Solenikov * Variables 7378731d33SDmitry Eremin-Solenikov */ 7478731d33SDmitry Eremin-Solenikov struct sharpsl_pm_status sharpsl_pm; 75d48898a3SDmitry Eremin-Solenikov static DECLARE_DELAYED_WORK(toggle_charger, sharpsl_charge_toggle); 76d48898a3SDmitry Eremin-Solenikov static DECLARE_DELAYED_WORK(sharpsl_bat, sharpsl_battery_thread); 7778731d33SDmitry Eremin-Solenikov DEFINE_LED_TRIGGER(sharpsl_charge_led_trigger); 7878731d33SDmitry Eremin-Solenikov 7978731d33SDmitry Eremin-Solenikov 80078abcf9SRichard Purdie 81078abcf9SRichard Purdie struct battery_thresh spitz_battery_levels_acin[] = { 82078abcf9SRichard Purdie { 213, 100}, 83078abcf9SRichard Purdie { 212, 98}, 84078abcf9SRichard Purdie { 211, 95}, 85078abcf9SRichard Purdie { 210, 93}, 86078abcf9SRichard Purdie { 209, 90}, 87078abcf9SRichard Purdie { 208, 88}, 88078abcf9SRichard Purdie { 207, 85}, 89078abcf9SRichard Purdie { 206, 83}, 90078abcf9SRichard Purdie { 205, 80}, 91078abcf9SRichard Purdie { 204, 78}, 92078abcf9SRichard Purdie { 203, 75}, 93078abcf9SRichard Purdie { 202, 73}, 94078abcf9SRichard Purdie { 201, 70}, 95078abcf9SRichard Purdie { 200, 68}, 96078abcf9SRichard Purdie { 199, 65}, 97078abcf9SRichard Purdie { 198, 63}, 98078abcf9SRichard Purdie { 197, 60}, 99078abcf9SRichard Purdie { 196, 58}, 100078abcf9SRichard Purdie { 195, 55}, 101078abcf9SRichard Purdie { 194, 53}, 102078abcf9SRichard Purdie { 193, 50}, 103078abcf9SRichard Purdie { 192, 48}, 104078abcf9SRichard Purdie { 192, 45}, 105078abcf9SRichard Purdie { 191, 43}, 106078abcf9SRichard Purdie { 191, 40}, 107078abcf9SRichard Purdie { 190, 38}, 108078abcf9SRichard Purdie { 190, 35}, 109078abcf9SRichard Purdie { 189, 33}, 110078abcf9SRichard Purdie { 188, 30}, 111078abcf9SRichard Purdie { 187, 28}, 112078abcf9SRichard Purdie { 186, 25}, 113078abcf9SRichard Purdie { 185, 23}, 114078abcf9SRichard Purdie { 184, 20}, 115078abcf9SRichard Purdie { 183, 18}, 116078abcf9SRichard Purdie { 182, 15}, 117078abcf9SRichard Purdie { 181, 13}, 118078abcf9SRichard Purdie { 180, 10}, 119078abcf9SRichard Purdie { 179, 8}, 120078abcf9SRichard Purdie { 178, 5}, 121078abcf9SRichard Purdie { 0, 0}, 122078abcf9SRichard Purdie }; 123078abcf9SRichard Purdie 124078abcf9SRichard Purdie struct battery_thresh spitz_battery_levels_noac[] = { 125078abcf9SRichard Purdie { 213, 100}, 126078abcf9SRichard Purdie { 212, 98}, 127078abcf9SRichard Purdie { 211, 95}, 128078abcf9SRichard Purdie { 210, 93}, 129078abcf9SRichard Purdie { 209, 90}, 130078abcf9SRichard Purdie { 208, 88}, 131078abcf9SRichard Purdie { 207, 85}, 132078abcf9SRichard Purdie { 206, 83}, 133078abcf9SRichard Purdie { 205, 80}, 134078abcf9SRichard Purdie { 204, 78}, 135078abcf9SRichard Purdie { 203, 75}, 136078abcf9SRichard Purdie { 202, 73}, 137078abcf9SRichard Purdie { 201, 70}, 138078abcf9SRichard Purdie { 200, 68}, 139078abcf9SRichard Purdie { 199, 65}, 140078abcf9SRichard Purdie { 198, 63}, 141078abcf9SRichard Purdie { 197, 60}, 142078abcf9SRichard Purdie { 196, 58}, 143078abcf9SRichard Purdie { 195, 55}, 144078abcf9SRichard Purdie { 194, 53}, 145078abcf9SRichard Purdie { 193, 50}, 146078abcf9SRichard Purdie { 192, 48}, 147078abcf9SRichard Purdie { 191, 45}, 148078abcf9SRichard Purdie { 190, 43}, 149078abcf9SRichard Purdie { 189, 40}, 150078abcf9SRichard Purdie { 188, 38}, 151078abcf9SRichard Purdie { 187, 35}, 152078abcf9SRichard Purdie { 186, 33}, 153078abcf9SRichard Purdie { 185, 30}, 154078abcf9SRichard Purdie { 184, 28}, 155078abcf9SRichard Purdie { 183, 25}, 156078abcf9SRichard Purdie { 182, 23}, 157078abcf9SRichard Purdie { 181, 20}, 158078abcf9SRichard Purdie { 180, 18}, 159078abcf9SRichard Purdie { 179, 15}, 160078abcf9SRichard Purdie { 178, 13}, 161078abcf9SRichard Purdie { 177, 10}, 162078abcf9SRichard Purdie { 176, 8}, 163078abcf9SRichard Purdie { 175, 5}, 164078abcf9SRichard Purdie { 0, 0}, 165078abcf9SRichard Purdie }; 166078abcf9SRichard Purdie 16725af3b0fSEric Miao /* MAX1111 Commands */ 16825af3b0fSEric Miao #define MAXCTRL_PD0 1u << 0 16925af3b0fSEric Miao #define MAXCTRL_PD1 1u << 1 17025af3b0fSEric Miao #define MAXCTRL_SGL 1u << 2 17125af3b0fSEric Miao #define MAXCTRL_UNI 1u << 3 17225af3b0fSEric Miao #define MAXCTRL_SEL_SH 4 17325af3b0fSEric Miao #define MAXCTRL_STR 1u << 7 17425af3b0fSEric Miao 175078abcf9SRichard Purdie /* 176078abcf9SRichard Purdie * Read MAX1111 ADC 177078abcf9SRichard Purdie */ 178b7557de4SRichard Purdie int sharpsl_pm_pxa_read_max1111(int channel) 179078abcf9SRichard Purdie { 180f8703dc8SRichard Purdie if (machine_is_tosa()) // Ugly, better move this function into another module 181f8703dc8SRichard Purdie return 0; 182f8703dc8SRichard Purdie 18345e2a9b4SEric Miao #ifdef CONFIG_CORGI_SSP_DEPRECATED 18445e2a9b4SEric Miao return corgi_ssp_max1111_get((channel << MAXCTRL_SEL_SH) | MAXCTRL_PD0 | MAXCTRL_PD1 18545e2a9b4SEric Miao | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR); 18645e2a9b4SEric Miao #else 18725af3b0fSEric Miao extern int max1111_read_channel(int); 18825af3b0fSEric Miao 189f16177c2SEric Miao /* max1111 accepts channels from 0-3, however, 190f16177c2SEric Miao * it is encoded from 0-7 here in the code. 191f16177c2SEric Miao */ 192f16177c2SEric Miao return max1111_read_channel(channel >> 1); 19325af3b0fSEric Miao #endif 194078abcf9SRichard Purdie } 195078abcf9SRichard Purdie 19678731d33SDmitry Eremin-Solenikov static int get_percentage(int voltage) 19778731d33SDmitry Eremin-Solenikov { 19878731d33SDmitry Eremin-Solenikov int i = sharpsl_pm.machinfo->bat_levels - 1; 19978731d33SDmitry Eremin-Solenikov int bl_status = sharpsl_pm.machinfo->backlight_get_status ? sharpsl_pm.machinfo->backlight_get_status() : 0; 20078731d33SDmitry Eremin-Solenikov struct battery_thresh *thresh; 20178731d33SDmitry Eremin-Solenikov 20278731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 20378731d33SDmitry Eremin-Solenikov thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_acin_bl : sharpsl_pm.machinfo->bat_levels_acin; 20478731d33SDmitry Eremin-Solenikov else 20578731d33SDmitry Eremin-Solenikov thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_noac_bl : sharpsl_pm.machinfo->bat_levels_noac; 20678731d33SDmitry Eremin-Solenikov 20778731d33SDmitry Eremin-Solenikov while (i > 0 && (voltage > thresh[i].voltage)) 20878731d33SDmitry Eremin-Solenikov i--; 20978731d33SDmitry Eremin-Solenikov 21078731d33SDmitry Eremin-Solenikov return thresh[i].percentage; 21178731d33SDmitry Eremin-Solenikov } 21278731d33SDmitry Eremin-Solenikov 21378731d33SDmitry Eremin-Solenikov static int get_apm_status(int voltage) 21478731d33SDmitry Eremin-Solenikov { 21578731d33SDmitry Eremin-Solenikov int low_thresh, high_thresh; 21678731d33SDmitry Eremin-Solenikov 21778731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) { 21878731d33SDmitry Eremin-Solenikov high_thresh = sharpsl_pm.machinfo->status_high_acin; 21978731d33SDmitry Eremin-Solenikov low_thresh = sharpsl_pm.machinfo->status_low_acin; 22078731d33SDmitry Eremin-Solenikov } else { 22178731d33SDmitry Eremin-Solenikov high_thresh = sharpsl_pm.machinfo->status_high_noac; 22278731d33SDmitry Eremin-Solenikov low_thresh = sharpsl_pm.machinfo->status_low_noac; 22378731d33SDmitry Eremin-Solenikov } 22478731d33SDmitry Eremin-Solenikov 22578731d33SDmitry Eremin-Solenikov if (voltage >= high_thresh) 22678731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_HIGH; 22778731d33SDmitry Eremin-Solenikov if (voltage >= low_thresh) 22878731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_LOW; 22978731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_CRITICAL; 23078731d33SDmitry Eremin-Solenikov } 23178731d33SDmitry Eremin-Solenikov 23278731d33SDmitry Eremin-Solenikov void sharpsl_battery_kick(void) 23378731d33SDmitry Eremin-Solenikov { 23478731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125)); 23578731d33SDmitry Eremin-Solenikov } 23678731d33SDmitry Eremin-Solenikov EXPORT_SYMBOL(sharpsl_battery_kick); 23778731d33SDmitry Eremin-Solenikov 23878731d33SDmitry Eremin-Solenikov 23978731d33SDmitry Eremin-Solenikov static void sharpsl_battery_thread(struct work_struct *private_) 24078731d33SDmitry Eremin-Solenikov { 24178731d33SDmitry Eremin-Solenikov int voltage, percent, apm_status, i = 0; 24278731d33SDmitry Eremin-Solenikov 24378731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo) 24478731d33SDmitry Eremin-Solenikov return; 24578731d33SDmitry Eremin-Solenikov 24678731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.ac_status = (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN) ? APM_AC_ONLINE : APM_AC_OFFLINE); 24778731d33SDmitry Eremin-Solenikov 24878731d33SDmitry Eremin-Solenikov /* Corgi cannot confirm when battery fully charged so periodically kick! */ 24978731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->batfull_irq && (sharpsl_pm.charge_mode == CHRG_ON) 25078731d33SDmitry Eremin-Solenikov && time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_ON_TIME_INTERVAL)) 25178731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 25278731d33SDmitry Eremin-Solenikov 25378731d33SDmitry Eremin-Solenikov while(1) { 25478731d33SDmitry Eremin-Solenikov voltage = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 25578731d33SDmitry Eremin-Solenikov 25678731d33SDmitry Eremin-Solenikov if (voltage > 0) break; 25778731d33SDmitry Eremin-Solenikov if (i++ > 5) { 25878731d33SDmitry Eremin-Solenikov voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage; 25978731d33SDmitry Eremin-Solenikov dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n"); 26078731d33SDmitry Eremin-Solenikov break; 26178731d33SDmitry Eremin-Solenikov } 26278731d33SDmitry Eremin-Solenikov } 26378731d33SDmitry Eremin-Solenikov 26478731d33SDmitry Eremin-Solenikov voltage = sharpsl_average_value(voltage); 26578731d33SDmitry Eremin-Solenikov apm_status = get_apm_status(voltage); 26678731d33SDmitry Eremin-Solenikov percent = get_percentage(voltage); 26778731d33SDmitry Eremin-Solenikov 26878731d33SDmitry Eremin-Solenikov /* At low battery voltages, the voltage has a tendency to start 26978731d33SDmitry Eremin-Solenikov creeping back up so we try to avoid this here */ 27078731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) || (apm_status == APM_BATTERY_STATUS_HIGH) || percent <= sharpsl_pm.battstat.mainbat_percent) { 27178731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_voltage = voltage; 27278731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_status = apm_status; 27378731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_percent = percent; 27478731d33SDmitry Eremin-Solenikov } 27578731d33SDmitry Eremin-Solenikov 27678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage, 27778731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies); 27878731d33SDmitry Eremin-Solenikov 27978731d33SDmitry Eremin-Solenikov #ifdef CONFIG_BACKLIGHT_CORGI 28078731d33SDmitry Eremin-Solenikov /* If battery is low. limit backlight intensity to save power. */ 28178731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 28278731d33SDmitry Eremin-Solenikov && ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW) || 28378731d33SDmitry Eremin-Solenikov (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) { 28478731d33SDmitry Eremin-Solenikov if (!(sharpsl_pm.flags & SHARPSL_BL_LIMIT)) { 28578731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->backlight_limit(1); 28678731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_BL_LIMIT; 28778731d33SDmitry Eremin-Solenikov } 28878731d33SDmitry Eremin-Solenikov } else if (sharpsl_pm.flags & SHARPSL_BL_LIMIT) { 28978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->backlight_limit(0); 29078731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_BL_LIMIT; 29178731d33SDmitry Eremin-Solenikov } 29278731d33SDmitry Eremin-Solenikov #endif 29378731d33SDmitry Eremin-Solenikov 29478731d33SDmitry Eremin-Solenikov /* Suspend if critical battery level */ 29578731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 29678731d33SDmitry Eremin-Solenikov && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL) 29778731d33SDmitry Eremin-Solenikov && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) { 29878731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 29978731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal Off\n"); 30078731d33SDmitry Eremin-Solenikov apm_queue_event(APM_CRITICAL_SUSPEND); 30178731d33SDmitry Eremin-Solenikov } 30278731d33SDmitry Eremin-Solenikov 30378731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME); 30478731d33SDmitry Eremin-Solenikov } 30578731d33SDmitry Eremin-Solenikov 30678731d33SDmitry Eremin-Solenikov void sharpsl_pm_led(int val) 30778731d33SDmitry Eremin-Solenikov { 30878731d33SDmitry Eremin-Solenikov if (val == SHARPSL_LED_ERROR) { 30978731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Charging Error!\n"); 31078731d33SDmitry Eremin-Solenikov } else if (val == SHARPSL_LED_ON) { 31178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge LED On\n"); 31278731d33SDmitry Eremin-Solenikov led_trigger_event(sharpsl_charge_led_trigger, LED_FULL); 31378731d33SDmitry Eremin-Solenikov } else { 31478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge LED Off\n"); 31578731d33SDmitry Eremin-Solenikov led_trigger_event(sharpsl_charge_led_trigger, LED_OFF); 31678731d33SDmitry Eremin-Solenikov } 31778731d33SDmitry Eremin-Solenikov } 31878731d33SDmitry Eremin-Solenikov 31978731d33SDmitry Eremin-Solenikov static void sharpsl_charge_on(void) 32078731d33SDmitry Eremin-Solenikov { 32178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Turning Charger On\n"); 32278731d33SDmitry Eremin-Solenikov 32378731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 32478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 32578731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250)); 32678731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500)); 32778731d33SDmitry Eremin-Solenikov } 32878731d33SDmitry Eremin-Solenikov 32978731d33SDmitry Eremin-Solenikov static void sharpsl_charge_off(void) 33078731d33SDmitry Eremin-Solenikov { 33178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n"); 33278731d33SDmitry Eremin-Solenikov 33378731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 33478731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_OFF); 33578731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 33678731d33SDmitry Eremin-Solenikov 33778731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, 0); 33878731d33SDmitry Eremin-Solenikov } 33978731d33SDmitry Eremin-Solenikov 34078731d33SDmitry Eremin-Solenikov static void sharpsl_charge_error(void) 34178731d33SDmitry Eremin-Solenikov { 34278731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ERROR); 34378731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 34478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ERROR; 34578731d33SDmitry Eremin-Solenikov } 34678731d33SDmitry Eremin-Solenikov 34778731d33SDmitry Eremin-Solenikov static void sharpsl_charge_toggle(struct work_struct *private_) 34878731d33SDmitry Eremin-Solenikov { 34978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Toogling Charger at time: %lx\n", jiffies); 35078731d33SDmitry Eremin-Solenikov 35178731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 35278731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 35378731d33SDmitry Eremin-Solenikov return; 35478731d33SDmitry Eremin-Solenikov } else if ((sharpsl_check_battery_temp() < 0) || (sharpsl_ac_check() < 0)) { 35578731d33SDmitry Eremin-Solenikov sharpsl_charge_error(); 35678731d33SDmitry Eremin-Solenikov return; 35778731d33SDmitry Eremin-Solenikov } 35878731d33SDmitry Eremin-Solenikov 35978731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ON); 36078731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 36178731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 36278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 36378731d33SDmitry Eremin-Solenikov 36478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_start_time = jiffies; 36578731d33SDmitry Eremin-Solenikov } 36678731d33SDmitry Eremin-Solenikov 36778731d33SDmitry Eremin-Solenikov static void sharpsl_ac_timer(unsigned long data) 36878731d33SDmitry Eremin-Solenikov { 36978731d33SDmitry Eremin-Solenikov int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 37078731d33SDmitry Eremin-Solenikov 37178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "AC Status: %d\n",acin); 37278731d33SDmitry Eremin-Solenikov 37378731d33SDmitry Eremin-Solenikov sharpsl_average_clear(); 37478731d33SDmitry Eremin-Solenikov if (acin && (sharpsl_pm.charge_mode != CHRG_ON)) 37578731d33SDmitry Eremin-Solenikov sharpsl_charge_on(); 37678731d33SDmitry Eremin-Solenikov else if (sharpsl_pm.charge_mode == CHRG_ON) 37778731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 37878731d33SDmitry Eremin-Solenikov 37978731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, 0); 38078731d33SDmitry Eremin-Solenikov } 38178731d33SDmitry Eremin-Solenikov 38278731d33SDmitry Eremin-Solenikov 383d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_ac_isr(int irq, void *dev_id) 38478731d33SDmitry Eremin-Solenikov { 38578731d33SDmitry Eremin-Solenikov /* Delay the event slightly to debounce */ 38678731d33SDmitry Eremin-Solenikov /* Must be a smaller delay than the chrg_full_isr below */ 38778731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 38878731d33SDmitry Eremin-Solenikov 38978731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 39078731d33SDmitry Eremin-Solenikov } 39178731d33SDmitry Eremin-Solenikov 39278731d33SDmitry Eremin-Solenikov static void sharpsl_chrg_full_timer(unsigned long data) 39378731d33SDmitry Eremin-Solenikov { 39478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies); 39578731d33SDmitry Eremin-Solenikov 39678731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 39778731d33SDmitry Eremin-Solenikov 39878731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 39978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n"); 40078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 40178731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 40278731d33SDmitry Eremin-Solenikov } else if (sharpsl_pm.full_count < 2) { 40378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n"); 40478731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 40578731d33SDmitry Eremin-Solenikov } else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) { 40678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n"); 40778731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 40878731d33SDmitry Eremin-Solenikov } else { 40978731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 41078731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_DONE; 41178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n"); 41278731d33SDmitry Eremin-Solenikov } 41378731d33SDmitry Eremin-Solenikov } 41478731d33SDmitry Eremin-Solenikov 41578731d33SDmitry Eremin-Solenikov /* Charging Finished Interrupt (Not present on Corgi) */ 41678731d33SDmitry Eremin-Solenikov /* Can trigger at the same time as an AC status change so 41778731d33SDmitry Eremin-Solenikov delay until after that has been processed */ 418d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id) 41978731d33SDmitry Eremin-Solenikov { 42078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.flags & SHARPSL_SUSPENDED) 42178731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 42278731d33SDmitry Eremin-Solenikov 42378731d33SDmitry Eremin-Solenikov /* delay until after any ac interrupt */ 42478731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500)); 42578731d33SDmitry Eremin-Solenikov 42678731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 42778731d33SDmitry Eremin-Solenikov } 42878731d33SDmitry Eremin-Solenikov 429d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id) 43078731d33SDmitry Eremin-Solenikov { 43178731d33SDmitry Eremin-Solenikov int is_fatal = 0; 43278731d33SDmitry Eremin-Solenikov 43378731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) { 43478731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n"); 43578731d33SDmitry Eremin-Solenikov is_fatal = 1; 43678731d33SDmitry Eremin-Solenikov } 43778731d33SDmitry Eremin-Solenikov 43878731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)) { 43978731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n"); 44078731d33SDmitry Eremin-Solenikov is_fatal = 1; 44178731d33SDmitry Eremin-Solenikov } 44278731d33SDmitry Eremin-Solenikov 44378731d33SDmitry Eremin-Solenikov if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) { 44478731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 44578731d33SDmitry Eremin-Solenikov apm_queue_event(APM_CRITICAL_SUSPEND); 44678731d33SDmitry Eremin-Solenikov } 44778731d33SDmitry Eremin-Solenikov 44878731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 44978731d33SDmitry Eremin-Solenikov } 45078731d33SDmitry Eremin-Solenikov 45178731d33SDmitry Eremin-Solenikov /* 45278731d33SDmitry Eremin-Solenikov * Maintain an average of the last 10 readings 45378731d33SDmitry Eremin-Solenikov */ 45478731d33SDmitry Eremin-Solenikov #define SHARPSL_CNV_VALUE_NUM 10 45578731d33SDmitry Eremin-Solenikov static int sharpsl_ad_index; 45678731d33SDmitry Eremin-Solenikov 45778731d33SDmitry Eremin-Solenikov static void sharpsl_average_clear(void) 45878731d33SDmitry Eremin-Solenikov { 45978731d33SDmitry Eremin-Solenikov sharpsl_ad_index = 0; 46078731d33SDmitry Eremin-Solenikov } 46178731d33SDmitry Eremin-Solenikov 46278731d33SDmitry Eremin-Solenikov static int sharpsl_average_value(int ad) 46378731d33SDmitry Eremin-Solenikov { 46478731d33SDmitry Eremin-Solenikov int i, ad_val = 0; 46578731d33SDmitry Eremin-Solenikov static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1]; 46678731d33SDmitry Eremin-Solenikov 46778731d33SDmitry Eremin-Solenikov if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) { 46878731d33SDmitry Eremin-Solenikov sharpsl_ad_index = 0; 46978731d33SDmitry Eremin-Solenikov return ad; 47078731d33SDmitry Eremin-Solenikov } 47178731d33SDmitry Eremin-Solenikov 47278731d33SDmitry Eremin-Solenikov sharpsl_ad[sharpsl_ad_index] = ad; 47378731d33SDmitry Eremin-Solenikov sharpsl_ad_index++; 47478731d33SDmitry Eremin-Solenikov if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) { 47578731d33SDmitry Eremin-Solenikov for (i=0; i < (SHARPSL_CNV_VALUE_NUM-1); i++) 47678731d33SDmitry Eremin-Solenikov sharpsl_ad[i] = sharpsl_ad[i+1]; 47778731d33SDmitry Eremin-Solenikov sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1; 47878731d33SDmitry Eremin-Solenikov } 47978731d33SDmitry Eremin-Solenikov for (i=0; i < sharpsl_ad_index; i++) 48078731d33SDmitry Eremin-Solenikov ad_val += sharpsl_ad[i]; 48178731d33SDmitry Eremin-Solenikov 48278731d33SDmitry Eremin-Solenikov return (ad_val / sharpsl_ad_index); 48378731d33SDmitry Eremin-Solenikov } 48478731d33SDmitry Eremin-Solenikov 48578731d33SDmitry Eremin-Solenikov /* 48678731d33SDmitry Eremin-Solenikov * Take an array of 5 integers, remove the maximum and minimum values 48778731d33SDmitry Eremin-Solenikov * and return the average. 48878731d33SDmitry Eremin-Solenikov */ 48978731d33SDmitry Eremin-Solenikov static int get_select_val(int *val) 49078731d33SDmitry Eremin-Solenikov { 49178731d33SDmitry Eremin-Solenikov int i, j, k, temp, sum = 0; 49278731d33SDmitry Eremin-Solenikov 49378731d33SDmitry Eremin-Solenikov /* Find MAX val */ 49478731d33SDmitry Eremin-Solenikov temp = val[0]; 49578731d33SDmitry Eremin-Solenikov j=0; 49678731d33SDmitry Eremin-Solenikov for (i=1; i<5; i++) { 49778731d33SDmitry Eremin-Solenikov if (temp < val[i]) { 49878731d33SDmitry Eremin-Solenikov temp = val[i]; 49978731d33SDmitry Eremin-Solenikov j = i; 50078731d33SDmitry Eremin-Solenikov } 50178731d33SDmitry Eremin-Solenikov } 50278731d33SDmitry Eremin-Solenikov 50378731d33SDmitry Eremin-Solenikov /* Find MIN val */ 50478731d33SDmitry Eremin-Solenikov temp = val[4]; 50578731d33SDmitry Eremin-Solenikov k=4; 50678731d33SDmitry Eremin-Solenikov for (i=3; i>=0; i--) { 50778731d33SDmitry Eremin-Solenikov if (temp > val[i]) { 50878731d33SDmitry Eremin-Solenikov temp = val[i]; 50978731d33SDmitry Eremin-Solenikov k = i; 51078731d33SDmitry Eremin-Solenikov } 51178731d33SDmitry Eremin-Solenikov } 51278731d33SDmitry Eremin-Solenikov 51378731d33SDmitry Eremin-Solenikov for (i=0; i<5; i++) 51478731d33SDmitry Eremin-Solenikov if (i != j && i != k ) 51578731d33SDmitry Eremin-Solenikov sum += val[i]; 51678731d33SDmitry Eremin-Solenikov 51778731d33SDmitry Eremin-Solenikov 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]); 51878731d33SDmitry Eremin-Solenikov 51978731d33SDmitry Eremin-Solenikov return (sum/3); 52078731d33SDmitry Eremin-Solenikov } 52178731d33SDmitry Eremin-Solenikov 52278731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_temp(void) 52378731d33SDmitry Eremin-Solenikov { 52478731d33SDmitry Eremin-Solenikov int val, i, buff[5]; 52578731d33SDmitry Eremin-Solenikov 52678731d33SDmitry Eremin-Solenikov /* Check battery temperature */ 52778731d33SDmitry Eremin-Solenikov for (i=0; i<5; i++) { 52878731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 52978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->measure_temp(1); 53078731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 53178731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_TEMP); 53278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->measure_temp(0); 53378731d33SDmitry Eremin-Solenikov } 53478731d33SDmitry Eremin-Solenikov 53578731d33SDmitry Eremin-Solenikov val = get_select_val(buff); 53678731d33SDmitry Eremin-Solenikov 53778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val); 53878731d33SDmitry Eremin-Solenikov if (val > sharpsl_pm.machinfo->charge_on_temp) { 53978731d33SDmitry Eremin-Solenikov printk(KERN_WARNING "Not charging: temperature out of limits.\n"); 54078731d33SDmitry Eremin-Solenikov return -1; 54178731d33SDmitry Eremin-Solenikov } 54278731d33SDmitry Eremin-Solenikov 54378731d33SDmitry Eremin-Solenikov return 0; 54478731d33SDmitry Eremin-Solenikov } 54578731d33SDmitry Eremin-Solenikov 54678731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 54778731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_voltage(void) 54878731d33SDmitry Eremin-Solenikov { 54978731d33SDmitry Eremin-Solenikov int val, i, buff[5]; 55078731d33SDmitry Eremin-Solenikov 55178731d33SDmitry Eremin-Solenikov /* disable charge, enable discharge */ 55278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 55378731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(1); 55478731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_WAIT_DISCHARGE_ON); 55578731d33SDmitry Eremin-Solenikov 55678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 55778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(1); 55878731d33SDmitry Eremin-Solenikov 55978731d33SDmitry Eremin-Solenikov /* Check battery voltage */ 56078731d33SDmitry Eremin-Solenikov for (i=0; i<5; i++) { 56178731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 56278731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT); 56378731d33SDmitry Eremin-Solenikov } 56478731d33SDmitry Eremin-Solenikov 56578731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 56678731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(0); 56778731d33SDmitry Eremin-Solenikov 56878731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(0); 56978731d33SDmitry Eremin-Solenikov 57078731d33SDmitry Eremin-Solenikov val = get_select_val(buff); 57178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val); 57278731d33SDmitry Eremin-Solenikov 57378731d33SDmitry Eremin-Solenikov if (val < sharpsl_pm.machinfo->charge_on_volt) 57478731d33SDmitry Eremin-Solenikov return -1; 57578731d33SDmitry Eremin-Solenikov 57678731d33SDmitry Eremin-Solenikov return 0; 57778731d33SDmitry Eremin-Solenikov } 57878731d33SDmitry Eremin-Solenikov #endif 57978731d33SDmitry Eremin-Solenikov 58078731d33SDmitry Eremin-Solenikov static int sharpsl_ac_check(void) 58178731d33SDmitry Eremin-Solenikov { 58278731d33SDmitry Eremin-Solenikov int temp, i, buff[5]; 58378731d33SDmitry Eremin-Solenikov 58478731d33SDmitry Eremin-Solenikov for (i=0; i<5; i++) { 58578731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_ACIN_VOLT); 58678731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN); 58778731d33SDmitry Eremin-Solenikov } 58878731d33SDmitry Eremin-Solenikov 58978731d33SDmitry Eremin-Solenikov temp = get_select_val(buff); 59078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n",temp); 59178731d33SDmitry Eremin-Solenikov 59278731d33SDmitry Eremin-Solenikov if ((temp > sharpsl_pm.machinfo->charge_acin_high) || (temp < sharpsl_pm.machinfo->charge_acin_low)) { 59378731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Error: AC check failed.\n"); 59478731d33SDmitry Eremin-Solenikov return -1; 59578731d33SDmitry Eremin-Solenikov } 59678731d33SDmitry Eremin-Solenikov 59778731d33SDmitry Eremin-Solenikov return 0; 59878731d33SDmitry Eremin-Solenikov } 59978731d33SDmitry Eremin-Solenikov 60078731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 60178731d33SDmitry Eremin-Solenikov static int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state) 60278731d33SDmitry Eremin-Solenikov { 60378731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_SUSPENDED; 60478731d33SDmitry Eremin-Solenikov flush_scheduled_work(); 60578731d33SDmitry Eremin-Solenikov 60678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 60778731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 60878731d33SDmitry Eremin-Solenikov else 60978731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 61078731d33SDmitry Eremin-Solenikov 61178731d33SDmitry Eremin-Solenikov return 0; 61278731d33SDmitry Eremin-Solenikov } 61378731d33SDmitry Eremin-Solenikov 61478731d33SDmitry Eremin-Solenikov static int sharpsl_pm_resume(struct platform_device *pdev) 61578731d33SDmitry Eremin-Solenikov { 61678731d33SDmitry Eremin-Solenikov /* Clear the reset source indicators as they break the bootloader upon reboot */ 61778731d33SDmitry Eremin-Solenikov RCSR = 0x0f; 61878731d33SDmitry Eremin-Solenikov sharpsl_average_clear(); 61978731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED; 62078731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_SUSPENDED; 62178731d33SDmitry Eremin-Solenikov 62278731d33SDmitry Eremin-Solenikov return 0; 62378731d33SDmitry Eremin-Solenikov } 62478731d33SDmitry Eremin-Solenikov 62578731d33SDmitry Eremin-Solenikov static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 62678731d33SDmitry Eremin-Solenikov { 62778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Time is: %08x\n",RCNR); 62878731d33SDmitry Eremin-Solenikov 62978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n",sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG); 63078731d33SDmitry Eremin-Solenikov /* not charging and AC-IN! */ 63178731d33SDmitry Eremin-Solenikov 63278731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN))) { 63378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n"); 63478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 63578731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 63678731d33SDmitry Eremin-Solenikov sharpsl_off_charge_battery(); 63778731d33SDmitry Eremin-Solenikov } 63878731d33SDmitry Eremin-Solenikov 63978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->presuspend(); 64078731d33SDmitry Eremin-Solenikov 64178731d33SDmitry Eremin-Solenikov PEDR = 0xffffffff; /* clear it */ 64278731d33SDmitry Eremin-Solenikov 64378731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE; 64478731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) { 64578731d33SDmitry Eremin-Solenikov RTSR &= RTSR_ALE; 64678731d33SDmitry Eremin-Solenikov RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND; 64778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n",RTAR); 64878731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE; 64978731d33SDmitry Eremin-Solenikov } else if (alarm_enable) { 65078731d33SDmitry Eremin-Solenikov RTSR &= RTSR_ALE; 65178731d33SDmitry Eremin-Solenikov RTAR = alarm_time; 65278731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n",RTAR); 65378731d33SDmitry Eremin-Solenikov } else { 65478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "No alarms set.\n"); 65578731d33SDmitry Eremin-Solenikov } 65678731d33SDmitry Eremin-Solenikov 65778731d33SDmitry Eremin-Solenikov pxa_pm_enter(state); 65878731d33SDmitry Eremin-Solenikov 65978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->postsuspend(); 66078731d33SDmitry Eremin-Solenikov 66178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n",PEDR); 66278731d33SDmitry Eremin-Solenikov } 66378731d33SDmitry Eremin-Solenikov 66478731d33SDmitry Eremin-Solenikov static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 66578731d33SDmitry Eremin-Solenikov { 66678731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable) ) 66778731d33SDmitry Eremin-Solenikov { 66878731d33SDmitry Eremin-Solenikov if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) { 66978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n"); 67078731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 67178731d33SDmitry Eremin-Solenikov return 1; 67278731d33SDmitry Eremin-Solenikov } 67378731d33SDmitry Eremin-Solenikov if(sharpsl_off_charge_battery()) { 67478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n"); 67578731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 67678731d33SDmitry Eremin-Solenikov return 1; 67778731d33SDmitry Eremin-Solenikov } 67878731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n"); 67978731d33SDmitry Eremin-Solenikov } 68078731d33SDmitry Eremin-Solenikov 68178731d33SDmitry Eremin-Solenikov if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) || (sharpsl_fatal_check() < 0) ) 68278731d33SDmitry Eremin-Solenikov { 68378731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n"); 68478731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 68578731d33SDmitry Eremin-Solenikov return 1; 68678731d33SDmitry Eremin-Solenikov } 68778731d33SDmitry Eremin-Solenikov 68878731d33SDmitry Eremin-Solenikov return 0; 68978731d33SDmitry Eremin-Solenikov } 69078731d33SDmitry Eremin-Solenikov 69178731d33SDmitry Eremin-Solenikov static int corgi_pxa_pm_enter(suspend_state_t state) 69278731d33SDmitry Eremin-Solenikov { 69378731d33SDmitry Eremin-Solenikov unsigned long alarm_time = RTAR; 69478731d33SDmitry Eremin-Solenikov unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0); 69578731d33SDmitry Eremin-Solenikov 69678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n"); 69778731d33SDmitry Eremin-Solenikov 69878731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_status, state); 69978731d33SDmitry Eremin-Solenikov 70078731d33SDmitry Eremin-Solenikov while (corgi_enter_suspend(alarm_time,alarm_status,state)) 70178731d33SDmitry Eremin-Solenikov {} 70278731d33SDmitry Eremin-Solenikov 70378731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->earlyresume) 70478731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->earlyresume(); 70578731d33SDmitry Eremin-Solenikov 70678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); 70778731d33SDmitry Eremin-Solenikov 70878731d33SDmitry Eremin-Solenikov return 0; 70978731d33SDmitry Eremin-Solenikov } 71078731d33SDmitry Eremin-Solenikov 71178731d33SDmitry Eremin-Solenikov /* 71278731d33SDmitry Eremin-Solenikov * Check for fatal battery errors 71378731d33SDmitry Eremin-Solenikov * Fatal returns -1 71478731d33SDmitry Eremin-Solenikov */ 71578731d33SDmitry Eremin-Solenikov static int sharpsl_fatal_check(void) 71678731d33SDmitry Eremin-Solenikov { 71778731d33SDmitry Eremin-Solenikov int buff[5], temp, i, acin; 71878731d33SDmitry Eremin-Solenikov 71978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check entered\n"); 72078731d33SDmitry Eremin-Solenikov 72178731d33SDmitry Eremin-Solenikov /* Check AC-Adapter */ 72278731d33SDmitry Eremin-Solenikov acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 72378731d33SDmitry Eremin-Solenikov 72478731d33SDmitry Eremin-Solenikov if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) { 72578731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 72678731d33SDmitry Eremin-Solenikov udelay(100); 72778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(1); /* enable discharge */ 72878731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_WAIT_DISCHARGE_ON); 72978731d33SDmitry Eremin-Solenikov } 73078731d33SDmitry Eremin-Solenikov 73178731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 73278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(1); 73378731d33SDmitry Eremin-Solenikov 73478731d33SDmitry Eremin-Solenikov /* Check battery : check inserting battery ? */ 73578731d33SDmitry Eremin-Solenikov for (i=0; i<5; i++) { 73678731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 73778731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT); 73878731d33SDmitry Eremin-Solenikov } 73978731d33SDmitry Eremin-Solenikov 74078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 74178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(0); 74278731d33SDmitry Eremin-Solenikov 74378731d33SDmitry Eremin-Solenikov if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) { 74478731d33SDmitry Eremin-Solenikov udelay(100); 74578731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 74678731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(0); 74778731d33SDmitry Eremin-Solenikov } 74878731d33SDmitry Eremin-Solenikov 74978731d33SDmitry Eremin-Solenikov temp = get_select_val(buff); 75078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check: acin: %d, discharge voltage: %d, no discharge: %ld\n", acin, temp, sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT)); 75178731d33SDmitry Eremin-Solenikov 75278731d33SDmitry Eremin-Solenikov if ((acin && (temp < sharpsl_pm.machinfo->fatal_acin_volt)) || 75378731d33SDmitry Eremin-Solenikov (!acin && (temp < sharpsl_pm.machinfo->fatal_noacin_volt))) 75478731d33SDmitry Eremin-Solenikov return -1; 75578731d33SDmitry Eremin-Solenikov return 0; 75678731d33SDmitry Eremin-Solenikov } 75778731d33SDmitry Eremin-Solenikov 75878731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_error(void) 75978731d33SDmitry Eremin-Solenikov { 76078731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Offline Charger: Error occurred.\n"); 76178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 76278731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ERROR); 76378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ERROR; 76478731d33SDmitry Eremin-Solenikov return 1; 76578731d33SDmitry Eremin-Solenikov } 76678731d33SDmitry Eremin-Solenikov 76778731d33SDmitry Eremin-Solenikov /* 76878731d33SDmitry Eremin-Solenikov * Charging Control while suspended 76978731d33SDmitry Eremin-Solenikov * Return 1 - go straight to sleep 77078731d33SDmitry Eremin-Solenikov * Return 0 - sleep or wakeup depending on other factors 77178731d33SDmitry Eremin-Solenikov */ 77278731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_battery(void) 77378731d33SDmitry Eremin-Solenikov { 77478731d33SDmitry Eremin-Solenikov int time; 77578731d33SDmitry Eremin-Solenikov 77678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode); 77778731d33SDmitry Eremin-Solenikov 77878731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_OFF) { 77978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n"); 78078731d33SDmitry Eremin-Solenikov 78178731d33SDmitry Eremin-Solenikov /* AC Check */ 78278731d33SDmitry Eremin-Solenikov if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery_temp() < 0)) 78378731d33SDmitry Eremin-Solenikov return sharpsl_off_charge_error(); 78478731d33SDmitry Eremin-Solenikov 78578731d33SDmitry Eremin-Solenikov /* Start Charging */ 78678731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ON); 78778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 78878731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 78978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 79078731d33SDmitry Eremin-Solenikov 79178731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 79278731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 79378731d33SDmitry Eremin-Solenikov 79478731d33SDmitry Eremin-Solenikov return 1; 79578731d33SDmitry Eremin-Solenikov } else if (sharpsl_pm.charge_mode != CHRG_ON) { 79678731d33SDmitry Eremin-Solenikov return 1; 79778731d33SDmitry Eremin-Solenikov } 79878731d33SDmitry Eremin-Solenikov 79978731d33SDmitry Eremin-Solenikov if (sharpsl_pm.full_count == 0) { 80078731d33SDmitry Eremin-Solenikov int time; 80178731d33SDmitry Eremin-Solenikov 80278731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n"); 80378731d33SDmitry Eremin-Solenikov 80478731d33SDmitry Eremin-Solenikov if ((sharpsl_check_battery_temp() < 0) || (sharpsl_check_battery_voltage() < 0)) 80578731d33SDmitry Eremin-Solenikov return sharpsl_off_charge_error(); 80678731d33SDmitry Eremin-Solenikov 80778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 80878731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 80978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 81078731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 81178731d33SDmitry Eremin-Solenikov 81278731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 81378731d33SDmitry Eremin-Solenikov 81478731d33SDmitry Eremin-Solenikov time = RCNR; 81578731d33SDmitry Eremin-Solenikov while(1) { 81678731d33SDmitry Eremin-Solenikov /* Check if any wakeup event had occurred */ 81778731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->charger_wakeup() != 0) 81878731d33SDmitry Eremin-Solenikov return 0; 81978731d33SDmitry Eremin-Solenikov /* Check for timeout */ 82078731d33SDmitry Eremin-Solenikov if ((RCNR - time) > SHARPSL_WAIT_CO_TIME) 82178731d33SDmitry Eremin-Solenikov return 1; 82278731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 82378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occurred. Retrying to check\n"); 82478731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 82578731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 82678731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 82778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 82878731d33SDmitry Eremin-Solenikov return 1; 82978731d33SDmitry Eremin-Solenikov } 83078731d33SDmitry Eremin-Solenikov } 83178731d33SDmitry Eremin-Solenikov } 83278731d33SDmitry Eremin-Solenikov 83378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n"); 83478731d33SDmitry Eremin-Solenikov 83578731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 83678731d33SDmitry Eremin-Solenikov 83778731d33SDmitry Eremin-Solenikov time = RCNR; 83878731d33SDmitry Eremin-Solenikov while(1) { 83978731d33SDmitry Eremin-Solenikov /* Check if any wakeup event had occurred */ 84078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->charger_wakeup() != 0) 84178731d33SDmitry Eremin-Solenikov return 0; 84278731d33SDmitry Eremin-Solenikov /* Check for timeout */ 84378731d33SDmitry Eremin-Solenikov if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) { 84478731d33SDmitry Eremin-Solenikov if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) { 84578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n"); 84678731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 84778731d33SDmitry Eremin-Solenikov } 84878731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 84978731d33SDmitry Eremin-Solenikov return 1; 85078731d33SDmitry Eremin-Solenikov } 85178731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 85278731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n"); 85378731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_OFF); 85478731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 85578731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_DONE; 85678731d33SDmitry Eremin-Solenikov return 1; 85778731d33SDmitry Eremin-Solenikov } 85878731d33SDmitry Eremin-Solenikov } 85978731d33SDmitry Eremin-Solenikov } 86078731d33SDmitry Eremin-Solenikov #else 86178731d33SDmitry Eremin-Solenikov #define sharpsl_pm_suspend NULL 86278731d33SDmitry Eremin-Solenikov #define sharpsl_pm_resume NULL 86378731d33SDmitry Eremin-Solenikov #endif 86478731d33SDmitry Eremin-Solenikov 86578731d33SDmitry Eremin-Solenikov static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf) 86678731d33SDmitry Eremin-Solenikov { 86778731d33SDmitry Eremin-Solenikov return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_percent); 86878731d33SDmitry Eremin-Solenikov } 86978731d33SDmitry Eremin-Solenikov 87078731d33SDmitry Eremin-Solenikov static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) 87178731d33SDmitry Eremin-Solenikov { 87278731d33SDmitry Eremin-Solenikov return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_voltage); 87378731d33SDmitry Eremin-Solenikov } 87478731d33SDmitry Eremin-Solenikov 87578731d33SDmitry Eremin-Solenikov static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL); 87678731d33SDmitry Eremin-Solenikov static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL); 87778731d33SDmitry Eremin-Solenikov 87878731d33SDmitry Eremin-Solenikov extern void (*apm_get_power_status)(struct apm_power_info *); 87978731d33SDmitry Eremin-Solenikov 88078731d33SDmitry Eremin-Solenikov static void sharpsl_apm_get_power_status(struct apm_power_info *info) 88178731d33SDmitry Eremin-Solenikov { 88278731d33SDmitry Eremin-Solenikov info->ac_line_status = sharpsl_pm.battstat.ac_status; 88378731d33SDmitry Eremin-Solenikov 88478731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 88578731d33SDmitry Eremin-Solenikov info->battery_status = APM_BATTERY_STATUS_CHARGING; 88678731d33SDmitry Eremin-Solenikov else 88778731d33SDmitry Eremin-Solenikov info->battery_status = sharpsl_pm.battstat.mainbat_status; 88878731d33SDmitry Eremin-Solenikov 88978731d33SDmitry Eremin-Solenikov info->battery_flag = (1 << info->battery_status); 89078731d33SDmitry Eremin-Solenikov info->battery_life = sharpsl_pm.battstat.mainbat_percent; 89178731d33SDmitry Eremin-Solenikov } 89278731d33SDmitry Eremin-Solenikov 89378731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 89478731d33SDmitry Eremin-Solenikov static struct platform_suspend_ops sharpsl_pm_ops = { 89578731d33SDmitry Eremin-Solenikov .enter = corgi_pxa_pm_enter, 89678731d33SDmitry Eremin-Solenikov .valid = suspend_valid_only_mem, 89778731d33SDmitry Eremin-Solenikov }; 89878731d33SDmitry Eremin-Solenikov #endif 89978731d33SDmitry Eremin-Solenikov 90078731d33SDmitry Eremin-Solenikov static int __init sharpsl_pm_probe(struct platform_device *pdev) 90178731d33SDmitry Eremin-Solenikov { 90278731d33SDmitry Eremin-Solenikov int ret; 90378731d33SDmitry Eremin-Solenikov 90478731d33SDmitry Eremin-Solenikov if (!pdev->dev.platform_data) 90578731d33SDmitry Eremin-Solenikov return -EINVAL; 90678731d33SDmitry Eremin-Solenikov 90778731d33SDmitry Eremin-Solenikov sharpsl_pm.dev = &pdev->dev; 90878731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo = pdev->dev.platform_data; 90978731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 91078731d33SDmitry Eremin-Solenikov sharpsl_pm.flags = 0; 91178731d33SDmitry Eremin-Solenikov 91278731d33SDmitry Eremin-Solenikov init_timer(&sharpsl_pm.ac_timer); 91378731d33SDmitry Eremin-Solenikov sharpsl_pm.ac_timer.function = sharpsl_ac_timer; 91478731d33SDmitry Eremin-Solenikov 91578731d33SDmitry Eremin-Solenikov init_timer(&sharpsl_pm.chrg_full_timer); 91678731d33SDmitry Eremin-Solenikov sharpsl_pm.chrg_full_timer.function = sharpsl_chrg_full_timer; 91778731d33SDmitry Eremin-Solenikov 91878731d33SDmitry Eremin-Solenikov led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger); 91978731d33SDmitry Eremin-Solenikov 92078731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->init(); 92178731d33SDmitry Eremin-Solenikov 922d48898a3SDmitry Eremin-Solenikov pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN); 923d48898a3SDmitry Eremin-Solenikov pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batfull | GPIO_IN); 924d48898a3SDmitry Eremin-Solenikov pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batlock | GPIO_IN); 925d48898a3SDmitry Eremin-Solenikov 926d48898a3SDmitry Eremin-Solenikov /* Register interrupt handlers */ 927d48898a3SDmitry Eremin-Solenikov if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr, IRQF_DISABLED, "AC Input Detect", sharpsl_ac_isr)) { 928d48898a3SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin)); 929d48898a3SDmitry Eremin-Solenikov } 930d48898a3SDmitry Eremin-Solenikov else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin),IRQ_TYPE_EDGE_BOTH); 931d48898a3SDmitry Eremin-Solenikov 932d48898a3SDmitry Eremin-Solenikov if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr, IRQF_DISABLED, "Battery Cover", sharpsl_fatal_isr)) { 933d48898a3SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock)); 934d48898a3SDmitry Eremin-Solenikov } 935d48898a3SDmitry Eremin-Solenikov else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock),IRQ_TYPE_EDGE_FALLING); 936d48898a3SDmitry Eremin-Solenikov 937d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->gpio_fatal) { 938d48898a3SDmitry Eremin-Solenikov if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr, IRQF_DISABLED, "Fatal Battery", sharpsl_fatal_isr)) { 939d48898a3SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal)); 940d48898a3SDmitry Eremin-Solenikov } 941d48898a3SDmitry Eremin-Solenikov else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal),IRQ_TYPE_EDGE_FALLING); 942d48898a3SDmitry Eremin-Solenikov } 943d48898a3SDmitry Eremin-Solenikov 944d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->batfull_irq) 945d48898a3SDmitry Eremin-Solenikov { 946d48898a3SDmitry Eremin-Solenikov /* Register interrupt handler. */ 947d48898a3SDmitry Eremin-Solenikov if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr, IRQF_DISABLED, "CO", sharpsl_chrg_full_isr)) { 948d48898a3SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull)); 949d48898a3SDmitry Eremin-Solenikov } 950d48898a3SDmitry Eremin-Solenikov else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull),IRQ_TYPE_EDGE_RISING); 951d48898a3SDmitry Eremin-Solenikov } 952d48898a3SDmitry Eremin-Solenikov 95378731d33SDmitry Eremin-Solenikov ret = device_create_file(&pdev->dev, &dev_attr_battery_percentage); 95478731d33SDmitry Eremin-Solenikov ret |= device_create_file(&pdev->dev, &dev_attr_battery_voltage); 95578731d33SDmitry Eremin-Solenikov if (ret != 0) 95678731d33SDmitry Eremin-Solenikov dev_warn(&pdev->dev, "Failed to register attributes (%d)\n", ret); 95778731d33SDmitry Eremin-Solenikov 95878731d33SDmitry Eremin-Solenikov apm_get_power_status = sharpsl_apm_get_power_status; 95978731d33SDmitry Eremin-Solenikov 96078731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 96178731d33SDmitry Eremin-Solenikov suspend_set_ops(&sharpsl_pm_ops); 96278731d33SDmitry Eremin-Solenikov #endif 96378731d33SDmitry Eremin-Solenikov 96478731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 96578731d33SDmitry Eremin-Solenikov 96678731d33SDmitry Eremin-Solenikov return 0; 96778731d33SDmitry Eremin-Solenikov } 96878731d33SDmitry Eremin-Solenikov 96978731d33SDmitry Eremin-Solenikov static int sharpsl_pm_remove(struct platform_device *pdev) 97078731d33SDmitry Eremin-Solenikov { 97178731d33SDmitry Eremin-Solenikov suspend_set_ops(NULL); 97278731d33SDmitry Eremin-Solenikov 97378731d33SDmitry Eremin-Solenikov device_remove_file(&pdev->dev, &dev_attr_battery_percentage); 97478731d33SDmitry Eremin-Solenikov device_remove_file(&pdev->dev, &dev_attr_battery_voltage); 97578731d33SDmitry Eremin-Solenikov 97678731d33SDmitry Eremin-Solenikov led_trigger_unregister_simple(sharpsl_charge_led_trigger); 97778731d33SDmitry Eremin-Solenikov 978d48898a3SDmitry Eremin-Solenikov free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr); 979d48898a3SDmitry Eremin-Solenikov free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr); 980d48898a3SDmitry Eremin-Solenikov 981d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->gpio_fatal) 982d48898a3SDmitry Eremin-Solenikov free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr); 983d48898a3SDmitry Eremin-Solenikov 984d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->batfull_irq) 985d48898a3SDmitry Eremin-Solenikov free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr); 986d48898a3SDmitry Eremin-Solenikov 987d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->exit) 98878731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->exit(); 98978731d33SDmitry Eremin-Solenikov 99078731d33SDmitry Eremin-Solenikov del_timer_sync(&sharpsl_pm.chrg_full_timer); 99178731d33SDmitry Eremin-Solenikov del_timer_sync(&sharpsl_pm.ac_timer); 99278731d33SDmitry Eremin-Solenikov 99378731d33SDmitry Eremin-Solenikov return 0; 99478731d33SDmitry Eremin-Solenikov } 99578731d33SDmitry Eremin-Solenikov 99678731d33SDmitry Eremin-Solenikov static struct platform_driver sharpsl_pm_driver = { 99778731d33SDmitry Eremin-Solenikov .probe = sharpsl_pm_probe, 99878731d33SDmitry Eremin-Solenikov .remove = sharpsl_pm_remove, 99978731d33SDmitry Eremin-Solenikov .suspend = sharpsl_pm_suspend, 100078731d33SDmitry Eremin-Solenikov .resume = sharpsl_pm_resume, 100178731d33SDmitry Eremin-Solenikov .driver = { 100278731d33SDmitry Eremin-Solenikov .name = "sharpsl-pm", 100378731d33SDmitry Eremin-Solenikov }, 100478731d33SDmitry Eremin-Solenikov }; 100578731d33SDmitry Eremin-Solenikov 100678731d33SDmitry Eremin-Solenikov static int __devinit sharpsl_pm_init(void) 100778731d33SDmitry Eremin-Solenikov { 100878731d33SDmitry Eremin-Solenikov return platform_driver_register(&sharpsl_pm_driver); 100978731d33SDmitry Eremin-Solenikov } 101078731d33SDmitry Eremin-Solenikov 101178731d33SDmitry Eremin-Solenikov static void sharpsl_pm_exit(void) 101278731d33SDmitry Eremin-Solenikov { 101378731d33SDmitry Eremin-Solenikov platform_driver_unregister(&sharpsl_pm_driver); 101478731d33SDmitry Eremin-Solenikov } 101578731d33SDmitry Eremin-Solenikov 101678731d33SDmitry Eremin-Solenikov late_initcall(sharpsl_pm_init); 101778731d33SDmitry Eremin-Solenikov module_exit(sharpsl_pm_exit); 1018