Skip to content

Commit d278b7a

Browse files
committed
Platform: OLPC: add a suspended flag to the EC driver
A problem we've noticed on XO-1.75 is when we suspend in the middle of an EC command. Don't allow that. In the process, create a private object for the generic EC driver to use; we have a framework for passing around a struct, use that rather than a proliferation of global variables. Signed-off-by: Andres Salomon <dilinger@queued.net> Acked-by: Paul Fox <pgf@laptop.org> Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
1 parent ac25041 commit d278b7a

File tree

1 file changed

+45
-1
lines changed

1 file changed

+45
-1
lines changed

drivers/platform/olpc/olpc-ec.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/spinlock.h>
1010
#include <linux/mutex.h>
1111
#include <linux/platform_device.h>
12+
#include <linux/slab.h>
1213
#include <linux/workqueue.h>
1314
#include <linux/module.h>
1415
#include <linux/list.h>
@@ -27,13 +28,29 @@ struct ec_cmd_desc {
2728
void *priv;
2829
};
2930

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+
3046
static void olpc_ec_worker(struct work_struct *w);
3147

3248
static DECLARE_WORK(ec_worker, olpc_ec_worker);
3349
static LIST_HEAD(ec_cmd_q);
3450
static DEFINE_SPINLOCK(ec_cmd_q_lock);
3551

3652
static struct olpc_ec_driver *ec_driver;
53+
static struct olpc_ec_priv *ec_priv;
3754
static void *ec_cb_arg;
3855
static DEFINE_MUTEX(ec_cb_lock);
3956

@@ -93,6 +110,7 @@ static void queue_ec_descriptor(struct ec_cmd_desc *desc)
93110

94111
int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
95112
{
113+
struct olpc_ec_priv *ec = ec_priv;
96114
struct ec_cmd_desc desc;
97115

98116
/* 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)
104122
if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
105123
return -ENODEV;
106124

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+
107132
might_sleep();
108133

109134
desc.cmd = cmd;
@@ -126,11 +151,19 @@ EXPORT_SYMBOL_GPL(olpc_ec_cmd);
126151

127152
static int olpc_ec_probe(struct platform_device *pdev)
128153
{
154+
struct olpc_ec_priv *ec;
129155
int err;
130156

131157
if (!ec_driver)
132158
return -ENODEV;
133159

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+
134167
err = ec_driver->probe ? ec_driver->probe(pdev) : 0;
135168

136169
return err;
@@ -139,12 +172,23 @@ static int olpc_ec_probe(struct platform_device *pdev)
139172
static int olpc_ec_suspend(struct device *dev)
140173
{
141174
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;
143184
}
144185

145186
static int olpc_ec_resume(struct device *dev)
146187
{
147188
struct platform_device *pdev = to_platform_device(dev);
189+
struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
190+
191+
ec->suspended = false;
148192
return ec_driver->resume ? ec_driver->resume(pdev) : 0;
149193
}
150194

0 commit comments

Comments
 (0)