]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/pci/pci.c
mmc: sdhci-xenon: kill xenon_clean_phy()
[karo-tx-linux.git] / drivers / pci / pci.c
index 7904d02ffdb97e5f23d915e9c83edbf599ddf58b..b01bd5bba8e604709c3b51f14f9fbdb796a2c4fb 100644 (file)
@@ -66,7 +66,8 @@ static void pci_dev_d3_sleep(struct pci_dev *dev)
        if (delay < pci_pm_d3_delay)
                delay = pci_pm_d3_delay;
 
-       msleep(delay);
+       if (delay)
+               msleep(delay);
 }
 
 #ifdef CONFIG_PCI_DOMAINS
@@ -827,7 +828,8 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state)
                 * because have already delayed for the bridge.
                 */
                if (dev->runtime_d3cold) {
-                       msleep(dev->d3cold_delay);
+                       if (dev->d3cold_delay)
+                               msleep(dev->d3cold_delay);
                        /*
                         * When powering on a bridge from D3cold, the
                         * whole hierarchy may be powered on into
@@ -1782,8 +1784,8 @@ static void pci_pme_list_scan(struct work_struct *work)
                }
        }
        if (!list_empty(&pci_pme_list))
-               schedule_delayed_work(&pci_pme_work,
-                                     msecs_to_jiffies(PME_TIMEOUT));
+               queue_delayed_work(system_freezable_wq, &pci_pme_work,
+                                  msecs_to_jiffies(PME_TIMEOUT));
        mutex_unlock(&pci_pme_list_mutex);
 }
 
@@ -1848,8 +1850,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
                        mutex_lock(&pci_pme_list_mutex);
                        list_add(&pme_dev->list, &pci_pme_list);
                        if (list_is_singular(&pci_pme_list))
-                               schedule_delayed_work(&pci_pme_work,
-                                                     msecs_to_jiffies(PME_TIMEOUT));
+                               queue_delayed_work(system_freezable_wq,
+                                                  &pci_pme_work,
+                                                  msecs_to_jiffies(PME_TIMEOUT));
                        mutex_unlock(&pci_pme_list_mutex);
                } else {
                        mutex_lock(&pci_pme_list_mutex);
@@ -3363,7 +3366,7 @@ unsigned long __weak pci_address_to_pio(phys_addr_t address)
  *     Only architectures that have memory mapped IO functions defined
  *     (and the PCI_IOBASE value defined) should call this function.
  */
-int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
+int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
 {
 #if defined(PCI_IOBASE) && defined(CONFIG_MMU)
        unsigned long vaddr = (unsigned long)PCI_IOBASE + res->start;
@@ -3383,6 +3386,7 @@ int __weak pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)
        return -ENODEV;
 #endif
 }
+EXPORT_SYMBOL(pci_remap_iospace);
 
 /**
  *     pci_unmap_iospace - Unmap the memory mapped I/O space
@@ -3400,6 +3404,89 @@ void pci_unmap_iospace(struct resource *res)
        unmap_kernel_range(vaddr, resource_size(res));
 #endif
 }
+EXPORT_SYMBOL(pci_unmap_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);
+       name = res->name ?: dev_name(dev);
+
+       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)
 {
@@ -3773,24 +3860,41 @@ static void pci_flr_wait(struct pci_dev *dev)
                         (i - 1) * 100);
 }
 
-static int pcie_flr(struct pci_dev *dev, int probe)
+/**
+ * pcie_has_flr - check if a device supports function level resets
+ * @dev:       device to check
+ *
+ * Returns true if the device advertises support for PCIe function level
+ * resets.
+ */
+static bool pcie_has_flr(struct pci_dev *dev)
 {
        u32 cap;
 
-       pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
-       if (!(cap & PCI_EXP_DEVCAP_FLR))
-               return -ENOTTY;
+       if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
+               return false;
 
-       if (probe)
-               return 0;
+       pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
+       return cap & PCI_EXP_DEVCAP_FLR;
+}
 
+/**
+ * pcie_flr - initiate a PCIe function level reset
+ * @dev:       device to reset
+ *
+ * Initiate a function level reset on @dev.  The caller should ensure the
+ * device supports FLR before calling this function, e.g. by using the
+ * pcie_has_flr() helper.
+ */
+void pcie_flr(struct pci_dev *dev)
+{
        if (!pci_wait_for_pending_transaction(dev))
                dev_err(&dev->dev, "timed out waiting for pending transaction; performing function level reset anyway\n");
 
        pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
        pci_flr_wait(dev);
-       return 0;
 }
+EXPORT_SYMBOL_GPL(pcie_flr);
 
 static int pci_af_flr(struct pci_dev *dev, int probe)
 {
@@ -3801,6 +3905,9 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
        if (!pos)
                return -ENOTTY;
 
+       if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
+               return -ENOTTY;
+
        pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap);
        if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR))
                return -ENOTTY;
@@ -3971,9 +4078,12 @@ static int __pci_dev_reset(struct pci_dev *dev, int probe)
        if (rc != -ENOTTY)
                goto done;
 
-       rc = pcie_flr(dev, probe);
-       if (rc != -ENOTTY)
+       if (pcie_has_flr(dev)) {
+               if (!probe)
+                       pcie_flr(dev);
+               rc = 0;
                goto done;
+       }
 
        rc = pci_af_flr(dev, probe);
        if (rc != -ENOTTY)
@@ -4932,6 +5042,8 @@ bool pci_device_is_present(struct pci_dev *pdev)
 {
        u32 v;
 
+       if (pci_dev_is_disconnected(pdev))
+               return false;
        return pci_bus_read_dev_vendor_id(pdev->bus, pdev->devfn, &v, 0);
 }
 EXPORT_SYMBOL_GPL(pci_device_is_present);
@@ -4947,6 +5059,11 @@ void pci_ignore_hotplug(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_ignore_hotplug);
 
+resource_size_t __weak pcibios_default_alignment(void)
+{
+       return 0;
+}
+
 #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
 static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
 static DEFINE_SPINLOCK(resource_alignment_lock);
@@ -4954,22 +5071,25 @@ static DEFINE_SPINLOCK(resource_alignment_lock);
 /**
  * pci_specified_resource_alignment - get resource alignment specified by user.
  * @dev: the PCI device to get
+ * @resize: whether or not to change resources' size when reassigning alignment
  *
  * RETURNS: Resource alignment if it is specified.
  *          Zero if it is not specified.
  */
-static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
+static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev,
+                                                       bool *resize)
 {
        int seg, bus, slot, func, align_order, count;
        unsigned short vendor, device, subsystem_vendor, subsystem_device;
-       resource_size_t align = 0;
+       resource_size_t align = pcibios_default_alignment();
        char *p;
 
        spin_lock(&resource_alignment_lock);
        p = resource_alignment_param;
-       if (!*p)
+       if (!*p && !align)
                goto out;
        if (pci_has_flag(PCI_PROBE_ONLY)) {
+               align = 0;
                pr_info_once("PCI: Ignoring requested alignments (PCI_PROBE_ONLY)\n");
                goto out;
        }
@@ -4999,6 +5119,7 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
                                (!device || (device == dev->device)) &&
                                (!subsystem_vendor || (subsystem_vendor == dev->subsystem_vendor)) &&
                                (!subsystem_device || (subsystem_device == dev->subsystem_device))) {
+                               *resize = true;
                                if (align_order == -1)
                                        align = PAGE_SIZE;
                                else
@@ -5024,6 +5145,7 @@ static resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
                                bus == dev->bus->number &&
                                slot == PCI_SLOT(dev->devfn) &&
                                func == PCI_FUNC(dev->devfn)) {
+                               *resize = true;
                                if (align_order == -1)
                                        align = PAGE_SIZE;
                                else
@@ -5043,6 +5165,68 @@ out:
        return align;
 }
 
+static void pci_request_resource_alignment(struct pci_dev *dev, int bar,
+                                          resource_size_t align, bool resize)
+{
+       struct resource *r = &dev->resource[bar];
+       resource_size_t size;
+
+       if (!(r->flags & IORESOURCE_MEM))
+               return;
+
+       if (r->flags & IORESOURCE_PCI_FIXED) {
+               dev_info(&dev->dev, "BAR%d %pR: ignoring requested alignment %#llx\n",
+                        bar, r, (unsigned long long)align);
+               return;
+       }
+
+       size = resource_size(r);
+       if (size >= align)
+               return;
+
+       /*
+        * Increase the alignment of the resource.  There are two ways we
+        * can do this:
+        *
+        * 1) Increase the size of the resource.  BARs are aligned on their
+        *    size, so when we reallocate space for this resource, we'll
+        *    allocate it with the larger alignment.  This also prevents
+        *    assignment of any other BARs inside the alignment region, so
+        *    if we're requesting page alignment, this means no other BARs
+        *    will share the page.
+        *
+        *    The disadvantage is that this makes the resource larger than
+        *    the hardware BAR, which may break drivers that compute things
+        *    based on the resource size, e.g., to find registers at a
+        *    fixed offset before the end of the BAR.
+        *
+        * 2) Retain the resource size, but use IORESOURCE_STARTALIGN and
+        *    set r->start to the desired alignment.  By itself this
+        *    doesn't prevent other BARs being put inside the alignment
+        *    region, but if we realign *every* resource of every device in
+        *    the system, none of them will share an alignment region.
+        *
+        * When the user has requested alignment for only some devices via
+        * the "pci=resource_alignment" argument, "resize" is true and we
+        * use the first method.  Otherwise we assume we're aligning all
+        * devices and we use the second.
+        */
+
+       dev_info(&dev->dev, "BAR%d %pR: requesting alignment to %#llx\n",
+                bar, r, (unsigned long long)align);
+
+       if (resize) {
+               r->start = 0;
+               r->end = align - 1;
+       } else {
+               r->flags &= ~IORESOURCE_SIZEALIGN;
+               r->flags |= IORESOURCE_STARTALIGN;
+               r->start = align;
+               r->end = r->start + size - 1;
+       }
+       r->flags |= IORESOURCE_UNSET;
+}
+
 /*
  * This function disables memory decoding and releases memory resources
  * of the device specified by kernel's boot parameter 'pci=resource_alignment='.
@@ -5054,8 +5238,9 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
 {
        int i;
        struct resource *r;
-       resource_size_t align, size;
+       resource_size_t align;
        u16 command;
+       bool resize = false;
 
        /*
         * VF BARs are read-only zero according to SR-IOV spec r1.1, sec
@@ -5067,7 +5252,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
                return;
 
        /* check if specified PCI is target device to reassign */
-       align = pci_specified_resource_alignment(dev);
+       align = pci_specified_resource_alignment(dev, &resize);
        if (!align)
                return;
 
@@ -5084,28 +5269,11 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev)
        command &= ~PCI_COMMAND_MEMORY;
        pci_write_config_word(dev, PCI_COMMAND, command);
 
-       for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
-               r = &dev->resource[i];
-               if (!(r->flags & IORESOURCE_MEM))
-                       continue;
-               if (r->flags & IORESOURCE_PCI_FIXED) {
-                       dev_info(&dev->dev, "Ignoring requested alignment for BAR%d: %pR\n",
-                               i, r);
-                       continue;
-               }
+       for (i = 0; i <= PCI_ROM_RESOURCE; i++)
+               pci_request_resource_alignment(dev, i, align, resize);
 
-               size = resource_size(r);
-               if (size < align) {
-                       size = align;
-                       dev_info(&dev->dev,
-                               "Rounding up size of resource #%d to %#llx.\n",
-                               i, (unsigned long long)size);
-               }
-               r->flags |= IORESOURCE_UNSET;
-               r->end = size - 1;
-               r->start = 0;
-       }
-       /* Need to disable bridge's resource window,
+       /*
+        * Need to disable bridge's resource window,
         * to enable the kernel to reassign new resource
         * window later on.
         */