Skip to content

Commit 6827ca5

Browse files
khfengstorulf
authored andcommitted
memstick: rtsx_usb_ms: Support runtime power management
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. Put the device to suspend when there's no card and the power mode is MEMSTICK_POWER_OFF. Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com> Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent ba9d5f8 commit 6827ca5

File tree

1 file changed

+100
-63
lines changed

1 file changed

+100
-63
lines changed

drivers/memstick/host/rtsx_usb_ms.c

+100-63
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,14 @@ struct rtsx_usb_ms {
4040

4141
struct mutex host_mutex;
4242
struct work_struct handle_req;
43-
44-
struct task_struct *detect_ms;
45-
struct completion detect_ms_exit;
43+
struct delayed_work poll_card;
4644

4745
u8 ssc_depth;
4846
unsigned int clock;
4947
int power_mode;
5048
unsigned char ifmode;
5149
bool eject;
50+
bool system_suspending;
5251
};
5352

5453
static inline struct device *ms_dev(struct rtsx_usb_ms *host)
@@ -545,7 +544,7 @@ static void rtsx_usb_ms_handle_req(struct work_struct *work)
545544
host->req->error);
546545
}
547546
} while (!rc);
548-
pm_runtime_put(ms_dev(host));
547+
pm_runtime_put_sync(ms_dev(host));
549548
}
550549

551550
}
@@ -585,14 +584,14 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
585584
break;
586585

587586
if (value == MEMSTICK_POWER_ON) {
588-
pm_runtime_get_sync(ms_dev(host));
587+
pm_runtime_get_noresume(ms_dev(host));
589588
err = ms_power_on(host);
589+
if (err)
590+
pm_runtime_put_noidle(ms_dev(host));
590591
} else if (value == MEMSTICK_POWER_OFF) {
591592
err = ms_power_off(host);
592-
if (host->msh->card)
593+
if (!err)
593594
pm_runtime_put_noidle(ms_dev(host));
594-
else
595-
pm_runtime_put(ms_dev(host));
596595
} else
597596
err = -EINVAL;
598597
if (!err)
@@ -638,12 +637,16 @@ static int rtsx_usb_ms_set_param(struct memstick_host *msh,
638637
}
639638
out:
640639
mutex_unlock(&ucr->dev_mutex);
641-
pm_runtime_put(ms_dev(host));
640+
pm_runtime_put_sync(ms_dev(host));
642641

643642
/* power-on delay */
644-
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
643+
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) {
645644
usleep_range(10000, 12000);
646645

646+
if (!host->eject)
647+
schedule_delayed_work(&host->poll_card, 100);
648+
}
649+
647650
dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err);
648651
return err;
649652
}
@@ -654,9 +657,24 @@ static int rtsx_usb_ms_suspend(struct device *dev)
654657
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
655658
struct memstick_host *msh = host->msh;
656659

657-
dev_dbg(ms_dev(host), "--> %s\n", __func__);
660+
/* Since we use rtsx_usb's resume callback to runtime resume its
661+
* children to implement remote wakeup signaling, this causes
662+
* rtsx_usb_ms' runtime resume callback runs after its suspend
663+
* callback:
664+
* rtsx_usb_ms_suspend()
665+
* rtsx_usb_resume()
666+
* -> rtsx_usb_ms_runtime_resume()
667+
* -> memstick_detect_change()
668+
*
669+
* rtsx_usb_suspend()
670+
*
671+
* To avoid this, skip runtime resume/suspend if system suspend is
672+
* underway.
673+
*/
658674

675+
host->system_suspending = true;
659676
memstick_suspend_host(msh);
677+
660678
return 0;
661679
}
662680

@@ -665,58 +683,85 @@ static int rtsx_usb_ms_resume(struct device *dev)
665683
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
666684
struct memstick_host *msh = host->msh;
667685

668-
dev_dbg(ms_dev(host), "--> %s\n", __func__);
669-
670686
memstick_resume_host(msh);
687+
host->system_suspending = false;
688+
671689
return 0;
672690
}
673691
#endif /* CONFIG_PM_SLEEP */
674692

675-
/*
676-
* Thread function of ms card slot detection. The thread starts right after
677-
* successful host addition. It stops while the driver removal function sets
678-
* host->eject true.
679-
*/
680-
static int rtsx_usb_detect_ms_card(void *__host)
693+
#ifdef CONFIG_PM
694+
static int rtsx_usb_ms_runtime_suspend(struct device *dev)
695+
{
696+
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
697+
698+
if (host->system_suspending)
699+
return 0;
700+
701+
if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF)
702+
return -EAGAIN;
703+
704+
return 0;
705+
}
706+
707+
static int rtsx_usb_ms_runtime_resume(struct device *dev)
708+
{
709+
struct rtsx_usb_ms *host = dev_get_drvdata(dev);
710+
711+
712+
if (host->system_suspending)
713+
return 0;
714+
715+
memstick_detect_change(host->msh);
716+
717+
return 0;
718+
}
719+
#endif /* CONFIG_PM */
720+
721+
static const struct dev_pm_ops rtsx_usb_ms_pm_ops = {
722+
SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume)
723+
SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL)
724+
};
725+
726+
727+
static void rtsx_usb_ms_poll_card(struct work_struct *work)
681728
{
682-
struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host;
729+
struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms,
730+
poll_card.work);
683731
struct rtsx_ucr *ucr = host->ucr;
684-
u8 val = 0;
685732
int err;
733+
u8 val;
686734

687-
for (;;) {
688-
pm_runtime_get_sync(ms_dev(host));
689-
mutex_lock(&ucr->dev_mutex);
690-
691-
/* Check pending MS card changes */
692-
err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
693-
if (err) {
694-
mutex_unlock(&ucr->dev_mutex);
695-
goto poll_again;
696-
}
735+
if (host->eject || host->power_mode != MEMSTICK_POWER_ON)
736+
return;
697737

698-
/* Clear the pending */
699-
rtsx_usb_write_register(ucr, CARD_INT_PEND,
700-
XD_INT | MS_INT | SD_INT,
701-
XD_INT | MS_INT | SD_INT);
738+
pm_runtime_get_sync(ms_dev(host));
739+
mutex_lock(&ucr->dev_mutex);
702740

741+
/* Check pending MS card changes */
742+
err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val);
743+
if (err) {
703744
mutex_unlock(&ucr->dev_mutex);
745+
goto poll_again;
746+
}
704747

705-
if (val & MS_INT) {
706-
dev_dbg(ms_dev(host), "MS slot change detected\n");
707-
memstick_detect_change(host->msh);
708-
}
748+
/* Clear the pending */
749+
rtsx_usb_write_register(ucr, CARD_INT_PEND,
750+
XD_INT | MS_INT | SD_INT,
751+
XD_INT | MS_INT | SD_INT);
709752

710-
poll_again:
711-
pm_runtime_put(ms_dev(host));
712-
if (host->eject)
713-
break;
753+
mutex_unlock(&ucr->dev_mutex);
714754

715-
schedule_timeout_idle(HZ);
755+
if (val & MS_INT) {
756+
dev_dbg(ms_dev(host), "MS slot change detected\n");
757+
memstick_detect_change(host->msh);
716758
}
717759

718-
complete(&host->detect_ms_exit);
719-
return 0;
760+
poll_again:
761+
pm_runtime_put_sync(ms_dev(host));
762+
763+
if (!host->eject && host->power_mode == MEMSTICK_POWER_ON)
764+
schedule_delayed_work(&host->poll_card, 100);
720765
}
721766

722767
static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
@@ -747,40 +792,36 @@ static int rtsx_usb_ms_drv_probe(struct platform_device *pdev)
747792
mutex_init(&host->host_mutex);
748793
INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req);
749794

750-
init_completion(&host->detect_ms_exit);
751-
host->detect_ms = kthread_create(rtsx_usb_detect_ms_card, host,
752-
"rtsx_usb_ms_%d", pdev->id);
753-
if (IS_ERR(host->detect_ms)) {
754-
dev_dbg(&(pdev->dev),
755-
"Unable to create polling thread.\n");
756-
err = PTR_ERR(host->detect_ms);
757-
goto err_out;
758-
}
795+
INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card);
759796

760797
msh->request = rtsx_usb_ms_request;
761798
msh->set_param = rtsx_usb_ms_set_param;
762799
msh->caps = MEMSTICK_CAP_PAR4;
763800

764-
pm_runtime_enable(&pdev->dev);
801+
pm_runtime_get_noresume(ms_dev(host));
802+
pm_runtime_set_active(ms_dev(host));
803+
pm_runtime_enable(ms_dev(host));
804+
765805
err = memstick_add_host(msh);
766806
if (err)
767807
goto err_out;
768808

769-
wake_up_process(host->detect_ms);
809+
pm_runtime_put(ms_dev(host));
810+
770811
return 0;
771812
err_out:
772813
memstick_free_host(msh);
773814
pm_runtime_disable(ms_dev(host));
815+
pm_runtime_put_noidle(ms_dev(host));
774816
return err;
775817
}
776818

777819
static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
778820
{
779821
struct rtsx_usb_ms *host = platform_get_drvdata(pdev);
780-
struct memstick_host *msh;
822+
struct memstick_host *msh = host->msh;
781823
int err;
782824

783-
msh = host->msh;
784825
host->eject = true;
785826
cancel_work_sync(&host->handle_req);
786827

@@ -798,7 +839,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
798839
}
799840
mutex_unlock(&host->host_mutex);
800841

801-
wait_for_completion(&host->detect_ms_exit);
802842
memstick_remove_host(msh);
803843
memstick_free_host(msh);
804844

@@ -817,9 +857,6 @@ static int rtsx_usb_ms_drv_remove(struct platform_device *pdev)
817857
return 0;
818858
}
819859

820-
static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops,
821-
rtsx_usb_ms_suspend, rtsx_usb_ms_resume);
822-
823860
static struct platform_device_id rtsx_usb_ms_ids[] = {
824861
{
825862
.name = "rtsx_usb_ms",

0 commit comments

Comments
 (0)