]>
Commit | Line | Data |
---|---|---|
bed0f502 GKH |
1 | From 7444a8092906ed44c09459780c56ba57043e39b1 Mon Sep 17 00:00:00 2001 |
2 | From: Daniel Mack <daniel@zonque.org> | |
3 | Date: Wed, 27 Jun 2018 20:58:45 +0200 | |
4 | Subject: libertas: fix suspend and resume for SDIO connected cards | |
5 | ||
6 | From: Daniel Mack <daniel@zonque.org> | |
7 | ||
8 | commit 7444a8092906ed44c09459780c56ba57043e39b1 upstream. | |
9 | ||
10 | Prior to commit 573185cc7e64 ("mmc: core: Invoke sdio func driver's PM | |
11 | callbacks from the sdio bus"), the MMC core used to call into the power | |
12 | management functions of SDIO clients itself and removed the card if the | |
13 | return code was non-zero. IOW, the mmc handled errors gracefully and didn't | |
14 | upchain them to the pm core. | |
15 | ||
16 | Since this change, the mmc core relies on generic power management | |
17 | functions which treat all errors as a reason to cancel the suspend | |
18 | immediately. This causes suspend attempts to fail when the libertas | |
19 | driver is loaded. | |
20 | ||
21 | To fix this, power down the card explicitly in if_sdio_suspend() when we | |
22 | know we're about to lose power and return success. Also set a flag in these | |
23 | cases, and power up the card again in if_sdio_resume(). | |
24 | ||
25 | Fixes: 573185cc7e64 ("mmc: core: Invoke sdio func driver's PM callbacks from the sdio bus") | |
26 | Cc: <stable@vger.kernel.org> | |
27 | Signed-off-by: Daniel Mack <daniel@zonque.org> | |
28 | Reviewed-by: Chris Ball <chris@printf.net> | |
29 | Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> | |
30 | Signed-off-by: Kalle Valo <kvalo@codeaurora.org> | |
31 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
32 | ||
33 | --- | |
34 | drivers/net/wireless/marvell/libertas/dev.h | 1 | |
35 | drivers/net/wireless/marvell/libertas/if_sdio.c | 30 +++++++++++++++++++----- | |
36 | 2 files changed, 25 insertions(+), 6 deletions(-) | |
37 | ||
38 | --- a/drivers/net/wireless/marvell/libertas/dev.h | |
39 | +++ b/drivers/net/wireless/marvell/libertas/dev.h | |
40 | @@ -104,6 +104,7 @@ struct lbs_private { | |
41 | u8 fw_ready; | |
42 | u8 surpriseremoved; | |
43 | u8 setup_fw_on_resume; | |
44 | + u8 power_up_on_resume; | |
45 | int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb); | |
46 | void (*reset_card) (struct lbs_private *priv); | |
47 | int (*power_save) (struct lbs_private *priv); | |
48 | --- a/drivers/net/wireless/marvell/libertas/if_sdio.c | |
49 | +++ b/drivers/net/wireless/marvell/libertas/if_sdio.c | |
50 | @@ -1290,15 +1290,23 @@ static void if_sdio_remove(struct sdio_f | |
51 | static int if_sdio_suspend(struct device *dev) | |
52 | { | |
53 | struct sdio_func *func = dev_to_sdio_func(dev); | |
54 | - int ret; | |
55 | struct if_sdio_card *card = sdio_get_drvdata(func); | |
56 | + struct lbs_private *priv = card->priv; | |
57 | + int ret; | |
58 | ||
59 | mmc_pm_flag_t flags = sdio_get_host_pm_caps(func); | |
60 | + priv->power_up_on_resume = false; | |
61 | ||
62 | /* If we're powered off anyway, just let the mmc layer remove the | |
63 | * card. */ | |
64 | - if (!lbs_iface_active(card->priv)) | |
65 | - return -ENOSYS; | |
66 | + if (!lbs_iface_active(priv)) { | |
67 | + if (priv->fw_ready) { | |
68 | + priv->power_up_on_resume = true; | |
69 | + if_sdio_power_off(card); | |
70 | + } | |
71 | + | |
72 | + return 0; | |
73 | + } | |
74 | ||
75 | dev_info(dev, "%s: suspend: PM flags = 0x%x\n", | |
76 | sdio_func_id(func), flags); | |
77 | @@ -1306,9 +1314,14 @@ static int if_sdio_suspend(struct device | |
78 | /* If we aren't being asked to wake on anything, we should bail out | |
79 | * and let the SD stack power down the card. | |
80 | */ | |
81 | - if (card->priv->wol_criteria == EHS_REMOVE_WAKEUP) { | |
82 | + if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { | |
83 | dev_info(dev, "Suspend without wake params -- powering down card\n"); | |
84 | - return -ENOSYS; | |
85 | + if (priv->fw_ready) { | |
86 | + priv->power_up_on_resume = true; | |
87 | + if_sdio_power_off(card); | |
88 | + } | |
89 | + | |
90 | + return 0; | |
91 | } | |
92 | ||
93 | if (!(flags & MMC_PM_KEEP_POWER)) { | |
94 | @@ -1321,7 +1334,7 @@ static int if_sdio_suspend(struct device | |
95 | if (ret) | |
96 | return ret; | |
97 | ||
98 | - ret = lbs_suspend(card->priv); | |
99 | + ret = lbs_suspend(priv); | |
100 | if (ret) | |
101 | return ret; | |
102 | ||
103 | @@ -1336,6 +1349,11 @@ static int if_sdio_resume(struct device | |
104 | ||
105 | dev_info(dev, "%s: resume: we're back\n", sdio_func_id(func)); | |
106 | ||
107 | + if (card->priv->power_up_on_resume) { | |
108 | + if_sdio_power_on(card); | |
109 | + wait_event(card->pwron_waitq, card->priv->fw_ready); | |
110 | + } | |
111 | + | |
112 | ret = lbs_resume(card->priv); | |
113 | ||
114 | return ret; |