2017-08-30 14:14:18

by Amitkumar Karwar

[permalink] [raw]
Subject: [PATCH] rsi: sdio suspend and resume support

From: Karun Eagalapati <[email protected]>

SDIO suspend and resume handlers are implemented and verified
that device works after suspend/resume cycle.

Signed-off-by: Karun Eagalapati <[email protected]>
Signed-off-by: Amitkumar Karwar <[email protected]>
---
drivers/net/wireless/rsi/rsi_91x_sdio.c | 126 +++++++++++++++++++++++++++++++-
drivers/net/wireless/rsi/rsi_sdio.h | 2 +
2 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
index 8d3a483..855b605 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -1059,16 +1059,134 @@ static void rsi_disconnect(struct sdio_func *pfunction)
}

#ifdef CONFIG_PM
+static int rsi_set_sdio_pm_caps(struct rsi_hw *adapter)
+{
+ struct rsi_91x_sdiodev *dev =
+ (struct rsi_91x_sdiodev *)adapter->rsi_dev;
+ struct sdio_func *func = dev->pfunction;
+ int ret;
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ rsi_dbg(ERR_ZONE, "Set sdio keep pwr flag failed: %d\n", ret);
+
+ return ret;
+}
+
+static int rsi_sdio_disable_interrupts(struct sdio_func *pfunc)
+{
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunc);
+ u8 isr_status = 0, data = 0;
+ int ret;
+
+ rsi_dbg(INFO_ZONE, "Waiting for interrupts to be cleared..");
+ do {
+ rsi_sdio_read_register(adapter, RSI_FN1_INT_REGISTER,
+ &isr_status);
+ rsi_dbg(INFO_ZONE, ".");
+ } while (isr_status);
+ rsi_dbg(INFO_ZONE, "Interrupts cleared\n");
+
+ sdio_claim_host(pfunc);
+ ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read int enable register\n",
+ __func__);
+ goto done;
+ }
+
+ data &= RSI_INT_ENABLE_MASK;
+ ret = rsi_cmd52writebyte(pfunc->card, RSI_INT_ENABLE_REGISTER, data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write to int enable register\n",
+ __func__);
+ goto done;
+ }
+ ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read int enable register\n",
+ __func__);
+ goto done;
+ }
+ rsi_dbg(INFO_ZONE, "int enable reg content = %x\n", data);
+
+done:
+ sdio_release_host(pfunc);
+ return ret;
+}
+
+static int rsi_sdio_enable_interrupts(struct sdio_func *pfunc)
+{
+ u8 data;
+ int ret;
+
+ sdio_claim_host(pfunc);
+ ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read int enable register\n", __func__);
+ goto done;
+ }
+
+ data |= ~RSI_INT_ENABLE_MASK & 0xff;
+
+ ret = rsi_cmd52writebyte(pfunc->card, RSI_INT_ENABLE_REGISTER, data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to write to int enable register\n",
+ __func__);
+ goto done;
+ }
+
+ ret = rsi_cmd52readbyte(pfunc->card, RSI_INT_ENABLE_REGISTER, &data);
+ if (ret < 0) {
+ rsi_dbg(ERR_ZONE,
+ "%s: Failed to read int enable register\n", __func__);
+ goto done;
+ }
+ rsi_dbg(INFO_ZONE, "int enable reg content = %x\n", data);
+
+done:
+ sdio_release_host(pfunc);
+ return ret;
+}
+
static int rsi_suspend(struct device *dev)
{
- /* Not yet implemented */
- return -ENOSYS;
+ int ret;
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_common *common;
+
+ if (!adapter) {
+ rsi_dbg(ERR_ZONE, "Device is not ready\n");
+ return -ENODEV;
+ }
+ common = adapter->priv;
+ rsi_sdio_disable_interrupts(pfunction);
+
+ ret = rsi_set_sdio_pm_caps(adapter);
+ if (ret)
+ rsi_dbg(INFO_ZONE,
+ "Setting power management caps failed\n");
+ common->fsm_state = FSM_CARD_NOT_READY;
+
+ return 0;
}

static int rsi_resume(struct device *dev)
{
- /* Not yet implemented */
- return -ENOSYS;
+ struct sdio_func *pfunction = dev_to_sdio_func(dev);
+ struct rsi_hw *adapter = sdio_get_drvdata(pfunction);
+ struct rsi_common *common = adapter->priv;
+
+ common->fsm_state = FSM_MAC_INIT_DONE;
+ rsi_sdio_enable_interrupts(pfunction);
+
+ return 0;
}

static const struct dev_pm_ops rsi_pm_ops = {
diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h
index 95e4bed..49c549b 100644
--- a/drivers/net/wireless/rsi/rsi_sdio.h
+++ b/drivers/net/wireless/rsi/rsi_sdio.h
@@ -48,6 +48,8 @@ enum sdio_interrupt_type {

#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
#define RSI_FN1_INT_REGISTER 0xf9
+#define RSI_INT_ENABLE_REGISTER 0x04
+#define RSI_INT_ENABLE_MASK 0xfc
#define RSI_SD_REQUEST_MASTER 0x10000

/* FOR SD CARD ONLY */
--
2.7.4


2017-09-21 14:34:47

by Amitkumar Karwar

[permalink] [raw]
Subject: Re: [PATCH] rsi: sdio suspend and resume support

On Tue, Sep 19, 2017 at 8:19 PM, Kalle Valo <[email protected]> wrote:
> Amitkumar Karwar <[email protected]> writes:
>
>> From: Karun Eagalapati <[email protected]>
>>
>> SDIO suspend and resume handlers are implemented and verified
>> that device works after suspend/resume cycle.
>>
>> Signed-off-by: Karun Eagalapati <[email protected]>
>> Signed-off-by: Amitkumar Karwar <[email protected]>
>
> [...]
>
>> +static int rsi_sdio_disable_interrupts(struct sdio_func *pfunc)
>> +{
>> + struct rsi_hw *adapter = sdio_get_drvdata(pfunc);
>> + u8 isr_status = 0, data = 0;
>> + int ret;
>> +
>> + rsi_dbg(INFO_ZONE, "Waiting for interrupts to be cleared..");
>> + do {
>> + rsi_sdio_read_register(adapter, RSI_FN1_INT_REGISTER,
>> + &isr_status);
>> + rsi_dbg(INFO_ZONE, ".");
>> + } while (isr_status);
>
> Never ending loops in kernel are always a bad idea, better to add a
> reasonable timeout if/when something goes wrong.

Thanks for the review. I have taken care of this in v2 patch.

Regards,
Amitkumar Karwar

2017-09-19 14:49:27

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH] rsi: sdio suspend and resume support

Amitkumar Karwar <[email protected]> writes:

> From: Karun Eagalapati <[email protected]>
>
> SDIO suspend and resume handlers are implemented and verified
> that device works after suspend/resume cycle.
>
> Signed-off-by: Karun Eagalapati <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>

[...]

> +static int rsi_sdio_disable_interrupts(struct sdio_func *pfunc)
> +{
> + struct rsi_hw *adapter = sdio_get_drvdata(pfunc);
> + u8 isr_status = 0, data = 0;
> + int ret;
> +
> + rsi_dbg(INFO_ZONE, "Waiting for interrupts to be cleared..");
> + do {
> + rsi_sdio_read_register(adapter, RSI_FN1_INT_REGISTER,
> + &isr_status);
> + rsi_dbg(INFO_ZONE, ".");
> + } while (isr_status);

Never ending loops in kernel are always a bad idea, better to add a
reasonable timeout if/when something goes wrong.

--
Kalle Valo