Merge branch 'pci/vpd'
authorBjorn Helgaas <bhelgaas@google.com>
Wed, 4 Apr 2018 18:28:40 +0000 (13:28 -0500)
committerBjorn Helgaas <helgaas@kernel.org>
Wed, 4 Apr 2018 18:28:40 +0000 (13:28 -0500)
  - consolidate VPD code in vpd.c (Bjorn Helgaas)

* pci/vpd:
  PCI/VPD: Move VPD structures to vpd.c
  PCI/VPD: Move VPD quirks to vpd.c
  PCI/VPD: Move VPD sysfs code to vpd.c
  PCI/VPD: Move VPD access code to vpd.c

83 files changed:
Documentation/admin-guide/kernel-parameters.txt
Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt [new file with mode: 0644]
MAINTAINERS
arch/ia64/pci/pci.c
arch/powerpc/include/asm/pci.h
arch/powerpc/kernel/pci-common.c
arch/sparc/kernel/pci.c
arch/sparc/kernel/pci_common.c
arch/sparc/kernel/pci_impl.h
arch/xtensa/include/asm/pci.h
arch/xtensa/kernel/pci.c
drivers/acpi/pci_root.c
drivers/acpi/scan.c
drivers/bus/Kconfig
drivers/bus/Makefile
drivers/bus/hisi_lpc.c [new file with mode: 0644]
drivers/char/xillybus/xillybus_pcie.c
drivers/fpga/altera-cvp.c
drivers/gpu/drm/i915/i915_drv.c
drivers/mcb/mcb-pci.c
drivers/net/ethernet/intel/fm10k/fm10k_pci.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/of/address.c
drivers/pci/Makefile
drivers/pci/access.c
drivers/pci/ats.c
drivers/pci/bus.c
drivers/pci/host-bridge.c
drivers/pci/hotplug/acpiphp_glue.c
drivers/pci/hotplug/cpqphp_ctrl.c
drivers/pci/hotplug/pciehp.h
drivers/pci/iov.c
drivers/pci/mmap.c
drivers/pci/msi.c
drivers/pci/pci-acpi.c
drivers/pci/pci-driver.c
drivers/pci/pci-label.c
drivers/pci/pci-stub.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/Makefile
drivers/pci/pcie/aer/aer_inject.c
drivers/pci/pcie/aer/aerdrv.c
drivers/pci/pcie/aer/aerdrv.h
drivers/pci/pcie/aer/aerdrv_acpi.c
drivers/pci/pcie/aer/aerdrv_core.c
drivers/pci/pcie/aer/aerdrv_errprint.c
drivers/pci/pcie/aer/ecrc.c
drivers/pci/pcie/aspm.c
drivers/pci/pcie/dpc.c [new file with mode: 0644]
drivers/pci/pcie/pcie-dpc.c [deleted file]
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_acpi.c
drivers/pci/pcie/portdrv_bus.c [deleted file]
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
drivers/pci/probe.c
drivers/pci/proc.c
drivers/pci/quirks.c
drivers/pci/rom.c
drivers/pci/search.c
drivers/pci/setup-bus.c
drivers/pci/setup-irq.c
drivers/pci/setup-res.c
drivers/pci/slot.c
drivers/pci/syscall.c
drivers/pci/vpd.c
drivers/pci/xen-pcifront.c
drivers/rapidio/devices/tsi721.c
include/acpi/acpi_bus.h
include/asm-generic/io.h
include/linux/logic_pio.h [new file with mode: 0644]
include/linux/pci.h
include/linux/pci_ids.h
include/linux/pcieport_if.h [deleted file]
include/uapi/linux/pci_regs.h
lib/Kconfig
lib/Makefile
lib/logic_pio.c [new file with mode: 0644]

index 1d1d53f85ddd79ce2218b7274b3114f41493ac3a..26565794a573c27d0ee61b9d5736bad01d7f01d1 100644 (file)
                force   Enable ASPM even on devices that claim not to support it.
                        WARNING: Forcing ASPM on may cause system lockups.
 
-       pcie_hp=        [PCIE] PCI Express Hotplug driver options:
-               nomsi   Do not use MSI for PCI Express Native Hotplug (this
-                       makes all PCIe ports use INTx for hotplug services).
-
-       pcie_ports=     [PCIE] PCIe ports handling:
-               auto    Ask the BIOS whether or not to use native PCIe services
-                       associated with PCIe ports (PME, hot-plug, AER).  Use
-                       them only if that is allowed by the BIOS.
-               native  Use native PCIe services associated with PCIe ports
-                       unconditionally.
-               compat  Treat PCIe ports as PCI-to-PCI bridges, disable the PCIe
-                       ports driver.
+       pcie_ports=     [PCIE] PCIe port services handling:
+               native  Use native PCIe services (PME, AER, DPC, PCIe hotplug)
+                       even if the platform doesn't give the OS permission to
+                       use them.  This may cause conflicts if the platform
+                       also tries to use these services.
+               compat  Disable native PCIe services (PME, AER, DPC, PCIe
+                       hotplug).
 
        pcie_port_pm=   [PCIE] PCIe port power management handling:
                off     Disable power management of all PCIe ports
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
new file mode 100644 (file)
index 0000000..10bd35f
--- /dev/null
@@ -0,0 +1,33 @@
+Hisilicon Hip06 Low Pin Count device
+  Hisilicon Hip06 SoCs implement a Low Pin Count (LPC) controller, which
+  provides I/O access to some legacy ISA devices.
+  Hip06 is based on arm64 architecture where there is no I/O space. So, the
+  I/O ports here are not CPU addresses, and there is no 'ranges' property in
+  LPC device node.
+
+Required properties:
+- compatible:  value should be as follows:
+       (a) "hisilicon,hip06-lpc"
+       (b) "hisilicon,hip07-lpc"
+- #address-cells: must be 2 which stick to the ISA/EISA binding doc.
+- #size-cells: must be 1 which stick to the ISA/EISA binding doc.
+- reg: base memory range where the LPC register set is mapped.
+
+Note:
+  The node name before '@' must be "isa" to represent the binding stick to the
+  ISA/EISA binding specification.
+
+Example:
+
+isa@a01b0000 {
+       compatible = "hisilicon,hip06-lpc";
+       #address-cells = <2>;
+       #size-cells = <1>;
+       reg = <0x0 0xa01b0000 0x0 0x1000>;
+
+       ipmi0: bt@e4 {
+               compatible = "ipmi-bt";
+               device_type = "ipmi";
+               reg = <0x01 0xe4 0x04>;
+       };
+};
index 3bdc260e36b7a7eaac26003b187c436655fc3412..39eedce231863968de709de19b0b04f12625b2e8 100644 (file)
@@ -6386,6 +6386,13 @@ W:       http://www.hisilicon.com
 S:     Maintained
 F:     drivers/net/ethernet/hisilicon/hns3/
 
+HISILICON LPC BUS DRIVER
+M:     john.garry@huawei.com
+W:     http://www.hisilicon.com
+S:     Maintained
+F:     drivers/bus/hisi_lpc.c
+F:     Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt
+
 HISILICON NETWORK SUBSYSTEM DRIVER
 M:     Yisen Zhuang <yisen.zhuang@huawei.com>
 M:     Salil Mehta <salil.mehta@huawei.com>
index f5ec736100ee6a6a036b6b5b3fa024125aa43154..7ccc64d5fe3ee09e3243cd54c9f7fc81985ef539 100644 (file)
@@ -398,7 +398,7 @@ pcibios_enable_device (struct pci_dev *dev, int mask)
        if (ret < 0)
                return ret;
 
-       if (!dev->msi_enabled)
+       if (!pci_dev_msi_enabled(dev))
                return acpi_pci_irq_enable(dev);
        return 0;
 }
@@ -407,7 +407,7 @@ void
 pcibios_disable_device (struct pci_dev *dev)
 {
        BUG_ON(atomic_read(&dev->enable_cnt));
-       if (!dev->msi_enabled)
+       if (!pci_dev_msi_enabled(dev))
                acpi_pci_irq_disable(dev);
 }
 
index d82802ff508889365fc921d4924a7675c872d616..401c62aad5e4ee6d8924cccd277d8e67914cd07f 100644 (file)
@@ -76,10 +76,11 @@ extern int pci_proc_domain(struct pci_bus *bus);
 
 struct vm_area_struct;
 
-/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */
-#define HAVE_PCI_MMAP          1
-#define arch_can_pci_mmap_io() 1
-#define arch_can_pci_mmap_wc() 1
+/* Tell PCI code what kind of PCI resource mappings we support */
+#define HAVE_PCI_MMAP                  1
+#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
+#define arch_can_pci_mmap_io()         1
+#define arch_can_pci_mmap_wc()         1
 
 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
                           size_t count);
index 446c79611d56cf0ff32f5a622348300970a59963..fe9733ffffaa426f62f2559f66a6771833d0961e 100644 (file)
@@ -410,72 +410,22 @@ static int pci_read_irq_line(struct pci_dev *pci_dev)
 }
 
 /*
- * Platform support for /proc/bus/pci/X/Y mmap()s,
- * modelled on the sparc64 implementation by Dave Miller.
+ * Platform support for /proc/bus/pci/X/Y mmap()s.
  *  -- paulus.
  */
-
-/*
- * Adjust vm_pgoff of VMA such that it is the physical page offset
- * corresponding to the 32-bit pci bus offset for DEV requested by the user.
- *
- * Basically, the user finds the base address for his device which he wishes
- * to mmap.  They read the 32-bit value from the config space base register,
- * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
- * offset parameter of mmap on /proc/bus/pci/XXX for that device.
- *
- * Returns negative error code on failure, zero on success.
- */
-static struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
-                                              resource_size_t *offset,
-                                              enum pci_mmap_state mmap_state)
+int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma)
 {
-       struct pci_controller *hose = pci_bus_to_host(dev->bus);
-       unsigned long io_offset = 0;
-       int i, res_bit;
-
-       if (hose == NULL)
-               return NULL;            /* should never happen */
-
-       /* If memory, add on the PCI bridge address offset */
-       if (mmap_state == pci_mmap_mem) {
-#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
-               *offset += hose->pci_mem_offset;
-#endif
-               res_bit = IORESOURCE_MEM;
-       } else {
-               io_offset = (unsigned long)hose->io_base_virt - _IO_BASE;
-               *offset += io_offset;
-               res_bit = IORESOURCE_IO;
-       }
-
-       /*
-        * Check that the offset requested corresponds to one of the
-        * resources of the device.
-        */
-       for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-               struct resource *rp = &dev->resource[i];
-               int flags = rp->flags;
+       struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+       resource_size_t ioaddr = pci_resource_start(pdev, bar);
 
-               /* treat ROM as memory (should be already) */
-               if (i == PCI_ROM_RESOURCE)
-                       flags |= IORESOURCE_MEM;
-
-               /* Active and same type? */
-               if ((flags & res_bit) == 0)
-                       continue;
-
-               /* In the range of this resource? */
-               if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end)
-                       continue;
+       if (!hose)
+               return -EINVAL;
 
-               /* found it! construct the final physical address */
-               if (mmap_state == pci_mmap_io)
-                       *offset += hose->io_base_phys - io_offset;
-               return rp;
-       }
+       /* Convert to an offset within this PCI controller */
+       ioaddr -= (unsigned long)hose->io_base_virt - _IO_BASE;
 
-       return NULL;
+       vma->vm_pgoff += (ioaddr + hose->io_base_phys) >> PAGE_SHIFT;
+       return 0;
 }
 
 /*
@@ -527,42 +477,6 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
        return prot;
 }
 
-
-/*
- * Perform the actual remap of the pages for a PCI device mapping, as
- * appropriate for this architecture.  The region in the process to map
- * is described by vm_start and vm_end members of VMA, the base physical
- * address is found in vm_pgoff.
- * The pci device structure is provided so that architectures may make mapping
- * decisions on a per-device or per-bus basis.
- *
- * Returns a negative error code on failure, zero on success.
- */
-int pci_mmap_page_range(struct pci_dev *dev, int bar,
-                       struct vm_area_struct *vma,
-                       enum pci_mmap_state mmap_state, int write_combine)
-{
-       resource_size_t offset =
-               ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
-       struct resource *rp;
-       int ret;
-
-       rp = __pci_mmap_make_offset(dev, &offset, mmap_state);
-       if (rp == NULL)
-               return -EINVAL;
-
-       vma->vm_pgoff = offset >> PAGE_SHIFT;
-       if (write_combine)
-               vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
-       else
-               vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
-       ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
-                              vma->vm_end - vma->vm_start, vma->vm_page_prot);
-
-       return ret;
-}
-
 /* This provides legacy IO read access on a bus */
 int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
 {
index 220d0f36560a3ac56e56a693f913bc2169f1b876..41b20edb427d71b92174db682c2172ba35bbc530 100644 (file)
@@ -664,12 +664,12 @@ struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm,
        printk("PCI: Scanning PBM %s\n", node->full_name);
 
        pci_add_resource_offset(&resources, &pbm->io_space,
-                               pbm->io_space.start);
+                               pbm->io_offset);
        pci_add_resource_offset(&resources, &pbm->mem_space,
-                               pbm->mem_space.start);
+                               pbm->mem_offset);
        if (pbm->mem64_space.flags)
                pci_add_resource_offset(&resources, &pbm->mem64_space,
-                                       pbm->mem_space.start);
+                                       pbm->mem64_offset);
        pbm->busn.start = pbm->pci_first_busno;
        pbm->busn.end   = pbm->pci_last_busno;
        pbm->busn.flags = IORESOURCE_BUS;
index 1e10fb26fa8801858275928cdc77d35457cc990b..38d46bcc8634eaa5931247ee72156b737bc79f82 100644 (file)
@@ -344,26 +344,6 @@ static void pci_register_legacy_regions(struct resource *io_res,
        p->end = p->start + 0x1ffffUL;
        p->flags = IORESOURCE_BUSY;
        request_resource(mem_res, p);
-
-       p = kzalloc(sizeof(*p), GFP_KERNEL);
-       if (!p)
-               return;
-
-       p->name = "System ROM";
-       p->start = mem_res->start + 0xf0000UL;
-       p->end = p->start + 0xffffUL;
-       p->flags = IORESOURCE_BUSY;
-       request_resource(mem_res, p);
-
-       p = kzalloc(sizeof(*p), GFP_KERNEL);
-       if (!p)
-               return;
-
-       p->name = "Video ROM";
-       p->start = mem_res->start + 0xc0000UL;
-       p->end = p->start + 0x7fffUL;
-       p->flags = IORESOURCE_BUSY;
-       request_resource(mem_res, p);
 }
 
 static void pci_register_iommu_region(struct pci_pbm_info *pbm)
@@ -397,6 +377,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
        int i, saw_mem, saw_io;
        int num_pbm_ranges;
 
+       /* Corresponding generic code in of_pci_get_host_bridge_resources() */
+
        saw_mem = saw_io = 0;
        pbm_ranges = of_get_property(pbm->op->dev.of_node, "ranges", &i);
        if (!pbm_ranges) {
@@ -411,13 +393,16 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
 
        for (i = 0; i < num_pbm_ranges; i++) {
                const struct linux_prom_pci_ranges *pr = &pbm_ranges[i];
-               unsigned long a, size;
+               unsigned long a, size, region_a;
                u32 parent_phys_hi, parent_phys_lo;
+               u32 child_phys_mid, child_phys_lo;
                u32 size_hi, size_lo;
                int type;
 
                parent_phys_hi = pr->parent_phys_hi;
                parent_phys_lo = pr->parent_phys_lo;
+               child_phys_mid = pr->child_phys_mid;
+               child_phys_lo = pr->child_phys_lo;
                if (tlb_type == hypervisor)
                        parent_phys_hi &= 0x0fffffff;
 
@@ -427,6 +412,8 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                type = (pr->child_phys_hi >> 24) & 0x3;
                a = (((unsigned long)parent_phys_hi << 32UL) |
                     ((unsigned long)parent_phys_lo  <<  0UL));
+               region_a = (((unsigned long)child_phys_mid << 32UL) |
+                    ((unsigned long)child_phys_lo  <<  0UL));
                size = (((unsigned long)size_hi << 32UL) |
                        ((unsigned long)size_lo  <<  0UL));
 
@@ -441,6 +428,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                        pbm->io_space.start = a;
                        pbm->io_space.end = a + size - 1UL;
                        pbm->io_space.flags = IORESOURCE_IO;
+                       pbm->io_offset = a - region_a;
                        saw_io = 1;
                        break;
 
@@ -449,6 +437,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                        pbm->mem_space.start = a;
                        pbm->mem_space.end = a + size - 1UL;
                        pbm->mem_space.flags = IORESOURCE_MEM;
+                       pbm->mem_offset = a - region_a;
                        saw_mem = 1;
                        break;
 
@@ -457,6 +446,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                        pbm->mem64_space.start = a;
                        pbm->mem64_space.end = a + size - 1UL;
                        pbm->mem64_space.flags = IORESOURCE_MEM;
+                       pbm->mem64_offset = a - region_a;
                        saw_mem = 1;
                        break;
 
@@ -472,14 +462,22 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                prom_halt();
        }
 
-       printk("%s: PCI IO[%llx] MEM[%llx]",
-              pbm->name,
-              pbm->io_space.start,
-              pbm->mem_space.start);
+       if (pbm->io_space.flags)
+               printk("%s: PCI IO %pR offset %llx\n",
+                      pbm->name, &pbm->io_space, pbm->io_offset);
+       if (pbm->mem_space.flags)
+               printk("%s: PCI MEM %pR offset %llx\n",
+                      pbm->name, &pbm->mem_space, pbm->mem_offset);
+       if (pbm->mem64_space.flags && pbm->mem_space.flags) {
+               if (pbm->mem64_space.start <= pbm->mem_space.end)
+                       pbm->mem64_space.start = pbm->mem_space.end + 1;
+               if (pbm->mem64_space.start > pbm->mem64_space.end)
+                       pbm->mem64_space.flags = 0;
+       }
+
        if (pbm->mem64_space.flags)
-               printk(" MEM64[%llx]",
-                      pbm->mem64_space.start);
-       printk("\n");
+               printk("%s: PCI MEM64 %pR offset %llx\n",
+                      pbm->name, &pbm->mem64_space, pbm->mem64_offset);
 
        pbm->io_space.name = pbm->mem_space.name = pbm->name;
        pbm->mem64_space.name = pbm->name;
index ac172961d276e8e22b96c700ffaedd1fc2868fd2..4e3d15189fa95f10a2e8e8ba4212820979938686 100644 (file)
@@ -100,6 +100,10 @@ struct pci_pbm_info {
        struct resource                 mem_space;
        struct resource                 mem64_space;
        struct resource                 busn;
+       /* offset */
+       resource_size_t                 io_offset;
+       resource_size_t                 mem_offset;
+       resource_size_t                 mem64_offset;
 
        /* Base of PCI Config space, can be per-PBM or shared. */
        unsigned long                   config_space;
index 5c83798e3b2e2ca8c9173861a660bd6621567dd0..d5a82153a7c5d87b4ad6e02085956eb486393e39 100644 (file)
@@ -44,9 +44,10 @@ extern struct pci_controller* pcibios_alloc_controller(void);
 
 #define PCI_DMA_BUS_IS_PHYS    (1)
 
-/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */
-#define HAVE_PCI_MMAP          1
-#define arch_can_pci_mmap_io() 1
+/* Tell PCI code what kind of PCI resource mappings we support */
+#define HAVE_PCI_MMAP                  1
+#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
+#define arch_can_pci_mmap_io()         1
 
 #endif /* __KERNEL__ */
 
index d981f01c8d8956abb4b5244072c85e4edae1d96d..b7c7a60c70009a71bbfbc8cbdee355429bc6e96f 100644 (file)
@@ -39,7 +39,6 @@
  * pcibios_align_resource
  * pcibios_fixup_bus
  * pci_bus_add_device
- * pci_mmap_page_range
  */
 
 struct pci_controller* pci_ctrl_head;
@@ -258,98 +257,21 @@ pci_controller_num(struct pci_dev *dev)
 #endif /* CONFIG_PROC_FS */
 
 /*
- * Platform support for /proc/bus/pci/X/Y mmap()s,
- * modelled on the sparc64 implementation by Dave Miller.
+ * Platform support for /proc/bus/pci/X/Y mmap()s.
  *  -- paulus.
  */
 
-/*
- * Adjust vm_pgoff of VMA such that it is the physical page offset
- * corresponding to the 32-bit pci bus offset for DEV requested by the user.
- *
- * Basically, the user finds the base address for his device which he wishes
- * to mmap.  They read the 32-bit value from the config space base register,
- * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
- * offset parameter of mmap on /proc/bus/pci/XXX for that device.
- *
- * Returns negative error code on failure, zero on success.
- */
-static __inline__ int
-__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
-                      enum pci_mmap_state mmap_state)
+int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma)
 {
-       struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       unsigned long io_offset = 0;
-       int i, res_bit;
+       struct pci_controller *pci_ctrl = (struct pci_controller*) pdev->sysdata;
+       resource_size_t ioaddr = pci_resource_start(pdev, bar);
 
        if (pci_ctrl == 0)
                return -EINVAL;         /* should never happen */
 
-       /* If memory, add on the PCI bridge address offset */
-       if (mmap_state == pci_mmap_mem) {
-               res_bit = IORESOURCE_MEM;
-       } else {
-               io_offset = (unsigned long)pci_ctrl->io_space.base;
-               offset += io_offset;
-               res_bit = IORESOURCE_IO;
-       }
-
-       /*
-        * Check that the offset requested corresponds to one of the
-        * resources of the device.
-        */
-       for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-               struct resource *rp = &dev->resource[i];
-               int flags = rp->flags;
-
-               /* treat ROM as memory (should be already) */
-               if (i == PCI_ROM_RESOURCE)
-                       flags |= IORESOURCE_MEM;
-
-               /* Active and same type? */
-               if ((flags & res_bit) == 0)
-                       continue;
-
-               /* In the range of this resource? */
-               if (offset < (rp->start & PAGE_MASK) || offset > rp->end)
-                       continue;
-
-               /* found it! construct the final physical address */
-               if (mmap_state == pci_mmap_io)
-                       offset += pci_ctrl->io_space.start - io_offset;
-               vma->vm_pgoff = offset >> PAGE_SHIFT;
-               return 0;
-       }
-
-       return -EINVAL;
-}
+       /* Convert to an offset within this PCI controller */
+       ioaddr -= (unsigned long)pci_ctrl->io_space.base;
 
-/*
- * Perform the actual remap of the pages for a PCI device mapping, as
- * appropriate for this architecture.  The region in the process to map
- * is described by vm_start and vm_end members of VMA, the base physical
- * address is found in vm_pgoff.
- * The pci device structure is provided so that architectures may make mapping
- * decisions on a per-device or per-bus basis.
- *
- * Returns a negative error code on failure, zero on success.
- */
-int pci_mmap_page_range(struct pci_dev *dev, int bar,
-                       struct vm_area_struct *vma,
-                       enum pci_mmap_state mmap_state,
-                       int write_combine)
-{
-       int ret;
-
-       ret = __pci_mmap_make_offset(dev, vma, mmap_state);
-       if (ret < 0)
-               return ret;
-
-       vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
-
-       ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
-                                vma->vm_end - vma->vm_start,vma->vm_page_prot);
-
-       return ret;
+       vma->vm_pgoff += (ioaddr + pci_ctrl->io_space.start) >> PAGE_SHIFT;
+       return 0;
 }
index 6fc204a524932e97f4a2743ae7076f1e12e86ad8..0da18bde6a165cd5d02dad6855850adfd1bde604 100644 (file)
@@ -729,7 +729,8 @@ next:
        }
 }
 
-static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
+static void acpi_pci_root_remap_iospace(struct fwnode_handle *fwnode,
+                       struct resource_entry *entry)
 {
 #ifdef PCI_IOBASE
        struct resource *res = entry->res;
@@ -738,7 +739,7 @@ static void acpi_pci_root_remap_iospace(struct resource_entry *entry)
        resource_size_t length = resource_size(res);
        unsigned long port;
 
-       if (pci_register_io_range(cpu_addr, length))
+       if (pci_register_io_range(fwnode, cpu_addr, length))
                goto err;
 
        port = pci_address_to_pio(cpu_addr);
@@ -780,7 +781,8 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info)
        else {
                resource_list_for_each_entry_safe(entry, tmp, list) {
                        if (entry->res->flags & IORESOURCE_IO)
-                               acpi_pci_root_remap_iospace(entry);
+                               acpi_pci_root_remap_iospace(&device->fwnode,
+                                               entry);
 
                        if (entry->res->flags & IORESOURCE_DISABLED)
                                resource_list_destroy_entry(entry);
@@ -871,6 +873,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
        struct acpi_device *device = root->device;
        int node = acpi_get_node(device->handle);
        struct pci_bus *bus;
+       struct pci_host_bridge *host_bridge;
 
        info->root = root;
        info->bridge = device;
@@ -895,9 +898,17 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
        if (!bus)
                goto out_release_info;
 
+       host_bridge = to_pci_host_bridge(bus->bridge);
+       if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+               host_bridge->native_hotplug = 0;
+       if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
+               host_bridge->native_aer = 0;
+       if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
+               host_bridge->native_pme = 0;
+
        pci_scan_child_bus(bus);
-       pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge),
-                                   acpi_pci_root_release_info, info);
+       pci_set_host_bridge_release(host_bridge, acpi_pci_root_release_info,
+                                   info);
        if (node != NUMA_NO_NODE)
                dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node);
        return bus;
index 8e63d937babb0de9d85cac007a03a6eda1258307..a4cbf3efc809350d6bd7eccf97cc85009dcb3147 100644 (file)
@@ -1524,11 +1524,25 @@ static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data)
        return -1;
 }
 
-static bool acpi_is_serial_bus_slave(struct acpi_device *device)
+static bool acpi_is_indirect_io_slave(struct acpi_device *device)
+{
+       struct acpi_device *parent = device->parent;
+       const struct acpi_device_id indirect_io_hosts[] = {
+               {"HISI0191", 0},
+               {}
+       };
+
+       return parent && !acpi_match_device_ids(parent, indirect_io_hosts);
+}
+
+static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
 {
        struct list_head resource_list;
        bool is_serial_bus_slave = false;
 
+       if (acpi_is_indirect_io_slave(device))
+               return true;
+
        /* Macs use device properties in lieu of _CRS resources */
        if (x86_apple_machine &&
            (fwnode_property_present(&device->fwnode, "spiSclkPeriod") ||
@@ -1560,7 +1574,8 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
        acpi_bus_get_flags(device);
        device->flags.match_driver = false;
        device->flags.initialized = true;
-       device->flags.serial_bus_slave = acpi_is_serial_bus_slave(device);
+       device->flags.enumeration_by_parent =
+               acpi_device_enumeration_by_parent(device);
        acpi_device_clear_enumerated(device);
        device_initialize(&device->dev);
        dev_set_uevent_suppress(&device->dev, true);
@@ -1858,10 +1873,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
 static void acpi_default_enumeration(struct acpi_device *device)
 {
        /*
-        * Do not enumerate SPI/I2C/UART slaves as they will be enumerated by
-        * their respective parents.
+        * Do not enumerate devices with enumeration_by_parent flag set as
+        * they will be enumerated by their respective parents.
         */
-       if (!device->flags.serial_bus_slave) {
+       if (!device->flags.enumeration_by_parent) {
                acpi_create_platform_device(device, NULL);
                acpi_device_set_enumerated(device);
        } else {
@@ -1958,7 +1973,7 @@ static void acpi_bus_attach(struct acpi_device *device)
                return;
 
        device->flags.match_driver = true;
-       if (ret > 0 && !device->flags.serial_bus_slave) {
+       if (ret > 0 && !device->flags.enumeration_by_parent) {
                acpi_device_set_enumerated(device);
                goto ok;
        }
@@ -1967,10 +1982,10 @@ static void acpi_bus_attach(struct acpi_device *device)
        if (ret < 0)
                return;
 
-       if (!device->pnp.type.platform_id && !device->flags.serial_bus_slave)
-               acpi_device_set_enumerated(device);
-       else
+       if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
                acpi_default_enumeration(device);
+       else
+               acpi_device_set_enumerated(device);
 
  ok:
        list_for_each_entry(child, &device->children, node)
index 57e011d36a79fce3156d38a7457e2875993f3cd3..a3fad0f0292f911c5a539ef42e413705d34d1eda 100644 (file)
@@ -65,6 +65,14 @@ config BRCMSTB_GISB_ARB
          arbiter. This driver provides timeout and target abort error handling
          and internal bus master decoding.
 
+config HISILICON_LPC
+       bool "Support for ISA I/O space on HiSilicon Hip06/7"
+       depends on ARM64 && (ARCH_HISI || COMPILE_TEST)
+       select INDIRECT_PIO
+       help
+         Driver to enable I/O access to devices attached to the Low Pin
+         Count bus on the HiSilicon Hip06/7 SoC.
+
 config IMX_WEIM
        bool "Freescale EIM DRIVER"
        depends on ARCH_MXC
index 9bcd0bf3954bf18209f2dfe0e65cc79e04de4dc1..50bb12a971a09b105129301eeeaab758ce4d0b1a 100644 (file)
@@ -7,6 +7,7 @@
 obj-$(CONFIG_ARM_CCI)          += arm-cci.o
 obj-$(CONFIG_ARM_CCN)          += arm-ccn.o
 
+obj-$(CONFIG_HISILICON_LPC)    += hisi_lpc.o
 obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o
 obj-$(CONFIG_IMX_WEIM)         += imx-weim.o
 obj-$(CONFIG_MIPS_CDMM)                += mips_cdmm.o
diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c
new file mode 100644 (file)
index 0000000..2d4611e
--- /dev/null
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Hisilicon Limited, All Rights Reserved.
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ * Author: Zou Rongrong <zourongrong@huawei.com>
+ * Author: John Garry <john.garry@huawei.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/logic_pio.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "hisi-lpc"
+
+/*
+ * Setting this bit means each IO operation will target a different port
+ * address; 0 means repeated IO operations will use the same port,
+ * such as BT.
+ */
+#define FG_INCRADDR_LPC                0x02
+
+struct lpc_cycle_para {
+       unsigned int opflags;
+       unsigned int csize; /* data length of each operation */
+};
+
+struct hisi_lpc_dev {
+       spinlock_t cycle_lock;
+       void __iomem  *membase;
+       struct logic_pio_hwaddr *io_host;
+};
+
+/* The max IO cycle counts supported is four per operation at maximum */
+#define LPC_MAX_DWIDTH 4
+
+#define LPC_REG_STARTUP_SIGNAL         0x00
+#define LPC_REG_STARTUP_SIGNAL_START   BIT(0)
+#define LPC_REG_OP_STATUS              0x04
+#define LPC_REG_OP_STATUS_IDLE         BIT(0)
+#define LPC_REG_OP_STATUS_FINISHED     BIT(1)
+#define LPC_REG_OP_LEN                 0x10 /* LPC cycles count per start */
+#define LPC_REG_CMD                    0x14
+#define LPC_REG_CMD_OP                 BIT(0) /* 0: read, 1: write */
+#define LPC_REG_CMD_SAMEADDR           BIT(3)
+#define LPC_REG_ADDR                   0x20 /* target address */
+#define LPC_REG_WDATA                  0x24 /* write FIFO */
+#define LPC_REG_RDATA                  0x28 /* read FIFO */
+
+/* The minimal nanosecond interval for each query on LPC cycle status */
+#define LPC_NSEC_PERWAIT       100
+
+/*
+ * The maximum waiting time is about 128us.  It is specific for stream I/O,
+ * such as ins.
+ *
+ * The fastest IO cycle time is about 390ns, but the worst case will wait
+ * for extra 256 lpc clocks, so (256 + 13) * 30ns = 8 us. The maximum burst
+ * cycles is 16. So, the maximum waiting time is about 128us under worst
+ * case.
+ *
+ * Choose 1300 as the maximum.
+ */
+#define LPC_MAX_WAITCNT                1300
+
+/* About 10us. This is specific for single IO operations, such as inb */
+#define LPC_PEROP_WAITCNT      100
+
+static int wait_lpc_idle(unsigned char *mbase, unsigned int waitcnt)
+{
+       u32 status;
+
+       do {
+               status = readl(mbase + LPC_REG_OP_STATUS);
+               if (status & LPC_REG_OP_STATUS_IDLE)
+                       return (status & LPC_REG_OP_STATUS_FINISHED) ? 0 : -EIO;
+               ndelay(LPC_NSEC_PERWAIT);
+       } while (--waitcnt);
+
+       return -ETIME;
+}
+
+/*
+ * hisi_lpc_target_in - trigger a series of LPC cycles for read operation
+ * @lpcdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @addr: the lpc I/O target port address
+ * @buf: where the read back data is stored
+ * @opcnt: how many I/O operations required, i.e. data width
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_target_in(struct hisi_lpc_dev *lpcdev,
+                             struct lpc_cycle_para *para, unsigned long addr,
+                             unsigned char *buf, unsigned long opcnt)
+{
+       unsigned int cmd_word;
+       unsigned int waitcnt;
+       unsigned long flags;
+       int ret;
+
+       if (!buf || !opcnt || !para || !para->csize || !lpcdev)
+               return -EINVAL;
+
+       cmd_word = 0; /* IO mode, Read */
+       waitcnt = LPC_PEROP_WAITCNT;
+       if (!(para->opflags & FG_INCRADDR_LPC)) {
+               cmd_word |= LPC_REG_CMD_SAMEADDR;
+               waitcnt = LPC_MAX_WAITCNT;
+       }
+
+       /* whole operation must be atomic */
+       spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+       writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
+       writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+       writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
+
+       writel(LPC_REG_STARTUP_SIGNAL_START,
+              lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
+
+       /* whether the operation is finished */
+       ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+       if (ret) {
+               spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+               return ret;
+       }
+
+       readsb(lpcdev->membase + LPC_REG_RDATA, buf, opcnt);
+
+       spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+
+       return 0;
+}
+
+/*
+ * hisi_lpc_target_out - trigger a series of LPC cycles for write operation
+ * @lpcdev: pointer to hisi lpc device
+ * @para: some parameters used to control the lpc I/O operations
+ * @addr: the lpc I/O target port address
+ * @buf: where the data to be written is stored
+ * @opcnt: how many I/O operations required, i.e. data width
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_target_out(struct hisi_lpc_dev *lpcdev,
+                              struct lpc_cycle_para *para, unsigned long addr,
+                              const unsigned char *buf, unsigned long opcnt)
+{
+       unsigned int waitcnt;
+       unsigned long flags;
+       u32 cmd_word;
+       int ret;
+
+       if (!buf || !opcnt || !para || !lpcdev)
+               return -EINVAL;
+
+       /* default is increasing address */
+       cmd_word = LPC_REG_CMD_OP; /* IO mode, write */
+       waitcnt = LPC_PEROP_WAITCNT;
+       if (!(para->opflags & FG_INCRADDR_LPC)) {
+               cmd_word |= LPC_REG_CMD_SAMEADDR;
+               waitcnt = LPC_MAX_WAITCNT;
+       }
+
+       spin_lock_irqsave(&lpcdev->cycle_lock, flags);
+
+       writel_relaxed(opcnt, lpcdev->membase + LPC_REG_OP_LEN);
+       writel_relaxed(cmd_word, lpcdev->membase + LPC_REG_CMD);
+       writel_relaxed(addr, lpcdev->membase + LPC_REG_ADDR);
+
+       writesb(lpcdev->membase + LPC_REG_WDATA, buf, opcnt);
+
+       writel(LPC_REG_STARTUP_SIGNAL_START,
+              lpcdev->membase + LPC_REG_STARTUP_SIGNAL);
+
+       /* whether the operation is finished */
+       ret = wait_lpc_idle(lpcdev->membase, waitcnt);
+
+       spin_unlock_irqrestore(&lpcdev->cycle_lock, flags);
+
+       return ret;
+}
+
+static unsigned long hisi_lpc_pio_to_addr(struct hisi_lpc_dev *lpcdev,
+                                         unsigned long pio)
+{
+       return pio - lpcdev->io_host->io_start + lpcdev->io_host->hw_start;
+}
+
+/*
+ * hisi_lpc_comm_in - input the data in a single operation
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @dwidth: the data length required to read from the target I/O port
+ *
+ * When success, data is returned. Otherwise, ~0 is returned.
+ */
+static u32 hisi_lpc_comm_in(void *hostdata, unsigned long pio, size_t dwidth)
+{
+       struct hisi_lpc_dev *lpcdev = hostdata;
+       struct lpc_cycle_para iopara;
+       unsigned long addr;
+       u32 rd_data = 0;
+       int ret;
+
+       if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
+               return ~0;
+
+       addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+       iopara.opflags = FG_INCRADDR_LPC;
+       iopara.csize = dwidth;
+
+       ret = hisi_lpc_target_in(lpcdev, &iopara, addr,
+                                (unsigned char *)&rd_data, dwidth);
+       if (ret)
+               return ~0;
+
+       return le32_to_cpu(rd_data);
+}
+
+/*
+ * hisi_lpc_comm_out - output the data in a single operation
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @val: a value to be output from caller, maximum is four bytes
+ * @dwidth: the data width required writing to the target I/O port
+ *
+ * This function corresponds to out(b,w,l) only.
+ */
+static void hisi_lpc_comm_out(void *hostdata, unsigned long pio,
+                             u32 val, size_t dwidth)
+{
+       struct hisi_lpc_dev *lpcdev = hostdata;
+       struct lpc_cycle_para iopara;
+       const unsigned char *buf;
+       unsigned long addr;
+
+       if (!lpcdev || !dwidth || dwidth > LPC_MAX_DWIDTH)
+               return;
+
+       val = cpu_to_le32(val);
+
+       buf = (const unsigned char *)&val;
+       addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+       iopara.opflags = FG_INCRADDR_LPC;
+       iopara.csize = dwidth;
+
+       hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth);
+}
+
+/*
+ * hisi_lpc_comm_ins - input the data in the buffer in multiple operations
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @buffer: a buffer where read/input data bytes are stored
+ * @dwidth: the data width required writing to the target I/O port
+ * @count: how many data units whose length is dwidth will be read
+ *
+ * When success, the data read back is stored in buffer pointed by buffer.
+ * Returns 0 on success, -errno otherwise.
+ */
+static u32 hisi_lpc_comm_ins(void *hostdata, unsigned long pio, void *buffer,
+                            size_t dwidth, unsigned int count)
+{
+       struct hisi_lpc_dev *lpcdev = hostdata;
+       unsigned char *buf = buffer;
+       struct lpc_cycle_para iopara;
+       unsigned long addr;
+
+       if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
+               return -EINVAL;
+
+       iopara.opflags = 0;
+       if (dwidth > 1)
+               iopara.opflags |= FG_INCRADDR_LPC;
+       iopara.csize = dwidth;
+
+       addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+
+       do {
+               int ret;
+
+               ret = hisi_lpc_target_in(lpcdev, &iopara, addr, buf, dwidth);
+               if (ret)
+                       return ret;
+               buf += dwidth;
+       } while (--count);
+
+       return 0;
+}
+
+/*
+ * hisi_lpc_comm_outs - output the data in the buffer in multiple operations
+ * @hostdata: pointer to the device information relevant to LPC controller
+ * @pio: the target I/O port address
+ * @buffer: a buffer where write/output data bytes are stored
+ * @dwidth: the data width required writing to the target I/O port
+ * @count: how many data units whose length is dwidth will be written
+ */
+static void hisi_lpc_comm_outs(void *hostdata, unsigned long pio,
+                              const void *buffer, size_t dwidth,
+                              unsigned int count)
+{
+       struct hisi_lpc_dev *lpcdev = hostdata;
+       struct lpc_cycle_para iopara;
+       const unsigned char *buf = buffer;
+       unsigned long addr;
+
+       if (!lpcdev || !buf || !count || !dwidth || dwidth > LPC_MAX_DWIDTH)
+               return;
+
+       iopara.opflags = 0;
+       if (dwidth > 1)
+               iopara.opflags |= FG_INCRADDR_LPC;
+       iopara.csize = dwidth;
+
+       addr = hisi_lpc_pio_to_addr(lpcdev, pio);
+       do {
+               if (hisi_lpc_target_out(lpcdev, &iopara, addr, buf, dwidth))
+                       break;
+               buf += dwidth;
+       } while (--count);
+}
+
+static const struct logic_pio_host_ops hisi_lpc_ops = {
+       .in = hisi_lpc_comm_in,
+       .out = hisi_lpc_comm_out,
+       .ins = hisi_lpc_comm_ins,
+       .outs = hisi_lpc_comm_outs,
+};
+
+#ifdef CONFIG_ACPI
+#define MFD_CHILD_NAME_PREFIX DRV_NAME"-"
+#define MFD_CHILD_NAME_LEN (ACPI_ID_LEN + sizeof(MFD_CHILD_NAME_PREFIX) - 1)
+
+struct hisi_lpc_mfd_cell {
+       struct mfd_cell_acpi_match acpi_match;
+       char name[MFD_CHILD_NAME_LEN];
+       char pnpid[ACPI_ID_LEN];
+};
+
+static int hisi_lpc_acpi_xlat_io_res(struct acpi_device *adev,
+                                    struct acpi_device *host,
+                                    struct resource *res)
+{
+       unsigned long sys_port;
+       resource_size_t len = resource_size(res);
+
+       sys_port = logic_pio_trans_hwaddr(&host->fwnode, res->start, len);
+       if (sys_port == ~0UL)
+               return -EFAULT;
+
+       res->start = sys_port;
+       res->end = sys_port + len;
+
+       return 0;
+}
+
+/*
+ * hisi_lpc_acpi_set_io_res - set the resources for a child's MFD
+ * @child: the device node to be updated the I/O resource
+ * @hostdev: the device node associated with host controller
+ * @res: double pointer to be set to the address of translated resources
+ * @num_res: pointer to variable to hold the number of translated resources
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * For a given host controller, each child device will have an associated
+ * host-relative address resource.  This function will return the translated
+ * logical PIO addresses for each child devices resources.
+ */
+static int hisi_lpc_acpi_set_io_res(struct device *child,
+                                   struct device *hostdev,
+                                   const struct resource **res, int *num_res)
+{
+       struct acpi_device *adev;
+       struct acpi_device *host;
+       struct resource_entry *rentry;
+       LIST_HEAD(resource_list);
+       struct resource *resources;
+       int count;
+       int i;
+
+       if (!child || !hostdev)
+               return -EINVAL;
+
+       host = to_acpi_device(hostdev);
+       adev = to_acpi_device(child);
+
+       if (!adev->status.present) {
+               dev_dbg(child, "device is not present\n");
+               return -EIO;
+       }
+
+       if (acpi_device_enumerated(adev)) {
+               dev_dbg(child, "has been enumerated\n");
+               return -EIO;
+       }
+
+       /*
+        * The following code segment to retrieve the resources is common to
+        * acpi_create_platform_device(), so consider a common helper function
+        * in future.
+        */
+       count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
+       if (count <= 0) {
+               dev_dbg(child, "failed to get resources\n");
+               return count ? count : -EIO;
+       }
+
+       resources = devm_kcalloc(hostdev, count, sizeof(*resources),
+                                GFP_KERNEL);
+       if (!resources) {
+               dev_warn(hostdev, "could not allocate memory for %d resources\n",
+                        count);
+               acpi_dev_free_resource_list(&resource_list);
+               return -ENOMEM;
+       }
+       count = 0;
+       list_for_each_entry(rentry, &resource_list, node)
+               resources[count++] = *rentry->res;
+
+       acpi_dev_free_resource_list(&resource_list);
+
+       /* translate the I/O resources */
+       for (i = 0; i < count; i++) {
+               int ret;
+
+               if (!(resources[i].flags & IORESOURCE_IO))
+                       continue;
+               ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
+               if (ret) {
+                       dev_err(child, "translate IO range %pR failed (%d)\n",
+                               &resources[i], ret);
+                       return ret;
+               }
+       }
+       *res = resources;
+       *num_res = count;
+
+       return 0;
+}
+
+/*
+ * hisi_lpc_acpi_probe - probe children for ACPI FW
+ * @hostdev: LPC host device pointer
+ *
+ * Returns 0 when successful, and a negative value for failure.
+ *
+ * Scan all child devices and create a per-device MFD with
+ * logical PIO translated IO resources.
+ */
+static int hisi_lpc_acpi_probe(struct device *hostdev)
+{
+       struct acpi_device *adev = ACPI_COMPANION(hostdev);
+       struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cells;
+       struct mfd_cell *mfd_cells;
+       struct acpi_device *child;
+       int size, ret, count = 0, cell_num = 0;
+
+       list_for_each_entry(child, &adev->children, node)
+               cell_num++;
+
+       /* allocate the mfd cell and companion ACPI info, one per child */
+       size = sizeof(*mfd_cells) + sizeof(*hisi_lpc_mfd_cells);
+       mfd_cells = devm_kcalloc(hostdev, cell_num, size, GFP_KERNEL);
+       if (!mfd_cells)
+               return -ENOMEM;
+
+       hisi_lpc_mfd_cells = (struct hisi_lpc_mfd_cell *)&mfd_cells[cell_num];
+       /* Only consider the children of the host */
+       list_for_each_entry(child, &adev->children, node) {
+               struct mfd_cell *mfd_cell = &mfd_cells[count];
+               struct hisi_lpc_mfd_cell *hisi_lpc_mfd_cell =
+                                       &hisi_lpc_mfd_cells[count];
+               struct mfd_cell_acpi_match *acpi_match =
+                                       &hisi_lpc_mfd_cell->acpi_match;
+               char *name = hisi_lpc_mfd_cell[count].name;
+               char *pnpid = hisi_lpc_mfd_cell[count].pnpid;
+               struct mfd_cell_acpi_match match = {
+                       .pnpid = pnpid,
+               };
+
+               /*
+                * For any instances of this host controller (Hip06 and Hip07
+                * are the only chipsets), we would not have multiple slaves
+                * with the same HID. And in any system we would have just one
+                * controller active. So don't worrry about MFD name clashes.
+                */
+               snprintf(name, MFD_CHILD_NAME_LEN, MFD_CHILD_NAME_PREFIX"%s",
+                        acpi_device_hid(child));
+               snprintf(pnpid, ACPI_ID_LEN, "%s", acpi_device_hid(child));
+
+               memcpy(acpi_match, &match, sizeof(*acpi_match));
+               mfd_cell->name = name;
+               mfd_cell->acpi_match = acpi_match;
+
+               ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev,
+                                              &mfd_cell->resources,
+                                              &mfd_cell->num_resources);
+               if (ret) {
+                       dev_warn(&child->dev, "set resource fail (%d)\n", ret);
+                       return ret;
+               }
+               count++;
+       }
+
+       ret = mfd_add_devices(hostdev, PLATFORM_DEVID_NONE,
+                             mfd_cells, cell_num, NULL, 0, NULL);
+       if (ret) {
+               dev_err(hostdev, "failed to add mfd cells (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct acpi_device_id hisi_lpc_acpi_match[] = {
+       {"HISI0191"},
+       {}
+};
+#else
+static int hisi_lpc_acpi_probe(struct device *dev)
+{
+       return -ENODEV;
+}
+#endif // CONFIG_ACPI
+
+/*
+ * hisi_lpc_probe - the probe callback function for hisi lpc host,
+ *                will finish all the initialization.
+ * @pdev: the platform device corresponding to hisi lpc host
+ *
+ * Returns 0 on success, non-zero on fail.
+ */
+static int hisi_lpc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct acpi_device *acpi_device = ACPI_COMPANION(dev);
+       struct logic_pio_hwaddr *range;
+       struct hisi_lpc_dev *lpcdev;
+       resource_size_t io_end;
+       struct resource *res;
+       int ret;
+
+       lpcdev = devm_kzalloc(dev, sizeof(*lpcdev), GFP_KERNEL);
+       if (!lpcdev)
+               return -ENOMEM;
+
+       spin_lock_init(&lpcdev->cycle_lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       lpcdev->membase = devm_ioremap_resource(dev, res);
+       if (IS_ERR(lpcdev->membase))
+               return PTR_ERR(lpcdev->membase);
+
+       range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL);
+       if (!range)
+               return -ENOMEM;
+
+       range->fwnode = dev->fwnode;
+       range->flags = LOGIC_PIO_INDIRECT;
+       range->size = PIO_INDIRECT_SIZE;
+
+       ret = logic_pio_register_range(range);
+       if (ret) {
+               dev_err(dev, "register IO range failed (%d)!\n", ret);
+               return ret;
+       }
+       lpcdev->io_host = range;
+
+       /* register the LPC host PIO resources */
+       if (acpi_device)
+               ret = hisi_lpc_acpi_probe(dev);
+       else
+               ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+       if (ret)
+               return ret;
+
+       lpcdev->io_host->hostdata = lpcdev;
+       lpcdev->io_host->ops = &hisi_lpc_ops;
+
+       io_end = lpcdev->io_host->io_start + lpcdev->io_host->size;
+       dev_info(dev, "registered range [%pa - %pa]\n",
+                &lpcdev->io_host->io_start, &io_end);
+
+       return ret;
+}
+
+static const struct of_device_id hisi_lpc_of_match[] = {
+       { .compatible = "hisilicon,hip06-lpc", },
+       { .compatible = "hisilicon,hip07-lpc", },
+       {}
+};
+
+static struct platform_driver hisi_lpc_driver = {
+       .driver = {
+               .name           = DRV_NAME,
+               .of_match_table = hisi_lpc_of_match,
+               .acpi_match_table = ACPI_PTR(hisi_lpc_acpi_match),
+       },
+       .probe = hisi_lpc_probe,
+};
+builtin_platform_driver(hisi_lpc_driver);
index dff2d153816471a6eef60b02dc4d1aa136314d7d..05e5324f60bd951f8182b0ce77213df6e6a1151d 100644 (file)
@@ -24,7 +24,6 @@ MODULE_LICENSE("GPL v2");
 
 #define PCI_DEVICE_ID_XILLYBUS         0xebeb
 
-#define PCI_VENDOR_ID_ALTERA           0x1172
 #define PCI_VENDOR_ID_ACTEL            0x11aa
 #define PCI_VENDOR_ID_LATTICE          0x1204
 
index 00e73d28077cee3732b61973222422f152ebb033..77b04e4b32544a3fd7359537c1e0acbe3ccf0f84 100644 (file)
@@ -384,8 +384,6 @@ static int altera_cvp_probe(struct pci_dev *pdev,
                            const struct pci_device_id *dev_id);
 static void altera_cvp_remove(struct pci_dev *pdev);
 
-#define PCI_VENDOR_ID_ALTERA   0x1172
-
 static struct pci_device_id altera_cvp_id_tbl[] = {
        { PCI_VDEVICE(ALTERA, PCI_ANY_ID) },
        { }
index 173d0095e3b2120e2bd2f4090cbe7da1e25c6619..ca17508fd28c065548ce650007496d57b7aae045 100644 (file)
@@ -434,7 +434,10 @@ static int i915_getparam(struct drm_device *dev, void *data,
 
 static int i915_get_bridge_dev(struct drm_i915_private *dev_priv)
 {
-       dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
+       int domain = pci_domain_nr(dev_priv->drm.pdev->bus);
+
+       dev_priv->bridge_dev =
+               pci_get_domain_bus_and_slot(domain, 0, PCI_DEVFN(0, 0));
        if (!dev_priv->bridge_dev) {
                DRM_ERROR("bridge device not found\n");
                return -1;
index af4d2f26f1c620a9cd0cd600739fd22e2aaee16a..c2d69e33bf2bfc1d16780f90802bf4695f699013 100644 (file)
@@ -117,6 +117,7 @@ static void mcb_pci_remove(struct pci_dev *pdev)
 
 static const struct pci_device_id mcb_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_MEN, PCI_DEVICE_ID_MEN_CHAMELEON) },
+       { PCI_DEVICE(PCI_VENDOR_ID_ALTERA, PCI_DEVICE_ID_MEN_CHAMELEON) },
        { 0 },
 };
 MODULE_DEVICE_TABLE(pci, mcb_pci_tbl);
index a434fecfdfeb6aaf74719d0182d89c5008efcd02..aa05fb534942d618183f16b8ba27291d18f224cd 100644 (file)
@@ -2120,91 +2120,6 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
        return 0;
 }
 
-static void fm10k_slot_warn(struct fm10k_intfc *interface)
-{
-       enum pcie_link_width width = PCIE_LNK_WIDTH_UNKNOWN;
-       enum pci_bus_speed speed = PCI_SPEED_UNKNOWN;
-       struct fm10k_hw *hw = &interface->hw;
-       int max_gts = 0, expected_gts = 0;
-
-       if (pcie_get_minimum_link(interface->pdev, &speed, &width) ||
-           speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) {
-               dev_warn(&interface->pdev->dev,
-                        "Unable to determine PCI Express bandwidth.\n");
-               return;
-       }
-
-       switch (speed) {
-       case PCIE_SPEED_2_5GT:
-               /* 8b/10b encoding reduces max throughput by 20% */
-               max_gts = 2 * width;
-               break;
-       case PCIE_SPEED_5_0GT:
-               /* 8b/10b encoding reduces max throughput by 20% */
-               max_gts = 4 * width;
-               break;
-       case PCIE_SPEED_8_0GT:
-               /* 128b/130b encoding has less than 2% impact on throughput */
-               max_gts = 8 * width;
-               break;
-       default:
-               dev_warn(&interface->pdev->dev,
-                        "Unable to determine PCI Express bandwidth.\n");
-               return;
-       }
-
-       dev_info(&interface->pdev->dev,
-                "PCI Express bandwidth of %dGT/s available\n",
-                max_gts);
-       dev_info(&interface->pdev->dev,
-                "(Speed:%s, Width: x%d, Encoding Loss:%s, Payload:%s)\n",
-                (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" :
-                 speed == PCIE_SPEED_5_0GT ? "5.0GT/s" :
-                 speed == PCIE_SPEED_2_5GT ? "2.5GT/s" :
-                 "Unknown"),
-                hw->bus.width,
-                (speed == PCIE_SPEED_2_5GT ? "20%" :
-                 speed == PCIE_SPEED_5_0GT ? "20%" :
-                 speed == PCIE_SPEED_8_0GT ? "<2%" :
-                 "Unknown"),
-                (hw->bus.payload == fm10k_bus_payload_128 ? "128B" :
-                 hw->bus.payload == fm10k_bus_payload_256 ? "256B" :
-                 hw->bus.payload == fm10k_bus_payload_512 ? "512B" :
-                 "Unknown"));
-
-       switch (hw->bus_caps.speed) {
-       case fm10k_bus_speed_2500:
-               /* 8b/10b encoding reduces max throughput by 20% */
-               expected_gts = 2 * hw->bus_caps.width;
-               break;
-       case fm10k_bus_speed_5000:
-               /* 8b/10b encoding reduces max throughput by 20% */
-               expected_gts = 4 * hw->bus_caps.width;
-               break;
-       case fm10k_bus_speed_8000:
-               /* 128b/130b encoding has less than 2% impact on throughput */
-               expected_gts = 8 * hw->bus_caps.width;
-               break;
-       default:
-               dev_warn(&interface->pdev->dev,
-                        "Unable to determine expected PCI Express bandwidth.\n");
-               return;
-       }
-
-       if (max_gts >= expected_gts)
-               return;
-
-       dev_warn(&interface->pdev->dev,
-                "This device requires %dGT/s of bandwidth for optimal performance.\n",
-                expected_gts);
-       dev_warn(&interface->pdev->dev,
-                "A %sslot with x%d lanes is suggested.\n",
-                (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s " :
-                 hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s " :
-                 hw->bus_caps.speed == fm10k_bus_speed_8000 ? "8.0GT/s " : ""),
-                hw->bus_caps.width);
-}
-
 /**
  * fm10k_probe - Device Initialization Routine
  * @pdev: PCI device information struct
@@ -2326,7 +2241,7 @@ static int fm10k_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        mod_timer(&interface->service_timer, (HZ * 2) + jiffies);
 
        /* print warning for non-optimal configurations */
-       fm10k_slot_warn(interface);
+       pcie_print_link_status(interface->pdev);
 
        /* report MAC address for logging */
        dev_info(&pdev->dev, "%pM\n", netdev->dev_addr);
index 4d84cab77105f30bbad0a2034ba12bf8ffc1544a..30cacac54e6907e83ebbd2faa79637ab5fd1f0e3 100644 (file)
@@ -623,85 +623,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
        return 0;
 }
 
-static int mlx4_get_pcie_dev_link_caps(struct mlx4_dev *dev,
-                                      enum pci_bus_speed *speed,
-                                      enum pcie_link_width *width)
-{
-       u32 lnkcap1, lnkcap2;
-       int err1, err2;
-
-#define  PCIE_MLW_CAP_SHIFT 4  /* start of MLW mask in link capabilities */
-
-       *speed = PCI_SPEED_UNKNOWN;
-       *width = PCIE_LNK_WIDTH_UNKNOWN;
-
-       err1 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP,
-                                         &lnkcap1);
-       err2 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP2,
-                                         &lnkcap2);
-       if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */
-               if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
-                       *speed = PCIE_SPEED_8_0GT;
-               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
-                       *speed = PCIE_SPEED_5_0GT;
-               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
-                       *speed = PCIE_SPEED_2_5GT;
-       }
-       if (!err1) {
-               *width = (lnkcap1 & PCI_EXP_LNKCAP_MLW) >> PCIE_MLW_CAP_SHIFT;
-               if (!lnkcap2) { /* pre-r3.0 */
-                       if (lnkcap1 & PCI_EXP_LNKCAP_SLS_5_0GB)
-                               *speed = PCIE_SPEED_5_0GT;
-                       else if (lnkcap1 & PCI_EXP_LNKCAP_SLS_2_5GB)
-                               *speed = PCIE_SPEED_2_5GT;
-               }
-       }
-
-       if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN) {
-               return err1 ? err1 :
-                       err2 ? err2 : -EINVAL;
-       }
-       return 0;
-}
-
-static void mlx4_check_pcie_caps(struct mlx4_dev *dev)
-{
-       enum pcie_link_width width, width_cap;
-       enum pci_bus_speed speed, speed_cap;
-       int err;
-
-#define PCIE_SPEED_STR(speed) \
-       (speed == PCIE_SPEED_8_0GT ? "8.0GT/s" : \
-        speed == PCIE_SPEED_5_0GT ? "5.0GT/s" : \
-        speed == PCIE_SPEED_2_5GT ? "2.5GT/s" : \
-        "Unknown")
-
-       err = mlx4_get_pcie_dev_link_caps(dev, &speed_cap, &width_cap);
-       if (err) {
-               mlx4_warn(dev,
-                         "Unable to determine PCIe device BW capabilities\n");
-               return;
-       }
-
-       err = pcie_get_minimum_link(dev->persist->pdev, &speed, &width);
-       if (err || speed == PCI_SPEED_UNKNOWN ||
-           width == PCIE_LNK_WIDTH_UNKNOWN) {
-               mlx4_warn(dev,
-                         "Unable to determine PCI device chain minimum BW\n");
-               return;
-       }
-
-       if (width != width_cap || speed != speed_cap)
-               mlx4_warn(dev,
-                         "PCIe BW is different than device's capability\n");
-
-       mlx4_info(dev, "PCIe link speed is %s, device supports %s\n",
-                 PCIE_SPEED_STR(speed), PCIE_SPEED_STR(speed_cap));
-       mlx4_info(dev, "PCIe link width is x%d, device supports x%d\n",
-                 width, width_cap);
-       return;
-}
-
 /*The function checks if there are live vf, return the num of them*/
 static int mlx4_how_many_lives_vf(struct mlx4_dev *dev)
 {
@@ -3475,7 +3396,7 @@ slave_start:
         * express device capabilities are under-satisfied by the bus.
         */
        if (!mlx4_is_slave(dev))
-               mlx4_check_pcie_caps(dev);
+               pcie_print_link_status(dev->persist->pdev);
 
        /* In master functions, the communication channel must be initialized
         * after obtaining its address from fw */
index 47bab842c5eea8656a68002440d616c7c94c4611..93291ec4a3d1f5df660ad37a387f689beddbd5bd 100644 (file)
@@ -3864,36 +3864,6 @@ void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len,
                indirection_rqt[i] = i % num_channels;
 }
 
-static int mlx5e_get_pci_bw(struct mlx5_core_dev *mdev, u32 *pci_bw)
-{
-       enum pcie_link_width width;
-       enum pci_bus_speed speed;
-       int err = 0;
-
-       err = pcie_get_minimum_link(mdev->pdev, &speed, &width);
-       if (err)
-               return err;
-
-       if (speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN)
-               return -EINVAL;
-
-       switch (speed) {
-       case PCIE_SPEED_2_5GT:
-               *pci_bw = 2500 * width;
-               break;
-       case PCIE_SPEED_5_0GT:
-               *pci_bw = 5000 * width;
-               break;
-       case PCIE_SPEED_8_0GT:
-               *pci_bw = 8000 * width;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 static bool cqe_compress_heuristic(u32 link_speed, u32 pci_bw)
 {
        return (link_speed && pci_bw &&
@@ -3979,7 +3949,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev,
        params->num_tc       = 1;
 
        mlx5e_get_max_linkspeed(mdev, &link_speed);
-       mlx5e_get_pci_bw(mdev, &pci_bw);
+       pci_bw = pcie_bandwidth_available(mdev->pdev, NULL, NULL, NULL);
        mlx5_core_dbg(mdev, "Max link speed = %d, PCI BW = %d\n",
                      link_speed, pci_bw);
 
index 2ef641c91c267ceec1cacb744f492ef2a1ff8dc0..622f02d34aaea8d94b3df5ec4c3bbb095a035d42 100644 (file)
@@ -1043,6 +1043,10 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv,
        dev_info(&pdev->dev, "firmware version: %d.%d.%d\n", fw_rev_maj(dev),
                 fw_rev_min(dev), fw_rev_sub(dev));
 
+       /* Only PFs hold the relevant PCIe information for this query */
+       if (mlx5_core_is_pf(dev))
+               pcie_print_link_status(dev->pdev);
+
        /* on load removing any previous indication of internal error, device is
         * up
         */
index ce4d3d8b85de4128b81aeebfa4ee155139135cd3..53349912ac753aabd7981a3064305a6f6c4b63aa 100644 (file)
@@ -2,8 +2,10 @@
 #define pr_fmt(fmt)    "OF: " fmt
 
 #include <linux/device.h>
+#include <linux/fwnode.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
+#include <linux/logic_pio.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/pci.h>
@@ -333,7 +335,8 @@ int of_pci_range_to_resource(struct of_pci_range *range,
 
        if (res->flags & IORESOURCE_IO) {
                unsigned long port;
-               err = pci_register_io_range(range->cpu_addr, range->size);
+               err = pci_register_io_range(&np->fwnode, range->cpu_addr,
+                               range->size);
                if (err)
                        goto invalid_range;
                port = pci_address_to_pio(range->cpu_addr);
@@ -560,9 +563,14 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
  * that translation is impossible (that is we are not dealing with a value
  * that can be mapped to a cpu physical address). This is not really specified
  * that way, but this is traditionally the way IBM at least do things
+ *
+ * Whenever the translation fails, the *host pointer will be set to the
+ * device that had registered logical PIO mapping, and the return code is
+ * relative to that node.
  */
 static u64 __of_translate_address(struct device_node *dev,
-                                 const __be32 *in_addr, const char *rprop)
+                                 const __be32 *in_addr, const char *rprop,
+                                 struct device_node **host)
 {
        struct device_node *parent = NULL;
        struct of_bus *bus, *pbus;
@@ -575,6 +583,7 @@ static u64 __of_translate_address(struct device_node *dev,
        /* Increase refcount at current level */
        of_node_get(dev);
 
+       *host = NULL;
        /* Get parent & match bus type */
        parent = of_get_parent(dev);
        if (parent == NULL)
@@ -595,6 +604,8 @@ static u64 __of_translate_address(struct device_node *dev,
 
        /* Translate */
        for (;;) {
+               struct logic_pio_hwaddr *iorange;
+
                /* Switch to parent bus */
                of_node_put(dev);
                dev = parent;
@@ -607,6 +618,19 @@ static u64 __of_translate_address(struct device_node *dev,
                        break;
                }
 
+               /*
+                * For indirectIO device which has no ranges property, get
+                * the address from reg directly.
+                */
+               iorange = find_io_range_by_fwnode(&dev->fwnode);
+               if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) {
+                       result = of_read_number(addr + 1, na - 1);
+                       pr_debug("indirectIO matched(%pOF) 0x%llx\n",
+                                dev, result);
+                       *host = of_node_get(dev);
+                       break;
+               }
+
                /* Get new parent bus and counts */
                pbus = of_match_bus(parent);
                pbus->count_cells(dev, &pna, &pns);
@@ -638,13 +662,32 @@ static u64 __of_translate_address(struct device_node *dev,
 
 u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
 {
-       return __of_translate_address(dev, in_addr, "ranges");
+       struct device_node *host;
+       u64 ret;
+
+       ret = __of_translate_address(dev, in_addr, "ranges", &host);
+       if (host) {
+               of_node_put(host);
+               return OF_BAD_ADDR;
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL(of_translate_address);
 
 u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
 {
-       return __of_translate_address(dev, in_addr, "dma-ranges");
+       struct device_node *host;
+       u64 ret;
+
+       ret = __of_translate_address(dev, in_addr, "dma-ranges", &host);
+
+       if (host) {
+               of_node_put(host);
+               return OF_BAD_ADDR;
+       }
+
+       return ret;
 }
 EXPORT_SYMBOL(of_translate_dma_address);
 
@@ -686,29 +729,48 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 }
 EXPORT_SYMBOL(of_get_address);
 
+static u64 of_translate_ioport(struct device_node *dev, const __be32 *in_addr,
+                       u64 size)
+{
+       u64 taddr;
+       unsigned long port;
+       struct device_node *host;
+
+       taddr = __of_translate_address(dev, in_addr, "ranges", &host);
+       if (host) {
+               /* host-specific port access */
+               port = logic_pio_trans_hwaddr(&host->fwnode, taddr, size);
+               of_node_put(host);
+       } else {
+               /* memory-mapped I/O range */
+               port = pci_address_to_pio(taddr);
+       }
+
+       if (port == (unsigned long)-1)
+               return OF_BAD_ADDR;
+
+       return port;
+}
+
 static int __of_address_to_resource(struct device_node *dev,
                const __be32 *addrp, u64 size, unsigned int flags,
                const char *name, struct resource *r)
 {
        u64 taddr;
 
-       if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
+       if (flags & IORESOURCE_MEM)
+               taddr = of_translate_address(dev, addrp);
+       else if (flags & IORESOURCE_IO)
+               taddr = of_translate_ioport(dev, addrp, size);
+       else
                return -EINVAL;
-       taddr = of_translate_address(dev, addrp);
+
        if (taddr == OF_BAD_ADDR)
                return -EINVAL;
        memset(r, 0, sizeof(struct resource));
-       if (flags & IORESOURCE_IO) {
-               unsigned long port;
-               port = pci_address_to_pio(taddr);
-               if (port == (unsigned long)-1)
-                       return -EINVAL;
-               r->start = port;
-               r->end = port + size - 1;
-       } else {
-               r->start = taddr;
-               r->end = taddr + size - 1;
-       }
+
+       r->start = taddr;
+       r->end = taddr + size - 1;
        r->flags = flags;
        r->name = name ? name : dev->full_name;
 
index 941970936840eadc9c14abcb41fd4627907ff8ba..952addc7bacfd985c886962772ac92bd888ccb80 100644 (file)
@@ -1,61 +1,40 @@
 # SPDX-License-Identifier: GPL-2.0
 #
 # Makefile for the PCI bus specific drivers.
-#
 
-obj-$(CONFIG_PCI)      += access.o bus.o probe.o host-bridge.o remove.o pci.o \
-                       pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
-                       irq.o vpd.o setup-bus.o vc.o mmap.o setup-irq.o
+obj-$(CONFIG_PCI)              += access.o bus.o probe.o host-bridge.o \
+                                  remove.o pci.o pci-driver.o search.o \
+                                  pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
+                                  setup-bus.o vc.o mmap.o setup-irq.o
 
 ifdef CONFIG_PCI
-obj-$(CONFIG_PROC_FS) += proc.o
-obj-$(CONFIG_SYSFS) += slot.o
-obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_PROC_FS)          += proc.o
+obj-$(CONFIG_SYSFS)            += slot.o
+obj-$(CONFIG_OF)               += of.o
 endif
 
-obj-$(CONFIG_PCI_QUIRKS) += quirks.o
-
-# Build PCI Express stuff if needed
-obj-$(CONFIG_PCIEPORTBUS) += pcie/
-
-# Build the PCI Hotplug drivers if we were asked to
-obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
-
-# Build the PCI MSI interrupt support
-obj-$(CONFIG_PCI_MSI) += msi.o
-
-obj-$(CONFIG_PCI_ATS) += ats.o
-obj-$(CONFIG_PCI_IOV) += iov.o
-
-#
-# ACPI Related PCI FW Functions
-# ACPI _DSM provided firmware instance and string name
-#
-obj-$(CONFIG_ACPI)    += pci-acpi.o
-
-# SMBIOS provided firmware instance and labels
-obj-$(CONFIG_PCI_LABEL) += pci-label.o
-
-# Intel MID platform PM support
-obj-$(CONFIG_X86_INTEL_MID) += pci-mid.o
-
-obj-$(CONFIG_PCI_SYSCALL) += syscall.o
-
-obj-$(CONFIG_PCI_STUB) += pci-stub.o
-
-obj-$(CONFIG_PCI_ECAM) += ecam.o
-
+obj-$(CONFIG_PCI_QUIRKS)       += quirks.o
+obj-$(CONFIG_PCIEPORTBUS)      += pcie/
+obj-$(CONFIG_HOTPLUG_PCI)      += hotplug/
+obj-$(CONFIG_PCI_MSI)          += msi.o
+obj-$(CONFIG_PCI_ATS)          += ats.o
+obj-$(CONFIG_PCI_IOV)          += iov.o
+obj-$(CONFIG_ACPI)             += pci-acpi.o
+obj-$(CONFIG_PCI_LABEL)                += pci-label.o
+obj-$(CONFIG_X86_INTEL_MID)    += pci-mid.o
+obj-$(CONFIG_PCI_SYSCALL)      += syscall.o
+obj-$(CONFIG_PCI_STUB)         += pci-stub.o
+obj-$(CONFIG_PCI_ECAM)         += ecam.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 
-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
-
-# PCI host controller drivers
-obj-y += host/
-obj-y += switch/
+obj-y                          += host/
+obj-y                          += switch/
 
+# Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)     += endpoint/
 
-# Endpoint library must be initialized before its users
 obj-$(CONFIG_PCIE_CADENCE)     += cadence/
 # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
 obj-y                          += dwc/
+
+ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
index e080eb74bda0424a0f62dcc031fc99b718ed1824..a3ad2fe185b9c517923fc0a26897af0e289dcdd6 100644 (file)
@@ -15,9 +15,9 @@
 DEFINE_RAW_SPINLOCK(pci_lock);
 
 /*
- *  Wrappers for all PCI configuration access functions.  They just check
- *  alignment, do locking and call the low-level functions pointed to
- *  by pci_dev->ops.
+ * Wrappers for all PCI configuration access functions.  They just check
+ * alignment, do locking and call the low-level functions pointed to
+ * by pci_dev->ops.
  */
 
 #define PCI_byte_BAD 0
@@ -318,8 +318,10 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
 
        raw_spin_lock_irqsave(&pci_lock, flags);
 
-       /* This indicates a problem in the caller, but we don't need
-        * to kill them, unlike a double-block above. */
+       /*
+        * This indicates a problem in the caller, but we don't need
+        * to kill them, unlike a double-block above.
+        */
        WARN_ON(!dev->block_cfg_access);
 
        dev->block_cfg_access = 0;
index 6ad80a1fd5a7a6098ac41b3dbd38bea427dc7839..89305b569d3d8899e1ea093474010b73e6468669 100644 (file)
@@ -1,14 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/ats.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- * Copyright (C) 2011 Advanced Micro Devices,
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
  *   Address Translation Service 1.0
  *   Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com>
  *   PASID support added by Joerg Roedel <joerg.roedel@amd.com>
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
+ * Copyright (C) 2011 Advanced Micro Devices,
  */
 
 #include <linux/export.h>
index 737d1c52f002d605fafa97f11828ef09e931c1aa..bc2ded4c451f95508daaf7426ff9cdf169f58899 100644 (file)
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     drivers/pci/bus.c
- *
  * From setup-res.c, by:
  *     Dave Rusling (david.rusling@reo.mts.dec.com)
  *     David Mosberger (davidm@cs.arizona.edu)
index ac8d812682962dd4ce23075df550395b36356c90..e01d53f5b32f6f99cda5d3753cbb3d3fba53fe03 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * host bridge related code
+ * Host bridge related code
  */
 
 #include <linux/kernel.h>
index e2198a2feeca8a5c3cd49ca21918ed17cab95fe6..b45b375c0e6c0011e45f010dc492069d2ead8265 100644 (file)
@@ -541,6 +541,7 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
 {
        unsigned long long sta = 0;
        struct acpiphp_func *func;
+       u32 dvid;
 
        list_for_each_entry(func, &slot->funcs, sibling) {
                if (func->flags & FUNC_HAS_STA) {
@@ -551,19 +552,27 @@ static unsigned int get_slot_status(struct acpiphp_slot *slot)
                        if (ACPI_SUCCESS(status) && sta)
                                break;
                } else {
-                       u32 dvid;
-
-                       pci_bus_read_config_dword(slot->bus,
-                                                 PCI_DEVFN(slot->device,
-                                                           func->function),
-                                                 PCI_VENDOR_ID, &dvid);
-                       if (dvid != 0xffffffff) {
+                       if (pci_bus_read_dev_vendor_id(slot->bus,
+                                       PCI_DEVFN(slot->device, func->function),
+                                       &dvid, 0)) {
                                sta = ACPI_STA_ALL;
                                break;
                        }
                }
        }
 
+       if (!sta) {
+               /*
+                * Check for the slot itself since it may be that the
+                * ACPI slot is a device below PCIe upstream port so in
+                * that case it may not even be reachable yet.
+                */
+               if (pci_bus_read_dev_vendor_id(slot->bus,
+                               PCI_DEVFN(slot->device, 0), &dvid, 0)) {
+                       sta = ACPI_STA_ALL;
+               }
+       }
+
        return (unsigned int)sta;
 }
 
index b1b6e45253b2ba0a8fa0c8476b321913bd216106..616df442520b4b69cb929ece971e54da0b8de806 100644 (file)
@@ -2812,18 +2812,16 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
 
                                        dbg("CND:      length = 0x%x\n", base);
                                        io_node = get_io_resource(&(resources->io_head), base);
+                                       if (!io_node)
+                                               return -ENOMEM;
                                        dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
                                            io_node->base, io_node->length, io_node->next);
                                        dbg("func (%p) io_head (%p)\n", func, func->io_head);
 
                                        /* allocate the resource to the board */
-                                       if (io_node) {
-                                               base = io_node->base;
-
-                                               io_node->next = func->io_head;
-                                               func->io_head = io_node;
-                                       } else
-                                               return -ENOMEM;
+                                       base = io_node->base;
+                                       io_node->next = func->io_head;
+                                       func->io_head = io_node;
                                } else if ((temp_register & 0x0BL) == 0x08) {
                                        /* Map prefetchable memory */
                                        base = temp_register & 0xFFFFFFF0;
index 636ed8f4b869f6688893072927d5e1632c556625..88e917c9120fd2a42f05c0a26434017e8bfc2913 100644 (file)
 #include <linux/pci_hotplug.h>
 #include <linux/delay.h>
 #include <linux/sched/signal.h>                /* signal_pending() */
-#include <linux/pcieport_if.h>
 #include <linux/mutex.h>
 #include <linux/workqueue.h>
 
+#include "../pcie/portdrv.h"
+
 #define MY_NAME        "pciehp"
 
 extern bool pciehp_poll_mode;
index 677924ae03503e57ae7258cba3b2063316757a74..8adf4a64f2919ad5f50f13365564b6d11917f08f 100644 (file)
@@ -1,12 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/iov.c
- *
- * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
- *
- * PCI Express I/O Virtualization (IOV) support.
+ * PCI Express I/O Virtualization (IOV) support
  *   Single Root IOV 1.0
  *   Address Translation Service 1.0
+ *
+ * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com>
  */
 
 #include <linux/pci.h>
@@ -114,6 +112,29 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
        return dev->sriov->barsz[resno - PCI_IOV_RESOURCES];
 }
 
+static void pci_read_vf_config_common(struct pci_dev *virtfn)
+{
+       struct pci_dev *physfn = virtfn->physfn;
+
+       /*
+        * Some config registers are the same across all associated VFs.
+        * Read them once from VF0 so we can skip reading them from the
+        * other VFs.
+        *
+        * PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
+        * have the same Revision ID and Subsystem ID, but we assume they
+        * do.
+        */
+       pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
+                             &physfn->sriov->class);
+       pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
+                            &physfn->sriov->hdr_type);
+       pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
+                            &physfn->sriov->subsystem_vendor);
+       pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
+                            &physfn->sriov->subsystem_device);
+}
+
 int pci_iov_add_virtfn(struct pci_dev *dev, int id)
 {
        int i;
@@ -136,13 +157,17 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
        virtfn->devfn = pci_iov_virtfn_devfn(dev, id);
        virtfn->vendor = dev->vendor;
        virtfn->device = iov->vf_device;
+       virtfn->is_virtfn = 1;
+       virtfn->physfn = pci_dev_get(dev);
+
+       if (id == 0)
+               pci_read_vf_config_common(virtfn);
+
        rc = pci_setup_device(virtfn);
        if (rc)
-               goto failed0;
+               goto failed1;
 
        virtfn->dev.parent = dev->dev.parent;
-       virtfn->physfn = pci_dev_get(dev);
-       virtfn->is_virtfn = 1;
        virtfn->multifunction = 0;
 
        for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
@@ -163,10 +188,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
        sprintf(buf, "virtfn%u", id);
        rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
        if (rc)
-               goto failed1;
+               goto failed2;
        rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
        if (rc)
-               goto failed2;
+               goto failed3;
 
        kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
 
@@ -174,11 +199,12 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
 
        return 0;
 
-failed2:
+failed3:
        sysfs_remove_link(&dev->dev.kobj, buf);
+failed2:
+       pci_stop_and_remove_bus_device(virtfn);
 failed1:
        pci_dev_put(dev);
-       pci_stop_and_remove_bus_device(virtfn);
 failed0:
        virtfn_remove_bus(dev->bus, bus);
 failed:
index 814a3ce341fcd116a93a5fa6fa4a12dfdfc0fbce..24505b08de4023546bea2a5890f6fc752817e4b8 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * mmap.c — generic PCI resource mmap helper
+ * Generic PCI resource mmap helper
  *
  * Copyright © 2017 Amazon.com, Inc. or its affiliates.
  *
index 8b0729c94bb739d66796e8c5c8e2b8eab0560dcb..30250631efe791fde3a9dfcf5afbe240335b5bbd 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       msi.c
- * Purpose:    PCI Message Signaled Interrupt (MSI)
+ * PCI Message Signaled Interrupt (MSI)
  *
  * Copyright (C) 2003-2004 Intel
  * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
index 78157688dcc9bd57d435a2ea55869d895961b279..1abdbf267c1965a79b535f7cdb7228ba8db542a2 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       pci-acpi.c
- * Purpose:    Provide PCI support in ACPI
+ * PCI support in ACPI
  *
  * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
  * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
index 3bed6beda0514925be66bc6996dd91f9dabf1bb0..28cf87a7ff4cedfb0756e73f855c17494914bdba 100644 (file)
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/pci-driver.c
- *
  * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
  * (C) Copyright 2007 Novell Inc.
  */
@@ -19,6 +17,7 @@
 #include <linux/suspend.h>
 #include <linux/kexec.h>
 #include "pci.h"
+#include "pcie/portdrv.h"
 
 struct pci_dynid {
        struct list_head node;
@@ -714,6 +713,18 @@ static void pci_pm_complete(struct device *dev)
 #endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_SUSPEND
+static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
+{
+       /*
+        * Some BIOSes forget to clear Root PME Status bits after system
+        * wakeup, which breaks ACPI-based runtime wakeup on PCI Express.
+        * Clear those bits now just in case (shouldn't hurt).
+        */
+       if (pci_is_pcie(pci_dev) &&
+           (pci_pcie_type(pci_dev) == PCI_EXP_TYPE_ROOT_PORT ||
+            pci_pcie_type(pci_dev) == PCI_EXP_TYPE_RC_EC))
+               pcie_clear_root_pme_status(pci_dev);
+}
 
 static int pci_pm_suspend(struct device *dev)
 {
@@ -873,6 +884,8 @@ static int pci_pm_resume_noirq(struct device *dev)
        if (pci_has_legacy_pm_support(pci_dev))
                return pci_legacy_resume_early(dev);
 
+       pcie_pme_root_status_cleanup(pci_dev);
+
        if (drv && drv->pm && drv->pm->resume_noirq)
                error = drv->pm->resume_noirq(dev);
 
@@ -1517,6 +1530,42 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
+#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+/**
+ * pci_uevent_ers - emit a uevent during recovery path of PCI device
+ * @pdev: PCI device undergoing error recovery
+ * @err_type: type of error event
+ */
+void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type)
+{
+       int idx = 0;
+       char *envp[3];
+
+       switch (err_type) {
+       case PCI_ERS_RESULT_NONE:
+       case PCI_ERS_RESULT_CAN_RECOVER:
+               envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY";
+               envp[idx++] = "DEVICE_ONLINE=0";
+               break;
+       case PCI_ERS_RESULT_RECOVERED:
+               envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY";
+               envp[idx++] = "DEVICE_ONLINE=1";
+               break;
+       case PCI_ERS_RESULT_DISCONNECT:
+               envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY";
+               envp[idx++] = "DEVICE_ONLINE=0";
+               break;
+       default:
+               break;
+       }
+
+       if (idx > 0) {
+               envp[idx++] = NULL;
+               kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
+       }
+}
+#endif
+
 static int pci_bus_num_vf(struct device *dev)
 {
        return pci_num_vf(to_pci_dev(dev));
@@ -1538,8 +1587,49 @@ struct bus_type pci_bus_type = {
 };
 EXPORT_SYMBOL(pci_bus_type);
 
+#ifdef CONFIG_PCIEPORTBUS
+static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct pcie_device *pciedev;
+       struct pcie_port_service_driver *driver;
+
+       if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
+               return 0;
+
+       pciedev = to_pcie_device(dev);
+       driver = to_service_driver(drv);
+
+       if (driver->service != pciedev->service)
+               return 0;
+
+       if (driver->port_type != PCIE_ANY_PORT &&
+           driver->port_type != pci_pcie_type(pciedev->port))
+               return 0;
+
+       return 1;
+}
+
+struct bus_type pcie_port_bus_type = {
+       .name           = "pci_express",
+       .match          = pcie_port_bus_match,
+};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
+#endif
+
 static int __init pci_driver_init(void)
 {
-       return bus_register(&pci_bus_type);
+       int ret;
+
+       ret = bus_register(&pci_bus_type);
+       if (ret)
+               return ret;
+
+#ifdef CONFIG_PCIEPORTBUS
+       ret = bus_register(&pcie_port_bus_type);
+       if (ret)
+               return ret;
+#endif
+
+       return 0;
 }
 postcore_initcall(pci_driver_init);
index a961a71d950fb604133f0f877ab60cce7aff110b..a5910f9428576672d0127c836e9d08019c86153e 100644 (file)
@@ -1,7 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Purpose: Export the firmware instance and label associated with
- * a pci device to sysfs
+ * Export the firmware instance and label associated with a PCI device to
+ * sysfs
+ *
  * Copyright (C) 2010 Dell Inc.
  * by Narendra K <Narendra_K@dell.com>,
  * Jordan Hargrave <Jordan_Hargrave@dell.com>
index 10d54f93904868f2a0a4471ad976937b425c4717..66f8a59fadbd90496196f0e6ba4eef3baeee75be 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
-/* pci-stub - simple stub driver to reserve a pci device
+/*
+ * Simple stub driver to reserve a PCI device
  *
  * Copyright (C) 2008 Red Hat, Inc.
  * Author:
index 4415e624cf7e3f5a4f82970477bb335a25bb2a0d..366d93af051d45f174fe96af4e6c1c3f106f678d 100644 (file)
@@ -1,7 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/pci-sysfs.c
- *
  * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
  * (C) Copyright 2002-2004 IBM Corp.
  * (C) Copyright 2003 Matthew Wilcox
@@ -12,7 +10,6 @@
  * File attributes for PCI devices
  *
  * Modeled after usb's driverfs.c
- *
  */
 
 
@@ -158,45 +155,18 @@ static DEVICE_ATTR_RO(resource);
 static ssize_t max_link_speed_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct pci_dev *pci_dev = to_pci_dev(dev);
-       u32 linkcap;
-       int err;
-       const char *speed;
-
-       err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
-       if (err)
-               return -EINVAL;
-
-       switch (linkcap & PCI_EXP_LNKCAP_SLS) {
-       case PCI_EXP_LNKCAP_SLS_8_0GB:
-               speed = "8 GT/s";
-               break;
-       case PCI_EXP_LNKCAP_SLS_5_0GB:
-               speed = "5 GT/s";
-               break;
-       case PCI_EXP_LNKCAP_SLS_2_5GB:
-               speed = "2.5 GT/s";
-               break;
-       default:
-               speed = "Unknown speed";
-       }
+       struct pci_dev *pdev = to_pci_dev(dev);
 
-       return sprintf(buf, "%s\n", speed);
+       return sprintf(buf, "%s\n", PCIE_SPEED2STR(pcie_get_speed_cap(pdev)));
 }
 static DEVICE_ATTR_RO(max_link_speed);
 
 static ssize_t max_link_width_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       struct pci_dev *pci_dev = to_pci_dev(dev);
-       u32 linkcap;
-       int err;
-
-       err = pcie_capability_read_dword(pci_dev, PCI_EXP_LNKCAP, &linkcap);
-       if (err)
-               return -EINVAL;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
-       return sprintf(buf, "%u\n", (linkcap & PCI_EXP_LNKCAP_MLW) >> 4);
+       return sprintf(buf, "%u\n", pcie_get_width_cap(pdev));
 }
 static DEVICE_ATTR_RO(max_link_width);
 
@@ -213,6 +183,9 @@ static ssize_t current_link_speed_show(struct device *dev,
                return -EINVAL;
 
        switch (linkstat & PCI_EXP_LNKSTA_CLS) {
+       case PCI_EXP_LNKSTA_CLS_16_0GB:
+               speed = "16 GT/s";
+               break;
        case PCI_EXP_LNKSTA_CLS_8_0GB:
                speed = "8 GT/s";
                break;
@@ -1489,11 +1462,10 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev)
        pcie_vpd_create_sysfs_dev_files(dev);
        pcie_aspm_create_sysfs_dev_files(dev);
 
-       if (!pci_probe_reset_function(dev)) {
+       if (dev->reset_fn) {
                retval = device_create_file(&dev->dev, &reset_attr);
                if (retval)
                        goto error;
-               dev->reset_fn = 1;
        }
        return 0;
 
index f6a4dd10d9b0da85a009a1cdd8b6498312573ac3..43aeecc4b675f96851704a85474fd1cb8823187a 100644 (file)
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     PCI Bus Services, see include/linux/pci.h for further explanation.
+ * PCI Bus Services, see include/linux/pci.h for further explanation.
  *
- *     Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
- *     David Mosberger-Tang
+ * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
  *
- *     Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz>
  */
 
 #include <linux/acpi.h>
@@ -22,6 +22,7 @@
 #include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/log2.h>
+#include <linux/logic_pio.h>
 #include <linux/pci-aspm.h>
 #include <linux/pm_wakeup.h>
 #include <linux/interrupt.h>
@@ -126,6 +127,9 @@ static int __init pcie_port_pm_setup(char *str)
 }
 __setup("pcie_port_pm=", pcie_port_pm_setup);
 
+/* Time to wait after a reset for device to become responsive */
+#define PCIE_RESET_READY_POLL_MS 60000
+
 /**
  * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
  * @bus: pointer to PCI bus structure to search
@@ -1683,6 +1687,15 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state)
 }
 EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state);
 
+/**
+ * pcie_clear_root_pme_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+void pcie_clear_root_pme_status(struct pci_dev *dev)
+{
+       pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
+}
+
 /**
  * pci_check_pme_status - Check if given device has generated PME.
  * @dev: Device to check.
@@ -3440,68 +3453,35 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
 }
 EXPORT_SYMBOL(pci_request_regions_exclusive);
 
-#ifdef PCI_IOBASE
-struct io_range {
-       struct list_head list;
-       phys_addr_t start;
-       resource_size_t size;
-};
-
-static LIST_HEAD(io_range_list);
-static DEFINE_SPINLOCK(io_range_lock);
-#endif
-
 /*
  * Record the PCI IO range (expressed as CPU physical address + size).
  * Return a negative value if an error has occured, zero otherwise
  */
-int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+                       resource_size_t size)
 {
-       int err = 0;
-
+       int ret = 0;
 #ifdef PCI_IOBASE
-       struct io_range *range;
-       resource_size_t allocated_size = 0;
-
-       /* check if the range hasn't been previously recorded */
-       spin_lock(&io_range_lock);
-       list_for_each_entry(range, &io_range_list, list) {
-               if (addr >= range->start && addr + size <= range->start + size) {
-                       /* range already registered, bail out */
-                       goto end_register;
-               }
-               allocated_size += range->size;
-       }
-
-       /* range not registed yet, check for available space */
-       if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
-               /* if it's too big check if 64K space can be reserved */
-               if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
-                       err = -E2BIG;
-                       goto end_register;
-               }
+       struct logic_pio_hwaddr *range;
 
-               size = SZ_64K;
-               pr_warn("Requested IO range too big, new size set to 64K\n");
-       }
+       if (!size || addr + size < addr)
+               return -EINVAL;
 
-       /* add the range to the list */
        range = kzalloc(sizeof(*range), GFP_ATOMIC);
-       if (!range) {
-               err = -ENOMEM;
-               goto end_register;
-       }
+       if (!range)
+               return -ENOMEM;
 
-       range->start = addr;
+       range->fwnode = fwnode;
        range->size = size;
+       range->hw_start = addr;
+       range->flags = LOGIC_PIO_CPU_MMIO;
 
-       list_add_tail(&range->list, &io_range_list);
-
-end_register:
-       spin_unlock(&io_range_lock);
+       ret = logic_pio_register_range(range);
+       if (ret)
+               kfree(range);
 #endif
 
-       return err;
+       return ret;
 }
 
 phys_addr_t pci_pio_to_address(unsigned long pio)
@@ -3509,21 +3489,10 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
        phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
 
 #ifdef PCI_IOBASE
-       struct io_range *range;
-       resource_size_t allocated_size = 0;
-
-       if (pio > IO_SPACE_LIMIT)
+       if (pio >= MMIO_UPPER_LIMIT)
                return address;
 
-       spin_lock(&io_range_lock);
-       list_for_each_entry(range, &io_range_list, list) {
-               if (pio >= allocated_size && pio < allocated_size + range->size) {
-                       address = range->start + pio - allocated_size;
-                       break;
-               }
-               allocated_size += range->size;
-       }
-       spin_unlock(&io_range_lock);
+       address = logic_pio_to_hwaddr(pio);
 #endif
 
        return address;
@@ -3532,21 +3501,7 @@ phys_addr_t pci_pio_to_address(unsigned long pio)
 unsigned long __weak pci_address_to_pio(phys_addr_t address)
 {
 #ifdef PCI_IOBASE
-       struct io_range *res;
-       resource_size_t offset = 0;
-       unsigned long addr = -1;
-
-       spin_lock(&io_range_lock);
-       list_for_each_entry(res, &io_range_list, list) {
-               if (address >= res->start && address < res->start + res->size) {
-                       addr = address - res->start + offset;
-                       break;
-               }
-               offset += res->size;
-       }
-       spin_unlock(&io_range_lock);
-
-       return addr;
+       return logic_pio_trans_cpuaddr(address);
 #else
        if (address > IO_SPACE_LIMIT)
                return (unsigned long)-1;
@@ -4017,20 +3972,13 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
 }
 EXPORT_SYMBOL(pci_wait_for_pending_transaction);
 
-static void pci_flr_wait(struct pci_dev *dev)
+static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
 {
-       int delay = 1, timeout = 60000;
+       int delay = 1;
        u32 id;
 
        /*
-        * Per PCIe r3.1, sec 6.6.2, a device must complete an FLR within
-        * 100ms, but may silently discard requests while the FLR is in
-        * progress.  Wait 100ms before trying to access the device.
-        */
-       msleep(100);
-
-       /*
-        * After 100ms, the device should not silently discard config
+        * After reset, the device should not silently discard config
         * requests, but it may still indicate that it needs more time by
         * responding to them with CRS completions.  The Root Port will
         * generally synthesize ~0 data to complete the read (except when
@@ -4044,14 +3992,14 @@ static void pci_flr_wait(struct pci_dev *dev)
        pci_read_config_dword(dev, PCI_COMMAND, &id);
        while (id == ~0) {
                if (delay > timeout) {
-                       pci_warn(dev, "not ready %dms after FLR; giving up\n",
-                                100 + delay - 1);
-                       return;
+                       pci_warn(dev, "not ready %dms after %s; giving up\n",
+                                delay - 1, reset_type);
+                       return -ENOTTY;
                }
 
                if (delay > 1000)
-                       pci_info(dev, "not ready %dms after FLR; waiting\n",
-                                100 + delay - 1);
+                       pci_info(dev, "not ready %dms after %s; waiting\n",
+                                delay - 1, reset_type);
 
                msleep(delay);
                delay *= 2;
@@ -4059,7 +4007,10 @@ static void pci_flr_wait(struct pci_dev *dev)
        }
 
        if (delay > 1000)
-               pci_info(dev, "ready %dms after FLR\n", 100 + delay - 1);
+               pci_info(dev, "ready %dms after %s\n", delay - 1,
+                        reset_type);
+
+       return 0;
 }
 
 /**
@@ -4088,13 +4039,21 @@ static bool pcie_has_flr(struct pci_dev *dev)
  * device supports FLR before calling this function, e.g. by using the
  * pcie_has_flr() helper.
  */
-void pcie_flr(struct pci_dev *dev)
+int pcie_flr(struct pci_dev *dev)
 {
        if (!pci_wait_for_pending_transaction(dev))
                pci_err(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);
+
+       /*
+        * Per PCIe r4.0, sec 6.6.2, a device must complete an FLR within
+        * 100ms, but may silently discard requests while the FLR is in
+        * progress.  Wait 100ms before trying to access the device.
+        */
+       msleep(100);
+
+       return pci_dev_wait(dev, "FLR", PCIE_RESET_READY_POLL_MS);
 }
 EXPORT_SYMBOL_GPL(pcie_flr);
 
@@ -4127,8 +4086,16 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
                pci_err(dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n");
 
        pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR);
-       pci_flr_wait(dev);
-       return 0;
+
+       /*
+        * Per Advanced Capabilities for Conventional PCI ECN, 13 April 2006,
+        * updated 27 July 2006; a device must complete an FLR within
+        * 100ms, but may silently discard requests while the FLR is in
+        * progress.  Wait 100ms before trying to access the device.
+        */
+       msleep(100);
+
+       return pci_dev_wait(dev, "AF_FLR", PCIE_RESET_READY_POLL_MS);
 }
 
 /**
@@ -4173,7 +4140,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
        pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr);
        pci_dev_d3_sleep(dev);
 
-       return 0;
+       return pci_dev_wait(dev, "PM D3->D0", PCIE_RESET_READY_POLL_MS);
 }
 
 void pci_reset_secondary_bus(struct pci_dev *dev)
@@ -4183,6 +4150,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev)
        pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
        ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+
        /*
         * PCI spec v3.0 7.6.4.2 requires minimum Trst of 1ms.  Double
         * this to 2ms to ensure that we meet the minimum requirement.
@@ -4214,9 +4182,11 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev)
  * Use the bridge control register to assert reset on the secondary bus.
  * Devices on the secondary bus are left in power-on state.
  */
-void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
+int pci_reset_bridge_secondary_bus(struct pci_dev *dev)
 {
        pcibios_reset_secondary_bus(dev);
+
+       return pci_dev_wait(dev, "bus reset", PCIE_RESET_READY_POLL_MS);
 }
 EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
 
@@ -4379,8 +4349,9 @@ int __pci_reset_function_locked(struct pci_dev *dev)
        if (rc != -ENOTTY)
                return rc;
        if (pcie_has_flr(dev)) {
-               pcie_flr(dev);
-               return 0;
+               rc = pcie_flr(dev);
+               if (rc != -ENOTTY)
+                       return rc;
        }
        rc = pci_af_flr(dev, 0);
        if (rc != -ENOTTY)
@@ -4450,9 +4421,8 @@ int pci_reset_function(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_probe_reset_function(dev);
-       if (rc)
-               return rc;
+       if (!dev->reset_fn)
+               return -ENOTTY;
 
        pci_dev_lock(dev);
        pci_dev_save_and_disable(dev);
@@ -4487,9 +4457,8 @@ int pci_reset_function_locked(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_probe_reset_function(dev);
-       if (rc)
-               return rc;
+       if (!dev->reset_fn)
+               return -ENOTTY;
 
        pci_dev_save_and_disable(dev);
 
@@ -4511,18 +4480,17 @@ int pci_try_reset_function(struct pci_dev *dev)
 {
        int rc;
 
-       rc = pci_probe_reset_function(dev);
-       if (rc)
-               return rc;
+       if (!dev->reset_fn)
+               return -ENOTTY;
 
        if (!pci_dev_trylock(dev))
                return -EAGAIN;
 
        pci_dev_save_and_disable(dev);
        rc = __pci_reset_function_locked(dev);
+       pci_dev_restore(dev);
        pci_dev_unlock(dev);
 
-       pci_dev_restore(dev);
        return rc;
 }
 EXPORT_SYMBOL_GPL(pci_try_reset_function);
@@ -4730,7 +4698,9 @@ static void pci_slot_restore(struct pci_slot *slot)
        list_for_each_entry(dev, &slot->bus->devices, bus_list) {
                if (!dev->slot || dev->slot != slot)
                        continue;
+               pci_dev_lock(dev);
                pci_dev_restore(dev);
+               pci_dev_unlock(dev);
                if (dev->subordinate)
                        pci_bus_restore(dev->subordinate);
        }
@@ -5146,6 +5116,180 @@ int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
 }
 EXPORT_SYMBOL(pcie_get_minimum_link);
 
+/**
+ * pcie_bandwidth_available - determine minimum link settings of a PCIe
+ *                           device and its bandwidth limitation
+ * @dev: PCI device to query
+ * @limiting_dev: storage for device causing the bandwidth limitation
+ * @speed: storage for speed of limiting device
+ * @width: storage for width of limiting device
+ *
+ * Walk up the PCI device chain and find the point where the minimum
+ * bandwidth is available.  Return the bandwidth available there and (if
+ * limiting_dev, speed, and width pointers are supplied) information about
+ * that point.  The bandwidth returned is in Mb/s, i.e., megabits/second of
+ * raw bandwidth.
+ */
+u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
+                            enum pci_bus_speed *speed,
+                            enum pcie_link_width *width)
+{
+       u16 lnksta;
+       enum pci_bus_speed next_speed;
+       enum pcie_link_width next_width;
+       u32 bw, next_bw;
+
+       if (speed)
+               *speed = PCI_SPEED_UNKNOWN;
+       if (width)
+               *width = PCIE_LNK_WIDTH_UNKNOWN;
+
+       bw = 0;
+
+       while (dev) {
+               pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
+
+               next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
+               next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
+                       PCI_EXP_LNKSTA_NLW_SHIFT;
+
+               next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
+
+               /* Check if current device limits the total bandwidth */
+               if (!bw || next_bw <= bw) {
+                       bw = next_bw;
+
+                       if (limiting_dev)
+                               *limiting_dev = dev;
+                       if (speed)
+                               *speed = next_speed;
+                       if (width)
+                               *width = next_width;
+               }
+
+               dev = pci_upstream_bridge(dev);
+       }
+
+       return bw;
+}
+EXPORT_SYMBOL(pcie_bandwidth_available);
+
+/**
+ * pcie_get_speed_cap - query for the PCI device's link speed capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device speed capability.  Return the maximum link speed
+ * supported by the device.
+ */
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev)
+{
+       u32 lnkcap2, lnkcap;
+
+       /*
+        * PCIe r4.0 sec 7.5.3.18 recommends using the Supported Link
+        * Speeds Vector in Link Capabilities 2 when supported, falling
+        * back to Max Link Speed in Link Capabilities otherwise.
+        */
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2);
+       if (lnkcap2) { /* PCIe r3.0-compliant */
+               if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB)
+                       return PCIE_SPEED_16_0GT;
+               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB)
+                       return PCIE_SPEED_8_0GT;
+               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB)
+                       return PCIE_SPEED_5_0GT;
+               else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB)
+                       return PCIE_SPEED_2_5GT;
+               return PCI_SPEED_UNKNOWN;
+       }
+
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+       if (lnkcap) {
+               if (lnkcap & PCI_EXP_LNKCAP_SLS_16_0GB)
+                       return PCIE_SPEED_16_0GT;
+               else if (lnkcap & PCI_EXP_LNKCAP_SLS_8_0GB)
+                       return PCIE_SPEED_8_0GT;
+               else if (lnkcap & PCI_EXP_LNKCAP_SLS_5_0GB)
+                       return PCIE_SPEED_5_0GT;
+               else if (lnkcap & PCI_EXP_LNKCAP_SLS_2_5GB)
+                       return PCIE_SPEED_2_5GT;
+       }
+
+       return PCI_SPEED_UNKNOWN;
+}
+
+/**
+ * pcie_get_width_cap - query for the PCI device's link width capability
+ * @dev: PCI device to query
+ *
+ * Query the PCI device width capability.  Return the maximum link width
+ * supported by the device.
+ */
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev)
+{
+       u32 lnkcap;
+
+       pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
+       if (lnkcap)
+               return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
+
+       return PCIE_LNK_WIDTH_UNKNOWN;
+}
+
+/**
+ * pcie_bandwidth_capable - calculate a PCI device's link bandwidth capability
+ * @dev: PCI device
+ * @speed: storage for link speed
+ * @width: storage for link width
+ *
+ * Calculate a PCI device's link bandwidth by querying for its link speed
+ * and width, multiplying them, and applying encoding overhead.  The result
+ * is in Mb/s, i.e., megabits/second of raw bandwidth.
+ */
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+                          enum pcie_link_width *width)
+{
+       *speed = pcie_get_speed_cap(dev);
+       *width = pcie_get_width_cap(dev);
+
+       if (*speed == PCI_SPEED_UNKNOWN || *width == PCIE_LNK_WIDTH_UNKNOWN)
+               return 0;
+
+       return *width * PCIE_SPEED2MBS_ENC(*speed);
+}
+
+/**
+ * pcie_print_link_status - Report the PCI device's link speed and width
+ * @dev: PCI device to query
+ *
+ * Report the available bandwidth at the device.  If this is less than the
+ * device is capable of, report the device's maximum possible bandwidth and
+ * the upstream link that limits its performance to less than that.
+ */
+void pcie_print_link_status(struct pci_dev *dev)
+{
+       enum pcie_link_width width, width_cap;
+       enum pci_bus_speed speed, speed_cap;
+       struct pci_dev *limiting_dev = NULL;
+       u32 bw_avail, bw_cap;
+
+       bw_cap = pcie_bandwidth_capable(dev, &speed_cap, &width_cap);
+       bw_avail = pcie_bandwidth_available(dev, &limiting_dev, &speed, &width);
+
+       if (bw_avail >= bw_cap)
+               pci_info(dev, "%u.%03u Gb/s available bandwidth (%s x%d link)\n",
+                        bw_cap / 1000, bw_cap % 1000,
+                        PCIE_SPEED2STR(speed_cap), width_cap);
+       else
+               pci_info(dev, "%u.%03u Gb/s available bandwidth, limited by %s x%d link at %s (capable of %u.%03u Gb/s with %s x%d link)\n",
+                        bw_avail / 1000, bw_avail % 1000,
+                        PCIE_SPEED2STR(speed), width,
+                        limiting_dev ? pci_name(limiting_dev) : "<unknown>",
+                        bw_cap / 1000, bw_cap % 1000,
+                        PCIE_SPEED2STR(speed_cap), width_cap);
+}
+EXPORT_SYMBOL(pcie_print_link_status);
+
 /**
  * pci_select_bars - Make BAR mask from the type of resource
  * @dev: the PCI device for which BAR mask is made
@@ -5611,8 +5755,9 @@ static int of_pci_bus_find_domain_nr(struct device *parent)
                use_dt_domains = 0;
                domain = pci_get_new_domain_nr();
        } else {
-               dev_err(parent, "Node %pOF has inconsistent \"linux,pci-domain\" property in DT\n",
-                       parent->of_node);
+               if (parent)
+                       pr_err("Node %pOF has ", parent->of_node);
+               pr_err("Inconsistent \"linux,pci-domain\" property in DT\n");
                domain = -1;
        }
 
index 9a41a63999678bc32eaf3af0efaaaacde4b23256..023f7cf25bff475f32b97da88033810f593ae133 100644 (file)
@@ -71,6 +71,7 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
 void pci_power_up(struct pci_dev *dev);
 void pci_disable_enabled_device(struct pci_dev *dev);
 int pci_finish_runtime_suspend(struct pci_dev *dev);
+void pcie_clear_root_pme_status(struct pci_dev *dev);
 int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
 void pci_pme_restore(struct pci_dev *dev);
 bool pci_dev_keep_suspended(struct pci_dev *dev);
@@ -238,6 +239,27 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
 void pci_reassigndev_resource_alignment(struct pci_dev *dev);
 void pci_disable_bridge_window(struct pci_dev *dev);
 
+/* PCIe link information */
+#define PCIE_SPEED2STR(speed) \
+       ((speed) == PCIE_SPEED_16_0GT ? "16 GT/s" : \
+        (speed) == PCIE_SPEED_8_0GT ? "8 GT/s" : \
+        (speed) == PCIE_SPEED_5_0GT ? "5 GT/s" : \
+        (speed) == PCIE_SPEED_2_5GT ? "2.5 GT/s" : \
+        "Unknown speed")
+
+/* PCIe speed to Mb/s reduced by encoding overhead */
+#define PCIE_SPEED2MBS_ENC(speed) \
+       ((speed) == PCIE_SPEED_16_0GT ? 16000*128/130 : \
+        (speed) == PCIE_SPEED_8_0GT  ?  8000*128/130 : \
+        (speed) == PCIE_SPEED_5_0GT  ?  5000*8/10 : \
+        (speed) == PCIE_SPEED_2_5GT  ?  2500*8/10 : \
+        0)
+
+enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev);
+enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev);
+u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed,
+                          enum pcie_link_width *width);
+
 /* Single Root I/O Virtualization */
 struct pci_sriov {
        int             pos;            /* Capability position */
@@ -256,6 +278,10 @@ struct pci_sriov {
        u16             driver_max_VFs; /* Max num VFs driver supports */
        struct pci_dev  *dev;           /* Lowest numbered PF */
        struct pci_dev  *self;          /* This PF */
+       u32             class;          /* VF device */
+       u8              hdr_type;       /* VF header type */
+       u16             subsystem_vendor; /* VF subsystem vendor */
+       u16             subsystem_device; /* VF subsystem device */
        resource_size_t barsz[PCI_SRIOV_NUM_BARS];      /* VF BAR size */
        bool            drivers_autoprobe; /* Auto probing of VFs by driver */
 };
index 223e4c34c29a5b2f048becd0fdd6ab7346e04a62..800e1d404a45500368d8f753a0f6ea07c5c331cc 100644 (file)
@@ -1,20 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0
 #
-# Makefile for PCI-Express PORT Driver
-#
-
-# Build PCI Express ASPM if needed
-obj-$(CONFIG_PCIEASPM)         += aspm.o
+# Makefile for PCI Express features and port driver
 
-pcieportdrv-y                  := portdrv_core.o portdrv_pci.o portdrv_bus.o
-pcieportdrv-$(CONFIG_ACPI)     += portdrv_acpi.o
+pcieportdrv-y                  := portdrv_core.o portdrv_pci.o
 
 obj-$(CONFIG_PCIEPORTBUS)      += pcieportdrv.o
 
-# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEASPM)         += aspm.o
 obj-$(CONFIG_PCIEAER)          += aer/
-
-obj-$(CONFIG_PCIE_PME) += pme.o
-
-obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
-obj-$(CONFIG_PCIE_PTM) += ptm.o
+obj-$(CONFIG_PCIE_PME)         += pme.o
+obj-$(CONFIG_PCIE_DPC)         += dpc.o
+obj-$(CONFIG_PCIE_PTM)         += ptm.o
index 25e1feb962c515608f2cbf366385d4979f4d4861..a49090935303c236b8d85a476c1209bd13d21929 100644 (file)
@@ -344,7 +344,7 @@ static int aer_inject(struct aer_error_inj *einj)
                goto out_put;
        }
 
-       pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       pos_cap_err = dev->aer_cap;
        if (!pos_cap_err) {
                pci_err(dev, "aer_inject: Device doesn't support AER\n");
                ret = -EPROTONOSUPPORT;
@@ -355,7 +355,7 @@ static int aer_inject(struct aer_error_inj *einj)
        pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_MASK,
                              &uncor_mask);
 
-       rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
+       rp_pos_cap_err = rpdev->aer_cap;
        if (!rp_pos_cap_err) {
                pci_err(rpdev, "aer_inject: Root port doesn't support AER\n");
                ret = -EPROTONOSUPPORT;
index da8331f5684d18dadfc4d90e55e1e15a425dfce3..779b3879b1b5edb28736e3cc555a5a82428a707e 100644 (file)
@@ -1,15 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/pcie/aer/aerdrv.c
- *
- * This file implements the AER root port service driver. The driver will
- * register an irq handler. When root port triggers an AER interrupt, the irq
- * handler will collect root port status and schedule a work.
+ * Implement the AER root port service driver. The driver registers an IRQ
+ * handler. When a root port triggers an AER interrupt, the IRQ handler
+ * collects root port status and schedules work.
  *
  * Copyright (C) 2006 Intel Corp.
  *     Tom Long Nguyen (tom.l.nguyen@intel.com)
  *     Zhang Yanmin (yanmin.zhang@intel.com)
- *
  */
 
 #include <linux/pci.h>
@@ -21,7 +18,6 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/pcieport_if.h>
 #include <linux/slab.h>
 
 #include "aerdrv.h"
index 5449e5ce139db62e52c3ed45ef3f0d428c1d1724..08b4584f62fe8bc03ae392f93314497d0ef8c626 100644 (file)
@@ -3,17 +3,17 @@
  * Copyright (C) 2006 Intel Corp.
  *     Tom Long Nguyen (tom.l.nguyen@intel.com)
  *     Zhang Yanmin (yanmin.zhang@intel.com)
- *
  */
 
 #ifndef _AERDRV_H_
 #define _AERDRV_H_
 
 #include <linux/workqueue.h>
-#include <linux/pcieport_if.h>
 #include <linux/aer.h>
 #include <linux/interrupt.h>
 
+#include "../portdrv.h"
+
 #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE|   \
                                        PCI_EXP_RTCTL_SENFEE|   \
                                        PCI_EXP_RTCTL_SEFEE)
index b2019440e8827788bf3af7684df28519e419704d..08c87de13cb83957b92ec6f26d455412c6b600b6 100644 (file)
@@ -5,7 +5,6 @@
  * Copyright (C) 2006 Intel Corp.
  *     Tom Long Nguyen (tom.l.nguyen@intel.com)
  *     Zhang Yanmin (yanmin.zhang@intel.com)
- *
  */
 
 #include <linux/module.h>
index a4bfea52e7d48a8b32332344e9bd8ea2e7f41965..0ea5acc40323385db1fbee15dbd4998e0ea54420 100644 (file)
@@ -1,16 +1,13 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/pcie/aer/aerdrv_core.c
- *
- * This file implements the core part of PCIe AER. When a PCIe
- * error is delivered, an error message will be collected and printed to
- * console, then, an error recovery procedure will be executed by following
- * the PCI error recovery rules.
+ * Implement the core part of PCIe AER. When a PCIe error is delivered, an
+ * error message will be collected and printed to console, then an error
+ * recovery procedure will be executed by following the PCI error recovery
+ * rules.
  *
  * Copyright (C) 2006 Intel Corp.
  *     Tom Long Nguyen (tom.l.nguyen@intel.com)
  *     Zhang Yanmin (yanmin.zhang@intel.com)
- *
  */
 
 #include <linux/module.h>
index 6a352e63869902f1ae038d156eeec37ccf6dfc63..cfc89dd578316dc5f32db14e5c302469c4aaf055 100644 (file)
@@ -1,13 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/pcie/aer/aerdrv_errprint.c
- *
  * Format error messages and print them to console.
  *
  * Copyright (C) 2006 Intel Corp.
  *     Tom Long Nguyen (tom.l.nguyen@intel.com)
  *     Zhang Yanmin (yanmin.zhang@intel.com)
- *
  */
 
 #include <linux/module.h>
index 26d3cac9e635a86f9e9ac5f2041711db5a36b830..039efb606e31921e73086d2f906dabaaa198adf4 100644 (file)
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *    Enables/disables PCIe ECRC checking.
+ * Enable/disable PCIe ECRC checking
  *
- *    (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ * (C) Copyright 2009 Hewlett-Packard Development Company, L.P.
  *    Andrew Patterson <andrew.patterson@hp.com>
  */
 
@@ -40,7 +40,7 @@ static int enable_ecrc_checking(struct pci_dev *dev)
        if (!pci_is_pcie(dev))
                return -ENODEV;
 
-       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       pos = dev->aer_cap;
        if (!pos)
                return -ENODEV;
 
@@ -68,7 +68,7 @@ static int disable_ecrc_checking(struct pci_dev *dev)
        if (!pci_is_pcie(dev))
                return -ENODEV;
 
-       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       pos = dev->aer_cap;
        if (!pos)
                return -ENODEV;
 
index 57feef2ecfe78bd5b9f8f2109aecc1eace7e5d68..f76eb7704f646853bb33e03e4d7719a695ea65c8 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       drivers/pci/pcie/aspm.c
- * Enabling PCIe link L0s/L1 state and Clock Power Management
+ * Enable PCIe link L0s/L1 state and Clock Power Management
  *
  * Copyright (C) 2007 Intel
  * Copyright (C) Zhang Yanmin (yanmin.zhang@intel.com)
@@ -228,6 +227,24 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
        if (!(reg16 & PCI_EXP_LNKSTA_SLC))
                same_clock = 0;
 
+       /* Port might be already in common clock mode */
+       pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
+       if (same_clock && (reg16 & PCI_EXP_LNKCTL_CCC)) {
+               bool consistent = true;
+
+               list_for_each_entry(child, &linkbus->devices, bus_list) {
+                       pcie_capability_read_word(child, PCI_EXP_LNKCTL,
+                                                 &reg16);
+                       if (!(reg16 & PCI_EXP_LNKCTL_CCC)) {
+                               consistent = false;
+                               break;
+                       }
+               }
+               if (consistent)
+                       return;
+               pci_warn(parent, "ASPM: current common clock configuration is broken, reconfiguring\n");
+       }
+
        /* Configure downstream component, all functions */
        list_for_each_entry(child, &linkbus->devices, bus_list) {
                pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
@@ -322,7 +339,7 @@ static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
 
 static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
 {
-       u64 threshold_ns = threshold_us * 1000;
+       u32 threshold_ns = threshold_us * 1000;
 
        /* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
        if (threshold_ns < 32) {
diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c
new file mode 100644 (file)
index 0000000..8c57d60
--- /dev/null
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Express Downstream Port Containment services driver
+ * Author: Keith Busch <keith.busch@intel.com>
+ *
+ * Copyright (C) 2016 Intel Corp.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include "portdrv.h"
+#include "../pci.h"
+#include "aer/aerdrv.h"
+
+struct dpc_dev {
+       struct pcie_device      *dev;
+       struct work_struct      work;
+       u16                     cap_pos;
+       bool                    rp_extensions;
+       u32                     rp_pio_status;
+       u8                      rp_log_size;
+};
+
+static const char * const rp_pio_error_string[] = {
+       "Configuration Request received UR Completion",  /* Bit Position 0  */
+       "Configuration Request received CA Completion",  /* Bit Position 1  */
+       "Configuration Request Completion Timeout",      /* Bit Position 2  */
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "I/O Request received UR Completion",            /* Bit Position 8  */
+       "I/O Request received CA Completion",            /* Bit Position 9  */
+       "I/O Request Completion Timeout",                /* Bit Position 10 */
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       "Memory Request received UR Completion",         /* Bit Position 16 */
+       "Memory Request received CA Completion",         /* Bit Position 17 */
+       "Memory Request Completion Timeout",             /* Bit Position 18 */
+};
+
+static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
+{
+       unsigned long timeout = jiffies + HZ;
+       struct pci_dev *pdev = dpc->dev->port;
+       struct device *dev = &dpc->dev->device;
+       u16 cap = dpc->cap_pos, status;
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+       while (status & PCI_EXP_DPC_RP_BUSY &&
+                                       !time_after(jiffies, timeout)) {
+               msleep(10);
+               pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+       }
+       if (status & PCI_EXP_DPC_RP_BUSY) {
+               dev_warn(dev, "DPC root port still busy\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+static void dpc_wait_link_inactive(struct dpc_dev *dpc)
+{
+       unsigned long timeout = jiffies + HZ;
+       struct pci_dev *pdev = dpc->dev->port;
+       struct device *dev = &dpc->dev->device;
+       u16 lnk_status;
+
+       pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+       while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
+                                       !time_after(jiffies, timeout)) {
+               msleep(10);
+               pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
+       }
+       if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
+               dev_warn(dev, "Link state not disabled for DPC event\n");
+}
+
+static void dpc_work(struct work_struct *work)
+{
+       struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
+       struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
+       struct pci_bus *parent = pdev->subordinate;
+       u16 cap = dpc->cap_pos, ctl;
+
+       pci_lock_rescan_remove();
+       list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
+                                        bus_list) {
+               pci_dev_get(dev);
+               pci_dev_set_disconnected(dev, NULL);
+               if (pci_has_subordinate(dev))
+                       pci_walk_bus(dev->subordinate,
+                                    pci_dev_set_disconnected, NULL);
+               pci_stop_and_remove_bus_device(dev);
+               pci_dev_put(dev);
+       }
+       pci_unlock_rescan_remove();
+
+       dpc_wait_link_inactive(dpc);
+       if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
+               return;
+       if (dpc->rp_extensions && dpc->rp_pio_status) {
+               pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
+                                      dpc->rp_pio_status);
+               dpc->rp_pio_status = 0;
+       }
+
+       pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+               PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
+       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
+                             ctl | PCI_EXP_DPC_CTL_INT_EN);
+}
+
+static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
+{
+       struct device *dev = &dpc->dev->device;
+       struct pci_dev *pdev = dpc->dev->port;
+       u16 cap = dpc->cap_pos, dpc_status, first_error;
+       u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix;
+       int i;
+
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask);
+       dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
+               status, mask);
+
+       dpc->rp_pio_status = status;
+
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc);
+       dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
+               sev, syserr, exc);
+
+       /* Get First Error Pointer */
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
+       first_error = (dpc_status & 0x1f00) >> 8;
+
+       status &= ~mask;
+       for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
+               if (status & (1 << i))
+                       dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
+                               first_error == i ? " (First)" : "");
+       }
+
+       if (dpc->rp_log_size < 4)
+               return;
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
+                             &dw0);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
+                             &dw1);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
+                             &dw2);
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
+                             &dw3);
+       dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
+               dw0, dw1, dw2, dw3);
+
+       if (dpc->rp_log_size < 5)
+               return;
+       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
+       dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log);
+
+       for (i = 0; i < dpc->rp_log_size - 5; i++) {
+               pci_read_config_dword(pdev,
+                       cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix);
+               dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
+       }
+}
+
+static irqreturn_t dpc_irq(int irq, void *context)
+{
+       struct dpc_dev *dpc = (struct dpc_dev *)context;
+       struct pci_dev *pdev = dpc->dev->port;
+       struct device *dev = &dpc->dev->device;
+       u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
+
+       if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
+               return IRQ_NONE;
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
+
+       if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
+               return IRQ_NONE;
+
+       if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
+               pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
+                                     PCI_EXP_DPC_STATUS_INTERRUPT);
+               return IRQ_HANDLED;
+       }
+
+       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
+                             ctl & ~PCI_EXP_DPC_CTL_INT_EN);
+
+       pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
+                            &source);
+
+       dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
+               status, source);
+
+       reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
+       ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
+
+       dev_warn(dev, "DPC %s detected, remove downstream devices\n",
+                (reason == 0) ? "unmasked uncorrectable error" :
+                (reason == 1) ? "ERR_NONFATAL" :
+                (reason == 2) ? "ERR_FATAL" :
+                (ext_reason == 0) ? "RP PIO error" :
+                (ext_reason == 1) ? "software trigger" :
+                                    "reserved error");
+       /* show RP PIO error detail information */
+       if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
+               dpc_process_rp_pio_error(dpc);
+
+       schedule_work(&dpc->work);
+
+       return IRQ_HANDLED;
+}
+
+#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
+static int dpc_probe(struct pcie_device *dev)
+{
+       struct dpc_dev *dpc;
+       struct pci_dev *pdev = dev->port;
+       struct device *device = &dev->device;
+       int status;
+       u16 ctl, cap;
+
+       if (pcie_aer_get_firmware_first(pdev))
+               return -ENOTSUPP;
+
+       dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
+       if (!dpc)
+               return -ENOMEM;
+
+       dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
+       dpc->dev = dev;
+       INIT_WORK(&dpc->work, dpc_work);
+       set_service_data(dev, dpc);
+
+       status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
+                                 "pcie-dpc", dpc);
+       if (status) {
+               dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
+                        status);
+               return status;
+       }
+
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
+
+       dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT);
+       if (dpc->rp_extensions) {
+               dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
+               if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) {
+                       dev_err(device, "RP PIO log size %u is invalid\n",
+                               dpc->rp_log_size);
+                       dpc->rp_log_size = 0;
+               }
+       }
+
+       ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
+       pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
+
+       dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
+               cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
+               FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
+               FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
+               FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
+       return status;
+}
+
+static void dpc_remove(struct pcie_device *dev)
+{
+       struct dpc_dev *dpc = get_service_data(dev);
+       struct pci_dev *pdev = dev->port;
+       u16 ctl;
+
+       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
+       ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
+       pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
+}
+
+static struct pcie_port_service_driver dpcdriver = {
+       .name           = "dpc",
+       .port_type      = PCIE_ANY_PORT,
+       .service        = PCIE_PORT_SERVICE_DPC,
+       .probe          = dpc_probe,
+       .remove         = dpc_remove,
+};
+
+static int __init dpc_service_init(void)
+{
+       return pcie_port_service_register(&dpcdriver);
+}
+device_initcall(dpc_service_init);
diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c
deleted file mode 100644 (file)
index 38e40c6..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * PCI Express Downstream Port Containment services driver
- * Author: Keith Busch <keith.busch@intel.com>
- *
- * Copyright (C) 2016 Intel Corp.
- */
-
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/pcieport_if.h>
-#include "../pci.h"
-#include "aer/aerdrv.h"
-
-struct dpc_dev {
-       struct pcie_device      *dev;
-       struct work_struct      work;
-       u16                     cap_pos;
-       bool                    rp_extensions;
-       u32                     rp_pio_status;
-       u8                      rp_log_size;
-};
-
-static const char * const rp_pio_error_string[] = {
-       "Configuration Request received UR Completion",  /* Bit Position 0  */
-       "Configuration Request received CA Completion",  /* Bit Position 1  */
-       "Configuration Request Completion Timeout",      /* Bit Position 2  */
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       "I/O Request received UR Completion",            /* Bit Position 8  */
-       "I/O Request received CA Completion",            /* Bit Position 9  */
-       "I/O Request Completion Timeout",                /* Bit Position 10 */
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       NULL,
-       "Memory Request received UR Completion",         /* Bit Position 16 */
-       "Memory Request received CA Completion",         /* Bit Position 17 */
-       "Memory Request Completion Timeout",             /* Bit Position 18 */
-};
-
-static int dpc_wait_rp_inactive(struct dpc_dev *dpc)
-{
-       unsigned long timeout = jiffies + HZ;
-       struct pci_dev *pdev = dpc->dev->port;
-       struct device *dev = &dpc->dev->device;
-       u16 cap = dpc->cap_pos, status;
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
-       while (status & PCI_EXP_DPC_RP_BUSY &&
-                                       !time_after(jiffies, timeout)) {
-               msleep(10);
-               pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
-       }
-       if (status & PCI_EXP_DPC_RP_BUSY) {
-               dev_warn(dev, "DPC root port still busy\n");
-               return -EBUSY;
-       }
-       return 0;
-}
-
-static void dpc_wait_link_inactive(struct dpc_dev *dpc)
-{
-       unsigned long timeout = jiffies + HZ;
-       struct pci_dev *pdev = dpc->dev->port;
-       struct device *dev = &dpc->dev->device;
-       u16 lnk_status;
-
-       pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-       while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
-                                       !time_after(jiffies, timeout)) {
-               msleep(10);
-               pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
-       }
-       if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
-               dev_warn(dev, "Link state not disabled for DPC event\n");
-}
-
-static void dpc_work(struct work_struct *work)
-{
-       struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
-       struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
-       struct pci_bus *parent = pdev->subordinate;
-       u16 cap = dpc->cap_pos, ctl;
-
-       pci_lock_rescan_remove();
-       list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
-                                        bus_list) {
-               pci_dev_get(dev);
-               pci_dev_set_disconnected(dev, NULL);
-               if (pci_has_subordinate(dev))
-                       pci_walk_bus(dev->subordinate,
-                                    pci_dev_set_disconnected, NULL);
-               pci_stop_and_remove_bus_device(dev);
-               pci_dev_put(dev);
-       }
-       pci_unlock_rescan_remove();
-
-       dpc_wait_link_inactive(dpc);
-       if (dpc->rp_extensions && dpc_wait_rp_inactive(dpc))
-               return;
-       if (dpc->rp_extensions && dpc->rp_pio_status) {
-               pci_write_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS,
-                                      dpc->rp_pio_status);
-               dpc->rp_pio_status = 0;
-       }
-
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
-               PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
-                             ctl | PCI_EXP_DPC_CTL_INT_EN);
-}
-
-static void dpc_process_rp_pio_error(struct dpc_dev *dpc)
-{
-       struct device *dev = &dpc->dev->device;
-       struct pci_dev *pdev = dpc->dev->port;
-       u16 cap = dpc->cap_pos, dpc_status, first_error;
-       u32 status, mask, sev, syserr, exc, dw0, dw1, dw2, dw3, log, prefix;
-       int i;
-
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_STATUS, &status);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_MASK, &mask);
-       dev_err(dev, "rp_pio_status: %#010x, rp_pio_mask: %#010x\n",
-               status, mask);
-
-       dpc->rp_pio_status = status;
-
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SEVERITY, &sev);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_SYSERROR, &syserr);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_EXCEPTION, &exc);
-       dev_err(dev, "RP PIO severity=%#010x, syserror=%#010x, exception=%#010x\n",
-               sev, syserr, exc);
-
-       /* Get First Error Pointer */
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
-       first_error = (dpc_status & 0x1f00) >> 8;
-
-       status &= ~mask;
-       for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
-               if (status & (1 << i))
-                       dev_err(dev, "[%2d] %s%s\n", i, rp_pio_error_string[i],
-                               first_error == i ? " (First)" : "");
-       }
-
-       if (dpc->rp_log_size < 4)
-               return;
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG,
-                             &dw0);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 4,
-                             &dw1);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 8,
-                             &dw2);
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_HEADER_LOG + 12,
-                             &dw3);
-       dev_err(dev, "TLP Header: %#010x %#010x %#010x %#010x\n",
-               dw0, dw1, dw2, dw3);
-
-       if (dpc->rp_log_size < 5)
-               return;
-       pci_read_config_dword(pdev, cap + PCI_EXP_DPC_RP_PIO_IMPSPEC_LOG, &log);
-       dev_err(dev, "RP PIO ImpSpec Log %#010x\n", log);
-
-       for (i = 0; i < dpc->rp_log_size - 5; i++) {
-               pci_read_config_dword(pdev,
-                       cap + PCI_EXP_DPC_RP_PIO_TLPPREFIX_LOG, &prefix);
-               dev_err(dev, "TLP Prefix Header: dw%d, %#010x\n", i, prefix);
-       }
-}
-
-static irqreturn_t dpc_irq(int irq, void *context)
-{
-       struct dpc_dev *dpc = (struct dpc_dev *)context;
-       struct pci_dev *pdev = dpc->dev->port;
-       struct device *dev = &dpc->dev->device;
-       u16 cap = dpc->cap_pos, ctl, status, source, reason, ext_reason;
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_CTL, &ctl);
-
-       if (!(ctl & PCI_EXP_DPC_CTL_INT_EN) || ctl == (u16)(~0))
-               return IRQ_NONE;
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &status);
-
-       if (!(status & PCI_EXP_DPC_STATUS_INTERRUPT))
-               return IRQ_NONE;
-
-       if (!(status & PCI_EXP_DPC_STATUS_TRIGGER)) {
-               pci_write_config_word(pdev, cap + PCI_EXP_DPC_STATUS,
-                                     PCI_EXP_DPC_STATUS_INTERRUPT);
-               return IRQ_HANDLED;
-       }
-
-       pci_write_config_word(pdev, cap + PCI_EXP_DPC_CTL,
-                             ctl & ~PCI_EXP_DPC_CTL_INT_EN);
-
-       pci_read_config_word(pdev, cap + PCI_EXP_DPC_SOURCE_ID,
-                            &source);
-
-       dev_info(dev, "DPC containment event, status:%#06x source:%#06x\n",
-               status, source);
-
-       reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
-       ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
-
-       dev_warn(dev, "DPC %s detected, remove downstream devices\n",
-                (reason == 0) ? "unmasked uncorrectable error" :
-                (reason == 1) ? "ERR_NONFATAL" :
-                (reason == 2) ? "ERR_FATAL" :
-                (ext_reason == 0) ? "RP PIO error" :
-                (ext_reason == 1) ? "software trigger" :
-                                    "reserved error");
-       /* show RP PIO error detail information */
-       if (dpc->rp_extensions && reason == 3 && ext_reason == 0)
-               dpc_process_rp_pio_error(dpc);
-
-       schedule_work(&dpc->work);
-
-       return IRQ_HANDLED;
-}
-
-#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
-static int dpc_probe(struct pcie_device *dev)
-{
-       struct dpc_dev *dpc;
-       struct pci_dev *pdev = dev->port;
-       struct device *device = &dev->device;
-       int status;
-       u16 ctl, cap;
-
-       if (pcie_aer_get_firmware_first(pdev))
-               return -ENOTSUPP;
-
-       dpc = devm_kzalloc(device, sizeof(*dpc), GFP_KERNEL);
-       if (!dpc)
-               return -ENOMEM;
-
-       dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
-       dpc->dev = dev;
-       INIT_WORK(&dpc->work, dpc_work);
-       set_service_data(dev, dpc);
-
-       status = devm_request_irq(device, dev->irq, dpc_irq, IRQF_SHARED,
-                                 "pcie-dpc", dpc);
-       if (status) {
-               dev_warn(device, "request IRQ%d failed: %d\n", dev->irq,
-                        status);
-               return status;
-       }
-
-       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
-       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
-
-       dpc->rp_extensions = (cap & PCI_EXP_DPC_CAP_RP_EXT);
-       if (dpc->rp_extensions) {
-               dpc->rp_log_size = (cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
-               if (dpc->rp_log_size < 4 || dpc->rp_log_size > 9) {
-                       dev_err(device, "RP PIO log size %u is invalid\n",
-                               dpc->rp_log_size);
-                       dpc->rp_log_size = 0;
-               }
-       }
-
-       ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
-       pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
-
-       dev_info(device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
-               cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
-               FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
-               FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), dpc->rp_log_size,
-               FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
-       return status;
-}
-
-static void dpc_remove(struct pcie_device *dev)
-{
-       struct dpc_dev *dpc = get_service_data(dev);
-       struct pci_dev *pdev = dev->port;
-       u16 ctl;
-
-       pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
-       ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
-       pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
-}
-
-static struct pcie_port_service_driver dpcdriver = {
-       .name           = "dpc",
-       .port_type      = PCIE_ANY_PORT,
-       .service        = PCIE_PORT_SERVICE_DPC,
-       .probe          = dpc_probe,
-       .remove         = dpc_remove,
-};
-
-static int __init dpc_service_init(void)
-{
-       return pcie_port_service_register(&dpcdriver);
-}
-device_initcall(dpc_service_init);
index 5480f54f7612aa6aac5baecb065ea07440c8075b..3ed67676ea2a16d14f41c9710d7b3a8edfd3b157 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
-#include <linux/pcieport_if.h>
 #include <linux/pm_runtime.h>
 
 #include "../pci.h"
index a854bc56911740684cd0df2b25a64ca32164beb4..d0c6783dbfe3307e48ef981cfa52a20908fb01ed 100644 (file)
@@ -1,6 +1,5 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * File:       portdrv.h
  * Purpose:    PCI Express Port Bus Driver's Internal Data Structures
  *
  * Copyright (C) 2004 Intel
 
 #include <linux/compiler.h>
 
-#define PCIE_PORT_DEVICE_MAXSERVICES   5
+extern bool pcie_ports_native;
+
+/* Service Type */
+#define PCIE_PORT_SERVICE_PME_SHIFT    0       /* Power Management Event */
+#define PCIE_PORT_SERVICE_PME          (1 << PCIE_PORT_SERVICE_PME_SHIFT)
+#define PCIE_PORT_SERVICE_AER_SHIFT    1       /* Advanced Error Reporting */
+#define PCIE_PORT_SERVICE_AER          (1 << PCIE_PORT_SERVICE_AER_SHIFT)
+#define PCIE_PORT_SERVICE_HP_SHIFT     2       /* Native Hotplug */
+#define PCIE_PORT_SERVICE_HP           (1 << PCIE_PORT_SERVICE_HP_SHIFT)
+#define PCIE_PORT_SERVICE_DPC_SHIFT    3       /* Downstream Port Containment */
+#define PCIE_PORT_SERVICE_DPC          (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
+
+#define PCIE_PORT_DEVICE_MAXSERVICES   4
+
+/* Port Type */
+#define PCIE_ANY_PORT                  (~0)
+
+struct pcie_device {
+       int             irq;        /* Service IRQ/MSI/MSI-X Vector */
+       struct pci_dev *port;       /* Root/Upstream/Downstream Port */
+       u32             service;    /* Port service this device represents */
+       void            *priv_data; /* Service Private Data */
+       struct device   device;     /* Generic Device Interface */
+};
+#define to_pcie_device(d) container_of(d, struct pcie_device, device)
+
+static inline void set_service_data(struct pcie_device *dev, void *data)
+{
+       dev->priv_data = data;
+}
+
+static inline void *get_service_data(struct pcie_device *dev)
+{
+       return dev->priv_data;
+}
+
+struct pcie_port_service_driver {
+       const char *name;
+       int (*probe) (struct pcie_device *dev);
+       void (*remove) (struct pcie_device *dev);
+       int (*suspend) (struct pcie_device *dev);
+       int (*resume) (struct pcie_device *dev);
+
+       /* Device driver may resume normal operations */
+       void (*error_resume)(struct pci_dev *dev);
+
+       /* Link Reset Capability - AER service driver specific */
+       pci_ers_result_t (*reset_link) (struct pci_dev *dev);
+
+       int port_type;  /* Type of the port this driver can handle */
+       u32 service;    /* Port service this device represents */
+
+       struct device_driver driver;
+};
+#define to_service_driver(d) \
+       container_of(d, struct pcie_port_service_driver, driver)
+
+int pcie_port_service_register(struct pcie_port_service_driver *new);
+void pcie_port_service_unregister(struct pcie_port_service_driver *new);
+
 /*
  * The PCIe Capability Interrupt Message Number (PCIe r3.1, sec 7.8.2) must
  * be one of the first 32 MSI-X entries.  Per PCI r3.0, sec 6.8.3.1, MSI
@@ -34,20 +92,6 @@ void pcie_port_bus_unregister(void);
 
 struct pci_dev;
 
-void pcie_clear_root_pme_status(struct pci_dev *dev);
-
-#ifdef CONFIG_HOTPLUG_PCI_PCIE
-extern bool pciehp_msi_disabled;
-
-static inline bool pciehp_no_msi(void)
-{
-       return pciehp_msi_disabled;
-}
-
-#else  /* !CONFIG_HOTPLUG_PCI_PCIE */
-static inline bool pciehp_no_msi(void) { return false; }
-#endif /* !CONFIG_HOTPLUG_PCI_PCIE */
-
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
@@ -68,15 +112,4 @@ static inline bool pcie_pme_no_msi(void) { return false; }
 static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
 #endif /* !CONFIG_PCIE_PME */
 
-#ifdef CONFIG_ACPI
-void pcie_port_acpi_setup(struct pci_dev *port, int *mask);
-
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
-{
-       pcie_port_acpi_setup(port, mask);
-}
-#else /* !CONFIG_ACPI */
-static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
-#endif /* !CONFIG_ACPI */
-
 #endif /* _PORTDRV_H_ */
index 319c94976873856ab98bdd6734673abb8a874606..8ab5d434b9c608bf4690509a9b6b0df2b344b928 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/errno.h>
 #include <linux/acpi.h>
 #include <linux/pci-acpi.h>
-#include <linux/pcieport_if.h>
 
 #include "aer/aerdrv.h"
 #include "../pci.h"
@@ -48,11 +47,11 @@ void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
 
        flags = root->osc_control_set;
 
-       *srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
+       *srv_mask = 0;
        if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
                *srv_mask |= PCIE_PORT_SERVICE_HP;
        if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
                *srv_mask |= PCIE_PORT_SERVICE_PME;
        if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
-               *srv_mask |= PCIE_PORT_SERVICE_AER;
+               *srv_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
 }
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
deleted file mode 100644 (file)
index f0fba55..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * File:       portdrv_bus.c
- * Purpose:    PCI Express Port Bus Driver's Bus Overloading Functions
- *
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/pm.h>
-
-#include <linux/pcieport_if.h>
-#include "portdrv.h"
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
-
-struct bus_type pcie_port_bus_type = {
-       .name           = "pci_express",
-       .match          = pcie_port_bus_match,
-};
-EXPORT_SYMBOL_GPL(pcie_port_bus_type);
-
-static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
-{
-       struct pcie_device *pciedev;
-       struct pcie_port_service_driver *driver;
-
-       if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
-               return 0;
-
-       pciedev = to_pcie_device(dev);
-       driver = to_service_driver(drv);
-
-       if (driver->service != pciedev->service)
-               return 0;
-
-       if ((driver->port_type != PCIE_ANY_PORT) &&
-           (driver->port_type != pci_pcie_type(pciedev->port)))
-               return 0;
-
-       return 1;
-}
-
-int pcie_port_bus_register(void)
-{
-       return bus_register(&pcie_port_bus_type);
-}
-
-void pcie_port_bus_unregister(void)
-{
-       bus_unregister(&pcie_port_bus_type);
-}
index ef3bad4ad010129b32752cb2e8719c778b58fd63..c9c0663db282d8a650ad236764bdeab9b19e3090 100644 (file)
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       portdrv_core.c
  * Purpose:    PCI Express Port Bus Driver's Core Functions
  *
  * Copyright (C) 2004 Intel
 #include <linux/pm_runtime.h>
 #include <linux/string.h>
 #include <linux/slab.h>
-#include <linux/pcieport_if.h>
 #include <linux/aer.h>
 
 #include "../pci.h"
 #include "portdrv.h"
 
-bool pciehp_msi_disabled;
-
-static int __init pciehp_setup(char *str)
-{
-       if (!strncmp(str, "nomsi", 5))
-               pciehp_msi_disabled = true;
-
-       return 1;
-}
-__setup("pcie_hp=", pciehp_setup);
-
 /**
  * release_pcie_device - free PCI Express port service device structure
  * @dev: Port service device to release
@@ -52,7 +39,7 @@ static void release_pcie_device(struct device *dev)
 static int pcie_message_numbers(struct pci_dev *dev, int mask,
                                u32 *pme, u32 *aer, u32 *dpc)
 {
-       u32 nvec = 0, pos, reg32;
+       u32 nvec = 0, pos;
        u16 reg16;
 
        /*
@@ -68,8 +55,11 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
                nvec = *pme + 1;
        }
 
+#ifdef CONFIG_PCIEAER
        if (mask & PCIE_PORT_SERVICE_AER) {
-               pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+               u32 reg32;
+
+               pos = dev->aer_cap;
                if (pos) {
                        pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
                                              &reg32);
@@ -77,6 +67,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
                        nvec = max(nvec, *aer + 1);
                }
        }
+#endif
 
        if (mask & PCIE_PORT_SERVICE_DPC) {
                pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC);
@@ -169,16 +160,13 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
                irqs[i] = -1;
 
        /*
-        * If we support PME or hotplug, but we can't use MSI/MSI-X for
-        * them, we have to fall back to INTx or other interrupts, e.g., a
-        * system shared interrupt.
+        * If we support PME but can't use MSI/MSI-X for it, we have to
+        * fall back to INTx or other interrupts, e.g., a system shared
+        * interrupt.
         */
        if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi())
                goto legacy_irq;
 
-       if ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())
-               goto legacy_irq;
-
        /* Try to use MSI-X or MSI if supported */
        if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0)
                return 0;
@@ -189,10 +177,8 @@ legacy_irq:
        if (ret < 0)
                return -ENODEV;
 
-       for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
-               if (i != PCIE_PORT_SERVICE_VC_SHIFT)
-                       irqs[i] = pci_irq_vector(dev, 0);
-       }
+       for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+               irqs[i] = pci_irq_vector(dev, 0);
 
        return 0;
 }
@@ -209,23 +195,13 @@ legacy_irq:
  */
 static int get_port_device_capability(struct pci_dev *dev)
 {
+       struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
        int services = 0;
-       int cap_mask = 0;
-
-       if (pcie_ports_disabled)
-               return 0;
-
-       cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
-                       | PCIE_PORT_SERVICE_VC;
-       if (pci_aer_available())
-               cap_mask |= PCIE_PORT_SERVICE_AER | PCIE_PORT_SERVICE_DPC;
 
-       if (pcie_ports_auto)
-               pcie_port_platform_notify(dev, &cap_mask);
-
-       /* Hot-Plug Capable */
-       if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
+       if (dev->is_hotplug_bridge &&
+           (pcie_ports_native || host->native_hotplug)) {
                services |= PCIE_PORT_SERVICE_HP;
+
                /*
                 * Disable hot-plug interrupts in case they have been enabled
                 * by the BIOS and the hot-plug service driver is not loaded.
@@ -233,23 +209,29 @@ static int get_port_device_capability(struct pci_dev *dev)
                pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
                          PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
        }
-       /* AER capable */
-       if ((cap_mask & PCIE_PORT_SERVICE_AER)
-           && pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
+
+#ifdef CONFIG_PCIEAER
+       if (dev->aer_cap && pci_aer_available() &&
+           (pcie_ports_native || host->native_aer)) {
                services |= PCIE_PORT_SERVICE_AER;
+
                /*
                 * Disable AER on this port in case it's been enabled by the
                 * BIOS (the AER service driver will enable it when necessary).
                 */
                pci_disable_pcie_error_reporting(dev);
        }
-       /* VC support */
-       if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
-               services |= PCIE_PORT_SERVICE_VC;
-       /* Root ports are capable of generating PME too */
-       if ((cap_mask & PCIE_PORT_SERVICE_PME)
-           && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+#endif
+
+       /*
+        * Root ports are capable of generating PME too.  Root Complex
+        * Event Collectors can also generate PMEs, but we don't handle
+        * those yet.
+        */
+       if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
+           (pcie_ports_native || host->native_pme)) {
                services |= PCIE_PORT_SERVICE_PME;
+
                /*
                 * Disable PME interrupt on this port in case it's been enabled
                 * by the BIOS (the PME service driver will enable it when
@@ -257,7 +239,9 @@ static int get_port_device_capability(struct pci_dev *dev)
                 */
                pcie_pme_interrupt_enable(dev, false);
        }
-       if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
+
+       if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC) &&
+           pci_aer_available() && services & PCIE_PORT_SERVICE_AER)
                services |= PCIE_PORT_SERVICE_DPC;
 
        return services;
@@ -335,7 +319,7 @@ int pcie_port_device_register(struct pci_dev *dev)
         */
        status = pcie_init_service_irqs(dev, irqs, capabilities);
        if (status) {
-               capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
+               capabilities &= PCIE_PORT_SERVICE_HP;
                if (!capabilities)
                        goto error_disable;
        }
index fb1c1bb8731676e66ca5fb68aba1bcf3d4bdc7d4..973f1b80a038c431edfe27bbc0bc4c718d53f01c 100644 (file)
@@ -1,9 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       portdrv_pci.c
  * Purpose:    PCI Express Port Bus Driver
  * Author:     Tom Nguyen <tom.l.nguyen@intel.com>
- * Version:    v1.0
  *
  * Copyright (C) 2004 Intel
  * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/init.h>
-#include <linux/pcieport_if.h>
 #include <linux/aer.h>
 #include <linux/dmi.h>
-#include <linux/pci-aspm.h>
 
 #include "../pci.h"
 #include "portdrv.h"
 bool pcie_ports_disabled;
 
 /*
- * If this switch is set, ACPI _OSC will be used to determine whether or not to
- * enable PCIe port native services.
+ * If the user specified "pcie_ports=native", use the PCIe services regardless
+ * of whether the platform has given us permission.  On ACPI systems, this
+ * means we ignore _OSC.
  */
-bool pcie_ports_auto = true;
+bool pcie_ports_native;
 
 static int __init pcie_port_setup(char *str)
 {
-       if (!strncmp(str, "compat", 6)) {
+       if (!strncmp(str, "compat", 6))
                pcie_ports_disabled = true;
-       } else if (!strncmp(str, "native", 6)) {
-               pcie_ports_disabled = false;
-               pcie_ports_auto = false;
-       } else if (!strncmp(str, "auto", 4)) {
-               pcie_ports_disabled = false;
-               pcie_ports_auto = true;
-       }
+       else if (!strncmp(str, "native", 6))
+               pcie_ports_native = true;
 
        return 1;
 }
@@ -50,15 +42,6 @@ __setup("pcie_ports=", pcie_port_setup);
 
 /* global data */
 
-/**
- * pcie_clear_root_pme_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-void pcie_clear_root_pme_status(struct pci_dev *dev)
-{
-       pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
-}
-
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
 {
        int retval;
@@ -71,20 +54,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
 }
 
 #ifdef CONFIG_PM
-static int pcie_port_resume_noirq(struct device *dev)
-{
-       struct pci_dev *pdev = to_pci_dev(dev);
-
-       /*
-        * Some BIOSes forget to clear Root PME Status bits after system wakeup
-        * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
-        * bits now just in case (shouldn't hurt).
-        */
-       if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
-               pcie_clear_root_pme_status(pdev);
-       return 0;
-}
-
 static int pcie_port_runtime_suspend(struct device *dev)
 {
        return to_pci_dev(dev)->bridge_d3 ? 0 : -EBUSY;
@@ -112,7 +81,6 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .thaw           = pcie_port_device_resume,
        .poweroff       = pcie_port_device_suspend,
        .restore        = pcie_port_device_resume,
-       .resume_noirq   = pcie_port_resume_noirq,
        .runtime_suspend = pcie_port_runtime_suspend,
        .runtime_resume = pcie_port_runtime_resume,
        .runtime_idle   = pcie_port_runtime_idle,
@@ -283,22 +251,11 @@ static const struct dmi_system_id pcie_portdrv_dmi_table[] __initconst = {
 
 static int __init pcie_portdrv_init(void)
 {
-       int retval;
-
        if (pcie_ports_disabled)
-               return pci_register_driver(&pcie_portdriver);
+               return -EACCES;
 
        dmi_check_system(pcie_portdrv_dmi_table);
 
-       retval = pcie_port_bus_register();
-       if (retval) {
-               printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
-               goto out;
-       }
-       retval = pci_register_driver(&pcie_portdriver);
-       if (retval)
-               pcie_port_bus_unregister();
- out:
-       return retval;
+       return pci_register_driver(&pcie_portdriver);
 }
 device_initcall(pcie_portdrv_init);
index ef5377438a1e65df31790a61472e34e708a835ab..caa07109e5f5dda9702dd98edf0e947c8a968bfe 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * probe.c - PCI detection and setup code
+ * PCI detection and setup code
  */
 
 #include <linux/kernel.h>
@@ -329,6 +329,10 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
        if (dev->non_compliant_bars)
                return;
 
+       /* Per PCIe r4.0, sec 9.3.4.1.11, the VF BARs are all RO Zero */
+       if (dev->is_virtfn)
+               return;
+
        for (pos = 0; pos < howmany; pos++) {
                struct resource *res = &dev->resource[pos];
                reg = PCI_BASE_ADDRESS_0 + (pos << 2);
@@ -540,6 +544,16 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
        INIT_LIST_HEAD(&bridge->windows);
        bridge->dev.release = pci_release_host_bridge_dev;
 
+       /*
+        * We assume we can manage these PCIe features.  Some systems may
+        * reserve these for use by the platform itself, e.g., an ACPI BIOS
+        * may implement its own AER handling and use _OSC to prevent the
+        * OS from interfering.
+        */
+       bridge->native_aer = 1;
+       bridge->native_hotplug = 1;
+       bridge->native_pme = 1;
+
        return bridge;
 }
 EXPORT_SYMBOL(pci_alloc_host_bridge);
@@ -592,7 +606,7 @@ const unsigned char pcie_link_speed[] = {
        PCIE_SPEED_2_5GT,               /* 1 */
        PCIE_SPEED_5_0GT,               /* 2 */
        PCIE_SPEED_8_0GT,               /* 3 */
-       PCI_SPEED_UNKNOWN,              /* 4 */
+       PCIE_SPEED_16_0GT,              /* 4 */
        PCI_SPEED_UNKNOWN,              /* 5 */
        PCI_SPEED_UNKNOWN,              /* 6 */
        PCI_SPEED_UNKNOWN,              /* 7 */
@@ -1230,6 +1244,13 @@ static void pci_read_irq(struct pci_dev *dev)
 {
        unsigned char irq;
 
+       /* VFs are not allowed to use INTx, so skip the config reads */
+       if (dev->is_virtfn) {
+               dev->pin = 0;
+               dev->irq = 0;
+               return;
+       }
+
        pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq);
        dev->pin = irq;
        if (irq)
@@ -1389,6 +1410,43 @@ int pci_cfg_space_size(struct pci_dev *dev)
        return PCI_CFG_SPACE_SIZE;
 }
 
+static u32 pci_class(struct pci_dev *dev)
+{
+       u32 class;
+
+#ifdef CONFIG_PCI_IOV
+       if (dev->is_virtfn)
+               return dev->physfn->sriov->class;
+#endif
+       pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+       return class;
+}
+
+static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device)
+{
+#ifdef CONFIG_PCI_IOV
+       if (dev->is_virtfn) {
+               *vendor = dev->physfn->sriov->subsystem_vendor;
+               *device = dev->physfn->sriov->subsystem_device;
+               return;
+       }
+#endif
+       pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, vendor);
+       pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device);
+}
+
+static u8 pci_hdr_type(struct pci_dev *dev)
+{
+       u8 hdr_type;
+
+#ifdef CONFIG_PCI_IOV
+       if (dev->is_virtfn)
+               return dev->physfn->sriov->hdr_type;
+#endif
+       pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
+       return hdr_type;
+}
+
 #define LEGACY_IO_RESOURCE     (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
 static void pci_msi_setup_pci_dev(struct pci_dev *dev)
@@ -1454,8 +1512,7 @@ int pci_setup_device(struct pci_dev *dev)
        struct pci_bus_region region;
        struct resource *res;
 
-       if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
-               return -EIO;
+       hdr_type = pci_hdr_type(dev);
 
        dev->sysdata = dev->bus->sysdata;
        dev->dev.parent = dev->bus->bridge;
@@ -1477,7 +1534,8 @@ int pci_setup_device(struct pci_dev *dev)
                     dev->bus->number, PCI_SLOT(dev->devfn),
                     PCI_FUNC(dev->devfn));
 
-       pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
+       class = pci_class(dev);
+
        dev->revision = class & 0xff;
        dev->class = class >> 8;                    /* upper 3 bytes */
 
@@ -1517,8 +1575,8 @@ int pci_setup_device(struct pci_dev *dev)
                        goto bad;
                pci_read_irq(dev);
                pci_read_bases(dev, 6, PCI_ROM_ADDRESS);
-               pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &dev->subsystem_vendor);
-               pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device);
+
+               pci_subsystem_ids(dev, &dev->subsystem_vendor, &dev->subsystem_device);
 
                /*
                 * Do the ugly legacy mode stuff here rather than broken chip
@@ -2121,6 +2179,9 @@ static void pci_init_capabilities(struct pci_dev *dev)
 
        /* Advanced Error Reporting */
        pci_aer_init(dev);
+
+       if (pci_probe_reset_function(dev) == 0)
+               dev->reset_fn = 1;
 }
 
 /*
index 58a662e3c4a667ee62fbba934cdc84d0ed72fa84..1ee8927a06357412c2163bc22304ef9a209aa660 100644 (file)
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     Procfs interface for the PCI bus.
+ * Procfs interface for the PCI bus
  *
- *     Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
  */
 
 #include <linux/init.h>
index fbe1127086c830d7ac99719b4efd79f1ea46d21e..76eaf4ad76520df9fbe257de9aafd48fd841c365 100644 (file)
@@ -1,15 +1,15 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *  This file contains work-arounds for many known PCI hardware
- *  bugs.  Devices present only on certain architectures (host
- *  bridges et cetera) should be handled in arch-specific code.
+ * This file contains work-arounds for many known PCI hardware bugs.
+ * Devices present only on certain architectures (host bridges et cetera)
+ * should be handled in arch-specific code.
  *
- *  Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
+ * Note: any quirks for hotpluggable devices must _NOT_ be declared __init.
  *
- *  Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
  *
- *  Init/reset quirks for USB host controllers should be in the
- *  USB quirks file, where their drivers can access reuse it.
+ * Init/reset quirks for USB host controllers should be in the USB quirks
+ * file, where their drivers can use them.
  */
 
 #include <linux/types.h>
@@ -3002,16 +3002,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb);
 static ktime_t fixup_debug_start(struct pci_dev *dev,
                                 void (*fn)(struct pci_dev *dev))
 {
-       ktime_t calltime = 0;
+       if (initcall_debug)
+               pci_info(dev, "calling  %pF @ %i\n", fn, task_pid_nr(current));
 
-       pci_dbg(dev, "calling %pF\n", fn);
-       if (initcall_debug) {
-               pr_debug("calling  %pF @ %i for %s\n",
-                        fn, task_pid_nr(current), dev_name(&dev->dev));
-               calltime = ktime_get();
-       }
-
-       return calltime;
+       return ktime_get();
 }
 
 static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
@@ -3020,13 +3014,11 @@ static void fixup_debug_report(struct pci_dev *dev, ktime_t calltime,
        ktime_t delta, rettime;
        unsigned long long duration;
 
-       if (initcall_debug) {
-               rettime = ktime_get();
-               delta = ktime_sub(rettime, calltime);
-               duration = (unsigned long long) ktime_to_ns(delta) >> 10;
-               pr_debug("pci fixup %pF returned after %lld usecs for %s\n",
-                        fn, duration, dev_name(&dev->dev));
-       }
+       rettime = ktime_get();
+       delta = ktime_sub(rettime, calltime);
+       duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+       if (initcall_debug || duration > 10000)
+               pci_info(dev, "%pF took %lld usecs\n", fn, duration);
 }
 
 /*
@@ -3775,6 +3767,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9182,
 /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c46 */
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x91a0,
                         quirk_dma_func1_alias);
+/* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c127 */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220,
+                        quirk_dma_func1_alias);
 /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */
 DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230,
                         quirk_dma_func1_alias);
@@ -4393,6 +4388,15 @@ static const struct pci_dev_acs_enabled {
        { PCI_VENDOR_ID_CAVIUM, PCI_ANY_ID, pci_quirk_cavium_acs },
        /* APM X-Gene */
        { PCI_VENDOR_ID_AMCC, 0xE004, pci_quirk_xgene_acs },
+       /* Ampere Computing */
+       { PCI_VENDOR_ID_AMPERE, 0xE005, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE006, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE007, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE008, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE009, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE00A, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE00B, pci_quirk_xgene_acs },
+       { PCI_VENDOR_ID_AMPERE, 0xE00C, pci_quirk_xgene_acs },
        { 0 }
 };
 
index 374a33443be996e052c243e590b5a4bcebe375cb..a7b5c37a85ec3d793d3b67d54db42bab03be828f 100644 (file)
@@ -1,11 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/rom.c
+ * PCI ROM access routines
  *
  * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
  * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
- *
- * PCI ROM access routines
  */
 #include <linux/kernel.h>
 #include <linux/export.h>
index bc1e023f13530d588f8d75eb826b8b23f0dd35e9..2b5f720862d37e70a2b6f75c8264dd042cb332ee 100644 (file)
@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     PCI searching functions.
+ * PCI searching functions
  *
- *     Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
+ * Copyright (C) 1993 -- 1997 Drew Eckhardt, Frederic Potter,
  *                                     David Mosberger-Tang
- *     Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
- *     Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 1997 -- 2000 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2003 -- 2004 Greg Kroah-Hartman <greg@kroah.com>
  */
 
 #include <linux/pci.h>
index 3cce29a069e6a70e50f30088c6f8274d85c5d5dd..072784f55ea5b0d1bec4b20952df0b918cad1852 100644 (file)
@@ -1,16 +1,12 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     drivers/pci/setup-bus.c
+ * Support routines for initializing a PCI subsystem
  *
  * Extruded from code written by
  *      Dave Rusling (david.rusling@reo.mts.dec.com)
  *      David Mosberger (davidm@cs.arizona.edu)
  *     David Miller (davem@redhat.com)
  *
- * Support routines for initializing a PCI subsystem.
- */
-
-/*
  * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
  *          PCI-PCI bridges cleanup, sorted resource allocation.
  * Feb 2002, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
index 5ad4ee7d7b1e973d9192419b5b50a4cacaf9a0ad..7129494754dd7396000789f0e2e297365d639f79 100644 (file)
@@ -1,13 +1,11 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     drivers/pci/setup-irq.c
+ * Support routines for initializing a PCI subsystem
  *
  * Extruded from code written by
  *      Dave Rusling (david.rusling@reo.mts.dec.com)
  *      David Mosberger (davidm@cs.arizona.edu)
  *     David Miller (davem@redhat.com)
- *
- * Support routines for initializing a PCI subsystem.
  */
 
 
index 369d48d6c6f1a53d4bc6ab5dd0b13a33eeccd90b..1ef01d79b52e3b74c428d93660bdcb4ff83d6ed8 100644 (file)
@@ -1,18 +1,14 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     drivers/pci/setup-res.c
+ * Support routines for initializing a PCI subsystem
  *
  * Extruded from code written by
  *      Dave Rusling (david.rusling@reo.mts.dec.com)
  *      David Mosberger (davidm@cs.arizona.edu)
  *     David Miller (davem@redhat.com)
  *
- * Support routines for initializing a PCI subsystem.
- */
-
-/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
-
-/*
+ * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de>
+ *
  * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
  *          Resource sorting
  */
index d10f556dc03ea9fd546a8b1a2c656d7cb8403b31..e634229ece8957607b881e4f1e93c6b7028c85fe 100644 (file)
@@ -1,6 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * drivers/pci/slot.c
  * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
  * Copyright (C) 2006-2009 Hewlett-Packard Development Company, L.P.
  *     Alex Chiang <achiang@hp.com>
@@ -76,6 +75,7 @@ static const char *pci_bus_speed_strings[] = {
        "2.5 GT/s PCIe",        /* 0x14 */
        "5.0 GT/s PCIe",        /* 0x15 */
        "8.0 GT/s PCIe",        /* 0x16 */
+       "16.0 GT/s PCIe",       /* 0x17 */
 };
 
 static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf)
index e725f99b5479dce8eeb0eca3e9cb27b75087764a..d96626c614f5674b4de24b1c11bcb13a456a6ca5 100644 (file)
@@ -1,11 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- *     pci_syscall.c
- *
- * For architectures where we want to allow direct access
- * to the PCI config stuff - it would probably be preferable
- * on PCs too, but there people just do it by hand with the
- * magic northbridge registers..
+ * For architectures where we want to allow direct access to the PCI config
+ * stuff - it would probably be preferable on PCs too, but there people
+ * just do it by hand with the magic northbridge registers.
  */
 
 #include <linux/errno.h>
index f24c3600be73fb8392918590dd932a0f41a89583..00b8b5d37056755d09d8dee020b53fb4b6d400c2 100644 (file)
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * File:       vpd.c
- * Purpose:    Provide PCI VPD support
+ * PCI VPD support
  *
  * Copyright (C) 2010 Broadcom Corporation.
  */
index 8785014f656eb93c5528741acd794be245974cab..eba6e33147a2fd355ff5de0efa6654885b36f939 100644 (file)
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Xen PCI Frontend.
+ * Xen PCI Frontend
  *
- *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
  */
 #include <linux/module.h>
 #include <linux/init.h>
index 9a68914100ad6b7e12c1464779b433d337cca7ee..bb655854713d8f41e6e8fb957b785171b53017da 100644 (file)
@@ -2880,8 +2880,9 @@ static int tsi721_probe(struct pci_dev *pdev,
                                 "Invalid MRRS override value %d", pcie_mrrs);
        }
 
-       /* Adjust PCIe completion timeout. */
-       pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2);
+       /* Set PCIe completion timeout to 1-10ms */
+       pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2,
+                                          PCI_EXP_DEVCTL2_COMP_TIMEOUT, 0x2);
 
        /*
         * FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
index c9608b0b80c602a7df9780f37eaef45948d60641..ba4dd54f2c8214c81af5449088982e604cb8d3aa 100644 (file)
@@ -215,7 +215,7 @@ struct acpi_device_flags {
        u32 of_compatible_ok:1;
        u32 coherent_dma:1;
        u32 cca_seen:1;
-       u32 serial_bus_slave:1;
+       u32 enumeration_by_parent:1;
        u32 reserved:19;
 };
 
index b4531e3b212092e37032a68c14eba2dcff76f504..5a59931666545bca578b2119b07cdf6a03b53f05 100644 (file)
@@ -351,6 +351,8 @@ static inline void writesq(volatile void __iomem *addr, const void *buffer,
 #define IO_SPACE_LIMIT 0xffff
 #endif
 
+#include <linux/logic_pio.h>
+
 /*
  * {in,out}{b,w,l}() access little endian I/O. {in,out}{b,w,l}_p() can be
  * implemented on hardware that needs an additional delay for I/O accesses to
@@ -899,7 +901,7 @@ static inline void iounmap(void __iomem *addr)
 #define ioport_map ioport_map
 static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
 {
-       return PCI_IOBASE + (port & IO_SPACE_LIMIT);
+       return PCI_IOBASE + (port & MMIO_UPPER_LIMIT);
 }
 #endif
 
diff --git a/include/linux/logic_pio.h b/include/linux/logic_pio.h
new file mode 100644 (file)
index 0000000..cbd9d84
--- /dev/null
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ */
+
+#ifndef __LINUX_LOGIC_PIO_H
+#define __LINUX_LOGIC_PIO_H
+
+#include <linux/fwnode.h>
+
+enum {
+       LOGIC_PIO_INDIRECT,             /* Indirect IO flag */
+       LOGIC_PIO_CPU_MMIO,             /* Memory-mapped IO flag */
+};
+
+struct logic_pio_hwaddr {
+       struct list_head list;
+       struct fwnode_handle *fwnode;
+       resource_size_t hw_start;
+       resource_size_t io_start;
+       resource_size_t size; /* range size populated */
+       unsigned long flags;
+
+       void *hostdata;
+       const struct logic_pio_host_ops *ops;
+};
+
+struct logic_pio_host_ops {
+       u32 (*in)(void *hostdata, unsigned long addr, size_t dwidth);
+       void (*out)(void *hostdata, unsigned long addr, u32 val,
+                   size_t dwidth);
+       u32 (*ins)(void *hostdata, unsigned long addr, void *buffer,
+                  size_t dwidth, unsigned int count);
+       void (*outs)(void *hostdata, unsigned long addr, const void *buffer,
+                    size_t dwidth, unsigned int count);
+};
+
+#ifdef CONFIG_INDIRECT_PIO
+u8 logic_inb(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+u16 logic_inw(unsigned long addr);
+u32 logic_inl(unsigned long addr);
+void logic_outb(u8 value, unsigned long addr);
+void logic_outw(u16 value, unsigned long addr);
+void logic_outl(u32 value, unsigned long addr);
+void logic_insb(unsigned long addr, void *buffer, unsigned int count);
+void logic_insl(unsigned long addr, void *buffer, unsigned int count);
+void logic_insw(unsigned long addr, void *buffer, unsigned int count);
+void logic_outsb(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsw(unsigned long addr, const void *buffer, unsigned int count);
+void logic_outsl(unsigned long addr, const void *buffer, unsigned int count);
+
+#ifndef inb
+#define inb logic_inb
+#endif
+
+#ifndef inw
+#define inw logic_inw
+#endif
+
+#ifndef inl
+#define inl logic_inl
+#endif
+
+#ifndef outb
+#define outb logic_outb
+#endif
+
+#ifndef outw
+#define outw logic_outw
+#endif
+
+#ifndef outl
+#define outl logic_outl
+#endif
+
+#ifndef insb
+#define insb logic_insb
+#endif
+
+#ifndef insw
+#define insw logic_insw
+#endif
+
+#ifndef insl
+#define insl logic_insl
+#endif
+
+#ifndef outsb
+#define outsb logic_outsb
+#endif
+
+#ifndef outsw
+#define outsw logic_outsw
+#endif
+
+#ifndef outsl
+#define outsl logic_outsl
+#endif
+
+/*
+ * We reserve 0x4000 bytes for Indirect IO as so far this library is only
+ * used by the HiSilicon LPC Host. If needed, we can reserve a wider IO
+ * area by redefining the macro below.
+ */
+#define PIO_INDIRECT_SIZE 0x4000
+#define MMIO_UPPER_LIMIT (IO_SPACE_LIMIT - PIO_INDIRECT_SIZE)
+#else
+#define MMIO_UPPER_LIMIT IO_SPACE_LIMIT
+#endif /* CONFIG_INDIRECT_PIO */
+
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode);
+unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
+                       resource_size_t hw_addr, resource_size_t size);
+int logic_pio_register_range(struct logic_pio_hwaddr *newrange);
+resource_size_t logic_pio_to_hwaddr(unsigned long pio);
+unsigned long logic_pio_trans_cpuaddr(resource_size_t hw_addr);
+
+#endif /* __LINUX_LOGIC_PIO_H */
index 024a1beda008c69d21bd65fe8d1a36cc92a6021a..283953c90b5277e48d5212b657743c0e081f0e2b 100644 (file)
@@ -256,6 +256,7 @@ enum pci_bus_speed {
        PCIE_SPEED_2_5GT                = 0x14,
        PCIE_SPEED_5_0GT                = 0x15,
        PCIE_SPEED_8_0GT                = 0x16,
+       PCIE_SPEED_16_0GT               = 0x17,
        PCI_SPEED_UNKNOWN               = 0xff,
 };
 
@@ -469,6 +470,9 @@ struct pci_host_bridge {
        struct msi_controller *msi;
        unsigned int    ignore_reset_delay:1;   /* For entire hierarchy */
        unsigned int    no_ext_tags:1;          /* No Extended Tags */
+       unsigned int    native_aer:1;           /* OS may use PCIe AER */
+       unsigned int    native_hotplug:1;       /* OS may use PCIe hotplug */
+       unsigned int    native_pme:1;           /* OS may use PCIe PME */
        /* Resource alignment requirements */
        resource_size_t (*align_resource)(struct pci_dev *dev,
                        const struct resource *res,
@@ -949,11 +953,6 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
 struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn);
 struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
                                            unsigned int devfn);
-static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
-                                                  unsigned int devfn)
-{
-       return pci_get_domain_bus_and_slot(0, bus, devfn);
-}
 struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from);
 int pci_dev_present(const struct pci_device_id *ids);
 
@@ -1082,7 +1081,11 @@ int pcie_get_mps(struct pci_dev *dev);
 int pcie_set_mps(struct pci_dev *dev, int mps);
 int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed,
                          enum pcie_link_width *width);
-void pcie_flr(struct pci_dev *dev);
+u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
+                            enum pci_bus_speed *speed,
+                            enum pcie_link_width *width);
+void pcie_print_link_status(struct pci_dev *dev);
+int pcie_flr(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
 int pci_reset_function_locked(struct pci_dev *dev);
@@ -1095,7 +1098,7 @@ int pci_reset_bus(struct pci_bus *bus);
 int pci_try_reset_bus(struct pci_bus *bus);
 void pci_reset_secondary_bus(struct pci_dev *dev);
 void pcibios_reset_secondary_bus(struct pci_dev *dev);
-void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
+int pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
@@ -1226,7 +1229,8 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
                        void *alignf_data);
 
 
-int pci_register_io_range(phys_addr_t addr, resource_size_t size);
+int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
+                       resource_size_t size);
 unsigned long pci_address_to_pio(phys_addr_t addr);
 phys_addr_t pci_pio_to_address(unsigned long pio);
 int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
@@ -1295,7 +1299,6 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus);
 void pci_setup_bridge(struct pci_bus *bus);
 resource_size_t pcibios_window_alignment(struct pci_bus *bus,
                                         unsigned long type);
-resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
 
 #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0)
 #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1)
@@ -1446,10 +1449,8 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
 
 #ifdef CONFIG_PCIEPORTBUS
 extern bool pcie_ports_disabled;
-extern bool pcie_ports_auto;
 #else
 #define pcie_ports_disabled    true
-#define pcie_ports_auto                false
 #endif
 
 #ifdef CONFIG_PCIEASPM
@@ -1661,9 +1662,6 @@ static inline struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
 static inline struct pci_dev *pci_get_slot(struct pci_bus *bus,
                                                unsigned int devfn)
 { return NULL; }
-static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
-                                               unsigned int devfn)
-{ return NULL; }
 static inline struct pci_dev *pci_get_domain_bus_and_slot(int domain,
                                        unsigned int bus, unsigned int devfn)
 { return NULL; }
@@ -1923,6 +1921,7 @@ void pcibios_release_device(struct pci_dev *dev);
 void pcibios_penalize_isa_irq(int irq, int active);
 int pcibios_alloc_irq(struct pci_dev *dev);
 void pcibios_free_irq(struct pci_dev *dev);
+resource_size_t pcibios_default_alignment(void);
 
 #ifdef CONFIG_HIBERNATE_CALLBACKS
 extern struct dev_pm_ops pcibios_pm_ops;
@@ -1955,6 +1954,11 @@ int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs);
 int pci_sriov_get_totalvfs(struct pci_dev *dev);
 resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno);
 void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe);
+
+/* Arch may override these (weak) */
+int pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs);
+int pcibios_sriov_disable(struct pci_dev *pdev);
+resource_size_t pcibios_iov_resource_alignment(struct pci_dev *dev, int resno);
 #else
 static inline int pci_iov_virtfn_bus(struct pci_dev *dev, int id)
 {
@@ -2182,24 +2186,11 @@ int pci_parse_request_of_pci_ranges(struct device *dev,
 /* Arch may override this (weak) */
 struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus);
 
-static inline struct device_node *
-pci_device_to_OF_node(const struct pci_dev *pdev)
-{
-       return pdev ? pdev->dev.of_node : NULL;
-}
-
-static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
-{
-       return bus ? bus->dev.of_node : NULL;
-}
-
 #else  /* CONFIG_OF */
 static inline void pci_set_of_node(struct pci_dev *dev) { }
 static inline void pci_release_of_node(struct pci_dev *dev) { }
 static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
 static inline void pci_release_bus_of_node(struct pci_bus *bus) { }
-static inline struct device_node *
-pci_device_to_OF_node(const struct pci_dev *pdev) { return NULL; }
 static inline struct irq_domain *
 pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; }
 static inline int pci_parse_request_of_pci_ranges(struct device *dev,
@@ -2210,6 +2201,17 @@ static inline int pci_parse_request_of_pci_ranges(struct device *dev,
 }
 #endif  /* CONFIG_OF */
 
+static inline struct device_node *
+pci_device_to_OF_node(const struct pci_dev *pdev)
+{
+       return pdev ? pdev->dev.of_node : NULL;
+}
+
+static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
+{
+       return bus ? bus->dev.of_node : NULL;
+}
+
 #ifdef CONFIG_ACPI
 struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus);
 
@@ -2280,41 +2282,9 @@ static inline bool pci_is_thunderbolt_attached(struct pci_dev *pdev)
        return false;
 }
 
-/**
- * pci_uevent_ers - emit a uevent during recovery path of pci device
- * @pdev: pci device to check
- * @err_type: type of error event
- *
- */
-static inline void pci_uevent_ers(struct pci_dev *pdev,
-                                 enum  pci_ers_result err_type)
-{
-       int idx = 0;
-       char *envp[3];
-
-       switch (err_type) {
-       case PCI_ERS_RESULT_NONE:
-       case PCI_ERS_RESULT_CAN_RECOVER:
-               envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY";
-               envp[idx++] = "DEVICE_ONLINE=0";
-               break;
-       case PCI_ERS_RESULT_RECOVERED:
-               envp[idx++] = "ERROR_EVENT=SUCCESSFUL_RECOVERY";
-               envp[idx++] = "DEVICE_ONLINE=1";
-               break;
-       case PCI_ERS_RESULT_DISCONNECT:
-               envp[idx++] = "ERROR_EVENT=FAILED_RECOVERY";
-               envp[idx++] = "DEVICE_ONLINE=0";
-               break;
-       default:
-               break;
-       }
-
-       if (idx > 0) {
-               envp[idx++] = NULL;
-               kobject_uevent_env(&pdev->dev.kobj, KOBJ_CHANGE, envp);
-       }
-}
+#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH)
+void pci_uevent_ers(struct pci_dev *pdev, enum  pci_ers_result err_type);
+#endif
 
 /* Provide the legacy pci_dma_* API */
 #include <linux/pci-dma-compat.h>
index a6b30667a33147ed6f6cc9c648e013cfc3126189..f22124e9887d291c86651e2c4852ba34b6c8eed1 100644 (file)
 #define PCI_DEVICE_ID_IMS_TT3D         0x9135
 
 #define PCI_VENDOR_ID_AMCC             0x10e8
+#define PCI_VENDOR_ID_AMPERE           0x1def
 
 #define PCI_VENDOR_ID_INTERG           0x10ea
 #define PCI_DEVICE_ID_INTERG_1682      0x1682
 #define PCI_DEVICE_ID_SERVERWORKS_CSB6LPC 0x0227
 #define PCI_DEVICE_ID_SERVERWORKS_HT1100LD 0x0408
 
+#define PCI_VENDOR_ID_ALTERA           0x1172
+
 #define PCI_VENDOR_ID_SBE              0x1176
 #define PCI_DEVICE_ID_SBE_WANXL100     0x0301
 #define PCI_DEVICE_ID_SBE_WANXL200     0x0302
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h
deleted file mode 100644 (file)
index b69769d..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * File:       pcieport_if.h
- * Purpose:    PCI Express Port Bus Driver's IF Data Structure
- *
- * Copyright (C) 2004 Intel
- * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
- */
-
-#ifndef _PCIEPORT_IF_H_
-#define _PCIEPORT_IF_H_
-
-/* Port Type */
-#define PCIE_ANY_PORT                  (~0)
-
-/* Service Type */
-#define PCIE_PORT_SERVICE_PME_SHIFT    0       /* Power Management Event */
-#define PCIE_PORT_SERVICE_PME          (1 << PCIE_PORT_SERVICE_PME_SHIFT)
-#define PCIE_PORT_SERVICE_AER_SHIFT    1       /* Advanced Error Reporting */
-#define PCIE_PORT_SERVICE_AER          (1 << PCIE_PORT_SERVICE_AER_SHIFT)
-#define PCIE_PORT_SERVICE_HP_SHIFT     2       /* Native Hotplug */
-#define PCIE_PORT_SERVICE_HP           (1 << PCIE_PORT_SERVICE_HP_SHIFT)
-#define PCIE_PORT_SERVICE_VC_SHIFT     3       /* Virtual Channel */
-#define PCIE_PORT_SERVICE_VC           (1 << PCIE_PORT_SERVICE_VC_SHIFT)
-#define PCIE_PORT_SERVICE_DPC_SHIFT    4       /* Downstream Port Containment */
-#define PCIE_PORT_SERVICE_DPC          (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
-
-struct pcie_device {
-       int             irq;        /* Service IRQ/MSI/MSI-X Vector */
-       struct pci_dev *port;       /* Root/Upstream/Downstream Port */
-       u32             service;    /* Port service this device represents */
-       void            *priv_data; /* Service Private Data */
-       struct device   device;     /* Generic Device Interface */
-};
-#define to_pcie_device(d) container_of(d, struct pcie_device, device)
-
-static inline void set_service_data(struct pcie_device *dev, void *data)
-{
-       dev->priv_data = data;
-}
-
-static inline void *get_service_data(struct pcie_device *dev)
-{
-       return dev->priv_data;
-}
-
-struct pcie_port_service_driver {
-       const char *name;
-       int (*probe) (struct pcie_device *dev);
-       void (*remove) (struct pcie_device *dev);
-       int (*suspend) (struct pcie_device *dev);
-       int (*resume) (struct pcie_device *dev);
-
-       /* Device driver may resume normal operations */
-       void (*error_resume)(struct pci_dev *dev);
-
-       /* Link Reset Capability - AER service driver specific */
-       pci_ers_result_t (*reset_link) (struct pci_dev *dev);
-
-       int port_type;  /* Type of the port this driver can handle */
-       u32 service;    /* Port service this device represents */
-
-       struct device_driver driver;
-};
-#define to_service_driver(d) \
-       container_of(d, struct pcie_port_service_driver, driver)
-
-int pcie_port_service_register(struct pcie_port_service_driver *new);
-void pcie_port_service_unregister(struct pcie_port_service_driver *new);
-
-#endif /* _PCIEPORT_IF_H_ */
index 0c79eac5e9b8ef00deb54bf61bd7004207aabeb1..103ba797a8f31ea0dbaf783df576a5b14c0fe4be 100644 (file)
 #define  PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */
 #define  PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
 #define  PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */
+#define  PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */
 #define  PCI_EXP_LNKCAP_MLW    0x000003f0 /* Maximum Link Width */
 #define  PCI_EXP_LNKCAP_ASPMS  0x00000c00 /* ASPM Support */
 #define  PCI_EXP_LNKCAP_L0SEL  0x00007000 /* L0s Exit Latency */
 #define  PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */
 #define  PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */
 #define  PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */
+#define  PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */
 #define  PCI_EXP_LNKSTA_NLW    0x03f0  /* Negotiated Link Width */
 #define  PCI_EXP_LNKSTA_NLW_X1 0x0010  /* Current Link Width x1 */
 #define  PCI_EXP_LNKSTA_NLW_X2 0x0020  /* Current Link Width x2 */
 #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2      44      /* v2 endpoints without link end here */
 #define PCI_EXP_LNKCAP2                44      /* Link Capabilities 2 */
 #define  PCI_EXP_LNKCAP2_SLS_2_5GB     0x00000002 /* Supported Speed 2.5GT/s */
-#define  PCI_EXP_LNKCAP2_SLS_5_0GB     0x00000004 /* Supported Speed 5.0GT/s */
-#define  PCI_EXP_LNKCAP2_SLS_8_0GB     0x00000008 /* Supported Speed 8.0GT/s */
+#define  PCI_EXP_LNKCAP2_SLS_5_0GB     0x00000004 /* Supported Speed 5GT/s */
+#define  PCI_EXP_LNKCAP2_SLS_8_0GB     0x00000008 /* Supported Speed 8GT/s */
+#define  PCI_EXP_LNKCAP2_SLS_16_0GB    0x00000010 /* Supported Speed 16GT/s */
 #define  PCI_EXP_LNKCAP2_CROSSLINK     0x00000100 /* Crosslink supported */
 #define PCI_EXP_LNKCTL2                48      /* Link Control 2 */
 #define PCI_EXP_LNKSTA2                50      /* Link Status 2 */
index e96089499371091938083bbc4cbdc45333d3aa79..5fe577673b985d91c68d8a907cebc4641290e9af 100644 (file)
@@ -55,6 +55,22 @@ config ARCH_USE_CMPXCHG_LOCKREF
 config ARCH_HAS_FAST_MULTIPLIER
        bool
 
+config INDIRECT_PIO
+       bool "Access I/O in non-MMIO mode"
+       depends on ARM64
+       help
+         On some platforms where no separate I/O space exists, there are I/O
+         hosts which can not be accessed in MMIO mode. Using the logical PIO
+         mechanism, the host-local I/O resource can be mapped into system
+         logic PIO space shared with MMIO hosts, such as PCI/PCIe, then the
+         system can access the I/O devices with the mapped-logic PIO through
+         I/O accessors.
+
+         This way has relatively little I/O performance cost. Please make
+         sure your devices really need this configure item enabled.
+
+         When in doubt, say N.
+
 config CRC_CCITT
        tristate "CRC-CCITT functions"
        help
index a90d4fcd748fcc658a1582f69e205913178017d7..4a9eacda3c8bf41b97e8d7bcf48a61e925aad457 100644 (file)
@@ -81,6 +81,8 @@ obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
 obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o
 obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o
 
+obj-y += logic_pio.o
+
 obj-$(CONFIG_GENERIC_HWEIGHT) += hweight.o
 
 obj-$(CONFIG_BTREE) += btree.o
diff --git a/lib/logic_pio.c b/lib/logic_pio.c
new file mode 100644 (file)
index 0000000..feea48f
--- /dev/null
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 HiSilicon Limited, All Rights Reserved.
+ * Author: Gabriele Paoloni <gabriele.paoloni@huawei.com>
+ * Author: Zhichang Yuan <yuanzhichang@hisilicon.com>
+ */
+
+#define pr_fmt(fmt)    "LOGIC PIO: " fmt
+
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/logic_pio.h>
+#include <linux/mm.h>
+#include <linux/rculist.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+/* The unique hardware address list */
+static LIST_HEAD(io_range_list);
+static DEFINE_MUTEX(io_range_mutex);
+
+/* Consider a kernel general helper for this */
+#define in_range(b, first, len)        ((b) >= (first) && (b) < (first) + (len))
+
+/**
+ * logic_pio_register_range - register logical PIO range for a host
+ * @new_range: pointer to the IO range to be registered.
+ *
+ * Returns 0 on success, the error code in case of failure.
+ *
+ * Register a new IO range node in the IO range list.
+ */
+int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
+{
+       struct logic_pio_hwaddr *range;
+       resource_size_t start;
+       resource_size_t end;
+       resource_size_t mmio_sz = 0;
+       resource_size_t iio_sz = MMIO_UPPER_LIMIT;
+       int ret = 0;
+
+       if (!new_range || !new_range->fwnode || !new_range->size)
+               return -EINVAL;
+
+       start = new_range->hw_start;
+       end = new_range->hw_start + new_range->size;
+
+       mutex_lock(&io_range_mutex);
+       list_for_each_entry_rcu(range, &io_range_list, list) {
+               if (range->fwnode == new_range->fwnode) {
+                       /* range already there */
+                       goto end_register;
+               }
+               if (range->flags == LOGIC_PIO_CPU_MMIO &&
+                   new_range->flags == LOGIC_PIO_CPU_MMIO) {
+                       /* for MMIO ranges we need to check for overlap */
+                       if (start >= range->hw_start + range->size ||
+                           end < range->hw_start) {
+                               mmio_sz += range->size;
+                       } else {
+                               ret = -EFAULT;
+                               goto end_register;
+                       }
+               } else if (range->flags == LOGIC_PIO_INDIRECT &&
+                          new_range->flags == LOGIC_PIO_INDIRECT) {
+                       iio_sz += range->size;
+               }
+       }
+
+       /* range not registered yet, check for available space */
+       if (new_range->flags == LOGIC_PIO_CPU_MMIO) {
+               if (mmio_sz + new_range->size - 1 > MMIO_UPPER_LIMIT) {
+                       /* if it's too big check if 64K space can be reserved */
+                       if (mmio_sz + SZ_64K - 1 > MMIO_UPPER_LIMIT) {
+                               ret = -E2BIG;
+                               goto end_register;
+                       }
+                       new_range->size = SZ_64K;
+                       pr_warn("Requested IO range too big, new size set to 64K\n");
+               }
+               new_range->io_start = mmio_sz;
+       } else if (new_range->flags == LOGIC_PIO_INDIRECT) {
+               if (iio_sz + new_range->size - 1 > IO_SPACE_LIMIT) {
+                       ret = -E2BIG;
+                       goto end_register;
+               }
+               new_range->io_start = iio_sz;
+       } else {
+               /* invalid flag */
+               ret = -EINVAL;
+               goto end_register;
+       }
+
+       list_add_tail_rcu(&new_range->list, &io_range_list);
+
+end_register:
+       mutex_unlock(&io_range_mutex);
+       return ret;
+}
+
+/**
+ * find_io_range_by_fwnode - find logical PIO range for given FW node
+ * @fwnode: FW node handle associated with logical PIO range
+ *
+ * Returns pointer to node on success, NULL otherwise.
+ *
+ * Traverse the io_range_list to find the registered node for @fwnode.
+ */
+struct logic_pio_hwaddr *find_io_range_by_fwnode(struct fwnode_handle *fwnode)
+{
+       struct logic_pio_hwaddr *range;
+
+       list_for_each_entry_rcu(range, &io_range_list, list) {
+               if (range->fwnode == fwnode)
+                       return range;
+       }
+       return NULL;
+}
+
+/* Return a registered range given an input PIO token */
+static struct logic_pio_hwaddr *find_io_range(unsigned long pio)
+{
+       struct logic_pio_hwaddr *range;
+
+       list_for_each_entry_rcu(range, &io_range_list, list) {
+               if (in_range(pio, range->io_start, range->size))
+                       return range;
+       }
+       pr_err("PIO entry token %lx invalid\n", pio);
+       return NULL;
+}
+
+/**
+ * logic_pio_to_hwaddr - translate logical PIO to HW address
+ * @pio: logical PIO value
+ *
+ * Returns HW address if valid, ~0 otherwise.
+ *
+ * Translate the input logical PIO to the corresponding hardware address.
+ * The input PIO should be unique in the whole logical PIO space.
+ */
+resource_size_t logic_pio_to_hwaddr(unsigned long pio)
+{
+       struct logic_pio_hwaddr *range;
+
+       range = find_io_range(pio);
+       if (range)
+               return range->hw_start + pio - range->io_start;
+
+       return (resource_size_t)~0;
+}
+
+/**
+ * logic_pio_trans_hwaddr - translate HW address to logical PIO
+ * @fwnode: FW node reference for the host
+ * @addr: Host-relative HW address
+ * @size: size to translate
+ *
+ * Returns Logical PIO value if successful, ~0UL otherwise
+ */
+unsigned long logic_pio_trans_hwaddr(struct fwnode_handle *fwnode,
+                                    resource_size_t addr, resource_size_t size)
+{
+       struct logic_pio_hwaddr *range;
+
+       range = find_io_range_by_fwnode(fwnode);
+       if (!range || range->flags == LOGIC_PIO_CPU_MMIO) {
+               pr_err("IO range not found or invalid\n");
+               return ~0UL;
+       }
+       if (range->size < size) {
+               pr_err("resource size %pa cannot fit in IO range size %pa\n",
+                      &size, &range->size);
+               return ~0UL;
+       }
+       return addr - range->hw_start + range->io_start;
+}
+
+unsigned long logic_pio_trans_cpuaddr(resource_size_t addr)
+{
+       struct logic_pio_hwaddr *range;
+
+       list_for_each_entry_rcu(range, &io_range_list, list) {
+               if (range->flags != LOGIC_PIO_CPU_MMIO)
+                       continue;
+               if (in_range(addr, range->hw_start, range->size))
+                       return addr - range->hw_start + range->io_start;
+       }
+       pr_err("addr %llx not registered in io_range_list\n",
+              (unsigned long long) addr);
+       return ~0UL;
+}
+
+#if defined(CONFIG_INDIRECT_PIO) && defined(PCI_IOBASE)
+#define BUILD_LOGIC_IO(bw, type)                                       \
+type logic_in##bw(unsigned long addr)                                  \
+{                                                                      \
+       type ret = (type)~0;                                            \
+                                                                       \
+       if (addr < MMIO_UPPER_LIMIT) {                                  \
+               ret = read##bw(PCI_IOBASE + addr);                      \
+       } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+               struct logic_pio_hwaddr *entry = find_io_range(addr);   \
+                                                                       \
+               if (entry && entry->ops)                                \
+                       ret = entry->ops->in(entry->hostdata,           \
+                                       addr, sizeof(type));            \
+               else                                                    \
+                       WARN_ON_ONCE(1);                                \
+       }                                                               \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+void logic_out##bw(type value, unsigned long addr)                     \
+{                                                                      \
+       if (addr < MMIO_UPPER_LIMIT) {                                  \
+               write##bw(value, PCI_IOBASE + addr);                    \
+       } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+               struct logic_pio_hwaddr *entry = find_io_range(addr);   \
+                                                                       \
+               if (entry && entry->ops)                                \
+                       entry->ops->out(entry->hostdata,                \
+                                       addr, value, sizeof(type));     \
+               else                                                    \
+                       WARN_ON_ONCE(1);                                \
+       }                                                               \
+}                                                                      \
+                                                                       \
+void logic_ins##bw(unsigned long addr, void *buffer,           \
+                  unsigned int count)                                  \
+{                                                                      \
+       if (addr < MMIO_UPPER_LIMIT) {                                  \
+               reads##bw(PCI_IOBASE + addr, buffer, count);            \
+       } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+               struct logic_pio_hwaddr *entry = find_io_range(addr);   \
+                                                                       \
+               if (entry && entry->ops)                                \
+                       entry->ops->ins(entry->hostdata,                \
+                               addr, buffer, sizeof(type), count);     \
+               else                                                    \
+                       WARN_ON_ONCE(1);                                \
+       }                                                               \
+                                                                       \
+}                                                                      \
+                                                                       \
+void logic_outs##bw(unsigned long addr, const void *buffer,            \
+                   unsigned int count)                                 \
+{                                                                      \
+       if (addr < MMIO_UPPER_LIMIT) {                                  \
+               writes##bw(PCI_IOBASE + addr, buffer, count);           \
+       } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \
+               struct logic_pio_hwaddr *entry = find_io_range(addr);   \
+                                                                       \
+               if (entry && entry->ops)                                \
+                       entry->ops->outs(entry->hostdata,               \
+                               addr, buffer, sizeof(type), count);     \
+               else                                                    \
+                       WARN_ON_ONCE(1);                                \
+       }                                                               \
+}
+
+BUILD_LOGIC_IO(b, u8)
+EXPORT_SYMBOL(logic_inb);
+EXPORT_SYMBOL(logic_insb);
+EXPORT_SYMBOL(logic_outb);
+EXPORT_SYMBOL(logic_outsb);
+
+BUILD_LOGIC_IO(w, u16)
+EXPORT_SYMBOL(logic_inw);
+EXPORT_SYMBOL(logic_insw);
+EXPORT_SYMBOL(logic_outw);
+EXPORT_SYMBOL(logic_outsw);
+
+BUILD_LOGIC_IO(l, u32)
+EXPORT_SYMBOL(logic_inl);
+EXPORT_SYMBOL(logic_insl);
+EXPORT_SYMBOL(logic_outl);
+EXPORT_SYMBOL(logic_outsl);
+
+#endif /* CONFIG_INDIRECT_PIO && PCI_IOBASE */