blob: ddb850207569b2b1d1c1ff63d3ebf43b95aef581 [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
30#define MAX_MSG_LENGTH 50
31#define TIMEOUT_MS 5000
32
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070033enum transports {
34 TRANSPORT_SMD,
35 TRANSPORT_HSIC,
36};
37
Matt Wagantallf8020902011-08-30 21:19:23 -070038struct sysmon_subsys {
39 struct mutex lock;
40 struct smd_channel *chan;
41 bool chan_open;
42 struct completion resp_ready;
43 char rx_buf[MAX_MSG_LENGTH];
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070044 enum transports transport;
Matt Wagantallf8020902011-08-30 21:19:23 -070045};
46
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070047static struct sysmon_subsys subsys[SYSMON_NUM_SS] = {
48 [SYSMON_SS_MODEM].transport = TRANSPORT_SMD,
49 [SYSMON_SS_LPASS].transport = TRANSPORT_SMD,
50 [SYSMON_SS_WCNSS].transport = TRANSPORT_SMD,
51 [SYSMON_SS_DSPS].transport = TRANSPORT_SMD,
52 [SYSMON_SS_Q6FW].transport = TRANSPORT_SMD,
53 [SYSMON_SS_EXT_MODEM].transport = TRANSPORT_HSIC,
54};
Matt Wagantallf8020902011-08-30 21:19:23 -070055
56static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
57 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
58 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
59 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
60 [SUBSYS_AFTER_POWERUP] = "after_powerup",
61};
62
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -070063static int sysmon_send_smd(struct sysmon_subsys *ss, char *tx_buf, size_t len)
64{
65 int ret;
66
67 if (!ss->chan_open)
68 return -ENODEV;
69
70 init_completion(&ss->resp_ready);
71 pr_debug("Sending SMD message: %s\n", tx_buf);
72 smd_write(ss->chan, tx_buf, len);
73 ret = wait_for_completion_timeout(&ss->resp_ready,
74 msecs_to_jiffies(TIMEOUT_MS));
75 if (!ret)
76 return -ETIMEDOUT;
77
78 return 0;
79}
80
81static int sysmon_send_hsic(struct sysmon_subsys *ss, char *tx_buf, size_t len)
82{
83 int ret;
84 size_t actual_len;
85
86 pr_debug("Sending HSIC message: %s\n", tx_buf);
87 ret = hsic_sysmon_write(HSIC_SYSMON_DEV_EXT_MODEM,
88 tx_buf, len, TIMEOUT_MS);
89 if (ret)
90 return ret;
91 ret = hsic_sysmon_read(HSIC_SYSMON_DEV_EXT_MODEM, ss->rx_buf,
92 ARRAY_SIZE(ss->rx_buf), &actual_len, TIMEOUT_MS);
93 return ret;
94}
95
Matt Wagantallf8020902011-08-30 21:19:23 -070096int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
97 enum subsys_notif_type notif)
98{
99 struct sysmon_subsys *ss = &subsys[dest_ss];
100 char tx_buf[MAX_MSG_LENGTH];
101 int ret;
102
103 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
104 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
105 event_ss == NULL)
106 return -EINVAL;
107
Matt Wagantallf8020902011-08-30 21:19:23 -0700108 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
109 notif_name[notif]);
Matt Wagantallf8020902011-08-30 21:19:23 -0700110
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700111 mutex_lock(&ss->lock);
112 switch (ss->transport) {
113 case TRANSPORT_SMD:
114 ret = sysmon_send_smd(ss, tx_buf, ARRAY_SIZE(tx_buf));
115 break;
116 case TRANSPORT_HSIC:
117 ret = sysmon_send_hsic(ss, tx_buf, ARRAY_SIZE(tx_buf));
118 break;
119 default:
120 ret = -EINVAL;
121 }
122 if (ret)
123 goto out;
124
125 pr_debug("Received response: %s\n", ss->rx_buf);
126 if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf)))
127 ret = -ENOSYS;
128 else
129 ret = 0;
130out:
131 mutex_unlock(&ss->lock);
Matt Wagantallf8020902011-08-30 21:19:23 -0700132 return ret;
133}
134
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700135static void sysmon_smd_notify(void *priv, unsigned int smd_event)
Matt Wagantallf8020902011-08-30 21:19:23 -0700136{
137 struct sysmon_subsys *ss = priv;
138
139 switch (smd_event) {
140 case SMD_EVENT_DATA: {
141 if (smd_read_avail(ss->chan) > 0) {
142 smd_read_from_cb(ss->chan, ss->rx_buf,
143 ARRAY_SIZE(ss->rx_buf));
144 complete(&ss->resp_ready);
145 }
146 break;
147 }
148 case SMD_EVENT_OPEN:
149 ss->chan_open = true;
150 break;
151 case SMD_EVENT_CLOSE:
152 ss->chan_open = false;
153 break;
154 }
155}
156
157static int sysmon_probe(struct platform_device *pdev)
158{
Matt Wagantallf8020902011-08-30 21:19:23 -0700159 struct sysmon_subsys *ss;
160 int ret;
161
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700162 if (pdev->id < 0 || pdev->id >= SYSMON_NUM_SS)
Matt Wagantallf8020902011-08-30 21:19:23 -0700163 return -ENODEV;
164
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700165 ss = &subsys[pdev->id];
Matt Wagantallf8020902011-08-30 21:19:23 -0700166 mutex_init(&ss->lock);
167
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700168 switch (ss->transport) {
169 case TRANSPORT_SMD:
170 if (pdev->id >= SMD_NUM_TYPE)
171 return -EINVAL;
172
173 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan, ss,
174 sysmon_smd_notify);
175 if (ret) {
176 pr_err("SMD open failed\n");
177 return ret;
178 }
179
180 smd_disable_read_intr(ss->chan);
181 break;
182 case TRANSPORT_HSIC:
183 if (pdev->id < SMD_NUM_TYPE)
184 return -EINVAL;
185
186 ret = hsic_sysmon_open(HSIC_SYSMON_DEV_EXT_MODEM);
187 if (ret) {
188 pr_err("HSIC open failed\n");
189 return ret;
190 }
191 break;
192 default:
193 return -EINVAL;
Matt Wagantallf8020902011-08-30 21:19:23 -0700194 }
Matt Wagantallf8020902011-08-30 21:19:23 -0700195
196 return 0;
197}
198
199static int __devexit sysmon_remove(struct platform_device *pdev)
200{
Matt Wagantallc3d9a2f2012-04-13 12:39:17 -0700201 struct sysmon_subsys *ss = &subsys[pdev->id];
202
203 switch (ss->transport) {
204 case TRANSPORT_SMD:
205 smd_close(ss->chan);
206 break;
207 case TRANSPORT_HSIC:
208 hsic_sysmon_close(HSIC_SYSMON_DEV_EXT_MODEM);
209 break;
210 }
211
Matt Wagantallf8020902011-08-30 21:19:23 -0700212 return 0;
213}
214
215static struct platform_driver sysmon_driver = {
216 .probe = sysmon_probe,
217 .remove = __devexit_p(sysmon_remove),
218 .driver = {
219 .name = "sys_mon",
220 .owner = THIS_MODULE,
221 },
222};
223
224static int __init sysmon_init(void)
225{
226 return platform_driver_register(&sysmon_driver);
227}
228subsys_initcall(sysmon_init);
229
230static void __exit sysmon_exit(void)
231{
232 platform_driver_unregister(&sysmon_driver);
233}
234module_exit(sysmon_exit);
235
236MODULE_LICENSE("GPL v2");
237MODULE_DESCRIPTION("system monitor communication library");
238MODULE_ALIAS("platform:sys_mon");