aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2017-06-06 08:59:23 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-06-06 20:49:05 +1000
commit9eb2ab74905ff59dd0de5fb3ece6b910863e8a31 (patch)
treee9c33dae3bb3bcdda24a5fd0eeef6d67e859bddc
parent68d9e87c11b589d709bb86786703ccb5c68bd8f8 (diff)
downloadskiboot-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.c10
-rw-r--r--hw/phb4.c34
-rw-r--r--include/pci-cfg.h3
3 files changed, 43 insertions, 4 deletions
diff --git a/core/pci.c b/core/pci.c
index c791ebd..1477358 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -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,
diff --git a/hw/phb4.c b/hw/phb4.c
index bf664af..b53dadf 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -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