xref: /linux/drivers/soc/qcom/pmic_pdcharger_ulog.c (revision d754ed2821fd9675d203cb73c4afcd593e28b7d0)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2019-2022, The Linux Foundation. All rights reserved.
4  * Copyright (c) 2023, Linaro Ltd
5  */
6 #include <linux/of_device.h>
7 #include <linux/module.h>
8 #include <linux/platform_device.h>
9 #include <linux/rpmsg.h>
10 #include <linux/slab.h>
11 #include <linux/soc/qcom/pdr.h>
12 #include <linux/debugfs.h>
13 
14 #define CREATE_TRACE_POINTS
15 #include "pmic_pdcharger_ulog.h"
16 
17 #define MSG_OWNER_CHG_ULOG		32778
18 #define MSG_TYPE_REQ_RESP		1
19 
20 #define GET_CHG_ULOG_REQ		0x18
21 #define SET_CHG_ULOG_PROP_REQ		0x19
22 
23 #define LOG_DEFAULT_TIME_MS		1000
24 
25 #define MAX_ULOG_SIZE			8192
26 
27 struct pmic_pdcharger_ulog_hdr {
28 	__le32 owner;
29 	__le32 type;
30 	__le32 opcode;
31 };
32 
33 struct pmic_pdcharger_ulog {
34 	struct rpmsg_device *rpdev;
35 	struct delayed_work ulog_work;
36 };
37 
38 struct get_ulog_req_msg {
39 	struct pmic_pdcharger_ulog_hdr	hdr;
40 	u32				log_size;
41 };
42 
43 struct get_ulog_resp_msg {
44 	struct pmic_pdcharger_ulog_hdr	hdr;
45 	u8				buf[MAX_ULOG_SIZE];
46 };
47 
48 static int pmic_pdcharger_ulog_write_async(struct pmic_pdcharger_ulog *pg, void *data, size_t len)
49 {
50 	return rpmsg_send(pg->rpdev->ept, data, len);
51 }
52 
53 static int pmic_pdcharger_ulog_request(struct pmic_pdcharger_ulog *pg)
54 {
55 	struct get_ulog_req_msg req_msg = {
56 		.hdr = {
57 			.owner = cpu_to_le32(MSG_OWNER_CHG_ULOG),
58 			.type = cpu_to_le32(MSG_TYPE_REQ_RESP),
59 			.opcode = cpu_to_le32(GET_CHG_ULOG_REQ)
60 		},
61 		.log_size = MAX_ULOG_SIZE
62 	};
63 
64 	return pmic_pdcharger_ulog_write_async(pg, &req_msg, sizeof(req_msg));
65 }
66 
67 static void pmic_pdcharger_ulog_work(struct work_struct *work)
68 {
69 	struct pmic_pdcharger_ulog *pg = container_of(work, struct pmic_pdcharger_ulog,
70 						      ulog_work.work);
71 	int rc;
72 
73 	rc = pmic_pdcharger_ulog_request(pg);
74 	if (rc) {
75 		dev_err(&pg->rpdev->dev, "Error requesting ulog, rc=%d\n", rc);
76 		return;
77 	}
78 }
79 
80 static void pmic_pdcharger_ulog_handle_message(struct pmic_pdcharger_ulog *pg,
81 					       struct get_ulog_resp_msg *resp_msg,
82 					       size_t len)
83 {
84 	char *token, *buf = resp_msg->buf;
85 
86 	if (len != sizeof(*resp_msg)) {
87 		dev_err(&pg->rpdev->dev, "Expected data length: %zu, received: %zu\n",
88 			sizeof(*resp_msg), len);
89 		return;
90 	}
91 
92 	buf[MAX_ULOG_SIZE - 1] = '\0';
93 
94 	do {
95 		token = strsep((char **)&buf, "\n");
96 		if (token && strlen(token))
97 			trace_pmic_pdcharger_ulog_msg(token);
98 	} while (token);
99 }
100 
101 static int pmic_pdcharger_ulog_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
102 					      int len, void *priv, u32 addr)
103 {
104 	struct pmic_pdcharger_ulog *pg = dev_get_drvdata(&rpdev->dev);
105 	struct pmic_pdcharger_ulog_hdr *hdr = data;
106 	u32 opcode;
107 
108 	opcode = le32_to_cpu(hdr->opcode);
109 
110 	switch (opcode) {
111 	case GET_CHG_ULOG_REQ:
112 		schedule_delayed_work(&pg->ulog_work, msecs_to_jiffies(LOG_DEFAULT_TIME_MS));
113 		pmic_pdcharger_ulog_handle_message(pg, data, len);
114 		break;
115 	default:
116 		dev_err(&pg->rpdev->dev, "Unknown opcode %u\n", opcode);
117 		break;
118 	}
119 
120 	return 0;
121 }
122 
123 static int pmic_pdcharger_ulog_rpmsg_probe(struct rpmsg_device *rpdev)
124 {
125 	struct pmic_pdcharger_ulog *pg;
126 	struct device *dev = &rpdev->dev;
127 
128 	pg = devm_kzalloc(dev, sizeof(*pg), GFP_KERNEL);
129 	if (!pg)
130 		return -ENOMEM;
131 
132 	pg->rpdev = rpdev;
133 	INIT_DELAYED_WORK(&pg->ulog_work, pmic_pdcharger_ulog_work);
134 
135 	dev_set_drvdata(dev, pg);
136 
137 	pmic_pdcharger_ulog_request(pg);
138 
139 	return 0;
140 }
141 
142 static void pmic_pdcharger_ulog_rpmsg_remove(struct rpmsg_device *rpdev)
143 {
144 	struct pmic_pdcharger_ulog *pg = dev_get_drvdata(&rpdev->dev);
145 
146 	cancel_delayed_work_sync(&pg->ulog_work);
147 }
148 
149 static const struct rpmsg_device_id pmic_pdcharger_ulog_rpmsg_id_match[] = {
150 	{ "PMIC_LOGS_ADSP_APPS" },
151 	{}
152 };
153 /*
154  * No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be
155  * loaded manually only.
156  */
157 
158 static struct rpmsg_driver pmic_pdcharger_ulog_rpmsg_driver = {
159 	.probe = pmic_pdcharger_ulog_rpmsg_probe,
160 	.remove = pmic_pdcharger_ulog_rpmsg_remove,
161 	.callback = pmic_pdcharger_ulog_rpmsg_callback,
162 	.id_table = pmic_pdcharger_ulog_rpmsg_id_match,
163 	.drv  = {
164 		.name  = "qcom_pmic_pdcharger_ulog_rpmsg",
165 	},
166 };
167 
168 module_rpmsg_driver(pmic_pdcharger_ulog_rpmsg_driver);
169 MODULE_DESCRIPTION("Qualcomm PMIC ChargerPD ULOG driver");
170 MODULE_LICENSE("GPL");
171