blob: 92964501baa01bd9b0fdd6b788cef9c7cc7bcc89 [file] [log] [blame]
Matt Wagantallf8020902011-08-30 21:19:23 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
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
27#include "sysmon.h"
28
29#define MAX_MSG_LENGTH 50
30#define TIMEOUT_MS 5000
31
32struct sysmon_subsys {
33 struct mutex lock;
34 struct smd_channel *chan;
35 bool chan_open;
36 struct completion resp_ready;
37 char rx_buf[MAX_MSG_LENGTH];
38};
39
40static struct sysmon_subsys subsys[SYSMON_NUM_SS];
41
42static const char *notif_name[SUBSYS_NOTIF_TYPE_COUNT] = {
43 [SUBSYS_BEFORE_SHUTDOWN] = "before_shutdown",
44 [SUBSYS_AFTER_SHUTDOWN] = "after_shutdown",
45 [SUBSYS_BEFORE_POWERUP] = "before_powerup",
46 [SUBSYS_AFTER_POWERUP] = "after_powerup",
47};
48
49int sysmon_send_event(enum subsys_id dest_ss, const char *event_ss,
50 enum subsys_notif_type notif)
51{
52 struct sysmon_subsys *ss = &subsys[dest_ss];
53 char tx_buf[MAX_MSG_LENGTH];
54 int ret;
55
56 if (dest_ss < 0 || dest_ss >= SYSMON_NUM_SS ||
57 notif < 0 || notif >= SUBSYS_NOTIF_TYPE_COUNT ||
58 event_ss == NULL)
59 return -EINVAL;
60
61 if (!ss->chan_open)
62 return -ENODEV;
63
64 mutex_lock(&ss->lock);
65 init_completion(&ss->resp_ready);
66 snprintf(tx_buf, ARRAY_SIZE(tx_buf), "ssr:%s:%s", event_ss,
67 notif_name[notif]);
68 pr_debug("Sending message: %s\n", tx_buf);
69 smd_write(ss->chan, tx_buf, ARRAY_SIZE(tx_buf));
70 ret = wait_for_completion_timeout(&ss->resp_ready,
71 msecs_to_jiffies(TIMEOUT_MS));
72 if (!ret) {
73 ret = -ETIMEDOUT;
74 } else if (strncmp(ss->rx_buf, "ssr:ack", ARRAY_SIZE(ss->rx_buf))) {
75 pr_debug("Received response: %s\n", ss->rx_buf);
76 ret = -ENOSYS;
77 } else {
78 ret = 0;
79 }
80 mutex_unlock(&ss->lock);
81
82 return ret;
83}
84
85static void sysmon_notify(void *priv, unsigned int smd_event)
86{
87 struct sysmon_subsys *ss = priv;
88
89 switch (smd_event) {
90 case SMD_EVENT_DATA: {
91 if (smd_read_avail(ss->chan) > 0) {
92 smd_read_from_cb(ss->chan, ss->rx_buf,
93 ARRAY_SIZE(ss->rx_buf));
94 complete(&ss->resp_ready);
95 }
96 break;
97 }
98 case SMD_EVENT_OPEN:
99 ss->chan_open = true;
100 break;
101 case SMD_EVENT_CLOSE:
102 ss->chan_open = false;
103 break;
104 }
105}
106
107static int sysmon_probe(struct platform_device *pdev)
108{
109 static const uint32_t ss_map[SMD_NUM_TYPE] = {
110 [SMD_APPS_MODEM] = SYSMON_SS_MODEM,
111 [SMD_APPS_QDSP] = SYSMON_SS_LPASS,
112 [SMD_APPS_WCNSS] = SYSMON_SS_WCNSS,
113 [SMD_APPS_DSPS] = SYSMON_SS_DSPS,
114 [SMD_APPS_Q6FW] = SYSMON_SS_Q6FW,
115 };
116 struct sysmon_subsys *ss;
117 int ret;
118
119 if (pdev == NULL)
120 return -EINVAL;
121
122 if (pdev->id < 0 || pdev->id >= SMD_NUM_TYPE ||
123 ss_map[pdev->id] < 0 || ss_map[pdev->id] >= SYSMON_NUM_SS)
124 return -ENODEV;
125
126 ss = &subsys[ss_map[pdev->id]];
127 mutex_init(&ss->lock);
128
129 /* Open and configure the SMD channel */
130 ret = smd_named_open_on_edge("sys_mon", pdev->id, &ss->chan,
131 ss, sysmon_notify);
132 if (ret) {
133 pr_err("SMD open failed\n");
134 return -ENOSYS;
135 }
136 smd_disable_read_intr(ss->chan);
137
138 return 0;
139}
140
141static int __devexit sysmon_remove(struct platform_device *pdev)
142{
143 smd_close(subsys[pdev->id].chan);
144 return 0;
145}
146
147static struct platform_driver sysmon_driver = {
148 .probe = sysmon_probe,
149 .remove = __devexit_p(sysmon_remove),
150 .driver = {
151 .name = "sys_mon",
152 .owner = THIS_MODULE,
153 },
154};
155
156static int __init sysmon_init(void)
157{
158 return platform_driver_register(&sysmon_driver);
159}
160subsys_initcall(sysmon_init);
161
162static void __exit sysmon_exit(void)
163{
164 platform_driver_unregister(&sysmon_driver);
165}
166module_exit(sysmon_exit);
167
168MODULE_LICENSE("GPL v2");
169MODULE_DESCRIPTION("system monitor communication library");
170MODULE_ALIAS("platform:sys_mon");