of_batterydata.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #define pr_fmt(fmt) "%s: " fmt, __func__
  13. #include <linux/of.h>
  14. #include <linux/slab.h>
  15. #include <linux/module.h>
  16. #include <linux/types.h>
  17. #include <linux/batterydata-lib.h>
  18. static int of_batterydata_read_lut(const struct device_node *np,
  19. int max_cols, int max_rows, int *ncols, int *nrows,
  20. int *col_legend_data, int *row_legend_data,
  21. int *lut_data)
  22. {
  23. struct property *prop;
  24. const __be32 *data;
  25. int cols, rows, size, i, j, *out_values;
  26. prop = of_find_property(np, "qcom,lut-col-legend", NULL);
  27. if (!prop) {
  28. pr_err("%s: No col legend found\n", np->name);
  29. return -EINVAL;
  30. } else if (!prop->value) {
  31. pr_err("%s: No col legend value found, np->name\n", np->name);
  32. return -ENODATA;
  33. } else if (prop->length > max_cols * sizeof(int)) {
  34. pr_err("%s: Too many columns\n", np->name);
  35. return -EINVAL;
  36. }
  37. cols = prop->length/sizeof(int);
  38. *ncols = cols;
  39. data = prop->value;
  40. for (i = 0; i < cols; i++)
  41. *col_legend_data++ = be32_to_cpup(data++);
  42. prop = of_find_property(np, "qcom,lut-row-legend", NULL);
  43. if (!prop || row_legend_data == NULL) {
  44. /* single row lut */
  45. rows = 1;
  46. } else if (!prop->value) {
  47. pr_err("%s: No row legend value found\n", np->name);
  48. return -ENODATA;
  49. } else if (prop->length > max_rows * sizeof(int)) {
  50. pr_err("%s: Too many rows\n", np->name);
  51. return -EINVAL;
  52. } else {
  53. rows = prop->length/sizeof(int);
  54. *nrows = rows;
  55. data = prop->value;
  56. for (i = 0; i < rows; i++)
  57. *row_legend_data++ = be32_to_cpup(data++);
  58. }
  59. prop = of_find_property(np, "qcom,lut-data", NULL);
  60. if (!prop) {
  61. pr_err("%s: No lut data found\n", np->name);
  62. return -EINVAL;
  63. }
  64. data = prop->value;
  65. size = prop->length/sizeof(int);
  66. if (size != cols * rows) {
  67. pr_err("%s: data size mismatch, %dx%d != %d\n",
  68. np->name, cols, rows, size);
  69. return -EINVAL;
  70. }
  71. for (i = 0; i < rows; i++) {
  72. out_values = lut_data + (max_cols * i);
  73. for (j = 0; j < cols; j++) {
  74. *out_values++ = be32_to_cpup(data++);
  75. pr_debug("Value = %d\n", *(out_values-1));
  76. }
  77. }
  78. return 0;
  79. }
  80. static int of_batterydata_read_sf_lut(struct device_node *data_node,
  81. const char *name, struct sf_lut *lut)
  82. {
  83. struct device_node *node = of_find_node_by_name(data_node, name);
  84. int rc;
  85. if (!lut) {
  86. pr_debug("No lut provided, skipping\n");
  87. return 0;
  88. } else if (!node) {
  89. pr_err("Couldn't find %s node.\n", name);
  90. return -EINVAL;
  91. }
  92. rc = of_batterydata_read_lut(node, PC_CC_COLS, PC_CC_ROWS,
  93. &lut->cols, &lut->rows, lut->row_entries,
  94. lut->percent, *lut->sf);
  95. if (rc) {
  96. pr_err("Failed to read %s node.\n", name);
  97. return rc;
  98. }
  99. return 0;
  100. }
  101. static int of_batterydata_read_pc_temp_ocv_lut(struct device_node *data_node,
  102. const char *name, struct pc_temp_ocv_lut *lut)
  103. {
  104. struct device_node *node = of_find_node_by_name(data_node, name);
  105. int rc;
  106. if (!lut) {
  107. pr_debug("No lut provided, skipping\n");
  108. return 0;
  109. } else if (!node) {
  110. pr_err("Couldn't find %s node.\n", name);
  111. return -EINVAL;
  112. }
  113. rc = of_batterydata_read_lut(node, PC_TEMP_COLS, PC_TEMP_ROWS,
  114. &lut->cols, &lut->rows, lut->temp, lut->percent,
  115. *lut->ocv);
  116. if (rc) {
  117. pr_err("Failed to read %s node.\n", name);
  118. return rc;
  119. }
  120. return 0;
  121. }
  122. static int of_batterydata_read_single_row_lut(struct device_node *data_node,
  123. const char *name, struct single_row_lut *lut)
  124. {
  125. struct device_node *node = of_find_node_by_name(data_node, name);
  126. int rc;
  127. if (!lut) {
  128. pr_debug("No lut provided, skipping\n");
  129. return 0;
  130. } else if (!node) {
  131. pr_err("Couldn't find %s node.\n", name);
  132. return -EINVAL;
  133. }
  134. rc = of_batterydata_read_lut(node, MAX_SINGLE_LUT_COLS, 1,
  135. &lut->cols, NULL, lut->x, NULL, lut->y);
  136. if (rc) {
  137. pr_err("Failed to read %s node.\n", name);
  138. return rc;
  139. }
  140. return 0;
  141. }
  142. static int of_batterydata_read_batt_id_kohm(const struct device_node *np,
  143. const char *propname, struct batt_ids *batt_ids)
  144. {
  145. struct property *prop;
  146. const __be32 *data;
  147. int num, i, *id_kohm = batt_ids->kohm;
  148. prop = of_find_property(np, "qcom,batt-id-kohm", NULL);
  149. if (!prop) {
  150. pr_err("%s: No battery id resistor found\n", np->name);
  151. return -EINVAL;
  152. } else if (!prop->value) {
  153. pr_err("%s: No battery id resistor value found, np->name\n",
  154. np->name);
  155. return -ENODATA;
  156. } else if (prop->length > MAX_BATT_ID_NUM * sizeof(__be32)) {
  157. pr_err("%s: Too many battery id resistors\n", np->name);
  158. return -EINVAL;
  159. }
  160. num = prop->length/sizeof(__be32);
  161. batt_ids->num = num;
  162. data = prop->value;
  163. for (i = 0; i < num; i++)
  164. *id_kohm++ = be32_to_cpup(data++);
  165. return 0;
  166. }
  167. #define OF_PROP_READ(property, qpnp_dt_property, node, rc, optional) \
  168. do { \
  169. if (rc) \
  170. break; \
  171. rc = of_property_read_u32(node, "qcom," qpnp_dt_property, \
  172. &property); \
  173. \
  174. if ((rc == -EINVAL) && optional) { \
  175. property = -EINVAL; \
  176. rc = 0; \
  177. } else if (rc) { \
  178. pr_err("Error reading " #qpnp_dt_property \
  179. " property rc = %d\n", rc); \
  180. } \
  181. } while (0)
  182. static int of_batterydata_load_battery_data(struct device_node *node,
  183. int best_id_kohm,
  184. struct bms_battery_data *batt_data)
  185. {
  186. int rc;
  187. rc = of_batterydata_read_single_row_lut(node, "qcom,fcc-temp-lut",
  188. batt_data->fcc_temp_lut);
  189. if (rc)
  190. return rc;
  191. rc = of_batterydata_read_pc_temp_ocv_lut(node,
  192. "qcom,pc-temp-ocv-lut",
  193. batt_data->pc_temp_ocv_lut);
  194. if (rc)
  195. return rc;
  196. rc = of_batterydata_read_sf_lut(node, "qcom,rbatt-sf-lut",
  197. batt_data->rbatt_sf_lut);
  198. if (rc)
  199. return rc;
  200. OF_PROP_READ(batt_data->fcc, "fcc-mah", node, rc, false);
  201. OF_PROP_READ(batt_data->default_rbatt_mohm,
  202. "default-rbatt-mohm", node, rc, false);
  203. OF_PROP_READ(batt_data->rbatt_capacitive_mohm,
  204. "rbatt-capacitive-mohm", node, rc, false);
  205. OF_PROP_READ(batt_data->flat_ocv_threshold_uv,
  206. "flat-ocv-threshold-uv", node, rc, true);
  207. OF_PROP_READ(batt_data->max_voltage_uv,
  208. "max-voltage-uv", node, rc, true);
  209. OF_PROP_READ(batt_data->cutoff_uv, "v-cutoff-uv", node, rc, true);
  210. OF_PROP_READ(batt_data->iterm_ua, "chg-term-ua", node, rc, true);
  211. batt_data->batt_id_kohm = best_id_kohm;
  212. return rc;
  213. }
  214. static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv,
  215. int rpull_up, int vadc_vdd)
  216. {
  217. int64_t resistor_value_kohm, denom;
  218. if (batt_id_uv == 0) {
  219. /* vadc not correct or batt id line grounded, report 0 kohms */
  220. return 0;
  221. }
  222. /* calculate the battery id resistance reported via ADC */
  223. denom = div64_s64(vadc_vdd * 1000000LL, batt_id_uv) - 1000000LL;
  224. if (denom == 0) {
  225. /* batt id connector might be open, return 0 kohms */
  226. return 0;
  227. }
  228. resistor_value_kohm = div64_s64(rpull_up * 1000000LL + denom/2, denom);
  229. pr_debug("batt id voltage = %d, resistor value = %lld\n",
  230. batt_id_uv, resistor_value_kohm);
  231. return resistor_value_kohm;
  232. }
  233. int of_batterydata_read_data(struct device_node *batterydata_container_node,
  234. struct bms_battery_data *batt_data,
  235. int batt_id_uv)
  236. {
  237. struct device_node *node, *best_node;
  238. struct batt_ids batt_ids;
  239. int delta, best_delta, batt_id_kohm, rpull_up_kohm,
  240. vadc_vdd_uv, best_id_kohm, i, rc = 0;
  241. node = batterydata_container_node;
  242. OF_PROP_READ(rpull_up_kohm, "rpull-up-kohm", node, rc, false);
  243. OF_PROP_READ(vadc_vdd_uv, "vref-batt-therm", node, rc, false);
  244. if (rc)
  245. return rc;
  246. batt_id_kohm = of_batterydata_convert_battery_id_kohm(batt_id_uv,
  247. rpull_up_kohm, vadc_vdd_uv);
  248. best_node = NULL;
  249. best_delta = 0;
  250. best_id_kohm = 0;
  251. /*
  252. * Find the battery data with a battery id resistor closest to this one
  253. */
  254. for_each_child_of_node(batterydata_container_node, node) {
  255. rc = of_batterydata_read_batt_id_kohm(node,
  256. "qcom,batt-id-kohm",
  257. &batt_ids);
  258. if (rc)
  259. continue;
  260. for (i = 0; i < batt_ids.num; i++) {
  261. delta = abs(batt_ids.kohm[i] - batt_id_kohm);
  262. if (delta < best_delta || !best_node) {
  263. best_node = node;
  264. best_delta = delta;
  265. best_id_kohm = batt_ids.kohm[i];
  266. }
  267. }
  268. }
  269. if (best_node == NULL) {
  270. pr_err("No battery data found\n");
  271. return -ENODATA;
  272. }
  273. return of_batterydata_load_battery_data(best_node,
  274. best_id_kohm, batt_data);
  275. }
  276. MODULE_LICENSE("GPL v2");