diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2017-06-06 08:59:23 +1000 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2017-06-06 20:49:05 +1000 |
commit | 9eb2ab74905ff59dd0de5fb3ece6b910863e8a31 (patch) | |
tree | e9c33dae3bb3bcdda24a5fd0eeef6d67e859bddc | |
parent | 68d9e87c11b589d709bb86786703ccb5c68bd8f8 (diff) | |
download | skiboot-9eb2ab74905ff59dd0de5fb3ece6b910863e8a31.zip skiboot-9eb2ab74905ff59dd0de5fb3ece6b910863e8a31.tar.gz skiboot-9eb2ab74905ff59dd0de5fb3ece6b910863e8a31.tar.bz2 |
phb4: Block D-state power management on direct slots
As current revisions of PHB4 don't properly handle the resulting
L1 link transition.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
-rw-r--r-- | core/pci.c | 10 | ||||
-rw-r--r-- | hw/phb4.c | 34 | ||||
-rw-r--r-- | include/pci-cfg.h | 3 |
3 files changed, 43 insertions, 4 deletions
@@ -202,11 +202,21 @@ static void pci_init_aer_cap(struct phb *phb, struct pci_device *pd) pci_set_cap(pd, PCIECAP_ID_AER, pos, NULL, true); } +static void pci_init_pm_cap(struct phb *phb, struct pci_device *pd) +{ + int64_t pos; + + pos = pci_find_cap(phb, pd->bdfn, PCI_CFG_CAP_ID_PM); + if (pos > 0) + pci_set_cap(pd, PCI_CFG_CAP_ID_PM, pos, NULL, false); +} + void pci_init_capabilities(struct phb *phb, struct pci_device *pd) { pci_init_pcie_cap(phb, pd); pci_init_aer_cap(phb, pd); pci_init_iov_cap(phb, pd); + pci_init_pm_cap(phb, pd); } static struct pci_device *pci_scan_one(struct phb *phb, struct pci_device *parent, @@ -644,9 +644,36 @@ static void phb4_endpoint_init(struct phb *phb, pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_CAPCTL, val32); } +static int64_t phb4_pcicfg_no_dstate(void *dev, + struct pci_cfg_reg_filter *pcrf, + uint32_t offset, uint32_t len, + uint32_t *data, bool write) +{ + uint32_t loff = offset - pcrf->start; + + /* Disable D-state change on children of the PHB. For now we + * simply block all writes to the PM control/status + */ + if (write && loff >= 4 && loff < 6) + return OPAL_SUCCESS; + + return OPAL_PARTIAL; +} + static void phb4_check_device_quirks(struct phb *phb, struct pci_device *dev) { - // FIXME: add quirks later if necessary + /* Some special adapter tweaks for devices directly under the PHB */ + if (dev->primary_bus != 1) + return; + + /* PM quirk */ + if (!pci_has_cap(dev, PCI_CFG_CAP_ID_PM, false)) + return; + + pci_add_cfg_reg_filter(dev, + pci_cap(dev, PCI_CFG_CAP_ID_PM, false), 8, + PCI_REG_FLAG_WRITE, + phb4_pcicfg_no_dstate); } static int phb4_device_init(struct phb *phb, struct pci_device *dev, @@ -654,9 +681,8 @@ static int phb4_device_init(struct phb *phb, struct pci_device *dev, { int ecap, aercap; - /* Some special adapter tweaks for devices directly under the PHB */ - if (dev->primary_bus == 1) - phb4_check_device_quirks(phb, dev); + /* Setup special device quirks */ + phb4_check_device_quirks(phb, dev); /* Common initialization for the device */ pci_device_init(phb, dev); diff --git a/include/pci-cfg.h b/include/pci-cfg.h index 530f0a8..6061584 100644 --- a/include/pci-cfg.h +++ b/include/pci-cfg.h @@ -108,6 +108,9 @@ #define PCI_CFG_CAP_ID 0 #define PCI_CFG_CAP_NEXT 1 +/* PCI Power Management capability */ +#define PCI_CFG_CAP_ID_PM 1 + /* PCI bridge subsystem ID capability */ #define PCI_CFG_CAP_ID_SUBSYS_VID 0x0d #define PCICAP_SUBSYS_VID_VENDOR 4 |