nvec_power.c 10 KB

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