public inbox for linux-mmc@vger.kernel.org
 help / color / mirror / Atom feed
From: Adrian Hunter <adrian.hunter@intel.com>
To: Ulf Hansson <ulf.hansson@linaro.org>
Cc: linux-mmc <linux-mmc@vger.kernel.org>,
	Haridhar Kalvala <haridhar.kalvala@intel.com>,
	Linux PM <linux-pm@vger.kernel.org>,
	"Rafael J. Wysocki" <rjw@rjwysocki.net>,
	Geert Uytterhoeven <geert@linux-m68k.org>
Subject: Re: [PATCH 1/9] mmc: sdhci-pci: Stop calling sdhci_enable_irq_wakeups()
Date: Mon, 8 Jan 2018 16:49:26 +0200	[thread overview]
Message-ID: <c2d38388-93a0-61c0-e323-74851c08c1fd@intel.com> (raw)
In-Reply-To: <CAPDyKFrZjTvYdT8BGKadTdTBsaAeoC3YOQBBPOvqpv1JpaOUoQ@mail.gmail.com>

On 21/12/17 17:33, Ulf Hansson wrote:
> + linux-pm, Rafael, Geert
> 
> On 21 December 2017 at 15:25, Adrian Hunter <adrian.hunter@intel.com> wrote:
>> On 21/12/17 16:15, Ulf Hansson wrote:
>>> On 20 December 2017 at 09:18, Adrian Hunter <adrian.hunter@intel.com> wrote:
>>>> sdhci_enable_irq_wakeups() is already called by sdhci_suspend_host() so
>>>> sdhci-pci should not need to call it. However sdhci_suspend_host() only
>>>> calls it if wakeups are enabled, and sdhci-pci does not enable them until
>>>> after calling sdhci_suspend_host(). So move the calls to
>>>> sdhci_pci_init_wakeup() before calling sdhci_suspend_host(), and
>>>> stop calling sdhci_enable_irq_wakeups(). That results in some
>>>> simplification because sdhci_pci_suspend_host() and
>>>> __sdhci_pci_suspend_host() no longer need to be separate functions.
>>>>
>>>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>>>> ---
>>>>  drivers/mmc/host/sdhci-pci-core.c | 58 ++++++++++++++-------------------------
>>>>  1 file changed, 21 insertions(+), 37 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>>>> index 110c634cfb43..2ffc09f088ee 100644
>>>> --- a/drivers/mmc/host/sdhci-pci-core.c
>>>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>>>> @@ -39,10 +39,29 @@
>>>>  static void sdhci_pci_hw_reset(struct sdhci_host *host);
>>>>
>>>>  #ifdef CONFIG_PM_SLEEP
>>>> -static int __sdhci_pci_suspend_host(struct sdhci_pci_chip *chip)
>>>> +static int sdhci_pci_init_wakeup(struct sdhci_pci_chip *chip)
>>>> +{
>>>> +       mmc_pm_flag_t pm_flags = 0;
>>>> +       int i;
>>>> +
>>>> +       for (i = 0; i < chip->num_slots; i++) {
>>>> +               struct sdhci_pci_slot *slot = chip->slots[i];
>>>> +
>>>> +               if (slot)
>>>> +                       pm_flags |= slot->host->mmc->pm_flags;
>>>> +       }
>>>> +
>>>> +       return device_init_wakeup(&chip->pdev->dev,
>>>> +                                 (pm_flags & MMC_PM_KEEP_POWER) &&
>>>> +                                 (pm_flags & MMC_PM_WAKE_SDIO_IRQ));
>>>
>>> A couple of comments here.
>>>
>>> Wakeup settings shouldn't be changed during system suspend. That means
>>> we shouldn't call device_init_wakeup() (or device_set_wakeup_enable()
>>> for that matter) here.
>>>
>>> Instead, device_init_wakeup() should be called during ->probe(), while
>>> device_set_wakeup_enable() should normally *not* have to be called at
>>> all by drivers. There are a exceptions for device_set_wakeup_enable(),
>>> however it should not have to be called during system suspend, at
>>> least.
>>>
>>> Of course, I realize that you are not changing the behavior in this
>>> regards in this patch, so I am fine this anyway.
>>>
>>> My point is, that the end result while doing this improvements in this
>>> series, should strive to that device_init_wakeup() and
>>> device_set_wakeup_enable() becomes used correctly. I don't think that
>>> is the case, or is it?
>>
>> Unfortunately we don't find out what the SDIO driver wants to do (i.e
>> pm_flags & MMC_PM_WAKE_SDIO_IRQ) until suspend.
> 
> That's true! Of course, we need to be able to act on this, somehow.
> 
>>
>> I considered enabling wakeups by default but wasn't sure that would not
>> increase power consumption in devices not expecting it.
> 
> I understand the problem, believe me. However, I would rather like to
> try to find a generic solution to these problems, else we will keep
> abusing existing wakeups APIs.
> 
> For your information, I have been working on several issues on how to
> handle the "wakeup path" correctly, which is linked to this problem.
> See a few quite small series for this below [1], [2].
> 
> I think the general problems can be described liked this:
> 
> 1) The dev->power.wakeup_path flag doesn't get set for the device by
> the PM core, in case device_set_wakeup_enable() is called from a
> ->suspend() callback. That also means, that the parent device don't
> get its >power.wakeup_path flag set in __device_suspend() by the PM
> core, while this may be expected by upper layers (PM domains, middle
> layers).
> 
> 2) device_may_wakeup() doesn't tell us the hole story about the
> wakeup. Only that is *may* be configured. :-)
> So in cases when device_may_wakeup() returns true, we still have these
> three conditions to consider, which we currently can't distinguish
> between:
> 
> *) The wakeup is configured and enabled, so the device should be
> enabled in the wakeup path.
> 
> **) The wakeup is configured and enabled, but as an out-band-wakeup
> signal (like a GPIO IRQ). This means the device shouldn't be enabled
> in the wakeup path.

So what about just adding can_oob_wakeup and oob_wakeup i.e.


diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 0f651efc58a1..162a511286b7 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -323,23 +323,32 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev,
 static ssize_t wakeup_show(struct device *dev, struct device_attribute *attr,
 			   char *buf)
 {
-	return sprintf(buf, "%s\n", device_can_wakeup(dev)
-		? (device_may_wakeup(dev) ? _enabled : _disabled)
-		: "");
+	return sprintf(buf, "%s\n", device_may_wakeup(dev) ||
+				    device_may_oob_wakeup(dev) ? _enabled :
+				    device_can_wakeup(dev) ||
+				    device_can_oob_wakeup(dev) ? _disabled :
+				    "");
 }
 
 static ssize_t wakeup_store(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t n)
 {
-	if (!device_can_wakeup(dev))
+	bool enable = false;
+
+	if (!device_can_wakeup(dev) && !device_can_oob_wakeup(dev))
 		return -EINVAL;
 
 	if (sysfs_streq(buf, _enabled))
-		device_set_wakeup_enable(dev, 1);
-	else if (sysfs_streq(buf, _disabled))
-		device_set_wakeup_enable(dev, 0);
-	else
+		enable = true;
+	else if (!sysfs_streq(buf, _disabled))
 		return -EINVAL;
+
+	if (device_can_wakeup(dev))
+		device_set_wakeup_enable(dev, enable);
+
+	if (device_can_oob_wakeup(dev))
+		device_set_oob_wakeup_enable(dev, enable);
+
 	return n;
 }
 
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index e73a081c6397..13e176edc3d7 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -392,6 +392,20 @@ int device_wakeup_disable(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_wakeup_disable);
 
+static void wakeup_sysfs_add_remove(struct device *dev, bool capable)
+{
+	if (device_is_registered(dev) && !list_empty(&dev->power.entry)) {
+		if (capable) {
+			int ret = wakeup_sysfs_add(dev);
+
+			if (ret)
+				dev_info(dev, "Wakeup sysfs attributes not added\n");
+		} else {
+			wakeup_sysfs_remove(dev);
+		}
+	}
+}
+
 /**
  * device_set_wakeup_capable - Set/reset device wakeup capability flag.
  * @dev: Device to handle.
@@ -410,20 +424,35 @@ void device_set_wakeup_capable(struct device *dev, bool capable)
 		return;
 
 	dev->power.can_wakeup = capable;
-	if (device_is_registered(dev) && !list_empty(&dev->power.entry)) {
-		if (capable) {
-			int ret = wakeup_sysfs_add(dev);
-
-			if (ret)
-				dev_info(dev, "Wakeup sysfs attributes not added\n");
-		} else {
-			wakeup_sysfs_remove(dev);
-		}
-	}
+	wakeup_sysfs_add_remove(dev, capable);
 }
 EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
 
 /**
+ * device_set_oob_wakeup_capable - Set/reset device oob wakeup capability flag.
+ * @dev: Device to handle.
+ * @capable: Whether or not @dev is capable of out-of-band wake up of the system
+ * from sleep.
+ *
+ * If @capable is set, set the @dev's power.can_oob_wakeup flag and add its
+ * wakeup-related attributes to sysfs.  Otherwise, unset the @dev's
+ * power.can_oob_wakeup flag and remove its wakeup-related attributes from
+ * sysfs.
+ *
+ * This function may sleep and it can't be called from any context where
+ * sleeping is not allowed.
+ */
+void device_set_oob_wakeup_capable(struct device *dev, bool capable)
+{
+	if (!!dev->power.can_oob_wakeup == !!capable)
+		return;
+
+	dev->power.can_oob_wakeup = capable;
+	wakeup_sysfs_add_remove(dev, capable);
+}
+EXPORT_SYMBOL_GPL(device_set_oob_wakeup_capable);
+
+/**
  * device_init_wakeup - Device wakeup initialization.
  * @dev: Device to handle.
  * @enable: Whether or not to enable @dev as a wakeup device.
diff --git a/include/linux/pm.h b/include/linux/pm.h
index e723b78d8357..22cfcacc0095 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -585,6 +585,8 @@ struct pm_subsys_data {
 struct dev_pm_info {
 	pm_message_t		power_state;
 	unsigned int		can_wakeup:1;
+	unsigned int		can_oob_wakeup:1;
+	unsigned int		oob_wakeup:1;
 	unsigned int		async_suspend:1;
 	bool			in_dpm_list:1;	/* Owned by the PM core */
 	bool			is_prepared:1;	/* Owned by the PM core */
diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h
index 4238dde0aaf0..64252cb0f97a 100644
--- a/include/linux/pm_wakeup.h
+++ b/include/linux/pm_wakeup.h
@@ -83,11 +83,30 @@ static inline bool device_can_wakeup(struct device *dev)
 	return dev->power.can_wakeup;
 }
 
+static inline bool device_can_oob_wakeup(struct device *dev)
+{
+	return dev->power.can_oob_wakeup;
+}
+
 static inline bool device_may_wakeup(struct device *dev)
 {
 	return dev->power.can_wakeup && !!dev->power.wakeup;
 }
 
+static inline bool device_may_oob_wakeup(struct device *dev)
+{
+	return dev->power.can_oob_wakeup && dev->power.oob_wakeup;
+}
+
+static inline int device_set_oob_wakeup_enable(struct device *dev, bool enable)
+{
+	if (!dev->power.can_oob_wakeup)
+		return -EINVAL;
+
+	dev->power.oob_wakeup = enable;
+	return 0;
+}
+
 static inline void device_set_wakeup_path(struct device *dev)
 {
 	dev->power.wakeup_path = true;


> 
> ***) The wakeup isn't configured, thus disabled, because there are no
> consumers of the wakeup IRQ registered (as may be the case for SDIO
> IRQ). This also means the device shouldn't be enabled in the wakeup
> path.
> 
> Potentially, one could consider **) and ***) being treated in the same
> way, via using an opt-out method, avoiding the wakeup path to be
> enabled. Currently I have been thinking of adding an "OUT_BAND_WAKEUP"
> driver PM flag, to deal with this, however there may be other
> preferred options.
> 
> Kind regards
> Uffe
> 
> [1]
> https://www.spinics.net/lists/linux-renesas-soc/msg21421.html
> [2]
> https://www.spinics.net/lists/linux-renesas-soc/msg20122.html
> (Actually the discussions there concluded that we may need an
> "OUT_BAND_WAKEUP" flag instead)
> 


  reply	other threads:[~2018-01-08 14:50 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-20  8:18 [PATCH 0/9] mmc: sdhci-pci: Respect PM flags when enabling card detect GPIO IRQ wakeup Adrian Hunter
2017-12-20  8:18 ` [PATCH 1/9] mmc: sdhci-pci: Stop calling sdhci_enable_irq_wakeups() Adrian Hunter
2017-12-21 14:15   ` Ulf Hansson
2017-12-21 14:25     ` Adrian Hunter
2017-12-21 15:33       ` Ulf Hansson
2018-01-08 14:49         ` Adrian Hunter [this message]
2018-01-09  0:26           ` Rafael J. Wysocki
2018-01-09  8:08             ` Adrian Hunter
2018-01-09 12:03               ` Rafael J. Wysocki
2018-01-09  0:46         ` Rafael J. Wysocki
2018-01-09  7:14           ` Ulf Hansson
2018-01-09 11:57             ` Rafael J. Wysocki
2017-12-20  8:18 ` [PATCH 2/9] mmc: sdhci-pci: Use device wakeup capability to determine MMC_PM_WAKE_SDIO_IRQ capability Adrian Hunter
2017-12-20  8:18 ` [PATCH 3/9] mmc: sdhci: Stop exporting sdhci_enable_irq_wakeups() Adrian Hunter
2017-12-20  8:18 ` [PATCH 4/9] mmc: sdhci: Handle failure of enable_irq_wake() Adrian Hunter
2017-12-20  8:18 ` [PATCH 5/9] mmc: sdhci: Rework sdhci_enable_irq_wakeups() Adrian Hunter
2017-12-20  8:18 ` [PATCH 6/9] mmc: sdhci: Do not unnecessarily enable wakeup for card detect interrupt Adrian Hunter
2017-12-20  8:18 ` [PATCH 7/9] mmc: sdhci: Do not unnecessarily enable wakeup for SDIO card interrupt Adrian Hunter
2017-12-20  8:18 ` [PATCH 8/9] mmc: slot-gpio: Add a function to enable/disable card detect IRQ wakeup Adrian Hunter
2017-12-20  8:18 ` [PATCH 9/9] mmc: sdhci-pci: Respect PM flags when enabling card detect GPIO " Adrian Hunter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c2d38388-93a0-61c0-e323-74851c08c1fd@intel.com \
    --to=adrian.hunter@intel.com \
    --cc=geert@linux-m68k.org \
    --cc=haridhar.kalvala@intel.com \
    --cc=linux-mmc@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=rjw@rjwysocki.net \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox