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