charger-ulog-glink.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  4. */
  5. #define pr_fmt(fmt) "CHARGER_ULOG: %s: " fmt, __func__
  6. #include <linux/debugfs.h>
  7. #include <linux/device.h>
  8. #include <linux/ipc_logging.h>
  9. #include <linux/ktime.h>
  10. #include <linux/module.h>
  11. #include <linux/mutex.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/rpmsg.h>
  14. #include <linux/slab.h>
  15. #include <linux/soc/qcom/pmic_glink.h>
  16. #define MSG_OWNER_CHG_ULOG 32778
  17. #define MSG_TYPE_REQ_RESP 1
  18. #define GET_CHG_ULOG_REQ 0x18
  19. #define SET_CHG_ULOG_PROP_REQ 0x19
  20. #define GET_CHG_INIT_ULOG_REQ 0x23
  21. #define LOG_CATEGORY_INIT (1ULL << 32)
  22. #define LOG_MIN_TIME_MS 500
  23. #define LOG_DEFAULT_TIME_MS 1000
  24. #define MAX_ULOG_SIZE 8192
  25. #define NUM_LOG_PAGES 10
  26. #define NUM_INIT_LOG_PAGES 8
  27. struct set_ulog_prop_req_msg {
  28. struct pmic_glink_hdr hdr;
  29. u64 log_category;
  30. u32 log_level;
  31. };
  32. struct get_ulog_req_msg {
  33. struct pmic_glink_hdr hdr;
  34. u32 log_size;
  35. };
  36. struct get_ulog_resp_msg {
  37. struct pmic_glink_hdr hdr;
  38. u8 buf[MAX_ULOG_SIZE];
  39. };
  40. struct chg_ulog_glink_dev {
  41. struct device *dev;
  42. struct pmic_glink_client *client;
  43. struct dentry *debugfs_dir;
  44. void *ipc_log;
  45. void *ipc_init_log;
  46. struct mutex lock;
  47. struct completion ack;
  48. struct delayed_work ulog_work;
  49. u8 ulog_buf[MAX_ULOG_SIZE];
  50. u64 log_category;
  51. u32 log_level;
  52. u32 log_time_ms;
  53. bool log_enable;
  54. bool init_log_enable;
  55. };
  56. #define WAIT_TIME_MS 1000
  57. static int chg_ulog_write(struct chg_ulog_glink_dev *cd, void *data,
  58. size_t len)
  59. {
  60. int rc;
  61. mutex_lock(&cd->lock);
  62. reinit_completion(&cd->ack);
  63. rc = pmic_glink_write(cd->client, data, len);
  64. if (!rc) {
  65. rc = wait_for_completion_timeout(&cd->ack,
  66. msecs_to_jiffies(WAIT_TIME_MS));
  67. if (!rc) {
  68. pr_err("Error, timed out sending message\n");
  69. mutex_unlock(&cd->lock);
  70. return -ETIMEDOUT;
  71. }
  72. rc = 0;
  73. }
  74. mutex_unlock(&cd->lock);
  75. return rc;
  76. }
  77. static int chg_ulog_request(struct chg_ulog_glink_dev *cd, bool init)
  78. {
  79. struct get_ulog_req_msg req_msg = { { 0 } };
  80. req_msg.hdr.owner = MSG_OWNER_CHG_ULOG;
  81. req_msg.hdr.type = MSG_TYPE_REQ_RESP;
  82. req_msg.hdr.opcode = init ? GET_CHG_INIT_ULOG_REQ : GET_CHG_ULOG_REQ;
  83. req_msg.log_size = MAX_ULOG_SIZE;
  84. return chg_ulog_write(cd, &req_msg, sizeof(req_msg));
  85. }
  86. static int chg_ulog_set_log_type(struct chg_ulog_glink_dev *cd, u64 category,
  87. u32 level)
  88. {
  89. struct set_ulog_prop_req_msg req_msg = { { 0 } };
  90. int rc;
  91. req_msg.hdr.owner = MSG_OWNER_CHG_ULOG;
  92. req_msg.hdr.type = MSG_TYPE_REQ_RESP;
  93. req_msg.hdr.opcode = SET_CHG_ULOG_PROP_REQ;
  94. req_msg.log_category = category;
  95. req_msg.log_level = level;
  96. rc = chg_ulog_write(cd, &req_msg, sizeof(req_msg));
  97. if (!rc)
  98. pr_debug("Set log category %llu log level %u\n", category,
  99. level);
  100. return rc;
  101. }
  102. static void chg_ulog_work(struct work_struct *work)
  103. {
  104. struct chg_ulog_glink_dev *cd = container_of(work,
  105. struct chg_ulog_glink_dev,
  106. ulog_work.work);
  107. int rc;
  108. rc = chg_ulog_request(cd, cd->init_log_enable);
  109. if (rc)
  110. pr_err("Error requesting ulog, rc=%d\n", rc);
  111. else if (cd->log_enable || cd->init_log_enable)
  112. schedule_delayed_work(&cd->ulog_work,
  113. msecs_to_jiffies(cd->log_time_ms));
  114. }
  115. static void ulog_store(struct chg_ulog_glink_dev *cd, void *ipc_ctxt,
  116. size_t len)
  117. {
  118. char *buf = cd->ulog_buf, *token = NULL;
  119. if (buf[0] == '\0') {
  120. pr_debug("buffer is NULL\n");
  121. if (cd->init_log_enable)
  122. cd->init_log_enable = false;
  123. return;
  124. }
  125. buf[len - 1] = '\0';
  126. if (len >= MAX_MSG_SIZE) {
  127. do {
  128. token = strsep((char **)&buf, "\n");
  129. if (token)
  130. ipc_log_string(ipc_ctxt, "%s", token);
  131. } while (token);
  132. } else {
  133. ipc_log_string(ipc_ctxt, "%s", buf);
  134. }
  135. }
  136. static void handle_ulog_message(struct chg_ulog_glink_dev *cd,
  137. struct get_ulog_resp_msg *resp_msg,
  138. size_t len)
  139. {
  140. void *ipc_ctxt;
  141. if (len != sizeof(*resp_msg)) {
  142. pr_err("Expected data length: %zu, received: %zu\n",
  143. sizeof(*resp_msg), len);
  144. return;
  145. }
  146. memcpy(cd->ulog_buf, resp_msg->buf, sizeof(cd->ulog_buf));
  147. ipc_ctxt = (resp_msg->hdr.opcode == GET_CHG_INIT_ULOG_REQ)
  148. ? cd->ipc_init_log : cd->ipc_log;
  149. ulog_store(cd, ipc_ctxt, len - sizeof(resp_msg->hdr));
  150. }
  151. static int chg_ulog_callback(void *priv, void *data, size_t len)
  152. {
  153. struct pmic_glink_hdr *hdr = data;
  154. struct chg_ulog_glink_dev *cd = priv;
  155. pr_debug("owner: %u type: %u opcode: %#x len: %zu\n", hdr->owner,
  156. hdr->type, hdr->opcode, len);
  157. switch (hdr->opcode) {
  158. case SET_CHG_ULOG_PROP_REQ:
  159. complete(&cd->ack);
  160. break;
  161. case GET_CHG_ULOG_REQ:
  162. case GET_CHG_INIT_ULOG_REQ:
  163. handle_ulog_message(cd, data, len);
  164. complete(&cd->ack);
  165. break;
  166. default:
  167. pr_err("Unknown opcode %u\n", hdr->opcode);
  168. break;
  169. }
  170. return 0;
  171. }
  172. static int ulog_cat_get(void *data, u64 *val)
  173. {
  174. struct chg_ulog_glink_dev *cd = data;
  175. *val = cd->log_category;
  176. return 0;
  177. }
  178. static int ulog_cat_set(void *data, u64 val)
  179. {
  180. int rc;
  181. struct chg_ulog_glink_dev *cd = data;
  182. if (cd->log_enable) {
  183. pr_err("Disable ulog before changing log category\n");
  184. return -EINVAL;
  185. }
  186. if (val == cd->log_category)
  187. return 0;
  188. rc = chg_ulog_set_log_type(cd, val, cd->log_level);
  189. if (rc)
  190. pr_err("Couldn't set log_category rc=%d\n", rc);
  191. else
  192. cd->log_category = val;
  193. return rc;
  194. }
  195. DEFINE_DEBUGFS_ATTRIBUTE(ulog_cat_fops, ulog_cat_get, ulog_cat_set,
  196. "%llu\n");
  197. static int ulog_level_get(void *data, u64 *val)
  198. {
  199. struct chg_ulog_glink_dev *cd = data;
  200. *val = cd->log_level;
  201. return 0;
  202. }
  203. static int ulog_level_set(void *data, u64 val)
  204. {
  205. int rc;
  206. struct chg_ulog_glink_dev *cd = data;
  207. u32 level = val;
  208. if (cd->log_enable) {
  209. pr_err("Disable ulog before changing log level\n");
  210. return -EINVAL;
  211. }
  212. if (level == cd->log_level)
  213. return 0;
  214. rc = chg_ulog_set_log_type(cd, cd->log_category, level);
  215. if (rc)
  216. pr_err("Couldn't set log_level rc=%d\n", rc);
  217. else
  218. cd->log_level = level;
  219. return rc;
  220. }
  221. DEFINE_DEBUGFS_ATTRIBUTE(ulog_level_fops, ulog_level_get, ulog_level_set,
  222. "%llu\n");
  223. static int ulog_en_get(void *data, u64 *val)
  224. {
  225. struct chg_ulog_glink_dev *cd = data;
  226. *val = cd->log_enable;
  227. return 0;
  228. }
  229. static int ulog_en_set(void *data, u64 val)
  230. {
  231. struct chg_ulog_glink_dev *cd = data;
  232. bool en = val;
  233. if (en == cd->log_enable)
  234. return 0;
  235. if (cd->log_category == LOG_CATEGORY_INIT)
  236. cd->init_log_enable = en;
  237. else
  238. cd->log_enable = en;
  239. if (en)
  240. schedule_delayed_work(&cd->ulog_work,
  241. msecs_to_jiffies(cd->log_time_ms));
  242. else
  243. cancel_delayed_work_sync(&cd->ulog_work);
  244. return 0;
  245. }
  246. DEFINE_DEBUGFS_ATTRIBUTE(ulog_en_fops, ulog_en_get, ulog_en_set, "%llu\n");
  247. static int ulog_time_get(void *data, u64 *val)
  248. {
  249. struct chg_ulog_glink_dev *cd = data;
  250. *val = cd->log_time_ms;
  251. return 0;
  252. }
  253. static int ulog_time_set(void *data, u64 val)
  254. {
  255. struct chg_ulog_glink_dev *cd = data;
  256. if (val == cd->log_time_ms)
  257. return 0;
  258. if (val < LOG_MIN_TIME_MS)
  259. return -EINVAL;
  260. cd->log_time_ms = val;
  261. return 0;
  262. }
  263. DEFINE_DEBUGFS_ATTRIBUTE(ulog_time_fops, ulog_time_get, ulog_time_set,
  264. "%llu\n");
  265. static int chg_ulog_add_debugfs(struct chg_ulog_glink_dev *cd)
  266. {
  267. struct dentry *dir, *file;
  268. int rc;
  269. dir = debugfs_create_dir("charger_ulog", NULL);
  270. if (IS_ERR(dir)) {
  271. rc = PTR_ERR(dir);
  272. pr_err("Failed to create charger_ulog debugfs directory: %d\n",
  273. rc);
  274. return rc;
  275. }
  276. file = debugfs_create_file_unsafe("category", 0600, dir, cd,
  277. &ulog_cat_fops);
  278. if (IS_ERR(file)) {
  279. rc = PTR_ERR(file);
  280. pr_err("Failed to create category %d\n", rc);
  281. goto out;
  282. }
  283. file = debugfs_create_file_unsafe("level", 0600, dir, cd,
  284. &ulog_level_fops);
  285. if (IS_ERR(file)) {
  286. rc = PTR_ERR(file);
  287. pr_err("Failed to create level %d\n", rc);
  288. goto out;
  289. }
  290. file = debugfs_create_file_unsafe("enable", 0600, dir, cd,
  291. &ulog_en_fops);
  292. if (IS_ERR(file)) {
  293. rc = PTR_ERR(file);
  294. pr_err("Failed to create enable %d\n", rc);
  295. goto out;
  296. }
  297. file = debugfs_create_file_unsafe("time_ms", 0600, dir, cd,
  298. &ulog_time_fops);
  299. if (IS_ERR(file)) {
  300. rc = PTR_ERR(file);
  301. pr_err("Failed to create time_ms %d\n", rc);
  302. goto out;
  303. }
  304. cd->debugfs_dir = dir;
  305. return 0;
  306. out:
  307. debugfs_remove_recursive(dir);
  308. return rc;
  309. }
  310. static int chg_ulog_probe(struct platform_device *pdev)
  311. {
  312. struct chg_ulog_glink_dev *cd;
  313. struct pmic_glink_client_data client_data = { };
  314. int rc;
  315. cd = devm_kzalloc(&pdev->dev, sizeof(*cd), GFP_KERNEL);
  316. if (!cd)
  317. return -ENOMEM;
  318. mutex_init(&cd->lock);
  319. init_completion(&cd->ack);
  320. INIT_DELAYED_WORK(&cd->ulog_work, chg_ulog_work);
  321. cd->log_time_ms = LOG_DEFAULT_TIME_MS;
  322. platform_set_drvdata(pdev, cd);
  323. cd->dev = &pdev->dev;
  324. client_data.id = MSG_OWNER_CHG_ULOG;
  325. client_data.name = "chg_ulog";
  326. client_data.msg_cb = chg_ulog_callback;
  327. client_data.priv = cd;
  328. cd->client = pmic_glink_register_client(cd->dev, &client_data);
  329. if (IS_ERR(cd->client))
  330. return dev_err_probe(cd->dev, PTR_ERR(cd->client),
  331. "Error in registering with pmic_glink %d\n", client_data.id);
  332. rc = chg_ulog_add_debugfs(cd);
  333. if (rc) {
  334. pmic_glink_unregister_client(cd->client);
  335. return dev_err_probe(cd->dev, -EINVAL, "Error in creating debugfs\n");
  336. }
  337. cd->ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "charger_ulog", 0);
  338. if (!cd->ipc_log) {
  339. pmic_glink_unregister_client(cd->client);
  340. debugfs_remove_recursive(cd->debugfs_dir);
  341. return dev_err_probe(cd->dev, -ENODEV, "Error in creating charger_ulog\n");
  342. }
  343. cd->ipc_init_log = ipc_log_context_create(NUM_INIT_LOG_PAGES,
  344. "charger_ulog_init", 0);
  345. if (!cd->ipc_init_log) {
  346. pmic_glink_unregister_client(cd->client);
  347. ipc_log_context_destroy(cd->ipc_log);
  348. debugfs_remove_recursive(cd->debugfs_dir);
  349. return dev_err_probe(cd->dev, -ENODEV, "Error in creating charger_ulog_init\n");
  350. }
  351. return 0;
  352. }
  353. static int chg_ulog_remove(struct platform_device *pdev)
  354. {
  355. struct chg_ulog_glink_dev *cd = platform_get_drvdata(pdev);
  356. int rc;
  357. debugfs_remove_recursive(cd->debugfs_dir);
  358. cancel_delayed_work_sync(&cd->ulog_work);
  359. rc = pmic_glink_unregister_client(cd->client);
  360. if (rc < 0)
  361. pr_err("Error unregistering from pmic_glink, rc=%d\n", rc);
  362. ipc_log_context_destroy(cd->ipc_log);
  363. ipc_log_context_destroy(cd->ipc_init_log);
  364. return 0;
  365. }
  366. static const struct of_device_id chg_ulog_match_table[] = {
  367. { .compatible = "qcom,charger-ulog-glink" },
  368. {},
  369. };
  370. static struct platform_driver chg_ulog_driver = {
  371. .driver = {
  372. .name = "charger_ulog_glink",
  373. .of_match_table = chg_ulog_match_table,
  374. },
  375. .probe = chg_ulog_probe,
  376. .remove = chg_ulog_remove,
  377. };
  378. module_platform_driver(chg_ulog_driver);
  379. MODULE_DESCRIPTION("QTI charger ulog glink driver");
  380. MODULE_LICENSE("GPL v2");