diff options
author | Shawn Nematbakhsh <shawnn@chromium.org> | 2016-12-16 12:57:37 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2017-02-13 04:29:42 -0500 |
commit | f00c06fd98576face871e62bb3aa045c5f647661 (patch) | |
tree | 4d90315ad420e0bfca71db76f197efa9869bcb70 | |
parent | a9eb186e13144782232cc6fa731441be54baf505 (diff) |
mfd: cros_ec: Send suspend state notification to EC
Notify EC when going to or returning from suspend so that proper actions
related to wake events can be taken.
Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org>
Signed-off-by: Thierry Escande <thierry.escande@collabora.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r-- | drivers/mfd/cros_ec.c | 49 | ||||
-rw-r--r-- | include/linux/mfd/cros_ec_commands.h | 14 |
2 files changed, 63 insertions, 0 deletions
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index ad48633c9a47..b8a50808bedb 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/mfd/core.h> | 24 | #include <linux/mfd/core.h> |
25 | #include <linux/mfd/cros_ec.h> | 25 | #include <linux/mfd/cros_ec.h> |
26 | #include <linux/suspend.h> | ||
26 | #include <asm/unaligned.h> | 27 | #include <asm/unaligned.h> |
27 | 28 | ||
28 | #define CROS_EC_DEV_EC_INDEX 0 | 29 | #define CROS_EC_DEV_EC_INDEX 0 |
@@ -65,6 +66,24 @@ static irqreturn_t ec_irq_thread(int irq, void *data) | |||
65 | return IRQ_HANDLED; | 66 | return IRQ_HANDLED; |
66 | } | 67 | } |
67 | 68 | ||
69 | static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) | ||
70 | { | ||
71 | struct { | ||
72 | struct cros_ec_command msg; | ||
73 | struct ec_params_host_sleep_event req; | ||
74 | } __packed buf; | ||
75 | |||
76 | memset(&buf, 0, sizeof(buf)); | ||
77 | |||
78 | buf.req.sleep_event = sleep_event; | ||
79 | |||
80 | buf.msg.command = EC_CMD_HOST_SLEEP_EVENT; | ||
81 | buf.msg.version = 0; | ||
82 | buf.msg.outsize = sizeof(buf.req); | ||
83 | |||
84 | return cros_ec_cmd_xfer(ec_dev, &buf.msg); | ||
85 | } | ||
86 | |||
68 | int cros_ec_register(struct cros_ec_device *ec_dev) | 87 | int cros_ec_register(struct cros_ec_device *ec_dev) |
69 | { | 88 | { |
70 | struct device *dev = ec_dev->dev; | 89 | struct device *dev = ec_dev->dev; |
@@ -136,6 +155,15 @@ int cros_ec_register(struct cros_ec_device *ec_dev) | |||
136 | } | 155 | } |
137 | } | 156 | } |
138 | 157 | ||
158 | /* | ||
159 | * Clear sleep event - this will fail harmlessly on platforms that | ||
160 | * don't implement the sleep event host command. | ||
161 | */ | ||
162 | err = cros_ec_sleep_event(ec_dev, 0); | ||
163 | if (err < 0) | ||
164 | dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec", | ||
165 | err); | ||
166 | |||
139 | dev_info(dev, "Chrome EC device registered\n"); | 167 | dev_info(dev, "Chrome EC device registered\n"); |
140 | 168 | ||
141 | return 0; | 169 | return 0; |
@@ -159,6 +187,16 @@ EXPORT_SYMBOL(cros_ec_remove); | |||
159 | int cros_ec_suspend(struct cros_ec_device *ec_dev) | 187 | int cros_ec_suspend(struct cros_ec_device *ec_dev) |
160 | { | 188 | { |
161 | struct device *dev = ec_dev->dev; | 189 | struct device *dev = ec_dev->dev; |
190 | int ret; | ||
191 | u8 sleep_event; | ||
192 | |||
193 | sleep_event = pm_suspend_via_firmware() ? HOST_SLEEP_EVENT_S3_RESUME : | ||
194 | HOST_SLEEP_EVENT_S0IX_RESUME; | ||
195 | |||
196 | ret = cros_ec_sleep_event(ec_dev, sleep_event); | ||
197 | if (ret < 0) | ||
198 | dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec", | ||
199 | ret); | ||
162 | 200 | ||
163 | if (device_may_wakeup(dev)) | 201 | if (device_may_wakeup(dev)) |
164 | ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); | 202 | ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); |
@@ -180,9 +218,20 @@ static void cros_ec_drain_events(struct cros_ec_device *ec_dev) | |||
180 | 218 | ||
181 | int cros_ec_resume(struct cros_ec_device *ec_dev) | 219 | int cros_ec_resume(struct cros_ec_device *ec_dev) |
182 | { | 220 | { |
221 | int ret; | ||
222 | u8 sleep_event; | ||
223 | |||
183 | ec_dev->suspended = false; | 224 | ec_dev->suspended = false; |
184 | enable_irq(ec_dev->irq); | 225 | enable_irq(ec_dev->irq); |
185 | 226 | ||
227 | sleep_event = pm_suspend_via_firmware() ? HOST_SLEEP_EVENT_S3_RESUME : | ||
228 | HOST_SLEEP_EVENT_S0IX_RESUME; | ||
229 | |||
230 | ret = cros_ec_sleep_event(ec_dev, sleep_event); | ||
231 | if (ret < 0) | ||
232 | dev_dbg(ec_dev->dev, "Error %d sending resume event to ec", | ||
233 | ret); | ||
234 | |||
186 | /* | 235 | /* |
187 | * In some cases, we need to distinguish between events that occur | 236 | * In some cases, we need to distinguish between events that occur |
188 | * during suspend if the EC is not a wake source. For example, | 237 | * during suspend if the EC is not a wake source. For example, |
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h index da1c188562bc..34ecae4d612c 100644 --- a/include/linux/mfd/cros_ec_commands.h +++ b/include/linux/mfd/cros_ec_commands.h | |||
@@ -2547,6 +2547,20 @@ struct ec_params_ext_power_current_limit { | |||
2547 | uint32_t limit; /* in mA */ | 2547 | uint32_t limit; /* in mA */ |
2548 | } __packed; | 2548 | } __packed; |
2549 | 2549 | ||
2550 | /* Inform the EC when entering a sleep state */ | ||
2551 | #define EC_CMD_HOST_SLEEP_EVENT 0xa9 | ||
2552 | |||
2553 | enum host_sleep_event { | ||
2554 | HOST_SLEEP_EVENT_S3_SUSPEND = 1, | ||
2555 | HOST_SLEEP_EVENT_S3_RESUME = 2, | ||
2556 | HOST_SLEEP_EVENT_S0IX_SUSPEND = 3, | ||
2557 | HOST_SLEEP_EVENT_S0IX_RESUME = 4 | ||
2558 | }; | ||
2559 | |||
2560 | struct ec_params_host_sleep_event { | ||
2561 | uint8_t sleep_event; | ||
2562 | } __packed; | ||
2563 | |||
2550 | /*****************************************************************************/ | 2564 | /*****************************************************************************/ |
2551 | /* Smart battery pass-through */ | 2565 | /* Smart battery pass-through */ |
2552 | 2566 | ||