Skip to content

Commit 2d8ff0b

Browse files
westerigregkh
authored andcommitted
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 <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent fa3af1c commit 2d8ff0b

File tree

7 files changed

+276
-20
lines changed

7 files changed

+276
-20
lines changed

drivers/thunderbolt/domain.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/device.h>
1313
#include <linux/idr.h>
1414
#include <linux/module.h>
15+
#include <linux/pm_runtime.h>
1516
#include <linux/slab.h>
1617
#include <linux/random.h>
1718
#include <crypto/hash.h>
@@ -132,6 +133,8 @@ static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
132133
if (!uuids)
133134
return -ENOMEM;
134135

136+
pm_runtime_get_sync(&tb->dev);
137+
135138
if (mutex_lock_interruptible(&tb->lock)) {
136139
ret = -ERESTARTSYS;
137140
goto out;
@@ -153,7 +156,10 @@ static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
153156
}
154157

155158
out:
159+
pm_runtime_mark_last_busy(&tb->dev);
160+
pm_runtime_put_autosuspend(&tb->dev);
156161
kfree(uuids);
162+
157163
return ret;
158164
}
159165

@@ -208,9 +214,11 @@ static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
208214
goto err_free_acl;
209215
}
210216

217+
pm_runtime_get_sync(&tb->dev);
218+
211219
if (mutex_lock_interruptible(&tb->lock)) {
212220
ret = -ERESTARTSYS;
213-
goto err_free_acl;
221+
goto err_rpm_put;
214222
}
215223
ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
216224
if (!ret) {
@@ -219,6 +227,9 @@ static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
219227
}
220228
mutex_unlock(&tb->lock);
221229

230+
err_rpm_put:
231+
pm_runtime_mark_last_busy(&tb->dev);
232+
pm_runtime_put_autosuspend(&tb->dev);
222233
err_free_acl:
223234
kfree(acl);
224235
err_free_str:
@@ -430,6 +441,13 @@ int tb_domain_add(struct tb *tb)
430441
/* This starts event processing */
431442
mutex_unlock(&tb->lock);
432443

444+
pm_runtime_no_callbacks(&tb->dev);
445+
pm_runtime_set_active(&tb->dev);
446+
pm_runtime_enable(&tb->dev);
447+
pm_runtime_set_autosuspend_delay(&tb->dev, TB_AUTOSUSPEND_DELAY);
448+
pm_runtime_mark_last_busy(&tb->dev);
449+
pm_runtime_use_autosuspend(&tb->dev);
450+
433451
return 0;
434452

435453
err_domain_del:
@@ -518,6 +536,28 @@ void tb_domain_complete(struct tb *tb)
518536
tb->cm_ops->complete(tb);
519537
}
520538

539+
int tb_domain_runtime_suspend(struct tb *tb)
540+
{
541+
if (tb->cm_ops->runtime_suspend) {
542+
int ret = tb->cm_ops->runtime_suspend(tb);
543+
if (ret)
544+
return ret;
545+
}
546+
tb_ctl_stop(tb->ctl);
547+
return 0;
548+
}
549+
550+
int tb_domain_runtime_resume(struct tb *tb)
551+
{
552+
tb_ctl_start(tb->ctl);
553+
if (tb->cm_ops->runtime_resume) {
554+
int ret = tb->cm_ops->runtime_resume(tb);
555+
if (ret)
556+
return ret;
557+
}
558+
return 0;
559+
}
560+
521561
/**
522562
* tb_domain_approve_switch() - Approve switch
523563
* @tb: Domain the switch belongs to

drivers/thunderbolt/icm.c

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/delay.h>
1616
#include <linux/mutex.h>
1717
#include <linux/pci.h>
18+
#include <linux/pm_runtime.h>
1819
#include <linux/platform_data/x86/apple.h>
1920
#include <linux/sizes.h>
2021
#include <linux/slab.h>
@@ -57,6 +58,7 @@
5758
* (only set when @upstream_port is not %NULL)
5859
* @safe_mode: ICM is in safe mode
5960
* @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
61+
* @rpm: Does the controller support runtime PM (RTD3)
6062
* @is_supported: Checks if we can support ICM on this controller
6163
* @get_mode: Read and return the ICM firmware mode (optional)
6264
* @get_route: Find a route string for given switch
@@ -74,13 +76,14 @@ struct icm {
7476
size_t max_boot_acl;
7577
int vnd_cap;
7678
bool safe_mode;
79+
bool rpm;
7780
bool (*is_supported)(struct tb *tb);
7881
int (*get_mode)(struct tb *tb);
7982
int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
8083
void (*save_devices)(struct tb *tb);
8184
int (*driver_ready)(struct tb *tb,
8285
enum tb_security_level *security_level,
83-
size_t *nboot_acl);
86+
size_t *nboot_acl, bool *rpm);
8487
void (*device_connected)(struct tb *tb,
8588
const struct icm_pkg_header *hdr);
8689
void (*device_disconnected)(struct tb *tb,
@@ -97,6 +100,47 @@ struct icm_notification {
97100
struct tb *tb;
98101
};
99102

103+
struct ep_name_entry {
104+
u8 len;
105+
u8 type;
106+
u8 data[0];
107+
};
108+
109+
#define EP_NAME_INTEL_VSS 0x10
110+
111+
/* Intel Vendor specific structure */
112+
struct intel_vss {
113+
u16 vendor;
114+
u16 model;
115+
u8 mc;
116+
u8 flags;
117+
u16 pci_devid;
118+
u32 nvm_version;
119+
};
120+
121+
#define INTEL_VSS_FLAGS_RTD3 BIT(0)
122+
123+
static const struct intel_vss *parse_intel_vss(const void *ep_name, size_t size)
124+
{
125+
const void *end = ep_name + size;
126+
127+
while (ep_name < end) {
128+
const struct ep_name_entry *ep = ep_name;
129+
130+
if (!ep->len)
131+
break;
132+
if (ep_name + ep->len > end)
133+
break;
134+
135+
if (ep->type == EP_NAME_INTEL_VSS)
136+
return (const struct intel_vss *)ep->data;
137+
138+
ep_name += ep->len;
139+
}
140+
141+
return NULL;
142+
}
143+
100144
static inline struct tb *icm_to_tb(struct icm *icm)
101145
{
102146
return ((void *)icm - sizeof(struct tb));
@@ -267,7 +311,7 @@ static void icm_fr_save_devices(struct tb *tb)
267311

268312
static int
269313
icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
270-
size_t *nboot_acl)
314+
size_t *nboot_acl, bool *rpm)
271315
{
272316
struct icm_fr_pkg_driver_ready_response reply;
273317
struct icm_pkg_driver_ready request = {
@@ -417,15 +461,19 @@ static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd)
417461
}
418462

419463
static void add_switch(struct tb_switch *parent_sw, u64 route,
420-
const uuid_t *uuid, u8 connection_id, u8 connection_key,
464+
const uuid_t *uuid, const u8 *ep_name,
465+
size_t ep_name_size, u8 connection_id, u8 connection_key,
421466
u8 link, u8 depth, enum tb_security_level security_level,
422467
bool authorized, bool boot)
423468
{
469+
const struct intel_vss *vss;
424470
struct tb_switch *sw;
425471

472+
pm_runtime_get_sync(&parent_sw->dev);
473+
426474
sw = tb_switch_alloc(parent_sw->tb, &parent_sw->dev, route);
427475
if (!sw)
428-
return;
476+
goto out;
429477

430478
sw->uuid = kmemdup(uuid, sizeof(*uuid), GFP_KERNEL);
431479
sw->connection_id = connection_id;
@@ -436,15 +484,22 @@ static void add_switch(struct tb_switch *parent_sw, u64 route,
436484
sw->security_level = security_level;
437485
sw->boot = boot;
438486

487+
vss = parse_intel_vss(ep_name, ep_name_size);
488+
if (vss)
489+
sw->rpm = !!(vss->flags & INTEL_VSS_FLAGS_RTD3);
490+
439491
/* Link the two switches now */
440492
tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw);
441493
tb_upstream_port(sw)->remote = tb_port_at(route, parent_sw);
442494

443495
if (tb_switch_add(sw)) {
444496
tb_port_at(tb_route(sw), parent_sw)->remote = NULL;
445497
tb_switch_put(sw);
446-
return;
447498
}
499+
500+
out:
501+
pm_runtime_mark_last_busy(&parent_sw->dev);
502+
pm_runtime_put_autosuspend(&parent_sw->dev);
448503
}
449504

450505
static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw,
@@ -484,16 +539,22 @@ static void add_xdomain(struct tb_switch *sw, u64 route,
484539
{
485540
struct tb_xdomain *xd;
486541

542+
pm_runtime_get_sync(&sw->dev);
543+
487544
xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, local_uuid, remote_uuid);
488545
if (!xd)
489-
return;
546+
goto out;
490547

491548
xd->link = link;
492549
xd->depth = depth;
493550

494551
tb_port_at(route, sw)->xdomain = xd;
495552

496553
tb_xdomain_add(xd);
554+
555+
out:
556+
pm_runtime_mark_last_busy(&sw->dev);
557+
pm_runtime_put_autosuspend(&sw->dev);
497558
}
498559

499560
static void update_xdomain(struct tb_xdomain *xd, u64 route, u8 link)
@@ -631,7 +692,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
631692
return;
632693
}
633694

634-
add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
695+
add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
696+
sizeof(pkg->ep_name), pkg->connection_id,
635697
pkg->connection_key, link, depth, security_level,
636698
authorized, boot);
637699

@@ -779,7 +841,7 @@ icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr)
779841

780842
static int
781843
icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
782-
size_t *nboot_acl)
844+
size_t *nboot_acl, bool *rpm)
783845
{
784846
struct icm_tr_pkg_driver_ready_response reply;
785847
struct icm_pkg_driver_ready request = {
@@ -798,6 +860,9 @@ icm_tr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
798860
if (nboot_acl)
799861
*nboot_acl = (reply.info & ICM_TR_INFO_BOOT_ACL_MASK) >>
800862
ICM_TR_INFO_BOOT_ACL_SHIFT;
863+
if (rpm)
864+
*rpm = !!(reply.hdr.flags & ICM_TR_FLAGS_RTD3);
865+
801866
return 0;
802867
}
803868

@@ -1027,7 +1092,8 @@ icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr)
10271092
return;
10281093
}
10291094

1030-
add_switch(parent_sw, route, &pkg->ep_uuid, pkg->connection_id,
1095+
add_switch(parent_sw, route, &pkg->ep_uuid, (const u8 *)pkg->ep_name,
1096+
sizeof(pkg->ep_name), pkg->connection_id,
10311097
0, 0, 0, security_level, authorized, boot);
10321098

10331099
tb_switch_put(parent_sw);
@@ -1206,7 +1272,7 @@ static int icm_ar_get_mode(struct tb *tb)
12061272

12071273
static int
12081274
icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
1209-
size_t *nboot_acl)
1275+
size_t *nboot_acl, bool *rpm)
12101276
{
12111277
struct icm_ar_pkg_driver_ready_response reply;
12121278
struct icm_pkg_driver_ready request = {
@@ -1225,6 +1291,9 @@ icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
12251291
if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED))
12261292
*nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >>
12271293
ICM_AR_INFO_BOOT_ACL_SHIFT;
1294+
if (rpm)
1295+
*rpm = !!(reply.hdr.flags & ICM_AR_FLAGS_RTD3);
1296+
12281297
return 0;
12291298
}
12301299

@@ -1378,13 +1447,13 @@ static void icm_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
13781447

13791448
static int
13801449
__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level,
1381-
size_t *nboot_acl)
1450+
size_t *nboot_acl, bool *rpm)
13821451
{
13831452
struct icm *icm = tb_priv(tb);
13841453
unsigned int retries = 50;
13851454
int ret;
13861455

1387-
ret = icm->driver_ready(tb, security_level, nboot_acl);
1456+
ret = icm->driver_ready(tb, security_level, nboot_acl, rpm);
13881457
if (ret) {
13891458
tb_err(tb, "failed to send driver ready to ICM\n");
13901459
return ret;
@@ -1654,7 +1723,8 @@ static int icm_driver_ready(struct tb *tb)
16541723
return 0;
16551724
}
16561725

1657-
ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl);
1726+
ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl,
1727+
&icm->rpm);
16581728
if (ret)
16591729
return ret;
16601730

@@ -1760,7 +1830,7 @@ static void icm_complete(struct tb *tb)
17601830
* Now all existing children should be resumed, start events
17611831
* from ICM to get updated status.
17621832
*/
1763-
__icm_driver_ready(tb, NULL, NULL);
1833+
__icm_driver_ready(tb, NULL, NULL, NULL);
17641834

17651835
/*
17661836
* We do not get notifications of devices that have been
@@ -1770,6 +1840,22 @@ static void icm_complete(struct tb *tb)
17701840
queue_delayed_work(tb->wq, &icm->rescan_work, msecs_to_jiffies(500));
17711841
}
17721842

1843+
static int icm_runtime_suspend(struct tb *tb)
1844+
{
1845+
nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DRV_UNLOADS, 0);
1846+
return 0;
1847+
}
1848+
1849+
static int icm_runtime_resume(struct tb *tb)
1850+
{
1851+
/*
1852+
* We can reuse the same resume functionality than with system
1853+
* suspend.
1854+
*/
1855+
icm_complete(tb);
1856+
return 0;
1857+
}
1858+
17731859
static int icm_start(struct tb *tb)
17741860
{
17751861
struct icm *icm = tb_priv(tb);
@@ -1788,6 +1874,7 @@ static int icm_start(struct tb *tb)
17881874
* prevent root switch NVM upgrade on Macs for now.
17891875
*/
17901876
tb->root_switch->no_nvm_upgrade = x86_apple_machine;
1877+
tb->root_switch->rpm = icm->rpm;
17911878

17921879
ret = tb_switch_add(tb->root_switch);
17931880
if (ret) {
@@ -1836,6 +1923,8 @@ static const struct tb_cm_ops icm_ar_ops = {
18361923
.stop = icm_stop,
18371924
.suspend = icm_suspend,
18381925
.complete = icm_complete,
1926+
.runtime_suspend = icm_runtime_suspend,
1927+
.runtime_resume = icm_runtime_resume,
18391928
.handle_event = icm_handle_event,
18401929
.get_boot_acl = icm_ar_get_boot_acl,
18411930
.set_boot_acl = icm_ar_set_boot_acl,
@@ -1854,6 +1943,8 @@ static const struct tb_cm_ops icm_tr_ops = {
18541943
.stop = icm_stop,
18551944
.suspend = icm_suspend,
18561945
.complete = icm_complete,
1946+
.runtime_suspend = icm_runtime_suspend,
1947+
.runtime_resume = icm_runtime_resume,
18571948
.handle_event = icm_handle_event,
18581949
.get_boot_acl = icm_ar_get_boot_acl,
18591950
.set_boot_acl = icm_ar_set_boot_acl,

0 commit comments

Comments
 (0)