blob: 0d62e6ad8759db38932e69563aa38cb3adaebeaf [file] [log] [blame]
Matt Wagantallf8020902011-08-30 21:19:23 -07001/*
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -07002 * Copyright (c) 2011-2012, Code Aurora Forum. 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;
41 struct smd_channel *chan;
42 bool chan_open;
43 struct completion resp_ready;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070044 char rx_buf[RX_BUF_SIZE];
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070045 enum transports transport;
Matt Wagantallf8020902011-08-30 21:19:23 -070046};
47
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070048static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
49 [SYSMON_SS_MODEM].transport = TRANSPORT_SMD,
50 [SYSMON_SS_LPASS].transport = TRANSPORT_SMD,
51 [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD,
52 [SYSMON_SS_DSPS].transport = TRANSPORT_SMD,
53 [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD,
54 [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
55};
Matt Wagantallf8020902011-08-30 21:19:23 -070056
57static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
58 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
59 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
60 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
61 [SUBSYS_AFTER_POWERUP] = "after_powerup",
62};
63
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070064static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf,
65 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070066{
67 int ret;
68
69 if (!ss->chan_open)
70 return -ENODEV;
71
72 init_completion(&ss->resp_ready);
73 pr_debug("Sending SMD message: %s\n", tx_buf);
74 smd_write(ss->chan, tx_buf, len);
75 ret = wait_for_completion_timeout(&ss->resp_ready,
76 msecs_to_jiffies(TIMEOUT_MS));
77 if (!ret)
78 return -ETIMEDOUT;
79
80 return 0;
81}
82
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070083static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf,
84 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070085{
86 int ret;
87 size_t actual_len;
88
89 pr_debug("Sending HSIC message: %s\n", tx_buf);
90 ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
91 tx_buf, len, TIMEOUT_MS);
92 if (ret)
93 return ret;
94 ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
95 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
96 return ret;
97}
98
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070099static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf,
100 size_t len)
101{
102 int ret;
103
104 switch (ss->transport) {
105 case TRANSPORT_SMD:
106 ret = sysmon_send_smd(ss, tx_buf, len);
107 break;
108 case TRANSPORT_HSIC:
109 ret = sysmon_send_hsic(ss, tx_buf, len);
110 break;
111 default:
112 ret = -EINVAL;
113 }
114
115 if (!ret)
116 pr_debug("Received response: %s\n", ss->rx_buf);
117
118 return ret;
119}
120
121/**
122 * sysmon_send_event() - Notify a subsystem of another's state change
123 * @dest_ss: ID of subsystem the notification should be sent to
124 * @event_ss: String name of the subsystem that generated the notification
125 * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN)
126 *
127 * Returns 0 for success, -EINVAL for invalid destination or notification IDs,
128 * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination
129 * subsystem does not respond, and -ENOSYS if the destination subsystem
130 * responds, but with something other than an acknowledgement.
131 *
132 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
133 */
Matt Wagantallf8020902011-08-30 21:19:23 -0700134int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
135 enum subsys_notif_type notif)
136{
137 struct sysmon_subsys *ss = &subsys[dest_ss];
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700138 char tx_buf[TX_BUF_SIZE];
Matt Wagantallf8020902011-08-30 21:19:23 -0700139 int ret;
140
141 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
142 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
143 event_ss == NULL)
144 return -EINVAL;
145
Matt Wagantallf8020902011-08-30 21:19:23 -0700146 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
147 notif_name[notif]);
Matt Wagantallf8020902011-08-30 21:19:23 -0700148
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700149 mutex_lock(&ss->lock);
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700150 ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700151 if (ret)
152 goto out;
153
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700154 if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
155 ret = -ENOSYS;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700156out:
157 mutex_unlock(&ss->lock);
158 return ret;
159}
160
161/**
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700162 * sysmon_send_shutdown() - send shutdown command to a
163 * subsystem.
164 * @dest_ss: ID of subsystem to send to.
165 *
166 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
167 * the SMD transport channel is not open, -ETIMEDOUT if the destination
168 * subsystem does not respond, and -ENOSYS if the destination subsystem
169 * responds with something unexpected.
170 *
171 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
172 */
173int sysmon_send_shutdown(enum subsys_id dest_ss)
174{
175 struct sysmon_subsys *ss = &subsys[dest_ss];
176 const char tx_buf[] = "ssr:poweroff";
177 int ret;
178
179 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS)
180 return -EINVAL;
181
182 mutex_lock(&ss->lock);
183 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
184 mutex_unlock(&ss->lock);
185 return ret;
186}
187
188/**
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700189 * sysmon_get_reason() - Retrieve failure reason from a subsystem.
190 * @dest_ss: ID of subsystem to query
191 * @buf: Caller-allocated buffer for the returned NUL-terminated reason
192 * @len: Length of @buf
193 *
194 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
195 * the SMD transport channel is not open, -ETIMEDOUT if the destination
196 * subsystem does not respond, and -ENOSYS if the destination subsystem
197 * responds with something unexpected.
198 *
199 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
200 */
201int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len)
202{
203 struct sysmon_subsys *ss = &subsys[dest_ss];
204 const char tx_buf[] = "ssr:retrieve:sfr";
205 const char expect[] = "ssr:return:";
206 size_t prefix_len = ARRAY_SIZE(expect) - 1;
207 int ret;
208
209 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
210 buf == NULL || len == 0)
211 return -EINVAL;
212
Kim,Do-Yeob1d7938c2012-08-21 14:48:25 +0900213 if ((!ss->chan_open) && (dest_ss != SYSMON_SS_EXT_MODEM))
Iliyan Malcheva4bfdcd2012-07-03 14:24:56 -0700214 return -ENODEV;
215
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700216 mutex_lock(&ss->lock);
217 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
218 if (ret)
219 goto out;
220
221 if (strncmp(ss->rx_buf, expect, prefix_len)) {
222 ret = -ENOSYS;
223 goto out;
224 }
225 strlcpy(buf, ss->rx_buf + prefix_len, len);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700226out:
227 mutex_unlock(&ss->lock);
Matt Wagantallf8020902011-08-30 21:19:23 -0700228 return ret;
229}
230
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700231static void sysmon_smd_notify(void *priv, unsigned int smd_event)
Matt Wagantallf8020902011-08-30 21:19:23 -0700232{
233 struct sysmon_subsys *ss = priv;
234
235 switch (smd_event) {
236 case SMD_EVENT_DATA: {
237 if (smd_read_avail(ss->chan) > 0) {
238 smd_read_from_cb(ss->chan, ss->rx_buf,
239 ARRAY_SIZE(ss->rx_buf));
240 complete(&ss->resp_ready);
241 }
242 break;
243 }
244 case SMD_EVENT_OPEN:
245 ss->chan_open = true;
246 break;
247 case SMD_EVENT_CLOSE:
248 ss->chan_open = false;
249 break;
250 }
251}
252
253static int sysmon_probe(struct platform_device *pdev)
254{
Matt Wagantallf8020902011-08-30 21:19:23 -0700255 struct sysmon_subsys *ss;
256 int ret;
257
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700258 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
Matt Wagantallf8020902011-08-30 21:19:23 -0700259 return -ENODEV;
260
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700261 ss = &subsys[pdev->id];
Matt Wagantallf8020902011-08-30 21:19:23 -0700262 mutex_init(&ss->lock);
263
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700264 switch (ss->transport) {
265 case TRANSPORT_SMD:
266 if (pdev->id >= SMD_NUM_TYPE)
267 return -EINVAL;
268
269 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
270 sysmon_smd_notify);
271 if (ret) {
272 pr_err("SMD open failed\n");
273 return ret;
274 }
275
276 smd_disable_read_intr(ss->chan);
277 break;
278 case TRANSPORT_HSIC:
279 if (pdev->id < SMD_NUM_TYPE)
280 return -EINVAL;
281
282 ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
283 if (ret) {
284 pr_err("HSIC open failed\n");
285 return ret;
286 }
287 break;
288 default:
289 return -EINVAL;
Matt Wagantallf8020902011-08-30 21:19:23 -0700290 }
Matt Wagantallf8020902011-08-30 21:19:23 -0700291
292 return 0;
293}
294
295static int __devexit sysmon_remove(struct platform_device *pdev)
296{
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700297 struct sysmon_subsys *ss = &subsys[pdev->id];
298
299 switch (ss->transport) {
300 case TRANSPORT_SMD:
301 smd_close(ss->chan);
302 break;
303 case TRANSPORT_HSIC:
304 hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
305 break;
306 }
307
Matt Wagantallf8020902011-08-30 21:19:23 -0700308 return 0;
309}
310
311static struct platform_driver sysmon_driver = {
312 .probe = sysmon_probe,
313 .remove = __devexit_p(sysmon_remove),
314 .driver = {
315 .name = "sys_mon",
316 .owner = THIS_MODULE,
317 },
318};
319
320static int __init sysmon_init(void)
321{
322 return platform_driver_register(&sysmon_driver);
323}
324subsys_initcall(sysmon_init);
325
326static void __exit sysmon_exit(void)
327{
328 platform_driver_unregister(&sysmon_driver);
329}
330module_exit(sysmon_exit);
331
332MODULE_LICENSE("GPL v2");
333MODULE_DESCRIPTION("system monitor communication library");
334MODULE_ALIAS("platform:sys_mon");