1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2078abcf9SRichard Purdie /* 3078abcf9SRichard Purdie * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00 4078abcf9SRichard Purdie * series of PDAs 5078abcf9SRichard Purdie * 6078abcf9SRichard Purdie * Copyright (c) 2004-2005 Richard Purdie 7078abcf9SRichard Purdie * 8078abcf9SRichard Purdie * Based on code written by Sharp for 2.4 kernels 9078abcf9SRichard Purdie */ 10078abcf9SRichard Purdie 11078abcf9SRichard Purdie #undef DEBUG 12078abcf9SRichard Purdie 13078abcf9SRichard Purdie #include <linux/module.h> 14078abcf9SRichard Purdie #include <linux/kernel.h> 15078abcf9SRichard Purdie #include <linux/interrupt.h> 16c5e1ae97SRichard Purdie #include <linux/platform_device.h> 1761fde514SRussell King #include <linux/apm-emulation.h> 1878731d33SDmitry Eremin-Solenikov #include <linux/timer.h> 1978731d33SDmitry Eremin-Solenikov #include <linux/delay.h> 2078731d33SDmitry Eremin-Solenikov #include <linux/leds.h> 2178731d33SDmitry Eremin-Solenikov #include <linux/suspend.h> 22d5af2778SDmitry Eremin-Solenikov #include <linux/gpio.h> 2323019a73SRob Herring #include <linux/io.h> 24078abcf9SRichard Purdie 25078abcf9SRichard Purdie #include <asm/mach-types.h> 264c25c5d2SArnd Bergmann #include "pm.h" 27e6acc406SArnd Bergmann #include "pxa2xx-regs.h" 284c25c5d2SArnd Bergmann #include "regs-rtc.h" 294c25c5d2SArnd Bergmann #include "sharpsl_pm.h" 3078731d33SDmitry Eremin-Solenikov 3178731d33SDmitry Eremin-Solenikov /* 3278731d33SDmitry Eremin-Solenikov * Constants 3378731d33SDmitry Eremin-Solenikov */ 3478731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_ON_TIME_INTERVAL (msecs_to_jiffies(1*60*1000)) /* 1 min */ 3578731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_FINISH_TIME (msecs_to_jiffies(10*60*1000)) /* 10 min */ 3678731d33SDmitry Eremin-Solenikov #define SHARPSL_BATCHK_TIME (msecs_to_jiffies(15*1000)) /* 15 sec */ 3778731d33SDmitry Eremin-Solenikov #define SHARPSL_BATCHK_TIME_SUSPEND (60*10) /* 10 min */ 3878731d33SDmitry Eremin-Solenikov 3978731d33SDmitry Eremin-Solenikov #define SHARPSL_WAIT_CO_TIME 15 /* 15 sec */ 4078731d33SDmitry Eremin-Solenikov #define SHARPSL_WAIT_DISCHARGE_ON 100 /* 100 msec */ 4178731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP 10 /* 10 msec */ 4278731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT 10 /* 10 msec */ 4378731d33SDmitry Eremin-Solenikov #define SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN 10 /* 10 msec */ 4478731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_WAIT_TIME 15 /* 15 msec */ 4578731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_CO_CHECK_TIME 5 /* 5 msec */ 4678731d33SDmitry Eremin-Solenikov #define SHARPSL_CHARGE_RETRY_CNT 1 /* eqv. 10 min */ 4778731d33SDmitry Eremin-Solenikov 4878731d33SDmitry Eremin-Solenikov /* 4978731d33SDmitry Eremin-Solenikov * Prototypes 5078731d33SDmitry Eremin-Solenikov */ 5178731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 5278731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_battery(void); 5378731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_voltage(void); 5478731d33SDmitry Eremin-Solenikov #endif 5578731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_temp(void); 5678731d33SDmitry Eremin-Solenikov static int sharpsl_ac_check(void); 5778731d33SDmitry Eremin-Solenikov static int sharpsl_average_value(int ad); 5878731d33SDmitry Eremin-Solenikov static void sharpsl_average_clear(void); 5978731d33SDmitry Eremin-Solenikov static void sharpsl_charge_toggle(struct work_struct *private_); 6078731d33SDmitry Eremin-Solenikov static void sharpsl_battery_thread(struct work_struct *private_); 6178731d33SDmitry Eremin-Solenikov 6278731d33SDmitry Eremin-Solenikov 6378731d33SDmitry Eremin-Solenikov /* 6478731d33SDmitry Eremin-Solenikov * Variables 6578731d33SDmitry Eremin-Solenikov */ 6678731d33SDmitry Eremin-Solenikov struct sharpsl_pm_status sharpsl_pm; 67d48898a3SDmitry Eremin-Solenikov static DECLARE_DELAYED_WORK(toggle_charger, sharpsl_charge_toggle); 68d48898a3SDmitry Eremin-Solenikov static DECLARE_DELAYED_WORK(sharpsl_bat, sharpsl_battery_thread); 6978731d33SDmitry Eremin-Solenikov DEFINE_LED_TRIGGER(sharpsl_charge_led_trigger); 7078731d33SDmitry Eremin-Solenikov 7178731d33SDmitry Eremin-Solenikov 72078abcf9SRichard Purdie 730ba01ebcSPavel Machek struct battery_thresh sharpsl_battery_levels_acin[] = { 74078abcf9SRichard Purdie { 213, 100}, 75078abcf9SRichard Purdie { 212, 98}, 76078abcf9SRichard Purdie { 211, 95}, 77078abcf9SRichard Purdie { 210, 93}, 78078abcf9SRichard Purdie { 209, 90}, 79078abcf9SRichard Purdie { 208, 88}, 80078abcf9SRichard Purdie { 207, 85}, 81078abcf9SRichard Purdie { 206, 83}, 82078abcf9SRichard Purdie { 205, 80}, 83078abcf9SRichard Purdie { 204, 78}, 84078abcf9SRichard Purdie { 203, 75}, 85078abcf9SRichard Purdie { 202, 73}, 86078abcf9SRichard Purdie { 201, 70}, 87078abcf9SRichard Purdie { 200, 68}, 88078abcf9SRichard Purdie { 199, 65}, 89078abcf9SRichard Purdie { 198, 63}, 90078abcf9SRichard Purdie { 197, 60}, 91078abcf9SRichard Purdie { 196, 58}, 92078abcf9SRichard Purdie { 195, 55}, 93078abcf9SRichard Purdie { 194, 53}, 94078abcf9SRichard Purdie { 193, 50}, 95078abcf9SRichard Purdie { 192, 48}, 96078abcf9SRichard Purdie { 192, 45}, 97078abcf9SRichard Purdie { 191, 43}, 98078abcf9SRichard Purdie { 191, 40}, 99078abcf9SRichard Purdie { 190, 38}, 100078abcf9SRichard Purdie { 190, 35}, 101078abcf9SRichard Purdie { 189, 33}, 102078abcf9SRichard Purdie { 188, 30}, 103078abcf9SRichard Purdie { 187, 28}, 104078abcf9SRichard Purdie { 186, 25}, 105078abcf9SRichard Purdie { 185, 23}, 106078abcf9SRichard Purdie { 184, 20}, 107078abcf9SRichard Purdie { 183, 18}, 108078abcf9SRichard Purdie { 182, 15}, 109078abcf9SRichard Purdie { 181, 13}, 110078abcf9SRichard Purdie { 180, 10}, 111078abcf9SRichard Purdie { 179, 8}, 112078abcf9SRichard Purdie { 178, 5}, 113078abcf9SRichard Purdie { 0, 0}, 114078abcf9SRichard Purdie }; 115078abcf9SRichard Purdie 1160ba01ebcSPavel Machek struct battery_thresh sharpsl_battery_levels_noac[] = { 117078abcf9SRichard Purdie { 213, 100}, 118078abcf9SRichard Purdie { 212, 98}, 119078abcf9SRichard Purdie { 211, 95}, 120078abcf9SRichard Purdie { 210, 93}, 121078abcf9SRichard Purdie { 209, 90}, 122078abcf9SRichard Purdie { 208, 88}, 123078abcf9SRichard Purdie { 207, 85}, 124078abcf9SRichard Purdie { 206, 83}, 125078abcf9SRichard Purdie { 205, 80}, 126078abcf9SRichard Purdie { 204, 78}, 127078abcf9SRichard Purdie { 203, 75}, 128078abcf9SRichard Purdie { 202, 73}, 129078abcf9SRichard Purdie { 201, 70}, 130078abcf9SRichard Purdie { 200, 68}, 131078abcf9SRichard Purdie { 199, 65}, 132078abcf9SRichard Purdie { 198, 63}, 133078abcf9SRichard Purdie { 197, 60}, 134078abcf9SRichard Purdie { 196, 58}, 135078abcf9SRichard Purdie { 195, 55}, 136078abcf9SRichard Purdie { 194, 53}, 137078abcf9SRichard Purdie { 193, 50}, 138078abcf9SRichard Purdie { 192, 48}, 139078abcf9SRichard Purdie { 191, 45}, 140078abcf9SRichard Purdie { 190, 43}, 141078abcf9SRichard Purdie { 189, 40}, 142078abcf9SRichard Purdie { 188, 38}, 143078abcf9SRichard Purdie { 187, 35}, 144078abcf9SRichard Purdie { 186, 33}, 145078abcf9SRichard Purdie { 185, 30}, 146078abcf9SRichard Purdie { 184, 28}, 147078abcf9SRichard Purdie { 183, 25}, 148078abcf9SRichard Purdie { 182, 23}, 149078abcf9SRichard Purdie { 181, 20}, 150078abcf9SRichard Purdie { 180, 18}, 151078abcf9SRichard Purdie { 179, 15}, 152078abcf9SRichard Purdie { 178, 13}, 153078abcf9SRichard Purdie { 177, 10}, 154078abcf9SRichard Purdie { 176, 8}, 155078abcf9SRichard Purdie { 175, 5}, 156078abcf9SRichard Purdie { 0, 0}, 157078abcf9SRichard Purdie }; 158078abcf9SRichard Purdie 15925af3b0fSEric Miao /* MAX1111 Commands */ 160b64b0b76SPavel Machek #define MAXCTRL_PD0 (1u << 0) 161b64b0b76SPavel Machek #define MAXCTRL_PD1 (1u << 1) 162b64b0b76SPavel Machek #define MAXCTRL_SGL (1u << 2) 163b64b0b76SPavel Machek #define MAXCTRL_UNI (1u << 3) 16425af3b0fSEric Miao #define MAXCTRL_SEL_SH 4 165b64b0b76SPavel Machek #define MAXCTRL_STR (1u << 7) 16625af3b0fSEric Miao 167438d7dc2SHaojian Zhuang extern int max1111_read_channel(int); 168078abcf9SRichard Purdie /* 169078abcf9SRichard Purdie * Read MAX1111 ADC 170078abcf9SRichard Purdie */ 171b7557de4SRichard Purdie int sharpsl_pm_pxa_read_max1111(int channel) 172078abcf9SRichard Purdie { 173f16177c2SEric Miao /* max1111 accepts channels from 0-3, however, 174f16177c2SEric Miao * it is encoded from 0-7 here in the code. 175f16177c2SEric Miao */ 176f16177c2SEric Miao return max1111_read_channel(channel >> 1); 177078abcf9SRichard Purdie } 178078abcf9SRichard Purdie 17978731d33SDmitry Eremin-Solenikov static int get_percentage(int voltage) 18078731d33SDmitry Eremin-Solenikov { 18178731d33SDmitry Eremin-Solenikov int i = sharpsl_pm.machinfo->bat_levels - 1; 18278731d33SDmitry Eremin-Solenikov int bl_status = sharpsl_pm.machinfo->backlight_get_status ? sharpsl_pm.machinfo->backlight_get_status() : 0; 18378731d33SDmitry Eremin-Solenikov struct battery_thresh *thresh; 18478731d33SDmitry Eremin-Solenikov 18578731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 18678731d33SDmitry Eremin-Solenikov thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_acin_bl : sharpsl_pm.machinfo->bat_levels_acin; 18778731d33SDmitry Eremin-Solenikov else 18878731d33SDmitry Eremin-Solenikov thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_noac_bl : sharpsl_pm.machinfo->bat_levels_noac; 18978731d33SDmitry Eremin-Solenikov 19078731d33SDmitry Eremin-Solenikov while (i > 0 && (voltage > thresh[i].voltage)) 19178731d33SDmitry Eremin-Solenikov i--; 19278731d33SDmitry Eremin-Solenikov 19378731d33SDmitry Eremin-Solenikov return thresh[i].percentage; 19478731d33SDmitry Eremin-Solenikov } 19578731d33SDmitry Eremin-Solenikov 19678731d33SDmitry Eremin-Solenikov static int get_apm_status(int voltage) 19778731d33SDmitry Eremin-Solenikov { 19878731d33SDmitry Eremin-Solenikov int low_thresh, high_thresh; 19978731d33SDmitry Eremin-Solenikov 20078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) { 20178731d33SDmitry Eremin-Solenikov high_thresh = sharpsl_pm.machinfo->status_high_acin; 20278731d33SDmitry Eremin-Solenikov low_thresh = sharpsl_pm.machinfo->status_low_acin; 20378731d33SDmitry Eremin-Solenikov } else { 20478731d33SDmitry Eremin-Solenikov high_thresh = sharpsl_pm.machinfo->status_high_noac; 20578731d33SDmitry Eremin-Solenikov low_thresh = sharpsl_pm.machinfo->status_low_noac; 20678731d33SDmitry Eremin-Solenikov } 20778731d33SDmitry Eremin-Solenikov 20878731d33SDmitry Eremin-Solenikov if (voltage >= high_thresh) 20978731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_HIGH; 21078731d33SDmitry Eremin-Solenikov if (voltage >= low_thresh) 21178731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_LOW; 21278731d33SDmitry Eremin-Solenikov return APM_BATTERY_STATUS_CRITICAL; 21378731d33SDmitry Eremin-Solenikov } 21478731d33SDmitry Eremin-Solenikov 21578731d33SDmitry Eremin-Solenikov void sharpsl_battery_kick(void) 21678731d33SDmitry Eremin-Solenikov { 21778731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125)); 21878731d33SDmitry Eremin-Solenikov } 21978731d33SDmitry Eremin-Solenikov EXPORT_SYMBOL(sharpsl_battery_kick); 22078731d33SDmitry Eremin-Solenikov 22178731d33SDmitry Eremin-Solenikov 22278731d33SDmitry Eremin-Solenikov static void sharpsl_battery_thread(struct work_struct *private_) 22378731d33SDmitry Eremin-Solenikov { 224b64b0b76SPavel Machek int voltage, percent, apm_status, i; 22578731d33SDmitry Eremin-Solenikov 22678731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo) 22778731d33SDmitry Eremin-Solenikov return; 22878731d33SDmitry Eremin-Solenikov 22978731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.ac_status = (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN) ? APM_AC_ONLINE : APM_AC_OFFLINE); 23078731d33SDmitry Eremin-Solenikov 23178731d33SDmitry Eremin-Solenikov /* Corgi cannot confirm when battery fully charged so periodically kick! */ 23278731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->batfull_irq && (sharpsl_pm.charge_mode == CHRG_ON) 23378731d33SDmitry Eremin-Solenikov && time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_ON_TIME_INTERVAL)) 23478731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 23578731d33SDmitry Eremin-Solenikov 236b64b0b76SPavel Machek for (i = 0; i < 5; i++) { 23778731d33SDmitry Eremin-Solenikov voltage = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 238b64b0b76SPavel Machek if (voltage > 0) 23978731d33SDmitry Eremin-Solenikov break; 24078731d33SDmitry Eremin-Solenikov } 241b64b0b76SPavel Machek if (voltage <= 0) { 242b64b0b76SPavel Machek voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage; 243b64b0b76SPavel Machek dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n"); 24478731d33SDmitry Eremin-Solenikov } 24578731d33SDmitry Eremin-Solenikov 24678731d33SDmitry Eremin-Solenikov voltage = sharpsl_average_value(voltage); 24778731d33SDmitry Eremin-Solenikov apm_status = get_apm_status(voltage); 24878731d33SDmitry Eremin-Solenikov percent = get_percentage(voltage); 24978731d33SDmitry Eremin-Solenikov 25078731d33SDmitry Eremin-Solenikov /* At low battery voltages, the voltage has a tendency to start 25178731d33SDmitry Eremin-Solenikov creeping back up so we try to avoid this here */ 252b64b0b76SPavel Machek if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) 253b64b0b76SPavel Machek || (apm_status == APM_BATTERY_STATUS_HIGH) 254b64b0b76SPavel Machek || percent <= sharpsl_pm.battstat.mainbat_percent) { 25578731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_voltage = voltage; 25678731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_status = apm_status; 25778731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_percent = percent; 25878731d33SDmitry Eremin-Solenikov } 25978731d33SDmitry Eremin-Solenikov 26078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage, 26178731d33SDmitry Eremin-Solenikov sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies); 26278731d33SDmitry Eremin-Solenikov 26378731d33SDmitry Eremin-Solenikov /* Suspend if critical battery level */ 26478731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 26578731d33SDmitry Eremin-Solenikov && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL) 26678731d33SDmitry Eremin-Solenikov && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) { 26778731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 26878731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal Off\n"); 26978731d33SDmitry Eremin-Solenikov apm_queue_event(APM_CRITICAL_SUSPEND); 27078731d33SDmitry Eremin-Solenikov } 27178731d33SDmitry Eremin-Solenikov 27278731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME); 27378731d33SDmitry Eremin-Solenikov } 27478731d33SDmitry Eremin-Solenikov 27578731d33SDmitry Eremin-Solenikov void sharpsl_pm_led(int val) 27678731d33SDmitry Eremin-Solenikov { 27778731d33SDmitry Eremin-Solenikov if (val == SHARPSL_LED_ERROR) { 27878731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Charging Error!\n"); 27978731d33SDmitry Eremin-Solenikov } else if (val == SHARPSL_LED_ON) { 28078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge LED On\n"); 28178731d33SDmitry Eremin-Solenikov led_trigger_event(sharpsl_charge_led_trigger, LED_FULL); 28278731d33SDmitry Eremin-Solenikov } else { 28378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge LED Off\n"); 28478731d33SDmitry Eremin-Solenikov led_trigger_event(sharpsl_charge_led_trigger, LED_OFF); 28578731d33SDmitry Eremin-Solenikov } 28678731d33SDmitry Eremin-Solenikov } 28778731d33SDmitry Eremin-Solenikov 28878731d33SDmitry Eremin-Solenikov static void sharpsl_charge_on(void) 28978731d33SDmitry Eremin-Solenikov { 29078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Turning Charger On\n"); 29178731d33SDmitry Eremin-Solenikov 29278731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 29378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 29478731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250)); 29578731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500)); 29678731d33SDmitry Eremin-Solenikov } 29778731d33SDmitry Eremin-Solenikov 29878731d33SDmitry Eremin-Solenikov static void sharpsl_charge_off(void) 29978731d33SDmitry Eremin-Solenikov { 30078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n"); 30178731d33SDmitry Eremin-Solenikov 30278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 30378731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_OFF); 30478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 30578731d33SDmitry Eremin-Solenikov 30678731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, 0); 30778731d33SDmitry Eremin-Solenikov } 30878731d33SDmitry Eremin-Solenikov 30978731d33SDmitry Eremin-Solenikov static void sharpsl_charge_error(void) 31078731d33SDmitry Eremin-Solenikov { 31178731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ERROR); 31278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 31378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ERROR; 31478731d33SDmitry Eremin-Solenikov } 31578731d33SDmitry Eremin-Solenikov 31678731d33SDmitry Eremin-Solenikov static void sharpsl_charge_toggle(struct work_struct *private_) 31778731d33SDmitry Eremin-Solenikov { 318b64b0b76SPavel Machek dev_dbg(sharpsl_pm.dev, "Toggling Charger at time: %lx\n", jiffies); 31978731d33SDmitry Eremin-Solenikov 32078731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 32178731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 32278731d33SDmitry Eremin-Solenikov return; 32378731d33SDmitry Eremin-Solenikov } else if ((sharpsl_check_battery_temp() < 0) || (sharpsl_ac_check() < 0)) { 32478731d33SDmitry Eremin-Solenikov sharpsl_charge_error(); 32578731d33SDmitry Eremin-Solenikov return; 32678731d33SDmitry Eremin-Solenikov } 32778731d33SDmitry Eremin-Solenikov 32878731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ON); 32978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 33078731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 33178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 33278731d33SDmitry Eremin-Solenikov 33378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_start_time = jiffies; 33478731d33SDmitry Eremin-Solenikov } 33578731d33SDmitry Eremin-Solenikov 33696d13082SKees Cook static void sharpsl_ac_timer(struct timer_list *unused) 33778731d33SDmitry Eremin-Solenikov { 33878731d33SDmitry Eremin-Solenikov int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 33978731d33SDmitry Eremin-Solenikov 34078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "AC Status: %d\n", acin); 34178731d33SDmitry Eremin-Solenikov 34278731d33SDmitry Eremin-Solenikov sharpsl_average_clear(); 34378731d33SDmitry Eremin-Solenikov if (acin && (sharpsl_pm.charge_mode != CHRG_ON)) 34478731d33SDmitry Eremin-Solenikov sharpsl_charge_on(); 34578731d33SDmitry Eremin-Solenikov else if (sharpsl_pm.charge_mode == CHRG_ON) 34678731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 34778731d33SDmitry Eremin-Solenikov 34878731d33SDmitry Eremin-Solenikov schedule_delayed_work(&sharpsl_bat, 0); 34978731d33SDmitry Eremin-Solenikov } 35078731d33SDmitry Eremin-Solenikov 35178731d33SDmitry Eremin-Solenikov 352d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_ac_isr(int irq, void *dev_id) 35378731d33SDmitry Eremin-Solenikov { 35478731d33SDmitry Eremin-Solenikov /* Delay the event slightly to debounce */ 35578731d33SDmitry Eremin-Solenikov /* Must be a smaller delay than the chrg_full_isr below */ 35678731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 35778731d33SDmitry Eremin-Solenikov 35878731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 35978731d33SDmitry Eremin-Solenikov } 36078731d33SDmitry Eremin-Solenikov 36196d13082SKees Cook static void sharpsl_chrg_full_timer(struct timer_list *unused) 36278731d33SDmitry Eremin-Solenikov { 36378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies); 36478731d33SDmitry Eremin-Solenikov 36578731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 36678731d33SDmitry Eremin-Solenikov 36778731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 36878731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n"); 36978731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 37078731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 37178731d33SDmitry Eremin-Solenikov } else if (sharpsl_pm.full_count < 2) { 37278731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n"); 37378731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 37478731d33SDmitry Eremin-Solenikov } else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) { 37578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n"); 37678731d33SDmitry Eremin-Solenikov schedule_delayed_work(&toggle_charger, 0); 37778731d33SDmitry Eremin-Solenikov } else { 37878731d33SDmitry Eremin-Solenikov sharpsl_charge_off(); 37978731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_DONE; 38078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n"); 38178731d33SDmitry Eremin-Solenikov } 38278731d33SDmitry Eremin-Solenikov } 38378731d33SDmitry Eremin-Solenikov 38478731d33SDmitry Eremin-Solenikov /* Charging Finished Interrupt (Not present on Corgi) */ 38578731d33SDmitry Eremin-Solenikov /* Can trigger at the same time as an AC status change so 38678731d33SDmitry Eremin-Solenikov delay until after that has been processed */ 387d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id) 38878731d33SDmitry Eremin-Solenikov { 38978731d33SDmitry Eremin-Solenikov if (sharpsl_pm.flags & SHARPSL_SUSPENDED) 39078731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 39178731d33SDmitry Eremin-Solenikov 39278731d33SDmitry Eremin-Solenikov /* delay until after any ac interrupt */ 39378731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500)); 39478731d33SDmitry Eremin-Solenikov 39578731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 39678731d33SDmitry Eremin-Solenikov } 39778731d33SDmitry Eremin-Solenikov 398d48898a3SDmitry Eremin-Solenikov static irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id) 39978731d33SDmitry Eremin-Solenikov { 40078731d33SDmitry Eremin-Solenikov int is_fatal = 0; 40178731d33SDmitry Eremin-Solenikov 40278731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) { 40378731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n"); 40478731d33SDmitry Eremin-Solenikov is_fatal = 1; 40578731d33SDmitry Eremin-Solenikov } 40678731d33SDmitry Eremin-Solenikov 40778731d33SDmitry Eremin-Solenikov if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)) { 40878731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n"); 40978731d33SDmitry Eremin-Solenikov is_fatal = 1; 41078731d33SDmitry Eremin-Solenikov } 41178731d33SDmitry Eremin-Solenikov 41278731d33SDmitry Eremin-Solenikov if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) { 41378731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 41478731d33SDmitry Eremin-Solenikov apm_queue_event(APM_CRITICAL_SUSPEND); 41578731d33SDmitry Eremin-Solenikov } 41678731d33SDmitry Eremin-Solenikov 41778731d33SDmitry Eremin-Solenikov return IRQ_HANDLED; 41878731d33SDmitry Eremin-Solenikov } 41978731d33SDmitry Eremin-Solenikov 42078731d33SDmitry Eremin-Solenikov /* 42178731d33SDmitry Eremin-Solenikov * Maintain an average of the last 10 readings 42278731d33SDmitry Eremin-Solenikov */ 42378731d33SDmitry Eremin-Solenikov #define SHARPSL_CNV_VALUE_NUM 10 42478731d33SDmitry Eremin-Solenikov static int sharpsl_ad_index; 42578731d33SDmitry Eremin-Solenikov 42678731d33SDmitry Eremin-Solenikov static void sharpsl_average_clear(void) 42778731d33SDmitry Eremin-Solenikov { 42878731d33SDmitry Eremin-Solenikov sharpsl_ad_index = 0; 42978731d33SDmitry Eremin-Solenikov } 43078731d33SDmitry Eremin-Solenikov 43178731d33SDmitry Eremin-Solenikov static int sharpsl_average_value(int ad) 43278731d33SDmitry Eremin-Solenikov { 43378731d33SDmitry Eremin-Solenikov int i, ad_val = 0; 43478731d33SDmitry Eremin-Solenikov static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1]; 43578731d33SDmitry Eremin-Solenikov 43678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) { 43778731d33SDmitry Eremin-Solenikov sharpsl_ad_index = 0; 43878731d33SDmitry Eremin-Solenikov return ad; 43978731d33SDmitry Eremin-Solenikov } 44078731d33SDmitry Eremin-Solenikov 44178731d33SDmitry Eremin-Solenikov sharpsl_ad[sharpsl_ad_index] = ad; 44278731d33SDmitry Eremin-Solenikov sharpsl_ad_index++; 44378731d33SDmitry Eremin-Solenikov if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) { 44478731d33SDmitry Eremin-Solenikov for (i = 0; i < (SHARPSL_CNV_VALUE_NUM-1); i++) 44578731d33SDmitry Eremin-Solenikov sharpsl_ad[i] = sharpsl_ad[i+1]; 44678731d33SDmitry Eremin-Solenikov sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1; 44778731d33SDmitry Eremin-Solenikov } 44878731d33SDmitry Eremin-Solenikov for (i = 0; i < sharpsl_ad_index; i++) 44978731d33SDmitry Eremin-Solenikov ad_val += sharpsl_ad[i]; 45078731d33SDmitry Eremin-Solenikov 451b64b0b76SPavel Machek return ad_val / sharpsl_ad_index; 45278731d33SDmitry Eremin-Solenikov } 45378731d33SDmitry Eremin-Solenikov 45478731d33SDmitry Eremin-Solenikov /* 45578731d33SDmitry Eremin-Solenikov * Take an array of 5 integers, remove the maximum and minimum values 45678731d33SDmitry Eremin-Solenikov * and return the average. 45778731d33SDmitry Eremin-Solenikov */ 45878731d33SDmitry Eremin-Solenikov static int get_select_val(int *val) 45978731d33SDmitry Eremin-Solenikov { 46078731d33SDmitry Eremin-Solenikov int i, j, k, temp, sum = 0; 46178731d33SDmitry Eremin-Solenikov 46278731d33SDmitry Eremin-Solenikov /* Find MAX val */ 46378731d33SDmitry Eremin-Solenikov temp = val[0]; 46478731d33SDmitry Eremin-Solenikov j = 0; 46578731d33SDmitry Eremin-Solenikov for (i = 1; i < 5; i++) { 46678731d33SDmitry Eremin-Solenikov if (temp < val[i]) { 46778731d33SDmitry Eremin-Solenikov temp = val[i]; 46878731d33SDmitry Eremin-Solenikov j = i; 46978731d33SDmitry Eremin-Solenikov } 47078731d33SDmitry Eremin-Solenikov } 47178731d33SDmitry Eremin-Solenikov 47278731d33SDmitry Eremin-Solenikov /* Find MIN val */ 47378731d33SDmitry Eremin-Solenikov temp = val[4]; 47478731d33SDmitry Eremin-Solenikov k = 4; 47578731d33SDmitry Eremin-Solenikov for (i = 3; i >= 0; i--) { 47678731d33SDmitry Eremin-Solenikov if (temp > val[i]) { 47778731d33SDmitry Eremin-Solenikov temp = val[i]; 47878731d33SDmitry Eremin-Solenikov k = i; 47978731d33SDmitry Eremin-Solenikov } 48078731d33SDmitry Eremin-Solenikov } 48178731d33SDmitry Eremin-Solenikov 48278731d33SDmitry Eremin-Solenikov for (i = 0; i < 5; i++) 48378731d33SDmitry Eremin-Solenikov if (i != j && i != k) 48478731d33SDmitry Eremin-Solenikov sum += val[i]; 48578731d33SDmitry Eremin-Solenikov 48678731d33SDmitry 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]); 48778731d33SDmitry Eremin-Solenikov 488b64b0b76SPavel Machek return sum/3; 48978731d33SDmitry Eremin-Solenikov } 49078731d33SDmitry Eremin-Solenikov 49178731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_temp(void) 49278731d33SDmitry Eremin-Solenikov { 49378731d33SDmitry Eremin-Solenikov int val, i, buff[5]; 49478731d33SDmitry Eremin-Solenikov 49578731d33SDmitry Eremin-Solenikov /* Check battery temperature */ 49678731d33SDmitry Eremin-Solenikov for (i = 0; i < 5; i++) { 49778731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 49878731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->measure_temp(1); 49978731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 50078731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_TEMP); 50178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->measure_temp(0); 50278731d33SDmitry Eremin-Solenikov } 50378731d33SDmitry Eremin-Solenikov 50478731d33SDmitry Eremin-Solenikov val = get_select_val(buff); 50578731d33SDmitry Eremin-Solenikov 50678731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val); 50778731d33SDmitry Eremin-Solenikov if (val > sharpsl_pm.machinfo->charge_on_temp) { 50878731d33SDmitry Eremin-Solenikov printk(KERN_WARNING "Not charging: temperature out of limits.\n"); 50978731d33SDmitry Eremin-Solenikov return -1; 51078731d33SDmitry Eremin-Solenikov } 51178731d33SDmitry Eremin-Solenikov 51278731d33SDmitry Eremin-Solenikov return 0; 51378731d33SDmitry Eremin-Solenikov } 51478731d33SDmitry Eremin-Solenikov 51578731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 51678731d33SDmitry Eremin-Solenikov static int sharpsl_check_battery_voltage(void) 51778731d33SDmitry Eremin-Solenikov { 51878731d33SDmitry Eremin-Solenikov int val, i, buff[5]; 51978731d33SDmitry Eremin-Solenikov 52078731d33SDmitry Eremin-Solenikov /* disable charge, enable discharge */ 52178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 52278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(1); 52378731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_WAIT_DISCHARGE_ON); 52478731d33SDmitry Eremin-Solenikov 52578731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 52678731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(1); 52778731d33SDmitry Eremin-Solenikov 52878731d33SDmitry Eremin-Solenikov /* Check battery voltage */ 52978731d33SDmitry Eremin-Solenikov for (i = 0; i < 5; i++) { 53078731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 53178731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT); 53278731d33SDmitry Eremin-Solenikov } 53378731d33SDmitry Eremin-Solenikov 53478731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->discharge1) 53578731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge1(0); 53678731d33SDmitry Eremin-Solenikov 53778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->discharge(0); 53878731d33SDmitry Eremin-Solenikov 53978731d33SDmitry Eremin-Solenikov val = get_select_val(buff); 54078731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val); 54178731d33SDmitry Eremin-Solenikov 54278731d33SDmitry Eremin-Solenikov if (val < sharpsl_pm.machinfo->charge_on_volt) 54378731d33SDmitry Eremin-Solenikov return -1; 54478731d33SDmitry Eremin-Solenikov 54578731d33SDmitry Eremin-Solenikov return 0; 54678731d33SDmitry Eremin-Solenikov } 54778731d33SDmitry Eremin-Solenikov #endif 54878731d33SDmitry Eremin-Solenikov 54978731d33SDmitry Eremin-Solenikov static int sharpsl_ac_check(void) 55078731d33SDmitry Eremin-Solenikov { 55178731d33SDmitry Eremin-Solenikov int temp, i, buff[5]; 55278731d33SDmitry Eremin-Solenikov 55378731d33SDmitry Eremin-Solenikov for (i = 0; i < 5; i++) { 55478731d33SDmitry Eremin-Solenikov buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_ACIN_VOLT); 55578731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN); 55678731d33SDmitry Eremin-Solenikov } 55778731d33SDmitry Eremin-Solenikov 55878731d33SDmitry Eremin-Solenikov temp = get_select_val(buff); 55978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n", temp); 56078731d33SDmitry Eremin-Solenikov 56178731d33SDmitry Eremin-Solenikov if ((temp > sharpsl_pm.machinfo->charge_acin_high) || (temp < sharpsl_pm.machinfo->charge_acin_low)) { 562b64b0b76SPavel Machek dev_err(sharpsl_pm.dev, "Error: AC check failed: voltage %d.\n", temp); 56378731d33SDmitry Eremin-Solenikov return -1; 56478731d33SDmitry Eremin-Solenikov } 56578731d33SDmitry Eremin-Solenikov 56678731d33SDmitry Eremin-Solenikov return 0; 56778731d33SDmitry Eremin-Solenikov } 56878731d33SDmitry Eremin-Solenikov 56978731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 57078731d33SDmitry Eremin-Solenikov static int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state) 57178731d33SDmitry Eremin-Solenikov { 57278731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_SUSPENDED; 57343829731STejun Heo flush_delayed_work(&toggle_charger); 57443829731STejun Heo flush_delayed_work(&sharpsl_bat); 57578731d33SDmitry Eremin-Solenikov 57678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 57778731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 57878731d33SDmitry Eremin-Solenikov else 57978731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 58078731d33SDmitry Eremin-Solenikov 58178731d33SDmitry Eremin-Solenikov return 0; 58278731d33SDmitry Eremin-Solenikov } 58378731d33SDmitry Eremin-Solenikov 58478731d33SDmitry Eremin-Solenikov static int sharpsl_pm_resume(struct platform_device *pdev) 58578731d33SDmitry Eremin-Solenikov { 58678731d33SDmitry Eremin-Solenikov /* Clear the reset source indicators as they break the bootloader upon reboot */ 58778731d33SDmitry Eremin-Solenikov RCSR = 0x0f; 58878731d33SDmitry Eremin-Solenikov sharpsl_average_clear(); 58978731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED; 59078731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_SUSPENDED; 59178731d33SDmitry Eremin-Solenikov 59278731d33SDmitry Eremin-Solenikov return 0; 59378731d33SDmitry Eremin-Solenikov } 59478731d33SDmitry Eremin-Solenikov 59578731d33SDmitry Eremin-Solenikov static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 59678731d33SDmitry Eremin-Solenikov { 59778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Time is: %08x\n", RCNR); 59878731d33SDmitry Eremin-Solenikov 59978731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n", sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG); 60078731d33SDmitry Eremin-Solenikov /* not charging and AC-IN! */ 60178731d33SDmitry Eremin-Solenikov 60278731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN))) { 60378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n"); 60478731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 60578731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 60678731d33SDmitry Eremin-Solenikov sharpsl_off_charge_battery(); 60778731d33SDmitry Eremin-Solenikov } 60878731d33SDmitry Eremin-Solenikov 60978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->presuspend(); 61078731d33SDmitry Eremin-Solenikov 61178731d33SDmitry Eremin-Solenikov PEDR = 0xffffffff; /* clear it */ 61278731d33SDmitry Eremin-Solenikov 61378731d33SDmitry Eremin-Solenikov sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE; 61478731d33SDmitry Eremin-Solenikov if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) { 61578731d33SDmitry Eremin-Solenikov RTSR &= RTSR_ALE; 61678731d33SDmitry Eremin-Solenikov RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND; 61778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n", RTAR); 61878731d33SDmitry Eremin-Solenikov sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE; 61978731d33SDmitry Eremin-Solenikov } else if (alarm_enable) { 62078731d33SDmitry Eremin-Solenikov RTSR &= RTSR_ALE; 62178731d33SDmitry Eremin-Solenikov RTAR = alarm_time; 62278731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n", RTAR); 62378731d33SDmitry Eremin-Solenikov } else { 62478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "No alarms set.\n"); 62578731d33SDmitry Eremin-Solenikov } 62678731d33SDmitry Eremin-Solenikov 62778731d33SDmitry Eremin-Solenikov pxa_pm_enter(state); 62878731d33SDmitry Eremin-Solenikov 62978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->postsuspend(); 63078731d33SDmitry Eremin-Solenikov 63178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n", PEDR); 63278731d33SDmitry Eremin-Solenikov } 63378731d33SDmitry Eremin-Solenikov 63478731d33SDmitry Eremin-Solenikov static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 63578731d33SDmitry Eremin-Solenikov { 636b64b0b76SPavel Machek if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable)) { 63778731d33SDmitry Eremin-Solenikov if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) { 63878731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n"); 63978731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 64078731d33SDmitry Eremin-Solenikov return 1; 64178731d33SDmitry Eremin-Solenikov } 64278731d33SDmitry Eremin-Solenikov if (sharpsl_off_charge_battery()) { 64378731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n"); 64478731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 64578731d33SDmitry Eremin-Solenikov return 1; 64678731d33SDmitry Eremin-Solenikov } 64778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n"); 64878731d33SDmitry Eremin-Solenikov } 64978731d33SDmitry Eremin-Solenikov 65099f329a2SPavel Machek if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) || 65199f329a2SPavel Machek (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL))) { 65278731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n"); 65378731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_enable, state); 65478731d33SDmitry Eremin-Solenikov return 1; 65578731d33SDmitry Eremin-Solenikov } 65678731d33SDmitry Eremin-Solenikov 65778731d33SDmitry Eremin-Solenikov return 0; 65878731d33SDmitry Eremin-Solenikov } 65978731d33SDmitry Eremin-Solenikov 66078731d33SDmitry Eremin-Solenikov static int corgi_pxa_pm_enter(suspend_state_t state) 66178731d33SDmitry Eremin-Solenikov { 66278731d33SDmitry Eremin-Solenikov unsigned long alarm_time = RTAR; 66378731d33SDmitry Eremin-Solenikov unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0); 66478731d33SDmitry Eremin-Solenikov 66578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n"); 66678731d33SDmitry Eremin-Solenikov 66778731d33SDmitry Eremin-Solenikov corgi_goto_sleep(alarm_time, alarm_status, state); 66878731d33SDmitry Eremin-Solenikov 66978731d33SDmitry Eremin-Solenikov while (corgi_enter_suspend(alarm_time, alarm_status, state)) 67078731d33SDmitry Eremin-Solenikov {} 67178731d33SDmitry Eremin-Solenikov 67278731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->earlyresume) 67378731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->earlyresume(); 67478731d33SDmitry Eremin-Solenikov 67578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); 67678731d33SDmitry Eremin-Solenikov 67778731d33SDmitry Eremin-Solenikov return 0; 67878731d33SDmitry Eremin-Solenikov } 67978731d33SDmitry Eremin-Solenikov 68078731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_error(void) 68178731d33SDmitry Eremin-Solenikov { 68278731d33SDmitry Eremin-Solenikov dev_err(sharpsl_pm.dev, "Offline Charger: Error occurred.\n"); 68378731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 68478731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ERROR); 68578731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ERROR; 68678731d33SDmitry Eremin-Solenikov return 1; 68778731d33SDmitry Eremin-Solenikov } 68878731d33SDmitry Eremin-Solenikov 68978731d33SDmitry Eremin-Solenikov /* 69078731d33SDmitry Eremin-Solenikov * Charging Control while suspended 69178731d33SDmitry Eremin-Solenikov * Return 1 - go straight to sleep 69278731d33SDmitry Eremin-Solenikov * Return 0 - sleep or wakeup depending on other factors 69378731d33SDmitry Eremin-Solenikov */ 69478731d33SDmitry Eremin-Solenikov static int sharpsl_off_charge_battery(void) 69578731d33SDmitry Eremin-Solenikov { 69678731d33SDmitry Eremin-Solenikov int time; 69778731d33SDmitry Eremin-Solenikov 69878731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode); 69978731d33SDmitry Eremin-Solenikov 70078731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_OFF) { 70178731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n"); 70278731d33SDmitry Eremin-Solenikov 70378731d33SDmitry Eremin-Solenikov /* AC Check */ 70478731d33SDmitry Eremin-Solenikov if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery_temp() < 0)) 70578731d33SDmitry Eremin-Solenikov return sharpsl_off_charge_error(); 70678731d33SDmitry Eremin-Solenikov 70778731d33SDmitry Eremin-Solenikov /* Start Charging */ 70878731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_ON); 70978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 71078731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 71178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 71278731d33SDmitry Eremin-Solenikov 71378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 71478731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 71578731d33SDmitry Eremin-Solenikov 71678731d33SDmitry Eremin-Solenikov return 1; 71778731d33SDmitry Eremin-Solenikov } else if (sharpsl_pm.charge_mode != CHRG_ON) { 71878731d33SDmitry Eremin-Solenikov return 1; 71978731d33SDmitry Eremin-Solenikov } 72078731d33SDmitry Eremin-Solenikov 72178731d33SDmitry Eremin-Solenikov if (sharpsl_pm.full_count == 0) { 72278731d33SDmitry Eremin-Solenikov int time; 72378731d33SDmitry Eremin-Solenikov 72478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n"); 72578731d33SDmitry Eremin-Solenikov 72678731d33SDmitry Eremin-Solenikov if ((sharpsl_check_battery_temp() < 0) || (sharpsl_check_battery_voltage() < 0)) 72778731d33SDmitry Eremin-Solenikov return sharpsl_off_charge_error(); 72878731d33SDmitry Eremin-Solenikov 72978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 73078731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 73178731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 73278731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_ON; 73378731d33SDmitry Eremin-Solenikov 73478731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 73578731d33SDmitry Eremin-Solenikov 73678731d33SDmitry Eremin-Solenikov time = RCNR; 73778731d33SDmitry Eremin-Solenikov while (1) { 73878731d33SDmitry Eremin-Solenikov /* Check if any wakeup event had occurred */ 739ca26475bSRobert Jarzmik if (sharpsl_pm.machinfo->charger_wakeup()) 74078731d33SDmitry Eremin-Solenikov return 0; 74178731d33SDmitry Eremin-Solenikov /* Check for timeout */ 74278731d33SDmitry Eremin-Solenikov if ((RCNR - time) > SHARPSL_WAIT_CO_TIME) 74378731d33SDmitry Eremin-Solenikov return 1; 74478731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 74578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occurred. Retrying to check\n"); 74678731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 74778731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 74878731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_WAIT_TIME); 74978731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(1); 75078731d33SDmitry Eremin-Solenikov return 1; 75178731d33SDmitry Eremin-Solenikov } 75278731d33SDmitry Eremin-Solenikov } 75378731d33SDmitry Eremin-Solenikov } 75478731d33SDmitry Eremin-Solenikov 75578731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n"); 75678731d33SDmitry Eremin-Solenikov 75778731d33SDmitry Eremin-Solenikov mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 75878731d33SDmitry Eremin-Solenikov 75978731d33SDmitry Eremin-Solenikov time = RCNR; 76078731d33SDmitry Eremin-Solenikov while (1) { 76178731d33SDmitry Eremin-Solenikov /* Check if any wakeup event had occurred */ 762b64b0b76SPavel Machek if (sharpsl_pm.machinfo->charger_wakeup()) 76378731d33SDmitry Eremin-Solenikov return 0; 76478731d33SDmitry Eremin-Solenikov /* Check for timeout */ 76578731d33SDmitry Eremin-Solenikov if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) { 76678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) { 76778731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n"); 76878731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count = 0; 76978731d33SDmitry Eremin-Solenikov } 77078731d33SDmitry Eremin-Solenikov sharpsl_pm.full_count++; 77178731d33SDmitry Eremin-Solenikov return 1; 77278731d33SDmitry Eremin-Solenikov } 77378731d33SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 77478731d33SDmitry Eremin-Solenikov dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n"); 77578731d33SDmitry Eremin-Solenikov sharpsl_pm_led(SHARPSL_LED_OFF); 77678731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->charge(0); 77778731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_DONE; 77878731d33SDmitry Eremin-Solenikov return 1; 77978731d33SDmitry Eremin-Solenikov } 78078731d33SDmitry Eremin-Solenikov } 78178731d33SDmitry Eremin-Solenikov } 78278731d33SDmitry Eremin-Solenikov #else 78378731d33SDmitry Eremin-Solenikov #define sharpsl_pm_suspend NULL 78478731d33SDmitry Eremin-Solenikov #define sharpsl_pm_resume NULL 78578731d33SDmitry Eremin-Solenikov #endif 78678731d33SDmitry Eremin-Solenikov 78778731d33SDmitry Eremin-Solenikov static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf) 78878731d33SDmitry Eremin-Solenikov { 78978731d33SDmitry Eremin-Solenikov return sprintf(buf, "%d\n", sharpsl_pm.battstat.mainbat_percent); 79078731d33SDmitry Eremin-Solenikov } 79178731d33SDmitry Eremin-Solenikov 79278731d33SDmitry Eremin-Solenikov static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) 79378731d33SDmitry Eremin-Solenikov { 79478731d33SDmitry Eremin-Solenikov return sprintf(buf, "%d\n", sharpsl_pm.battstat.mainbat_voltage); 79578731d33SDmitry Eremin-Solenikov } 79678731d33SDmitry Eremin-Solenikov 797c828a892SJoe Perches static DEVICE_ATTR_RO(battery_percentage); 798c828a892SJoe Perches static DEVICE_ATTR_RO(battery_voltage); 79978731d33SDmitry Eremin-Solenikov 80078731d33SDmitry Eremin-Solenikov extern void (*apm_get_power_status)(struct apm_power_info *); 80178731d33SDmitry Eremin-Solenikov 80278731d33SDmitry Eremin-Solenikov static void sharpsl_apm_get_power_status(struct apm_power_info *info) 80378731d33SDmitry Eremin-Solenikov { 80478731d33SDmitry Eremin-Solenikov info->ac_line_status = sharpsl_pm.battstat.ac_status; 80578731d33SDmitry Eremin-Solenikov 80678731d33SDmitry Eremin-Solenikov if (sharpsl_pm.charge_mode == CHRG_ON) 80778731d33SDmitry Eremin-Solenikov info->battery_status = APM_BATTERY_STATUS_CHARGING; 80878731d33SDmitry Eremin-Solenikov else 80978731d33SDmitry Eremin-Solenikov info->battery_status = sharpsl_pm.battstat.mainbat_status; 81078731d33SDmitry Eremin-Solenikov 81178731d33SDmitry Eremin-Solenikov info->battery_flag = (1 << info->battery_status); 81278731d33SDmitry Eremin-Solenikov info->battery_life = sharpsl_pm.battstat.mainbat_percent; 81378731d33SDmitry Eremin-Solenikov } 81478731d33SDmitry Eremin-Solenikov 81578731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 8162f55ac07SLionel Debroux static const struct platform_suspend_ops sharpsl_pm_ops = { 81751cdd928SPavel Machek .prepare = pxa_pm_prepare, 81851cdd928SPavel Machek .finish = pxa_pm_finish, 81978731d33SDmitry Eremin-Solenikov .enter = corgi_pxa_pm_enter, 82078731d33SDmitry Eremin-Solenikov .valid = suspend_valid_only_mem, 82178731d33SDmitry Eremin-Solenikov }; 82278731d33SDmitry Eremin-Solenikov #endif 82378731d33SDmitry Eremin-Solenikov 824351a102dSGreg Kroah-Hartman static int sharpsl_pm_probe(struct platform_device *pdev) 82578731d33SDmitry Eremin-Solenikov { 82674595e17SRob Herring int ret, irq; 82778731d33SDmitry Eremin-Solenikov 82878731d33SDmitry Eremin-Solenikov if (!pdev->dev.platform_data) 82978731d33SDmitry Eremin-Solenikov return -EINVAL; 83078731d33SDmitry Eremin-Solenikov 83178731d33SDmitry Eremin-Solenikov sharpsl_pm.dev = &pdev->dev; 83278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo = pdev->dev.platform_data; 83378731d33SDmitry Eremin-Solenikov sharpsl_pm.charge_mode = CHRG_OFF; 83478731d33SDmitry Eremin-Solenikov sharpsl_pm.flags = 0; 83578731d33SDmitry Eremin-Solenikov 83696d13082SKees Cook timer_setup(&sharpsl_pm.ac_timer, sharpsl_ac_timer, 0); 83778731d33SDmitry Eremin-Solenikov 83896d13082SKees Cook timer_setup(&sharpsl_pm.chrg_full_timer, sharpsl_chrg_full_timer, 0); 83978731d33SDmitry Eremin-Solenikov 84078731d33SDmitry Eremin-Solenikov led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger); 84178731d33SDmitry Eremin-Solenikov 84278731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->init(); 84378731d33SDmitry Eremin-Solenikov 844d5af2778SDmitry Eremin-Solenikov gpio_request(sharpsl_pm.machinfo->gpio_acin, "AC IN"); 845d5af2778SDmitry Eremin-Solenikov gpio_direction_input(sharpsl_pm.machinfo->gpio_acin); 846d5af2778SDmitry Eremin-Solenikov gpio_request(sharpsl_pm.machinfo->gpio_batfull, "Battery Full"); 847d5af2778SDmitry Eremin-Solenikov gpio_direction_input(sharpsl_pm.machinfo->gpio_batfull); 848d5af2778SDmitry Eremin-Solenikov gpio_request(sharpsl_pm.machinfo->gpio_batlock, "Battery Lock"); 849d5af2778SDmitry Eremin-Solenikov gpio_direction_input(sharpsl_pm.machinfo->gpio_batlock); 850d48898a3SDmitry Eremin-Solenikov 851d48898a3SDmitry Eremin-Solenikov /* Register interrupt handlers */ 85274595e17SRob Herring irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_acin); 853ed7936f9SMichael Opdenacker if (request_irq(irq, sharpsl_ac_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "AC Input Detect", sharpsl_ac_isr)) { 85474595e17SRob Herring dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq); 855d48898a3SDmitry Eremin-Solenikov } 856d48898a3SDmitry Eremin-Solenikov 85774595e17SRob Herring irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_batlock); 858ed7936f9SMichael Opdenacker if (request_irq(irq, sharpsl_fatal_isr, IRQF_TRIGGER_FALLING, "Battery Cover", sharpsl_fatal_isr)) { 85974595e17SRob Herring dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq); 860d48898a3SDmitry Eremin-Solenikov } 861d48898a3SDmitry Eremin-Solenikov 862d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->gpio_fatal) { 86374595e17SRob Herring irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_fatal); 864ed7936f9SMichael Opdenacker if (request_irq(irq, sharpsl_fatal_isr, IRQF_TRIGGER_FALLING, "Fatal Battery", sharpsl_fatal_isr)) { 86574595e17SRob Herring dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq); 866d48898a3SDmitry Eremin-Solenikov } 867d48898a3SDmitry Eremin-Solenikov } 868d48898a3SDmitry Eremin-Solenikov 869b64b0b76SPavel Machek if (sharpsl_pm.machinfo->batfull_irq) { 870d48898a3SDmitry Eremin-Solenikov /* Register interrupt handler. */ 87174595e17SRob Herring irq = gpio_to_irq(sharpsl_pm.machinfo->gpio_batfull); 872ed7936f9SMichael Opdenacker if (request_irq(irq, sharpsl_chrg_full_isr, IRQF_TRIGGER_RISING, "CO", sharpsl_chrg_full_isr)) { 87374595e17SRob Herring dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", irq); 874d48898a3SDmitry Eremin-Solenikov } 875d48898a3SDmitry Eremin-Solenikov } 876d48898a3SDmitry Eremin-Solenikov 87778731d33SDmitry Eremin-Solenikov ret = device_create_file(&pdev->dev, &dev_attr_battery_percentage); 87878731d33SDmitry Eremin-Solenikov ret |= device_create_file(&pdev->dev, &dev_attr_battery_voltage); 87978731d33SDmitry Eremin-Solenikov if (ret != 0) 88078731d33SDmitry Eremin-Solenikov dev_warn(&pdev->dev, "Failed to register attributes (%d)\n", ret); 88178731d33SDmitry Eremin-Solenikov 88278731d33SDmitry Eremin-Solenikov apm_get_power_status = sharpsl_apm_get_power_status; 88378731d33SDmitry Eremin-Solenikov 88478731d33SDmitry Eremin-Solenikov #ifdef CONFIG_PM 88578731d33SDmitry Eremin-Solenikov suspend_set_ops(&sharpsl_pm_ops); 88678731d33SDmitry Eremin-Solenikov #endif 88778731d33SDmitry Eremin-Solenikov 88878731d33SDmitry Eremin-Solenikov mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 88978731d33SDmitry Eremin-Solenikov 89078731d33SDmitry Eremin-Solenikov return 0; 89178731d33SDmitry Eremin-Solenikov } 89278731d33SDmitry Eremin-Solenikov 893*74a5b94bSUwe Kleine-König static void sharpsl_pm_remove(struct platform_device *pdev) 89478731d33SDmitry Eremin-Solenikov { 89578731d33SDmitry Eremin-Solenikov suspend_set_ops(NULL); 89678731d33SDmitry Eremin-Solenikov 89778731d33SDmitry Eremin-Solenikov device_remove_file(&pdev->dev, &dev_attr_battery_percentage); 89878731d33SDmitry Eremin-Solenikov device_remove_file(&pdev->dev, &dev_attr_battery_voltage); 89978731d33SDmitry Eremin-Solenikov 90078731d33SDmitry Eremin-Solenikov led_trigger_unregister_simple(sharpsl_charge_led_trigger); 90178731d33SDmitry Eremin-Solenikov 90274595e17SRob Herring free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr); 90374595e17SRob Herring free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr); 904d48898a3SDmitry Eremin-Solenikov 905d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->gpio_fatal) 90674595e17SRob Herring free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr); 907d48898a3SDmitry Eremin-Solenikov 908d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->batfull_irq) 90974595e17SRob Herring free_irq(gpio_to_irq(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr); 910d48898a3SDmitry Eremin-Solenikov 911d5af2778SDmitry Eremin-Solenikov gpio_free(sharpsl_pm.machinfo->gpio_batlock); 912d5af2778SDmitry Eremin-Solenikov gpio_free(sharpsl_pm.machinfo->gpio_batfull); 913d5af2778SDmitry Eremin-Solenikov gpio_free(sharpsl_pm.machinfo->gpio_acin); 914d5af2778SDmitry Eremin-Solenikov 915d48898a3SDmitry Eremin-Solenikov if (sharpsl_pm.machinfo->exit) 91678731d33SDmitry Eremin-Solenikov sharpsl_pm.machinfo->exit(); 91778731d33SDmitry Eremin-Solenikov 91878731d33SDmitry Eremin-Solenikov del_timer_sync(&sharpsl_pm.chrg_full_timer); 91978731d33SDmitry Eremin-Solenikov del_timer_sync(&sharpsl_pm.ac_timer); 92078731d33SDmitry Eremin-Solenikov } 92178731d33SDmitry Eremin-Solenikov 92278731d33SDmitry Eremin-Solenikov static struct platform_driver sharpsl_pm_driver = { 92378731d33SDmitry Eremin-Solenikov .probe = sharpsl_pm_probe, 924*74a5b94bSUwe Kleine-König .remove_new = sharpsl_pm_remove, 92578731d33SDmitry Eremin-Solenikov .suspend = sharpsl_pm_suspend, 92678731d33SDmitry Eremin-Solenikov .resume = sharpsl_pm_resume, 92778731d33SDmitry Eremin-Solenikov .driver = { 92878731d33SDmitry Eremin-Solenikov .name = "sharpsl-pm", 92978731d33SDmitry Eremin-Solenikov }, 93078731d33SDmitry Eremin-Solenikov }; 93178731d33SDmitry Eremin-Solenikov 932351a102dSGreg Kroah-Hartman static int sharpsl_pm_init(void) 93378731d33SDmitry Eremin-Solenikov { 93478731d33SDmitry Eremin-Solenikov return platform_driver_register(&sharpsl_pm_driver); 93578731d33SDmitry Eremin-Solenikov } 93678731d33SDmitry Eremin-Solenikov 93778731d33SDmitry Eremin-Solenikov static void sharpsl_pm_exit(void) 93878731d33SDmitry Eremin-Solenikov { 93978731d33SDmitry Eremin-Solenikov platform_driver_unregister(&sharpsl_pm_driver); 94078731d33SDmitry Eremin-Solenikov } 94178731d33SDmitry Eremin-Solenikov 94278731d33SDmitry Eremin-Solenikov late_initcall(sharpsl_pm_init); 94378731d33SDmitry Eremin-Solenikov module_exit(sharpsl_pm_exit); 944