wilco-charger.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Charging control driver for the Wilco EC
  4. *
  5. * Copyright 2019 Google LLC
  6. *
  7. * See Documentation/ABI/testing/sysfs-class-power and
  8. * Documentation/ABI/testing/sysfs-class-power-wilco for userspace interface
  9. * and other info.
  10. */
  11. #include <linux/module.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/platform_data/wilco-ec.h>
  14. #include <linux/power_supply.h>
  15. #define DRV_NAME "wilco-charger"
  16. /* Property IDs and related EC constants */
  17. #define PID_CHARGE_MODE 0x0710
  18. #define PID_CHARGE_LOWER_LIMIT 0x0711
  19. #define PID_CHARGE_UPPER_LIMIT 0x0712
  20. enum charge_mode {
  21. CHARGE_MODE_STD = 1, /* Used for Standard */
  22. CHARGE_MODE_EXP = 2, /* Express Charge, used for Fast */
  23. CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */
  24. CHARGE_MODE_AUTO = 4, /* Used for Adaptive */
  25. CHARGE_MODE_CUSTOM = 5, /* Used for Custom */
  26. CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */
  27. };
  28. #define CHARGE_LOWER_LIMIT_MIN 50
  29. #define CHARGE_LOWER_LIMIT_MAX 95
  30. #define CHARGE_UPPER_LIMIT_MIN 55
  31. #define CHARGE_UPPER_LIMIT_MAX 100
  32. /* Convert from POWER_SUPPLY_PROP_CHARGE_TYPE value to the EC's charge mode */
  33. static int psp_val_to_charge_mode(int psp_val)
  34. {
  35. switch (psp_val) {
  36. case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
  37. return CHARGE_MODE_AC;
  38. case POWER_SUPPLY_CHARGE_TYPE_FAST:
  39. return CHARGE_MODE_EXP;
  40. case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
  41. return CHARGE_MODE_STD;
  42. case POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE:
  43. return CHARGE_MODE_AUTO;
  44. case POWER_SUPPLY_CHARGE_TYPE_CUSTOM:
  45. return CHARGE_MODE_CUSTOM;
  46. case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
  47. return CHARGE_MODE_LONGLIFE;
  48. default:
  49. return -EINVAL;
  50. }
  51. }
  52. /* Convert from EC's charge mode to POWER_SUPPLY_PROP_CHARGE_TYPE value */
  53. static int charge_mode_to_psp_val(enum charge_mode mode)
  54. {
  55. switch (mode) {
  56. case CHARGE_MODE_AC:
  57. return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
  58. case CHARGE_MODE_EXP:
  59. return POWER_SUPPLY_CHARGE_TYPE_FAST;
  60. case CHARGE_MODE_STD:
  61. return POWER_SUPPLY_CHARGE_TYPE_STANDARD;
  62. case CHARGE_MODE_AUTO:
  63. return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE;
  64. case CHARGE_MODE_CUSTOM:
  65. return POWER_SUPPLY_CHARGE_TYPE_CUSTOM;
  66. case CHARGE_MODE_LONGLIFE:
  67. return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
  68. default:
  69. return -EINVAL;
  70. }
  71. }
  72. static enum power_supply_property wilco_charge_props[] = {
  73. POWER_SUPPLY_PROP_CHARGE_TYPE,
  74. POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD,
  75. POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
  76. };
  77. static int wilco_charge_get_property(struct power_supply *psy,
  78. enum power_supply_property psp,
  79. union power_supply_propval *val)
  80. {
  81. struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
  82. u32 property_id;
  83. int ret;
  84. u8 raw;
  85. switch (psp) {
  86. case POWER_SUPPLY_PROP_CHARGE_TYPE:
  87. property_id = PID_CHARGE_MODE;
  88. break;
  89. case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
  90. property_id = PID_CHARGE_LOWER_LIMIT;
  91. break;
  92. case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
  93. property_id = PID_CHARGE_UPPER_LIMIT;
  94. break;
  95. default:
  96. return -EINVAL;
  97. }
  98. ret = wilco_ec_get_byte_property(ec, property_id, &raw);
  99. if (ret < 0)
  100. return ret;
  101. if (property_id == PID_CHARGE_MODE) {
  102. ret = charge_mode_to_psp_val(raw);
  103. if (ret < 0)
  104. return -EBADMSG;
  105. raw = ret;
  106. }
  107. val->intval = raw;
  108. return 0;
  109. }
  110. static int wilco_charge_set_property(struct power_supply *psy,
  111. enum power_supply_property psp,
  112. const union power_supply_propval *val)
  113. {
  114. struct wilco_ec_device *ec = power_supply_get_drvdata(psy);
  115. int mode;
  116. switch (psp) {
  117. case POWER_SUPPLY_PROP_CHARGE_TYPE:
  118. mode = psp_val_to_charge_mode(val->intval);
  119. if (mode < 0)
  120. return -EINVAL;
  121. return wilco_ec_set_byte_property(ec, PID_CHARGE_MODE, mode);
  122. case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD:
  123. if (val->intval < CHARGE_LOWER_LIMIT_MIN ||
  124. val->intval > CHARGE_LOWER_LIMIT_MAX)
  125. return -EINVAL;
  126. return wilco_ec_set_byte_property(ec, PID_CHARGE_LOWER_LIMIT,
  127. val->intval);
  128. case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
  129. if (val->intval < CHARGE_UPPER_LIMIT_MIN ||
  130. val->intval > CHARGE_UPPER_LIMIT_MAX)
  131. return -EINVAL;
  132. return wilco_ec_set_byte_property(ec, PID_CHARGE_UPPER_LIMIT,
  133. val->intval);
  134. default:
  135. return -EINVAL;
  136. }
  137. }
  138. static int wilco_charge_property_is_writeable(struct power_supply *psy,
  139. enum power_supply_property psp)
  140. {
  141. return 1;
  142. }
  143. static const struct power_supply_desc wilco_ps_desc = {
  144. .properties = wilco_charge_props,
  145. .num_properties = ARRAY_SIZE(wilco_charge_props),
  146. .get_property = wilco_charge_get_property,
  147. .set_property = wilco_charge_set_property,
  148. .property_is_writeable = wilco_charge_property_is_writeable,
  149. .name = DRV_NAME,
  150. .type = POWER_SUPPLY_TYPE_MAINS,
  151. };
  152. static int wilco_charge_probe(struct platform_device *pdev)
  153. {
  154. struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
  155. struct power_supply_config psy_cfg = {};
  156. struct power_supply *psy;
  157. psy_cfg.drv_data = ec;
  158. psy = devm_power_supply_register(&pdev->dev, &wilco_ps_desc, &psy_cfg);
  159. return PTR_ERR_OR_ZERO(psy);
  160. }
  161. static struct platform_driver wilco_charge_driver = {
  162. .probe = wilco_charge_probe,
  163. .driver = {
  164. .name = DRV_NAME,
  165. }
  166. };
  167. module_platform_driver(wilco_charge_driver);
  168. MODULE_ALIAS("platform:" DRV_NAME);
  169. MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
  170. MODULE_LICENSE("GPL v2");
  171. MODULE_DESCRIPTION("Wilco EC charge control driver");