blob: 4b151cce3afa1e73f2d610832122a35b9c0766e4 [file] [log] [blame]
Matt Wagantallf8020902011-08-30 21:19:23 -07001/*
Duy Truong790f06d2013-02-13 16:38:12 -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;
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 Wagantall93190172012-10-25 15:04:37 -070046 struct device *dev;
Matt Wagantallf8020902011-08-30 21:19:23 -070047};
48
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070049static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
50 [SYSMON_SS_MODEM].transport = TRANSPORT_SMD,
51 [SYSMON_SS_LPASS].transport = TRANSPORT_SMD,
52 [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD,
53 [SYSMON_SS_DSPS].transport = TRANSPORT_SMD,
54 [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD,
55 [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
56};
Matt Wagantallf8020902011-08-30 21:19:23 -070057
58static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
59 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
60 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
61 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
62 [SUBSYS_AFTER_POWERUP] = "after_powerup",
63};
64
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070065static int sysmon_send_smd(struct sysmon_subsys *ss, const char *tx_buf,
66 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070067{
68 int ret;
69
70 if (!ss->chan_open)
71 return -ENODEV;
72
73 init_completion(&ss->resp_ready);
74 pr_debug("Sending SMD message: %s\n", tx_buf);
75 smd_write(ss->chan, tx_buf, len);
76 ret = wait_for_completion_timeout(&ss->resp_ready,
77 msecs_to_jiffies(TIMEOUT_MS));
78 if (!ret)
79 return -ETIMEDOUT;
80
81 return 0;
82}
83
Matt Wagantalle89b3ac2012-04-30 14:01:39 -070084static int sysmon_send_hsic(struct sysmon_subsys *ss, const char *tx_buf,
85 size_t len)
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070086{
87 int ret;
88 size_t actual_len;
89
90 pr_debug("Sending HSIC message: %s\n", tx_buf);
91 ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
92 tx_buf, len, TIMEOUT_MS);
93 if (ret)
94 return ret;
95 ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
96 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
97 return ret;
98}
99
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700100static int sysmon_send_msg(struct sysmon_subsys *ss, const char *tx_buf,
101 size_t len)
102{
103 int ret;
104
105 switch (ss->transport) {
106 case TRANSPORT_SMD:
107 ret = sysmon_send_smd(ss, tx_buf, len);
108 break;
109 case TRANSPORT_HSIC:
110 ret = sysmon_send_hsic(ss, tx_buf, len);
111 break;
112 default:
113 ret = -EINVAL;
114 }
115
116 if (!ret)
117 pr_debug("Received response: %s\n", ss->rx_buf);
118
119 return ret;
120}
121
122/**
123 * sysmon_send_event() - Notify a subsystem of another's state change
124 * @dest_ss: ID of subsystem the notification should be sent to
125 * @event_ss: String name of the subsystem that generated the notification
126 * @notif: ID of the notification type (ex. SUBSYS_BEFORE_SHUTDOWN)
127 *
128 * Returns 0 for success, -EINVAL for invalid destination or notification IDs,
129 * -ENODEV if the transport channel is not open, -ETIMEDOUT if the destination
130 * subsystem does not respond, and -ENOSYS if the destination subsystem
131 * responds, but with something other than an acknowledgement.
132 *
133 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
134 */
Matt Wagantallf8020902011-08-30 21:19:23 -0700135int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
136 enum subsys_notif_type notif)
137{
138 struct sysmon_subsys *ss = &subsys[dest_ss];
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700139 char tx_buf[TX_BUF_SIZE];
Matt Wagantallf8020902011-08-30 21:19:23 -0700140 int ret;
141
Matt Wagantall93190172012-10-25 15:04:37 -0700142 if (ss->dev == NULL)
143 return -ENODEV;
144
Matt Wagantallf8020902011-08-30 21:19:23 -0700145 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
146 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
147 event_ss == NULL)
148 return -EINVAL;
149
Matt Wagantallf8020902011-08-30 21:19:23 -0700150 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
151 notif_name[notif]);
Matt Wagantallf8020902011-08-30 21:19:23 -0700152
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700153 mutex_lock(&ss->lock);
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700154 ret = sysmon_send_msg(ss, tx_buf, strlen(tx_buf));
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700155 if (ret)
156 goto out;
157
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700158 if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
159 ret = -ENOSYS;
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700160out:
161 mutex_unlock(&ss->lock);
162 return ret;
163}
164
165/**
Joel King069dcbc2012-09-25 15:06:52 -0700166 * sysmon_send_shutdown() - send shutdown command to a
167 * subsystem.
168 * @dest_ss: ID of subsystem to send to.
169 *
170 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
171 * the SMD transport channel is not open, -ETIMEDOUT if the destination
172 * subsystem does not respond, and -ENOSYS if the destination subsystem
173 * responds with something unexpected.
174 *
175 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
176 */
177int sysmon_send_shutdown(enum subsys_id dest_ss)
178{
179 struct sysmon_subsys *ss = &subsys[dest_ss];
180 const char tx_buf[] = "system:shutdown";
181 const char expect[] = "system:ack";
182 size_t prefix_len = ARRAY_SIZE(expect) - 1;
183 int ret;
184
Matt Wagantall93190172012-10-25 15:04:37 -0700185 if (ss->dev == NULL)
186 return -ENODEV;
187
Joel King069dcbc2012-09-25 15:06:52 -0700188 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS)
189 return -EINVAL;
190
191 mutex_lock(&ss->lock);
192 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
193 if (ret)
194 goto out;
195
196 if (strncmp(ss->rx_buf, expect, prefix_len))
197 ret = -ENOSYS;
198out:
199 mutex_unlock(&ss->lock);
200 return ret;
201}
202
203/**
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700204 * sysmon_get_reason() - Retrieve failure reason from a subsystem.
205 * @dest_ss: ID of subsystem to query
206 * @buf: Caller-allocated buffer for the returned NUL-terminated reason
207 * @len: Length of @buf
208 *
209 * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if
210 * the SMD transport channel is not open, -ETIMEDOUT if the destination
211 * subsystem does not respond, and -ENOSYS if the destination subsystem
212 * responds with something unexpected.
213 *
214 * If CONFIG_MSM_SYSMON_COMM is not defined, always return success (0).
215 */
216int sysmon_get_reason(enum subsys_id dest_ss, char *buf, size_t len)
217{
218 struct sysmon_subsys *ss = &subsys[dest_ss];
219 const char tx_buf[] = "ssr:retrieve:sfr";
220 const char expect[] = "ssr:return:";
221 size_t prefix_len = ARRAY_SIZE(expect) - 1;
222 int ret;
223
Matt Wagantall93190172012-10-25 15:04:37 -0700224 if (ss->dev == NULL)
225 return -ENODEV;
226
Matt Wagantalle89b3ac2012-04-30 14:01:39 -0700227 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
228 buf == NULL || len == 0)
229 return -EINVAL;
230
231 mutex_lock(&ss->lock);
232 ret = sysmon_send_msg(ss, tx_buf, ARRAY_SIZE(tx_buf));
233 if (ret)
234 goto out;
235
236 if (strncmp(ss->rx_buf, expect, prefix_len)) {
237 ret = -ENOSYS;
238 goto out;
239 }
240 strlcpy(buf, ss->rx_buf + prefix_len, len);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700241out:
242 mutex_unlock(&ss->lock);
Matt Wagantallf8020902011-08-30 21:19:23 -0700243 return ret;
244}
245
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700246static void sysmon_smd_notify(void *priv, unsigned int smd_event)
Matt Wagantallf8020902011-08-30 21:19:23 -0700247{
248 struct sysmon_subsys *ss = priv;
249
250 switch (smd_event) {
251 case SMD_EVENT_DATA: {
252 if (smd_read_avail(ss->chan) > 0) {
253 smd_read_from_cb(ss->chan, ss->rx_buf,
254 ARRAY_SIZE(ss->rx_buf));
255 complete(&ss->resp_ready);
256 }
257 break;
258 }
259 case SMD_EVENT_OPEN:
260 ss->chan_open = true;
261 break;
262 case SMD_EVENT_CLOSE:
263 ss->chan_open = false;
264 break;
265 }
266}
267
268static int sysmon_probe(struct platform_device *pdev)
269{
Matt Wagantallf8020902011-08-30 21:19:23 -0700270 struct sysmon_subsys *ss;
271 int ret;
272
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700273 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
Matt Wagantallf8020902011-08-30 21:19:23 -0700274 return -ENODEV;
275
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700276 ss = &subsys[pdev->id];
Matt Wagantallf8020902011-08-30 21:19:23 -0700277 mutex_init(&ss->lock);
278
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700279 switch (ss->transport) {
280 case TRANSPORT_SMD:
281 if (pdev->id >= SMD_NUM_TYPE)
282 return -EINVAL;
283
284 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
285 sysmon_smd_notify);
286 if (ret) {
287 pr_err("SMD open failed\n");
288 return ret;
289 }
290
291 smd_disable_read_intr(ss->chan);
292 break;
293 case TRANSPORT_HSIC:
294 if (pdev->id < SMD_NUM_TYPE)
295 return -EINVAL;
296
297 ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
298 if (ret) {
299 pr_err("HSIC open failed\n");
300 return ret;
301 }
302 break;
303 default:
304 return -EINVAL;
Matt Wagantallf8020902011-08-30 21:19:23 -0700305 }
Matt Wagantall93190172012-10-25 15:04:37 -0700306 ss->dev = &pdev->dev;
Matt Wagantallf8020902011-08-30 21:19:23 -0700307
308 return 0;
309}
310
311static int __devexit sysmon_remove(struct platform_device *pdev)
312{
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700313 struct sysmon_subsys *ss = &subsys[pdev->id];
314
Matt Wagantall93190172012-10-25 15:04:37 -0700315 ss->dev = NULL;
316
317 mutex_lock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700318 switch (ss->transport) {
319 case TRANSPORT_SMD:
320 smd_close(ss->chan);
321 break;
322 case TRANSPORT_HSIC:
323 hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
324 break;
325 }
Matt Wagantall93190172012-10-25 15:04:37 -0700326 mutex_unlock(&ss->lock);
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700327
Matt Wagantallf8020902011-08-30 21:19:23 -0700328 return 0;
329}
330
331static struct platform_driver sysmon_driver = {
332 .probe = sysmon_probe,
333 .remove = __devexit_p(sysmon_remove),
334 .driver = {
335 .name = "sys_mon",
336 .owner = THIS_MODULE,
337 },
338};
339
340static int __init sysmon_init(void)
341{
342 return platform_driver_register(&sysmon_driver);
343}
344subsys_initcall(sysmon_init);
345
346static void __exit sysmon_exit(void)
347{
348 platform_driver_unregister(&sysmon_driver);
349}
350module_exit(sysmon_exit);
351
352MODULE_LICENSE("GPL v2");
353MODULE_DESCRIPTION("system monitor communication library");
354MODULE_ALIAS("platform:sys_mon");