ipaq_micro_battery.c 7.8 KB

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