diff options
author | Daniel Drake <drake@endlessm.com> | 2018-09-27 16:47:33 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-09-27 16:47:33 -0400 |
commit | 083874549fdfefa629dfa752785e20427dde1511 (patch) | |
tree | d5d0379bb9783300847e22e0e61b0c145df525ac | |
parent | f188b99f0b2d33794b4af8a225f95d1e968c0a3f (diff) |
PCI: Reprogram bridge prefetch registers on resume
On 38+ Intel-based ASUS products, the NVIDIA GPU becomes unusable after S3
suspend/resume. The affected products include multiple generations of
NVIDIA GPUs and Intel SoCs. After resume, nouveau logs many errors such
as:
fifo: fault 00 [READ] at 0000005555555000 engine 00 [GR] client 04
[HUB/FE] reason 4a [] on channel -1 [007fa91000 unknown]
DRM: failed to idle channel 0 [DRM]
Similarly, the NVIDIA proprietary driver also fails after resume (black
screen, 100% CPU usage in Xorg process). We shipped a sample to NVIDIA for
diagnosis, and their response indicated that it's a problem with the parent
PCI bridge (on the Intel SoC), not the GPU.
Runtime suspend/resume works fine, only S3 suspend is affected.
We found a workaround: on resume, rewrite the Intel PCI bridge
'Prefetchable Base Upper 32 Bits' register (PCI_PREF_BASE_UPPER32). In the
cases that I checked, this register has value 0 and we just have to rewrite
that value.
Linux already saves and restores PCI config space during suspend/resume,
but this register was being skipped because upon resume, it already has
value 0 (the correct, pre-suspend value).
Intel appear to have previously acknowledged this behaviour and the
requirement to rewrite this register:
https://bugzilla.kernel.org/show_bug.cgi?id=116851#c23
Based on that, rewrite the prefetch register values even when that appears
unnecessary.
We have confirmed this solution on all the affected models we have in-hands
(X542UQ, UX533FD, X530UN, V272UN).
Additionally, this solves an issue where r8169 MSI-X interrupts were broken
after S3 suspend/resume on ASUS X441UAR. This issue was recently worked
around in commit 7bb05b85bc2d ("r8169: don't use MSI-X on RTL8106e"). It
also fixes the same issue on RTL6186evl/8111evl on an Aimfor-tech laptop
that we had not yet patched. I suspect it will also fix the issue that was
worked around in commit 7c53a722459c ("r8169: don't use MSI-X on
RTL8168g").
Thomas Martitz reports that this change also solves an issue where the AMD
Radeon Polaris 10 GPU on the HP Zbook 14u G5 is unresponsive after S3
suspend/resume.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=201069
Signed-off-by: Daniel Drake <drake@endlessm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-By: Peter Wu <peter@lekensteyn.nl>
CC: stable@vger.kernel.org
-rw-r--r-- | drivers/pci/pci.c | 27 |
1 files changed, 19 insertions, 8 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1835f3a7aa8d..51b6c81671c1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -1289,12 +1289,12 @@ int pci_save_state(struct pci_dev *dev) | |||
1289 | EXPORT_SYMBOL(pci_save_state); | 1289 | EXPORT_SYMBOL(pci_save_state); |
1290 | 1290 | ||
1291 | static void pci_restore_config_dword(struct pci_dev *pdev, int offset, | 1291 | static void pci_restore_config_dword(struct pci_dev *pdev, int offset, |
1292 | u32 saved_val, int retry) | 1292 | u32 saved_val, int retry, bool force) |
1293 | { | 1293 | { |
1294 | u32 val; | 1294 | u32 val; |
1295 | 1295 | ||
1296 | pci_read_config_dword(pdev, offset, &val); | 1296 | pci_read_config_dword(pdev, offset, &val); |
1297 | if (val == saved_val) | 1297 | if (!force && val == saved_val) |
1298 | return; | 1298 | return; |
1299 | 1299 | ||
1300 | for (;;) { | 1300 | for (;;) { |
@@ -1313,25 +1313,36 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset, | |||
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | static void pci_restore_config_space_range(struct pci_dev *pdev, | 1315 | static void pci_restore_config_space_range(struct pci_dev *pdev, |
1316 | int start, int end, int retry) | 1316 | int start, int end, int retry, |
1317 | bool force) | ||
1317 | { | 1318 | { |
1318 | int index; | 1319 | int index; |
1319 | 1320 | ||
1320 | for (index = end; index >= start; index--) | 1321 | for (index = end; index >= start; index--) |
1321 | pci_restore_config_dword(pdev, 4 * index, | 1322 | pci_restore_config_dword(pdev, 4 * index, |
1322 | pdev->saved_config_space[index], | 1323 | pdev->saved_config_space[index], |
1323 | retry); | 1324 | retry, force); |
1324 | } | 1325 | } |
1325 | 1326 | ||
1326 | static void pci_restore_config_space(struct pci_dev *pdev) | 1327 | static void pci_restore_config_space(struct pci_dev *pdev) |
1327 | { | 1328 | { |
1328 | if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) { | 1329 | if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) { |
1329 | pci_restore_config_space_range(pdev, 10, 15, 0); | 1330 | pci_restore_config_space_range(pdev, 10, 15, 0, false); |
1330 | /* Restore BARs before the command register. */ | 1331 | /* Restore BARs before the command register. */ |
1331 | pci_restore_config_space_range(pdev, 4, 9, 10); | 1332 | pci_restore_config_space_range(pdev, 4, 9, 10, false); |
1332 | pci_restore_config_space_range(pdev, 0, 3, 0); | 1333 | pci_restore_config_space_range(pdev, 0, 3, 0, false); |
1334 | } else if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
1335 | pci_restore_config_space_range(pdev, 12, 15, 0, false); | ||
1336 | |||
1337 | /* | ||
1338 | * Force rewriting of prefetch registers to avoid S3 resume | ||
1339 | * issues on Intel PCI bridges that occur when these | ||
1340 | * registers are not explicitly written. | ||
1341 | */ | ||
1342 | pci_restore_config_space_range(pdev, 9, 11, 0, true); | ||
1343 | pci_restore_config_space_range(pdev, 0, 8, 0, false); | ||
1333 | } else { | 1344 | } else { |
1334 | pci_restore_config_space_range(pdev, 0, 15, 0); | 1345 | pci_restore_config_space_range(pdev, 0, 15, 0, false); |
1335 | } | 1346 | } |
1336 | } | 1347 | } |
1337 | 1348 | ||