summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c37
-rw-r--r--drivers/platform/chrome/cros_ec_dev.h6
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c85
3 files changed, 125 insertions, 3 deletions
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 20ce1c23fb5c..7c2622319211 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -21,6 +21,7 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
@@ -435,6 +436,10 @@ static int ec_device_probe(struct platform_device *pdev)
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
cros_ec_sensors_register(ec);
+ /* Take control of the lightbar from the EC. */
+ if (ec_has_lightbar(ec))
+ lb_manual_suspend_ctrl(ec, 1);
+
return 0;
failed:
@@ -446,6 +451,10 @@ static int ec_device_remove(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
+ /* Let the EC take over the lightbar again. */
+ if (ec_has_lightbar(ec))
+ lb_manual_suspend_ctrl(ec, 0);
+
cros_ec_debugfs_remove(ec);
cdev_del(&ec->cdev);
@@ -459,9 +468,37 @@ static const struct platform_device_id cros_ec_id[] = {
};
MODULE_DEVICE_TABLE(platform, cros_ec_id);
+static int ec_device_suspend(struct device *dev)
+{
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ if (ec_has_lightbar(ec))
+ lb_suspend(ec);
+
+ return 0;
+}
+
+static int ec_device_resume(struct device *dev)
+{
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ if (ec_has_lightbar(ec))
+ lb_resume(ec);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cros_ec_dev_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = ec_device_suspend,
+ .resume = ec_device_resume,
+#endif
+};
+
static struct platform_driver cros_ec_dev_driver = {
.driver = {
.name = "cros-ec-ctl",
+ .pm = &cros_ec_dev_pm_ops,
},
.probe = ec_device_probe,
.remove = ec_device_remove,
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h
index bfd2c84c3571..45e9453608c5 100644
--- a/drivers/platform/chrome/cros_ec_dev.h
+++ b/drivers/platform/chrome/cros_ec_dev.h
@@ -43,4 +43,10 @@ struct cros_ec_readmem {
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
+/* Lightbar utilities */
+extern bool ec_has_lightbar(struct cros_ec_dev *ec);
+extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable);
+extern int lb_suspend(struct cros_ec_dev *ec);
+extern int lb_resume(struct cros_ec_dev *ec);
+
#endif /* _CROS_EC_DEV_H_ */
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index 26675059707e..4df379dc4bb9 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -341,6 +341,80 @@ exit:
return ret;
}
+static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
+{
+ struct ec_params_lightbar *param;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = cmd;
+
+ ret = lb_throttle();
+ if (ret)
+ goto error;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0)
+ goto error;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = 0;
+error:
+ kfree(msg);
+
+ return ret;
+}
+
+int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
+{
+ struct ec_params_lightbar *param;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+
+ param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
+ param->manual_suspend_ctrl.enable = enable;
+
+ ret = lb_throttle();
+ if (ret)
+ goto error;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0)
+ goto error;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = 0;
+error:
+ kfree(msg);
+
+ return ret;
+}
+
+int lb_suspend(struct cros_ec_dev *ec)
+{
+ return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND);
+}
+
+int lb_resume(struct cros_ec_dev *ec)
+{
+ return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME);
+}
+
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -473,6 +547,11 @@ static struct attribute *__lb_cmds_attrs[] = {
NULL,
};
+bool ec_has_lightbar(struct cros_ec_dev *ec)
+{
+ return !!get_lightbar_version(ec, NULL, NULL);
+}
+
static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
@@ -489,10 +568,10 @@ static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
return 0;
/* Only instantiate this stuff if the EC has a lightbar */
- if (get_lightbar_version(ec, NULL, NULL))
+ if (ec_has_lightbar(ec))
return a->mode;
- else
- return 0;
+
+ return 0;
}
struct attribute_group cros_ec_lightbar_attr_group = {