1/* 2 * Battery and Power Management code for the Sharp SL-C7xx and SL-Cxx00 3 * series of PDAs 4 * 5 * Copyright (c) 2004-2005 Richard Purdie 6 * 7 * Based on code written by Sharp for 2.4 kernels 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 */ 14 15#undef DEBUG 16 17#include <linux/module.h> 18#include <linux/timer.h> 19#include <linux/init.h> 20#include <linux/kernel.h> 21#include <linux/apm_bios.h> 22#include <linux/delay.h> 23#include <linux/interrupt.h> 24#include <linux/platform_device.h> 25#include <linux/leds.h> 26#include <linux/apm-emulation.h> 27 28#include <asm/hardware.h> 29#include <asm/mach-types.h> 30#include <asm/irq.h> 31#include <asm/arch/pm.h> 32#include <asm/arch/pxa-regs.h> 33#include <asm/arch/sharpsl.h> 34#include <asm/hardware/sharpsl_pm.h> 35 36/* 37 * Constants 38 */ 39#define SHARPSL_CHARGE_ON_TIME_INTERVAL (msecs_to_jiffies(1*60*1000)) /* 1 min */ 40#define SHARPSL_CHARGE_FINISH_TIME (msecs_to_jiffies(10*60*1000)) /* 10 min */ 41#define SHARPSL_BATCHK_TIME (msecs_to_jiffies(15*1000)) /* 15 sec */ 42#define SHARPSL_BATCHK_TIME_SUSPEND (60*10) /* 10 min */ 43 44#define SHARPSL_WAIT_CO_TIME 15 /* 15 sec */ 45#define SHARPSL_WAIT_DISCHARGE_ON 100 /* 100 msec */ 46#define SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP 10 /* 10 msec */ 47#define SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT 10 /* 10 msec */ 48#define SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN 10 /* 10 msec */ 49#define SHARPSL_CHARGE_WAIT_TIME 15 /* 15 msec */ 50#define SHARPSL_CHARGE_CO_CHECK_TIME 5 /* 5 msec */ 51#define SHARPSL_CHARGE_RETRY_CNT 1 /* eqv. 10 min */ 52 53/* 54 * Prototypes 55 */ 56static int sharpsl_off_charge_battery(void); 57static int sharpsl_check_battery_temp(void); 58static int sharpsl_check_battery_voltage(void); 59static int sharpsl_ac_check(void); 60static int sharpsl_fatal_check(void); 61static int sharpsl_average_value(int ad); 62static void sharpsl_average_clear(void); 63static void sharpsl_charge_toggle(struct work_struct *private_); 64static void sharpsl_battery_thread(struct work_struct *private_); 65 66 67/* 68 * Variables 69 */ 70struct sharpsl_pm_status sharpsl_pm; 71DECLARE_DELAYED_WORK(toggle_charger, sharpsl_charge_toggle); 72DECLARE_DELAYED_WORK(sharpsl_bat, sharpsl_battery_thread); 73DEFINE_LED_TRIGGER(sharpsl_charge_led_trigger); 74 75 76static int get_percentage(int voltage) 77{ 78 int i = sharpsl_pm.machinfo->bat_levels - 1; 79 int bl_status = sharpsl_pm.machinfo->backlight_get_status ? sharpsl_pm.machinfo->backlight_get_status() : 0; 80 struct battery_thresh *thresh; 81 82 if (sharpsl_pm.charge_mode == CHRG_ON) 83 thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_acin_bl : sharpsl_pm.machinfo->bat_levels_acin; 84 else 85 thresh = bl_status ? sharpsl_pm.machinfo->bat_levels_noac_bl : sharpsl_pm.machinfo->bat_levels_noac; 86 87 while (i > 0 && (voltage > thresh[i].voltage)) 88 i--; 89 90 return thresh[i].percentage; 91} 92 93static int get_apm_status(int voltage) 94{ 95 int low_thresh, high_thresh; 96 97 if (sharpsl_pm.charge_mode == CHRG_ON) { 98 high_thresh = sharpsl_pm.machinfo->status_high_acin; 99 low_thresh = sharpsl_pm.machinfo->status_low_acin; 100 } else { 101 high_thresh = sharpsl_pm.machinfo->status_high_noac; 102 low_thresh = sharpsl_pm.machinfo->status_low_noac; 103 } 104 105 if (voltage >= high_thresh) 106 return APM_BATTERY_STATUS_HIGH; 107 if (voltage >= low_thresh) 108 return APM_BATTERY_STATUS_LOW; 109 return APM_BATTERY_STATUS_CRITICAL; 110} 111 112void sharpsl_battery_kick(void) 113{ 114 schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125)); 115} 116EXPORT_SYMBOL(sharpsl_battery_kick); 117 118 119static void sharpsl_battery_thread(struct work_struct *private_) 120{ 121 int voltage, percent, apm_status, i = 0; 122 123 if (!sharpsl_pm.machinfo) 124 return; 125 126 sharpsl_pm.battstat.ac_status = (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN) ? APM_AC_ONLINE : APM_AC_OFFLINE); 127 128 /* Corgi cannot confirm when battery fully charged so periodically kick! */ 129 if (!sharpsl_pm.machinfo->batfull_irq && (sharpsl_pm.charge_mode == CHRG_ON) 130 && time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_ON_TIME_INTERVAL)) 131 schedule_delayed_work(&toggle_charger, 0); 132 133 while(1) { 134 voltage = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 135 136 if (voltage > 0) break; 137 if (i++ > 5) { 138 voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage; 139 dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n"); 140 break; 141 } 142 } 143 144 voltage = sharpsl_average_value(voltage); 145 apm_status = get_apm_status(voltage); 146 percent = get_percentage(voltage); 147 148 /* At low battery voltages, the voltage has a tendency to start 149 creeping back up so we try to avoid this here */ 150 if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) || (apm_status == APM_BATTERY_STATUS_HIGH) || percent <= sharpsl_pm.battstat.mainbat_percent) { 151 sharpsl_pm.battstat.mainbat_voltage = voltage; 152 sharpsl_pm.battstat.mainbat_status = apm_status; 153 sharpsl_pm.battstat.mainbat_percent = percent; 154 } 155 156 dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %ld\n", voltage, 157 sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies); 158 159 /* If battery is low. limit backlight intensity to save power. */ 160 if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 161 && ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW) || 162 (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) { 163 if (!(sharpsl_pm.flags & SHARPSL_BL_LIMIT)) { 164 sharpsl_pm.machinfo->backlight_limit(1); 165 sharpsl_pm.flags |= SHARPSL_BL_LIMIT; 166 } 167 } else if (sharpsl_pm.flags & SHARPSL_BL_LIMIT) { 168 sharpsl_pm.machinfo->backlight_limit(0); 169 sharpsl_pm.flags &= ~SHARPSL_BL_LIMIT; 170 } 171 172 /* Suspend if critical battery level */ 173 if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE) 174 && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL) 175 && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) { 176 sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 177 dev_err(sharpsl_pm.dev, "Fatal Off\n"); 178 apm_queue_event(APM_CRITICAL_SUSPEND); 179 } 180 181 schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME); 182} 183 184void sharpsl_pm_led(int val) 185{ 186 if (val == SHARPSL_LED_ERROR) { 187 dev_err(sharpsl_pm.dev, "Charging Error!\n"); 188 } else if (val == SHARPSL_LED_ON) { 189 dev_dbg(sharpsl_pm.dev, "Charge LED On\n"); 190 led_trigger_event(sharpsl_charge_led_trigger, LED_FULL); 191 } else { 192 dev_dbg(sharpsl_pm.dev, "Charge LED Off\n"); 193 led_trigger_event(sharpsl_charge_led_trigger, LED_OFF); 194 } 195} 196 197static void sharpsl_charge_on(void) 198{ 199 dev_dbg(sharpsl_pm.dev, "Turning Charger On\n"); 200 201 sharpsl_pm.full_count = 0; 202 sharpsl_pm.charge_mode = CHRG_ON; 203 schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250)); 204 schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500)); 205} 206 207static void sharpsl_charge_off(void) 208{ 209 dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n"); 210 211 sharpsl_pm.machinfo->charge(0); 212 sharpsl_pm_led(SHARPSL_LED_OFF); 213 sharpsl_pm.charge_mode = CHRG_OFF; 214 215 schedule_delayed_work(&sharpsl_bat, 0); 216} 217 218static void sharpsl_charge_error(void) 219{ 220 sharpsl_pm_led(SHARPSL_LED_ERROR); 221 sharpsl_pm.machinfo->charge(0); 222 sharpsl_pm.charge_mode = CHRG_ERROR; 223} 224 225static void sharpsl_charge_toggle(struct work_struct *private_) 226{ 227 dev_dbg(sharpsl_pm.dev, "Toogling Charger at time: %lx\n", jiffies); 228 229 if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 230 sharpsl_charge_off(); 231 return; 232 } else if ((sharpsl_check_battery_temp() < 0) || (sharpsl_ac_check() < 0)) { 233 sharpsl_charge_error(); 234 return; 235 } 236 237 sharpsl_pm_led(SHARPSL_LED_ON); 238 sharpsl_pm.machinfo->charge(0); 239 mdelay(SHARPSL_CHARGE_WAIT_TIME); 240 sharpsl_pm.machinfo->charge(1); 241 242 sharpsl_pm.charge_start_time = jiffies; 243} 244 245static void sharpsl_ac_timer(unsigned long data) 246{ 247 int acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 248 249 dev_dbg(sharpsl_pm.dev, "AC Status: %d\n",acin); 250 251 sharpsl_average_clear(); 252 if (acin && (sharpsl_pm.charge_mode != CHRG_ON)) 253 sharpsl_charge_on(); 254 else if (sharpsl_pm.charge_mode == CHRG_ON) 255 sharpsl_charge_off(); 256 257 schedule_delayed_work(&sharpsl_bat, 0); 258} 259 260 261irqreturn_t sharpsl_ac_isr(int irq, void *dev_id) 262{ 263 /* Delay the event slightly to debounce */ 264 /* Must be a smaller delay than the chrg_full_isr below */ 265 mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 266 267 return IRQ_HANDLED; 268} 269 270static void sharpsl_chrg_full_timer(unsigned long data) 271{ 272 dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies); 273 274 sharpsl_pm.full_count++; 275 276 if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN)) { 277 dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n"); 278 if (sharpsl_pm.charge_mode == CHRG_ON) 279 sharpsl_charge_off(); 280 } else if (sharpsl_pm.full_count < 2) { 281 dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n"); 282 schedule_delayed_work(&toggle_charger, 0); 283 } else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) { 284 dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n"); 285 schedule_delayed_work(&toggle_charger, 0); 286 } else { 287 sharpsl_charge_off(); 288 sharpsl_pm.charge_mode = CHRG_DONE; 289 dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n"); 290 } 291} 292 293/* Charging Finished Interrupt (Not present on Corgi) */ 294/* Can trigger at the same time as an AC status change so 295 delay until after that has been processed */ 296irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id) 297{ 298 if (sharpsl_pm.flags & SHARPSL_SUSPENDED) 299 return IRQ_HANDLED; 300 301 /* delay until after any ac interrupt */ 302 mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500)); 303 304 return IRQ_HANDLED; 305} 306 307irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id) 308{ 309 int is_fatal = 0; 310 311 if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) { 312 dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n"); 313 is_fatal = 1; 314 } 315 316 if (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL)) { 317 dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n"); 318 is_fatal = 1; 319 } 320 321 if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) { 322 sharpsl_pm.flags |= SHARPSL_APM_QUEUED; 323 apm_queue_event(APM_CRITICAL_SUSPEND); 324 } 325 326 return IRQ_HANDLED; 327} 328 329/* 330 * Maintain an average of the last 10 readings 331 */ 332#define SHARPSL_CNV_VALUE_NUM 10 333static int sharpsl_ad_index; 334 335static void sharpsl_average_clear(void) 336{ 337 sharpsl_ad_index = 0; 338} 339 340static int sharpsl_average_value(int ad) 341{ 342 int i, ad_val = 0; 343 static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1]; 344 345 if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) { 346 sharpsl_ad_index = 0; 347 return ad; 348 } 349 350 sharpsl_ad[sharpsl_ad_index] = ad; 351 sharpsl_ad_index++; 352 if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) { 353 for (i=0; i < (SHARPSL_CNV_VALUE_NUM-1); i++) 354 sharpsl_ad[i] = sharpsl_ad[i+1]; 355 sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1; 356 } 357 for (i=0; i < sharpsl_ad_index; i++) 358 ad_val += sharpsl_ad[i]; 359 360 return (ad_val / sharpsl_ad_index); 361} 362 363/* 364 * Take an array of 5 integers, remove the maximum and minimum values 365 * and return the average. 366 */ 367static int get_select_val(int *val) 368{ 369 int i, j, k, temp, sum = 0; 370 371 /* Find MAX val */ 372 temp = val[0]; 373 j=0; 374 for (i=1; i<5; i++) { 375 if (temp < val[i]) { 376 temp = val[i]; 377 j = i; 378 } 379 } 380 381 /* Find MIN val */ 382 temp = val[4]; 383 k=4; 384 for (i=3; i>=0; i--) { 385 if (temp > val[i]) { 386 temp = val[i]; 387 k = i; 388 } 389 } 390 391 for (i=0; i<5; i++) 392 if (i != j && i != k ) 393 sum += val[i]; 394 395 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]); 396 397 return (sum/3); 398} 399 400static int sharpsl_check_battery_temp(void) 401{ 402 int val, i, buff[5]; 403 404 /* Check battery temperature */ 405 for (i=0; i<5; i++) { 406 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 407 sharpsl_pm.machinfo->measure_temp(1); 408 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP); 409 buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_TEMP); 410 sharpsl_pm.machinfo->measure_temp(0); 411 } 412 413 val = get_select_val(buff); 414 415 dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val); 416 if (val > sharpsl_pm.machinfo->charge_on_temp) { 417 printk(KERN_WARNING "Not charging: temperature out of limits.\n"); 418 return -1; 419 } 420 421 return 0; 422} 423 424static int sharpsl_check_battery_voltage(void) 425{ 426 int val, i, buff[5]; 427 428 /* disable charge, enable discharge */ 429 sharpsl_pm.machinfo->charge(0); 430 sharpsl_pm.machinfo->discharge(1); 431 mdelay(SHARPSL_WAIT_DISCHARGE_ON); 432 433 if (sharpsl_pm.machinfo->discharge1) 434 sharpsl_pm.machinfo->discharge1(1); 435 436 /* Check battery voltage */ 437 for (i=0; i<5; i++) { 438 buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 439 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT); 440 } 441 442 if (sharpsl_pm.machinfo->discharge1) 443 sharpsl_pm.machinfo->discharge1(0); 444 445 sharpsl_pm.machinfo->discharge(0); 446 447 val = get_select_val(buff); 448 dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val); 449 450 if (val < sharpsl_pm.machinfo->charge_on_volt) 451 return -1; 452 453 return 0; 454} 455 456static int sharpsl_ac_check(void) 457{ 458 int temp, i, buff[5]; 459 460 for (i=0; i<5; i++) { 461 buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_ACIN_VOLT); 462 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_ACIN); 463 } 464 465 temp = get_select_val(buff); 466 dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n",temp); 467 468 if ((temp > sharpsl_pm.machinfo->charge_acin_high) || (temp < sharpsl_pm.machinfo->charge_acin_low)) { 469 dev_err(sharpsl_pm.dev, "Error: AC check failed.\n"); 470 return -1; 471 } 472 473 return 0; 474} 475 476#ifdef CONFIG_PM 477static int sharpsl_pm_suspend(struct platform_device *pdev, pm_message_t state) 478{ 479 sharpsl_pm.flags |= SHARPSL_SUSPENDED; 480 flush_scheduled_work(); 481 482 if (sharpsl_pm.charge_mode == CHRG_ON) 483 sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG; 484 else 485 sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 486 487 return 0; 488} 489 490static int sharpsl_pm_resume(struct platform_device *pdev) 491{ 492 /* Clear the reset source indicators as they break the bootloader upon reboot */ 493 RCSR = 0x0f; 494 sharpsl_average_clear(); 495 sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED; 496 sharpsl_pm.flags &= ~SHARPSL_SUSPENDED; 497 498 return 0; 499} 500 501static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 502{ 503 dev_dbg(sharpsl_pm.dev, "Time is: %08x\n",RCNR); 504 505 dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n",sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG); 506 /* not charging and AC-IN! */ 507 508 if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN))) { 509 dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n"); 510 sharpsl_pm.charge_mode = CHRG_OFF; 511 sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG; 512 sharpsl_off_charge_battery(); 513 } 514 515 sharpsl_pm.machinfo->presuspend(); 516 517 PEDR = 0xffffffff; /* clear it */ 518 519 sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE; 520 if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) { 521 RTSR &= RTSR_ALE; 522 RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND; 523 dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n",RTAR); 524 sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE; 525 } else if (alarm_enable) { 526 RTSR &= RTSR_ALE; 527 RTAR = alarm_time; 528 dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n",RTAR); 529 } else { 530 dev_dbg(sharpsl_pm.dev, "No alarms set.\n"); 531 } 532 533 pxa_pm_enter(state); 534 535 sharpsl_pm.machinfo->postsuspend(); 536 537 dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n",PEDR); 538} 539 540static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state) 541{ 542 if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable) ) 543 { 544 if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) { 545 dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n"); 546 corgi_goto_sleep(alarm_time, alarm_enable, state); 547 return 1; 548 } 549 if(sharpsl_off_charge_battery()) { 550 dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n"); 551 corgi_goto_sleep(alarm_time, alarm_enable, state); 552 return 1; 553 } 554 dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n"); 555 } 556 557 if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) || (sharpsl_fatal_check() < 0) ) 558 { 559 dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n"); 560 corgi_goto_sleep(alarm_time, alarm_enable, state); 561 return 1; 562 } 563 564 return 0; 565} 566 567static int corgi_pxa_pm_enter(suspend_state_t state) 568{ 569 unsigned long alarm_time = RTAR; 570 unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0); 571 572 dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n"); 573 574 corgi_goto_sleep(alarm_time, alarm_status, state); 575 576 while (corgi_enter_suspend(alarm_time,alarm_status,state)) 577 {} 578 579 if (sharpsl_pm.machinfo->earlyresume) 580 sharpsl_pm.machinfo->earlyresume(); 581 582 dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n"); 583 584 return 0; 585} 586#endif 587 588 589/* 590 * Check for fatal battery errors 591 * Fatal returns -1 592 */ 593static int sharpsl_fatal_check(void) 594{ 595 int buff[5], temp, i, acin; 596 597 dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check entered\n"); 598 599 /* Check AC-Adapter */ 600 acin = sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_ACIN); 601 602 if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) { 603 sharpsl_pm.machinfo->charge(0); 604 udelay(100); 605 sharpsl_pm.machinfo->discharge(1); /* enable discharge */ 606 mdelay(SHARPSL_WAIT_DISCHARGE_ON); 607 } 608 609 if (sharpsl_pm.machinfo->discharge1) 610 sharpsl_pm.machinfo->discharge1(1); 611 612 /* Check battery : check inserting battery ? */ 613 for (i=0; i<5; i++) { 614 buff[i] = sharpsl_pm.machinfo->read_devdata(SHARPSL_BATT_VOLT); 615 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT); 616 } 617 618 if (sharpsl_pm.machinfo->discharge1) 619 sharpsl_pm.machinfo->discharge1(0); 620 621 if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) { 622 udelay(100); 623 sharpsl_pm.machinfo->charge(1); 624 sharpsl_pm.machinfo->discharge(0); 625 } 626 627 temp = get_select_val(buff); 628 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)); 629 630 if ((acin && (temp < sharpsl_pm.machinfo->fatal_acin_volt)) || 631 (!acin && (temp < sharpsl_pm.machinfo->fatal_noacin_volt))) 632 return -1; 633 return 0; 634} 635 636static int sharpsl_off_charge_error(void) 637{ 638 dev_err(sharpsl_pm.dev, "Offline Charger: Error occurred.\n"); 639 sharpsl_pm.machinfo->charge(0); 640 sharpsl_pm_led(SHARPSL_LED_ERROR); 641 sharpsl_pm.charge_mode = CHRG_ERROR; 642 return 1; 643} 644 645/* 646 * Charging Control while suspended 647 * Return 1 - go straight to sleep 648 * Return 0 - sleep or wakeup depending on other factors 649 */ 650static int sharpsl_off_charge_battery(void) 651{ 652 int time; 653 654 dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode); 655 656 if (sharpsl_pm.charge_mode == CHRG_OFF) { 657 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n"); 658 659 /* AC Check */ 660 if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery_temp() < 0)) 661 return sharpsl_off_charge_error(); 662 663 /* Start Charging */ 664 sharpsl_pm_led(SHARPSL_LED_ON); 665 sharpsl_pm.machinfo->charge(0); 666 mdelay(SHARPSL_CHARGE_WAIT_TIME); 667 sharpsl_pm.machinfo->charge(1); 668 669 sharpsl_pm.charge_mode = CHRG_ON; 670 sharpsl_pm.full_count = 0; 671 672 return 1; 673 } else if (sharpsl_pm.charge_mode != CHRG_ON) { 674 return 1; 675 } 676 677 if (sharpsl_pm.full_count == 0) { 678 int time; 679 680 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n"); 681 682 if ((sharpsl_check_battery_temp() < 0) || (sharpsl_check_battery_voltage() < 0)) 683 return sharpsl_off_charge_error(); 684 685 sharpsl_pm.machinfo->charge(0); 686 mdelay(SHARPSL_CHARGE_WAIT_TIME); 687 sharpsl_pm.machinfo->charge(1); 688 sharpsl_pm.charge_mode = CHRG_ON; 689 690 mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 691 692 time = RCNR; 693 while(1) { 694 /* Check if any wakeup event had occurred */ 695 if (sharpsl_pm.machinfo->charger_wakeup() != 0) 696 return 0; 697 /* Check for timeout */ 698 if ((RCNR - time) > SHARPSL_WAIT_CO_TIME) 699 return 1; 700 if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 701 dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occurred. Retrying to check\n"); 702 sharpsl_pm.full_count++; 703 sharpsl_pm.machinfo->charge(0); 704 mdelay(SHARPSL_CHARGE_WAIT_TIME); 705 sharpsl_pm.machinfo->charge(1); 706 return 1; 707 } 708 } 709 } 710 711 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n"); 712 713 mdelay(SHARPSL_CHARGE_CO_CHECK_TIME); 714 715 time = RCNR; 716 while(1) { 717 /* Check if any wakeup event had occurred */ 718 if (sharpsl_pm.machinfo->charger_wakeup() != 0) 719 return 0; 720 /* Check for timeout */ 721 if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) { 722 if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) { 723 dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n"); 724 sharpsl_pm.full_count = 0; 725 } 726 sharpsl_pm.full_count++; 727 return 1; 728 } 729 if (sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_CHRGFULL)) { 730 dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n"); 731 sharpsl_pm_led(SHARPSL_LED_OFF); 732 sharpsl_pm.machinfo->charge(0); 733 sharpsl_pm.charge_mode = CHRG_DONE; 734 return 1; 735 } 736 } 737} 738 739 740static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf) 741{ 742 return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_percent); 743} 744 745static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) 746{ 747 return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_voltage); 748} 749 750static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL); 751static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL); 752 753extern void (*apm_get_power_status)(struct apm_power_info *); 754 755static void sharpsl_apm_get_power_status(struct apm_power_info *info) 756{ 757 info->ac_line_status = sharpsl_pm.battstat.ac_status; 758 759 if (sharpsl_pm.charge_mode == CHRG_ON) 760 info->battery_status = APM_BATTERY_STATUS_CHARGING; 761 else 762 info->battery_status = sharpsl_pm.battstat.mainbat_status; 763 764 info->battery_flag = (1 << info->battery_status); 765 info->battery_life = sharpsl_pm.battstat.mainbat_percent; 766} 767 768static struct pm_ops sharpsl_pm_ops = { 769 .prepare = pxa_pm_prepare, 770 .enter = corgi_pxa_pm_enter, 771 .finish = pxa_pm_finish, 772 .valid = pm_valid_only_mem, 773}; 774 775static int __init sharpsl_pm_probe(struct platform_device *pdev) 776{ 777 int ret; 778 779 if (!pdev->dev.platform_data) 780 return -EINVAL; 781 782 sharpsl_pm.dev = &pdev->dev; 783 sharpsl_pm.machinfo = pdev->dev.platform_data; 784 sharpsl_pm.charge_mode = CHRG_OFF; 785 sharpsl_pm.flags = 0; 786 787 init_timer(&sharpsl_pm.ac_timer); 788 sharpsl_pm.ac_timer.function = sharpsl_ac_timer; 789 790 init_timer(&sharpsl_pm.chrg_full_timer); 791 sharpsl_pm.chrg_full_timer.function = sharpsl_chrg_full_timer; 792 793 led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger); 794 795 sharpsl_pm.machinfo->init(); 796 797 ret = device_create_file(&pdev->dev, &dev_attr_battery_percentage); 798 ret |= device_create_file(&pdev->dev, &dev_attr_battery_voltage); 799 if (ret != 0) 800 dev_warn(&pdev->dev, "Failed to register attributes (%d)\n", ret); 801 802 apm_get_power_status = sharpsl_apm_get_power_status; 803 804 pm_set_ops(&sharpsl_pm_ops); 805 806 mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250)); 807 808 return 0; 809} 810 811static int sharpsl_pm_remove(struct platform_device *pdev) 812{ 813 pm_set_ops(NULL); 814 815 device_remove_file(&pdev->dev, &dev_attr_battery_percentage); 816 device_remove_file(&pdev->dev, &dev_attr_battery_voltage); 817 818 led_trigger_unregister_simple(sharpsl_charge_led_trigger); 819 820 sharpsl_pm.machinfo->exit(); 821 822 del_timer_sync(&sharpsl_pm.chrg_full_timer); 823 del_timer_sync(&sharpsl_pm.ac_timer); 824 825 return 0; 826} 827 828static struct platform_driver sharpsl_pm_driver = { 829 .probe = sharpsl_pm_probe, 830 .remove = sharpsl_pm_remove, 831 .suspend = sharpsl_pm_suspend, 832 .resume = sharpsl_pm_resume, 833 .driver = { 834 .name = "sharpsl-pm", 835 }, 836}; 837 838static int __devinit sharpsl_pm_init(void) 839{ 840 return platform_driver_register(&sharpsl_pm_driver); 841} 842 843static void sharpsl_pm_exit(void) 844{ 845 platform_driver_unregister(&sharpsl_pm_driver); 846} 847 848late_initcall(sharpsl_pm_init); 849module_exit(sharpsl_pm_exit); 850