nvec_power.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /*
  2. * nvec_power: power supply driver for a NVIDIA compliant embedded controller
  3. *
  4. * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
  5. *
  6. * Authors: Ilya Petrov <ilya.muromec@gmail.com>
  7. * Marc Dietrich <marvin24@gmx.de>
  8. *
  9. * This file is subject to the terms and conditions of the GNU General Public
  10. * License. See the file "COPYING" in the main directory of this archive
  11. * for more details.
  12. *
  13. */
  14. #include <linux/module.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/err.h>
  17. #include <linux/power_supply.h>
  18. #include <linux/slab.h>
  19. #include <linux/workqueue.h>
  20. #include <linux/delay.h>
  21. #include "nvec.h"
  22. #define GET_SYSTEM_STATUS 0x00
  23. struct nvec_power {
  24. struct notifier_block notifier;
  25. struct delayed_work poller;
  26. struct nvec_chip *nvec;
  27. int on;
  28. int bat_present;
  29. int bat_status;
  30. int bat_voltage_now;
  31. int bat_current_now;
  32. int bat_current_avg;
  33. int time_remain;
  34. int charge_full_design;
  35. int charge_last_full;
  36. int critical_capacity;
  37. int capacity_remain;
  38. int bat_temperature;
  39. int bat_cap;
  40. int bat_type_enum;
  41. char bat_manu[30];
  42. char bat_model[30];
  43. char bat_type[30];
  44. };
  45. enum {
  46. SLOT_STATUS,
  47. VOLTAGE,
  48. TIME_REMAINING,
  49. CURRENT,
  50. AVERAGE_CURRENT,
  51. AVERAGING_TIME_INTERVAL,
  52. CAPACITY_REMAINING,
  53. LAST_FULL_CHARGE_CAPACITY,
  54. DESIGN_CAPACITY,
  55. CRITICAL_CAPACITY,
  56. TEMPERATURE,
  57. MANUFACTURER,
  58. MODEL,
  59. TYPE,
  60. };
  61. enum {
  62. AC,
  63. BAT,
  64. };
  65. struct bat_response {
  66. u8 event_type;
  67. u8 length;
  68. u8 sub_type;
  69. u8 status;
  70. /* payload */
  71. union {
  72. char plc[30];
  73. u16 plu;
  74. s16 pls;
  75. };
  76. };
  77. static struct power_supply *nvec_bat_psy;
  78. static struct power_supply *nvec_psy;
  79. static int nvec_power_notifier(struct notifier_block *nb,
  80. unsigned long event_type, void *data)
  81. {
  82. struct nvec_power *power =
  83. container_of(nb, struct nvec_power, notifier);
  84. struct bat_response *res = (struct bat_response *)data;
  85. if (event_type != NVEC_SYS)
  86. return NOTIFY_DONE;
  87. if (res->sub_type == 0) {
  88. if (power->on != res->plu) {
  89. power->on = res->plu;
  90. power_supply_changed(nvec_psy);
  91. }
  92. return NOTIFY_STOP;
  93. }
  94. return NOTIFY_OK;
  95. }
  96. static const int bat_init[] = {
  97. LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
  98. MANUFACTURER, MODEL, TYPE,
  99. };
  100. static void get_bat_mfg_data(struct nvec_power *power)
  101. {
  102. int i;
  103. char buf[] = { NVEC_BAT, SLOT_STATUS };
  104. for (i = 0; i < ARRAY_SIZE(bat_init); i++) {
  105. buf[1] = bat_init[i];
  106. nvec_write_async(power->nvec, buf, 2);
  107. }
  108. }
  109. static int nvec_power_bat_notifier(struct notifier_block *nb,
  110. unsigned long event_type, void *data)
  111. {
  112. struct nvec_power *power =
  113. container_of(nb, struct nvec_power, notifier);
  114. struct bat_response *res = (struct bat_response *)data;
  115. int status_changed = 0;
  116. if (event_type != NVEC_BAT)
  117. return NOTIFY_DONE;
  118. switch (res->sub_type) {
  119. case SLOT_STATUS:
  120. if (res->plc[0] & 1) {
  121. if (power->bat_present == 0) {
  122. status_changed = 1;
  123. get_bat_mfg_data(power);
  124. }
  125. power->bat_present = 1;
  126. switch ((res->plc[0] >> 1) & 3) {
  127. case 0:
  128. power->bat_status =
  129. POWER_SUPPLY_STATUS_NOT_CHARGING;
  130. break;
  131. case 1:
  132. power->bat_status =
  133. POWER_SUPPLY_STATUS_CHARGING;
  134. break;
  135. case 2:
  136. power->bat_status =
  137. POWER_SUPPLY_STATUS_DISCHARGING;
  138. break;
  139. default:
  140. power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
  141. }
  142. } else {
  143. if (power->bat_present == 1)
  144. status_changed = 1;
  145. power->bat_present = 0;
  146. power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
  147. }
  148. power->bat_cap = res->plc[1];
  149. if (status_changed)
  150. power_supply_changed(nvec_bat_psy);
  151. break;
  152. case VOLTAGE:
  153. power->bat_voltage_now = res->plu * 1000;
  154. break;
  155. case TIME_REMAINING:
  156. power->time_remain = res->plu * 3600;
  157. break;
  158. case CURRENT:
  159. power->bat_current_now = res->pls * 1000;
  160. break;
  161. case AVERAGE_CURRENT:
  162. power->bat_current_avg = res->pls * 1000;
  163. break;
  164. case CAPACITY_REMAINING:
  165. power->capacity_remain = res->plu * 1000;
  166. break;
  167. case LAST_FULL_CHARGE_CAPACITY:
  168. power->charge_last_full = res->plu * 1000;
  169. break;
  170. case DESIGN_CAPACITY:
  171. power->charge_full_design = res->plu * 1000;
  172. break;
  173. case CRITICAL_CAPACITY:
  174. power->critical_capacity = res->plu * 1000;
  175. break;
  176. case TEMPERATURE:
  177. power->bat_temperature = res->plu - 2732;
  178. break;
  179. case MANUFACTURER:
  180. memcpy(power->bat_manu, &res->plc, res->length - 2);
  181. power->bat_model[res->length - 2] = '\0';
  182. break;
  183. case MODEL:
  184. memcpy(power->bat_model, &res->plc, res->length - 2);
  185. power->bat_model[res->length - 2] = '\0';
  186. break;
  187. case TYPE:
  188. memcpy(power->bat_type, &res->plc, res->length - 2);
  189. power->bat_type[res->length - 2] = '\0';
  190. /* this differs a little from the spec
  191. fill in more if you find some */
  192. if (!strncmp(power->bat_type, "Li", 30))
  193. power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
  194. else
  195. power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
  196. break;
  197. default:
  198. return NOTIFY_STOP;
  199. }
  200. return NOTIFY_STOP;
  201. }
  202. static int nvec_power_get_property(struct power_supply *psy,
  203. enum power_supply_property psp,
  204. union power_supply_propval *val)
  205. {
  206. struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
  207. switch (psp) {
  208. case POWER_SUPPLY_PROP_ONLINE:
  209. val->intval = power->on;
  210. break;
  211. default:
  212. return -EINVAL;
  213. }
  214. return 0;
  215. }
  216. static int nvec_battery_get_property(struct power_supply *psy,
  217. enum power_supply_property psp,
  218. union power_supply_propval *val)
  219. {
  220. struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
  221. switch (psp) {
  222. case POWER_SUPPLY_PROP_STATUS:
  223. val->intval = power->bat_status;
  224. break;
  225. case POWER_SUPPLY_PROP_CAPACITY:
  226. val->intval = power->bat_cap;
  227. break;
  228. case POWER_SUPPLY_PROP_PRESENT:
  229. val->intval = power->bat_present;
  230. break;
  231. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  232. val->intval = power->bat_voltage_now;
  233. break;
  234. case POWER_SUPPLY_PROP_CURRENT_NOW:
  235. val->intval = power->bat_current_now;
  236. break;
  237. case POWER_SUPPLY_PROP_CURRENT_AVG:
  238. val->intval = power->bat_current_avg;
  239. break;
  240. case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
  241. val->intval = power->time_remain;
  242. break;
  243. case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
  244. val->intval = power->charge_full_design;
  245. break;
  246. case POWER_SUPPLY_PROP_CHARGE_FULL:
  247. val->intval = power->charge_last_full;
  248. break;
  249. case POWER_SUPPLY_PROP_CHARGE_EMPTY:
  250. val->intval = power->critical_capacity;
  251. break;
  252. case POWER_SUPPLY_PROP_CHARGE_NOW:
  253. val->intval = power->capacity_remain;
  254. break;
  255. case POWER_SUPPLY_PROP_TEMP:
  256. val->intval = power->bat_temperature;
  257. break;
  258. case POWER_SUPPLY_PROP_MANUFACTURER:
  259. val->strval = power->bat_manu;
  260. break;
  261. case POWER_SUPPLY_PROP_MODEL_NAME:
  262. val->strval = power->bat_model;
  263. break;
  264. case POWER_SUPPLY_PROP_TECHNOLOGY:
  265. val->intval = power->bat_type_enum;
  266. break;
  267. default:
  268. return -EINVAL;
  269. }
  270. return 0;
  271. }
  272. static enum power_supply_property nvec_power_props[] = {
  273. POWER_SUPPLY_PROP_ONLINE,
  274. };
  275. static enum power_supply_property nvec_battery_props[] = {
  276. POWER_SUPPLY_PROP_STATUS,
  277. POWER_SUPPLY_PROP_PRESENT,
  278. POWER_SUPPLY_PROP_CAPACITY,
  279. POWER_SUPPLY_PROP_VOLTAGE_NOW,
  280. POWER_SUPPLY_PROP_CURRENT_NOW,
  281. #ifdef EC_FULL_DIAG
  282. POWER_SUPPLY_PROP_CURRENT_AVG,
  283. POWER_SUPPLY_PROP_TEMP,
  284. POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
  285. #endif
  286. POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
  287. POWER_SUPPLY_PROP_CHARGE_FULL,
  288. POWER_SUPPLY_PROP_CHARGE_EMPTY,
  289. POWER_SUPPLY_PROP_CHARGE_NOW,
  290. POWER_SUPPLY_PROP_MANUFACTURER,
  291. POWER_SUPPLY_PROP_MODEL_NAME,
  292. POWER_SUPPLY_PROP_TECHNOLOGY,
  293. };
  294. static char *nvec_power_supplied_to[] = {
  295. "battery",
  296. };
  297. static const struct power_supply_desc nvec_bat_psy_desc = {
  298. .name = "battery",
  299. .type = POWER_SUPPLY_TYPE_BATTERY,
  300. .properties = nvec_battery_props,
  301. .num_properties = ARRAY_SIZE(nvec_battery_props),
  302. .get_property = nvec_battery_get_property,
  303. };
  304. static const struct power_supply_desc nvec_psy_desc = {
  305. .name = "ac",
  306. .type = POWER_SUPPLY_TYPE_MAINS,
  307. .properties = nvec_power_props,
  308. .num_properties = ARRAY_SIZE(nvec_power_props),
  309. .get_property = nvec_power_get_property,
  310. };
  311. static int counter;
  312. static int const bat_iter[] = {
  313. SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
  314. #ifdef EC_FULL_DIAG
  315. AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
  316. #endif
  317. };
  318. static void nvec_power_poll(struct work_struct *work)
  319. {
  320. char buf[] = { NVEC_SYS, GET_SYSTEM_STATUS };
  321. struct nvec_power *power = container_of(work, struct nvec_power,
  322. poller.work);
  323. if (counter >= ARRAY_SIZE(bat_iter))
  324. counter = 0;
  325. /* AC status via sys req */
  326. nvec_write_async(power->nvec, buf, 2);
  327. msleep(100);
  328. /* select a battery request function via round robin
  329. doing it all at once seems to overload the power supply */
  330. buf[0] = NVEC_BAT;
  331. buf[1] = bat_iter[counter++];
  332. nvec_write_async(power->nvec, buf, 2);
  333. schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
  334. };
  335. static int nvec_power_probe(struct platform_device *pdev)
  336. {
  337. struct power_supply **psy;
  338. const struct power_supply_desc *psy_desc;
  339. struct nvec_power *power;
  340. struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
  341. struct power_supply_config psy_cfg = {};
  342. power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT);
  343. if (!power)
  344. return -ENOMEM;
  345. dev_set_drvdata(&pdev->dev, power);
  346. power->nvec = nvec;
  347. switch (pdev->id) {
  348. case AC:
  349. psy = &nvec_psy;
  350. psy_desc = &nvec_psy_desc;
  351. psy_cfg.supplied_to = nvec_power_supplied_to;
  352. psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to);
  353. power->notifier.notifier_call = nvec_power_notifier;
  354. INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
  355. schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
  356. break;
  357. case BAT:
  358. psy = &nvec_bat_psy;
  359. psy_desc = &nvec_bat_psy_desc;
  360. power->notifier.notifier_call = nvec_power_bat_notifier;
  361. break;
  362. default:
  363. return -ENODEV;
  364. }
  365. nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
  366. if (pdev->id == BAT)
  367. get_bat_mfg_data(power);
  368. *psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
  369. return PTR_ERR_OR_ZERO(*psy);
  370. }
  371. static int nvec_power_remove(struct platform_device *pdev)
  372. {
  373. struct nvec_power *power = platform_get_drvdata(pdev);
  374. cancel_delayed_work_sync(&power->poller);
  375. nvec_unregister_notifier(power->nvec, &power->notifier);
  376. switch (pdev->id) {
  377. case AC:
  378. power_supply_unregister(nvec_psy);
  379. break;
  380. case BAT:
  381. power_supply_unregister(nvec_bat_psy);
  382. }
  383. return 0;
  384. }
  385. static struct platform_driver nvec_power_driver = {
  386. .probe = nvec_power_probe,
  387. .remove = nvec_power_remove,
  388. .driver = {
  389. .name = "nvec-power",
  390. }
  391. };
  392. module_platform_driver(nvec_power_driver);
  393. MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
  394. MODULE_LICENSE("GPL");
  395. MODULE_DESCRIPTION("NVEC battery and AC driver");
  396. MODULE_ALIAS("platform:nvec-power");