ipaq_micro_battery.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * This program is free software; you can redistribute it and/or modify
  3. * it under the terms of the GNU General Public License version 2 as
  4. * published by the Free Software Foundation.
  5. *
  6. * h3xxx atmel micro companion support, battery subdevice
  7. * based on previous kernel 2.4 version
  8. * Author : Alessandro Gardich <gremlin@gremlin.it>
  9. * Author : Linus Walleij <linus.walleij@linaro.org>
  10. *
  11. */
  12. #include <linux/module.h>
  13. #include <linux/init.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/mfd/ipaq-micro.h>
  16. #include <linux/power_supply.h>
  17. #include <linux/workqueue.h>
  18. #define BATT_PERIOD 100000 /* 100 seconds in milliseconds */
  19. #define MICRO_BATT_CHEM_ALKALINE 0x01
  20. #define MICRO_BATT_CHEM_NICD 0x02
  21. #define MICRO_BATT_CHEM_NIMH 0x03
  22. #define MICRO_BATT_CHEM_LION 0x04
  23. #define MICRO_BATT_CHEM_LIPOLY 0x05
  24. #define MICRO_BATT_CHEM_NOT_INSTALLED 0x06
  25. #define MICRO_BATT_CHEM_UNKNOWN 0xff
  26. #define MICRO_BATT_STATUS_HIGH 0x01
  27. #define MICRO_BATT_STATUS_LOW 0x02
  28. #define MICRO_BATT_STATUS_CRITICAL 0x04
  29. #define MICRO_BATT_STATUS_CHARGING 0x08
  30. #define MICRO_BATT_STATUS_CHARGEMAIN 0x10
  31. #define MICRO_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
  32. #define MICRO_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */
  33. #define MICRO_BATT_STATUS_FULL 0x40 /* Battery fully charged */
  34. #define MICRO_BATT_STATUS_NOBATTERY 0x80
  35. #define MICRO_BATT_STATUS_UNKNOWN 0xff
  36. struct micro_battery {
  37. struct ipaq_micro *micro;
  38. struct workqueue_struct *wq;
  39. struct delayed_work update;
  40. u8 ac;
  41. u8 chemistry;
  42. unsigned int voltage;
  43. u16 temperature;
  44. u8 flag;
  45. };
  46. static void micro_battery_work(struct work_struct *work)
  47. {
  48. struct micro_battery *mb = container_of(work,
  49. struct micro_battery, update.work);
  50. struct ipaq_micro_msg msg_battery = {
  51. .id = MSG_BATTERY,
  52. };
  53. struct ipaq_micro_msg msg_sensor = {
  54. .id = MSG_THERMAL_SENSOR,
  55. };
  56. /* First send battery message */
  57. ipaq_micro_tx_msg_sync(mb->micro, &msg_battery);
  58. if (msg_battery.rx_len < 4)
  59. pr_info("ERROR");
  60. /*
  61. * Returned message format:
  62. * byte 0: 0x00 = Not plugged in
  63. * 0x01 = AC adapter plugged in
  64. * byte 1: chemistry
  65. * byte 2: voltage LSB
  66. * byte 3: voltage MSB
  67. * byte 4: flags
  68. * byte 5-9: same for battery 2
  69. */
  70. mb->ac = msg_battery.rx_data[0];
  71. mb->chemistry = msg_battery.rx_data[1];
  72. mb->voltage = ((((unsigned short)msg_battery.rx_data[3] << 8) +
  73. msg_battery.rx_data[2]) * 5000L) * 1000 / 1024;
  74. mb->flag = msg_battery.rx_data[4];
  75. if (msg_battery.rx_len == 9)
  76. pr_debug("second battery ignored\n");
  77. /* Then read the sensor */
  78. ipaq_micro_tx_msg_sync(mb->micro, &msg_sensor);
  79. mb->temperature = msg_sensor.rx_data[1] << 8 | msg_sensor.rx_data[0];
  80. queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
  81. }
  82. static int get_capacity(struct power_supply *b)
  83. {
  84. struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
  85. switch (mb->flag & 0x07) {
  86. case MICRO_BATT_STATUS_HIGH:
  87. return 100;
  88. break;
  89. case MICRO_BATT_STATUS_LOW:
  90. return 50;
  91. break;
  92. case MICRO_BATT_STATUS_CRITICAL:
  93. return 5;
  94. break;
  95. default:
  96. break;
  97. }
  98. return 0;
  99. }
  100. static int get_status(struct power_supply *b)
  101. {
  102. struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
  103. if (mb->flag == MICRO_BATT_STATUS_UNKNOWN)
  104. return POWER_SUPPLY_STATUS_UNKNOWN;
  105. if (mb->flag & MICRO_BATT_STATUS_FULL)
  106. return POWER_SUPPLY_STATUS_FULL;
  107. if ((mb->flag & MICRO_BATT_STATUS_CHARGING) ||
  108. (mb->flag & MICRO_BATT_STATUS_CHARGEMAIN))
  109. return POWER_SUPPLY_STATUS_CHARGING;
  110. return POWER_SUPPLY_STATUS_DISCHARGING;
  111. }
  112. static int micro_batt_get_property(struct power_supply *b,
  113. enum power_supply_property psp,
  114. union power_supply_propval *val)
  115. {
  116. struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
  117. switch (psp) {
  118. case POWER_SUPPLY_PROP_TECHNOLOGY:
  119. switch (mb->chemistry) {
  120. case MICRO_BATT_CHEM_NICD:
  121. val->intval = POWER_SUPPLY_TECHNOLOGY_NiCd;
  122. break;
  123. case MICRO_BATT_CHEM_NIMH:
  124. val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
  125. break;
  126. case MICRO_BATT_CHEM_LION:
  127. val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
  128. break;
  129. case MICRO_BATT_CHEM_LIPOLY:
  130. val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO;
  131. break;
  132. default:
  133. val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
  134. break;
  135. };
  136. break;
  137. case POWER_SUPPLY_PROP_STATUS:
  138. val->intval = get_status(b);
  139. break;
  140. case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  141. val->intval = 4700000;
  142. break;
  143. case POWER_SUPPLY_PROP_CAPACITY:
  144. val->intval = get_capacity(b);
  145. break;
  146. case POWER_SUPPLY_PROP_TEMP:
  147. val->intval = mb->temperature;
  148. break;
  149. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  150. val->intval = mb->voltage;
  151. break;
  152. default:
  153. return -EINVAL;
  154. };
  155. return 0;
  156. }
  157. static int micro_ac_get_property(struct power_supply *b,
  158. enum power_supply_property psp,
  159. union power_supply_propval *val)
  160. {
  161. struct micro_battery *mb = dev_get_drvdata(b->dev.parent);
  162. switch (psp) {
  163. case POWER_SUPPLY_PROP_ONLINE:
  164. val->intval = mb->ac;
  165. break;
  166. default:
  167. return -EINVAL;
  168. };
  169. return 0;
  170. }
  171. static enum power_supply_property micro_batt_power_props[] = {
  172. POWER_SUPPLY_PROP_TECHNOLOGY,
  173. POWER_SUPPLY_PROP_STATUS,
  174. POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
  175. POWER_SUPPLY_PROP_CAPACITY,
  176. POWER_SUPPLY_PROP_TEMP,
  177. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  178. };
  179. static const struct power_supply_desc micro_batt_power_desc = {
  180. .name = "main-battery",
  181. .type = POWER_SUPPLY_TYPE_BATTERY,
  182. .properties = micro_batt_power_props,
  183. .num_properties = ARRAY_SIZE(micro_batt_power_props),
  184. .get_property = micro_batt_get_property,
  185. .use_for_apm = 1,
  186. };
  187. static enum power_supply_property micro_ac_power_props[] = {
  188. POWER_SUPPLY_PROP_ONLINE,
  189. };
  190. static const struct power_supply_desc micro_ac_power_desc = {
  191. .name = "ac",
  192. .type = POWER_SUPPLY_TYPE_MAINS,
  193. .properties = micro_ac_power_props,
  194. .num_properties = ARRAY_SIZE(micro_ac_power_props),
  195. .get_property = micro_ac_get_property,
  196. };
  197. static struct power_supply *micro_batt_power, *micro_ac_power;
  198. static int micro_batt_probe(struct platform_device *pdev)
  199. {
  200. struct micro_battery *mb;
  201. int ret;
  202. mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
  203. if (!mb)
  204. return -ENOMEM;
  205. mb->micro = dev_get_drvdata(pdev->dev.parent);
  206. mb->wq = create_singlethread_workqueue("ipaq-battery-wq");
  207. if (!mb->wq)
  208. return -ENOMEM;
  209. INIT_DELAYED_WORK(&mb->update, micro_battery_work);
  210. platform_set_drvdata(pdev, mb);
  211. queue_delayed_work(mb->wq, &mb->update, 1);
  212. micro_batt_power = power_supply_register(&pdev->dev,
  213. &micro_batt_power_desc, NULL);
  214. if (IS_ERR(micro_batt_power)) {
  215. ret = PTR_ERR(micro_batt_power);
  216. goto batt_err;
  217. }
  218. micro_ac_power = power_supply_register(&pdev->dev,
  219. &micro_ac_power_desc, NULL);
  220. if (IS_ERR(micro_ac_power)) {
  221. ret = PTR_ERR(micro_ac_power);
  222. goto ac_err;
  223. }
  224. dev_info(&pdev->dev, "iPAQ micro battery driver\n");
  225. return 0;
  226. ac_err:
  227. power_supply_unregister(micro_ac_power);
  228. batt_err:
  229. cancel_delayed_work_sync(&mb->update);
  230. destroy_workqueue(mb->wq);
  231. return ret;
  232. }
  233. static int micro_batt_remove(struct platform_device *pdev)
  234. {
  235. struct micro_battery *mb = platform_get_drvdata(pdev);
  236. power_supply_unregister(micro_ac_power);
  237. power_supply_unregister(micro_batt_power);
  238. cancel_delayed_work_sync(&mb->update);
  239. destroy_workqueue(mb->wq);
  240. return 0;
  241. }
  242. static int micro_batt_suspend(struct device *dev)
  243. {
  244. struct micro_battery *mb = dev_get_drvdata(dev);
  245. cancel_delayed_work_sync(&mb->update);
  246. return 0;
  247. }
  248. static int micro_batt_resume(struct device *dev)
  249. {
  250. struct micro_battery *mb = dev_get_drvdata(dev);
  251. queue_delayed_work(mb->wq, &mb->update, msecs_to_jiffies(BATT_PERIOD));
  252. return 0;
  253. }
  254. static const struct dev_pm_ops micro_batt_dev_pm_ops = {
  255. SET_SYSTEM_SLEEP_PM_OPS(micro_batt_suspend, micro_batt_resume)
  256. };
  257. static struct platform_driver micro_batt_device_driver = {
  258. .driver = {
  259. .name = "ipaq-micro-battery",
  260. .pm = &micro_batt_dev_pm_ops,
  261. },
  262. .probe = micro_batt_probe,
  263. .remove = micro_batt_remove,
  264. };
  265. module_platform_driver(micro_batt_device_driver);
  266. MODULE_LICENSE("GPL");
  267. MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
  268. MODULE_ALIAS("platform:battery-ipaq-micro");