diff options
author | Mika Westerberg <mika.westerberg@linux.intel.com> | 2018-07-25 11:48:39 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-07-25 10:55:29 +0200 |
commit | 2d8ff0b586fb1c5bd81a3ab286dcc6bbc432044e (patch) | |
tree | 0bdc18fec7a9d72e29b0b754a24b9a1df876b34b /drivers/thunderbolt/nhi.c | |
parent | fa3af1cb1ec073265c9c87ec44faf006f2f12d96 (diff) | |
download | linux-2d8ff0b586fb1c5bd81a3ab286dcc6bbc432044e.tar.bz2 |
thunderbolt: Add support for runtime PM
When Thunderbolt host controller is set to RTD3 mode (Runtime D3) it is
present all the time. Because of this it is important to runtime suspend
the controller whenever possible. In case of ICM we have following rules
which all needs to be true before the host controller can be put to D3:
- The controller firmware reports to support RTD3
- All the connected devices announce support for RTD3
- There is no active XDomain connection
Implement this using standard Linux runtime PM APIs so that when all the
children devices are runtime suspended, the Thunderbolt host controller
PCI device is runtime suspended as well. The ICM firmware then starts
powering down power domains towards RTD3 but it can prevent this if it
detects that there is an active Display Port stream (this is not visible
to the software, though).
The Thunderbolt host controller will be runtime resumed either when
there is a remote wake event (device is connected or disconnected), or
when there is access from userspace that requires hardware access.
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt/nhi.c')
-rw-r--r-- | drivers/thunderbolt/nhi.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index e3b7695fe37e..88cff05a1808 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -900,7 +900,32 @@ static void nhi_complete(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); - tb_domain_complete(tb); + /* + * If we were runtime suspended when system suspend started, + * schedule runtime resume now. It should bring the domain back + * to functional state. + */ + if (pm_runtime_suspended(&pdev->dev)) + pm_runtime_resume(&pdev->dev); + else + tb_domain_complete(tb); +} + +static int nhi_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct tb *tb = pci_get_drvdata(pdev); + + return tb_domain_runtime_suspend(tb); +} + +static int nhi_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct tb *tb = pci_get_drvdata(pdev); + + nhi_enable_int_throttling(tb->nhi); + return tb_domain_runtime_resume(tb); } static void nhi_shutdown(struct tb_nhi *nhi) @@ -1048,6 +1073,11 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) } pci_set_drvdata(pdev, tb); + pm_runtime_allow(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, TB_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + return 0; } @@ -1056,6 +1086,10 @@ static void nhi_remove(struct pci_dev *pdev) struct tb *tb = pci_get_drvdata(pdev); struct tb_nhi *nhi = tb->nhi; + pm_runtime_get_sync(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + tb_domain_remove(tb); nhi_shutdown(nhi); } @@ -1078,6 +1112,8 @@ static const struct dev_pm_ops nhi_pm_ops = { .freeze = nhi_suspend, .poweroff = nhi_suspend, .complete = nhi_complete, + .runtime_suspend = nhi_runtime_suspend, + .runtime_resume = nhi_runtime_resume, }; static struct pci_device_id nhi_ids[] = { |