9
9
#include <linux/spinlock.h>
10
10
#include <linux/mutex.h>
11
11
#include <linux/platform_device.h>
12
+ #include <linux/slab.h>
12
13
#include <linux/workqueue.h>
13
14
#include <linux/module.h>
14
15
#include <linux/list.h>
@@ -27,13 +28,29 @@ struct ec_cmd_desc {
27
28
void * priv ;
28
29
};
29
30
31
+ struct olpc_ec_priv {
32
+ struct olpc_ec_driver * drv ;
33
+
34
+ /*
35
+ * Running an EC command while suspending means we don't always finish
36
+ * the command before the machine suspends. This means that the EC
37
+ * is expecting the command protocol to finish, but we after a period
38
+ * of time (while the OS is asleep) the EC times out and restarts its
39
+ * idle loop. Meanwhile, the OS wakes up, thinks it's still in the
40
+ * middle of the command protocol, starts throwing random things at
41
+ * the EC... and everyone's uphappy.
42
+ */
43
+ bool suspended ;
44
+ };
45
+
30
46
static void olpc_ec_worker (struct work_struct * w );
31
47
32
48
static DECLARE_WORK (ec_worker , olpc_ec_worker ) ;
33
49
static LIST_HEAD (ec_cmd_q );
34
50
static DEFINE_SPINLOCK (ec_cmd_q_lock );
35
51
36
52
static struct olpc_ec_driver * ec_driver ;
53
+ static struct olpc_ec_priv * ec_priv ;
37
54
static void * ec_cb_arg ;
38
55
static DEFINE_MUTEX (ec_cb_lock );
39
56
@@ -93,6 +110,7 @@ static void queue_ec_descriptor(struct ec_cmd_desc *desc)
93
110
94
111
int olpc_ec_cmd (u8 cmd , u8 * inbuf , size_t inlen , u8 * outbuf , size_t outlen )
95
112
{
113
+ struct olpc_ec_priv * ec = ec_priv ;
96
114
struct ec_cmd_desc desc ;
97
115
98
116
/* XXX: this will be removed in later patches */
@@ -104,6 +122,13 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
104
122
if (WARN_ON (!ec_driver || !ec_driver -> ec_cmd ))
105
123
return - ENODEV ;
106
124
125
+ if (!ec )
126
+ return - ENOMEM ;
127
+
128
+ /* Suspending in the middle of a command hoses things really badly */
129
+ if (WARN_ON (ec -> suspended ))
130
+ return - EBUSY ;
131
+
107
132
might_sleep ();
108
133
109
134
desc .cmd = cmd ;
@@ -126,11 +151,19 @@ EXPORT_SYMBOL_GPL(olpc_ec_cmd);
126
151
127
152
static int olpc_ec_probe (struct platform_device * pdev )
128
153
{
154
+ struct olpc_ec_priv * ec ;
129
155
int err ;
130
156
131
157
if (!ec_driver )
132
158
return - ENODEV ;
133
159
160
+ ec = kzalloc (sizeof (* ec ), GFP_KERNEL );
161
+ if (!ec )
162
+ return - ENOMEM ;
163
+ ec -> drv = ec_driver ;
164
+ ec_priv = ec ;
165
+ platform_set_drvdata (pdev , ec );
166
+
134
167
err = ec_driver -> probe ? ec_driver -> probe (pdev ) : 0 ;
135
168
136
169
return err ;
@@ -139,12 +172,23 @@ static int olpc_ec_probe(struct platform_device *pdev)
139
172
static int olpc_ec_suspend (struct device * dev )
140
173
{
141
174
struct platform_device * pdev = to_platform_device (dev );
142
- return ec_driver -> suspend ? ec_driver -> suspend (pdev ) : 0 ;
175
+ struct olpc_ec_priv * ec = platform_get_drvdata (pdev );
176
+ int err = 0 ;
177
+
178
+ if (ec_driver -> suspend )
179
+ err = ec_driver -> suspend (pdev );
180
+ if (!err )
181
+ ec -> suspended = true;
182
+
183
+ return err ;
143
184
}
144
185
145
186
static int olpc_ec_resume (struct device * dev )
146
187
{
147
188
struct platform_device * pdev = to_platform_device (dev );
189
+ struct olpc_ec_priv * ec = platform_get_drvdata (pdev );
190
+
191
+ ec -> suspended = false;
148
192
return ec_driver -> resume ? ec_driver -> resume (pdev ) : 0 ;
149
193
}
150
194
0 commit comments