sharpsl_pm.c 27 KB

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