summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt/switch.c
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2018-07-25 04:48:39 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-07-25 04:55:29 -0400
commit2d8ff0b586fb1c5bd81a3ab286dcc6bbc432044e (patch)
tree0bdc18fec7a9d72e29b0b754a24b9a1df876b34b /drivers/thunderbolt/switch.c
parentfa3af1cb1ec073265c9c87ec44faf006f2f12d96 (diff)
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/switch.c')
-rw-r--r--drivers/thunderbolt/switch.c65
1 files changed, 61 insertions, 4 deletions
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 25758671ddf4..7442bc4c6433 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -8,6 +8,7 @@
8#include <linux/delay.h> 8#include <linux/delay.h>
9#include <linux/idr.h> 9#include <linux/idr.h>
10#include <linux/nvmem-provider.h> 10#include <linux/nvmem-provider.h>
11#include <linux/pm_runtime.h>
11#include <linux/sizes.h> 12#include <linux/sizes.h>
12#include <linux/slab.h> 13#include <linux/slab.h>
13#include <linux/vmalloc.h> 14#include <linux/vmalloc.h>
@@ -236,8 +237,14 @@ static int tb_switch_nvm_read(void *priv, unsigned int offset, void *val,
236 size_t bytes) 237 size_t bytes)
237{ 238{
238 struct tb_switch *sw = priv; 239 struct tb_switch *sw = priv;
240 int ret;
241
242 pm_runtime_get_sync(&sw->dev);
243 ret = dma_port_flash_read(sw->dma_port, offset, val, bytes);
244 pm_runtime_mark_last_busy(&sw->dev);
245 pm_runtime_put_autosuspend(&sw->dev);
239 246
240 return dma_port_flash_read(sw->dma_port, offset, val, bytes); 247 return ret;
241} 248}
242 249
243static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val, 250static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
@@ -722,6 +729,7 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
722 * the new tunnel too early. 729 * the new tunnel too early.
723 */ 730 */
724 pci_lock_rescan_remove(); 731 pci_lock_rescan_remove();
732 pm_runtime_get_sync(&sw->dev);
725 733
726 switch (val) { 734 switch (val) {
727 /* Approve switch */ 735 /* Approve switch */
@@ -742,6 +750,8 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
742 break; 750 break;
743 } 751 }
744 752
753 pm_runtime_mark_last_busy(&sw->dev);
754 pm_runtime_put_autosuspend(&sw->dev);
745 pci_unlock_rescan_remove(); 755 pci_unlock_rescan_remove();
746 756
747 if (!ret) { 757 if (!ret) {
@@ -888,9 +898,18 @@ static ssize_t nvm_authenticate_store(struct device *dev,
888 nvm_clear_auth_status(sw); 898 nvm_clear_auth_status(sw);
889 899
890 if (val) { 900 if (val) {
901 if (!sw->nvm->buf) {
902 ret = -EINVAL;
903 goto exit_unlock;
904 }
905
906 pm_runtime_get_sync(&sw->dev);
891 ret = nvm_validate_and_write(sw); 907 ret = nvm_validate_and_write(sw);
892 if (ret) 908 if (ret) {
909 pm_runtime_mark_last_busy(&sw->dev);
910 pm_runtime_put_autosuspend(&sw->dev);
893 goto exit_unlock; 911 goto exit_unlock;
912 }
894 913
895 sw->nvm->authenticating = true; 914 sw->nvm->authenticating = true;
896 915
@@ -898,6 +917,8 @@ static ssize_t nvm_authenticate_store(struct device *dev,
898 ret = nvm_authenticate_host(sw); 917 ret = nvm_authenticate_host(sw);
899 else 918 else
900 ret = nvm_authenticate_device(sw); 919 ret = nvm_authenticate_device(sw);
920 pm_runtime_mark_last_busy(&sw->dev);
921 pm_runtime_put_autosuspend(&sw->dev);
901 } 922 }
902 923
903exit_unlock: 924exit_unlock:
@@ -1023,9 +1044,29 @@ static void tb_switch_release(struct device *dev)
1023 kfree(sw); 1044 kfree(sw);
1024} 1045}
1025 1046
1047/*
1048 * Currently only need to provide the callbacks. Everything else is handled
1049 * in the connection manager.
1050 */
1051static int __maybe_unused tb_switch_runtime_suspend(struct device *dev)
1052{
1053 return 0;
1054}
1055
1056static int __maybe_unused tb_switch_runtime_resume(struct device *dev)
1057{
1058 return 0;
1059}
1060
1061static const struct dev_pm_ops tb_switch_pm_ops = {
1062 SET_RUNTIME_PM_OPS(tb_switch_runtime_suspend, tb_switch_runtime_resume,
1063 NULL)
1064};
1065
1026struct device_type tb_switch_type = { 1066struct device_type tb_switch_type = {
1027 .name = "thunderbolt_device", 1067 .name = "thunderbolt_device",
1028 .release = tb_switch_release, 1068 .release = tb_switch_release,
1069 .pm = &tb_switch_pm_ops,
1029}; 1070};
1030 1071
1031static int tb_switch_get_generation(struct tb_switch *sw) 1072static int tb_switch_get_generation(struct tb_switch *sw)
@@ -1365,10 +1406,21 @@ int tb_switch_add(struct tb_switch *sw)
1365 return ret; 1406 return ret;
1366 1407
1367 ret = tb_switch_nvm_add(sw); 1408 ret = tb_switch_nvm_add(sw);
1368 if (ret) 1409 if (ret) {
1369 device_del(&sw->dev); 1410 device_del(&sw->dev);
1411 return ret;
1412 }
1370 1413
1371 return ret; 1414 pm_runtime_set_active(&sw->dev);
1415 if (sw->rpm) {
1416 pm_runtime_set_autosuspend_delay(&sw->dev, TB_AUTOSUSPEND_DELAY);
1417 pm_runtime_use_autosuspend(&sw->dev);
1418 pm_runtime_mark_last_busy(&sw->dev);
1419 pm_runtime_enable(&sw->dev);
1420 pm_request_autosuspend(&sw->dev);
1421 }
1422
1423 return 0;
1372} 1424}
1373 1425
1374/** 1426/**
@@ -1383,6 +1435,11 @@ void tb_switch_remove(struct tb_switch *sw)
1383{ 1435{
1384 int i; 1436 int i;
1385 1437
1438 if (sw->rpm) {
1439 pm_runtime_get_sync(&sw->dev);
1440 pm_runtime_disable(&sw->dev);
1441 }
1442
1386 /* port 0 is the switch itself and never has a remote */ 1443 /* port 0 is the switch itself and never has a remote */
1387 for (i = 1; i <= sw->config.max_port_number; i++) { 1444 for (i = 1; i <= sw->config.max_port_number; i++) {
1388 if (tb_is_upstream_port(&sw->ports[i])) 1445 if (tb_is_upstream_port(&sw->ports[i]))