blob: 610a3bfef2cf4ab49d4495017848168c38fc85fb [file] [log] [blame]
Matt Wagantallf8020902011-08-30 21:19:23 -07001/*
Jack Phamb754b042013-02-13 11:54:14 -08002 * Copyright (c) 2011-2013, 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;
Jack Phamb754b042013-02-13 11:54:14 -080048 enum hsic_sysmon_device_id hsic_id;
Matt Wagantallf8020902011-08-30 21:19:23 -070049};
50
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070051static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
Jack Phamb754b042013-02-13 11:54:14 -080052 [SYSMON_SS_MODEM].transport = TRANSPORT_SMD,
53 [SYSMON_SS_LPASS].transport = TRANSPORT_SMD,
54 [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD,
55 [SYSMON_SS_DSPS].transport = TRANSPORT_SMD,
56 [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD,
57 [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
58 [SYSMON_SS_EXT_MODEM2].transport = TRANSPORT_HSIC,
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070059};
Matt Wagantallf8020902011-08-30 21:19:23 -070060
61static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
62 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
63 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
64 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
65 [SUBSYS_AFTER_POWERUP] = "after_powerup",
66};
67
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070068static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf,
69 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070070{
71 int ret;
72
73 if (!ss->chan_open)
74 return -ENODEV;
75
76 init_completion(&ss->resp_ready);
77 pr_debug("Sending SMD message: %s\n", tx_buf);
78 smd_write(ss->chan, tx_buf, len);
79 ret = wait_for_completion_timeout(&ss->resp_ready,
80 msecs_to_jiffies(TIMEOUT_MS));
81 if (!ret)
82 return -ETIMEDOUT;
83
84 return 0;
85}
86
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070087static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf,
88 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070089{
90 int ret;
91 size_t actual_len;
92
93 pr_debug("Sending HSIC message: %s\n", tx_buf);
Jack Phamb754b042013-02-13 11:54:14 -080094 ret = hsic_sysmon_write(ss->hsic_id, tx_buf, len, TIMEOUT_MS);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070095 if (ret)
96 return ret;
Jack Phamb754b042013-02-13 11:54:14 -080097 ret = hsic_sysmon_read(ss->hsic_id, ss->rx_buf,
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070098 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
99 return ret;
100}
101
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700102static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf,
103 size_t len)
104{
105 int ret;
106
107 switch (ss->transport) {
108 case TRANSPORT_SMD:
109 ret = sysmon_send_smd(ss, tx_buf, len);
110 break;
111 case TRANSPORT_HSIC:
112 ret = sysmon_send_hsic(ss, tx_buf, len);
113 break;
114 default:
115 ret = -EINVAL;
116 }
117
118 if (!ret)
119 pr_debug("Received response: %s\n", ss->rx_buf);
120
121 return ret;
122}
123
124/**
125 * sysmon_send_event() - Notify a subsystem of another's state change
126 * @dest_ss: ID of subsystem the notification should be sent to
127 * @event_ss: String name of the subsystem that generated the notification
128 * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN)
129 *
130 * Returns 0 for success, -EINVAL for invalid destination or notification IDs,
131 * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination
132 * subsystem does not respond, and -ENOSYS if the destination subsystem
133 * responds, but with something other than an acknowledgement.
134 *
135 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
136 */
Matt Wagantallf8020902011-08-30 21:19:23 -0700137int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
138 enum subsys_notif_type notif)
139{
140 struct sysmon_subsys *ss = &subsys[dest_ss];
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700141 char tx_buf[TX_BUF_SIZE];
Matt Wagantallf8020902011-08-30 21:19:23 -0700142 int ret;
143
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700144 if (ss->dev == NULL)
145 return -ENODEV;
146
Matt Wagantallf8020902011-08-30 21:19:23 -0700147 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
148 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
149 event_ss == NULL)
150 return -EINVAL;
151
Matt Wagantallf8020902011-08-30 21:19:23 -0700152 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
153 notif_name[notif]);
Matt Wagantallf8020902011-08-30 21:19:23 -0700154
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700155 mutex_lock(&ss->lock);
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700156 ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700157 if (ret)
158 goto out;
159
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700160 if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
161 ret = -ENOSYS;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700162out:
163 mutex_unlock(&ss->lock);
164 return ret;
165}
166
167/**
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700168 * sysmon_send_shutdown() - send shutdown command to a
169 * subsystem.
170 * @dest_ss: ID of subsystem to send to.
171 *
172 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
173 * the SMD transport channel is not open, -ETIMEDOUT if the destination
174 * subsystem does not respond, and -ENOSYS if the destination subsystem
175 * responds with something unexpected.
176 *
177 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
178 */
179int sysmon_send_shutdown(enum subsys_id dest_ss)
180{
181 struct sysmon_subsys *ss = &subsys[dest_ss];
Ajay Dudani52757612012-09-25 15:06:52 -0700182 const char tx_buf[] = "system:shutdown";
183 const char expect[] = "system:ack";
184 size_t prefix_len = ARRAY_SIZE(expect) - 1;
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700185 int ret;
186
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700187 if (ss->dev == NULL)
188 return -ENODEV;
189
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700190 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS)
191 return -EINVAL;
192
193 mutex_lock(&ss->lock);
194 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
Ajay Dudani52757612012-09-25 15:06:52 -0700195 if (ret)
196 goto out;
197
198 if (strncmp(ss->rx_buf, expect, prefix_len))
199 ret = -ENOSYS;
200out:
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700201 mutex_unlock(&ss->lock);
202 return ret;
203}
204
205/**
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700206 * sysmon_get_reason() - Retrieve failure reason from a subsystem.
207 * @dest_ss: ID of subsystem to query
208 * @buf: Caller-allocated buffer for the returned NUL-terminated reason
209 * @len: Length of @buf
210 *
211 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
212 * the SMD transport channel is not open, -ETIMEDOUT if the destination
213 * subsystem does not respond, and -ENOSYS if the destination subsystem
214 * responds with something unexpected.
215 *
216 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
217 */
218int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len)
219{
220 struct sysmon_subsys *ss = &subsys[dest_ss];
221 const char tx_buf[] = "ssr:retrieve:sfr";
222 const char expect[] = "ssr:return:";
223 size_t prefix_len = ARRAY_SIZE(expect) - 1;
224 int ret;
225
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700226 if (ss->dev == NULL)
227 return -ENODEV;
228
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700229 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
230 buf == NULL || len == 0)
231 return -EINVAL;
232
Kim,Do-Yeob1d7938c2012-08-21 14:48:25 +0900233 if ((!ss->chan_open) && (dest_ss != SYSMON_SS_EXT_MODEM))
Iliyan Malcheva4bfdcd2012-07-03 14:24:56 -0700234 return -ENODEV;
235
Devin Kim5980aa82012-10-12 11:38:53 -0700236 if (!ss->probed)
237 return -ENODEV;
238
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700239 mutex_lock(&ss->lock);
240 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
241 if (ret)
242 goto out;
243
244 if (strncmp(ss->rx_buf, expect, prefix_len)) {
245 ret = -ENOSYS;
246 goto out;
247 }
248 strlcpy(buf, ss->rx_buf + prefix_len, len);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700249out:
250 mutex_unlock(&ss->lock);
Matt Wagantallf8020902011-08-30 21:19:23 -0700251 return ret;
252}
253
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700254static void sysmon_smd_notify(void *priv, unsigned int smd_event)
Matt Wagantallf8020902011-08-30 21:19:23 -0700255{
256 struct sysmon_subsys *ss = priv;
257
258 switch (smd_event) {
259 case SMD_EVENT_DATA: {
260 if (smd_read_avail(ss->chan) > 0) {
261 smd_read_from_cb(ss->chan, ss->rx_buf,
262 ARRAY_SIZE(ss->rx_buf));
263 complete(&ss->resp_ready);
264 }
265 break;
266 }
267 case SMD_EVENT_OPEN:
268 ss->chan_open = true;
269 break;
270 case SMD_EVENT_CLOSE:
271 ss->chan_open = false;
272 break;
273 }
274}
275
276static int sysmon_probe(struct platform_device *pdev)
277{
Matt Wagantallf8020902011-08-30 21:19:23 -0700278 struct sysmon_subsys *ss;
279 int ret;
280
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700281 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
Matt Wagantallf8020902011-08-30 21:19:23 -0700282 return -ENODEV;
283
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700284 ss = &subsys[pdev->id];
Matt Wagantallf8020902011-08-30 21:19:23 -0700285 mutex_init(&ss->lock);
Devin Kim5980aa82012-10-12 11:38:53 -0700286 ss->probed = true;
Matt Wagantallf8020902011-08-30 21:19:23 -0700287
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700288 switch (ss->transport) {
289 case TRANSPORT_SMD:
290 if (pdev->id >= SMD_NUM_TYPE)
291 return -EINVAL;
292
293 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
294 sysmon_smd_notify);
295 if (ret) {
296 pr_err("SMD open failed\n");
297 return ret;
298 }
299
300 smd_disable_read_intr(ss->chan);
301 break;
302 case TRANSPORT_HSIC:
303 if (pdev->id < SMD_NUM_TYPE)
304 return -EINVAL;
305
Jack Phamb754b042013-02-13 11:54:14 -0800306 ss->hsic_id = HSIC_SYSMON_DEV_EXT_MODEM +
307 (pdev->id - SYSMON_SS_EXT_MODEM);
308 ret = hsic_sysmon_open(ss->hsic_id);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700309 if (ret) {
310 pr_err("HSIC open failed\n");
311 return ret;
312 }
313 break;
314 default:
315 return -EINVAL;
Matt Wagantallf8020902011-08-30 21:19:23 -0700316 }
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700317 ss->dev = &pdev->dev;
Matt Wagantallf8020902011-08-30 21:19:23 -0700318
319 return 0;
320}
321
322static int __devexit sysmon_remove(struct platform_device *pdev)
323{
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700324 struct sysmon_subsys *ss = &subsys[pdev->id];
325
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700326 ss->dev = NULL;
327
328 mutex_lock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700329 switch (ss->transport) {
330 case TRANSPORT_SMD:
331 smd_close(ss->chan);
332 break;
333 case TRANSPORT_HSIC:
Jack Phamb754b042013-02-13 11:54:14 -0800334 hsic_sysmon_close(ss->hsic_id);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700335 break;
336 }
Matt Wagantall9414bbe2012-10-25 15:04:37 -0700337 mutex_unlock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700338
Matt Wagantallf8020902011-08-30 21:19:23 -0700339 return 0;
340}
341
342static struct platform_driver sysmon_driver = {
343 .probe = sysmon_probe,
344 .remove = __devexit_p(sysmon_remove),
345 .driver = {
346 .name = "sys_mon",
347 .owner = THIS_MODULE,
348 },
349};
350
351static int __init sysmon_init(void)
352{
353 return platform_driver_register(&sysmon_driver);
354}
355subsys_initcall(sysmon_init);
356
357static void __exit sysmon_exit(void)
358{
359 platform_driver_unregister(&sysmon_driver);
360}
361module_exit(sysmon_exit);
362
363MODULE_LICENSE("GPL v2");
364MODULE_DESCRIPTION("system monitor communication library");
365MODULE_ALIAS("platform:sys_mon");