diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-06 18:31:06 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-06 18:31:06 -0700 |
commit | 3c0d551e02b2590fa71a1354f2f1994551a33315 (patch) | |
tree | da94dc3559fe0c63fcc13852b53ba3d3b08d5292 | |
parent | 19fd08b85bc7e0502b55cd726f466df82ee7e777 (diff) | |
parent | 5f764419098671cfffcfc44f8a5220afd3e37864 (diff) | |
download | op-kernel-dev-3c0d551e02b2590fa71a1354f2f1994551a33315.zip op-kernel-dev-3c0d551e02b2590fa71a1354f2f1994551a33315.tar.gz |
Merge tag 'pci-v4.17-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI updates from Bjorn Helgaas:
- move pci_uevent_ers() out of pci.h (Michael Ellerman)
- skip ASPM common clock warning if BIOS already configured it (Sinan
Kaya)
- fix ASPM Coverity warning about threshold_ns (Gustavo A. R. Silva)
- remove last user of pci_get_bus_and_slot() and the function itself
(Sinan Kaya)
- add decoding for 16 GT/s link speed (Jay Fang)
- add interfaces to get max link speed and width (Tal Gilboa)
- add pcie_bandwidth_capable() to compute max supported link bandwidth
(Tal Gilboa)
- add pcie_bandwidth_available() to compute bandwidth available to
device (Tal Gilboa)
- add pcie_print_link_status() to log link speed and whether it's
limited (Tal Gilboa)
- use PCI core interfaces to report when device performance may be
limited by its slot instead of doing it in each driver (Tal Gilboa)
- fix possible cpqphp NULL pointer dereference (Shawn Lin)
- rescan more of the hierarchy on ACPI hotplug to fix Thunderbolt/xHCI
hotplug (Mika Westerberg)
- add support for PCI I/O port space that's neither directly accessible
via CPU in/out instructions nor directly mapped into CPU physical
memory space. This is fairly intrusive and includes minor changes to
interfaces used for I/O space on most platforms (Zhichang Yuan, John
Garry)
- add support for HiSilicon Hip06/Hip07 LPC I/O space (Zhichang Yuan,
John Garry)
- use PCI_EXP_DEVCTL2_COMP_TIMEOUT in rapidio/tsi721 (Bjorn Helgaas)
- remove possible NULL pointer dereference in of_pci_bus_find_domain_nr()
(Shawn Lin)
- report quirk timings with dev_info (Bjorn Helgaas)
- report quirks that take longer than 10ms (Bjorn Helgaas)
- add and use Altera Vendor ID (Johannes Thumshirn)
- tidy Makefiles and comments (Bjorn Helgaas)
- don't set up INTx if MSI or MSI-X is enabled to align cris, frv,
ia64, and mn10300 with x86 (Bjorn Helgaas)
- move pcieport_if.h to drivers/pci/pcie/ to encapsulate it (Frederick
Lawler)
- merge pcieport_if.h into portdrv.h (Bjorn Helgaas)
- move workaround for BIOS PME issue from portdrv to PCI core (Bjorn
Helgaas)
- completely disable portdrv with "pcie_ports=compat" (Bjorn Helgaas)
- remove portdrv link order dependency (Bjorn Helgaas)
- remove support for unused VC portdrv service (Bjorn Helgaas)
- simplify portdrv feature permission checking (Bjorn Helgaas)
- remove "pcie_hp=nomsi" parameter (use "pci=nomsi" instead) (Bjorn
Helgaas)
- remove unnecessary "pcie_ports=auto" parameter (Bjorn Helgaas)
- use cached AER capability offset (Frederick Lawler)
- don't enable DPC if BIOS hasn't granted AER control (Mika Westerberg)
- rename pcie-dpc.c to dpc.c (Bjorn Helgaas)
- use generic pci_mmap_resource_range() instead of powerpc and xtensa
arch-specific versions (David Woodhouse)
- support arbitrary PCI host bridge offsets on sparc (Yinghai Lu)
- remove System and Video ROM reservations on sparc (Bjorn Helgaas)
- probe for device reset support during enumeration instead of runtime
(Bjorn Helgaas)
- add ACS quirk for Ampere (née APM) root ports (Feng Kan)
- add function 1 DMA alias quirk for Marvell 88SE9220 (Thomas
Vincent-Cross)
- protect device restore with device lock (Sinan Kaya)
- handle failure of FLR gracefully (Sinan Kaya)
- handle CRS (config retry status) after device resets (Sinan Kaya)
- skip various config reads for SR-IOV VFs as an optimization
(KarimAllah Ahmed)
- consolidate VPD code in vpd.c (Bjorn Helgaas)
- add Tegra dependency on PCI_MSI_IRQ_DOMAIN (Arnd Bergmann)
- add DT support for R-Car r8a7743 (Biju Das)
- fix a PCI_EJECT vs PCI_BUS_RELATIONS race condition in Hyper-V host
bridge driver that causes a general protection fault (Dexuan Cui)
- fix Hyper-V host bridge hang in MSI setup on 1-vCPU VMs with SR-IOV
(Dexuan Cui)
- fix Hyper-V host bridge hang when ejecting a VF before setting up MSI
(Dexuan Cui)
- make several structures static (Fengguang Wu)
- increase number of MSI IRQs supported by Synopsys DesignWare bridges
from 32 to 256 (Gustavo Pimentel)
- implemented multiplexed IRQ domain API and remove obsolete MSI IRQ
API from DesignWare drivers (Gustavo Pimentel)
- add Tegra power management support (Manikanta Maddireddy)
- add Tegra loadable module support (Manikanta Maddireddy)
- handle 64-bit BARs correctly in endpoint support (Niklas Cassel)
- support optional regulator for HiSilicon STB (Shawn Guo)
- use regulator bulk API for Qualcomm apq8064 (Srinivas Kandagatla)
- support power supplies for Qualcomm msm8996 (Srinivas Kandagatla)
* tag 'pci-v4.17-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (123 commits)
MAINTAINERS: Add John Garry as maintainer for HiSilicon LPC driver
HISI LPC: Add ACPI support
ACPI / scan: Do not enumerate Indirect IO host children
ACPI / scan: Rename acpi_is_serial_bus_slave() for more general use
HISI LPC: Support the LPC host on Hip06/Hip07 with DT bindings
of: Add missing I/O range exception for indirect-IO devices
PCI: Apply the new generic I/O management on PCI IO hosts
PCI: Add fwnode handler as input param of pci_register_io_range()
PCI: Remove __weak tag from pci_register_io_range()
MAINTAINERS: Add missing /drivers/pci/cadence directory entry
fm10k: Report PCIe link properties with pcie_print_link_status()
net/mlx5e: Use pcie_bandwidth_available() to compute bandwidth
net/mlx5: Report PCIe link properties with pcie_print_link_status()
net/mlx4_core: Report PCIe link properties with pcie_print_link_status()
PCI: Add pcie_print_link_status() to log link speed and whether it's limited
PCI: Add pcie_bandwidth_available() to compute bandwidth available to device
misc: pci_endpoint_test: Handle 64-bit BARs properly
PCI: designware-ep: Make dw_pcie_ep_reset_bar() handle 64-bit BARs properly
PCI: endpoint: Make sure that BAR_5 does not have 64-bit flag set when clearing
PCI: endpoint: Make epc->ops->clear_bar()/pci_epc_clear_bar() take struct *epf_bar
...
118 files changed, 3491 insertions, 2365 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 7efed1c..683145d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3154,18 +3154,13 @@ 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 index 0000000..10bd35f9 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon-low-pin-count.txt @@ -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>; + }; +}; diff --git a/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt b/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt index c84bc02..760b4d7 100644 --- a/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt +++ b/Documentation/devicetree/bindings/pci/hisilicon-histb-pcie.txt @@ -34,6 +34,7 @@ Required properties Optional properties: - reset-gpios: The gpio to generate PCIe PERST# assert and deassert signal. +- vpcie-supply: The regulator in charge of PCIe port power. - phys: List of phandle and phy mode specifier, should be 0. - phy-names: Must be "phy". diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt index 3a6ce55..20227a8 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt @@ -78,7 +78,7 @@ Examples for MT7623: #reset-cells = <1>; }; - pcie: pcie-controller@1a140000 { + pcie: pcie@1a140000 { compatible = "mediatek,mt7623-pcie"; device_type = "pci"; reg = <0 0x1a140000 0 0x1000>, /* PCIe shared registers */ @@ -111,7 +111,6 @@ Examples for MT7623: 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */ pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -123,7 +122,6 @@ Examples for MT7623: }; pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -135,7 +133,6 @@ Examples for MT7623: }; pcie@2,0 { - device_type = "pci"; reg = <0x1000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -148,6 +145,7 @@ Examples for MT7623: }; Examples for MT2712: + pcie: pcie@11700000 { compatible = "mediatek,mt2712-pcie"; device_type = "pci"; @@ -169,7 +167,6 @@ Examples for MT2712: ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>; pcie0: pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -189,7 +186,6 @@ Examples for MT2712: }; pcie1: pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -210,6 +206,7 @@ Examples for MT2712: }; Examples for MT7622: + pcie: pcie@1a140000 { compatible = "mediatek,mt7622-pcie"; device_type = "pci"; @@ -243,7 +240,6 @@ Examples for MT7622: ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>; pcie0: pcie@0,0 { - device_type = "pci"; reg = <0x0000 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; @@ -263,7 +259,6 @@ Examples for MT7622: }; pcie1: pcie@1,0 { - device_type = "pci"; reg = <0x0800 0 0 0 0>; #address-cells = <3>; #size-cells = <2>; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 3c9d321..1fd703b 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -189,6 +189,10 @@ Value type: <phandle> Definition: A phandle to the analog power supply for IC which generates reference clock +- vddpe-3v3-supply: + Usage: optional + Value type: <phandle> + Definition: A phandle to the PCIe endpoint power supply - phys: Usage: required for apq8084 diff --git a/Documentation/devicetree/bindings/pci/rcar-pci.txt b/Documentation/devicetree/bindings/pci/rcar-pci.txt index 76ba3a6..1fb614e 100644 --- a/Documentation/devicetree/bindings/pci/rcar-pci.txt +++ b/Documentation/devicetree/bindings/pci/rcar-pci.txt @@ -1,13 +1,15 @@ * Renesas R-Car PCIe interface Required properties: -compatible: "renesas,pcie-r8a7779" for the R8A7779 SoC; +compatible: "renesas,pcie-r8a7743" for the R8A7743 SoC; + "renesas,pcie-r8a7779" for the R8A7779 SoC; "renesas,pcie-r8a7790" for the R8A7790 SoC; "renesas,pcie-r8a7791" for the R8A7791 SoC; "renesas,pcie-r8a7793" for the R8A7793 SoC; "renesas,pcie-r8a7795" for the R8A7795 SoC; "renesas,pcie-r8a7796" for the R8A7796 SoC; - "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 compatible device. + "renesas,pcie-rcar-gen2" for a generic R-Car Gen2 or + RZ/G1 compatible device. "renesas,pcie-rcar-gen3" for a generic R-Car Gen3 compatible device. When compatible with the generic version, nodes must list the diff --git a/MAINTAINERS b/MAINTAINERS index 0407846..5e929ac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6366,6 +6366,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> @@ -10820,6 +10827,7 @@ L: linux-pci@vger.kernel.org Q: http://patchwork.ozlabs.org/project/linux-pci/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/ S: Supported +F: drivers/pci/cadence/ F: drivers/pci/host/ F: drivers/pci/dwc/ diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index f5ec736..7ccc64d 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -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); } diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index d82802f..401c62a 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -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); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 446c796..fe9733f 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -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) { diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 220d0f3..41b20ed 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -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; diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 1e10fb26..38d46bc 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -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; diff --git a/arch/sparc/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h index ac17296..4e3d151 100644 --- a/arch/sparc/kernel/pci_impl.h +++ b/arch/sparc/kernel/pci_impl.h @@ -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; diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index 5c83798..d5a8215 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -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__ */ diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index d981f01..b7c7a60 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -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; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 6fc204a..0da18bd 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -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; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 490498e..cc234e6 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1525,11 +1525,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") || @@ -1561,7 +1575,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); @@ -1859,10 +1874,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 { @@ -1959,7 +1974,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; } @@ -1968,10 +1983,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) diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index ff70850..d1c0b60 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -29,6 +29,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 diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 3d473b8..b8f036c 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -5,6 +5,8 @@ # Interconnect bus drivers for ARM platforms obj-$(CONFIG_ARM_CCI) += arm-cci.o + +obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o # DPAA2 fsl-mc bus diff --git a/drivers/bus/hisi_lpc.c b/drivers/bus/hisi_lpc.c new file mode 100644 index 0000000..2d4611e --- /dev/null +++ b/drivers/bus/hisi_lpc.c @@ -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); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 07c07d5..84ca369 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -449,7 +449,10 @@ static int i915_getparam_ioctl(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; diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 320276f..fe8897e 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -534,12 +534,14 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, } for (bar = BAR_0; bar <= BAR_5; bar++) { - base = pci_ioremap_bar(pdev, bar); - if (!base) { - dev_err(dev, "failed to read BAR%d\n", bar); - WARN_ON(bar == test_reg_bar); + if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { + base = pci_ioremap_bar(pdev, bar); + if (!base) { + dev_err(dev, "failed to read BAR%d\n", bar); + WARN_ON(bar == test_reg_bar); + } + test->bar[bar] = base; } - test->bar[bar] = base; } test->base = test->bar[test_reg_bar]; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index cffcb18..c4a2b68 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -2122,91 +2122,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 @@ -2328,7 +2243,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); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 5af5019..bfef692 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -624,85 +624,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) { @@ -3476,7 +3397,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 */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d68f510..b29c1d9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4033,43 +4033,13 @@ 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 slow_pci_heuristic(struct mlx5_core_dev *mdev) { u32 link_speed = 0; u32 pci_bw = 0; 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_once(mdev, "Max link speed = %d, PCI BW = %d\n", link_speed, pci_bw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 13b6f66..63a8ea3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1044,6 +1044,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 */ diff --git a/drivers/of/address.c b/drivers/of/address.c index ce4d3d8..5334991 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -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; diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 9419709..952addc 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -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 diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 5e9a982..a3ad2fe 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -#include <linux/delay.h> #include <linux/pci.h> #include <linux/module.h> -#include <linux/sched/signal.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/wait.h> @@ -17,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 @@ -264,372 +262,6 @@ PCI_USER_WRITE_CONFIG(byte, u8) PCI_USER_WRITE_CONFIG(word, u16) PCI_USER_WRITE_CONFIG(dword, u32) -/* VPD access through PCI 2.2+ VPD capability */ - -/** - * pci_read_vpd - Read one entry from Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to read - * @buf: pointer to where to store result - */ -ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->read(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_read_vpd); - -/** - * pci_write_vpd - Write entry to Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to write - * @buf: buffer containing write data - */ -ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->write(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_write_vpd); - -/** - * pci_set_vpd_size - Set size of Vital Product Data space - * @dev: pci device struct - * @len: size of vpd space - */ -int pci_set_vpd_size(struct pci_dev *dev, size_t len) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->set_size(dev, len); -} -EXPORT_SYMBOL(pci_set_vpd_size); - -#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) - -/** - * pci_vpd_size - determine actual size of Vital Product Data - * @dev: pci device struct - * @old_size: current assumed size, also maximum allowed size - */ -static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) -{ - size_t off = 0; - unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ - - while (off < old_size && - pci_read_vpd(dev, off, 1, header) == 1) { - unsigned char tag; - - if (header[0] & PCI_VPD_LRDT) { - /* Large Resource Data Type Tag */ - tag = pci_vpd_lrdt_tag(header); - /* Only read length from known tag items */ - if ((tag == PCI_VPD_LTIN_ID_STRING) || - (tag == PCI_VPD_LTIN_RO_DATA) || - (tag == PCI_VPD_LTIN_RW_DATA)) { - if (pci_read_vpd(dev, off+1, 2, - &header[1]) != 2) { - pci_warn(dev, "invalid large VPD tag %02x size at offset %zu", - tag, off + 1); - return 0; - } - off += PCI_VPD_LRDT_TAG_SIZE + - pci_vpd_lrdt_size(header); - } - } else { - /* Short Resource Data Type Tag */ - off += PCI_VPD_SRDT_TAG_SIZE + - pci_vpd_srdt_size(header); - tag = pci_vpd_srdt_tag(header); - } - - if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ - return off; - - if ((tag != PCI_VPD_LTIN_ID_STRING) && - (tag != PCI_VPD_LTIN_RO_DATA) && - (tag != PCI_VPD_LTIN_RW_DATA)) { - pci_warn(dev, "invalid %s VPD tag %02x at offset %zu", - (header[0] & PCI_VPD_LRDT) ? "large" : "short", - tag, off); - return 0; - } - } - return 0; -} - -/* - * Wait for last operation to complete. - * This code has to spin since there is no other notification from the PCI - * hardware. Since the VPD is often implemented by serial attachment to an - * EEPROM, it may take many milliseconds to complete. - * - * Returns 0 on success, negative values indicate error. - */ -static int pci_vpd_wait(struct pci_dev *dev) -{ - struct pci_vpd *vpd = dev->vpd; - unsigned long timeout = jiffies + msecs_to_jiffies(125); - unsigned long max_sleep = 16; - u16 status; - int ret; - - if (!vpd->busy) - return 0; - - while (time_before(jiffies, timeout)) { - ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, - &status); - if (ret < 0) - return ret; - - if ((status & PCI_VPD_ADDR_F) == vpd->flag) { - vpd->busy = 0; - return 0; - } - - if (fatal_signal_pending(current)) - return -EINTR; - - usleep_range(10, max_sleep); - if (max_sleep < 1024) - max_sleep *= 2; - } - - pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n"); - return -ETIMEDOUT; -} - -static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) -{ - struct pci_vpd *vpd = dev->vpd; - int ret; - loff_t end = pos + count; - u8 *buf = arg; - - if (pos < 0) - return -EINVAL; - - if (!vpd->valid) { - vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); - } - - if (vpd->len == 0) - return -EIO; - - if (pos > vpd->len) - return 0; - - if (end > vpd->len) { - end = vpd->len; - count = end - pos; - } - - if (mutex_lock_killable(&vpd->lock)) - return -EINTR; - - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - - while (pos < end) { - u32 val; - unsigned int i, skip; - - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos & ~3); - if (ret < 0) - break; - vpd->busy = 1; - vpd->flag = PCI_VPD_ADDR_F; - ret = pci_vpd_wait(dev); - if (ret < 0) - break; - - ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); - if (ret < 0) - break; - - skip = pos & 3; - for (i = 0; i < sizeof(u32); i++) { - if (i >= skip) { - *buf++ = val; - if (++pos == end) - break; - } - val >>= 8; - } - } -out: - mutex_unlock(&vpd->lock); - return ret ? ret : count; -} - -static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) -{ - struct pci_vpd *vpd = dev->vpd; - const u8 *buf = arg; - loff_t end = pos + count; - int ret = 0; - - if (pos < 0 || (pos & 3) || (count & 3)) - return -EINVAL; - - if (!vpd->valid) { - vpd->valid = 1; - vpd->len = pci_vpd_size(dev, vpd->len); - } - - if (vpd->len == 0) - return -EIO; - - if (end > vpd->len) - return -EINVAL; - - if (mutex_lock_killable(&vpd->lock)) - return -EINTR; - - ret = pci_vpd_wait(dev); - if (ret < 0) - goto out; - - while (pos < end) { - u32 val; - - val = *buf++; - val |= *buf++ << 8; - val |= *buf++ << 16; - val |= *buf++ << 24; - - ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); - if (ret < 0) - break; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos | PCI_VPD_ADDR_F); - if (ret < 0) - break; - - vpd->busy = 1; - vpd->flag = 0; - ret = pci_vpd_wait(dev); - if (ret < 0) - break; - - pos += sizeof(u32); - } -out: - mutex_unlock(&vpd->lock); - return ret ? ret : count; -} - -static int pci_vpd_set_size(struct pci_dev *dev, size_t len) -{ - struct pci_vpd *vpd = dev->vpd; - - if (len == 0 || len > PCI_VPD_MAX_SIZE) - return -EIO; - - vpd->valid = 1; - vpd->len = len; - - return 0; -} - -static const struct pci_vpd_ops pci_vpd_ops = { - .read = pci_vpd_read, - .write = pci_vpd_write, - .set_size = pci_vpd_set_size, -}; - -static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_read_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - ssize_t ret; - - if (!tdev) - return -ENODEV; - - ret = pci_write_vpd(tdev, pos, count, arg); - pci_dev_put(tdev); - return ret; -} - -static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len) -{ - struct pci_dev *tdev = pci_get_slot(dev->bus, - PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - int ret; - - if (!tdev) - return -ENODEV; - - ret = pci_set_vpd_size(tdev, len); - pci_dev_put(tdev); - return ret; -} - -static const struct pci_vpd_ops pci_vpd_f0_ops = { - .read = pci_vpd_f0_read, - .write = pci_vpd_f0_write, - .set_size = pci_vpd_f0_set_size, -}; - -int pci_vpd_init(struct pci_dev *dev) -{ - struct pci_vpd *vpd; - u8 cap; - - cap = pci_find_capability(dev, PCI_CAP_ID_VPD); - if (!cap) - return -ENODEV; - - vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); - if (!vpd) - return -ENOMEM; - - vpd->len = PCI_VPD_MAX_SIZE; - if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) - vpd->ops = &pci_vpd_f0_ops; - else - vpd->ops = &pci_vpd_ops; - mutex_init(&vpd->lock); - vpd->cap = cap; - vpd->busy = 0; - vpd->valid = 0; - dev->vpd = vpd; - return 0; -} - -void pci_vpd_release(struct pci_dev *dev) -{ - kfree(dev->vpd); -} - /** * pci_cfg_access_lock - Lock PCI config reads/writes * @dev: pci device struct @@ -686,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; diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 6ad80a1..89305b5 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -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> diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 737d1c5..bc2ded4 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -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) diff --git a/drivers/pci/cadence/pcie-cadence-ep.c b/drivers/pci/cadence/pcie-cadence-ep.c index 3c3a977..3d8283e 100644 --- a/drivers/pci/cadence/pcie-cadence-ep.c +++ b/drivers/pci/cadence/pcie-cadence-ep.c @@ -77,16 +77,19 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, return 0; } -static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, + struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + dma_addr_t bar_phys = epf_bar->phys_addr; + enum pci_barno bar = epf_bar->barno; + int flags = epf_bar->flags; u32 addr0, addr1, reg, cfg, b, aperture, ctrl; u64 sz; /* BAR size is 2^(aperture + 7) */ - sz = max_t(size_t, size, CDNS_PCIE_EP_MIN_APERTURE); + sz = max_t(size_t, epf_bar->size, CDNS_PCIE_EP_MIN_APERTURE); /* * roundup_pow_of_two() returns an unsigned long, which is not suited * for 64bit values. @@ -103,6 +106,9 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar, if (is_64bits && (bar & 1)) return -EINVAL; + if (is_64bits && !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + if (is_64bits && is_prefetch) ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; else if (is_prefetch) @@ -139,10 +145,11 @@ static int cdns_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, enum pci_barno bar, } static void cdns_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; + enum pci_barno bar = epf_bar->barno; u32 reg, cfg, b, ctrl; if (bar < BAR_4) { diff --git a/drivers/pci/dwc/Kconfig b/drivers/pci/dwc/Kconfig index 0f666b1..2f3f5c5 100644 --- a/drivers/pci/dwc/Kconfig +++ b/drivers/pci/dwc/Kconfig @@ -176,6 +176,7 @@ config PCIE_ARTPEC6_EP config PCIE_KIRIN depends on OF && ARM64 bool "HiSilicon Kirin series SoCs PCIe controllers" + depends on PCI_MSI_IRQ_DOMAIN depends on PCI select PCIEPORTBUS select PCIE_DW_HOST diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c index ca62781..4cc1e5d 100644 --- a/drivers/pci/dwc/pci-exynos.c +++ b/drivers/pci/dwc/pci-exynos.c @@ -294,15 +294,6 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) -{ - struct exynos_pcie *ep = arg; - struct dw_pcie *pci = ep->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static void exynos_pcie_msi_init(struct exynos_pcie *ep) { struct dw_pcie *pci = ep->pci; @@ -428,15 +419,6 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep, dev_err(dev, "failed to get msi irq\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - exynos_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "exynos-pcie", ep); - if (ret) { - dev_err(dev, "failed to request msi irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index 4fddbd0..4818ef8 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -542,15 +542,6 @@ static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) return -EINVAL; } -static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg) -{ - struct imx6_pcie *imx6_pcie = arg; - struct dw_pcie *pci = imx6_pcie->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { struct dw_pcie *pci = imx6_pcie->pci; @@ -674,15 +665,6 @@ static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, dev_err(dev, "failed to get MSI irq\n"); return -ENODEV; } - - ret = devm_request_irq(dev, pp->msi_irq, - imx6_pcie_msi_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "mx6-pcie-msi", imx6_pcie); - if (ret) { - dev_err(dev, "failed to request MSI irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pci-keystone-dw.c b/drivers/pci/dwc/pci-keystone-dw.c index 99a0e70..0682213 100644 --- a/drivers/pci/dwc/pci-keystone-dw.c +++ b/drivers/pci/dwc/pci-keystone-dw.c @@ -120,20 +120,15 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) } } -static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) +void ks_dw_pcie_msi_irq_ack(int irq, struct pcie_port *pp) { - u32 offset, reg_offset, bit_pos; + u32 reg_offset, bit_pos; struct keystone_pcie *ks_pcie; - struct msi_desc *msi; - struct pcie_port *pp; struct dw_pcie *pci; - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); pci = to_dw_pcie_from_pp(pp); ks_pcie = to_keystone_pcie(pci); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); + update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4), BIT(bit_pos)); @@ -162,85 +157,9 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) BIT(bit_pos)); } -static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) -{ - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) - pci_msi_mask_irq(d); - } - - ks_dw_pcie_msi_clear_irq(pp, offset); -} - -static void ks_dw_pcie_msi_irq_unmask(struct irq_data *d) -{ - struct msi_desc *msi; - struct pcie_port *pp; - u32 offset; - - msi = irq_data_get_msi_desc(d); - pp = (struct pcie_port *) msi_desc_to_pci_sysdata(msi); - offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); - - /* Mask the end point if PVM implemented */ - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (msi->msi_attrib.maskbit) - pci_msi_unmask_irq(d); - } - - ks_dw_pcie_msi_set_irq(pp, offset); -} - -static struct irq_chip ks_dw_pcie_msi_irq_chip = { - .name = "Keystone-PCIe-MSI-IRQ", - .irq_ack = ks_dw_pcie_msi_irq_ack, - .irq_mask = ks_dw_pcie_msi_irq_mask, - .irq_unmask = ks_dw_pcie_msi_irq_unmask, -}; - -static int ks_dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) +int ks_dw_pcie_msi_host_init(struct pcie_port *pp) { - irq_set_chip_and_handler(irq, &ks_dw_pcie_msi_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, domain->host_data); - - return 0; -} - -static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { - .map = ks_dw_pcie_msi_map, -}; - -int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); - struct device *dev = pci->dev; - int i; - - pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np, - MAX_MSI_IRQS, - &ks_dw_pcie_msi_domain_ops, - chip); - if (!pp->irq_domain) { - dev_err(dev, "irq domain init failed\n"); - return -ENXIO; - } - - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); - - return 0; + return dw_pcie_allocate_domains(pp); } void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c index d4f8ab9..d55ae07 100644 --- a/drivers/pci/dwc/pci-keystone.c +++ b/drivers/pci/dwc/pci-keystone.c @@ -297,6 +297,7 @@ static const struct dw_pcie_host_ops keystone_pcie_host_ops = { .msi_clear_irq = ks_dw_pcie_msi_clear_irq, .get_msi_addr = ks_dw_pcie_get_msi_addr, .msi_host_init = ks_dw_pcie_msi_host_init, + .msi_irq_ack = ks_dw_pcie_msi_irq_ack, .scan_bus = ks_dw_pcie_v3_65_scan_bus, }; diff --git a/drivers/pci/dwc/pci-keystone.h b/drivers/pci/dwc/pci-keystone.h index 1dd1f3e..8a13da3 100644 --- a/drivers/pci/dwc/pci-keystone.h +++ b/drivers/pci/dwc/pci-keystone.h @@ -49,9 +49,9 @@ int ks_dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie); void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie); +void ks_dw_pcie_msi_irq_ack(int i, struct pcie_port *pp); void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq); void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp); -int ks_dw_pcie_msi_host_init(struct pcie_port *pp, - struct msi_controller *chip); +int ks_dw_pcie_msi_host_init(struct pcie_port *pp); int ks_dw_pcie_link_up(struct dw_pcie *pci); diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c index a7b4159..3724d3e 100644 --- a/drivers/pci/dwc/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c @@ -182,8 +182,7 @@ static int ls1021_pcie_host_init(struct pcie_port *pp) return ls_pcie_host_init(pp); } -static int ls_pcie_msi_host_init(struct pcie_port *pp, - struct msi_controller *chip) +static int ls_pcie_msi_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 93b3df9..e66cede 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -383,15 +383,6 @@ static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { .host_init = artpec6_pcie_host_init, }; -static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg) -{ - struct artpec6_pcie *artpec6_pcie = arg; - struct dw_pcie *pci = artpec6_pcie->pci; - struct pcie_port *pp = &pci->pp; - - return dw_handle_msi_irq(pp); -} - static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, struct platform_device *pdev) { @@ -406,15 +397,6 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, dev_err(dev, "failed to get MSI irq\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - artpec6_pcie_msi_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "artpec6-pcie-msi", artpec6_pcie); - if (ret) { - dev_err(dev, "failed to request MSI irq\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c index 3a6feef..f07678b 100644 --- a/drivers/pci/dwc/pcie-designware-ep.c +++ b/drivers/pci/dwc/pcie-designware-ep.c @@ -19,7 +19,8 @@ void dw_pcie_ep_linkup(struct dw_pcie_ep *ep) pci_epc_linkup(epc); } -void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar, + int flags) { u32 reg; @@ -27,9 +28,18 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) dw_pcie_dbi_ro_wr_en(pci); dw_pcie_writel_dbi2(pci, reg, 0x0); dw_pcie_writel_dbi(pci, reg, 0x0); + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, 0x0); + dw_pcie_writel_dbi(pci, reg + 4, 0x0); + } dw_pcie_dbi_ro_wr_dis(pci); } +void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) +{ + __dw_pcie_ep_reset_bar(pci, bar, 0); +} + static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr) { @@ -104,25 +114,28 @@ static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr, } static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar) + struct pci_epf_bar *epf_bar) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; u32 atu_index = ep->bar_to_atu[bar]; - dw_pcie_ep_reset_bar(pci, bar); + __dw_pcie_ep_reset_bar(pci, bar, epf_bar->flags); dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND); clear_bit(atu_index, ep->ib_window_map); } static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) + struct pci_epf_bar *epf_bar) { int ret; struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + size_t size = epf_bar->size; + int flags = epf_bar->flags; enum dw_pcie_as_type as_type; u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); @@ -131,13 +144,20 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, else as_type = DW_PCIE_AS_IO; - ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type); + ret = dw_pcie_ep_inbound_atu(ep, bar, epf_bar->phys_addr, as_type); if (ret) return ret; dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi2(pci, reg, size - 1); + + dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1)); dw_pcie_writel_dbi(pci, reg, flags); + + if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { + dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1)); + dw_pcie_writel_dbi(pci, reg + 4, 0); + } + dw_pcie_dbi_ro_wr_dis(pci); return 0; @@ -322,7 +342,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct device_node *np = dev->of_node; if (!pci->dbi_base || !pci->dbi_base2) { - dev_err(dev, "dbi_base/deb_base2 is not populated\n"); + dev_err(dev, "dbi_base/dbi_base2 is not populated\n"); return -EINVAL; } diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index dc9303a..6c409079 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -8,6 +8,7 @@ * Author: Jingoo Han <jg1.han@samsung.com> */ +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/of_address.h> #include <linux/of_pci.h> @@ -42,22 +43,46 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return dw_pcie_write(pci->dbi_base + where, size, val); } -static struct irq_chip dw_msi_irq_chip = { +static void dw_msi_ack_irq(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void dw_msi_mask_irq(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void dw_msi_unmask_irq(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip dw_pcie_msi_irq_chip = { .name = "PCI-MSI", - .irq_enable = pci_msi_unmask_irq, - .irq_disable = pci_msi_mask_irq, - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, + .irq_ack = dw_msi_ack_irq, + .irq_mask = dw_msi_mask_irq, + .irq_unmask = dw_msi_unmask_irq, +}; + +static struct msi_domain_info dw_pcie_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), + .chip = &dw_pcie_msi_irq_chip, }; /* MSI int handler */ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { - u32 val; int i, pos, irq; + u32 val, num_ctrls; irqreturn_t ret = IRQ_NONE; - for (i = 0; i < MAX_MSI_CTRLS; i++) { + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + + for (i = 0; i < num_ctrls; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, &val); if (!val) @@ -78,206 +103,216 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) return ret; } -void dw_pcie_msi_init(struct pcie_port *pp) +/* Chained MSI interrupt service routine */ +static void dw_chained_msi_isr(struct irq_desc *desc) { - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - struct page *page; - u64 msi_target; + struct irq_chip *chip = irq_desc_get_chip(desc); + struct pcie_port *pp; - page = alloc_page(GFP_KERNEL); - pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, pp->msi_data)) { - dev_err(dev, "failed to map MSI data\n"); - __free_page(page); - return; - } - msi_target = (u64)pp->msi_data; + chained_irq_enter(chip, desc); - /* program the msi_data */ - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, - (u32)(msi_target & 0xffffffff)); - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, - (u32)(msi_target >> 32 & 0xffffffff)); + pp = irq_desc_get_handler_data(desc); + dw_handle_msi_irq(pp); + + chained_irq_exit(chip, desc); } -static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) +static void dw_pci_setup_msi_msg(struct irq_data *data, struct msi_msg *msg) { - unsigned int res, bit, val; + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u64 msi_target; - res = (irq / 32) * 12; - bit = irq % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val &= ~(1 << bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); -} + if (pp->ops->get_msi_addr) + msi_target = pp->ops->get_msi_addr(pp); + else + msi_target = (u64)pp->msi_data; -static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base, - unsigned int nvec, unsigned int pos) -{ - unsigned int i; - - for (i = 0; i < nvec; i++) { - irq_set_msi_desc_off(irq_base, i, NULL); - /* Disable corresponding interrupt on MSI controller */ - if (pp->ops->msi_clear_irq) - pp->ops->msi_clear_irq(pp, pos + i); - else - dw_pcie_msi_clear_irq(pp, pos + i); - } + msg->address_lo = lower_32_bits(msi_target); + msg->address_hi = upper_32_bits(msi_target); - bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec)); + if (pp->ops->get_msi_data) + msg->data = pp->ops->get_msi_data(pp, data->hwirq); + else + msg->data = data->hwirq; + + dev_dbg(pci->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); } -static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) +static int dw_pci_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) { - unsigned int res, bit, val; - - res = (irq / 32) * 12; - bit = irq % 32; - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); - val |= 1 << bit; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); + return -EINVAL; } -static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) +static void dw_pci_bottom_mask(struct irq_data *data) { - int irq, pos0, i; - struct pcie_port *pp; - - pp = (struct pcie_port *)msi_desc_to_pci_sysdata(desc); - pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, - order_base_2(no_irqs)); - if (pos0 < 0) - goto no_valid_irq; + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; - irq = irq_find_mapping(pp->irq_domain, pos0); - if (!irq) - goto no_valid_irq; + raw_spin_lock_irqsave(&pp->lock, flags); - /* - * irq_create_mapping (called from dw_pcie_host_init) pre-allocates - * descs so there is no need to allocate descs here. We can therefore - * assume that if irq_find_mapping above returns non-zero, then the - * descs are also successfully allocated. - */ + if (pp->ops->msi_clear_irq) { + pp->ops->msi_clear_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / 32; + res = ctrl * 12; + bit = data->hwirq % 32; - for (i = 0; i < no_irqs; i++) { - if (irq_set_msi_desc_off(irq, i, desc) != 0) { - clear_irq_range(pp, irq, i, pos0); - goto no_valid_irq; - } - /*Enable corresponding interrupt in MSI interrupt controller */ - if (pp->ops->msi_set_irq) - pp->ops->msi_set_irq(pp, pos0 + i); - else - dw_pcie_msi_set_irq(pp, pos0 + i); + pp->irq_status[ctrl] &= ~(1 << bit); + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); } - *pos = pos0; - desc->nvec_used = no_irqs; - desc->msi_attrib.multiple = order_base_2(no_irqs); - - return irq; - -no_valid_irq: - *pos = pos0; - return -ENOSPC; + raw_spin_unlock_irqrestore(&pp->lock, flags); } -static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos) +static void dw_pci_bottom_unmask(struct irq_data *data) { - struct msi_msg msg; - u64 msi_target; + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned int res, bit, ctrl; + unsigned long flags; - if (pp->ops->get_msi_addr) - msi_target = pp->ops->get_msi_addr(pp); - else - msi_target = (u64)pp->msi_data; + raw_spin_lock_irqsave(&pp->lock, flags); - msg.address_lo = (u32)(msi_target & 0xffffffff); - msg.address_hi = (u32)(msi_target >> 32 & 0xffffffff); + if (pp->ops->msi_set_irq) { + pp->ops->msi_set_irq(pp, data->hwirq); + } else { + ctrl = data->hwirq / 32; + res = ctrl * 12; + bit = data->hwirq % 32; - if (pp->ops->get_msi_data) - msg.data = pp->ops->get_msi_data(pp, pos); - else - msg.data = pos; + pp->irq_status[ctrl] |= 1 << bit; + dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, + pp->irq_status[ctrl]); + } - pci_write_msi_msg(irq, &msg); + raw_spin_unlock_irqrestore(&pp->lock, flags); } -static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, - struct msi_desc *desc) +static void dw_pci_bottom_ack(struct irq_data *d) { - int irq, pos; - struct pcie_port *pp = pdev->bus->sysdata; - - if (desc->msi_attrib.is_msix) - return -EINVAL; - - irq = assign_irq(1, desc, &pos); - if (irq < 0) - return irq; + struct msi_desc *msi = irq_data_get_msi_desc(d); + struct pcie_port *pp; - dw_msi_setup_msg(pp, irq, pos); + pp = msi_desc_to_pci_sysdata(msi); - return 0; + if (pp->ops->msi_irq_ack) + pp->ops->msi_irq_ack(d->hwirq, pp); } -static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev, - int nvec, int type) +static struct irq_chip dw_pci_msi_bottom_irq_chip = { + .name = "DWPCI-MSI", + .irq_ack = dw_pci_bottom_ack, + .irq_compose_msi_msg = dw_pci_setup_msi_msg, + .irq_set_affinity = dw_pci_msi_set_affinity, + .irq_mask = dw_pci_bottom_mask, + .irq_unmask = dw_pci_bottom_unmask, +}; + +static int dw_pcie_irq_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) { -#ifdef CONFIG_PCI_MSI - int irq, pos; - struct msi_desc *desc; - struct pcie_port *pp = pdev->bus->sysdata; + struct pcie_port *pp = domain->host_data; + unsigned long flags; + u32 i; + int bit; + + raw_spin_lock_irqsave(&pp->lock, flags); - /* MSI-X interrupts are not supported */ - if (type == PCI_CAP_ID_MSIX) - return -EINVAL; + bit = bitmap_find_free_region(pp->msi_irq_in_use, pp->num_vectors, + order_base_2(nr_irqs)); - WARN_ON(!list_is_singular(&pdev->dev.msi_list)); - desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list); + raw_spin_unlock_irqrestore(&pp->lock, flags); - irq = assign_irq(nvec, desc, &pos); - if (irq < 0) - return irq; + if (bit < 0) + return -ENOSPC; - dw_msi_setup_msg(pp, irq, pos); + for (i = 0; i < nr_irqs; i++) + irq_domain_set_info(domain, virq + i, bit + i, + &dw_pci_msi_bottom_irq_chip, + pp, handle_edge_irq, + NULL, NULL); return 0; -#else - return -EINVAL; -#endif } -static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) +static void dw_pcie_irq_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) { - struct irq_data *data = irq_get_irq_data(irq); - struct msi_desc *msi = irq_data_get_msi_desc(data); - struct pcie_port *pp = (struct pcie_port *)msi_desc_to_pci_sysdata(msi); - - clear_irq_range(pp, irq, 1, data->hwirq); + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct pcie_port *pp = irq_data_get_irq_chip_data(data); + unsigned long flags; + + raw_spin_lock_irqsave(&pp->lock, flags); + bitmap_release_region(pp->msi_irq_in_use, data->hwirq, + order_base_2(nr_irqs)); + raw_spin_unlock_irqrestore(&pp->lock, flags); } -static struct msi_controller dw_pcie_msi_chip = { - .setup_irq = dw_msi_setup_irq, - .setup_irqs = dw_msi_setup_irqs, - .teardown_irq = dw_msi_teardown_irq, +static const struct irq_domain_ops dw_pcie_msi_domain_ops = { + .alloc = dw_pcie_irq_domain_alloc, + .free = dw_pcie_irq_domain_free, }; -static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) +int dw_pcie_allocate_domains(struct pcie_port *pp) { - irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); - irq_set_chip_data(irq, domain->host_data); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); + + pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, + &dw_pcie_msi_domain_ops, pp); + if (!pp->irq_domain) { + dev_err(pci->dev, "failed to create IRQ domain\n"); + return -ENOMEM; + } + + pp->msi_domain = pci_msi_create_irq_domain(fwnode, + &dw_pcie_msi_domain_info, + pp->irq_domain); + if (!pp->msi_domain) { + dev_err(pci->dev, "failed to create MSI domain\n"); + irq_domain_remove(pp->irq_domain); + return -ENOMEM; + } return 0; } -static const struct irq_domain_ops msi_domain_ops = { - .map = dw_pcie_msi_map, -}; +void dw_pcie_free_msi(struct pcie_port *pp) +{ + irq_set_chained_handler(pp->msi_irq, NULL); + irq_set_handler_data(pp->msi_irq, NULL); + + irq_domain_remove(pp->msi_domain); + irq_domain_remove(pp->irq_domain); +} + +void dw_pcie_msi_init(struct pcie_port *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct page *page; + u64 msi_target; + + page = alloc_page(GFP_KERNEL); + pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, pp->msi_data)) { + dev_err(dev, "failed to map MSI data\n"); + __free_page(page); + return; + } + msi_target = (u64)pp->msi_data; + + /* program the msi_data */ + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, + lower_32_bits(msi_target)); + dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, + upper_32_bits(msi_target)); +} int dw_pcie_host_init(struct pcie_port *pp) { @@ -285,11 +320,13 @@ int dw_pcie_host_init(struct pcie_port *pp) struct device *dev = pci->dev; struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); + struct resource_entry *win, *tmp; struct pci_bus *bus, *child; struct pci_host_bridge *bridge; struct resource *cfg_res; - int i, ret; - struct resource_entry *win, *tmp; + int ret; + + raw_spin_lock_init(&pci->pp.lock); cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { @@ -388,20 +425,35 @@ int dw_pcie_host_init(struct pcie_port *pp) pci->num_viewport = 2; if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (!pp->ops->msi_host_init) { - pp->irq_domain = irq_domain_add_linear(dev->of_node, - MAX_MSI_IRQS, &msi_domain_ops, - &dw_pcie_msi_chip); - if (!pp->irq_domain) { - dev_err(dev, "irq domain init failed\n"); - ret = -ENXIO; + /* + * If a specific SoC driver needs to change the + * default number of vectors, it needs to implement + * the set_num_vectors callback. + */ + if (!pp->ops->set_num_vectors) { + pp->num_vectors = MSI_DEF_NUM_VECTORS; + } else { + pp->ops->set_num_vectors(pp); + + if (pp->num_vectors > MAX_MSI_IRQS || + pp->num_vectors == 0) { + dev_err(dev, + "Invalid number of vectors\n"); goto error; } + } - for (i = 0; i < MAX_MSI_IRQS; i++) - irq_create_mapping(pp->irq_domain, i); + if (!pp->ops->msi_host_init) { + ret = dw_pcie_allocate_domains(pp); + if (ret) + goto error; + + if (pp->msi_irq) + irq_set_chained_handler_and_data(pp->msi_irq, + dw_chained_msi_isr, + pp); } else { - ret = pp->ops->msi_host_init(pp, &dw_pcie_msi_chip); + ret = pp->ops->msi_host_init(pp); if (ret < 0) goto error; } @@ -421,10 +473,6 @@ int dw_pcie_host_init(struct pcie_port *pp) bridge->ops = &dw_pcie_ops; bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; - if (IS_ENABLED(CONFIG_PCI_MSI)) { - bridge->msi = &dw_pcie_msi_chip; - dw_pcie_msi_chip.dev = dev; - } ret = pci_scan_root_bus_bridge(bridge); if (ret) @@ -593,11 +641,17 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) void dw_pcie_setup_rc(struct pcie_port *pp) { - u32 val; + u32 val, ctrl, num_ctrls; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); dw_pcie_setup(pci); + num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; + + /* Initialize IRQ Status array */ + for (ctrl = 0; ctrl < num_ctrls; ctrl++) + dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + (ctrl * 12), 4, + &pp->irq_status[ctrl]); /* setup RC BARs */ dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004); dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000); diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c index ebdf28b..5416aa8 100644 --- a/drivers/pci/dwc/pcie-designware-plat.c +++ b/drivers/pci/dwc/pcie-designware-plat.c @@ -25,13 +25,6 @@ struct dw_plat_pcie { struct dw_pcie *pci; }; -static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static int dw_plat_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -63,15 +56,6 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp, pp->msi_irq = platform_get_irq(pdev, 0); if (pp->msi_irq < 0) return pp->msi_irq; - - ret = devm_request_irq(dev, pp->msi_irq, - dw_plat_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "dw-plat-pcie-msi", pp); - if (ret) { - dev_err(dev, "failed to request MSI IRQ\n"); - return ret; - } } pp->root_bus_nr = -1; diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index 11b1386..fe811db 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -107,13 +107,10 @@ #define MSI_MESSAGE_DATA_32 0x58 #define MSI_MESSAGE_DATA_64 0x5C -/* - * Maximum number of MSI IRQs can be 256 per controller. But keep - * it 32 as of now. Probably we will never need more than 32. If needed, - * then increment it in multiple of 32. - */ -#define MAX_MSI_IRQS 32 -#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) +#define MAX_MSI_IRQS 256 +#define MAX_MSI_IRQS_PER_CTRL 32 +#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL) +#define MSI_DEF_NUM_VECTORS 32 /* Maximum number of inbound/outbound iATUs */ #define MAX_IATU_IN 256 @@ -149,7 +146,9 @@ struct dw_pcie_host_ops { phys_addr_t (*get_msi_addr)(struct pcie_port *pp); u32 (*get_msi_data)(struct pcie_port *pp, int pos); void (*scan_bus)(struct pcie_port *pp); - int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); + void (*set_num_vectors)(struct pcie_port *pp); + int (*msi_host_init)(struct pcie_port *pp); + void (*msi_irq_ack)(int irq, struct pcie_port *pp); }; struct pcie_port { @@ -174,7 +173,11 @@ struct pcie_port { const struct dw_pcie_host_ops *ops; int msi_irq; struct irq_domain *irq_domain; + struct irq_domain *msi_domain; dma_addr_t msi_data; + u32 num_vectors; + u32 irq_status[MAX_MSI_CTRLS]; + raw_spinlock_t lock; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; @@ -316,8 +319,10 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci) #ifdef CONFIG_PCIE_DW_HOST irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); +void dw_pcie_free_msi(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); +int dw_pcie_allocate_domains(struct pcie_port *pp); #else static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) { @@ -328,6 +333,10 @@ static inline void dw_pcie_msi_init(struct pcie_port *pp) { } +static inline void dw_pcie_free_msi(struct pcie_port *pp) +{ +} + static inline void dw_pcie_setup_rc(struct pcie_port *pp) { } @@ -336,6 +345,11 @@ static inline int dw_pcie_host_init(struct pcie_port *pp) { return 0; } + +static inline int dw_pcie_allocate_domains(struct pcie_port *pp) +{ + return 0; +} #endif #ifdef CONFIG_PCIE_DW_EP diff --git a/drivers/pci/dwc/pcie-histb.c b/drivers/pci/dwc/pcie-histb.c index 70b5c0b..3611d6c 100644 --- a/drivers/pci/dwc/pcie-histb.c +++ b/drivers/pci/dwc/pcie-histb.c @@ -61,6 +61,7 @@ struct histb_pcie { struct reset_control *bus_reset; void __iomem *ctrl; int reset_gpio; + struct regulator *vpcie; }; static u32 histb_pcie_readl(struct histb_pcie *histb_pcie, u32 reg) @@ -207,13 +208,6 @@ static struct dw_pcie_host_ops histb_pcie_host_ops = { .host_init = histb_pcie_host_init, }; -static irqreturn_t histb_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static void histb_pcie_host_disable(struct histb_pcie *hipcie) { reset_control_assert(hipcie->soft_reset); @@ -227,6 +221,9 @@ static void histb_pcie_host_disable(struct histb_pcie *hipcie) if (gpio_is_valid(hipcie->reset_gpio)) gpio_set_value_cansleep(hipcie->reset_gpio, 0); + + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); } static int histb_pcie_host_enable(struct pcie_port *pp) @@ -237,6 +234,14 @@ static int histb_pcie_host_enable(struct pcie_port *pp) int ret; /* power on PCIe device if have */ + if (hipcie->vpcie) { + ret = regulator_enable(hipcie->vpcie); + if (ret) { + dev_err(dev, "failed to enable regulator: %d\n", ret); + return ret; + } + } + if (gpio_is_valid(hipcie->reset_gpio)) gpio_set_value_cansleep(hipcie->reset_gpio, 1); @@ -276,13 +281,14 @@ static int histb_pcie_host_enable(struct pcie_port *pp) return 0; err_aux_clk: - clk_disable_unprepare(hipcie->aux_clk); -err_pipe_clk: clk_disable_unprepare(hipcie->pipe_clk); -err_sys_clk: +err_pipe_clk: clk_disable_unprepare(hipcie->sys_clk); -err_bus_clk: +err_sys_clk: clk_disable_unprepare(hipcie->bus_clk); +err_bus_clk: + if (hipcie->vpcie) + regulator_disable(hipcie->vpcie); return ret; } @@ -332,6 +338,13 @@ static int histb_pcie_probe(struct platform_device *pdev) return PTR_ERR(pci->dbi_base); } + hipcie->vpcie = devm_regulator_get_optional(dev, "vpcie"); + if (IS_ERR(hipcie->vpcie)) { + if (PTR_ERR(hipcie->vpcie) == -EPROBE_DEFER) + return -EPROBE_DEFER; + hipcie->vpcie = NULL; + } + hipcie->reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &of_flags); if (of_flags & OF_GPIO_ACTIVE_LOW) @@ -393,14 +406,6 @@ static int histb_pcie_probe(struct platform_device *pdev) dev_err(dev, "Failed to get MSI IRQ\n"); return pp->msi_irq; } - - ret = devm_request_irq(dev, pp->msi_irq, - histb_pcie_msi_irq_handler, - IRQF_SHARED, "histb-pcie-msi", pp); - if (ret) { - dev_err(dev, "cannot request MSI IRQ\n"); - return ret; - } } hipcie->phy = devm_phy_get(dev, "phy"); diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c index 13d839b..a6b88c7 100644 --- a/drivers/pci/dwc/pcie-kirin.c +++ b/drivers/pci/dwc/pcie-kirin.c @@ -8,7 +8,6 @@ * Author: Xiaowei Song <songxiaowei@huawei.com> */ -#include <asm/compiler.h> #include <linux/compiler.h> #include <linux/clk.h> #include <linux/delay.h> @@ -505,7 +504,7 @@ static const struct of_device_id kirin_pcie_match[] = { {}, }; -struct platform_driver kirin_pcie_driver = { +static struct platform_driver kirin_pcie_driver = { .probe = kirin_pcie_probe, .driver = { .name = "kirin-pcie", diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 6310c66..5897af7 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -79,6 +79,7 @@ #define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358 #define SLV_ADDR_SPACE_SZ 0x10000000 +#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3 struct qcom_pcie_resources_2_1_0 { struct clk *iface_clk; struct clk *core_clk; @@ -88,9 +89,7 @@ struct qcom_pcie_resources_2_1_0 { struct reset_control *ahb_reset; struct reset_control *por_reset; struct reset_control *phy_reset; - struct regulator *vdda; - struct regulator *vdda_phy; - struct regulator *vdda_refclk; + struct regulator_bulk_data supplies[QCOM_PCIE_2_1_0_MAX_SUPPLY]; }; struct qcom_pcie_resources_1_0_0 { @@ -102,12 +101,14 @@ struct qcom_pcie_resources_1_0_0 { struct regulator *vdda; }; +#define QCOM_PCIE_2_3_2_MAX_SUPPLY 2 struct qcom_pcie_resources_2_3_2 { struct clk *aux_clk; struct clk *master_clk; struct clk *slave_clk; struct clk *cfg_clk; struct clk *pipe_clk; + struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; }; struct qcom_pcie_resources_2_4_0 { @@ -180,13 +181,6 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } -static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -{ - struct pcie_port *pp = arg; - - return dw_handle_msi_irq(pp); -} - static int qcom_pcie_establish_link(struct qcom_pcie *pcie) { struct dw_pcie *pci = pcie->pci; @@ -216,18 +210,15 @@ static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + int ret; - res->vdda = devm_regulator_get(dev, "vdda"); - if (IS_ERR(res->vdda)) - return PTR_ERR(res->vdda); - - res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); - if (IS_ERR(res->vdda_phy)) - return PTR_ERR(res->vdda_phy); - - res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); - if (IS_ERR(res->vdda_refclk)) - return PTR_ERR(res->vdda_refclk); + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vdda_phy"; + res->supplies[2].supply = "vdda_refclk"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; res->iface_clk = devm_clk_get(dev, "iface"); if (IS_ERR(res->iface_clk)) @@ -273,9 +264,7 @@ static void qcom_pcie_deinit_2_1_0(struct qcom_pcie *pcie) clk_disable_unprepare(res->iface_clk); clk_disable_unprepare(res->core_clk); clk_disable_unprepare(res->phy_clk); - regulator_disable(res->vdda); - regulator_disable(res->vdda_phy); - regulator_disable(res->vdda_refclk); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) @@ -286,24 +275,12 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie) u32 val; int ret; - ret = regulator_enable(res->vdda); - if (ret) { - dev_err(dev, "cannot enable vdda regulator\n"); + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); return ret; } - ret = regulator_enable(res->vdda_refclk); - if (ret) { - dev_err(dev, "cannot enable vdda_refclk regulator\n"); - goto err_refclk; - } - - ret = regulator_enable(res->vdda_phy); - if (ret) { - dev_err(dev, "cannot enable vdda_phy regulator\n"); - goto err_vdda_phy; - } - ret = reset_control_assert(res->ahb_reset); if (ret) { dev_err(dev, "cannot assert ahb reset\n"); @@ -387,11 +364,7 @@ err_clk_core: err_clk_phy: clk_disable_unprepare(res->iface_clk); err_assert_ahb: - regulator_disable(res->vdda_phy); -err_vdda_phy: - regulator_disable(res->vdda_refclk); -err_refclk: - regulator_disable(res->vdda); + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); return ret; } @@ -521,6 +494,14 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + int ret; + + res->supplies[0].supply = "vdda"; + res->supplies[1].supply = "vddpe-3v3"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies), + res->supplies); + if (ret) + return ret; res->aux_clk = devm_clk_get(dev, "aux"); if (IS_ERR(res->aux_clk)) @@ -550,6 +531,8 @@ static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) clk_disable_unprepare(res->master_clk); clk_disable_unprepare(res->cfg_clk); clk_disable_unprepare(res->aux_clk); + + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); } static void qcom_pcie_post_deinit_2_3_2(struct qcom_pcie *pcie) @@ -567,10 +550,16 @@ static int qcom_pcie_init_2_3_2(struct qcom_pcie *pcie) u32 val; int ret; + ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies); + if (ret < 0) { + dev_err(dev, "cannot enable regulators\n"); + return ret; + } + ret = clk_prepare_enable(res->aux_clk); if (ret) { dev_err(dev, "cannot prepare/enable aux clock\n"); - return ret; + goto err_aux_clk; } ret = clk_prepare_enable(res->cfg_clk); @@ -621,6 +610,9 @@ err_master_clk: err_cfg_clk: clk_disable_unprepare(res->aux_clk); +err_aux_clk: + regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); + return ret; } @@ -1262,15 +1254,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) pp->msi_irq = platform_get_irq_byname(pdev, "msi"); if (pp->msi_irq < 0) return pp->msi_irq; - - ret = devm_request_irq(dev, pp->msi_irq, - qcom_pcie_msi_irq_handler, - IRQF_SHARED | IRQF_NO_THREAD, - "qcom-pcie-msi", pp); - if (ret) { - dev_err(dev, "cannot request msi irq\n"); - return ret; - } } ret = phy_init(pcie->phy); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 64d8a17..7cef851 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -70,7 +70,7 @@ struct pci_epf_test_data { bool linkup_notifier; }; -static int bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; +static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static int pci_epf_test_copy(struct pci_epf_test *epf_test) { @@ -344,21 +344,23 @@ static void pci_epf_test_unbind(struct pci_epf *epf) { struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epc *epc = epf->epc; + struct pci_epf_bar *epf_bar; int bar; cancel_delayed_work(&epf_test->cmd_handler); pci_epc_stop(epc); for (bar = BAR_0; bar <= BAR_5; bar++) { + epf_bar = &epf->bar[bar]; + if (epf_test->reg[bar]) { pci_epf_free_space(epf, epf_test->reg[bar], bar); - pci_epc_clear_bar(epc, epf->func_no, bar); + pci_epc_clear_bar(epc, epf->func_no, epf_bar); } } } static int pci_epf_test_set_bar(struct pci_epf *epf) { - int flags; int bar; int ret; struct pci_epf_bar *epf_bar; @@ -367,21 +369,27 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) struct pci_epf_test *epf_test = epf_get_drvdata(epf); enum pci_barno test_reg_bar = epf_test->test_reg_bar; - flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32; - if (sizeof(dma_addr_t) == 0x8) - flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - for (bar = BAR_0; bar <= BAR_5; bar++) { epf_bar = &epf->bar[bar]; - ret = pci_epc_set_bar(epc, epf->func_no, bar, - epf_bar->phys_addr, - epf_bar->size, flags); + + epf_bar->flags |= upper_32_bits(epf_bar->size) ? + PCI_BASE_ADDRESS_MEM_TYPE_64 : + PCI_BASE_ADDRESS_MEM_TYPE_32; + + ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); if (ret) { pci_epf_free_space(epf, epf_test->reg[bar], bar); dev_err(dev, "failed to set BAR%d\n", bar); if (bar == test_reg_bar) return ret; } + /* + * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 + * if the specific implementation required a 64-bit BAR, + * even if we only requested a 32-bit BAR. + */ + if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) + bar++; } return 0; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e245bba..b0ee427 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -276,22 +276,25 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); * pci_epc_clear_bar() - reset the BAR * @epc: the EPC device for which the BAR has to be cleared * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be reset + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to reset the BAR of the endpoint device. */ -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar) +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { unsigned long flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) return; if (!epc->ops->clear_bar) return; spin_lock_irqsave(&epc->lock, flags); - epc->ops->clear_bar(epc, func_no, bar); + epc->ops->clear_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); @@ -300,26 +303,31 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar); * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space * @epc: the EPC device on which BAR has to be configured * @func_no: the endpoint function number in the EPC device - * @bar: the BAR number that has to be configured - * @size: the size of the addr space - * @flags: specify memory allocation/io allocation/32bit address/64 bit address + * @epf_bar: the struct epf_bar that contains the BAR information * * Invoke to configure the BAR of the endpoint device. */ -int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags) +int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar) { int ret; unsigned long irq_flags; - - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) + int flags = epf_bar->flags; + + if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || + (epf_bar->barno == BAR_5 && + flags & PCI_BASE_ADDRESS_MEM_TYPE_64) || + (flags & PCI_BASE_ADDRESS_SPACE_IO && + flags & PCI_BASE_ADDRESS_IO_MASK) || + (upper_32_bits(epf_bar->size) && + !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))) return -EINVAL; if (!epc->ops->set_bar) return 0; spin_lock_irqsave(&epc->lock, irq_flags); - ret = epc->ops->set_bar(epc, func_no, bar, bar_phys, size, flags); + ret = epc->ops->set_bar(epc, func_no, epf_bar); spin_unlock_irqrestore(&epc->lock, irq_flags); return ret; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 766ce1d..465b5f0 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -98,6 +98,8 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar) epf->bar[bar].phys_addr = 0; epf->bar[bar].size = 0; + epf->bar[bar].barno = 0; + epf->bar[bar].flags = 0; } EXPORT_SYMBOL_GPL(pci_epf_free_space); @@ -126,6 +128,8 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) epf->bar[bar].phys_addr = phys_addr; epf->bar[bar].size = size; + epf->bar[bar].barno = bar; + epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY; return space; } @@ -200,29 +204,17 @@ struct pci_epf *pci_epf_create(const char *name) int ret; struct pci_epf *epf; struct device *dev; - char *func_name; - char *buf; + int len; epf = kzalloc(sizeof(*epf), GFP_KERNEL); - if (!epf) { - ret = -ENOMEM; - goto err_ret; - } - - buf = kstrdup(name, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto free_epf; - } - - func_name = buf; - buf = strchrnul(buf, '.'); - *buf = '\0'; + if (!epf) + return ERR_PTR(-ENOMEM); - epf->name = kstrdup(func_name, GFP_KERNEL); + len = strchrnul(name, '.') - name; + epf->name = kstrndup(name, len, GFP_KERNEL); if (!epf->name) { - ret = -ENOMEM; - goto free_func_name; + kfree(epf); + return ERR_PTR(-ENOMEM); } dev = &epf->dev; @@ -231,28 +223,18 @@ struct pci_epf *pci_epf_create(const char *name) dev->type = &pci_epf_type; ret = dev_set_name(dev, "%s", name); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } ret = device_add(dev); - if (ret) - goto put_dev; + if (ret) { + put_device(dev); + return ERR_PTR(ret); + } - kfree(func_name); return epf; - -put_dev: - put_device(dev); - kfree(epf->name); - -free_func_name: - kfree(func_name); - -free_epf: - kfree(epf); - -err_ret: - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(pci_epf_create); diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index ac8d812..e01d53f 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * host bridge related code + * Host bridge related code */ #include <linux/kernel.h> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index a4ed748..0d0177ce 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -38,6 +38,7 @@ config PCI_FTPCI100 config PCI_TEGRA bool "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA + depends on PCI_MSI_IRQ_DOMAIN help Say Y here if you want support for the PCIe host controller found on NVIDIA Tegra SoCs. @@ -215,7 +216,6 @@ config PCIE_TANGO_SMP8759 config VMD depends on PCI_MSI && X86_64 && SRCU tristate "Intel Volume Management Device Driver" - default N ---help--- Adds support for the Intel Volume Management Device (VMD). VMD is a secondary PCI host bridge that allows PCI Express root ports, diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index b9617d1..5008fd8 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -586,11 +586,11 @@ static int faraday_pci_probe(struct platform_device *pdev) * We encode bridge variants here, we have at least two so it doesn't * hurt to have infrastructure to encompass future variants as well. */ -const struct faraday_pci_variant faraday_regular = { +static const struct faraday_pci_variant faraday_regular = { .cascaded_irq = true, }; -const struct faraday_pci_variant faraday_dual = { +static const struct faraday_pci_variant faraday_dual = { .cascaded_irq = false, }; diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 2faf38e..50cdefe 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -447,7 +447,6 @@ struct hv_pcibus_device { spinlock_t device_list_lock; /* Protect lists below */ void __iomem *cfg_addr; - struct semaphore enum_sem; struct list_head resources_for_children; struct list_head children; @@ -461,6 +460,8 @@ struct hv_pcibus_device { struct retarget_msi_interrupt retarget_msi_interrupt_params; spinlock_t retarget_msi_interrupt_lock; + + struct workqueue_struct *wq; }; /* @@ -520,6 +521,8 @@ struct hv_pci_compl { s32 completion_status; }; +static void hv_pci_onchannelcallback(void *context); + /** * hv_pci_generic_compl() - Invoked for a completion packet * @context: Set up by the sender of the packet. @@ -653,7 +656,7 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, break; } /* - * Make sure the write was done before we release the spinlock + * Make sure the read was done before we release the spinlock * allowing consecutive reads/writes. */ mb(); @@ -664,6 +667,31 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, } } +static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev) +{ + u16 ret; + unsigned long flags; + void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + + PCI_VENDOR_ID; + + spin_lock_irqsave(&hpdev->hbus->config_lock, flags); + + /* Choose the function to be read. (See comment above) */ + writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr); + /* Make sure the function was chosen before we start reading. */ + mb(); + /* Read from that function's config space. */ + ret = readw(addr); + /* + * mb() is not required here, because the spin_unlock_irqrestore() + * is a barrier. + */ + + spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags); + + return ret; +} + /** * _hv_pcifront_write_config() - Internal PCI config write * @hpdev: The PCI driver's representation of the device @@ -1106,8 +1134,37 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) * Since this function is called with IRQ locks held, can't * do normal wait for completion; instead poll. */ - while (!try_wait_for_completion(&comp.comp_pkt.host_event)) + while (!try_wait_for_completion(&comp.comp_pkt.host_event)) { + /* 0xFFFF means an invalid PCI VENDOR ID. */ + if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) { + dev_err_once(&hbus->hdev->device, + "the device has gone\n"); + goto free_int_desc; + } + + /* + * When the higher level interrupt code calls us with + * interrupt disabled, we must poll the channel by calling + * the channel callback directly when channel->target_cpu is + * the current CPU. When the higher level interrupt code + * calls us with interrupt enabled, let's add the + * local_bh_disable()/enable() to avoid race. + */ + local_bh_disable(); + + if (hbus->hdev->channel->target_cpu == smp_processor_id()) + hv_pci_onchannelcallback(hbus); + + local_bh_enable(); + + if (hpdev->state == hv_pcichild_ejecting) { + dev_err_once(&hbus->hdev->device, + "the device is being ejected\n"); + goto free_int_desc; + } + udelay(100); + } if (comp.comp_pkt.completion_status < 0) { dev_err(&hbus->hdev->device, @@ -1590,12 +1647,8 @@ static struct hv_pci_dev *get_pcichild_wslot(struct hv_pcibus_device *hbus, * It must also treat the omission of a previously observed device as * notification that the device no longer exists. * - * Note that this function is a work item, and it may not be - * invoked in the order that it was queued. Back to back - * updates of the list of present devices may involve queuing - * multiple work items, and this one may run before ones that - * were sent later. As such, this function only does something - * if is the last one in the queue. + * Note that this function is serialized with hv_eject_device_work(), + * because both are pushed to the ordered workqueue hbus->wq. */ static void pci_devices_present_work(struct work_struct *work) { @@ -1616,11 +1669,6 @@ static void pci_devices_present_work(struct work_struct *work) INIT_LIST_HEAD(&removed); - if (down_interruptible(&hbus->enum_sem)) { - put_hvpcibus(hbus); - return; - } - /* Pull this off the queue and process it if it was the last one. */ spin_lock_irqsave(&hbus->device_list_lock, flags); while (!list_empty(&hbus->dr_list)) { @@ -1637,7 +1685,6 @@ static void pci_devices_present_work(struct work_struct *work) spin_unlock_irqrestore(&hbus->device_list_lock, flags); if (!dr) { - up(&hbus->enum_sem); put_hvpcibus(hbus); return; } @@ -1724,7 +1771,6 @@ static void pci_devices_present_work(struct work_struct *work) break; } - up(&hbus->enum_sem); put_hvpcibus(hbus); kfree(dr); } @@ -1743,6 +1789,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, struct hv_dr_state *dr; struct hv_dr_work *dr_wrk; unsigned long flags; + bool pending_dr; dr_wrk = kzalloc(sizeof(*dr_wrk), GFP_NOWAIT); if (!dr_wrk) @@ -1766,11 +1813,21 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, } spin_lock_irqsave(&hbus->device_list_lock, flags); + /* + * If pending_dr is true, we have already queued a work, + * which will see the new dr. Otherwise, we need to + * queue a new work. + */ + pending_dr = !list_empty(&hbus->dr_list); list_add_tail(&dr->list_entry, &hbus->dr_list); spin_unlock_irqrestore(&hbus->device_list_lock, flags); - get_hvpcibus(hbus); - schedule_work(&dr_wrk->wrk); + if (pending_dr) { + kfree(dr_wrk); + } else { + get_hvpcibus(hbus); + queue_work(hbus->wq, &dr_wrk->wrk); + } } /** @@ -1796,10 +1853,7 @@ static void hv_eject_device_work(struct work_struct *work) hpdev = container_of(work, struct hv_pci_dev, wrk); - if (hpdev->state != hv_pcichild_ejecting) { - put_pcichild(hpdev, hv_pcidev_ref_pnp); - return; - } + WARN_ON(hpdev->state != hv_pcichild_ejecting); /* * Ejection can come before or after the PCI bus has been set up, so @@ -1848,7 +1902,7 @@ static void hv_pci_eject_device(struct hv_pci_dev *hpdev) get_pcichild(hpdev, hv_pcidev_ref_pnp); INIT_WORK(&hpdev->wrk, hv_eject_device_work); get_hvpcibus(hpdev->hbus); - schedule_work(&hpdev->wrk); + queue_work(hpdev->hbus->wq, &hpdev->wrk); } /** @@ -2461,13 +2515,18 @@ static int hv_pci_probe(struct hv_device *hdev, spin_lock_init(&hbus->config_lock); spin_lock_init(&hbus->device_list_lock); spin_lock_init(&hbus->retarget_msi_interrupt_lock); - sema_init(&hbus->enum_sem, 1); init_completion(&hbus->remove_event); + hbus->wq = alloc_ordered_workqueue("hv_pci_%x", 0, + hbus->sysdata.domain); + if (!hbus->wq) { + ret = -ENOMEM; + goto free_bus; + } ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0, hv_pci_onchannelcallback, hbus); if (ret) - goto free_bus; + goto destroy_wq; hv_set_drvdata(hdev, hbus); @@ -2536,6 +2595,8 @@ free_config: hv_free_config_window(hbus); close: vmbus_close(hdev->channel); +destroy_wq: + destroy_workqueue(hbus->wq); free_bus: free_page((unsigned long)hbus); return ret; @@ -2615,6 +2676,7 @@ static int hv_pci_remove(struct hv_device *hdev) irq_domain_free_fwnode(hbus->sysdata.fwnode); put_hvpcibus(hbus); wait_for_completion(&hbus->remove_event); + destroy_workqueue(hbus->wq); free_page((unsigned long)hbus); return 0; } diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index a28370b..dd4f1a6 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -53,7 +53,6 @@ #define RCAR_PCI_INT_PME (1 << 19) #define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ RCAR_PCI_INT_SIGRETABORT | \ - RCAR_PCI_INT_SIGRETABORT | \ RCAR_PCI_INT_REMABORT | \ RCAR_PCI_INT_PERR | \ RCAR_PCI_INT_SIGSERR | \ diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index dd9b3bc..389e74b 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -18,10 +18,12 @@ #include <linux/delay.h> #include <linux/export.h> #include <linux/interrupt.h> +#include <linux/iopoll.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/module.h> #include <linux/msi.h> #include <linux/of_address.h> #include <linux/of_pci.h> @@ -139,6 +141,8 @@ #define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) #define AFI_INTR_EN_PRSNT_SENSE (1 << 8) +#define AFI_PCIE_PME 0xf0 + #define AFI_PCIE_CONFIG 0x0f8 #define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) #define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe @@ -219,6 +223,8 @@ #define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ +#define PME_ACK_TIMEOUT 10000 + struct tegra_msi { struct msi_controller chip; DECLARE_BITMAP(used, INT_PCI_MSI_NR); @@ -230,8 +236,16 @@ struct tegra_msi { }; /* used to differentiate between Tegra SoC generations */ +struct tegra_pcie_port_soc { + struct { + u8 turnoff_bit; + u8 ack_bit; + } pme; +}; + struct tegra_pcie_soc { unsigned int num_ports; + const struct tegra_pcie_port_soc *ports; unsigned int msi_base_shift; u32 pads_pll_ctl; u32 tx_ref_sel; @@ -549,14 +563,25 @@ static int tegra_pcie_request_resources(struct tegra_pcie *pcie) pci_add_resource(windows, &pcie->busn); err = devm_request_pci_bus_resources(dev, windows); - if (err < 0) + if (err < 0) { + pci_free_resource_list(windows); return err; + } pci_remap_iospace(&pcie->pio, pcie->io.start); return 0; } +static void tegra_pcie_free_resources(struct tegra_pcie *pcie) +{ + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct list_head *windows = &host->windows; + + pci_unmap_iospace(&pcie->pio); + pci_free_resource_list(windows); +} + static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { struct tegra_pcie *pcie = pdev->bus->sysdata; @@ -966,24 +991,35 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) return 0; } -static void tegra_pcie_power_off(struct tegra_pcie *pcie) +static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) { - struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; int err; - /* TODO: disable and unprepare clocks? */ + reset_control_assert(pcie->pcie_xrst); - if (soc->program_uphy) { + if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_off(pcie); if (err < 0) - dev_err(dev, "failed to power off PHY(s): %d\n", err); + dev_err(pcie->dev, "failed to power off PHY(s): %d\n", + err); } +} + +static void tegra_pcie_power_off(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; - reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); + clk_disable_unprepare(pcie->pll_e); + if (soc->has_cml_clk) + clk_disable_unprepare(pcie->cml_clk); + clk_disable_unprepare(pcie->afi_clk); + clk_disable_unprepare(pcie->pex_clk); + if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); @@ -1192,6 +1228,30 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) return 0; } +static void tegra_pcie_phys_put(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port; + struct device *dev = pcie->dev; + int err, i; + + if (pcie->legacy_phy) { + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(dev, "failed to teardown PHY: %d\n", err); + return; + } + + list_for_each_entry(port, &pcie->ports, list) { + for (i = 0; i < port->lanes; i++) { + err = phy_exit(port->phys[i]); + if (err < 0) + dev_err(dev, "failed to teardown PHY#%u: %d\n", + i, err); + } + } +} + + static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; @@ -1220,31 +1280,25 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) } } - err = tegra_pcie_power_on(pcie); - if (err) { - dev_err(dev, "failed to power up: %d\n", err); - return err; - } - pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); pcie->pads = devm_ioremap_resource(dev, pads); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); - goto poweroff; + goto phys_put; } afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); pcie->afi = devm_ioremap_resource(dev, afi); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); - goto poweroff; + goto phys_put; } /* request configuration space, but remap later, on demand */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs"); if (!res) { err = -EADDRNOTAVAIL; - goto poweroff; + goto phys_put; } pcie->cs = *res; @@ -1255,14 +1309,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); if (IS_ERR(pcie->cfg)) { err = PTR_ERR(pcie->cfg); - goto poweroff; + goto phys_put; } /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); if (err < 0) { dev_err(dev, "failed to get IRQ: %d\n", err); - goto poweroff; + goto phys_put; } pcie->irq = err; @@ -1270,36 +1324,56 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); if (err) { dev_err(dev, "failed to register IRQ: %d\n", err); - goto poweroff; + goto phys_put; } return 0; -poweroff: - tegra_pcie_power_off(pcie); +phys_put: + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); return err; } static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { - struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; - int err; if (pcie->irq > 0) free_irq(pcie->irq, pcie); - tegra_pcie_power_off(pcie); - - if (soc->program_uphy) { - err = phy_exit(pcie->phy); - if (err < 0) - dev_err(dev, "failed to teardown PHY: %d\n", err); - } + if (soc->program_uphy) + tegra_pcie_phys_put(pcie); return 0; } +static void tegra_pcie_pme_turnoff(struct tegra_pcie_port *port) +{ + struct tegra_pcie *pcie = port->pcie; + const struct tegra_pcie_soc *soc = pcie->soc; + int err; + u32 val; + u8 ack_bit; + + val = afi_readl(pcie, AFI_PCIE_PME); + val |= (0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); + + ack_bit = soc->ports[port->index].pme.ack_bit; + err = readl_poll_timeout(pcie->afi + AFI_PCIE_PME, val, + val & (0x1 << ack_bit), 1, PME_ACK_TIMEOUT); + if (err) + dev_err(pcie->dev, "PME Ack is not received on port: %d\n", + port->index); + + usleep_range(10000, 11000); + + val = afi_readl(pcie, AFI_PCIE_PME); + val &= ~(0x1 << soc->ports[port->index].pme.turnoff_bit); + afi_writel(pcie, val, AFI_PCIE_PME); +} + static int tegra_msi_alloc(struct tegra_msi *chip) { int msi; @@ -1436,15 +1510,13 @@ static const struct irq_domain_ops msi_domain_ops = { .map = tegra_msi_map, }; -static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) +static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); struct platform_device *pdev = to_platform_device(pcie->dev); - const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; struct device *dev = pcie->dev; int err; - u32 reg; mutex_init(&msi->lock); @@ -1477,6 +1549,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) /* setup AFI/FPCI range */ msi->pages = __get_free_pages(GFP_KERNEL, 0); msi->phys = virt_to_phys((void *)msi->pages); + host->msi = &msi->chip; + + return 0; + +err: + irq_domain_remove(msi->domain); + return err; +} + +static void tegra_pcie_enable_msi(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_msi *msi = &pcie->msi; + u32 reg; afi_writel(pcie, msi->phys >> soc->msi_base_shift, AFI_MSI_FPCI_BAR_ST); afi_writel(pcie, msi->phys, AFI_MSI_AXI_BAR_ST); @@ -1497,20 +1583,29 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) reg = afi_readl(pcie, AFI_INTR_MASK); reg |= AFI_INTR_MASK_MSI_MASK; afi_writel(pcie, reg, AFI_INTR_MASK); +} - host->msi = &msi->chip; +static void tegra_pcie_msi_teardown(struct tegra_pcie *pcie) +{ + struct tegra_msi *msi = &pcie->msi; + unsigned int i, irq; - return 0; + free_pages(msi->pages, 0); + + if (msi->irq > 0) + free_irq(msi->irq, pcie); + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } -err: irq_domain_remove(msi->domain); - return err; } static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) { - struct tegra_msi *msi = &pcie->msi; - unsigned int i, irq; u32 value; /* mask the MSI interrupt */ @@ -1528,19 +1623,6 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_MSI_EN_VEC6); afi_writel(pcie, 0, AFI_MSI_EN_VEC7); - free_pages(msi->pages, 0); - - if (msi->irq > 0) - free_irq(msi->irq, pcie); - - for (i = 0; i < INT_PCI_MSI_NR; i++) { - irq = irq_find_mapping(msi->domain, i); - if (irq > 0) - irq_dispose_mapping(irq); - } - - irq_domain_remove(msi->domain); - return 0; } @@ -2035,8 +2117,22 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) } } +static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + tegra_pcie_port_disable(port); +} + +static const struct tegra_pcie_port_soc tegra20_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, +}; + static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 0, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, @@ -2050,8 +2146,15 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, }; +static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 16, .pme.ack_bit = 18 }, +}; + static const struct tegra_pcie_soc tegra30_pcie = { .num_ports = 3, + .ports = tegra30_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2068,6 +2171,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { static const struct tegra_pcie_soc tegra124_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2083,6 +2187,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { static const struct tegra_pcie_soc tegra210_pcie = { .num_ports = 2, + .ports = tegra20_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2096,8 +2201,15 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, }; +static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { + { .pme.turnoff_bit = 0, .pme.ack_bit = 5 }, + { .pme.turnoff_bit = 8, .pme.ack_bit = 10 }, + { .pme.turnoff_bit = 12, .pme.ack_bit = 14 }, +}; + static const struct tegra_pcie_soc tegra186_pcie = { .num_ports = 3, + .ports = tegra186_pcie_ports, .msi_base_shift = 8, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, @@ -2209,6 +2321,12 @@ static const struct file_operations tegra_pcie_ports_ops = { .release = seq_release, }; +static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) +{ + debugfs_remove_recursive(pcie->debugfs); + pcie->debugfs = NULL; +} + static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { struct dentry *file; @@ -2225,8 +2343,7 @@ static int tegra_pcie_debugfs_init(struct tegra_pcie *pcie) return 0; remove: - debugfs_remove_recursive(pcie->debugfs); - pcie->debugfs = NULL; + tegra_pcie_debugfs_exit(pcie); return -ENOMEM; } @@ -2244,6 +2361,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(host); host->sysdata = pcie; + platform_set_drvdata(pdev, pcie); pcie->soc = of_device_get_match_data(dev); INIT_LIST_HEAD(&pcie->ports); @@ -2259,26 +2377,22 @@ static int tegra_pcie_probe(struct platform_device *pdev) return err; } - err = tegra_pcie_enable_controller(pcie); - if (err) - goto put_resources; - - err = tegra_pcie_request_resources(pcie); - if (err) + err = tegra_pcie_msi_setup(pcie); + if (err < 0) { + dev_err(dev, "failed to enable MSI support: %d\n", err); goto put_resources; + } - /* setup the AFI address translations */ - tegra_pcie_setup_translations(pcie); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - err = tegra_pcie_enable_msi(pcie); - if (err < 0) { - dev_err(dev, "failed to enable MSI support: %d\n", err); - goto put_resources; - } + pm_runtime_enable(pcie->dev); + err = pm_runtime_get_sync(pcie->dev); + if (err) { + dev_err(dev, "fail to enable pcie controller: %d\n", err); + goto teardown_msi; } - tegra_pcie_enable_ports(pcie); + err = tegra_pcie_request_resources(pcie); + if (err) + goto pm_runtime_put; host->busnr = pcie->busn.start; host->dev.parent = &pdev->dev; @@ -2289,7 +2403,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = pci_scan_root_bus_bridge(host); if (err < 0) { dev_err(dev, "failed to register host: %d\n", err); - goto disable_msi; + goto free_resources; } pci_bus_size_bridges(host->bus); @@ -2308,20 +2422,108 @@ static int tegra_pcie_probe(struct platform_device *pdev) return 0; -disable_msi: - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_disable_msi(pcie); +free_resources: + tegra_pcie_free_resources(pcie); +pm_runtime_put: + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); +teardown_msi: + tegra_pcie_msi_teardown(pcie); put_resources: tegra_pcie_put_resources(pcie); return err; } +static int tegra_pcie_remove(struct platform_device *pdev) +{ + struct tegra_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + struct tegra_pcie_port *port, *tmp; + + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_pcie_debugfs_exit(pcie); + + pci_stop_root_bus(host->bus); + pci_remove_root_bus(host->bus); + tegra_pcie_free_resources(pcie); + pm_runtime_put_sync(pcie->dev); + pm_runtime_disable(pcie->dev); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_msi_teardown(pcie); + + tegra_pcie_put_resources(pcie); + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + tegra_pcie_port_free(port); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + tegra_pcie_pme_turnoff(port); + + tegra_pcie_disable_ports(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_disable_msi(pcie); + + tegra_pcie_disable_controller(pcie); + tegra_pcie_power_off(pcie); + + return 0; +} + +static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) +{ + struct tegra_pcie *pcie = dev_get_drvdata(dev); + int err; + + err = tegra_pcie_power_on(pcie); + if (err) { + dev_err(dev, "tegra pcie power on fail: %d\n", err); + return err; + } + err = tegra_pcie_enable_controller(pcie); + if (err) { + dev_err(dev, "tegra pcie controller enable fail: %d\n", err); + goto poweroff; + } + tegra_pcie_setup_translations(pcie); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + tegra_pcie_enable_msi(pcie); + + tegra_pcie_enable_ports(pcie); + + return 0; + +poweroff: + tegra_pcie_power_off(pcie); + + return err; +} + +static const struct dev_pm_ops tegra_pcie_pm_ops = { + SET_RUNTIME_PM_OPS(tegra_pcie_pm_suspend, tegra_pcie_pm_resume, NULL) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_pcie_pm_suspend, + tegra_pcie_pm_resume) +}; + static struct platform_driver tegra_pcie_driver = { .driver = { .name = "tegra-pcie", .of_match_table = tegra_pcie_of_match, .suppress_bind_attrs = true, + .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, + .remove = tegra_pcie_remove, }; -builtin_platform_driver(tegra_pcie_driver); +module_platform_driver(tegra_pcie_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/host/pci-v3-semi.c b/drivers/pci/host/pci-v3-semi.c index 7fef648..0a4dea7 100644 --- a/drivers/pci/host/pci-v3-semi.c +++ b/drivers/pci/host/pci-v3-semi.c @@ -673,7 +673,7 @@ static int v3_get_dma_range_config(struct v3_pci *v3, dev_err(v3->dev, "illegal dma memory chunk size\n"); return -EINVAL; break; - }; + } val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; *pci_map = val; diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c index df8e4bd..f4c02da 100644 --- a/drivers/pci/host/pci-xgene-msi.c +++ b/drivers/pci/host/pci-xgene-msi.c @@ -456,7 +456,7 @@ static int xgene_msi_probe(struct platform_device *pdev) xgene_msi->msi_regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(xgene_msi->msi_regs)) { dev_err(&pdev->dev, "no reg space\n"); - rc = -EINVAL; + rc = PTR_ERR(xgene_msi->msi_regs); goto error; } xgene_msi->msi_addr = res->start; diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index 2235f47..a6af62e 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -145,7 +145,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie, static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) { int i; - bool sop = 0; + bool sop = false; u32 ctrl; u32 reg0, reg1; u32 comp_status = 1; diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c index 603c834..aa55b06 100644 --- a/drivers/pci/host/pcie-iproc-bcma.c +++ b/drivers/pci/host/pcie-iproc-bcma.c @@ -25,8 +25,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class); static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { - struct pci_sys_data *sys = dev->sysdata; - struct iproc_pcie *pcie = sys->private_data; + struct iproc_pcie *pcie = dev->sysdata; struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev); return bcma_core_irq(bdev, 5); diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index cbb0954..3c76c5f 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -377,14 +377,7 @@ static const u16 iproc_pcie_reg_paxc_v2[] = { static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) { - struct iproc_pcie *pcie; -#ifdef CONFIG_ARM - struct pci_sys_data *sys = bus->sysdata; - - pcie = sys->private_data; -#else - pcie = bus->sysdata; -#endif + struct iproc_pcie *pcie = bus->sysdata; return pcie; } @@ -1331,7 +1324,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { struct device *dev; int ret; - void *sysdata; struct pci_bus *child; struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -1376,13 +1368,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) goto err_power_off_phy; } -#ifdef CONFIG_ARM - pcie->sysdata.private_data = pcie; - sysdata = &pcie->sysdata; -#else - sysdata = pcie; -#endif - ret = iproc_pcie_check_link(pcie); if (ret) { dev_err(dev, "no PCIe EP device detected\n"); @@ -1399,7 +1384,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->busnr = 0; host->dev.parent = dev; host->ops = &iproc_pcie_ops; - host->sysdata = sysdata; + host->sysdata = pcie; host->map_irq = pcie->map_irq; host->swizzle_irq = pci_common_swizzle; diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index d55f56a..814b600 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -54,7 +54,6 @@ struct iproc_msi; * @reg_offsets: register offsets * @base: PCIe host controller I/O register base * @base_addr: PCIe host controller register base physical address - * @sysdata: Per PCI controller data (ARM-specific) * @root_bus: pointer to root bus * @phy: optional PHY device that controls the Serdes * @map_irq: function callback to map interrupts @@ -80,9 +79,6 @@ struct iproc_pcie { u16 *reg_offsets; void __iomem *base; phys_addr_t base_addr; -#ifdef CONFIG_ARM - struct pci_sys_data sysdata; -#endif struct resource mem; struct pci_bus *root_bus; struct phy *phy; diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index b4c4aad..6ab28f2 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -435,7 +435,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) } msleep(1); - }; + } dev_err(dev, "Speed change timed out\n"); diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index 0acaf48..4839ae5 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -630,7 +630,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) * For high range MSI interrupts: disable, clear any pending, * and enable */ - nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_HI); nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) & MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI); @@ -641,7 +641,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie) * For low range MSI interrupts: disable, clear any pending, * and enable */ - nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); + nwl_bridge_writel(pcie, 0, MSGF_MSI_MASK_LO); nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) & MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index e2198a2f..b45b375 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -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; } diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index b1b6e45..616df44 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -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; diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 636ed8f..88e917c 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -20,10 +20,11 @@ #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; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 677924a..8adf4a6 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -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: diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c index 814a3ce..24505b0 100644 --- a/drivers/pci/mmap.c +++ b/drivers/pci/mmap.c @@ -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. * diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 8b0729c..3025063 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -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) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 7815768..1abdbf2 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -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> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 6a67cdb..6ace470 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -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); @@ -1522,6 +1535,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)); @@ -1543,8 +1592,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); diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index a961a71..a5910f9 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -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> diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c index 10d54f9..66f8a59 100644 --- a/drivers/pci/pci-stub.c +++ b/drivers/pci/pci-stub.c @@ -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: diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eb6bee8..366d93a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -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; @@ -982,38 +955,6 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj, return count; } -static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) -{ - struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - - if (bin_attr->size > 0) { - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; - } - - return pci_read_vpd(dev, off, count, buf); -} - -static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t off, size_t count) -{ - struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - - if (bin_attr->size > 0) { - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; - } - - return pci_write_vpd(dev, off, count, buf); -} - #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -1517,46 +1458,20 @@ static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_stor static int pci_create_capabilities_sysfs(struct pci_dev *dev) { int retval; - struct bin_attribute *attr; - /* If the device has VPD, try to expose it in sysfs. */ - if (dev->vpd) { - attr = kzalloc(sizeof(*attr), GFP_ATOMIC); - if (!attr) - return -ENOMEM; - - sysfs_bin_attr_init(attr); - attr->size = 0; - attr->attr.name = "vpd"; - attr->attr.mode = S_IRUSR | S_IWUSR; - attr->read = read_vpd_attr; - attr->write = write_vpd_attr; - retval = sysfs_create_bin_file(&dev->dev.kobj, attr); - if (retval) { - kfree(attr); - return retval; - } - dev->vpd->attr = attr; - } - - /* Active State Power Management */ + 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; error: pcie_aspm_remove_sysfs_dev_files(dev); - if (dev->vpd && dev->vpd->attr) { - sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); - kfree(dev->vpd->attr); - } - + pcie_vpd_remove_sysfs_dev_files(dev); return retval; } @@ -1630,11 +1545,7 @@ err: static void pci_remove_capabilities_sysfs(struct pci_dev *dev) { - if (dev->vpd && dev->vpd->attr) { - sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); - kfree(dev->vpd->attr); - } - + pcie_vpd_remove_sysfs_dev_files(dev); pcie_aspm_remove_sysfs_dev_files(dev); if (dev->reset_fn) { device_remove_file(&dev->dev, &reset_attr); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 99ec0ef..aa86e90 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -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 @@ -1684,6 +1688,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. * @@ -3436,68 +3449,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) @@ -3505,21 +3485,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; @@ -3528,21 +3497,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; @@ -4013,20 +3968,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 @@ -4040,14 +3988,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; @@ -4055,7 +4003,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; } /** @@ -4084,13 +4035,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); @@ -4123,8 +4082,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); } /** @@ -4169,7 +4136,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) @@ -4179,6 +4146,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. @@ -4210,9 +4178,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); @@ -4375,8 +4345,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) @@ -4446,9 +4417,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); @@ -4483,9 +4453,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); @@ -4507,18 +4476,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); @@ -4726,7 +4694,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); } @@ -5143,6 +5113,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 * @flags: resource type mask to be selected @@ -5607,8 +5751,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; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index fcd8191..023f7cf 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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); @@ -104,25 +105,10 @@ static inline bool pci_power_manageable(struct pci_dev *pci_dev) return !pci_has_subordinate(pci_dev) || pci_dev->bridge_d3; } -struct pci_vpd_ops { - ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); - ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); - int (*set_size)(struct pci_dev *dev, size_t len); -}; - -struct pci_vpd { - const struct pci_vpd_ops *ops; - struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */ - struct mutex lock; - unsigned int len; - u16 flag; - u8 cap; - u8 busy:1; - u8 valid:1; -}; - int pci_vpd_init(struct pci_dev *dev); void pci_vpd_release(struct pci_dev *dev); +void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev); +void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev); /* PCI /proc functions */ #ifdef CONFIG_PROC_FS @@ -253,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 */ @@ -271,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 */ }; diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 223e4c3..800e1d4 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile @@ -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 diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 25e1feb..a490909 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -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; diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index da8331f..779b387 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -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" diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 5449e5c..08b4584 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -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) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index b201944..08c87de 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -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> diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index a4bfea5..0ea5acc 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -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> diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 6a352e6..cfc89dd 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -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> diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c index 26d3cac..039efb60 100644 --- a/drivers/pci/pcie/aer/ecrc.c +++ b/drivers/pci/pcie/aer/ecrc.c @@ -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; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 57feef2..f76eb77 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -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, ®16); + 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, + ®16); + 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, ®16); @@ -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/pcie-dpc.c b/drivers/pci/pcie/dpc.c index 38e40c6..8c57d60 100644 --- a/drivers/pci/pcie/pcie-dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -10,7 +10,8 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/pci.h> -#include <linux/pcieport_if.h> + +#include "portdrv.h" #include "../pci.h" #include "aer/aerdrv.h" diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index 5480f54..3ed6767 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -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" diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index a854bc5..d0c6783 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -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 @@ -12,7 +11,66 @@ #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_ */ diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index 319c949..8ab5d43 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -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 index f0fba55..0000000 --- a/drivers/pci/pcie/portdrv_bus.c +++ /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); -} diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index ef3bad4..c9c0663 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -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 @@ -15,23 +14,11 @@ #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, ®32); @@ -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; } diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index fb1c1bb..973f1b8 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -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) @@ -15,10 +13,8 @@ #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" @@ -27,22 +23,18 @@ 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); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3c365dc..ac91b6f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -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> @@ -330,6 +330,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); @@ -541,6 +545,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); @@ -593,7 +607,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 */ @@ -1231,6 +1245,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) @@ -1390,6 +1411,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) @@ -1455,8 +1513,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; @@ -1478,7 +1535,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 */ @@ -1518,8 +1576,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 @@ -2122,6 +2180,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; } /* diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 58a662e..1ee8927 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -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> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 36db209..26141b1 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -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> @@ -1968,31 +1968,6 @@ static void quirk_netmos(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); -/* - * Quirk non-zero PCI functions to route VPD access through function 0 for - * devices that share VPD resources between functions. The functions are - * expected to be identical devices. - */ -static void quirk_f0_vpd_link(struct pci_dev *dev) -{ - struct pci_dev *f0; - - if (!PCI_FUNC(dev->devfn)) - return; - - f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); - if (!f0) - return; - - if (f0->vpd && dev->class == f0->class && - dev->vendor == f0->vendor && dev->device == f0->device) - dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; - - pci_dev_put(f0); -} -DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, - PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); - static void quirk_e100_interrupt(struct pci_dev *dev) { u16 command, pmcsr; @@ -2183,83 +2158,6 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); -/* - * If a device follows the VPD format spec, the PCI core will not read or - * write past the VPD End Tag. But some vendors do not follow the VPD - * format spec, so we can't tell how much data is safe to access. Devices - * may behave unpredictably if we access too much. Blacklist these devices - * so we don't touch VPD at all. - */ -static void quirk_blacklist_vpd(struct pci_dev *dev) -{ - if (dev->vpd) { - dev->vpd->len = 0; - pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); - } -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, - quirk_blacklist_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd); - -/* - * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the - * VPD end tag will hang the device. This problem was initially - * observed when a vpd entry was created in sysfs - * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry - * will dump 32k of data. Reading a full 32k will cause an access - * beyond the VPD end tag causing the device to hang. Once the device - * is hung, the bnx2 driver will not be able to reset the device. - * We believe that it is legal to read beyond the end tag and - * therefore the solution is to limit the read/write length. - */ -static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev) -{ - /* - * Only disable the VPD capability for 5706, 5706S, 5708, - * 5708S and 5709 rev. A - */ - if ((dev->device == PCI_DEVICE_ID_NX2_5706) || - (dev->device == PCI_DEVICE_ID_NX2_5706S) || - (dev->device == PCI_DEVICE_ID_NX2_5708) || - (dev->device == PCI_DEVICE_ID_NX2_5708S) || - ((dev->device == PCI_DEVICE_ID_NX2_5709) && - (dev->revision & 0xf0) == 0x0)) { - if (dev->vpd) - dev->vpd->len = 0x80; - } -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5706S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5708S, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709, - quirk_brcm_570x_limit_vpd); -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, - PCI_DEVICE_ID_NX2_5709S, - quirk_brcm_570x_limit_vpd); - static void quirk_brcm_5719_limit_mrrs(struct pci_dev *dev) { u32 rev; @@ -3086,16 +2984,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, @@ -3104,13 +2996,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); } /* @@ -3399,32 +3289,6 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE, quirk_thunderbolt_hotplug_msi); -static void quirk_chelsio_extend_vpd(struct pci_dev *dev) -{ - int chip = (dev->device & 0xf000) >> 12; - int func = (dev->device & 0x0f00) >> 8; - int prod = (dev->device & 0x00ff) >> 0; - - /* - * If this is a T3-based adapter, there's a 1KB VPD area at offset - * 0xc00 which contains the preferred VPD values. If this is a T4 or - * later based adapter, the special VPD is at offset 0x400 for the - * Physical Functions (the SR-IOV Virtual Functions have no VPD - * Capabilities). The PCI VPD Access core routines will normally - * compute the size of the VPD by parsing the VPD Data Structure at - * offset 0x000. This will result in silent failures when attempting - * to accesses these other VPD areas which are beyond those computed - * limits. - */ - if (chip == 0x0 && prod >= 0x20) - pci_set_vpd_size(dev, 8192); - else if (chip >= 0x4 && func < 0x8) - pci_set_vpd_size(dev, 2048); -} - -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, - quirk_chelsio_extend_vpd); - #ifdef CONFIG_ACPI /* * Apple: Shutdown Cactus Ridge Thunderbolt controller. @@ -3885,6 +3749,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); @@ -4505,6 +4372,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 } }; diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 374a334..a7b5c37 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -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> diff --git a/drivers/pci/search.c b/drivers/pci/search.c index bc1e023..2b5f720 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -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> diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 3cce29a..072784f 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -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> diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index 5ad4ee7..7129494 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -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. */ diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3654472..0cd83a4 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -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 */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index d10f556..e634229 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -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) diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index e725f99..d96626c 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -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> diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index 70fba57..8617565 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -1,13 +1,465 @@ // SPDX-License-Identifier: GPL-2.0 /* - * File: vpd.c - * Purpose: Provide PCI VPD support + * PCI VPD support * * Copyright (C) 2010 Broadcom Corporation. */ #include <linux/pci.h> +#include <linux/delay.h> #include <linux/export.h> +#include <linux/sched/signal.h> +#include "pci.h" + +/* VPD access through PCI 2.2+ VPD capability */ + +struct pci_vpd_ops { + ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); + ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); + int (*set_size)(struct pci_dev *dev, size_t len); +}; + +struct pci_vpd { + const struct pci_vpd_ops *ops; + struct bin_attribute *attr; /* Descriptor for sysfs VPD entry */ + struct mutex lock; + unsigned int len; + u16 flag; + u8 cap; + unsigned int busy:1; + unsigned int valid:1; +}; + +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @buf: pointer to where to store result + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to write + * @buf: buffer containing write data + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + +/** + * pci_set_vpd_size - Set size of Vital Product Data space + * @dev: pci device struct + * @len: size of vpd space + */ +int pci_set_vpd_size(struct pci_dev *dev, size_t len) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->set_size(dev, len); +} +EXPORT_SYMBOL(pci_set_vpd_size); + +#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) + +/** + * pci_vpd_size - determine actual size of Vital Product Data + * @dev: pci device struct + * @old_size: current assumed size, also maximum allowed size + */ +static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) +{ + size_t off = 0; + unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ + + while (off < old_size && + pci_read_vpd(dev, off, 1, header) == 1) { + unsigned char tag; + + if (header[0] & PCI_VPD_LRDT) { + /* Large Resource Data Type Tag */ + tag = pci_vpd_lrdt_tag(header); + /* Only read length from known tag items */ + if ((tag == PCI_VPD_LTIN_ID_STRING) || + (tag == PCI_VPD_LTIN_RO_DATA) || + (tag == PCI_VPD_LTIN_RW_DATA)) { + if (pci_read_vpd(dev, off+1, 2, + &header[1]) != 2) { + pci_warn(dev, "invalid large VPD tag %02x size at offset %zu", + tag, off + 1); + return 0; + } + off += PCI_VPD_LRDT_TAG_SIZE + + pci_vpd_lrdt_size(header); + } + } else { + /* Short Resource Data Type Tag */ + off += PCI_VPD_SRDT_TAG_SIZE + + pci_vpd_srdt_size(header); + tag = pci_vpd_srdt_tag(header); + } + + if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ + return off; + + if ((tag != PCI_VPD_LTIN_ID_STRING) && + (tag != PCI_VPD_LTIN_RO_DATA) && + (tag != PCI_VPD_LTIN_RW_DATA)) { + pci_warn(dev, "invalid %s VPD tag %02x at offset %zu", + (header[0] & PCI_VPD_LRDT) ? "large" : "short", + tag, off); + return 0; + } + } + return 0; +} + +/* + * Wait for last operation to complete. + * This code has to spin since there is no other notification from the PCI + * hardware. Since the VPD is often implemented by serial attachment to an + * EEPROM, it may take many milliseconds to complete. + * + * Returns 0 on success, negative values indicate error. + */ +static int pci_vpd_wait(struct pci_dev *dev) +{ + struct pci_vpd *vpd = dev->vpd; + unsigned long timeout = jiffies + msecs_to_jiffies(125); + unsigned long max_sleep = 16; + u16 status; + int ret; + + if (!vpd->busy) + return 0; + + while (time_before(jiffies, timeout)) { + ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, + &status); + if (ret < 0) + return ret; + + if ((status & PCI_VPD_ADDR_F) == vpd->flag) { + vpd->busy = 0; + return 0; + } + + if (fatal_signal_pending(current)) + return -EINTR; + + usleep_range(10, max_sleep); + if (max_sleep < 1024) + max_sleep *= 2; + } + + pci_warn(dev, "VPD access failed. This is likely a firmware bug on this device. Contact the card vendor for a firmware update\n"); + return -ETIMEDOUT; +} + +static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) +{ + struct pci_vpd *vpd = dev->vpd; + int ret; + loff_t end = pos + count; + u8 *buf = arg; + + if (pos < 0) + return -EINVAL; + + if (!vpd->valid) { + vpd->valid = 1; + vpd->len = pci_vpd_size(dev, vpd->len); + } + + if (vpd->len == 0) + return -EIO; + + if (pos > vpd->len) + return 0; + + if (end > vpd->len) { + end = vpd->len; + count = end - pos; + } + + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + + ret = pci_vpd_wait(dev); + if (ret < 0) + goto out; + + while (pos < end) { + u32 val; + unsigned int i, skip; + + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos & ~3); + if (ret < 0) + break; + vpd->busy = 1; + vpd->flag = PCI_VPD_ADDR_F; + ret = pci_vpd_wait(dev); + if (ret < 0) + break; + + ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); + if (ret < 0) + break; + + skip = pos & 3; + for (i = 0; i < sizeof(u32); i++) { + if (i >= skip) { + *buf++ = val; + if (++pos == end) + break; + } + val >>= 8; + } + } +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; +} + +static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) +{ + struct pci_vpd *vpd = dev->vpd; + const u8 *buf = arg; + loff_t end = pos + count; + int ret = 0; + + if (pos < 0 || (pos & 3) || (count & 3)) + return -EINVAL; + + if (!vpd->valid) { + vpd->valid = 1; + vpd->len = pci_vpd_size(dev, vpd->len); + } + + if (vpd->len == 0) + return -EIO; + + if (end > vpd->len) + return -EINVAL; + + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + + ret = pci_vpd_wait(dev); + if (ret < 0) + goto out; + + while (pos < end) { + u32 val; + + val = *buf++; + val |= *buf++ << 8; + val |= *buf++ << 16; + val |= *buf++ << 24; + + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); + if (ret < 0) + break; + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos | PCI_VPD_ADDR_F); + if (ret < 0) + break; + + vpd->busy = 1; + vpd->flag = 0; + ret = pci_vpd_wait(dev); + if (ret < 0) + break; + + pos += sizeof(u32); + } +out: + mutex_unlock(&vpd->lock); + return ret ? ret : count; +} + +static int pci_vpd_set_size(struct pci_dev *dev, size_t len) +{ + struct pci_vpd *vpd = dev->vpd; + + if (len == 0 || len > PCI_VPD_MAX_SIZE) + return -EIO; + + vpd->valid = 1; + vpd->len = len; + + return 0; +} + +static const struct pci_vpd_ops pci_vpd_ops = { + .read = pci_vpd_read, + .write = pci_vpd_write, + .set_size = pci_vpd_set_size, +}; + +static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) + return -ENODEV; + + ret = pci_read_vpd(tdev, pos, count, arg); + pci_dev_put(tdev); + return ret; +} + +static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) + return -ENODEV; + + ret = pci_write_vpd(tdev, pos, count, arg); + pci_dev_put(tdev); + return ret; +} + +static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len) +{ + struct pci_dev *tdev = pci_get_slot(dev->bus, + PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + int ret; + + if (!tdev) + return -ENODEV; + + ret = pci_set_vpd_size(tdev, len); + pci_dev_put(tdev); + return ret; +} + +static const struct pci_vpd_ops pci_vpd_f0_ops = { + .read = pci_vpd_f0_read, + .write = pci_vpd_f0_write, + .set_size = pci_vpd_f0_set_size, +}; + +int pci_vpd_init(struct pci_dev *dev) +{ + struct pci_vpd *vpd; + u8 cap; + + cap = pci_find_capability(dev, PCI_CAP_ID_VPD); + if (!cap) + return -ENODEV; + + vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); + if (!vpd) + return -ENOMEM; + + vpd->len = PCI_VPD_MAX_SIZE; + if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) + vpd->ops = &pci_vpd_f0_ops; + else + vpd->ops = &pci_vpd_ops; + mutex_init(&vpd->lock); + vpd->cap = cap; + vpd->busy = 0; + vpd->valid = 0; + dev->vpd = vpd; + return 0; +} + +void pci_vpd_release(struct pci_dev *dev) +{ + kfree(dev->vpd); +} + +static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); + + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } + + return pci_read_vpd(dev, off, count, buf); +} + +static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); + + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } + + return pci_write_vpd(dev, off, count, buf); +} + +void pcie_vpd_create_sysfs_dev_files(struct pci_dev *dev) +{ + int retval; + struct bin_attribute *attr; + + if (!dev->vpd) + return; + + attr = kzalloc(sizeof(*attr), GFP_ATOMIC); + if (!attr) + return; + + sysfs_bin_attr_init(attr); + attr->size = 0; + attr->attr.name = "vpd"; + attr->attr.mode = S_IRUSR | S_IWUSR; + attr->read = read_vpd_attr; + attr->write = write_vpd_attr; + retval = sysfs_create_bin_file(&dev->dev.kobj, attr); + if (retval) { + kfree(attr); + return; + } + + dev->vpd->attr = attr; +} + +void pcie_vpd_remove_sysfs_dev_files(struct pci_dev *dev) +{ + if (dev->vpd && dev->vpd->attr) { + sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); + kfree(dev->vpd->attr); + } +} int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt) { @@ -61,3 +513,132 @@ int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off, return -ENOENT; } EXPORT_SYMBOL_GPL(pci_vpd_find_info_keyword); + +#ifdef CONFIG_PCI_QUIRKS +/* + * Quirk non-zero PCI functions to route VPD access through function 0 for + * devices that share VPD resources between functions. The functions are + * expected to be identical devices. + */ +static void quirk_f0_vpd_link(struct pci_dev *dev) +{ + struct pci_dev *f0; + + if (!PCI_FUNC(dev->devfn)) + return; + + f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + if (!f0) + return; + + if (f0->vpd && dev->class == f0->class && + dev->vendor == f0->vendor && dev->device == f0->device) + dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; + + pci_dev_put(f0); +} +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); + +/* + * If a device follows the VPD format spec, the PCI core will not read or + * write past the VPD End Tag. But some vendors do not follow the VPD + * format spec, so we can't tell how much data is safe to access. Devices + * may behave unpredictably if we access too much. Blacklist these devices + * so we don't touch VPD at all. + */ +static void quirk_blacklist_vpd(struct pci_dev *dev) +{ + if (dev->vpd) { + dev->vpd->len = 0; + pci_warn(dev, FW_BUG "disabling VPD access (can't determine size of non-standard VPD format)\n"); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, + quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd); + +/* + * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the + * VPD end tag will hang the device. This problem was initially + * observed when a vpd entry was created in sysfs + * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry + * will dump 32k of data. Reading a full 32k will cause an access + * beyond the VPD end tag causing the device to hang. Once the device + * is hung, the bnx2 driver will not be able to reset the device. + * We believe that it is legal to read beyond the end tag and + * therefore the solution is to limit the read/write length. + */ +static void quirk_brcm_570x_limit_vpd(struct pci_dev *dev) +{ + /* + * Only disable the VPD capability for 5706, 5706S, 5708, + * 5708S and 5709 rev. A + */ + if ((dev->device == PCI_DEVICE_ID_NX2_5706) || + (dev->device == PCI_DEVICE_ID_NX2_5706S) || + (dev->device == PCI_DEVICE_ID_NX2_5708) || + (dev->device == PCI_DEVICE_ID_NX2_5708S) || + ((dev->device == PCI_DEVICE_ID_NX2_5709) && + (dev->revision & 0xf0) == 0x0)) { + if (dev->vpd) + dev->vpd->len = 0x80; + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5706S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5708S, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709, + quirk_brcm_570x_limit_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, + PCI_DEVICE_ID_NX2_5709S, + quirk_brcm_570x_limit_vpd); + +static void quirk_chelsio_extend_vpd(struct pci_dev *dev) +{ + int chip = (dev->device & 0xf000) >> 12; + int func = (dev->device & 0x0f00) >> 8; + int prod = (dev->device & 0x00ff) >> 0; + + /* + * If this is a T3-based adapter, there's a 1KB VPD area at offset + * 0xc00 which contains the preferred VPD values. If this is a T4 or + * later based adapter, the special VPD is at offset 0x400 for the + * Physical Functions (the SR-IOV Virtual Functions have no VPD + * Capabilities). The PCI VPD Access core routines will normally + * compute the size of the VPD by parsing the VPD Data Structure at + * offset 0x000. This will result in silent failures when attempting + * to accesses these other VPD areas which are beyond those computed + * limits. + */ + if (chip == 0x0 && prod >= 0x20) + pci_set_vpd_size(dev, 8192); + else if (chip >= 0x4 && func < 0x8) + pci_set_vpd_size(dev, 2048); +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, + quirk_chelsio_extend_vpd); + +#endif diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index 8785014..eba6e33 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -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> diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 9a68914..bb65585 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -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 diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c9608b0..ba4dd54 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -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; }; diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 8de9cc2..04c4cc6 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -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 __iomem *ioremap_wt(phys_addr_t offset, size_t size) #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 index 0000000..cbd9d84 --- /dev/null +++ b/include/linux/logic_pio.h @@ -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 */ diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index a1a5e5d..af657ca 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -39,10 +39,9 @@ struct pci_epc_ops { int (*write_header)(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int (*set_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); + struct pci_epf_bar *epf_bar); void (*clear_bar)(struct pci_epc *epc, u8 func_no, - enum pci_barno bar); + struct pci_epf_bar *epf_bar); int (*map_addr)(struct pci_epc *epc, u8 func_no, phys_addr_t addr, u64 pci_addr, size_t size); void (*unmap_addr)(struct pci_epc *epc, u8 func_no, @@ -127,9 +126,9 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, - enum pci_barno bar, - dma_addr_t bar_phys, size_t size, int flags); -void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, int bar); + struct pci_epf_bar *epf_bar); +void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, + struct pci_epf_bar *epf_bar); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, u64 pci_addr, size_t size); diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index e897bf0..f7d6f48 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -97,6 +97,8 @@ struct pci_epf_driver { struct pci_epf_bar { dma_addr_t phys_addr; size_t size; + enum pci_barno barno; + int flags; }; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index ae42289..73178a2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -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); @@ -1228,7 +1231,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); @@ -1297,7 +1301,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) @@ -1448,10 +1451,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 @@ -1663,9 +1664,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; } @@ -1925,6 +1923,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; @@ -1957,6 +1956,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) { @@ -2184,24 +2188,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, @@ -2212,6 +2203,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); @@ -2282,41 +2284,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> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2d61d9b..cc608fc5 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1334,6 +1334,7 @@ #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 diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h deleted file mode 100644 index b69769d..0000000 --- a/include/linux/pcieport_if.h +++ /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_ */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 0c79eac..103ba79 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -520,6 +520,7 @@ #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 */ @@ -547,6 +548,7 @@ #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 */ @@ -648,8 +650,9 @@ #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 */ diff --git a/lib/Kconfig b/lib/Kconfig index e960894..5fe5776 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -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 diff --git a/lib/Makefile b/lib/Makefile index 0bd50d71f..8fc0d3a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -82,6 +82,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 index 0000000..feea48f --- /dev/null +++ b/lib/logic_pio.c @@ -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 */ |