collie_battery.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Battery and Power Management code for the Sharp SL-5x00
  4. *
  5. * Copyright (C) 2009 Thomas Kunze
  6. *
  7. * based on tosa_battery.c
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/power_supply.h>
  12. #include <linux/delay.h>
  13. #include <linux/spinlock.h>
  14. #include <linux/interrupt.h>
  15. #include <linux/gpio.h>
  16. #include <linux/mfd/ucb1x00.h>
  17. #include <asm/mach/sharpsl_param.h>
  18. #include <asm/mach-types.h>
  19. #include <mach/collie.h>
  20. static DEFINE_MUTEX(bat_lock); /* protects gpio pins */
  21. static struct work_struct bat_work;
  22. static struct ucb1x00 *ucb;
  23. struct collie_bat {
  24. int status;
  25. struct power_supply *psy;
  26. int full_chrg;
  27. struct mutex work_lock; /* protects data */
  28. bool (*is_present)(struct collie_bat *bat);
  29. int gpio_full;
  30. int gpio_charge_on;
  31. int technology;
  32. int gpio_bat;
  33. int adc_bat;
  34. int adc_bat_divider;
  35. int bat_max;
  36. int bat_min;
  37. int gpio_temp;
  38. int adc_temp;
  39. int adc_temp_divider;
  40. };
  41. static struct collie_bat collie_bat_main;
  42. static unsigned long collie_read_bat(struct collie_bat *bat)
  43. {
  44. unsigned long value = 0;
  45. if (bat->gpio_bat < 0 || bat->adc_bat < 0)
  46. return 0;
  47. mutex_lock(&bat_lock);
  48. gpio_set_value(bat->gpio_bat, 1);
  49. msleep(5);
  50. ucb1x00_adc_enable(ucb);
  51. value = ucb1x00_adc_read(ucb, bat->adc_bat, UCB_SYNC);
  52. ucb1x00_adc_disable(ucb);
  53. gpio_set_value(bat->gpio_bat, 0);
  54. mutex_unlock(&bat_lock);
  55. value = value * 1000000 / bat->adc_bat_divider;
  56. return value;
  57. }
  58. static unsigned long collie_read_temp(struct collie_bat *bat)
  59. {
  60. unsigned long value = 0;
  61. if (bat->gpio_temp < 0 || bat->adc_temp < 0)
  62. return 0;
  63. mutex_lock(&bat_lock);
  64. gpio_set_value(bat->gpio_temp, 1);
  65. msleep(5);
  66. ucb1x00_adc_enable(ucb);
  67. value = ucb1x00_adc_read(ucb, bat->adc_temp, UCB_SYNC);
  68. ucb1x00_adc_disable(ucb);
  69. gpio_set_value(bat->gpio_temp, 0);
  70. mutex_unlock(&bat_lock);
  71. value = value * 10000 / bat->adc_temp_divider;
  72. return value;
  73. }
  74. static int collie_bat_get_property(struct power_supply *psy,
  75. enum power_supply_property psp,
  76. union power_supply_propval *val)
  77. {
  78. int ret = 0;
  79. struct collie_bat *bat = power_supply_get_drvdata(psy);
  80. if (bat->is_present && !bat->is_present(bat)
  81. && psp != POWER_SUPPLY_PROP_PRESENT) {
  82. return -ENODEV;
  83. }
  84. switch (psp) {
  85. case POWER_SUPPLY_PROP_STATUS:
  86. val->intval = bat->status;
  87. break;
  88. case POWER_SUPPLY_PROP_TECHNOLOGY:
  89. val->intval = bat->technology;
  90. break;
  91. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  92. val->intval = collie_read_bat(bat);
  93. break;
  94. case POWER_SUPPLY_PROP_VOLTAGE_MAX:
  95. if (bat->full_chrg == -1)
  96. val->intval = bat->bat_max;
  97. else
  98. val->intval = bat->full_chrg;
  99. break;
  100. case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  101. val->intval = bat->bat_max;
  102. break;
  103. case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
  104. val->intval = bat->bat_min;
  105. break;
  106. case POWER_SUPPLY_PROP_TEMP:
  107. val->intval = collie_read_temp(bat);
  108. break;
  109. case POWER_SUPPLY_PROP_PRESENT:
  110. val->intval = bat->is_present ? bat->is_present(bat) : 1;
  111. break;
  112. default:
  113. ret = -EINVAL;
  114. break;
  115. }
  116. return ret;
  117. }
  118. static void collie_bat_external_power_changed(struct power_supply *psy)
  119. {
  120. schedule_work(&bat_work);
  121. }
  122. static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
  123. {
  124. pr_info("collie_bat_gpio irq\n");
  125. schedule_work(&bat_work);
  126. return IRQ_HANDLED;
  127. }
  128. static void collie_bat_update(struct collie_bat *bat)
  129. {
  130. int old;
  131. struct power_supply *psy = bat->psy;
  132. mutex_lock(&bat->work_lock);
  133. old = bat->status;
  134. if (bat->is_present && !bat->is_present(bat)) {
  135. printk(KERN_NOTICE "%s not present\n", psy->desc->name);
  136. bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
  137. bat->full_chrg = -1;
  138. } else if (power_supply_am_i_supplied(psy)) {
  139. if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) {
  140. gpio_set_value(bat->gpio_charge_on, 1);
  141. mdelay(15);
  142. }
  143. if (gpio_get_value(bat->gpio_full)) {
  144. if (old == POWER_SUPPLY_STATUS_CHARGING ||
  145. bat->full_chrg == -1)
  146. bat->full_chrg = collie_read_bat(bat);
  147. gpio_set_value(bat->gpio_charge_on, 0);
  148. bat->status = POWER_SUPPLY_STATUS_FULL;
  149. } else {
  150. gpio_set_value(bat->gpio_charge_on, 1);
  151. bat->status = POWER_SUPPLY_STATUS_CHARGING;
  152. }
  153. } else {
  154. gpio_set_value(bat->gpio_charge_on, 0);
  155. bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
  156. }
  157. if (old != bat->status)
  158. power_supply_changed(psy);
  159. mutex_unlock(&bat->work_lock);
  160. }
  161. static void collie_bat_work(struct work_struct *work)
  162. {
  163. collie_bat_update(&collie_bat_main);
  164. }
  165. static enum power_supply_property collie_bat_main_props[] = {
  166. POWER_SUPPLY_PROP_STATUS,
  167. POWER_SUPPLY_PROP_TECHNOLOGY,
  168. POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
  169. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  170. POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
  171. POWER_SUPPLY_PROP_VOLTAGE_MAX,
  172. POWER_SUPPLY_PROP_PRESENT,
  173. POWER_SUPPLY_PROP_TEMP,
  174. };
  175. static enum power_supply_property collie_bat_bu_props[] = {
  176. POWER_SUPPLY_PROP_STATUS,
  177. POWER_SUPPLY_PROP_TECHNOLOGY,
  178. POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
  179. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  180. POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
  181. POWER_SUPPLY_PROP_VOLTAGE_MAX,
  182. POWER_SUPPLY_PROP_PRESENT,
  183. };
  184. static const struct power_supply_desc collie_bat_main_desc = {
  185. .name = "main-battery",
  186. .type = POWER_SUPPLY_TYPE_BATTERY,
  187. .properties = collie_bat_main_props,
  188. .num_properties = ARRAY_SIZE(collie_bat_main_props),
  189. .get_property = collie_bat_get_property,
  190. .external_power_changed = collie_bat_external_power_changed,
  191. .use_for_apm = 1,
  192. };
  193. static struct collie_bat collie_bat_main = {
  194. .status = POWER_SUPPLY_STATUS_DISCHARGING,
  195. .full_chrg = -1,
  196. .psy = NULL,
  197. .gpio_full = COLLIE_GPIO_CO,
  198. .gpio_charge_on = COLLIE_GPIO_CHARGE_ON,
  199. .technology = POWER_SUPPLY_TECHNOLOGY_LIPO,
  200. .gpio_bat = COLLIE_GPIO_MBAT_ON,
  201. .adc_bat = UCB_ADC_INP_AD1,
  202. .adc_bat_divider = 155,
  203. .bat_max = 4310000,
  204. .bat_min = 1551 * 1000000 / 414,
  205. .gpio_temp = COLLIE_GPIO_TMP_ON,
  206. .adc_temp = UCB_ADC_INP_AD0,
  207. .adc_temp_divider = 10000,
  208. };
  209. static const struct power_supply_desc collie_bat_bu_desc = {
  210. .name = "backup-battery",
  211. .type = POWER_SUPPLY_TYPE_BATTERY,
  212. .properties = collie_bat_bu_props,
  213. .num_properties = ARRAY_SIZE(collie_bat_bu_props),
  214. .get_property = collie_bat_get_property,
  215. .external_power_changed = collie_bat_external_power_changed,
  216. };
  217. static struct collie_bat collie_bat_bu = {
  218. .status = POWER_SUPPLY_STATUS_UNKNOWN,
  219. .full_chrg = -1,
  220. .psy = NULL,
  221. .gpio_full = -1,
  222. .gpio_charge_on = -1,
  223. .technology = POWER_SUPPLY_TECHNOLOGY_LiMn,
  224. .gpio_bat = COLLIE_GPIO_BBAT_ON,
  225. .adc_bat = UCB_ADC_INP_AD1,
  226. .adc_bat_divider = 155,
  227. .bat_max = 3000000,
  228. .bat_min = 1900000,
  229. .gpio_temp = -1,
  230. .adc_temp = -1,
  231. .adc_temp_divider = -1,
  232. };
  233. static struct gpio collie_batt_gpios[] = {
  234. { COLLIE_GPIO_CO, GPIOF_IN, "main battery full" },
  235. { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN, "main battery low" },
  236. { COLLIE_GPIO_CHARGE_ON, GPIOF_OUT_INIT_LOW, "main charge on" },
  237. { COLLIE_GPIO_MBAT_ON, GPIOF_OUT_INIT_LOW, "main battery" },
  238. { COLLIE_GPIO_TMP_ON, GPIOF_OUT_INIT_LOW, "main battery temp" },
  239. { COLLIE_GPIO_BBAT_ON, GPIOF_OUT_INIT_LOW, "backup battery" },
  240. };
  241. #ifdef CONFIG_PM
  242. static int wakeup_enabled;
  243. static int collie_bat_suspend(struct ucb1x00_dev *dev)
  244. {
  245. /* flush all pending status updates */
  246. flush_work(&bat_work);
  247. if (device_may_wakeup(&dev->ucb->dev) &&
  248. collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING)
  249. wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
  250. else
  251. wakeup_enabled = 0;
  252. return 0;
  253. }
  254. static int collie_bat_resume(struct ucb1x00_dev *dev)
  255. {
  256. if (wakeup_enabled)
  257. disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO));
  258. /* things may have changed while we were away */
  259. schedule_work(&bat_work);
  260. return 0;
  261. }
  262. #else
  263. #define collie_bat_suspend NULL
  264. #define collie_bat_resume NULL
  265. #endif
  266. static int collie_bat_probe(struct ucb1x00_dev *dev)
  267. {
  268. int ret;
  269. struct power_supply_config psy_main_cfg = {}, psy_bu_cfg = {};
  270. if (!machine_is_collie())
  271. return -ENODEV;
  272. ucb = dev->ucb;
  273. ret = gpio_request_array(collie_batt_gpios,
  274. ARRAY_SIZE(collie_batt_gpios));
  275. if (ret)
  276. return ret;
  277. mutex_init(&collie_bat_main.work_lock);
  278. INIT_WORK(&bat_work, collie_bat_work);
  279. psy_main_cfg.drv_data = &collie_bat_main;
  280. collie_bat_main.psy = power_supply_register(&dev->ucb->dev,
  281. &collie_bat_main_desc,
  282. &psy_main_cfg);
  283. if (IS_ERR(collie_bat_main.psy)) {
  284. ret = PTR_ERR(collie_bat_main.psy);
  285. goto err_psy_reg_main;
  286. }
  287. psy_bu_cfg.drv_data = &collie_bat_bu;
  288. collie_bat_bu.psy = power_supply_register(&dev->ucb->dev,
  289. &collie_bat_bu_desc,
  290. &psy_bu_cfg);
  291. if (IS_ERR(collie_bat_bu.psy)) {
  292. ret = PTR_ERR(collie_bat_bu.psy);
  293. goto err_psy_reg_bu;
  294. }
  295. ret = request_irq(gpio_to_irq(COLLIE_GPIO_CO),
  296. collie_bat_gpio_isr,
  297. IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
  298. "main full", &collie_bat_main);
  299. if (ret)
  300. goto err_irq;
  301. device_init_wakeup(&ucb->dev, 1);
  302. schedule_work(&bat_work);
  303. return 0;
  304. err_irq:
  305. power_supply_unregister(collie_bat_bu.psy);
  306. err_psy_reg_bu:
  307. power_supply_unregister(collie_bat_main.psy);
  308. err_psy_reg_main:
  309. /* see comment in collie_bat_remove */
  310. cancel_work_sync(&bat_work);
  311. gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
  312. return ret;
  313. }
  314. static void collie_bat_remove(struct ucb1x00_dev *dev)
  315. {
  316. free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
  317. power_supply_unregister(collie_bat_bu.psy);
  318. power_supply_unregister(collie_bat_main.psy);
  319. /*
  320. * Now cancel the bat_work. We won't get any more schedules,
  321. * since all sources (isr and external_power_changed) are
  322. * unregistered now.
  323. */
  324. cancel_work_sync(&bat_work);
  325. gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
  326. }
  327. static struct ucb1x00_driver collie_bat_driver = {
  328. .add = collie_bat_probe,
  329. .remove = collie_bat_remove,
  330. .suspend = collie_bat_suspend,
  331. .resume = collie_bat_resume,
  332. };
  333. static int __init collie_bat_init(void)
  334. {
  335. return ucb1x00_register_driver(&collie_bat_driver);
  336. }
  337. static void __exit collie_bat_exit(void)
  338. {
  339. ucb1x00_unregister_driver(&collie_bat_driver);
  340. }
  341. module_init(collie_bat_init);
  342. module_exit(collie_bat_exit);
  343. MODULE_LICENSE("GPL");
  344. MODULE_AUTHOR("Thomas Kunze");
  345. MODULE_DESCRIPTION("Collie battery driver");