1 | // SPDX-License-Identifier: ISC |
2 | /* |
3 | * Copyright (C) 2019 Lorenzo Bianconi <lorenzo@kernel.org> |
4 | */ |
5 | |
6 | #include "mt76.h" |
7 | #include <linux/pci.h> |
8 | |
9 | void mt76_pci_disable_aspm(struct pci_dev *pdev) |
10 | { |
11 | struct pci_dev *parent = pdev->bus->self; |
12 | u16 aspm_conf, parent_aspm_conf = 0; |
13 | |
14 | pcie_capability_read_word(dev: pdev, PCI_EXP_LNKCTL, val: &aspm_conf); |
15 | aspm_conf &= PCI_EXP_LNKCTL_ASPMC; |
16 | if (parent) { |
17 | pcie_capability_read_word(dev: parent, PCI_EXP_LNKCTL, |
18 | val: &parent_aspm_conf); |
19 | parent_aspm_conf &= PCI_EXP_LNKCTL_ASPMC; |
20 | } |
21 | |
22 | if (!aspm_conf && (!parent || !parent_aspm_conf)) { |
23 | /* aspm already disabled */ |
24 | return; |
25 | } |
26 | |
27 | dev_info(&pdev->dev, "disabling ASPM %s %s\n" , |
28 | (aspm_conf & PCI_EXP_LNKCTL_ASPM_L0S) ? "L0s" : "" , |
29 | (aspm_conf & PCI_EXP_LNKCTL_ASPM_L1) ? "L1" : "" ); |
30 | |
31 | if (IS_ENABLED(CONFIG_PCIEASPM)) { |
32 | int err; |
33 | |
34 | err = pci_disable_link_state(pdev, state: aspm_conf); |
35 | if (!err) |
36 | return; |
37 | } |
38 | |
39 | /* both device and parent should have the same ASPM setting. |
40 | * disable ASPM in downstream component first and then upstream. |
41 | */ |
42 | pcie_capability_clear_word(dev: pdev, PCI_EXP_LNKCTL, clear: aspm_conf); |
43 | if (parent) |
44 | pcie_capability_clear_word(dev: parent, PCI_EXP_LNKCTL, |
45 | clear: aspm_conf); |
46 | } |
47 | EXPORT_SYMBOL_GPL(mt76_pci_disable_aspm); |
48 | |