blob: 9810c3fd84314e0a8141041e8b78de75207bc8ab [file] [log] [blame]
Kyle Yane45fa022016-08-29 11:40:26 -07001/*
2 * Copyright (c) 2011-2014, 2016 The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "%s: " fmt, __func__
16#undef DEBUG
17
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/string.h>
21#include <linux/completion.h>
22#include <linux/platform_device.h>
23#include <linux/slab.h>
24#include <soc/qcom/hsic_sysmon.h>
25#include <soc/qcom/sysmon.h>
26#include <soc/qcom/subsystem_notif.h>
27#include <soc/qcom/smd.h>
28
29#define TX_BUF_SIZE 50
30#define RX_BUF_SIZE 500
31#define TIMEOUT_MS 500
32
33enum transports {
34 TRANSPORT_SMD,
35 TRANSPORT_HSIC,
36};
37
38struct sysmon_subsys {
39 struct mutex lock;
40 struct smd_channel *chan;
41 bool chan_open;
42 struct completion resp_ready;
43 char rx_buf[RX_BUF_SIZE];
44 enum transports transport;
45 struct device *dev;
46 u32 pid;
47 struct list_head list;
48};
49
50static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
51 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
52 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
53 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
54 [SUBSYS_AFTER_POWERUP] = "after_powerup",
55};
56
57static LIST_HEAD(sysmon_list);
58static DEFINE_MUTEX(sysmon_list_lock);
59
60static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf,
61 size_t len)
62{
63 int ret;
64
65 if (!ss->chan_open)
66 return -ENODEV;
67
68 init_completion(&ss->resp_ready);
69 pr_debug("Sending SMD message: %s\n", tx_buf);
70 smd_write(ss->chan, tx_buf, len);
71 ret = wait_for_completion_timeout(&ss->resp_ready,
72 msecs_to_jiffies(TIMEOUT_MS));
73 if (!ret)
74 return -ETIMEDOUT;
75
76 return 0;
77}
78
79static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf,
80 size_t len)
81{
82 int ret;
83 size_t actual_len;
84
85 pr_debug("Sending HSIC message: %s\n", tx_buf);
86 ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
87 tx_buf, len, TIMEOUT_MS);
88 if (ret)
89 return ret;
90 ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
91 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
92 return ret;
93}
94
95static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf,
96 size_t len)
97{
98 int ret;
99
100 switch (ss->transport) {
101 case TRANSPORT_SMD:
102 ret = sysmon_send_smd(ss, tx_buf, len);
103 break;
104 case TRANSPORT_HSIC:
105 ret = sysmon_send_hsic(ss, tx_buf, len);
106 break;
107 default:
108 ret = -EINVAL;
109 }
110
111 if (!ret)
112 pr_debug("Received response: %s\n", ss->rx_buf);
113
114 return ret;
115}
116
117/**
118 * sysmon_send_event_no_qmi() - Notify a subsystem of another's state change
119 * @dest_desc: Subsystem descriptor of the subsystem the notification
120 * should be sent to
121 * @event_desc: Subsystem descriptor of the subsystem that generated the
122 * notification
123 * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN)
124 *
125 * Returns 0 for success, -EINVAL for invalid destination or notification IDs,
126 * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination
127 * subsystem does not respond, and -EPROTO if the destination subsystem
128 * responds, but with something other than an acknowledgment.
129 *
130 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
131 */
132int sysmon_send_event_no_qmi(struct subsys_desc *dest_desc,
133 struct subsys_desc *event_desc,
134 enum subsys_notif_type notif)
135{
136
137 char tx_buf[TX_BUF_SIZE];
138 int ret;
139 struct sysmon_subsys *tmp, *ss = NULL;
140 const char *event_ss = event_desc->name;
141
142 mutex_lock(&sysmon_list_lock);
143 list_for_each_entry(tmp, &sysmon_list, list)
144 if (tmp->pid == dest_desc->sysmon_pid)
145 ss = tmp;
146 mutex_unlock(&sysmon_list_lock);
147
148 if (ss == NULL)
149 return -EINVAL;
150
151 if (ss->dev == NULL)
152 return -ENODEV;
153
154 if (notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT || event_ss == NULL ||
155 notif_name[notif] == NULL)
156 return -EINVAL;
157
158 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
159 notif_name[notif]);
160
161 mutex_lock(&ss->lock);
162 ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
163 if (ret) {
164 pr_err("Message sending failed %d\n", ret);
165 goto out;
166 }
167
168 if (strcmp(ss->rx_buf, "ssr:ack")) {
169 pr_debug("Unexpected response %s\n", ss->rx_buf);
170 ret = -EPROTO;
171 }
172out:
173 mutex_unlock(&ss->lock);
174 return ret;
175}
176EXPORT_SYMBOL(sysmon_send_event_no_qmi);
177
178/**
179 * sysmon_send_shutdown_no_qmi() - send shutdown command to a subsystem.
180 * @dest_desc: Subsystem descriptor of the subsystem to send to
181 *
182 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
183 * the SMD transport channel is not open, -ETIMEDOUT if the destination
184 * subsystem does not respond, and -EPROTO if the destination subsystem
185 * responds with something unexpected.
186 *
187 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
188 */
189int sysmon_send_shutdown_no_qmi(struct subsys_desc *dest_desc)
190{
191 struct sysmon_subsys *tmp, *ss = NULL;
192 const char tx_buf[] = "system:shutdown";
193 const char expect[] = "system:ack";
194 int ret;
195
196 mutex_lock(&sysmon_list_lock);
197 list_for_each_entry(tmp, &sysmon_list, list)
198 if (tmp->pid == dest_desc->sysmon_pid)
199 ss = tmp;
200 mutex_unlock(&sysmon_list_lock);
201
202 if (ss == NULL)
203 return -EINVAL;
204
205 if (ss->dev == NULL)
206 return -ENODEV;
207
208 mutex_lock(&ss->lock);
209 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
210 if (ret) {
211 pr_err("Message sending failed %d\n", ret);
212 goto out;
213 }
214
215 if (strcmp(ss->rx_buf, expect)) {
216 pr_err("Unexpected response %s\n", ss->rx_buf);
217 ret = -EPROTO;
218 }
219out:
220 mutex_unlock(&ss->lock);
221 return ret;
222}
223EXPORT_SYMBOL(sysmon_send_shutdown_no_qmi);
224
225/**
226 * sysmon_get_reason_no_qmi() - Retrieve failure reason from a subsystem.
227 * @dest_desc: Subsystem descriptor of the subsystem to query
228 * @buf: Caller-allocated buffer for the returned NUL-terminated reason
229 * @len: Length of @buf
230 *
231 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
232 * the SMD transport channel is not open, -ETIMEDOUT if the destination
233 * subsystem does not respond, and -EPROTO if the destination subsystem
234 * responds with something unexpected.
235 *
236 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
237 */
238int sysmon_get_reason_no_qmi(struct subsys_desc *dest_desc,
239 char *buf, size_t len)
240{
241 struct sysmon_subsys *tmp, *ss = NULL;
242 const char tx_buf[] = "ssr:retrieve:sfr";
243 const char expect[] = "ssr:return:";
244 size_t prefix_len = ARRAY_SIZE(expect) - 1;
245 int ret;
246
247 mutex_lock(&sysmon_list_lock);
248 list_for_each_entry(tmp, &sysmon_list, list)
249 if (tmp->pid == dest_desc->sysmon_pid)
250 ss = tmp;
251 mutex_unlock(&sysmon_list_lock);
252
253 if (ss == NULL || buf == NULL || len == 0)
254 return -EINVAL;
255
256 if (ss->dev == NULL)
257 return -ENODEV;
258
259 mutex_lock(&ss->lock);
260 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
261 if (ret) {
262 pr_err("Message sending failed %d\n", ret);
263 goto out;
264 }
265
266 if (strncmp(ss->rx_buf, expect, prefix_len)) {
267 pr_err("Unexpected response %s\n", ss->rx_buf);
268 ret = -EPROTO;
269 goto out;
270 }
271 strlcpy(buf, ss->rx_buf + prefix_len, len);
272out:
273 mutex_unlock(&ss->lock);
274 return ret;
275}
276EXPORT_SYMBOL(sysmon_get_reason_no_qmi);
277
278static void sysmon_smd_notify(void *priv, unsigned int smd_event)
279{
280 struct sysmon_subsys *ss = priv;
281
282 switch (smd_event) {
283 case SMD_EVENT_DATA: {
284 if (smd_read_avail(ss->chan) > 0) {
285 smd_read_from_cb(ss->chan, ss->rx_buf,
286 ARRAY_SIZE(ss->rx_buf));
287 complete(&ss->resp_ready);
288 }
289 break;
290 }
291 case SMD_EVENT_OPEN:
292 ss->chan_open = true;
293 break;
294 case SMD_EVENT_CLOSE:
295 ss->chan_open = false;
296 break;
297 }
298}
299
300static int sysmon_probe(struct platform_device *pdev)
301{
302 struct sysmon_subsys *ss;
303 int ret;
304
305 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
306 return -ENODEV;
307
308 ss = devm_kzalloc(&pdev->dev, sizeof(*ss), GFP_KERNEL);
309 if (!ss)
310 return -ENOMEM;
311
312 mutex_init(&ss->lock);
313 if (pdev->id == SYSMON_SS_EXT_MODEM) {
314 ss->transport = TRANSPORT_HSIC;
315 ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
316 if (ret) {
317 pr_err("HSIC open failed\n");
318 return ret;
319 }
320 } else if (pdev->id < SMD_NUM_TYPE) {
321 ss->transport = TRANSPORT_SMD;
322 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan,
323 ss, sysmon_smd_notify);
324 if (ret) {
325 pr_err("SMD open failed\n");
326 return ret;
327 }
328 smd_disable_read_intr(ss->chan);
329 } else
330 return -EINVAL;
331
332 ss->dev = &pdev->dev;
333 ss->pid = pdev->id;
334
335 mutex_lock(&sysmon_list_lock);
336 INIT_LIST_HEAD(&ss->list);
337 list_add_tail(&ss->list, &sysmon_list);
338 mutex_unlock(&sysmon_list_lock);
339 return 0;
340}
341
342static int sysmon_remove(struct platform_device *pdev)
343{
344 struct sysmon_subsys *sysmon, *tmp, *ss = NULL;
345
346 mutex_lock(&sysmon_list_lock);
347 list_for_each_entry_safe(sysmon, tmp, &sysmon_list, list) {
348 if (sysmon->pid == pdev->id) {
349 ss = sysmon;
350 list_del(&ss->list);
351 }
352 }
353 mutex_unlock(&sysmon_list_lock);
354
355 if (ss == NULL)
356 return -EINVAL;
357
358 mutex_lock(&ss->lock);
359 switch (ss->transport) {
360 case TRANSPORT_SMD:
361 smd_close(ss->chan);
362 break;
363 case TRANSPORT_HSIC:
364 hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
365 break;
366 }
367 mutex_unlock(&ss->lock);
368
369 return 0;
370}
371
372static struct platform_driver sysmon_driver = {
373 .probe = sysmon_probe,
374 .remove = sysmon_remove,
375 .driver = {
376 .name = "sys_mon",
377 .owner = THIS_MODULE,
378 },
379};
380
381static int __init sysmon_init(void)
382{
383 return platform_driver_register(&sysmon_driver);
384}
385subsys_initcall(sysmon_init);
386
387static void __exit sysmon_exit(void)
388{
389 platform_driver_unregister(&sysmon_driver);
390}
391module_exit(sysmon_exit);
392
393MODULE_LICENSE("GPL v2");
394MODULE_DESCRIPTION("system monitor communication library");
395MODULE_ALIAS("platform:sys_mon");