.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* Realtek USB Memstick Card Interface driver |
---|
2 | 3 | * |
---|
3 | 4 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or modify it |
---|
6 | | - * under the terms of the GNU General Public License version 2 |
---|
7 | | - * as published by the Free Software Foundation. |
---|
8 | | - * |
---|
9 | | - * This program is distributed in the hope that it will be useful, but |
---|
10 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | | - * General Public License for more details. |
---|
13 | | - * |
---|
14 | | - * You should have received a copy of the GNU General Public License along |
---|
15 | | - * with this program; if not, see <http://www.gnu.org/licenses/>. |
---|
16 | 5 | * |
---|
17 | 6 | * Author: |
---|
18 | 7 | * Roger Tseng <rogerable@realtek.com> |
---|
.. | .. |
---|
40 | 29 | |
---|
41 | 30 | struct mutex host_mutex; |
---|
42 | 31 | struct work_struct handle_req; |
---|
43 | | - |
---|
44 | | - struct task_struct *detect_ms; |
---|
45 | | - struct completion detect_ms_exit; |
---|
| 32 | + struct delayed_work poll_card; |
---|
46 | 33 | |
---|
47 | 34 | u8 ssc_depth; |
---|
48 | 35 | unsigned int clock; |
---|
49 | 36 | int power_mode; |
---|
50 | 37 | unsigned char ifmode; |
---|
51 | 38 | bool eject; |
---|
| 39 | + bool system_suspending; |
---|
52 | 40 | }; |
---|
53 | 41 | |
---|
54 | 42 | static inline struct device *ms_dev(struct rtsx_usb_ms *host) |
---|
.. | .. |
---|
545 | 533 | host->req->error); |
---|
546 | 534 | } |
---|
547 | 535 | } while (!rc); |
---|
548 | | - pm_runtime_put(ms_dev(host)); |
---|
| 536 | + pm_runtime_put_sync(ms_dev(host)); |
---|
549 | 537 | } |
---|
550 | 538 | |
---|
551 | 539 | } |
---|
.. | .. |
---|
585 | 573 | break; |
---|
586 | 574 | |
---|
587 | 575 | if (value == MEMSTICK_POWER_ON) { |
---|
588 | | - pm_runtime_get_sync(ms_dev(host)); |
---|
| 576 | + pm_runtime_get_noresume(ms_dev(host)); |
---|
589 | 577 | err = ms_power_on(host); |
---|
| 578 | + if (err) |
---|
| 579 | + pm_runtime_put_noidle(ms_dev(host)); |
---|
590 | 580 | } else if (value == MEMSTICK_POWER_OFF) { |
---|
591 | 581 | err = ms_power_off(host); |
---|
592 | | - if (host->msh->card) |
---|
| 582 | + if (!err) |
---|
593 | 583 | pm_runtime_put_noidle(ms_dev(host)); |
---|
594 | | - else |
---|
595 | | - pm_runtime_put(ms_dev(host)); |
---|
596 | 584 | } else |
---|
597 | 585 | err = -EINVAL; |
---|
598 | 586 | if (!err) |
---|
.. | .. |
---|
638 | 626 | } |
---|
639 | 627 | out: |
---|
640 | 628 | mutex_unlock(&ucr->dev_mutex); |
---|
641 | | - pm_runtime_put(ms_dev(host)); |
---|
| 629 | + pm_runtime_put_sync(ms_dev(host)); |
---|
642 | 630 | |
---|
643 | 631 | /* power-on delay */ |
---|
644 | | - if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) |
---|
| 632 | + if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) { |
---|
645 | 633 | usleep_range(10000, 12000); |
---|
| 634 | + |
---|
| 635 | + if (!host->eject) |
---|
| 636 | + schedule_delayed_work(&host->poll_card, 100); |
---|
| 637 | + } |
---|
646 | 638 | |
---|
647 | 639 | dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err); |
---|
648 | 640 | return err; |
---|
.. | .. |
---|
654 | 646 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
---|
655 | 647 | struct memstick_host *msh = host->msh; |
---|
656 | 648 | |
---|
657 | | - dev_dbg(ms_dev(host), "--> %s\n", __func__); |
---|
| 649 | + /* Since we use rtsx_usb's resume callback to runtime resume its |
---|
| 650 | + * children to implement remote wakeup signaling, this causes |
---|
| 651 | + * rtsx_usb_ms' runtime resume callback runs after its suspend |
---|
| 652 | + * callback: |
---|
| 653 | + * rtsx_usb_ms_suspend() |
---|
| 654 | + * rtsx_usb_resume() |
---|
| 655 | + * -> rtsx_usb_ms_runtime_resume() |
---|
| 656 | + * -> memstick_detect_change() |
---|
| 657 | + * |
---|
| 658 | + * rtsx_usb_suspend() |
---|
| 659 | + * |
---|
| 660 | + * To avoid this, skip runtime resume/suspend if system suspend is |
---|
| 661 | + * underway. |
---|
| 662 | + */ |
---|
658 | 663 | |
---|
| 664 | + host->system_suspending = true; |
---|
659 | 665 | memstick_suspend_host(msh); |
---|
| 666 | + |
---|
660 | 667 | return 0; |
---|
661 | 668 | } |
---|
662 | 669 | |
---|
.. | .. |
---|
665 | 672 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
---|
666 | 673 | struct memstick_host *msh = host->msh; |
---|
667 | 674 | |
---|
668 | | - dev_dbg(ms_dev(host), "--> %s\n", __func__); |
---|
669 | | - |
---|
670 | 675 | memstick_resume_host(msh); |
---|
| 676 | + host->system_suspending = false; |
---|
| 677 | + |
---|
671 | 678 | return 0; |
---|
672 | 679 | } |
---|
673 | 680 | #endif /* CONFIG_PM_SLEEP */ |
---|
674 | 681 | |
---|
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) |
---|
| 682 | +#ifdef CONFIG_PM |
---|
| 683 | +static int rtsx_usb_ms_runtime_suspend(struct device *dev) |
---|
681 | 684 | { |
---|
682 | | - struct rtsx_usb_ms *host = (struct rtsx_usb_ms *)__host; |
---|
| 685 | + struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
---|
| 686 | + |
---|
| 687 | + if (host->system_suspending) |
---|
| 688 | + return 0; |
---|
| 689 | + |
---|
| 690 | + if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF) |
---|
| 691 | + return -EAGAIN; |
---|
| 692 | + |
---|
| 693 | + return 0; |
---|
| 694 | +} |
---|
| 695 | + |
---|
| 696 | +static int rtsx_usb_ms_runtime_resume(struct device *dev) |
---|
| 697 | +{ |
---|
| 698 | + struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
---|
| 699 | + |
---|
| 700 | + |
---|
| 701 | + if (host->system_suspending) |
---|
| 702 | + return 0; |
---|
| 703 | + |
---|
| 704 | + memstick_detect_change(host->msh); |
---|
| 705 | + |
---|
| 706 | + return 0; |
---|
| 707 | +} |
---|
| 708 | +#endif /* CONFIG_PM */ |
---|
| 709 | + |
---|
| 710 | +static const struct dev_pm_ops rtsx_usb_ms_pm_ops = { |
---|
| 711 | + SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume) |
---|
| 712 | + SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL) |
---|
| 713 | +}; |
---|
| 714 | + |
---|
| 715 | + |
---|
| 716 | +static void rtsx_usb_ms_poll_card(struct work_struct *work) |
---|
| 717 | +{ |
---|
| 718 | + struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms, |
---|
| 719 | + poll_card.work); |
---|
683 | 720 | struct rtsx_ucr *ucr = host->ucr; |
---|
684 | | - u8 val = 0; |
---|
685 | 721 | int err; |
---|
| 722 | + u8 val; |
---|
686 | 723 | |
---|
687 | | - for (;;) { |
---|
688 | | - pm_runtime_get_sync(ms_dev(host)); |
---|
689 | | - mutex_lock(&ucr->dev_mutex); |
---|
| 724 | + if (host->eject || host->power_mode != MEMSTICK_POWER_ON) |
---|
| 725 | + return; |
---|
690 | 726 | |
---|
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 | | - } |
---|
| 727 | + pm_runtime_get_sync(ms_dev(host)); |
---|
| 728 | + mutex_lock(&ucr->dev_mutex); |
---|
697 | 729 | |
---|
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); |
---|
702 | | - |
---|
| 730 | + /* Check pending MS card changes */ |
---|
| 731 | + err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val); |
---|
| 732 | + if (err) { |
---|
703 | 733 | mutex_unlock(&ucr->dev_mutex); |
---|
704 | | - |
---|
705 | | - if (val & MS_INT) { |
---|
706 | | - dev_dbg(ms_dev(host), "MS slot change detected\n"); |
---|
707 | | - memstick_detect_change(host->msh); |
---|
708 | | - } |
---|
709 | | - |
---|
710 | | -poll_again: |
---|
711 | | - pm_runtime_put(ms_dev(host)); |
---|
712 | | - if (host->eject) |
---|
713 | | - break; |
---|
714 | | - |
---|
715 | | - schedule_timeout_idle(HZ); |
---|
| 734 | + goto poll_again; |
---|
716 | 735 | } |
---|
717 | 736 | |
---|
718 | | - complete(&host->detect_ms_exit); |
---|
719 | | - return 0; |
---|
| 737 | + /* Clear the pending */ |
---|
| 738 | + rtsx_usb_write_register(ucr, CARD_INT_PEND, |
---|
| 739 | + XD_INT | MS_INT | SD_INT, |
---|
| 740 | + XD_INT | MS_INT | SD_INT); |
---|
| 741 | + |
---|
| 742 | + mutex_unlock(&ucr->dev_mutex); |
---|
| 743 | + |
---|
| 744 | + if (val & MS_INT) { |
---|
| 745 | + dev_dbg(ms_dev(host), "MS slot change detected\n"); |
---|
| 746 | + memstick_detect_change(host->msh); |
---|
| 747 | + } |
---|
| 748 | + |
---|
| 749 | +poll_again: |
---|
| 750 | + pm_runtime_put_sync(ms_dev(host)); |
---|
| 751 | + |
---|
| 752 | + if (!host->eject && host->power_mode == MEMSTICK_POWER_ON) |
---|
| 753 | + schedule_delayed_work(&host->poll_card, 100); |
---|
720 | 754 | } |
---|
721 | 755 | |
---|
722 | 756 | static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) |
---|
.. | .. |
---|
747 | 781 | mutex_init(&host->host_mutex); |
---|
748 | 782 | INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); |
---|
749 | 783 | |
---|
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 | | - } |
---|
| 784 | + INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); |
---|
759 | 785 | |
---|
760 | 786 | msh->request = rtsx_usb_ms_request; |
---|
761 | 787 | msh->set_param = rtsx_usb_ms_set_param; |
---|
762 | 788 | msh->caps = MEMSTICK_CAP_PAR4; |
---|
763 | 789 | |
---|
764 | | - pm_runtime_enable(&pdev->dev); |
---|
| 790 | + pm_runtime_get_noresume(ms_dev(host)); |
---|
| 791 | + pm_runtime_set_active(ms_dev(host)); |
---|
| 792 | + pm_runtime_enable(ms_dev(host)); |
---|
| 793 | + |
---|
765 | 794 | err = memstick_add_host(msh); |
---|
766 | 795 | if (err) |
---|
767 | 796 | goto err_out; |
---|
768 | 797 | |
---|
769 | | - wake_up_process(host->detect_ms); |
---|
| 798 | + pm_runtime_put(ms_dev(host)); |
---|
| 799 | + |
---|
770 | 800 | return 0; |
---|
771 | 801 | err_out: |
---|
| 802 | + pm_runtime_disable(ms_dev(host)); |
---|
| 803 | + pm_runtime_put_noidle(ms_dev(host)); |
---|
772 | 804 | memstick_free_host(msh); |
---|
773 | 805 | return err; |
---|
774 | 806 | } |
---|
.. | .. |
---|
776 | 808 | static int rtsx_usb_ms_drv_remove(struct platform_device *pdev) |
---|
777 | 809 | { |
---|
778 | 810 | struct rtsx_usb_ms *host = platform_get_drvdata(pdev); |
---|
779 | | - struct memstick_host *msh; |
---|
| 811 | + struct memstick_host *msh = host->msh; |
---|
780 | 812 | int err; |
---|
781 | 813 | |
---|
782 | | - msh = host->msh; |
---|
783 | 814 | host->eject = true; |
---|
784 | 815 | cancel_work_sync(&host->handle_req); |
---|
785 | 816 | |
---|
786 | 817 | mutex_lock(&host->host_mutex); |
---|
787 | 818 | if (host->req) { |
---|
788 | | - dev_dbg(&(pdev->dev), |
---|
| 819 | + dev_dbg(ms_dev(host), |
---|
789 | 820 | "%s: Controller removed during transfer\n", |
---|
790 | 821 | dev_name(&msh->dev)); |
---|
791 | 822 | host->req->error = -ENOMEDIUM; |
---|
.. | .. |
---|
797 | 828 | } |
---|
798 | 829 | mutex_unlock(&host->host_mutex); |
---|
799 | 830 | |
---|
800 | | - wait_for_completion(&host->detect_ms_exit); |
---|
801 | | - memstick_remove_host(msh); |
---|
802 | | - memstick_free_host(msh); |
---|
803 | | - |
---|
804 | 831 | /* Balance possible unbalanced usage count |
---|
805 | 832 | * e.g. unconditional module removal |
---|
806 | 833 | */ |
---|
807 | 834 | if (pm_runtime_active(ms_dev(host))) |
---|
808 | 835 | pm_runtime_put(ms_dev(host)); |
---|
809 | 836 | |
---|
810 | | - pm_runtime_disable(&pdev->dev); |
---|
811 | | - platform_set_drvdata(pdev, NULL); |
---|
812 | | - |
---|
813 | | - dev_dbg(&(pdev->dev), |
---|
| 837 | + pm_runtime_disable(ms_dev(host)); |
---|
| 838 | + memstick_remove_host(msh); |
---|
| 839 | + dev_dbg(ms_dev(host), |
---|
814 | 840 | ": Realtek USB Memstick controller has been removed\n"); |
---|
| 841 | + memstick_free_host(msh); |
---|
| 842 | + platform_set_drvdata(pdev, NULL); |
---|
815 | 843 | |
---|
816 | 844 | return 0; |
---|
817 | 845 | } |
---|
818 | | - |
---|
819 | | -static SIMPLE_DEV_PM_OPS(rtsx_usb_ms_pm_ops, |
---|
820 | | - rtsx_usb_ms_suspend, rtsx_usb_ms_resume); |
---|
821 | 846 | |
---|
822 | 847 | static struct platform_device_id rtsx_usb_ms_ids[] = { |
---|
823 | 848 | { |
---|