diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 352 |
1 files changed, 38 insertions, 314 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 75388584e60d9..da7560b29da97 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1067,6 +1067,34 @@ disable_acs_redir: } /** + * pcie_read_tlp_log - read TLP Header Log + * @dev: PCIe device + * @where: PCI Config offset of TLP Header Log + * @tlp_log: TLP Log structure to fill + * + * Fill @tlp_log from TLP Header Log registers, e.g., AER or DPC. + * + * Return: 0 on success and filled TLP Log structure, <0 on error. + */ +int pcie_read_tlp_log(struct pci_dev *dev, int where, + struct pcie_tlp_log *tlp_log) +{ + int i, ret; + + memset(tlp_log, 0, sizeof(*tlp_log)); + + for (i = 0; i < 4; i++) { + ret = pci_read_config_dword(dev, where + i * 4, + &tlp_log->dw[i]); + if (ret) + return pcibios_err_to_errno(ret); + } + + return 0; +} +EXPORT_SYMBOL_GPL(pcie_read_tlp_log); + +/** * pci_restore_bars - restore a device's BAR values (e.g. after wake-up) * @dev: PCI device to have its BARs restored * @@ -1622,25 +1650,10 @@ static int pci_save_pcie_state(struct pci_dev *dev) pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]); pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]); - return 0; -} - -void pci_bridge_reconfigure_ltr(struct pci_dev *dev) -{ -#ifdef CONFIG_PCIEASPM - struct pci_dev *bridge; - u32 ctl; + pci_save_aspm_l1ss_state(dev); + pci_save_ltr_state(dev); - bridge = pci_upstream_bridge(dev); - if (bridge && bridge->ltr_path) { - pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2, &ctl); - if (!(ctl & PCI_EXP_DEVCTL2_LTR_EN)) { - pci_dbg(bridge, "re-enabling LTR\n"); - pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_LTR_EN); - } - } -#endif + return 0; } static void pci_restore_pcie_state(struct pci_dev *dev) @@ -1649,6 +1662,13 @@ static void pci_restore_pcie_state(struct pci_dev *dev) struct pci_cap_saved_state *save_state; u16 *cap; + /* + * Restore max latencies (in the LTR capability) before enabling + * LTR itself in PCI_EXP_DEVCTL2. + */ + pci_restore_ltr_state(dev); + pci_restore_aspm_l1ss_state(dev); + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); if (!save_state) return; @@ -1706,46 +1726,6 @@ static void pci_restore_pcix_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]); } -static void pci_save_ltr_state(struct pci_dev *dev) -{ - int ltr; - struct pci_cap_saved_state *save_state; - u32 *cap; - - if (!pci_is_pcie(dev)) - return; - - ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); - if (!ltr) - return; - - save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); - if (!save_state) { - pci_err(dev, "no suspend buffer for LTR; ASPM issues possible after resume\n"); - return; - } - - /* Some broken devices only support dword access to LTR */ - cap = &save_state->cap.data[0]; - pci_read_config_dword(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, cap); -} - -static void pci_restore_ltr_state(struct pci_dev *dev) -{ - struct pci_cap_saved_state *save_state; - int ltr; - u32 *cap; - - save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_LTR); - ltr = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); - if (!save_state || !ltr) - return; - - /* Some broken devices only support dword access to LTR */ - cap = &save_state->cap.data[0]; - pci_write_config_dword(dev, ltr + PCI_LTR_MAX_SNOOP_LAT, *cap); -} - /** * pci_save_state - save the PCI configuration space of a device before * suspending @@ -1770,7 +1750,6 @@ int pci_save_state(struct pci_dev *dev) if (i != 0) return i; - pci_save_ltr_state(dev); pci_save_dpc_state(dev); pci_save_aer_state(dev); pci_save_ptm_state(dev); @@ -1871,12 +1850,6 @@ void pci_restore_state(struct pci_dev *dev) if (!dev->state_saved) return; - /* - * Restore max latencies (in the LTR capability) before enabling - * LTR itself (in the PCIe capability). - */ - pci_restore_ltr_state(dev); - pci_restore_pcie_state(dev); pci_restore_pasid_state(dev); pci_restore_pri_state(dev); @@ -2157,107 +2130,6 @@ int pci_enable_device(struct pci_dev *dev) EXPORT_SYMBOL(pci_enable_device); /* - * Managed PCI resources. This manages device on/off, INTx/MSI/MSI-X - * on/off and BAR regions. pci_dev itself records MSI/MSI-X status, so - * there's no need to track it separately. pci_devres is initialized - * when a device is enabled using managed PCI device enable interface. - */ -struct pci_devres { - unsigned int enabled:1; - unsigned int pinned:1; - unsigned int orig_intx:1; - unsigned int restore_intx:1; - unsigned int mwi:1; - u32 region_mask; -}; - -static void pcim_release(struct device *gendev, void *res) -{ - struct pci_dev *dev = to_pci_dev(gendev); - struct pci_devres *this = res; - int i; - - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) - if (this->region_mask & (1 << i)) - pci_release_region(dev, i); - - if (this->mwi) - pci_clear_mwi(dev); - - if (this->restore_intx) - pci_intx(dev, this->orig_intx); - - if (this->enabled && !this->pinned) - pci_disable_device(dev); -} - -static struct pci_devres *get_pci_dr(struct pci_dev *pdev) -{ - struct pci_devres *dr, *new_dr; - - dr = devres_find(&pdev->dev, pcim_release, NULL, NULL); - if (dr) - return dr; - - new_dr = devres_alloc(pcim_release, sizeof(*new_dr), GFP_KERNEL); - if (!new_dr) - return NULL; - return devres_get(&pdev->dev, new_dr, NULL, NULL); -} - -static struct pci_devres *find_pci_dr(struct pci_dev *pdev) -{ - if (pci_is_managed(pdev)) - return devres_find(&pdev->dev, pcim_release, NULL, NULL); - return NULL; -} - -/** - * pcim_enable_device - Managed pci_enable_device() - * @pdev: PCI device to be initialized - * - * Managed pci_enable_device(). - */ -int pcim_enable_device(struct pci_dev *pdev) -{ - struct pci_devres *dr; - int rc; - - dr = get_pci_dr(pdev); - if (unlikely(!dr)) - return -ENOMEM; - if (dr->enabled) - return 0; - - rc = pci_enable_device(pdev); - if (!rc) { - pdev->is_managed = 1; - dr->enabled = 1; - } - return rc; -} -EXPORT_SYMBOL(pcim_enable_device); - -/** - * pcim_pin_device - Pin managed PCI device - * @pdev: PCI device to pin - * - * Pin managed PCI device @pdev. Pinned device won't be disabled on - * driver detach. @pdev must have been enabled with - * pcim_enable_device(). - */ -void pcim_pin_device(struct pci_dev *pdev) -{ - struct pci_devres *dr; - - dr = find_pci_dr(pdev); - WARN_ON(!dr || !dr->enabled); - if (dr) - dr->pinned = 1; -} -EXPORT_SYMBOL(pcim_pin_device); - -/* * pcibios_device_add - provide arch specific hooks when adding device dev * @dev: the PCI device being added * @@ -4280,133 +4152,6 @@ void pci_unmap_iospace(struct resource *res) } EXPORT_SYMBOL(pci_unmap_iospace); -static void devm_pci_unmap_iospace(struct device *dev, void *ptr) -{ - struct resource **res = ptr; - - pci_unmap_iospace(*res); -} - -/** - * devm_pci_remap_iospace - Managed pci_remap_iospace() - * @dev: Generic device to remap IO address for - * @res: Resource describing the I/O space - * @phys_addr: physical address of range to be mapped - * - * Managed pci_remap_iospace(). Map is automatically unmapped on driver - * detach. - */ -int devm_pci_remap_iospace(struct device *dev, const struct resource *res, - phys_addr_t phys_addr) -{ - const struct resource **ptr; - int error; - - ptr = devres_alloc(devm_pci_unmap_iospace, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - error = pci_remap_iospace(res, phys_addr); - if (error) { - devres_free(ptr); - } else { - *ptr = res; - devres_add(dev, ptr); - } - - return error; -} -EXPORT_SYMBOL(devm_pci_remap_iospace); - -/** - * devm_pci_remap_cfgspace - Managed pci_remap_cfgspace() - * @dev: Generic device to remap IO address for - * @offset: Resource address to map - * @size: Size of map - * - * Managed pci_remap_cfgspace(). Map is automatically unmapped on driver - * detach. - */ -void __iomem *devm_pci_remap_cfgspace(struct device *dev, - resource_size_t offset, - resource_size_t size) -{ - void __iomem **ptr, *addr; - - ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - addr = pci_remap_cfgspace(offset, size); - if (addr) { - *ptr = addr; - devres_add(dev, ptr); - } else - devres_free(ptr); - - return addr; -} -EXPORT_SYMBOL(devm_pci_remap_cfgspace); - -/** - * devm_pci_remap_cfg_resource - check, request region and ioremap cfg resource - * @dev: generic device to handle the resource for - * @res: configuration space resource to be handled - * - * Checks that a resource is a valid memory region, requests the memory - * region and ioremaps with pci_remap_cfgspace() API that ensures the - * proper PCI configuration space memory attributes are guaranteed. - * - * All operations are managed and will be undone on driver detach. - * - * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code - * on failure. Usage example:: - * - * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - * base = devm_pci_remap_cfg_resource(&pdev->dev, res); - * if (IS_ERR(base)) - * return PTR_ERR(base); - */ -void __iomem *devm_pci_remap_cfg_resource(struct device *dev, - struct resource *res) -{ - resource_size_t size; - const char *name; - void __iomem *dest_ptr; - - BUG_ON(!dev); - - if (!res || resource_type(res) != IORESOURCE_MEM) { - dev_err(dev, "invalid resource\n"); - return IOMEM_ERR_PTR(-EINVAL); - } - - size = resource_size(res); - - if (res->name) - name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", dev_name(dev), - res->name); - else - name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); - if (!name) - return IOMEM_ERR_PTR(-ENOMEM); - - if (!devm_request_mem_region(dev, res->start, size, name)) { - dev_err(dev, "can't request region for resource %pR\n", res); - return IOMEM_ERR_PTR(-EBUSY); - } - - dest_ptr = devm_pci_remap_cfgspace(dev, res->start, size); - if (!dest_ptr) { - dev_err(dev, "ioremap failed for resource %pR\n", res); - devm_release_mem_region(dev, res->start, size); - dest_ptr = IOMEM_ERR_PTR(-ENOMEM); - } - - return dest_ptr; -} -EXPORT_SYMBOL(devm_pci_remap_cfg_resource); - static void __pci_set_master(struct pci_dev *dev, bool enable) { u16 old_cmd, cmd; @@ -4557,27 +4302,6 @@ int pci_set_mwi(struct pci_dev *dev) EXPORT_SYMBOL(pci_set_mwi); /** - * pcim_set_mwi - a device-managed pci_set_mwi() - * @dev: the PCI device for which MWI is enabled - * - * Managed pci_set_mwi(). - * - * RETURNS: An appropriate -ERRNO error value on error, or zero for success. - */ -int pcim_set_mwi(struct pci_dev *dev) -{ - struct pci_devres *dr; - - dr = find_pci_dr(dev); - if (!dr) - return -ENOMEM; - - dr->mwi = 1; - return pci_set_mwi(dev); -} -EXPORT_SYMBOL(pcim_set_mwi); - -/** * pci_try_set_mwi - enables memory-write-invalidate PCI transaction * @dev: the PCI device for which MWI is enabled * |