blob: b42658a959638569ea310ec19ba942bf8d950add [file] [log] [blame]
Matt Wagantallf8020902011-08-30 21:19:23 -07001/*
Duy Truonge833aca2013-02-12 13:35:08 -08002 * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
Matt Wagantallf8020902011-08-30 21:19:23 -07003 *
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
24#include <mach/msm_smd.h>
25#include <mach/subsystem_notif.h>
26
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070027#include "hsic_sysmon.h"
Matt Wagantallf8020902011-08-30 21:19:23 -070028#include "sysmon.h"
29
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070030#define TX_BUF_SIZE 50
31#define RX_BUF_SIZE 500
Matt Wagantallf8020902011-08-30 21:19:23 -070032#define TIMEOUT_MS 5000
33
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070034enum transports {
35 TRANSPORT_SMD,
36 TRANSPORT_HSIC,
37};
38
Matt Wagantallf8020902011-08-30 21:19:23 -070039struct sysmon_subsys {
40 struct mutex lock;
Devin Kim5980aa82012-10-12 11:38:53 -070041 bool probed;
Matt Wagantallf8020902011-08-30 21:19:23 -070042 struct smd_channel *chan;
43 bool chan_open;
44 struct completion resp_ready;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070045 char rx_buf[RX_BUF_SIZE];
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070046 enum transports transport;
Matt Wagantall9414bbe2012-10-25 15:04:37 -070047 struct device *dev;
Matt Wagantallf8020902011-08-30 21:19:23 -070048};
49
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070050static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
51 [SYSMON_SS_MODEM].transport = TRANSPORT_SMD,
52 [SYSMON_SS_LPASS].transport = TRANSPORT_SMD,
53 [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD,
54 [SYSMON_SS_DSPS].transport = TRANSPORT_SMD,
55 [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD,
56 [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
57};
Matt Wagantallf8020902011-08-30 21:19:23 -070058
59static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
60 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
61 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
62 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
63 [SUBSYS_AFTER_POWERUP] = "after_powerup",
64};
65
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070066static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf,
67 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070068{
69 int ret;
70
71 if (!ss->chan_open)
72 return -ENODEV;
73
74 init_completion(&ss->resp_ready);
75 pr_debug("Sending SMD message: %s\n", tx_buf);
76 smd_write(ss->chan, tx_buf, len);
77 ret = wait_for_completion_timeout(&ss->resp_ready,
78 msecs_to_jiffies(TIMEOUT_MS));
79 if (!ret)
80 return -ETIMEDOUT;
81
82 return 0;
83}
84
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070085static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf,
86 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070087{
88 int ret;
89 size_t actual_len;
90
91 pr_debug("Sending HSIC message: %s\n", tx_buf);
92 ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
93 tx_buf, len, TIMEOUT_MS);
94 if (ret)
95 return ret;
96 ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
97 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
98 return ret;
99}
100
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700101static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf,
102 size_t len)
103{
104 int ret;
105
106 switch (ss->transport) {
107 case TRANSPORT_SMD:
108 ret = sysmon_send_smd(ss, tx_buf, len);
109 break;
110 case TRANSPORT_HSIC:
111 ret = sysmon_send_hsic(ss, tx_buf, len);
112 break;
113 default:
114 ret = -EINVAL;
115 }
116
117 if (!ret)
118 pr_debug("Received response: %s\n", ss->rx_buf);
119
120 return ret;
121}
122
123/**
124 * sysmon_send_event() - Notify a subsystem of another's state change
125 * @dest_ss: ID of subsystem the notification should be sent to
126 * @event_ss: String name of the subsystem that generated the notification
127 * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN)
128 *
129 * Returns 0 for success, -EINVAL for invalid destination or notification IDs,
130 * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination
131 * subsystem does not respond, and -ENOSYS if the destination subsystem
132 * responds, but with something other than an acknowledgement.
133 *
134 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
135 */
Matt Wagantallf8020902011-08-30 21:19:23 -0700136int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
137 enum subsys_notif_type notif)
138{
139 struct sysmon_subsys *ss = &subsys[dest_ss];
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700140 char tx_buf[TX_BUF_SIZE];
Matt Wagantallf8020902011-08-30 21:19:23 -0700141 int ret;
142
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700143 if (ss->dev == NULL)
144 return -ENODEV;
145
Matt Wagantallf8020902011-08-30 21:19:23 -0700146 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
147 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
148 event_ss == NULL)
149 return -EINVAL;
150
Matt Wagantallf8020902011-08-30 21:19:23 -0700151 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
152 notif_name[notif]);
Matt Wagantallf8020902011-08-30 21:19:23 -0700153
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700154 mutex_lock(&ss->lock);
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700155 ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700156 if (ret)
157 goto out;
158
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700159 if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
160 ret = -ENOSYS;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700161out:
162 mutex_unlock(&ss->lock);
163 return ret;
164}
165
166/**
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700167 * sysmon_send_shutdown() - send shutdown command to a
168 * subsystem.
169 * @dest_ss: ID of subsystem to send to.
170 *
171 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
172 * the SMD transport channel is not open, -ETIMEDOUT if the destination
173 * subsystem does not respond, and -ENOSYS if the destination subsystem
174 * responds with something unexpected.
175 *
176 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
177 */
178int sysmon_send_shutdown(enum subsys_id dest_ss)
179{
180 struct sysmon_subsys *ss = &subsys[dest_ss];
Ajay Dudani52757612012-09-25 15:06:52 -0700181 const char tx_buf[] = "system:shutdown";
182 const char expect[] = "system:ack";
183 size_t prefix_len = ARRAY_SIZE(expect) - 1;
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700184 int ret;
185
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700186 if (ss->dev == NULL)
187 return -ENODEV;
188
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700189 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS)
190 return -EINVAL;
191
192 mutex_lock(&ss->lock);
193 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
Ajay Dudani52757612012-09-25 15:06:52 -0700194 if (ret)
195 goto out;
196
197 if (strncmp(ss->rx_buf, expect, prefix_len))
198 ret = -ENOSYS;
199out:
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700200 mutex_unlock(&ss->lock);
201 return ret;
202}
203
204/**
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700205 * sysmon_get_reason() - Retrieve failure reason from a subsystem.
206 * @dest_ss: ID of subsystem to query
207 * @buf: Caller-allocated buffer for the returned NUL-terminated reason
208 * @len: Length of @buf
209 *
210 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
211 * the SMD transport channel is not open, -ETIMEDOUT if the destination
212 * subsystem does not respond, and -ENOSYS if the destination subsystem
213 * responds with something unexpected.
214 *
215 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
216 */
217int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len)
218{
219 struct sysmon_subsys *ss = &subsys[dest_ss];
220 const char tx_buf[] = "ssr:retrieve:sfr";
221 const char expect[] = "ssr:return:";
222 size_t prefix_len = ARRAY_SIZE(expect) - 1;
223 int ret;
224
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700225 if (ss->dev == NULL)
226 return -ENODEV;
227
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700228 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
229 buf == NULL || len == 0)
230 return -EINVAL;
231
Kim,Do-Yeob1d7938c2012-08-21 14:48:25 +0900232 if ((!ss->chan_open) && (dest_ss != SYSMON_SS_EXT_MODEM))
Iliyan Malcheva4bfdcd2012-07-03 14:24:56 -0700233 return -ENODEV;
234
Devin Kim5980aa82012-10-12 11:38:53 -0700235 if (!ss->probed)
236 return -ENODEV;
237
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700238 mutex_lock(&ss->lock);
239 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
240 if (ret)
241 goto out;
242
243 if (strncmp(ss->rx_buf, expect, prefix_len)) {
244 ret = -ENOSYS;
245 goto out;
246 }
247 strlcpy(buf, ss->rx_buf + prefix_len, len);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700248out:
249 mutex_unlock(&ss->lock);
Matt Wagantallf8020902011-08-30 21:19:23 -0700250 return ret;
251}
252
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700253static void sysmon_smd_notify(void *priv, unsigned int smd_event)
Matt Wagantallf8020902011-08-30 21:19:23 -0700254{
255 struct sysmon_subsys *ss = priv;
256
257 switch (smd_event) {
258 case SMD_EVENT_DATA: {
259 if (smd_read_avail(ss->chan) > 0) {
260 smd_read_from_cb(ss->chan, ss->rx_buf,
261 ARRAY_SIZE(ss->rx_buf));
262 complete(&ss->resp_ready);
263 }
264 break;
265 }
266 case SMD_EVENT_OPEN:
267 ss->chan_open = true;
268 break;
269 case SMD_EVENT_CLOSE:
270 ss->chan_open = false;
271 break;
272 }
273}
274
275static int sysmon_probe(struct platform_device *pdev)
276{
Matt Wagantallf8020902011-08-30 21:19:23 -0700277 struct sysmon_subsys *ss;
278 int ret;
279
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700280 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
Matt Wagantallf8020902011-08-30 21:19:23 -0700281 return -ENODEV;
282
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700283 ss = &subsys[pdev->id];
Matt Wagantallf8020902011-08-30 21:19:23 -0700284 mutex_init(&ss->lock);
Devin Kim5980aa82012-10-12 11:38:53 -0700285 ss->probed = true;
Matt Wagantallf8020902011-08-30 21:19:23 -0700286
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700287 switch (ss->transport) {
288 case TRANSPORT_SMD:
289 if (pdev->id >= SMD_NUM_TYPE)
290 return -EINVAL;
291
292 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
293 sysmon_smd_notify);
294 if (ret) {
295 pr_err("SMD open failed\n");
296 return ret;
297 }
298
299 smd_disable_read_intr(ss->chan);
300 break;
301 case TRANSPORT_HSIC:
302 if (pdev->id < SMD_NUM_TYPE)
303 return -EINVAL;
304
305 ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
306 if (ret) {
307 pr_err("HSIC open failed\n");
308 return ret;
309 }
310 break;
311 default:
312 return -EINVAL;
Matt Wagantallf8020902011-08-30 21:19:23 -0700313 }
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700314 ss->dev = &pdev->dev;
Matt Wagantallf8020902011-08-30 21:19:23 -0700315
316 return 0;
317}
318
319static int __devexit sysmon_remove(struct platform_device *pdev)
320{
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700321 struct sysmon_subsys *ss = &subsys[pdev->id];
322
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700323 ss->dev = NULL;
324
325 mutex_lock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700326 switch (ss->transport) {
327 case TRANSPORT_SMD:
328 smd_close(ss->chan);
329 break;
330 case TRANSPORT_HSIC:
331 hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
332 break;
333 }
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700334 mutex_unlock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700335
Matt Wagantallf8020902011-08-30 21:19:23 -0700336 return 0;
337}
338
339static struct platform_driver sysmon_driver = {
340 .probe = sysmon_probe,
341 .remove = __devexit_p(sysmon_remove),
342 .driver = {
343 .name = "sys_mon",
344 .owner = THIS_MODULE,
345 },
346};
347
348static int __init sysmon_init(void)
349{
350 return platform_driver_register(&sysmon_driver);
351}
352subsys_initcall(sysmon_init);
353
354static void __exit sysmon_exit(void)
355{
356 platform_driver_unregister(&sysmon_driver);
357}
358module_exit(sysmon_exit);
359
360MODULE_LICENSE("GPL v2");
361MODULE_DESCRIPTION("system monitor communication library");
362MODULE_ALIAS("platform:sys_mon");