From f651eb15baee2078238b4fa038486d6d89d454ed Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megous@megous.com>
Date: Tue, 8 Sep 2020 02:51:57 +0200
Subject: [PATCH 259/467] misc: modem-power: Add blocking variant of sysfs
 power state update function

Makes writing modem powerup scripts easier.

Signed-off-by: Ondrej Jirman <megous@megous.com>
---
 drivers/misc/modem-power.c | 77 +++++++++++++++++++++++++-------------
 1 file changed, 50 insertions(+), 27 deletions(-)

diff --git a/drivers/misc/modem-power.c b/drivers/misc/modem-power.c
index 5cb4e76c7..68aa7ecc6 100644
--- a/drivers/misc/modem-power.c
+++ b/drivers/misc/modem-power.c
@@ -944,14 +944,7 @@ static void mpwr_power_down(struct mpwr_dev* mpwr)
 
 	dev_info(dev, "powering down");
 
-	set_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
-	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
-
 	ret = mpwr->variant->power_down(mpwr);
-
-	clear_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
-	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
-
 	if (ret) {
 		dev_err(dev, "power down failed");
 	} else {
@@ -978,14 +971,7 @@ static void mpwr_power_up(struct mpwr_dev* mpwr)
 
 	dev_info(dev, "powering up");
 
-	set_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
-	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
-
 	ret = mpwr->variant->power_up(mpwr);
-
-	clear_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
-	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
-
 	if (ret) {
 		dev_err(dev, "power up failed");
 	} else {
@@ -1099,9 +1085,27 @@ static void mpwr_work_handler(struct work_struct *work)
 
 	mutex_unlock(&mpwr->modem_lock);
 
+	clear_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
+	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
+	wake_up(&mpwr->wait);
+
 	pm_relax(mpwr->dev);
 }
 
+static void mpwr_request_power_change(struct mpwr_dev* mpwr, int request)
+{
+	unsigned long flags;
+
+	set_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags);
+	sysfs_notify(&mpwr->dev->kobj, NULL, "is_busy");
+
+	spin_lock_irqsave(&mpwr->lock, flags);
+	mpwr->last_request = request;
+	spin_unlock_irqrestore(&mpwr->lock, flags);
+
+	queue_work(mpwr->wq, &mpwr->power_work);
+}
+
 static irqreturn_t mpwr_gpio_isr(int irq, void *dev_id)
 {
 	struct mpwr_dev *mpwr = dev_id;
@@ -1162,7 +1166,6 @@ static ssize_t powered_store(struct device *dev,
 			     const char *buf, size_t len)
 {
 	struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev));
-	unsigned long flags;
 	bool status;
 	int ret;
 
@@ -1170,11 +1173,35 @@ static ssize_t powered_store(struct device *dev,
 	if (ret)
 		return ret;
 
-	spin_lock_irqsave(&mpwr->lock, flags);
-	mpwr->last_request = status ? MPWR_REQ_PWUP : MPWR_REQ_PWDN;
-	spin_unlock_irqrestore(&mpwr->lock, flags);
+	mpwr_request_power_change(mpwr, status ? MPWR_REQ_PWUP : MPWR_REQ_PWDN);
 
-	queue_work(mpwr->wq, &mpwr->power_work);
+	return len;
+}
+
+static ssize_t powered_blocking_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t len)
+{
+	struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev));
+	bool status;
+	int ret;
+
+	ret = kstrtobool(buf, &status);
+	if (ret)
+		return ret;
+
+	mpwr_request_power_change(mpwr, status ? MPWR_REQ_PWUP : MPWR_REQ_PWDN);
+
+	ret = wait_event_interruptible_timeout(mpwr->wait,
+					       !test_bit(MPWR_F_POWER_CHANGE_INPROGRESS, mpwr->flags),
+					       msecs_to_jiffies(60000));
+	if (ret <= 0) {
+		dev_err(mpwr->dev, "Power state change timeout\n");
+		return -EIO;
+	}
+
+	if (!!status != !!test_bit(MPWR_F_POWERED, mpwr->flags))
+		return -EIO;
 
 	return len;
 }
@@ -1260,25 +1287,20 @@ static ssize_t hard_reset_store(struct device *dev,
 				const char *buf, size_t len)
 {
 	struct mpwr_dev *mpwr = platform_get_drvdata(to_platform_device(dev));
-	unsigned long flags;
 	bool val;
 	int ret;
 
 	ret = kstrtobool(buf, &val);
 	if (ret)
 		return ret;
-
-	if (val) {
-		spin_lock_irqsave(&mpwr->lock, flags);
-		mpwr->last_request = MPWR_REQ_RESET;
-		spin_unlock_irqrestore(&mpwr->lock, flags);
-		queue_work(mpwr->wq, &mpwr->power_work);
-	}
+	if (val)
+		mpwr_request_power_change(mpwr, MPWR_REQ_RESET);
 
 	return len;
 }
 
 static DEVICE_ATTR_RW(powered);
+static DEVICE_ATTR_WO(powered_blocking);
 static DEVICE_ATTR_RW(dumb_powerup);
 static DEVICE_ATTR_RW(wait_rdy);
 static DEVICE_ATTR_RO(killswitched);
@@ -1287,6 +1309,7 @@ static DEVICE_ATTR_WO(hard_reset);
 
 static struct attribute *mpwr_attrs[] = {
 	&dev_attr_powered.attr,
+	&dev_attr_powered_blocking.attr,
 	&dev_attr_dumb_powerup.attr,
 	&dev_attr_wait_rdy.attr,
 	&dev_attr_killswitched.attr,
-- 
2.34.0

