aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@rpsys.net>2005-11-10 12:42:29 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2005-11-10 12:42:29 -0500
commit078abcf95cdb95c78d786dbc61ae3c22ee70fb61 (patch)
treefec0579ccb61371019248eee68c624734c8533d2
parentb1faebb672edecf48d4745fb05eeca3c704b6823 (diff)
[ARM] 3096/1: Add SharpSL Zaurus power and battery management core driver
Patch from Richard Purdie This patch adds a power and battery management core driver which with the addition of the right device files, supports the c7x0 and cxx00 series of Sharp Zaurus handhelds. The driver is complex for several reasons. Battery charging is manually monitored and controlled. When suspended, the device needs to periodically partially resume, check the charging status and then re-suspend. It does without bothering the higher linux layers as a full resume and re-suspend is unnecessary. The code is carefully written to avoid interrupts or calling code outside the module under these circumstances. It also vets the various wake up sources and monitors the device's power situation. Hooks to limit the backlight intensity and to notify the battery monitoring code of backlight events are connected/added as the backlight is one of the biggest users of power on the device. Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/mach-pxa/sharpsl.h87
-rw-r--r--arch/arm/mach-pxa/sharpsl_pm.c992
-rw-r--r--drivers/video/backlight/corgi_bl.c6
-rw-r--r--include/asm-arm/arch-pxa/sharpsl.h8
4 files changed, 1092 insertions, 1 deletions
diff --git a/arch/arm/mach-pxa/sharpsl.h b/arch/arm/mach-pxa/sharpsl.h
index 3977a77aacdd..4879c0f7da72 100644
--- a/arch/arm/mach-pxa/sharpsl.h
+++ b/arch/arm/mach-pxa/sharpsl.h
@@ -32,3 +32,90 @@ void corgi_put_hsync(void);
32void spitz_put_hsync(void); 32void spitz_put_hsync(void);
33void corgi_wait_hsync(void); 33void corgi_wait_hsync(void);
34void spitz_wait_hsync(void); 34void spitz_wait_hsync(void);
35
36/*
37 * SharpSL Battery/PM Driver
38 */
39
40struct sharpsl_charger_machinfo {
41 void (*init)(void);
42 int gpio_acin;
43 int gpio_batfull;
44 int gpio_batlock;
45 int gpio_fatal;
46 int (*status_acin)(void);
47 void (*discharge)(int);
48 void (*discharge1)(int);
49 void (*charge)(int);
50 void (*chargeled)(int);
51 void (*measure_temp)(int);
52 void (*presuspend)(void);
53 void (*postsuspend)(void);
54 unsigned long (*charger_wakeup)(void);
55 int (*should_wakeup)(unsigned int resume_on_alarm);
56 int bat_levels;
57 struct battery_thresh *bat_levels_noac;
58 struct battery_thresh *bat_levels_acin;
59 int status_high_acin;
60 int status_low_acin;
61 int status_high_noac;
62 int status_low_noac;
63};
64
65struct battery_thresh {
66 int voltage;
67 int percentage;
68};
69
70struct battery_stat {
71 int ac_status; /* APM AC Present/Not Present */
72 int mainbat_status; /* APM Main Battery Status */
73 int mainbat_percent; /* Main Battery Percentage Charge */
74 int mainbat_voltage; /* Main Battery Voltage */
75};
76
77struct sharpsl_pm_status {
78 struct device *dev;
79 struct timer_list ac_timer;
80 struct timer_list chrg_full_timer;
81
82 int charge_mode;
83#define CHRG_ERROR (-1)
84#define CHRG_OFF (0)
85#define CHRG_ON (1)
86#define CHRG_DONE (2)
87
88 unsigned int flags;
89#define SHARPSL_SUSPENDED (1 << 0) /* Device is Suspended */
90#define SHARPSL_ALARM_ACTIVE (1 << 1) /* Alarm is for charging event (not user) */
91#define SHARPSL_BL_LIMIT (1 << 2) /* Backlight Intensity Limited */
92#define SHARPSL_APM_QUEUED (1 << 3) /* APM Event Queued */
93#define SHARPSL_DO_OFFLINE_CHRG (1 << 4) /* Trigger the offline charger */
94
95 int full_count;
96 unsigned long charge_start_time;
97 struct sharpsl_charger_machinfo *machinfo;
98 struct battery_stat battstat;
99};
100
101extern struct sharpsl_pm_status sharpsl_pm;
102extern struct battery_thresh spitz_battery_levels_acin[];
103extern struct battery_thresh spitz_battery_levels_noac[];
104
105#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x))
106
107#define SHARPSL_LED_ERROR 2
108#define SHARPSL_LED_ON 1
109#define SHARPSL_LED_OFF 0
110
111#define CHARGE_ON() sharpsl_pm.machinfo->charge(1)
112#define CHARGE_OFF() sharpsl_pm.machinfo->charge(0)
113#define CHARGE_LED_ON() sharpsl_pm.machinfo->chargeled(SHARPSL_LED_ON)
114#define CHARGE_LED_OFF() sharpsl_pm.machinfo->chargeled(SHARPSL_LED_OFF)
115#define CHARGE_LED_ERR() sharpsl_pm.machinfo->chargeled(SHARPSL_LED_ERROR)
116#define DISCHARGE_ON() sharpsl_pm.machinfo->discharge(1)
117#define DISCHARGE_OFF() sharpsl_pm.machinfo->discharge(0)
118#define STATUS_AC_IN sharpsl_pm.machinfo->status_acin()
119#define STATUS_BATT_LOCKED READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batlock)
120#define STATUS_CHRG_FULL READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_batfull)
121#define STATUS_FATAL READ_GPIO_BIT(sharpsl_pm.machinfo->gpio_fatal)
diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c
new file mode 100644
index 000000000000..6c9e871c53d8
--- /dev/null
+++ b/arch/arm/mach-pxa/sharpsl_pm.c
@@ -0,0 +1,992 @@
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/device.h>
25
26#include <asm/hardware.h>
27#include <asm/hardware/scoop.h>
28#include <asm/mach-types.h>
29#include <asm/irq.h>
30#include <asm/apm.h>
31
32#include <asm/arch/pm.h>
33#include <asm/arch/pxa-regs.h>
34#include <asm/arch/sharpsl.h>
35#include "sharpsl.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#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_JKVAD 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#define SHARPSL_CHARGE_ON_VOLT 0x99 /* 2.9V */
54#define SHARPSL_CHARGE_ON_TEMP 0xe0 /* 2.9V */
55#define SHARPSL_CHARGE_ON_JKVAD_HIGH 0x9b /* 6V */
56#define SHARPSL_CHARGE_ON_JKVAD_LOW 0x34 /* 2V */
57#define SHARPSL_FATAL_ACIN_VOLT 182 /* 3.45V */
58#define SHARPSL_FATAL_NOACIN_VOLT 170 /* 3.40V */
59
60struct battery_thresh spitz_battery_levels_acin[] = {
61 { 213, 100},
62 { 212, 98},
63 { 211, 95},
64 { 210, 93},
65 { 209, 90},
66 { 208, 88},
67 { 207, 85},
68 { 206, 83},
69 { 205, 80},
70 { 204, 78},
71 { 203, 75},
72 { 202, 73},
73 { 201, 70},
74 { 200, 68},
75 { 199, 65},
76 { 198, 63},
77 { 197, 60},
78 { 196, 58},
79 { 195, 55},
80 { 194, 53},
81 { 193, 50},
82 { 192, 48},
83 { 192, 45},
84 { 191, 43},
85 { 191, 40},
86 { 190, 38},
87 { 190, 35},
88 { 189, 33},
89 { 188, 30},
90 { 187, 28},
91 { 186, 25},
92 { 185, 23},
93 { 184, 20},
94 { 183, 18},
95 { 182, 15},
96 { 181, 13},
97 { 180, 10},
98 { 179, 8},
99 { 178, 5},
100 { 0, 0},
101};
102
103struct battery_thresh spitz_battery_levels_noac[] = {
104 { 213, 100},
105 { 212, 98},
106 { 211, 95},
107 { 210, 93},
108 { 209, 90},
109 { 208, 88},
110 { 207, 85},
111 { 206, 83},
112 { 205, 80},
113 { 204, 78},
114 { 203, 75},
115 { 202, 73},
116 { 201, 70},
117 { 200, 68},
118 { 199, 65},
119 { 198, 63},
120 { 197, 60},
121 { 196, 58},
122 { 195, 55},
123 { 194, 53},
124 { 193, 50},
125 { 192, 48},
126 { 191, 45},
127 { 190, 43},
128 { 189, 40},
129 { 188, 38},
130 { 187, 35},
131 { 186, 33},
132 { 185, 30},
133 { 184, 28},
134 { 183, 25},
135 { 182, 23},
136 { 181, 20},
137 { 180, 18},
138 { 179, 15},
139 { 178, 13},
140 { 177, 10},
141 { 176, 8},
142 { 175, 5},
143 { 0, 0},
144};
145
146/* MAX1111 Commands */
147#define MAXCTRL_PD0 1u << 0
148#define MAXCTRL_PD1 1u << 1
149#define MAXCTRL_SGL 1u << 2
150#define MAXCTRL_UNI 1u << 3
151#define MAXCTRL_SEL_SH 4
152#define MAXCTRL_STR 1u << 7
153
154/* MAX1111 Channel Definitions */
155#define BATT_AD 4u
156#define BATT_THM 2u
157#define JK_VAD 6u
158
159
160/*
161 * Prototypes
162 */
163static int sharpsl_read_MainBattery(void);
164static int sharpsl_off_charge_battery(void);
165static int sharpsl_check_battery(int mode);
166static int sharpsl_ac_check(void);
167static int sharpsl_fatal_check(void);
168static int sharpsl_average_value(int ad);
169static void sharpsl_average_clear(void);
170static void sharpsl_charge_toggle(void *private_);
171static void sharpsl_battery_thread(void *private_);
172
173
174/*
175 * Variables
176 */
177struct sharpsl_pm_status sharpsl_pm;
178DECLARE_WORK(toggle_charger, sharpsl_charge_toggle, NULL);
179DECLARE_WORK(sharpsl_bat, sharpsl_battery_thread, NULL);
180
181
182static int get_percentage(int voltage)
183{
184 int i = sharpsl_pm.machinfo->bat_levels - 1;
185 struct battery_thresh *thresh;
186
187 if (sharpsl_pm.charge_mode == CHRG_ON)
188 thresh=sharpsl_pm.machinfo->bat_levels_acin;
189 else
190 thresh=sharpsl_pm.machinfo->bat_levels_noac;
191
192 while (i > 0 && (voltage > thresh[i].voltage))
193 i--;
194
195 return thresh[i].percentage;
196}
197
198static int get_apm_status(int voltage)
199{
200 int low_thresh, high_thresh;
201
202 if (sharpsl_pm.charge_mode == CHRG_ON) {
203 high_thresh = sharpsl_pm.machinfo->status_high_acin;
204 low_thresh = sharpsl_pm.machinfo->status_low_acin;
205 } else {
206 high_thresh = sharpsl_pm.machinfo->status_high_noac;
207 low_thresh = sharpsl_pm.machinfo->status_low_noac;
208 }
209
210 if (voltage >= high_thresh)
211 return APM_BATTERY_STATUS_HIGH;
212 if (voltage >= low_thresh)
213 return APM_BATTERY_STATUS_LOW;
214 return APM_BATTERY_STATUS_CRITICAL;
215}
216
217void sharpsl_battery_kick(void)
218{
219 schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125));
220}
221EXPORT_SYMBOL(sharpsl_battery_kick);
222
223
224static void sharpsl_battery_thread(void *private_)
225{
226 int voltage, percent, apm_status, i = 0;
227
228 if (!sharpsl_pm.machinfo)
229 return;
230
231 sharpsl_pm.battstat.ac_status = (!(STATUS_AC_IN) ? APM_AC_OFFLINE : APM_AC_ONLINE);
232
233 /* Corgi cannot confirm when battery fully charged so periodically kick! */
234 if (machine_is_corgi() && (sharpsl_pm.charge_mode == CHRG_ON)
235 && time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_ON_TIME_INTERVAL))
236 schedule_work(&toggle_charger);
237
238 while(1) {
239 voltage = sharpsl_read_MainBattery();
240 if (voltage > 0) break;
241 if (i++ > 5) {
242 voltage = sharpsl_pm.machinfo->bat_levels_noac[0].voltage;
243 dev_warn(sharpsl_pm.dev, "Warning: Cannot read main battery!\n");
244 break;
245 }
246 }
247
248 voltage = sharpsl_average_value(voltage);
249 apm_status = get_apm_status(voltage);
250 percent = get_percentage(voltage);
251
252 /* At low battery voltages, the voltage has a tendency to start
253 creeping back up so we try to avoid this here */
254 if ((sharpsl_pm.battstat.ac_status == APM_AC_ONLINE) || (apm_status == APM_BATTERY_STATUS_HIGH) || percent <= sharpsl_pm.battstat.mainbat_percent) {
255 sharpsl_pm.battstat.mainbat_voltage = voltage;
256 sharpsl_pm.battstat.mainbat_status = apm_status;
257 sharpsl_pm.battstat.mainbat_percent = percent;
258 }
259
260 dev_dbg(sharpsl_pm.dev, "Battery: voltage: %d, status: %d, percentage: %d, time: %d\n", voltage,
261 sharpsl_pm.battstat.mainbat_status, sharpsl_pm.battstat.mainbat_percent, jiffies);
262
263 /* If battery is low. limit backlight intensity to save power. */
264 if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
265 && ((sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_LOW) ||
266 (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL))) {
267 if (!(sharpsl_pm.flags & SHARPSL_BL_LIMIT)) {
268 corgibl_limit_intensity(1);
269 sharpsl_pm.flags |= SHARPSL_BL_LIMIT;
270 }
271 } else if (sharpsl_pm.flags & SHARPSL_BL_LIMIT) {
272 corgibl_limit_intensity(0);
273 sharpsl_pm.flags &= ~SHARPSL_BL_LIMIT;
274 }
275
276 /* Suspend if critical battery level */
277 if ((sharpsl_pm.battstat.ac_status != APM_AC_ONLINE)
278 && (sharpsl_pm.battstat.mainbat_status == APM_BATTERY_STATUS_CRITICAL)
279 && !(sharpsl_pm.flags & SHARPSL_APM_QUEUED)) {
280 sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
281 dev_err(sharpsl_pm.dev, "Fatal Off\n");
282 apm_queue_event(APM_CRITICAL_SUSPEND);
283 }
284
285 schedule_delayed_work(&sharpsl_bat, SHARPSL_BATCHK_TIME);
286}
287
288static void sharpsl_charge_on(void)
289{
290 dev_dbg(sharpsl_pm.dev, "Turning Charger On\n");
291
292 sharpsl_pm.full_count = 0;
293 sharpsl_pm.charge_mode = CHRG_ON;
294 schedule_delayed_work(&toggle_charger, msecs_to_jiffies(250));
295 schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(500));
296}
297
298static void sharpsl_charge_off(void)
299{
300 dev_dbg(sharpsl_pm.dev, "Turning Charger Off\n");
301
302 CHARGE_OFF();
303 CHARGE_LED_OFF();
304 sharpsl_pm.charge_mode = CHRG_OFF;
305
306 schedule_work(&sharpsl_bat);
307}
308
309static void sharpsl_charge_error(void)
310{
311 CHARGE_LED_ERR();
312 CHARGE_OFF();
313 sharpsl_pm.charge_mode = CHRG_ERROR;
314}
315
316static void sharpsl_charge_toggle(void *private_)
317{
318 dev_dbg(sharpsl_pm.dev, "Toogling Charger at time: %lx\n", jiffies);
319
320 if (STATUS_AC_IN == 0) {
321 sharpsl_charge_off();
322 return;
323 } else if ((sharpsl_check_battery(1) < 0) || (sharpsl_ac_check() < 0)) {
324 sharpsl_charge_error();
325 return;
326 }
327
328 CHARGE_LED_ON();
329 CHARGE_OFF();
330 mdelay(SHARPSL_CHARGE_WAIT_TIME);
331 CHARGE_ON();
332
333 sharpsl_pm.charge_start_time = jiffies;
334}
335
336static void sharpsl_ac_timer(unsigned long data)
337{
338 int acin = STATUS_AC_IN;
339
340 dev_dbg(sharpsl_pm.dev, "AC Status: %d\n",acin);
341
342 sharpsl_average_clear();
343 if (acin && (sharpsl_pm.charge_mode != CHRG_ON))
344 sharpsl_charge_on();
345 else if (sharpsl_pm.charge_mode == CHRG_ON)
346 sharpsl_charge_off();
347
348 schedule_work(&sharpsl_bat);
349}
350
351
352static irqreturn_t sharpsl_ac_isr(int irq, void *dev_id, struct pt_regs *fp)
353{
354 /* Delay the event slightly to debounce */
355 /* Must be a smaller delay than the chrg_full_isr below */
356 mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
357
358 return IRQ_HANDLED;
359}
360
361static void sharpsl_chrg_full_timer(unsigned long data)
362{
363 dev_dbg(sharpsl_pm.dev, "Charge Full at time: %lx\n", jiffies);
364
365 sharpsl_pm.full_count++;
366
367 if (STATUS_AC_IN == 0) {
368 dev_dbg(sharpsl_pm.dev, "Charge Full: AC removed - stop charging!\n");
369 if (sharpsl_pm.charge_mode == CHRG_ON)
370 sharpsl_charge_off();
371 } else if (sharpsl_pm.full_count < 2) {
372 dev_dbg(sharpsl_pm.dev, "Charge Full: Count too low\n");
373 schedule_work(&toggle_charger);
374 } else if (time_after(jiffies, sharpsl_pm.charge_start_time + SHARPSL_CHARGE_FINISH_TIME)) {
375 dev_dbg(sharpsl_pm.dev, "Charge Full: Interrupt generated too slowly - retry.\n");
376 schedule_work(&toggle_charger);
377 } else {
378 sharpsl_charge_off();
379 sharpsl_pm.charge_mode = CHRG_DONE;
380 dev_dbg(sharpsl_pm.dev, "Charge Full: Charging Finished\n");
381 }
382}
383
384/* Charging Finished Interrupt (Not present on Corgi) */
385/* Can trigger at the same time as an AC staus change so
386 delay until after that has been processed */
387static irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id, struct pt_regs *fp)
388{
389 if (sharpsl_pm.flags & SHARPSL_SUSPENDED)
390 return IRQ_HANDLED;
391
392 /* delay until after any ac interrupt */
393 mod_timer(&sharpsl_pm.chrg_full_timer, jiffies + msecs_to_jiffies(500));
394
395 return IRQ_HANDLED;
396}
397
398static irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id, struct pt_regs *fp)
399{
400 int is_fatal = 0;
401
402 if (STATUS_BATT_LOCKED == 0) {
403 dev_err(sharpsl_pm.dev, "Battery now Unlocked! Suspending.\n");
404 is_fatal = 1;
405 }
406
407 if (sharpsl_pm.machinfo->gpio_fatal && (STATUS_FATAL == 0)) {
408 dev_err(sharpsl_pm.dev, "Fatal Batt Error! Suspending.\n");
409 is_fatal = 1;
410 }
411
412 if (!(sharpsl_pm.flags & SHARPSL_APM_QUEUED) && is_fatal) {
413 sharpsl_pm.flags |= SHARPSL_APM_QUEUED;
414 apm_queue_event(APM_CRITICAL_SUSPEND);
415 }
416
417 return IRQ_HANDLED;
418}
419
420/*
421 * Maintain an average of the last 10 readings
422 */
423#define SHARPSL_CNV_VALUE_NUM 10
424static int sharpsl_ad_index;
425
426static void sharpsl_average_clear(void)
427{
428 sharpsl_ad_index = 0;
429}
430
431static int sharpsl_average_value(int ad)
432{
433 int i, ad_val = 0;
434 static int sharpsl_ad[SHARPSL_CNV_VALUE_NUM+1];
435
436 if (sharpsl_pm.battstat.mainbat_status != APM_BATTERY_STATUS_HIGH) {
437 sharpsl_ad_index = 0;
438 return ad;
439 }
440
441 sharpsl_ad[sharpsl_ad_index] = ad;
442 sharpsl_ad_index++;
443 if (sharpsl_ad_index >= SHARPSL_CNV_VALUE_NUM) {
444 for (i=0; i < (SHARPSL_CNV_VALUE_NUM-1); i++)
445 sharpsl_ad[i] = sharpsl_ad[i+1];
446 sharpsl_ad_index = SHARPSL_CNV_VALUE_NUM - 1;
447 }
448 for (i=0; i < sharpsl_ad_index; i++)
449 ad_val += sharpsl_ad[i];
450
451 return (ad_val / sharpsl_ad_index);
452}
453
454
455/*
456 * Read MAX1111 ADC
457 */
458static int read_max1111(int channel)
459{
460 return corgi_ssp_max1111_get((channel << MAXCTRL_SEL_SH) | MAXCTRL_PD0 | MAXCTRL_PD1
461 | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR);
462}
463
464static int sharpsl_read_MainBattery(void)
465{
466 return read_max1111(BATT_AD);
467}
468
469static int sharpsl_read_Temp(void)
470{
471 int temp;
472
473 sharpsl_pm.machinfo->measure_temp(1);
474
475 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
476 temp = read_max1111(BATT_THM);
477
478 sharpsl_pm.machinfo->measure_temp(0);
479
480 return temp;
481}
482
483static int sharpsl_read_jkvad(void)
484{
485 return read_max1111(JK_VAD);
486}
487
488/*
489 * Take an array of 5 integers, remove the maximum and minimum values
490 * and return the average.
491 */
492static int get_select_val(int *val)
493{
494 int i, j, k, temp, sum = 0;
495
496 /* Find MAX val */
497 temp = val[0];
498 j=0;
499 for (i=1; i<5; i++) {
500 if (temp < val[i]) {
501 temp = val[i];
502 j = i;
503 }
504 }
505
506 /* Find MIN val */
507 temp = val[4];
508 k=4;
509 for (i=3; i>=0; i--) {
510 if (temp > val[i]) {
511 temp = val[i];
512 k = i;
513 }
514 }
515
516 for (i=0; i<5; i++)
517 if (i != j && i != k )
518 sum += val[i];
519
520 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]);
521
522 return (sum/3);
523}
524
525/* mode 0 - Check temperature and voltage
526 * 1 - Check temperature only */
527static int sharpsl_check_battery(int mode)
528{
529 int val, i, buff[5];
530
531 /* Check battery temperature */
532 for (i=0; i<5; i++) {
533 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_TEMP);
534 buff[i] = sharpsl_read_Temp();
535 }
536
537 val = get_select_val(buff);
538
539 dev_dbg(sharpsl_pm.dev, "Temperature: %d\n", val);
540 if (val > SHARPSL_CHARGE_ON_TEMP)
541 return -1;
542 if (mode == 1)
543 return 0;
544
545 /* disable charge, enable discharge */
546 CHARGE_OFF();
547 DISCHARGE_ON();
548 mdelay(SHARPSL_WAIT_DISCHARGE_ON);
549
550 if (sharpsl_pm.machinfo->discharge1)
551 sharpsl_pm.machinfo->discharge1(1);
552
553 /* Check battery voltage */
554 for (i=0; i<5; i++) {
555 buff[i] = sharpsl_read_MainBattery();
556 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
557 }
558
559 if (sharpsl_pm.machinfo->discharge1)
560 sharpsl_pm.machinfo->discharge1(0);
561
562 DISCHARGE_OFF();
563
564 val = get_select_val(buff);
565 dev_dbg(sharpsl_pm.dev, "Battery Voltage: %d\n", val);
566
567 if (val < SHARPSL_CHARGE_ON_VOLT)
568 return -1;
569
570 return 0;
571}
572
573static int sharpsl_ac_check(void)
574{
575 int temp, i, buff[5];
576
577 for (i=0; i<5; i++) {
578 buff[i] = sharpsl_read_jkvad();
579 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_JKVAD);
580 }
581
582 temp = get_select_val(buff);
583 dev_dbg(sharpsl_pm.dev, "AC Voltage: %d\n",temp);
584
585 if ((temp > SHARPSL_CHARGE_ON_JKVAD_HIGH) || (temp < SHARPSL_CHARGE_ON_JKVAD_LOW)) {
586 dev_err(sharpsl_pm.dev, "Error: AC check failed.\n");
587 return -1;
588 }
589
590 return 0;
591}
592
593#ifdef CONFIG_PM
594static int sharpsl_pm_suspend(struct device *dev, pm_message_t state)
595{
596 sharpsl_pm.flags |= SHARPSL_SUSPENDED;
597 flush_scheduled_work();
598
599 if (sharpsl_pm.charge_mode == CHRG_ON)
600 sharpsl_pm.flags |= SHARPSL_DO_OFFLINE_CHRG;
601 else
602 sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
603
604 return 0;
605}
606
607static int sharpsl_pm_resume(struct device *dev)
608{
609 /* Clear the reset source indicators as they break the bootloader upon reboot */
610 RCSR = 0x0f;
611 sharpsl_average_clear();
612 sharpsl_pm.flags &= ~SHARPSL_APM_QUEUED;
613 sharpsl_pm.flags &= ~SHARPSL_SUSPENDED;
614
615 return 0;
616}
617
618static void corgi_goto_sleep(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
619{
620 dev_dbg(sharpsl_pm.dev, "Time is: %08x\n",RCNR);
621
622 dev_dbg(sharpsl_pm.dev, "Offline Charge Activate = %d\n",sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG);
623 /* not charging and AC-IN! */
624
625 if ((sharpsl_pm.flags & SHARPSL_DO_OFFLINE_CHRG) && (STATUS_AC_IN != 0)) {
626 dev_dbg(sharpsl_pm.dev, "Activating Offline Charger...\n");
627 sharpsl_pm.charge_mode = CHRG_OFF;
628 sharpsl_pm.flags &= ~SHARPSL_DO_OFFLINE_CHRG;
629 sharpsl_off_charge_battery();
630 }
631
632 sharpsl_pm.machinfo->presuspend();
633
634 PEDR = 0xffffffff; /* clear it */
635
636 sharpsl_pm.flags &= ~SHARPSL_ALARM_ACTIVE;
637 if ((sharpsl_pm.charge_mode == CHRG_ON) && ((alarm_enable && ((alarm_time - RCNR) > (SHARPSL_BATCHK_TIME_SUSPEND + 30))) || !alarm_enable)) {
638 RTSR &= RTSR_ALE;
639 RTAR = RCNR + SHARPSL_BATCHK_TIME_SUSPEND;
640 dev_dbg(sharpsl_pm.dev, "Charging alarm at: %08x\n",RTAR);
641 sharpsl_pm.flags |= SHARPSL_ALARM_ACTIVE;
642 } else if (alarm_enable) {
643 RTSR &= RTSR_ALE;
644 RTAR = alarm_time;
645 dev_dbg(sharpsl_pm.dev, "User alarm at: %08x\n",RTAR);
646 } else {
647 dev_dbg(sharpsl_pm.dev, "No alarms set.\n");
648 }
649
650 pxa_pm_enter(state);
651
652 sharpsl_pm.machinfo->postsuspend();
653
654 dev_dbg(sharpsl_pm.dev, "Corgi woken up from suspend: %08x\n",PEDR);
655}
656
657static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enable, suspend_state_t state)
658{
659 if (!sharpsl_pm.machinfo->should_wakeup(!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE) && alarm_enable) )
660 {
661 if (!(sharpsl_pm.flags & SHARPSL_ALARM_ACTIVE)) {
662 dev_dbg(sharpsl_pm.dev, "No user triggered wakeup events and not charging. Strange. Suspend.\n");
663 corgi_goto_sleep(alarm_time, alarm_enable, state);
664 return 1;
665 }
666 if(sharpsl_off_charge_battery()) {
667 dev_dbg(sharpsl_pm.dev, "Charging. Suspend...\n");
668 corgi_goto_sleep(alarm_time, alarm_enable, state);
669 return 1;
670 }
671 dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n");
672 }
673
674 if ((STATUS_BATT_LOCKED == 0) || (sharpsl_fatal_check() < 0) )
675 {
676 dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n");
677 corgi_goto_sleep(alarm_time, alarm_enable, state);
678 return 1;
679 }
680
681 return 0;
682}
683
684static int corgi_pxa_pm_enter(suspend_state_t state)
685{
686 unsigned long alarm_time = RTAR;
687 unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0);
688
689 dev_dbg(sharpsl_pm.dev, "SharpSL suspending for first time.\n");
690
691 corgi_goto_sleep(alarm_time, alarm_status, state);
692
693 while (corgi_enter_suspend(alarm_time,alarm_status,state))
694 {}
695
696 dev_dbg(sharpsl_pm.dev, "SharpSL resuming...\n");
697
698 return 0;
699}
700#endif
701
702
703/*
704 * Check for fatal battery errors
705 * Fatal returns -1
706 */
707static int sharpsl_fatal_check(void)
708{
709 int buff[5], temp, i, acin;
710
711 dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check entered\n");
712
713 /* Check AC-Adapter */
714 acin = STATUS_AC_IN;
715
716 if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
717 CHARGE_OFF();
718 udelay(100);
719 DISCHARGE_ON(); /* enable discharge */
720 mdelay(SHARPSL_WAIT_DISCHARGE_ON);
721 }
722
723 if (sharpsl_pm.machinfo->discharge1)
724 sharpsl_pm.machinfo->discharge1(1);
725
726 /* Check battery : check inserting battery ? */
727 for (i=0; i<5; i++) {
728 buff[i] = sharpsl_read_MainBattery();
729 mdelay(SHARPSL_CHECK_BATTERY_WAIT_TIME_VOLT);
730 }
731
732 if (sharpsl_pm.machinfo->discharge1)
733 sharpsl_pm.machinfo->discharge1(0);
734
735 if (acin && (sharpsl_pm.charge_mode == CHRG_ON)) {
736 udelay(100);
737 CHARGE_ON();
738 DISCHARGE_OFF();
739 }
740
741 temp = get_select_val(buff);
742 dev_dbg(sharpsl_pm.dev, "sharpsl_fatal_check: acin: %d, discharge voltage: %d, no discharge: %d\n", acin, temp, sharpsl_read_MainBattery());
743
744 if ((acin && (temp < SHARPSL_FATAL_ACIN_VOLT)) ||
745 (!acin && (temp < SHARPSL_FATAL_NOACIN_VOLT)))
746 return -1;
747 return 0;
748}
749
750static int sharpsl_off_charge_error(void)
751{
752 dev_err(sharpsl_pm.dev, "Offline Charger: Error occured.\n");
753 CHARGE_OFF();
754 CHARGE_LED_ERR();
755 sharpsl_pm.charge_mode = CHRG_ERROR;
756 return 1;
757}
758
759/*
760 * Charging Control while suspended
761 * Return 1 - go straight to sleep
762 * Return 0 - sleep or wakeup depending on other factors
763 */
764static int sharpsl_off_charge_battery(void)
765{
766 int time;
767
768 dev_dbg(sharpsl_pm.dev, "Charge Mode: %d\n", sharpsl_pm.charge_mode);
769
770 if (sharpsl_pm.charge_mode == CHRG_OFF) {
771 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 1\n");
772
773 /* AC Check */
774 if ((sharpsl_ac_check() < 0) || (sharpsl_check_battery(1) < 0))
775 return sharpsl_off_charge_error();
776
777 /* Start Charging */
778 CHARGE_LED_ON();
779 CHARGE_OFF();
780 mdelay(SHARPSL_CHARGE_WAIT_TIME);
781 CHARGE_ON();
782
783 sharpsl_pm.charge_mode = CHRG_ON;
784 sharpsl_pm.full_count = 0;
785
786 return 1;
787 } else if (sharpsl_pm.charge_mode != CHRG_ON) {
788 return 1;
789 }
790
791 if (sharpsl_pm.full_count == 0) {
792 int time;
793
794 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 2\n");
795
796 if (sharpsl_check_battery(0) < 0)
797 return sharpsl_off_charge_error();
798
799 CHARGE_OFF();
800 mdelay(SHARPSL_CHARGE_WAIT_TIME);
801 CHARGE_ON();
802 sharpsl_pm.charge_mode = CHRG_ON;
803
804 mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
805
806 time = RCNR;
807 while(1) {
808 /* Check if any wakeup event had occured */
809 if (sharpsl_pm.machinfo->charger_wakeup() != 0)
810 return 0;
811 /* Check for timeout */
812 if ((RCNR - time) > SHARPSL_WAIT_CO_TIME)
813 return 1;
814 if (STATUS_CHRG_FULL) {
815 dev_dbg(sharpsl_pm.dev, "Offline Charger: Charge full occured. Retrying to check\n");
816 sharpsl_pm.full_count++;
817 CHARGE_OFF();
818 mdelay(SHARPSL_CHARGE_WAIT_TIME);
819 CHARGE_ON();
820 return 1;
821 }
822 }
823 }
824
825 dev_dbg(sharpsl_pm.dev, "Offline Charger: Step 3\n");
826
827 mdelay(SHARPSL_CHARGE_CO_CHECK_TIME);
828
829 time = RCNR;
830 while(1) {
831 /* Check if any wakeup event had occured */
832 if (sharpsl_pm.machinfo->charger_wakeup() != 0)
833 return 0;
834 /* Check for timeout */
835 if ((RCNR-time) > SHARPSL_WAIT_CO_TIME) {
836 if (sharpsl_pm.full_count > SHARPSL_CHARGE_RETRY_CNT) {
837 dev_dbg(sharpsl_pm.dev, "Offline Charger: Not charged sufficiently. Retrying.\n");
838 sharpsl_pm.full_count = 0;
839 }
840 sharpsl_pm.full_count++;
841 return 1;
842 }
843 if (STATUS_CHRG_FULL) {
844 dev_dbg(sharpsl_pm.dev, "Offline Charger: Charging complete.\n");
845 CHARGE_LED_OFF();
846 CHARGE_OFF();
847 sharpsl_pm.charge_mode = CHRG_DONE;
848 return 1;
849 }
850 }
851}
852
853
854static ssize_t battery_percentage_show(struct device *dev, struct device_attribute *attr, char *buf)
855{
856 return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_percent);
857}
858
859static ssize_t battery_voltage_show(struct device *dev, struct device_attribute *attr, char *buf)
860{
861 return sprintf(buf, "%d\n",sharpsl_pm.battstat.mainbat_voltage);
862}
863
864static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL);
865static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL);
866
867extern void (*apm_get_power_status)(struct apm_power_info *);
868
869static void sharpsl_apm_get_power_status(struct apm_power_info *info)
870{
871 info->ac_line_status = sharpsl_pm.battstat.ac_status;
872
873 if (sharpsl_pm.charge_mode == CHRG_ON)
874 info->battery_status = APM_BATTERY_STATUS_CHARGING;
875 else
876 info->battery_status = sharpsl_pm.battstat.mainbat_status;
877
878 info->battery_flag = (1 << info->battery_status);
879 info->battery_life = sharpsl_pm.battstat.mainbat_percent;
880}
881
882static struct pm_ops sharpsl_pm_ops = {
883 .pm_disk_mode = PM_DISK_FIRMWARE,
884 .prepare = pxa_pm_prepare,
885 .enter = corgi_pxa_pm_enter,
886 .finish = pxa_pm_finish,
887};
888
889static int __init sharpsl_pm_probe(struct device *dev)
890{
891 if (!dev->platform_data)
892 return -EINVAL;
893
894 sharpsl_pm.dev = dev;
895 sharpsl_pm.machinfo = dev->platform_data;
896 sharpsl_pm.charge_mode = CHRG_OFF;
897 sharpsl_pm.flags = 0;
898
899 sharpsl_pm.machinfo->init();
900
901 init_timer(&sharpsl_pm.ac_timer);
902 sharpsl_pm.ac_timer.function = sharpsl_ac_timer;
903
904 init_timer(&sharpsl_pm.chrg_full_timer);
905 sharpsl_pm.chrg_full_timer.function = sharpsl_chrg_full_timer;
906
907 pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN);
908 pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batfull | GPIO_IN);
909 pxa_gpio_mode(sharpsl_pm.machinfo->gpio_batlock | GPIO_IN);
910
911 /* Register interrupt handlers */
912 if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr, SA_INTERRUPT, "AC Input Detect", sharpsl_ac_isr)) {
913 dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin));
914 }
915 else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin),IRQT_BOTHEDGE);
916
917 if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr, SA_INTERRUPT, "Battery Cover", sharpsl_fatal_isr)) {
918 dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock));
919 }
920 else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock),IRQT_FALLING);
921
922 if (sharpsl_pm.machinfo->gpio_fatal) {
923 if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr, SA_INTERRUPT, "Fatal Battery", sharpsl_fatal_isr)) {
924 dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal));
925 }
926 else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal),IRQT_FALLING);
927 }
928
929 if (!machine_is_corgi())
930 {
931 /* Register interrupt handler. */
932 if (request_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr, SA_INTERRUPT, "CO", sharpsl_chrg_full_isr)) {
933 dev_err(sharpsl_pm.dev, "Could not get irq %d.\n", IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull));
934 }
935 else set_irq_type(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull),IRQT_RISING);
936 }
937
938 device_create_file(dev, &dev_attr_battery_percentage);
939 device_create_file(dev, &dev_attr_battery_voltage);
940
941 apm_get_power_status = sharpsl_apm_get_power_status;
942
943 pm_set_ops(&sharpsl_pm_ops);
944
945 mod_timer(&sharpsl_pm.ac_timer, jiffies + msecs_to_jiffies(250));
946
947 return 0;
948}
949
950static int sharpsl_pm_remove(struct device *dev)
951{
952 pm_set_ops(NULL);
953
954 device_remove_file(dev, &dev_attr_battery_percentage);
955 device_remove_file(dev, &dev_attr_battery_voltage);
956
957 free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_acin), sharpsl_ac_isr);
958 free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batlock), sharpsl_fatal_isr);
959
960 if (sharpsl_pm.machinfo->gpio_fatal)
961 free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_fatal), sharpsl_fatal_isr);
962
963 if (!machine_is_corgi())
964 free_irq(IRQ_GPIO(sharpsl_pm.machinfo->gpio_batfull), sharpsl_chrg_full_isr);
965
966 del_timer_sync(&sharpsl_pm.chrg_full_timer);
967 del_timer_sync(&sharpsl_pm.ac_timer);
968
969 return 0;
970}
971
972static struct device_driver sharpsl_pm_driver = {
973 .name = "sharpsl-pm",
974 .bus = &platform_bus_type,
975 .probe = sharpsl_pm_probe,
976 .remove = sharpsl_pm_remove,
977 .suspend = sharpsl_pm_suspend,
978 .resume = sharpsl_pm_resume,
979};
980
981static int __devinit sharpsl_pm_init(void)
982{
983 return driver_register(&sharpsl_pm_driver);
984}
985
986static void sharpsl_pm_exit(void)
987{
988 driver_unregister(&sharpsl_pm_driver);
989}
990
991late_initcall(sharpsl_pm_init);
992module_exit(sharpsl_pm_exit);
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c
index 4867498f68e8..bd9a6996aee7 100644
--- a/drivers/video/backlight/corgi_bl.c
+++ b/drivers/video/backlight/corgi_bl.c
@@ -48,6 +48,12 @@ static void corgibl_send_intensity(int intensity)
48 corgibl_mach_set_intensity(intensity); 48 corgibl_mach_set_intensity(intensity);
49 49
50 spin_unlock_irqrestore(&bl_lock, flags); 50 spin_unlock_irqrestore(&bl_lock, flags);
51
52 corgi_kick_batt = symbol_get(sharpsl_battery_kick);
53 if (corgi_kick_batt) {
54 corgi_kick_batt();
55 symbol_put(sharpsl_battery_kick);
56 }
51} 57}
52 58
53static void corgibl_blank(int blank) 59static void corgibl_blank(int blank)
diff --git a/include/asm-arm/arch-pxa/sharpsl.h b/include/asm-arm/arch-pxa/sharpsl.h
index 311f2bb5386a..0b43495d24b4 100644
--- a/include/asm-arm/arch-pxa/sharpsl.h
+++ b/include/asm-arm/arch-pxa/sharpsl.h
@@ -21,12 +21,18 @@ struct corgits_machinfo {
21 void (*wait_hsync)(void); 21 void (*wait_hsync)(void);
22}; 22};
23 23
24
24/* 25/*
25 * SharpSL Backlight 26 * SharpSL Backlight
26 */ 27 */
27
28struct corgibl_machinfo { 28struct corgibl_machinfo {
29 int max_intensity; 29 int max_intensity;
30 void (*set_bl_intensity)(int intensity); 30 void (*set_bl_intensity)(int intensity);
31}; 31};
32extern void corgibl_limit_intensity(int limit);
33
32 34
35/*
36 * SharpSL Battery/PM Driver
37 */
38extern void sharpsl_battery_kick(void);