PageRenderTime 23ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/power/s2mg001_fuelgauge.c

https://gitlab.com/SerenityS/android_kernel_exynos3475
C | 423 lines | 308 code | 81 blank | 34 comment | 38 complexity | 8e14f75491ea9f337a6a7bd5c31e5105 MD5 | raw file
  1. /*
  2. * s2mg001_fuelgauge.c
  3. * Samsung S2MG001 Fuel Gauge Driver
  4. *
  5. * Copyright (C) 2012 Samsung Electronics
  6. *
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #define DEBUG
  13. #include <linux/power/sec_fuelgauge.h>
  14. static int s2mg001_write_reg(struct i2c_client *client, int reg, u8 *buf)
  15. {
  16. int ret;
  17. ret = i2c_smbus_write_i2c_block_data(client, reg, 2, buf);
  18. if (ret < 0)
  19. dev_err(&client->dev, "%s: Error(%d)\n", __func__, ret);
  20. return ret;
  21. }
  22. static int s2mg001_read_reg(struct i2c_client *client, int reg, u8 *buf)
  23. {
  24. int ret = 0;
  25. buf[0] = i2c_smbus_read_byte_data(client, reg);
  26. if (buf[0] < 0)
  27. dev_err(&client->dev, "%s: err %d\n", __func__, ret);
  28. buf[1] = i2c_smbus_read_byte_data(client, reg + 1);
  29. if (buf[1] < 0)
  30. dev_err(&client->dev, "%s: err %d\n", __func__, ret);
  31. return 0;
  32. }
  33. static int s2mg001_init_regs(struct i2c_client *client)
  34. {
  35. int ret = 0;
  36. u8 data;
  37. ret = i2c_smbus_write_byte_data(client, S2MG001_REG_START, 0x03);
  38. if (ret < 0)
  39. dev_err(&client->dev, "%s: Error(%d)\n", __func__, ret);
  40. data = i2c_smbus_read_byte_data(client, 0x2e);
  41. data &= ~(0x01 << 3);
  42. ret = i2c_smbus_write_byte_data(client, 0x2e, data);
  43. if (ret < 0)
  44. dev_err(&client->dev, "%s: Error(%d)\n", __func__, ret);
  45. return ret;
  46. }
  47. static void s2mg001_alert_init(struct i2c_client *client)
  48. {
  49. struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client);
  50. u8 data[2];
  51. /* VBAT Threshold setting */
  52. data[0] = 0x00 & 0x0f;
  53. /* SOC Threshold setting */
  54. data[0] = data[0] | (fuelgauge->pdata->fuel_alert_soc << 4);
  55. data[1] = 0x00;
  56. s2mg001_write_reg(client, S2MG001_REG_IRQ_LVL, data);
  57. }
  58. static bool s2mg001_check_status(struct i2c_client *client)
  59. {
  60. u8 data[2];
  61. bool ret = false;
  62. /* check if Smn was generated */
  63. if (s2mg001_read_reg(client, S2MG001_REG_STATUS, data) < 0)
  64. return ret;
  65. dev_dbg(&client->dev, "%s: status to (%02x%02x)\n",
  66. __func__, data[1], data[0]);
  67. if (data[1] & (0x1 << 1))
  68. return true;
  69. else
  70. return false;
  71. }
  72. static int s2mg001_set_temperature(struct i2c_client *client, int temperature)
  73. {
  74. u8 data[2];
  75. char val;
  76. val = temperature / 10;
  77. data[0] = val;
  78. data[1] = 0x00;
  79. s2mg001_write_reg(client, S2MG001_REG_RTEMP, data);
  80. dev_dbg(&client->dev, "%s: temperature to (%d)\n",
  81. __func__, temperature);
  82. return temperature;
  83. }
  84. static int s2mg001_get_temperature(struct i2c_client *client)
  85. {
  86. u8 data[2];
  87. s32 temperature = 0;
  88. if (s2mg001_read_reg(client, S2MG001_REG_RTEMP, data) < 0)
  89. return -ERANGE;
  90. /* data[] store 2's compliment format number */
  91. if (data[0] & (0x1 << 7)) {
  92. /* Negative */
  93. temperature = ((~(data[0])) & 0xFF) + 1;
  94. temperature *= -10;
  95. } else {
  96. temperature = data[0] & 0x7F;
  97. temperature *= 10;
  98. }
  99. dev_dbg(&client->dev, "%s: temperature (%d)\n",
  100. __func__, temperature);
  101. return temperature;
  102. }
  103. /* soc should be 0.01% unit */
  104. static int s2mg001_get_soc(struct i2c_client *client)
  105. {
  106. u8 data[2], check_data[2];
  107. u16 compliment;
  108. int rsoc, i;
  109. for (i = 0; i < 50; i++) {
  110. if (s2mg001_read_reg(client, S2MG001_REG_RSOC, data) < 0)
  111. return -EINVAL;
  112. if (s2mg001_read_reg(client, S2MG001_REG_RSOC, check_data) < 0)
  113. return -EINVAL;
  114. if ((data[0] == check_data[0]) && (data[1] == check_data[1]))
  115. break;
  116. }
  117. compliment = (data[1] << 8) | (data[0]);
  118. /* data[] store 2's compliment format number */
  119. if (compliment & (0x1 << 15)) {
  120. /* Negative */
  121. rsoc = ((~compliment) & 0xFFFF) + 1;
  122. rsoc = (rsoc * (-10000)) / (0x1 << 12);
  123. } else {
  124. rsoc = compliment & 0x7FFF;
  125. rsoc = ((rsoc * 10000) / (0x1 << 12));
  126. }
  127. dev_info(&client->dev, "%s: raw capacity (0x%x:%d)\n", __func__,
  128. compliment, rsoc);
  129. return min(rsoc, 10000);
  130. }
  131. static int s2mg001_get_ocv(struct i2c_client *client)
  132. {
  133. u8 data[2];
  134. u32 rocv = 0;
  135. if (s2mg001_read_reg(client, S2MG001_REG_ROCV, data) < 0)
  136. return -EINVAL;
  137. rocv = ((data[0] + (data[1] << 8)) * 1000) >> 13;
  138. dev_dbg(&client->dev, "%s: rocv (%d)\n", __func__, rocv);
  139. return rocv;
  140. }
  141. static int s2mg001_get_vbat(struct i2c_client *client)
  142. {
  143. u8 data[2];
  144. u32 vbat = 0;
  145. if (s2mg001_read_reg(client, S2MG001_REG_RVBAT, data) < 0)
  146. return -EINVAL;
  147. vbat = ((data[0] + (data[1] << 8)) * 1000) >> 13;
  148. dev_dbg(&client->dev, "%s: vbat (%d)\n", __func__, vbat);
  149. return vbat;
  150. }
  151. static int s2mg001_get_avgvbat(struct i2c_client *client)
  152. {
  153. u8 data[2];
  154. u32 new_vbat, old_vbat = 0;
  155. int cnt;
  156. for (cnt = 0; cnt < 5; cnt++) {
  157. if (s2mg001_read_reg(client, S2MG001_REG_RVBAT, data) < 0)
  158. return -EINVAL;
  159. new_vbat = ((data[0] + (data[1] << 8)) * 1000) >> 13;
  160. if (cnt == 0)
  161. old_vbat = new_vbat;
  162. else
  163. old_vbat = new_vbat / 2 + old_vbat / 2;
  164. }
  165. dev_dbg(&client->dev, "%s: avgvbat (%d)\n", __func__, old_vbat);
  166. return old_vbat;
  167. }
  168. bool sec_hal_fg_init(struct i2c_client *client)
  169. {
  170. /* initialize fuel gauge registers */
  171. s2mg001_init_regs(client);
  172. return true;
  173. }
  174. bool sec_hal_fg_suspend(struct i2c_client *client)
  175. {
  176. return true;
  177. }
  178. bool sec_hal_fg_resume(struct i2c_client *client)
  179. {
  180. return true;
  181. }
  182. bool sec_hal_fg_reset(struct i2c_client *client)
  183. {
  184. return true;
  185. }
  186. bool sec_hal_fg_fuelalert_init(struct i2c_client *client, int soc)
  187. {
  188. struct sec_fuelgauge_info *fuelgauge = i2c_get_clientdata(client);
  189. u8 data[2];
  190. /* 1. Set s2mg001 alert configuration. */
  191. s2mg001_alert_init(client);
  192. if (s2mg001_read_reg(client, S2MG001_REG_IRQ, data) < 0)
  193. return -1;
  194. /*Enable VBAT, SOC */
  195. data[1] &= 0xfc;
  196. /*Disable IDLE_ST, INIT)ST */
  197. data[1] |= 0x0c;
  198. s2mg001_write_reg(client, S2MG001_REG_IRQ, data);
  199. dev_dbg(&client->dev, "%s: irq_reg(%02x%02x) irq(%d)\n",
  200. __func__, data[1], data[0], fuelgauge->pdata->fg_irq);
  201. return true;
  202. }
  203. bool sec_hal_fg_is_fuelalerted(struct i2c_client *client)
  204. {
  205. return s2mg001_check_status(client);
  206. }
  207. bool sec_hal_fg_fuelalert_process(void *irq_data, bool is_fuel_alerted)
  208. {
  209. struct sec_fuelgauge_info *fuelgauge = irq_data;
  210. int ret;
  211. ret = i2c_smbus_write_byte_data(fuelgauge->client, S2MG001_REG_IRQ, 0x00);
  212. if (ret < 0)
  213. dev_err(&fuelgauge->client->dev, "%s: Error(%d)\n", __func__, ret);
  214. return ret;
  215. }
  216. bool sec_hal_fg_full_charged(struct i2c_client *client)
  217. {
  218. return true;
  219. }
  220. bool sec_hal_fg_get_property(struct i2c_client *client,
  221. enum power_supply_property psp,
  222. union power_supply_propval *val)
  223. {
  224. switch (psp) {
  225. /* Cell voltage (VCELL, mV) */
  226. case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  227. val->intval = s2mg001_get_vbat(client);
  228. break;
  229. /* Additional Voltage Information (mV) */
  230. case POWER_SUPPLY_PROP_VOLTAGE_AVG:
  231. switch (val->intval) {
  232. case SEC_BATTERY_VOLTAGE_AVERAGE:
  233. val->intval = s2mg001_get_avgvbat(client);
  234. break;
  235. case SEC_BATTERY_VOLTAGE_OCV:
  236. val->intval = s2mg001_get_ocv(client);
  237. break;
  238. }
  239. break;
  240. /* Current (mA) */
  241. case POWER_SUPPLY_PROP_CURRENT_NOW:
  242. val->intval = 0;
  243. break;
  244. /* Average Current (mA) */
  245. case POWER_SUPPLY_PROP_CURRENT_AVG:
  246. val->intval = 0;
  247. break;
  248. /* SOC (%) */
  249. case POWER_SUPPLY_PROP_CAPACITY:
  250. if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RAW)
  251. val->intval = s2mg001_get_soc(client);
  252. else
  253. val->intval = s2mg001_get_soc(client) / 10;
  254. break;
  255. /* Battery Temperature */
  256. case POWER_SUPPLY_PROP_TEMP:
  257. /* Target Temperature */
  258. case POWER_SUPPLY_PROP_TEMP_AMBIENT:
  259. val->intval = s2mg001_get_temperature(client);
  260. break;
  261. default:
  262. return false;
  263. }
  264. return true;
  265. }
  266. bool sec_hal_fg_set_property(struct i2c_client *client,
  267. enum power_supply_property psp,
  268. const union power_supply_propval *val)
  269. {
  270. switch (psp) {
  271. /* Battery Temperature */
  272. case POWER_SUPPLY_PROP_TEMP:
  273. /* Target Temperature */
  274. case POWER_SUPPLY_PROP_TEMP_AMBIENT:
  275. s2mg001_set_temperature(client, val->intval);
  276. break;
  277. default:
  278. return false;
  279. }
  280. return true;
  281. }
  282. ssize_t sec_hal_fg_show_attrs(struct device *dev,
  283. const ptrdiff_t offset, char *buf)
  284. {
  285. struct power_supply *psy = dev_get_drvdata(dev);
  286. struct sec_fuelgauge_info *fg =
  287. container_of(psy, struct sec_fuelgauge_info, psy_fg);
  288. int i = 0;
  289. switch (offset) {
  290. /* case FG_REG: */
  291. /* break; */
  292. case FG_DATA:
  293. i += scnprintf(buf + i, PAGE_SIZE - i, "%02x%02x\n",
  294. fg->reg_data[1], fg->reg_data[0]);
  295. break;
  296. default:
  297. i = -EINVAL;
  298. break;
  299. }
  300. return i;
  301. }
  302. ssize_t sec_hal_fg_store_attrs(struct device *dev,
  303. const ptrdiff_t offset,
  304. const char *buf, size_t count)
  305. {
  306. struct power_supply *psy = dev_get_drvdata(dev);
  307. struct sec_fuelgauge_info *fg =
  308. container_of(psy, struct sec_fuelgauge_info, psy_fg);
  309. int ret = 0;
  310. int x = 0;
  311. u8 data[2];
  312. switch (offset) {
  313. case FG_REG:
  314. if (sscanf(buf, "%x\n", &x) == 1) {
  315. fg->reg_addr = x;
  316. s2mg001_read_reg(fg->client,
  317. fg->reg_addr, fg->reg_data);
  318. dev_dbg(&fg->client->dev,
  319. "%s: (read) addr = 0x%x, data = 0x%02x%02x\n",
  320. __func__, fg->reg_addr,
  321. fg->reg_data[1], fg->reg_data[0]);
  322. ret = count;
  323. }
  324. break;
  325. case FG_DATA:
  326. if (sscanf(buf, "%x\n", &x) == 1) {
  327. data[0] = (x & 0xFF00) >> 8;
  328. data[1] = (x & 0x00FF);
  329. dev_dbg(&fg->client->dev,
  330. "%s: (write) addr = 0x%x, data = 0x%02x%02x\n",
  331. __func__, fg->reg_addr, data[1], data[0]);
  332. s2mg001_write_reg(fg->client,
  333. fg->reg_addr, data);
  334. ret = count;
  335. }
  336. break;
  337. default:
  338. ret = -EINVAL;
  339. break;
  340. }
  341. return ret;
  342. }