| .. | .. |
|---|
| 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 | { |
|---|