Re: [PATCH 4/5] memstick: rtsx_usb_ms: Support runtime power management

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 31 July 2018 at 08:17, Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx> wrote:
> In order to let host's parent device, rtsx_usb, to use USB remote wake
> up signaling to do card detection, it needs to be suspended. Hence it's
> necessary to add runtime PM support for the memstick host.
>
> To keep memstick host stays suspended when it's not in use, convert the
> card detection function from kthread to delayed_work, which can be
> scheduled when the host is resumed and can be canceled when the host is
> suspended.
>
> Use an idle function check if there's no card and the power mode is
> MEMSTICK_POWER_OFF. If both criteria are met, put the device to suspend.
>
> Signed-off-by: Kai-Heng Feng <kai.heng.feng@xxxxxxxxxxxxx>
> ---
>  drivers/memstick/host/rtsx_usb_ms.c | 138 ++++++++++++++--------------
>  1 file changed, 71 insertions(+), 67 deletions(-)
>
> diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c
> index cd12f3d1c088..126eb310980a 100644
> --- a/drivers/memstick/host/rtsx_usb_ms.c
> +++ b/drivers/memstick/host/rtsx_usb_ms.c
> @@ -40,15 +40,14 @@ struct rtsx_usb_ms {
>
>         struct mutex            host_mutex;
>         struct work_struct      handle_req;
> -
> -       struct task_struct      *detect_ms;
> -       struct completion       detect_ms_exit;
> +       struct delayed_work     poll_card;
>
>         u8                      ssc_depth;
>         unsigned int            clock;
>         int                     power_mode;
>         unsigned char           ifmode;
>         bool                    eject;
> +       bool                    suspend;
>  };
>
>  static inline struct device *ms_dev(struct rtsx_usb_ms *host)
> @@ -524,7 +523,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
>         int rc;
>
>         if (!host->req) {
> -               pm_runtime_get_sync(ms_dev(host));
> +               pm_runtime_get_noresume(ms_dev(host));

I don't think this is safe.

The memstick core doesn't manage runtime PM, hence there are no
guarantee that the device is runtime resumed at this point, or is
there?

>                 do {
>                         rc = memstick_next_req(msh, &host->req);
>                         dev_dbg(ms_dev(host), "next req %d\n", rc);
> @@ -545,7 +544,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
>                                                 host->req->error);
>                         }
>                 } while (!rc);
> -               pm_runtime_put(ms_dev(host));
> +               pm_runtime_put_noidle(ms_dev(host));

According to the above, I think this should stay as is. Or perhaps you
want to use pm_runtime_put_sync() instead, as to avoid the device from
being unnecessary resumed and hence also its parent.

>         }
>
>  }
> @@ -572,7 +571,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
>         dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
>                         __func__, param, value);
>
> -       pm_runtime_get_sync(ms_dev(host));
> +       pm_runtime_get_noresume(ms_dev(host));

Ditto.

>         mutex_lock(&ucr->dev_mutex);
>
>         err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
> @@ -585,14 +584,14 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
>                         break;
>
>                 if (value == MEMSTICK_POWER_ON) {
> -                       pm_runtime_get_sync(ms_dev(host));
> +                       pm_runtime_get_noresume(ms_dev(host));

Ditto.

>                         err = ms_power_on(host);
> +                       if (err)
> +                               pm_runtime_put_noidle(ms_dev(host));
>                 } else if (value == MEMSTICK_POWER_OFF) {
>                         err = ms_power_off(host);
> -                       if (host->msh->card)
> +                       if (!err)
>                                 pm_runtime_put_noidle(ms_dev(host));
> -                       else
> -                               pm_runtime_put(ms_dev(host));

Ditto.

>                 } else
>                         err = -EINVAL;
>                 if (!err)
> @@ -638,7 +637,7 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
>         }
>  out:
>         mutex_unlock(&ucr->dev_mutex);
> -       pm_runtime_put(ms_dev(host));
> +       pm_runtime_put_noidle(ms_dev(host));

Ditto.

>
>         /* power-on delay */
>         if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
> @@ -648,75 +647,86 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
>         return err;
>  }
>
> -#ifdef CONFIG_PM_SLEEP
> -static int rtsx_usb_ms_suspend(struct device *dev)
> +#ifdef CONFIG_PM
> +static int rtsx_usb_ms_runtime_suspend(struct device *dev)
>  {
>         struct rtsx_usb_ms *host = dev_get_drvdata(dev);
>         struct memstick_host *msh = host->msh;
>
> -       dev_dbg(ms_dev(host), "--> %s\n", __func__);
> -
> +       host->suspend = true;
>         memstick_suspend_host(msh);
> +
>         return 0;
>  }
>
> -static int rtsx_usb_ms_resume(struct device *dev)
> +static int rtsx_usb_ms_runtime_resume(struct device *dev)
>  {
>         struct rtsx_usb_ms *host = dev_get_drvdata(dev);
>         struct memstick_host *msh = host->msh;
>
> -       dev_dbg(ms_dev(host), "--> %s\n", __func__);
> -
>         memstick_resume_host(msh);
> +       host->suspend = false;
> +       schedule_delayed_work(&host->poll_card, 100);
> +
>         return 0;
>  }
> -#endif /* CONFIG_PM_SLEEP */
>
> -/*
> - * Thread function of ms card slot detection. The thread starts right after
> - * successful host addition. It stops while the driver removal function sets
> - * host->eject true.
> - */
> -static int rtsx_usb_detect_ms_card(void *__host)
> +static int rtsx_usb_ms_runtime_idle(struct device *dev)
> +{
> +       struct rtsx_usb_ms *host = dev_get_drvdata(dev);
> +
> +       if (!host->msh->card && host->power_mode == MEMSTICK_POWER_OFF) {
> +               pm_schedule_suspend(dev, 0);
> +               return 0;
> +       }
> +
> +       return -EAGAIN;
> +}
> +
> +static UNIVERSAL_DEV_PM_OPS(rtsx_usb_ms_pm_ops,
> +               rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume,
> +               rtsx_usb_ms_runtime_idle);
> +#endif /* CONFIG_PM */
> +
> +static void rtsx_usb_ms_poll_card(struct work_struct *work)
>  {
> -       struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host;
> +       struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms,
> +                       poll_card.work);
>         struct rtsx_ucr *ucr = host->ucr;
> -       u8 val = 0;
>         int err;
> +       u8 val;
>
> -       for (;;) {
> -               pm_runtime_get_sync(ms_dev(host));
> -               mutex_lock(&ucr->dev_mutex);
> +       if (host->eject || host->suspend)

The runtime PM state of the device could potentially be changed by
user space, via sysfs, as well.

It seems like what you really should be checking is whether
host->power_mode is MEMSTICK_POWER_OFF and then act accordingly.

I also think you be able to implement this without a ->runtime_idle()
callback, as it just makes this a bit unnecessary complicated.

> +               return;
>
> -               /* Check pending MS card changes */
> -               err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
> -               if (err) {
> -                       mutex_unlock(&ucr->dev_mutex);
> -                       goto poll_again;
> -               }
> -
> -               /* Clear the pending */
> -               rtsx_usb_write_register(ucr, CARD_INT_PEND,
> -                               XD_INT | MS_INT | SD_INT,
> -                               XD_INT | MS_INT | SD_INT);
> +       pm_runtime_get_noresume(ms_dev(host));
> +       mutex_lock(&ucr->dev_mutex);
>
> +       /* Check pending MS card changes */
> +       err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
> +       if (err) {
>                 mutex_unlock(&ucr->dev_mutex);
> +               goto poll_again;
> +       }
>
> -               if (val & MS_INT) {
> -                       dev_dbg(ms_dev(host), "MS slot change detected\n");
> -                       memstick_detect_change(host->msh);
> -               }
> +       /* Clear the pending */
> +       rtsx_usb_write_register(ucr, CARD_INT_PEND,
> +                       XD_INT | MS_INT | SD_INT,
> +                       XD_INT | MS_INT | SD_INT);
>
> -poll_again:
> -               pm_runtime_put(ms_dev(host));
> -               if (host->eject)
> -                       break;
> +       mutex_unlock(&ucr->dev_mutex);
> +       pm_runtime_put_noidle(ms_dev(host));
>
> -               schedule_timeout_idle(HZ);
> +       if (val & MS_INT) {
> +               dev_dbg(ms_dev(host), "MS slot change detected\n");
> +               memstick_detect_change(host->msh);
>         }
>
> -       complete(&host->detect_ms_exit);
> -       return 0;
> +       pm_runtime_idle(ms_dev(host));
> +
> +poll_again:
> +       if (!host->eject && !host->suspend)
> +               schedule_delayed_work(&host->poll_card, 100);
>  }
>
>  static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
> @@ -747,26 +757,21 @@ static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
>         mutex_init(&host->host_mutex);
>         INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req);
>
> -       init_completion(&host->detect_ms_exit);
> -       host->detect_ms = kthread_create(rtsx_usb_detect_ms_card, host,
> -                       "rtsx_usb_ms_%d", pdev->id);
> -       if (IS_ERR(host->detect_ms)) {
> -               dev_dbg(&(pdev->dev),
> -                               "Unable to create polling thread.\n");
> -               err = PTR_ERR(host->detect_ms);
> -               goto err_out;
> -       }
> +       INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card);
>
>         msh->request = rtsx_usb_ms_request;
>         msh->set_param = rtsx_usb_ms_set_param;
>         msh->caps = MEMSTICK_CAP_PAR4;
>
> -       pm_runtime_enable(&pdev->dev);
> +       pm_runtime_set_active(ms_dev(host));
> +       pm_runtime_enable(ms_dev(host));
> +
>         err = memstick_add_host(msh);
>         if (err)
>                 goto err_out;
>
> -       wake_up_process(host->detect_ms);
> +       schedule_delayed_work(&host->poll_card, 100);
> +
>         return 0;
>  err_out:
>         memstick_free_host(msh);
> @@ -782,6 +787,7 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
>         msh = host->msh;
>         host->eject = true;
>         cancel_work_sync(&host->handle_req);
> +       cancel_delayed_work_sync(&host->poll_card);
>
>         mutex_lock(&host->host_mutex);
>         if (host->req) {
> @@ -797,7 +803,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
>         }
>         mutex_unlock(&host->host_mutex);
>
> -       wait_for_completion(&host->detect_ms_exit);
>         memstick_remove_host(msh);
>         memstick_free_host(msh);
>
> @@ -816,9 +821,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
>         return 0;
>  }
>
> -static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops,
> -               rtsx_usb_ms_suspend, rtsx_usb_ms_resume);
> -
>  static struct platform_device_id rtsx_usb_ms_ids[] = {
>         {
>                 .name = "rtsx_usb_ms",
> @@ -834,7 +836,9 @@ static struct platform_driver rtsx_usb_ms_driver = {
>         .id_table       = rtsx_usb_ms_ids,
>         .driver         = {
>                 .name   = "rtsx_usb_ms",
> +#ifdef CONFIG_PM
>                 .pm     = &rtsx_usb_ms_pm_ops,
> +#endif
>         },
>  };
>  module_platform_driver(rtsx_usb_ms_driver);
> --
> 2.17.1
>

Kind regards
Uffe
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux