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