diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-pxa/sharpsl.h | 87 | ||||
-rw-r--r-- | arch/arm/mach-pxa/sharpsl_pm.c | 992 |
2 files changed, 1079 insertions, 0 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); | |||
32 | void spitz_put_hsync(void); | 32 | void spitz_put_hsync(void); |
33 | void corgi_wait_hsync(void); | 33 | void corgi_wait_hsync(void); |
34 | void spitz_wait_hsync(void); | 34 | void spitz_wait_hsync(void); |
35 | |||
36 | /* | ||
37 | * SharpSL Battery/PM Driver | ||
38 | */ | ||
39 | |||
40 | struct 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 | |||
65 | struct battery_thresh { | ||
66 | int voltage; | ||
67 | int percentage; | ||
68 | }; | ||
69 | |||
70 | struct 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 | |||
77 | struct 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 | |||
101 | extern struct sharpsl_pm_status sharpsl_pm; | ||
102 | extern struct battery_thresh spitz_battery_levels_acin[]; | ||
103 | extern 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 | |||
60 | struct 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 | |||
103 | struct 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 | */ | ||
163 | static int sharpsl_read_MainBattery(void); | ||
164 | static int sharpsl_off_charge_battery(void); | ||
165 | static int sharpsl_check_battery(int mode); | ||
166 | static int sharpsl_ac_check(void); | ||
167 | static int sharpsl_fatal_check(void); | ||
168 | static int sharpsl_average_value(int ad); | ||
169 | static void sharpsl_average_clear(void); | ||
170 | static void sharpsl_charge_toggle(void *private_); | ||
171 | static void sharpsl_battery_thread(void *private_); | ||
172 | |||
173 | |||
174 | /* | ||
175 | * Variables | ||
176 | */ | ||
177 | struct sharpsl_pm_status sharpsl_pm; | ||
178 | DECLARE_WORK(toggle_charger, sharpsl_charge_toggle, NULL); | ||
179 | DECLARE_WORK(sharpsl_bat, sharpsl_battery_thread, NULL); | ||
180 | |||
181 | |||
182 | static 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 | |||
198 | static 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 | |||
217 | void sharpsl_battery_kick(void) | ||
218 | { | ||
219 | schedule_delayed_work(&sharpsl_bat, msecs_to_jiffies(125)); | ||
220 | } | ||
221 | EXPORT_SYMBOL(sharpsl_battery_kick); | ||
222 | |||
223 | |||
224 | static 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 | |||
288 | static 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 | |||
298 | static 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 | |||
309 | static void sharpsl_charge_error(void) | ||
310 | { | ||
311 | CHARGE_LED_ERR(); | ||
312 | CHARGE_OFF(); | ||
313 | sharpsl_pm.charge_mode = CHRG_ERROR; | ||
314 | } | ||
315 | |||
316 | static 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 | |||
336 | static 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 | |||
352 | static 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 | |||
361 | static 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 */ | ||
387 | static 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 | |||
398 | static 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 | ||
424 | static int sharpsl_ad_index; | ||
425 | |||
426 | static void sharpsl_average_clear(void) | ||
427 | { | ||
428 | sharpsl_ad_index = 0; | ||
429 | } | ||
430 | |||
431 | static 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 | */ | ||
458 | static 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 | |||
464 | static int sharpsl_read_MainBattery(void) | ||
465 | { | ||
466 | return read_max1111(BATT_AD); | ||
467 | } | ||
468 | |||
469 | static 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 | |||
483 | static 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 | */ | ||
492 | static 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 */ | ||
527 | static 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 | |||
573 | static 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 | ||
594 | static 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 | |||
607 | static 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 | |||
618 | static 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 | |||
657 | static 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 | |||
684 | static 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 | */ | ||
707 | static 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 | |||
750 | static 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 | */ | ||
764 | static 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 | |||
854 | static 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 | |||
859 | static 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 | |||
864 | static DEVICE_ATTR(battery_percentage, 0444, battery_percentage_show, NULL); | ||
865 | static DEVICE_ATTR(battery_voltage, 0444, battery_voltage_show, NULL); | ||
866 | |||
867 | extern void (*apm_get_power_status)(struct apm_power_info *); | ||
868 | |||
869 | static 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 | |||
882 | static 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 | |||
889 | static 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 | |||
950 | static 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 | |||
972 | static 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 | |||
981 | static int __devinit sharpsl_pm_init(void) | ||
982 | { | ||
983 | return driver_register(&sharpsl_pm_driver); | ||
984 | } | ||
985 | |||
986 | static void sharpsl_pm_exit(void) | ||
987 | { | ||
988 | driver_unregister(&sharpsl_pm_driver); | ||
989 | } | ||
990 | |||
991 | late_initcall(sharpsl_pm_init); | ||
992 | module_exit(sharpsl_pm_exit); | ||