aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/probe.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2019-05-13 18:34:31 -0500
committerBjorn Helgaas <bhelgaas@google.com>2019-05-13 18:34:31 -0500
commit3ea6f739dca4370f89d8c171b4b0433046256798 (patch)
treeb404bfd66121cfebcfbe57403e59c0d59c706954 /drivers/pci/probe.c
parentMerge branch 'pci/enumeration' (diff)
parentPCI/ACPI: Advertise _HPX Type 3 support via _OSC (diff)
downloadlinux-dev-3ea6f739dca4370f89d8c171b4b0433046256798.tar.xz
linux-dev-3ea6f739dca4370f89d8c171b4b0433046256798.zip
Merge branch 'pci/hotplug'
- Fix RPA and RPA DLPAR refcount issues (Tyrel Datwyler) - Stop exporting pci_get_hp_params() (Alexandru Gagniuc) - Simplify _HPP, _HPX parsing (Alexandru Gagniuc) - Add support for _HPX Type 3 settings (Alexandru Gagniuc) - Tell firmware we support _HPX Type 3 via _OSC (Alexandru Gagniuc) * pci/hotplug: PCI/ACPI: Advertise _HPX Type 3 support via _OSC PCI/ACPI: Implement _HPX Type 3 Setting Record PCI/ACPI: Remove the need for 'struct hotplug_params' PCI/ACPI: Do not export pci_get_hp_params() PCI: rpaphp: Get/put device node reference during slot alloc/dealloc PCI: rpadlpar: Fix leaked device_node references in add/remove paths
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r--drivers/pci/probe.c130
1 files changed, 120 insertions, 10 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index a6874c306908..50cd9c17c08f 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2077,6 +2077,119 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
*/
}
+static u16 hpx3_device_type(struct pci_dev *dev)
+{
+ u16 pcie_type = pci_pcie_type(dev);
+ const int pcie_to_hpx3_type[] = {
+ [PCI_EXP_TYPE_ENDPOINT] = HPX_TYPE_ENDPOINT,
+ [PCI_EXP_TYPE_LEG_END] = HPX_TYPE_LEG_END,
+ [PCI_EXP_TYPE_RC_END] = HPX_TYPE_RC_END,
+ [PCI_EXP_TYPE_RC_EC] = HPX_TYPE_RC_EC,
+ [PCI_EXP_TYPE_ROOT_PORT] = HPX_TYPE_ROOT_PORT,
+ [PCI_EXP_TYPE_UPSTREAM] = HPX_TYPE_UPSTREAM,
+ [PCI_EXP_TYPE_DOWNSTREAM] = HPX_TYPE_DOWNSTREAM,
+ [PCI_EXP_TYPE_PCI_BRIDGE] = HPX_TYPE_PCI_BRIDGE,
+ [PCI_EXP_TYPE_PCIE_BRIDGE] = HPX_TYPE_PCIE_BRIDGE,
+ };
+
+ if (pcie_type >= ARRAY_SIZE(pcie_to_hpx3_type))
+ return 0;
+
+ return pcie_to_hpx3_type[pcie_type];
+}
+
+static u8 hpx3_function_type(struct pci_dev *dev)
+{
+ if (dev->is_virtfn)
+ return HPX_FN_SRIOV_VIRT;
+ else if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV) > 0)
+ return HPX_FN_SRIOV_PHYS;
+ else
+ return HPX_FN_NORMAL;
+}
+
+static bool hpx3_cap_ver_matches(u8 pcie_cap_id, u8 hpx3_cap_id)
+{
+ u8 cap_ver = hpx3_cap_id & 0xf;
+
+ if ((hpx3_cap_id & BIT(4)) && cap_ver >= pcie_cap_id)
+ return true;
+ else if (cap_ver == pcie_cap_id)
+ return true;
+
+ return false;
+}
+
+static void program_hpx_type3_register(struct pci_dev *dev,
+ const struct hpx_type3 *reg)
+{
+ u32 match_reg, write_reg, header, orig_value;
+ u16 pos;
+
+ if (!(hpx3_device_type(dev) & reg->device_type))
+ return;
+
+ if (!(hpx3_function_type(dev) & reg->function_type))
+ return;
+
+ switch (reg->config_space_location) {
+ case HPX_CFG_PCICFG:
+ pos = 0;
+ break;
+ case HPX_CFG_PCIE_CAP:
+ pos = pci_find_capability(dev, reg->pci_exp_cap_id);
+ if (pos == 0)
+ return;
+
+ break;
+ case HPX_CFG_PCIE_CAP_EXT:
+ pos = pci_find_ext_capability(dev, reg->pci_exp_cap_id);
+ if (pos == 0)
+ return;
+
+ pci_read_config_dword(dev, pos, &header);
+ if (!hpx3_cap_ver_matches(PCI_EXT_CAP_VER(header),
+ reg->pci_exp_cap_ver))
+ return;
+
+ break;
+ case HPX_CFG_VEND_CAP: /* Fall through */
+ case HPX_CFG_DVSEC: /* Fall through */
+ default:
+ pci_warn(dev, "Encountered _HPX type 3 with unsupported config space location");
+ return;
+ }
+
+ pci_read_config_dword(dev, pos + reg->match_offset, &match_reg);
+
+ if ((match_reg & reg->match_mask_and) != reg->match_value)
+ return;
+
+ pci_read_config_dword(dev, pos + reg->reg_offset, &write_reg);
+ orig_value = write_reg;
+ write_reg &= reg->reg_mask_and;
+ write_reg |= reg->reg_mask_or;
+
+ if (orig_value == write_reg)
+ return;
+
+ pci_write_config_dword(dev, pos + reg->reg_offset, write_reg);
+
+ pci_dbg(dev, "Applied _HPX3 at [0x%x]: 0x%08x -> 0x%08x",
+ pos, orig_value, write_reg);
+}
+
+static void program_hpx_type3(struct pci_dev *dev, struct hpx_type3 *hpx3)
+{
+ if (!hpx3)
+ return;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ program_hpx_type3_register(dev, hpx3);
+}
+
int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
{
struct pci_host_bridge *host;
@@ -2257,8 +2370,12 @@ static void pci_configure_serr(struct pci_dev *dev)
static void pci_configure_device(struct pci_dev *dev)
{
- struct hotplug_params hpp;
- int ret;
+ static const struct hotplug_program_ops hp_ops = {
+ .program_type0 = program_hpp_type0,
+ .program_type1 = program_hpp_type1,
+ .program_type2 = program_hpp_type2,
+ .program_type3 = program_hpx_type3,
+ };
pci_configure_mps(dev);
pci_configure_extended_tags(dev, NULL);
@@ -2267,14 +2384,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_eetlp_prefix(dev);
pci_configure_serr(dev);
- memset(&hpp, 0, sizeof(hpp));
- ret = pci_get_hp_params(dev, &hpp);
- if (ret)
- return;
-
- program_hpp_type2(dev, hpp.t2);
- program_hpp_type1(dev, hpp.t1);
- program_hpp_type0(dev, hpp.t0);
+ pci_acpi_program_hp_params(dev, &hp_ops);
}
static void pci_release_capabilities(struct pci_dev *dev)