diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index f4d2662871ab..be35e9a1ee75 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -338,7 +338,7 @@ the PCI_IRQ_MSI and PCI_IRQ_MSIX flags will fail, so try to always specify PCI_IRQ_INTX as well. Drivers that have different interrupt handlers for MSI/MSI-X and -legacy INTx should chose the right one based on the msi_enabled +legacy INTx should choose the right one based on the msi_enabled and msix_enabled flags in the pci_dev structure after calling pci_alloc_irq_vectors. diff --git a/Documentation/PCI/pciebus-howto.rst b/Documentation/PCI/pciebus-howto.rst index 375d9ce171f6..9cc133ccdeec 100644 --- a/Documentation/PCI/pciebus-howto.rst +++ b/Documentation/PCI/pciebus-howto.rst @@ -97,7 +97,7 @@ register its service with the PCI Express Port Bus driver (see section 5.2.1 & 5.2.2). It is important that a service driver initializes the pcie_port_service_driver data structure, included in header file /include/linux/pcieport_if.h, before calling these APIs. -Failure to do so will result an identity mismatch, which prevents +Failure to do so will result in an identity mismatch, which prevents the PCI Express Port Bus driver from loading a service driver. pcie_port_service_register diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index 9d1349855b42..e8b8131f5f23 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -66,16 +66,34 @@ properties: - const: dma reset-gpio: + deprecated: true description: Should specify the GPIO for controlling the PCI bus device reset signal. It's not polarity aware and defaults to active-low reset sequence (L=reset state, H=operation state) (optional required). + This property is deprecated, instead of referencing this property from the + host bridge node, use the reset-gpios property from the root port node. reset-gpio-active-high: + deprecated: true description: If present then the reset sequence using the GPIO specified in the "reset-gpio" property is reversed (H=reset state, L=operation state) (optional required). + This property is deprecated along with the reset-gpio property above, use + the reset-gpios property from the root port node. type: boolean + pcie@0: + description: + Describe the i.MX6 PCIe Root Port. + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + unevaluatedProperties: false + required: - compatible - reg @@ -236,6 +254,7 @@ unevaluatedProperties: false examples: - | #include + #include #include pcie: pcie@1ffc000 { @@ -262,5 +281,18 @@ examples: <&clks IMX6QDL_CLK_LVDS1_GATE>, <&clks IMX6QDL_CLK_PCIE_REF_125M>; clock-names = "pcie", "pcie_bus", "pcie_phy"; + + pcie_port0: pcie@0 { + compatible = "pciclass,0604"; + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + }; }; ... diff --git a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml index 54e2890ae631..394bb46b38e6 100644 --- a/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/intel-gw-pcie.yaml @@ -27,16 +27,20 @@ properties: - const: snps,dw-pcie reg: + minItems: 3 items: - description: Controller control and status registers. - description: PCIe configuration registers. - description: Controller application registers. + - description: Internal Address Translation Unit (iATU) registers. reg-names: + minItems: 3 items: - const: dbi - const: config - const: app + - const: atu ranges: maxItems: 1 @@ -95,8 +99,9 @@ examples: #size-cells = <2>; reg = <0xd0e00000 0x1000>, <0xd2000000 0x800000>, - <0xd0a41000 0x1000>; - reg-names = "dbi", "config", "app"; + <0xd0a41000 0x1000>, + <0xd0ec0000 0x1000>; + reg-names = "dbi", "config", "app", "atu"; linux,pci-domain = <0>; max-link-speed = <4>; bus-range = <0x00 0x08>; diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml index 0b8c78ec4f91..c009a7a52bc6 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml @@ -14,6 +14,7 @@ properties: oneOf: - enum: - airoha,an7583-pcie + - econet,en7528-pcie - mediatek,mt2712-pcie - mediatek,mt7622-pcie - mediatek,mt7629-pcie @@ -226,6 +227,31 @@ allOf: mediatek,pbus-csr: false + - if: + properties: + compatible: + contains: + const: econet,en7528-pcie + then: + properties: + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + + resets: false + + reset-names: false + + power-domains: false + + mediatek,pbus-csr: false + + required: + - phys + - phy-names + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml index 3a94a9c1bb15..fb706b1397a3 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml @@ -20,6 +20,7 @@ properties: - const: qcom,pcie-sm8550 - items: - enum: + - qcom,eliza-pcie - qcom,kaanapali-pcie - qcom,sar2130p-pcie - qcom,pcie-sm8650 @@ -91,6 +92,55 @@ required: allOf: - $ref: qcom,pcie-common.yaml# + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + reg: + minItems: 6 + reg-names: + minItems: 6 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + clocks: + minItems: 8 + maxItems: 8 + clock-names: + minItems: 8 + maxItems: 8 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + interrupts: + minItems: 9 + interrupt-names: + minItems: 9 + + - if: + properties: + compatible: + contains: + const: qcom,eliza-pcie + then: + properties: + resets: + minItems: 2 + reset-names: + minItems: 2 unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml index a67108c48feb..90086909e921 100644 --- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml @@ -4,21 +4,27 @@ $id: http://devicetree.org/schemas/pci/renesas,r9a08g045-pcie.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Renesas RZ/G3S PCIe host controller +title: Renesas RZ/G3S PCIe host controller (and similar SoCs) maintainers: - Claudiu Beznea -description: - Renesas RZ/G3{E,S} PCIe host controllers comply with PCIe - Base Specification 4.0 and support up to 5 GT/s (Gen2) for RZ/G3S and - up to 8 GT/s (Gen3) for RZ/G3E. +description: | + PCIe host controller found in Renesas RZ/G3S and similar SoCs complies + with PCIe Base Specification 4.0 and supports different link speeds + depending on the SoC variant: + - Gen2 (5 GT/s): RZ/G3S + - Gen3 (8 GT/s): RZ/G3E, RZ/V2N properties: compatible: - enum: - - renesas,r9a08g045-pcie # RZ/G3S - - renesas,r9a09g047-pcie # RZ/G3E + oneOf: + - enum: + - renesas,r9a08g045-pcie # RZ/G3S + - renesas,r9a09g047-pcie # RZ/G3E + - items: + - const: renesas,r9a09g056-pcie # RZ/V2N + - const: renesas,r9a09g047-pcie reg: maxItems: 1 @@ -152,6 +158,7 @@ patternProperties: enum: - 0x0033 - 0x0039 + - 0x003b clocks: items: diff --git a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml index f8b7ca57fff1..ab482488b047 100644 --- a/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/sophgo,sg2042-pcie-host.yaml @@ -30,6 +30,8 @@ properties: device-id: const: 0x2042 + dma-coherent: true + msi-parent: true allOf: @@ -60,5 +62,6 @@ examples: vendor-id = <0x1f1c>; device-id = <0x2042>; cdns,no-bar-match-nbits = <48>; + dma-coherent; msi-parent = <&msi>; }; diff --git a/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml new file mode 100644 index 000000000000..512b935bf5d1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/ultrarisc,dp1000-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UltraRISC DP1000 PCIe Host Controller + +description: + UltraRISC DP1000 SoC PCIe host controller is based on the DesignWare PCIe IP. + +maintainers: + - Xincheng Zhang + - Jia Wang + +allOf: + - $ref: /schemas/pci/snps,dw-pcie.yaml# + +properties: + compatible: + const: ultrarisc,dp1000-pcie + + reg: + items: + - description: Data Bus Interface (DBI) registers. + - description: PCIe configuration space region. + + reg-names: + items: + - const: dbi + - const: config + + num-lanes: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [4, 16] + description: Number of lanes to use. + + interrupts: + items: + - description: MSI interrupt + - description: Legacy INTA interrupt + - description: Legacy INTB interrupt + - description: Legacy INTC interrupt + - description: Legacy INTD interrupt + + interrupt-names: + items: + - const: msi + - const: inta + - const: intb + - const: intc + - const: intd + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + +unevaluatedProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@21000000 { + compatible = "ultrarisc,dp1000-pcie"; + reg = <0x0 0x21000000 0x0 0x01000000>, + <0x0 0x4fff0000 0x0 0x00010000>; + reg-names = "dbi", "config"; + ranges = <0x81000000 0x0 0x4fbf0000 0x0 0x4fbf0000 0x0 0x00400000>, + <0x82000000 0x0 0x40000000 0x0 0x40000000 0x0 0x0fbf0000>, + <0xc3000000 0x40 0x00000000 0x40 0x00000000 0xd 0x00000000>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + device_type = "pci"; + dma-coherent; + bus-range = <0x0 0xff>; + num-lanes = <16>; + interrupt-parent = <&plic>; + interrupts = <43>, <44>, <45>, <46>, <47>; + interrupt-names = "msi", "inta", "intb", "intc", "intd"; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &plic 44>, + <0x0 0x0 0x0 0x2 &plic 45>, + <0x0 0x0 0x0 0x3 &plic 46>, + <0x0 0x0 0x0 0x4 &plic 47>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 18b8406091ba..3eeb134cb611 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21050,6 +21050,14 @@ S: Maintained F: Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml F: drivers/pci/controller/plda/pcie-starfive.c +PCIE DRIVER FOR ULTRARISC DP1000 +M: Xincheng Zhang +M: Jia Wang +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml +F: drivers/pci/controller/dwc/pcie-ultrarisc.c + PCIE ENDPOINT DRIVER FOR QUALCOMM M: Manivannan Sadhasivam L: linux-pci@vger.kernel.org diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 6c04fcbdc8ed..ad5d1391e1fa 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -84,8 +84,17 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, extern int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, enum pci_mmap_state mmap_state); -extern void pci_adjust_legacy_attr(struct pci_bus *bus, - enum pci_mmap_state mmap_type); +extern bool pci_legacy_has_sparse(struct pci_bus *bus, + enum pci_mmap_state type); #define HAVE_PCI_LEGACY 1 +extern const struct attribute_group pci_dev_resource_attr_group; +extern const struct attribute_group pci_dev_resource_sparse_attr_group; +extern const struct attribute_group pci_dev_resource_dense_attr_group; + +#define ARCH_PCI_DEV_GROUPS \ + &pci_dev_resource_attr_group, \ + &pci_dev_resource_sparse_attr_group, \ + &pci_dev_resource_dense_attr_group, + #endif /* __ALPHA_PCI_H */ diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index 3048758304b5..94dbc470cd6c 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -11,8 +11,7 @@ */ #include -#include -#include +#include #include static int hose_mmap_page_range(struct pci_controller *hose, @@ -36,20 +35,18 @@ static int hose_mmap_page_range(struct pci_controller *hose, static int __pci_mmap_fits(struct pci_dev *pdev, int num, struct vm_area_struct *vma, int sparse) { + resource_size_t len = pci_resource_len(pdev, num); unsigned long nr, start, size; int shift = sparse ? 5 : 0; + if (!len) + return 0; + nr = vma_pages(vma); start = vma->vm_pgoff; - size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1; + size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1; - if (start < size && size - start >= nr) - return 1; - WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d " - "(size 0x%08lx)\n", - current->comm, sparse ? " sparse" : "", start, start + nr, - pci_name(pdev), num, size); - return 0; + return start < size && size - start >= nr; } /** @@ -68,26 +65,25 @@ static int pci_mmap_resource(struct kobject *kobj, struct vm_area_struct *vma, int sparse) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - struct resource *res = attr->private; + int barno = (unsigned long)attr->private; enum pci_mmap_state mmap_type; struct pci_bus_region bar; - int i; + int ret; - for (i = 0; i < PCI_STD_NUM_BARS; i++) - if (res == &pdev->resource[i]) - break; - if (i >= PCI_STD_NUM_BARS) - return -ENODEV; + ret = security_locked_down(LOCKDOWN_PCI_ACCESS); + if (ret) + return ret; - if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) + if (pci_resource_is_mem(pdev, barno) && + iomem_is_exclusive(pci_resource_start(pdev, barno))) return -EINVAL; - if (!__pci_mmap_fits(pdev, i, vma, sparse)) + if (!__pci_mmap_fits(pdev, barno, vma, sparse)) return -EINVAL; - pcibios_resource_to_bus(pdev->bus, &bar, res); + pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, barno)); vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0)); - mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + mmap_type = pci_resource_is_mem(pdev, barno) ? pci_mmap_mem : pci_mmap_io; return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse); } @@ -106,34 +102,26 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj, return pci_mmap_resource(kobj, attr, vma, 0); } -/** - * pci_remove_resource_files - cleanup resource files - * @pdev: pci_dev to cleanup - * - * If we created resource files for @dev, remove them from sysfs and - * free their resources. - */ -void pci_remove_resource_files(struct pci_dev *pdev) -{ - int i; - - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - struct bin_attribute *res_attr; - - res_attr = pdev->res_attr[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - - res_attr = pdev->res_attr_wc[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - } +#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \ +static const struct bin_attribute \ +pci_dev_resource##_bar##_suffix##_attr = { \ + .attr = { .name = __stringify(_name), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .mmap = (_mmap), \ } +#define pci_dev_resource_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar,, \ + pci_mmap_resource_dense) + +#define pci_dev_resource_sparse_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \ + pci_mmap_resource_sparse) + +#define pci_dev_resource_dense_attr(_bar) \ + __pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \ + pci_mmap_resource_dense) + static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) { struct pci_bus_region bar; @@ -141,7 +129,7 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) long dense_offset; unsigned long sparse_size; - pcibios_resource_to_bus(pdev->bus, &bar, &pdev->resource[num]); + pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, num)); /* All core logic chips have 4G sparse address space, except CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM @@ -153,109 +141,10 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num) return bar.end < sparse_size; } -static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name, - char *suffix, struct bin_attribute *res_attr, - unsigned long sparse) -{ - size_t size = pci_resource_len(pdev, num); - - sprintf(name, "resource%d%s", num, suffix); - res_attr->mmap = sparse ? pci_mmap_resource_sparse : - pci_mmap_resource_dense; - res_attr->attr.name = name; - res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->size = sparse ? size << 5 : size; - res_attr->private = &pdev->resource[num]; - return sysfs_create_bin_file(&pdev->dev.kobj, res_attr); -} - -static int pci_create_attr(struct pci_dev *pdev, int num) -{ - /* allocate attribute structure, piggyback attribute name */ - int retval, nlen1, nlen2 = 0, res_count = 1; - unsigned long sparse_base, dense_base; - struct bin_attribute *attr; - struct pci_controller *hose = pdev->sysdata; - char *suffix, *attr_name; - - suffix = ""; /* Assume bwx machine, normal resourceN files. */ - nlen1 = 10; - - if (pdev->resource[num].flags & IORESOURCE_MEM) { - sparse_base = hose->sparse_mem_base; - dense_base = hose->dense_mem_base; - if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) { - sparse_base = 0; - suffix = "_dense"; - nlen1 = 16; /* resourceN_dense */ - } - } else { - sparse_base = hose->sparse_io_base; - dense_base = hose->dense_io_base; - } - - if (sparse_base) { - suffix = "_sparse"; - nlen1 = 17; - if (dense_base) { - nlen2 = 16; /* resourceN_dense */ - res_count = 2; - } - } - - attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC); - if (!attr) - return -ENOMEM; - - /* Create bwx, sparse or single dense file */ - attr_name = (char *)(attr + res_count); - pdev->res_attr[num] = attr; - retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr, - sparse_base); - if (retval || res_count == 1) - return retval; - - /* Create dense file */ - attr_name += nlen1; - attr++; - pdev->res_attr_wc[num] = attr; - return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0); -} - -/** - * pci_create_resource_files - create resource files in sysfs for @pdev - * @pdev: pci_dev in question - * - * Walk the resources in @dev creating files for each resource available. - * - * Return: %0 on success, or negative error code - */ -int pci_create_resource_files(struct pci_dev *pdev) -{ - int i; - int retval; - - /* Expose the PCI resources from this device as files */ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - - /* skip empty resources */ - if (!pci_resource_len(pdev, i)) - continue; - - retval = pci_create_attr(pdev, i); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } - return 0; -} - /* Legacy I/O bus mapping stuff. */ -static int __legacy_mmap_fits(struct pci_controller *hose, - struct vm_area_struct *vma, - unsigned long res_size, int sparse) +static int __legacy_mmap_fits(struct vm_area_struct *vma, + unsigned long res_size) { unsigned long nr, start, size; @@ -263,13 +152,7 @@ static int __legacy_mmap_fits(struct pci_controller *hose, start = vma->vm_pgoff; size = ((res_size - 1) >> PAGE_SHIFT) + 1; - if (start < size && size - start >= nr) - return 1; - WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d " - "(size 0x%08lx)\n", - current->comm, sparse ? " sparse" : "", start, start + nr, - hose->index, size); - return 0; + return start < size && size - start >= nr; } static inline int has_sparse(struct pci_controller *hose, @@ -290,36 +173,22 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, int sparse = has_sparse(hose, mmap_type); unsigned long res_size; - res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size : - bus->legacy_io->size; - if (!__legacy_mmap_fits(hose, vma, res_size, sparse)) + res_size = (mmap_type == pci_mmap_mem) ? PCI_LEGACY_MEM_SIZE : + PCI_LEGACY_IO_SIZE; + if (sparse) + res_size <<= 5; + + if (!__legacy_mmap_fits(vma, res_size)) return -EINVAL; return hose_mmap_page_range(hose, vma, mmap_type, sparse); } -/** - * pci_adjust_legacy_attr - adjustment of legacy file attributes - * @bus: bus to create files under - * @mmap_type: I/O port or memory - * - * Adjust file name and size for sparse mappings. - */ -void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type) +bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type) { struct pci_controller *hose = bus->sysdata; - if (!has_sparse(hose, mmap_type)) - return; - - if (mmap_type == pci_mmap_mem) { - bus->legacy_mem->attr.name = "legacy_mem_sparse"; - bus->legacy_mem->size <<= 5; - } else { - bus->legacy_io->attr.name = "legacy_io_sparse"; - bus->legacy_io->size <<= 5; - } - return; + return has_sparse(hose, type); } /* Legacy I/O bus read/write functions */ @@ -370,3 +239,166 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size) } return -EINVAL; } + +pci_dev_resource_attr(0); +pci_dev_resource_attr(1); +pci_dev_resource_attr(2); +pci_dev_resource_attr(3); +pci_dev_resource_attr(4); +pci_dev_resource_attr(5); + +pci_dev_resource_sparse_attr(0); +pci_dev_resource_sparse_attr(1); +pci_dev_resource_sparse_attr(2); +pci_dev_resource_sparse_attr(3); +pci_dev_resource_sparse_attr(4); +pci_dev_resource_sparse_attr(5); + +pci_dev_resource_dense_attr(0); +pci_dev_resource_dense_attr(1); +pci_dev_resource_dense_attr(2); +pci_dev_resource_dense_attr(3); +pci_dev_resource_dense_attr(4); +pci_dev_resource_dense_attr(5); + +static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev, + int bar) +{ + return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io; +} + +static inline umode_t __pci_resource_attr_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + if (!pci_resource_len(pdev, bar)) + return 0; + + return a->attr.mode; +} + +static umode_t pci_dev_resource_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + + if (has_sparse(hose, pci_bar_mmap_type(pdev, bar))) + return 0; + + return __pci_resource_attr_is_visible(kobj, a, bar); +} + +static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar); + + if (!has_sparse(hose, type)) + return 0; + + if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) + return 0; + + return __pci_resource_attr_is_visible(kobj, a, bar); +} + +static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + struct pci_controller *hose = pdev->sysdata; + enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar); + unsigned long dense_base; + + if (!has_sparse(hose, type)) + return 0; + + if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar)) + return __pci_resource_attr_is_visible(kobj, a, bar); + + dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base : + hose->dense_io_base; + if (!dense_base) + return 0; + + return __pci_resource_attr_is_visible(kobj, a, bar); +} + +static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj, + int bar, bool sparse) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + size_t size = pci_resource_len(pdev, bar); + + return sparse ? size << 5 : size; +} + +static size_t pci_dev_resource_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + return __pci_dev_resource_bin_size(kobj, bar, false); +} + +static size_t pci_dev_resource_sparse_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int bar) +{ + return __pci_dev_resource_bin_size(kobj, bar, true); +} + +static const struct bin_attribute *const pci_dev_resource_attrs[] = { + &pci_dev_resource0_attr, + &pci_dev_resource1_attr, + &pci_dev_resource2_attr, + &pci_dev_resource3_attr, + &pci_dev_resource4_attr, + &pci_dev_resource5_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_sparse_attrs[] = { + &pci_dev_resource0_sparse_attr, + &pci_dev_resource1_sparse_attr, + &pci_dev_resource2_sparse_attr, + &pci_dev_resource3_sparse_attr, + &pci_dev_resource4_sparse_attr, + &pci_dev_resource5_sparse_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_dense_attrs[] = { + &pci_dev_resource0_dense_attr, + &pci_dev_resource1_dense_attr, + &pci_dev_resource2_dense_attr, + &pci_dev_resource3_dense_attr, + &pci_dev_resource4_dense_attr, + &pci_dev_resource5_dense_attr, + NULL, +}; + +const struct attribute_group pci_dev_resource_attr_group = { + .bin_attrs = pci_dev_resource_attrs, + .is_bin_visible = pci_dev_resource_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +const struct attribute_group pci_dev_resource_sparse_attr_group = { + .bin_attrs = pci_dev_resource_sparse_attrs, + .is_bin_visible = pci_dev_resource_sparse_is_visible, + .bin_size = pci_dev_resource_sparse_bin_size, +}; + +const struct attribute_group pci_dev_resource_dense_attr_group = { + .bin_attrs = pci_dev_resource_dense_attrs, + .is_bin_visible = pci_dev_resource_dense_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 3298eec123a3..256f9309bf4f 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -169,7 +169,7 @@ struct machdep_calls { #ifdef CONFIG_PCI_IOV void (*pcibios_fixup_sriov)(struct pci_dev *pdev); - resource_size_t (*pcibios_iov_resource_alignment)(struct pci_dev *, int resno); + resource_size_t (*pcibios_iov_resource_alignment)(const struct pci_dev *, int resno); int (*pcibios_sriov_enable)(struct pci_dev *pdev, u16 num_vfs); int (*pcibios_sriov_disable)(struct pci_dev *pdev); #endif /* CONFIG_PCI_IOV */ diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 46a9c4491ed0..72f286e74786 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -82,8 +82,6 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, extern int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, enum pci_mmap_state mmap_state); -extern void pci_adjust_legacy_attr(struct pci_bus *bus, - enum pci_mmap_state mmap_type); #define HAVE_PCI_LEGACY 1 extern void pcibios_claim_one_bus(struct pci_bus *b); diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 8efe95a0c4ff..3c4ca90e2ab7 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -254,7 +254,7 @@ resource_size_t pcibios_default_alignment(void) } #ifdef CONFIG_PCI_IOV -resource_size_t pcibios_iov_resource_alignment(struct pci_dev *pdev, int resno) +resource_size_t pcibios_iov_resource_alignment(const struct pci_dev *pdev, int resno) { if (ppc_md.pcibios_iov_resource_alignment) return ppc_md.pcibios_iov_resource_alignment(pdev, resno); diff --git a/arch/powerpc/platforms/powernv/pci-sriov.c b/arch/powerpc/platforms/powernv/pci-sriov.c index 7105a573aec4..8652078801f2 100644 --- a/arch/powerpc/platforms/powernv/pci-sriov.c +++ b/arch/powerpc/platforms/powernv/pci-sriov.c @@ -244,8 +244,8 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev) } } -resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, - int resno) +resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev, + int resno) { resource_size_t align = pci_iov_resource_size(pdev, resno); struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus); diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 42075501663b..3ac718d471c2 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -251,13 +251,14 @@ struct pnv_iov_data { struct resource holes[PCI_SRIOV_NUM_BARS]; }; -static inline struct pnv_iov_data *pnv_iov_get(struct pci_dev *pdev) +static inline struct pnv_iov_data *pnv_iov_get(const struct pci_dev *pdev) { return pdev->dev.archdata.iov_data; } void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev); -resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno); +resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev, + int resno); int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs); int pnv_pcibios_sriov_disable(struct pci_dev *pdev); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 50b26ed8432d..1223dc961242 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -658,7 +658,8 @@ enum get_iov_fw_value_index { WDW_SIZE = 3 /* Get Window Size */ }; -static resource_size_t pseries_get_iov_fw_value(struct pci_dev *dev, int resno, +static resource_size_t pseries_get_iov_fw_value(const struct pci_dev *dev, + int resno, enum get_iov_fw_value_index value) { const int *indexes; @@ -788,7 +789,7 @@ static void pseries_pci_fixup_iov_resources(struct pci_dev *pdev) pseries_disable_sriov_resources(pdev); } -static resource_size_t pseries_pci_iov_resource_alignment(struct pci_dev *pdev, +static resource_size_t pseries_pci_iov_resource_alignment(const struct pci_dev *pdev, int resno) { const __be32 *reg; diff --git a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h index f2d70641c377..a84380125fe5 100644 --- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h @@ -29,15 +29,10 @@ #define ADF_4XXX_DEVICE_NAME "4xxx" #define ADF_420XX_DEVICE_NAME "420xx" #define ADF_6XXX_DEVICE_NAME "6xxx" -#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 #define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941 -#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 #define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943 -#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 #define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945 -#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 #define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947 -#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949 #define ADF_DEVICE_FUSECTL_OFFSET 0x40 diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index f95411363ea9..1dce26d4da83 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -10,9 +10,6 @@ #endif /* PCI Config */ -#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb -#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 -#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d #define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index dbd017cabbb9..3635741c3e7a 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -1100,7 +1100,6 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test) data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA); addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET); - bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); @@ -1108,6 +1107,11 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test) pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR); + if (bar < BAR_0 || bar >= PCI_STD_NUM_BARS) { + dev_err(dev, "BAR %d reported by endpoint out of range [0, %u]\n", + bar, PCI_STD_NUM_BARS - 1); + return -ERANGE; + } writel(data, test->bar[bar] + addr); diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index d3ecf25a5162..af5755472842 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I */ +#include #include #include #include @@ -43,6 +44,18 @@ #define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4) #define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4) +/* + * Legacy doorbell slot layout when paired with pci-epf-*ntb: + * + * slot 0 : reserved for link events + * slot 1 : unused (historical extra offset) + * slot 2 : DB#0 + * slot 3 : DB#1 + * ... + * + * Thus, NTB_EPF_MIN_DB_COUNT=3 means that we at least create vectors for + * doorbells DB#0 and DB#1. + */ #define NTB_EPF_MIN_DB_COUNT 3 #define NTB_EPF_MAX_DB_COUNT 31 @@ -69,8 +82,21 @@ enum epf_ntb_bar { NTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + #define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1) +struct ntb_epf_dev; + +struct ntb_epf_irq_ctx { + struct ntb_epf_dev *ndev; + unsigned int irq_no; +}; + struct ntb_epf_dev { struct ntb_dev ntb; struct device *dev; @@ -90,8 +116,9 @@ struct ntb_epf_dev { unsigned int self_spad; unsigned int peer_spad; - int db_val; + atomic64_t db_val; u64 db_valid_mask; + struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1]; }; #define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb) @@ -315,16 +342,29 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb) static irqreturn_t ntb_epf_vec_isr(int irq, void *dev) { - struct ntb_epf_dev *ndev = dev; - int irq_no; + struct ntb_epf_irq_ctx *ctx = dev; + struct ntb_epf_dev *ndev = ctx->ndev; + unsigned int db_vector; + unsigned int irq_no = ctx->irq_no; - irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0); - ndev->db_val = irq_no + 1; - - if (irq_no == 0) + if (irq_no == EPF_IRQ_LINK) { ntb_link_event(&ndev->ntb); - else - ntb_db_event(&ndev->ntb, irq_no); + } else if (irq_no == EPF_IRQ_RESERVED_DB) { + dev_warn_ratelimited(ndev->dev, + "Unexpected reserved doorbell slot IRQ received\n"); + } else { + db_vector = irq_no - EPF_IRQ_DB_START; + if (ndev->db_count < NTB_EPF_MIN_DB_COUNT || + db_vector >= ndev->db_count - 1) { + dev_warn_ratelimited(ndev->dev, + "Unexpected doorbell vector %u (db_count %u)\n", + db_vector, ndev->db_count); + return IRQ_HANDLED; + } + + atomic64_or(BIT_ULL(db_vector), &ndev->db_val); + ntb_db_event(&ndev->ntb, db_vector); + } return IRQ_HANDLED; } @@ -350,31 +390,30 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max) argument &= ~MSIX_ENABLE; } + ndev->db_count = irq - 1; for (i = 0; i < irq; i++) { + ndev->irq_ctx[i].ndev = ndev; + ndev->irq_ctx[i].irq_no = i; ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr, - 0, "ntb_epf", ndev); + 0, "ntb_epf", &ndev->irq_ctx[i]); if (ret) { dev_err(dev, "Failed to request irq\n"); - goto err_request_irq; + goto err_free_irq; } } - ndev->db_count = irq - 1; - ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL, argument | irq); if (ret) { dev_err(dev, "Failed to configure doorbell\n"); - goto err_configure_db; + goto err_free_irq; } return 0; -err_configure_db: - for (i = 0; i < ndev->db_count + 1; i++) - free_irq(pci_irq_vector(pdev, i), ndev); - -err_request_irq: +err_free_irq: + while (i--) + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); return ret; @@ -395,6 +434,36 @@ static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb) return ntb_ndev(ntb)->db_valid_mask; } +static int ntb_epf_db_vector_count(struct ntb_dev *ntb) +{ + struct ntb_epf_dev *ndev = ntb_ndev(ntb); + unsigned int db_count = ndev->db_count; + + /* + * db_count includes an extra skipped slot due to the legacy + * doorbell layout. Expose only the real doorbell vectors. + */ + if (db_count < NTB_EPF_MIN_DB_COUNT) + return 0; + + return db_count - 1; +} + +static u64 ntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + int nr_vec; + + /* + * db_count includes one skipped slot in the legacy layout. Valid + * doorbell vectors are therefore [0 .. (db_count - 2)]. + */ + nr_vec = ntb_epf_db_vector_count(ntb); + if (db_vector < 0 || db_vector >= nr_vec) + return 0; + + return BIT_ULL(db_vector); +} + static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) { return 0; @@ -473,6 +542,14 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx, static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); + /* + * ffs() returns a 1-based bit index (bit 0 -> 1). + * + * With slot 0 reserved for link events, DB#0 would naturally map to + * slot 1. Historically an extra +1 offset was added, so DB#0 maps to + * slot 2 and slot 1 remains unused. Keep this mapping for + * backward-compatibility. + */ u32 interrupt_num = ffs(db_bits) + 1; struct device *dev = ndev->dev; u32 db_entry_size; @@ -499,7 +576,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - return ndev->db_val; + return atomic64_read(&ndev->db_val); } static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) @@ -511,7 +588,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits) { struct ntb_epf_dev *ndev = ntb_ndev(ntb); - ndev->db_val = 0; + atomic64_and(~db_bits, &ndev->db_val); return 0; } @@ -521,6 +598,8 @@ static const struct ntb_dev_ops ntb_epf_ops = { .spad_count = ntb_epf_spad_count, .peer_mw_count = ntb_epf_peer_mw_count, .db_valid_mask = ntb_epf_db_valid_mask, + .db_vector_count = ntb_epf_db_vector_count, + .db_vector_mask = ntb_epf_db_vector_mask, .db_set_mask = ntb_epf_db_set_mask, .mw_set_trans = ntb_epf_mw_set_trans, .mw_clear_trans = ntb_epf_mw_clear_trans, @@ -552,6 +631,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) struct device *dev = ndev->dev; int ret; + ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { + dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); + return -EINVAL; + } + /* One Link interrupt and rest doorbell interrupt */ ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1, NTB_EPF_MAX_DB_COUNT + 1); @@ -560,15 +645,13 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev) return ret; } - ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; - ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT); + /* + * ndev->db_count includes an extra skipped slot due to the legacy + * doorbell layout, hence -1. + */ + ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1; ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); - if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) { - dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count); - return -EINVAL; - } - return 0; } @@ -662,7 +745,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev) ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1); for (i = 0; i < ndev->db_count + 1; i++) - free_irq(pci_irq_vector(pdev, i), ndev); + free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]); pci_free_irq_vectors(pdev); } diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 8438c904ec49..69932d640b53 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -3950,6 +3950,7 @@ static int nvme_suspend(struct device *dev) * use host managed nvme power settings for lowest idle power if * possible. This should have quicker resume latency than a full device * shutdown. But if the firmware is involved after the suspend or the + * platform has any limitation in waking from low power states or the * device does not support any non-default power states, shut down the * device fully. * @@ -3958,7 +3959,7 @@ static int nvme_suspend(struct device *dev) * down, so as to allow the platform to achieve its minimum low-power * state (which may not be possible if the link is up). */ - if (pm_suspend_via_firmware() || !ctrl->npss || + if (!pci_suspend_retains_context(pdev) || !ctrl->npss || !pcie_aspm_enabled(pdev) || (ndev->ctrl.quirks & NVME_QUIRK_SIMPLE_SUSPEND)) return nvme_disable_prepare_reset(ndev, true); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 33c88432b728..0c7408509ba2 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -251,63 +251,6 @@ config PCI_DYNAMIC_OF_NODES Once this option is selected, the device tree nodes will be generated for all PCI bridges. -choice - prompt "PCI Express hierarchy optimization setting" - default PCIE_BUS_DEFAULT - depends on EXPERT - help - MPS (Max Payload Size) and MRRS (Max Read Request Size) are PCIe - device parameters that affect performance and the ability to - support hotplug and peer-to-peer DMA. - - The following choices set the MPS and MRRS optimization strategy - at compile-time. The choices are the same as those offered for - the kernel command-line parameter 'pci', i.e., - 'pci=pcie_bus_tune_off', 'pci=pcie_bus_safe', - 'pci=pcie_bus_perf', and 'pci=pcie_bus_peer2peer'. - - This is a compile-time setting and can be overridden by the above - command-line parameters. If unsure, choose PCIE_BUS_DEFAULT. - -config PCIE_BUS_TUNE_OFF - bool "Tune Off" - help - Use the BIOS defaults; don't touch MPS at all. This is the same - as booting with 'pci=pcie_bus_tune_off'. - -config PCIE_BUS_DEFAULT - bool "Default" - help - Default choice; ensure that the MPS matches upstream bridge. - -config PCIE_BUS_SAFE - bool "Safe" - help - Use largest MPS that boot-time devices support. If you have a - closed system with no possibility of adding new devices, this - will use the largest MPS that's supported by all devices. This - is the same as booting with 'pci=pcie_bus_safe'. - -config PCIE_BUS_PERFORMANCE - bool "Performance" - help - Use MPS and MRRS for best performance. Ensure that a given - device's MPS is no larger than its parent MPS, which allows us to - keep all switches/bridges to the max MPS supported by their - parent. This is the same as booting with 'pci=pcie_bus_perf'. - -config PCIE_BUS_PEER2PEER - bool "Peer2peer" - help - Set MPS = 128 for all devices. MPS configuration effected by the - other options could cause the MPS on one root port to be - different than that of the MPS on another, which may cause - hot-added devices or peer-to-peer DMA to fail. Set MPS to the - smallest possible value (128B) system-wide to avoid these issues. - This is the same as booting with 'pci=pcie_bus_peer2peer'. - -endchoice - config VGA_ARB bool "VGA Arbitration" if EXPERT default y diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 6c1ad1f542d9..655ed53436d3 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -354,7 +354,6 @@ void pci_bus_add_device(struct pci_dev *dev) pci_fixup_device(pci_fixup_final, dev); if (pci_is_bridge(dev)) of_pci_make_dev_node(dev); - pci_create_sysfs_dev_files(dev); pci_proc_attach_device(dev); pci_bridge_d3_update(dev); diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index bfdfe98d5aba..ae916e7b1927 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -206,6 +206,7 @@ static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, (pcie_get_link_speed(link_speed) == PCI_SPEED_UNKNOWN)) link_speed = 2; + pcie->cdns_pcie->max_link_speed = link_speed; val = link_speed - 1; ret = regmap_update_bits(syscon, offset, GENERATION_SEL_MASK, val); if (ret) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c index 2b0211870f02..18e4b6c760b5 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c @@ -14,6 +14,7 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../pci-host-common.h" #define LINK_RETRAIN_TIMEOUT HZ @@ -115,6 +116,9 @@ int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc, if (!ret && rc->quirk_retrain_flag) ret = cdns_pcie_retrain(pcie, pcie_link_up); + if (!ret) + pci_host_common_link_train_delay(pcie->max_link_speed); + return ret; } EXPORT_SYMBOL_GPL(cdns_pcie_host_start_link); diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c index 0f540bed58e8..8ef58ed01daa 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c @@ -15,6 +15,8 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../pci-host-common.h" +#include "../../pci.h" static u8 bar_aperture_mask[] = { [RP_BAR0] = 0x3F, @@ -304,6 +306,8 @@ int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc) ret = cdns_pcie_host_wait_for_link(pcie, cdns_pcie_hpa_link_up); if (ret) dev_dbg(dev, "PCIe link never came up\n"); + else + pci_host_common_link_train_delay(pcie->max_link_speed); return ret; } @@ -313,6 +317,7 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; struct pci_host_bridge *bridge; enum cdns_pcie_rp_bar bar; struct cdns_pcie *pcie; @@ -343,6 +348,9 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc) rc->cfg_res = res; } + if (pcie->max_link_speed < 1) + pcie->max_link_speed = of_pci_get_max_link_speed(np); + /* Put EROM Bar aperture to 0 */ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_EROM, 0x0); diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 0bc9e6e90e0e..c12cedf05ad5 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -13,6 +13,7 @@ #include "pcie-cadence.h" #include "pcie-cadence-host-common.h" +#include "../../pci.h" static u8 bar_aperture_mask[] = { [RP_BAR0] = 0x1F, @@ -365,8 +366,10 @@ void cdns_pcie_host_disable(struct cdns_pcie_rc *rc) struct pci_host_bridge *bridge; bridge = pci_host_bridge_from_priv(rc); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); cdns_pcie_host_deinit(rc); cdns_pcie_host_link_disable(rc); @@ -397,6 +400,9 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) rc->device_id = 0xffff; of_property_read_u32(np, "device-id", &rc->device_id); + if (pcie->max_link_speed < 1) + pcie->max_link_speed = of_pci_get_max_link_speed(np); + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); if (IS_ERR(pcie->reg_base)) { dev_err(dev, "missing \"reg\"\n"); diff --git a/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h index 026e131600de..72299121fd44 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h +++ b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h @@ -14,6 +14,8 @@ #include #include +#include "../../pci.h" + /* High Performance Architecture (HPA) PCIe controller registers */ #define CDNS_PCIE_HPA_IP_REG_BANK 0x01000000 #define CDNS_PCIE_HPA_IP_CFG_CTRL_REG_BANK 0x01003C00 @@ -119,15 +121,15 @@ #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r) (0x1008 + ((r) & 0x1F) * 0x0080) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(28, 24) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x0) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MEM_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x2) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_IO_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x4) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG0_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x5) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG1_RDWR) #define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG \ - FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x10) + FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MSG) /* Region r Outbound PCIe Descriptor Register */ #define CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r) (0x100C + ((r) & 0x1F) * 0x0080) diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 574e9cf4d003..042a4c49bb9a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -86,6 +86,7 @@ struct cdns_plat_pcie_of_data { * @ops: Platform-specific ops to control various inputs from Cadence PCIe * wrapper * @cdns_pcie_reg_offsets: Register bank offsets for different SoC + * @max_link_speed: Maximum supported link speed */ struct cdns_pcie { void __iomem *reg_base; @@ -98,6 +99,7 @@ struct cdns_pcie { struct device_link **link; const struct cdns_pcie_ops *ops; const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets; + int max_link_speed; }; /** diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index f2fde13107f2..aa0b784c85b4 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -114,6 +114,7 @@ config PCI_IMX6_HOST depends on PCI_MSI select PCIE_DW_HOST select PCI_IMX6 + select PCI_PWRCTRL_GENERIC help Enables support for the PCIe controller in the i.MX SoCs to work in Root Complex mode. The PCI controller on i.MX is based @@ -560,4 +561,17 @@ config PCIE_VISCONTI_HOST Say Y here if you want PCIe controller support on Toshiba Visconti SoC. This driver supports TMPV7708 SoC. +config PCIE_ULTRARISC + tristate "UltraRISC PCIe host controller" + depends on ARCH_ULTRARISC || COMPILE_TEST + select PCIE_DW_HOST + select PCI_MSI + default y if ARCH_ULTRARISC + help + Enables support for the PCIe controller in the UltraRISC SoC. + This driver supports UR-DP1000 SoC. + + By default, this symbol is enabled when ARCH_ULTRARISC is active, + requiring no further configuration on that platform. + endmenu diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 7177451db8aa..d4f88cfc6229 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o obj-$(CONFIG_PCIE_SPACEMIT_K1) += pcie-spacemit-k1.o obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o +obj-$(CONFIG_PCIE_ULTRARISC) += pcie-ultrarisc.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index cd904659c321..3fc889944f02 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -92,7 +92,6 @@ struct dra7xx_pcie { struct phy **phy; struct irq_domain *irq_domain; struct clk *clk; - enum dw_pcie_device_mode mode; }; struct dra7xx_pcie_of_data { @@ -328,7 +327,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) dev_dbg(dev, "Link Request Reset\n"); if (reg & LINK_UP_EVT) { - if (dra7xx->mode == DW_PCIE_EP_TYPE) + if (dra7xx->pci->mode == DW_PCIE_EP_TYPE) dw_pcie_ep_linkup(ep); dev_dbg(dev, "Link-up state change\n"); } @@ -828,7 +827,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev) default: dev_err(dev, "INVALID device type %d\n", mode); } - dra7xx->mode = mode; + dra7xx->pci->mode = mode; ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler, IRQF_SHARED | IRQF_ONESHOT, @@ -841,7 +840,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev) return 0; err_deinit: - if (dra7xx->mode == DW_PCIE_RC_TYPE) + if (dra7xx->pci->mode == DW_PCIE_RC_TYPE) dw_pcie_host_deinit(&dra7xx->pci->pp); else dw_pcie_ep_deinit(&dra7xx->pci->ep); @@ -865,7 +864,7 @@ static int dra7xx_pcie_suspend(struct device *dev) struct dw_pcie *pci = dra7xx->pci; u32 val; - if (dra7xx->mode != DW_PCIE_RC_TYPE) + if (pci->mode != DW_PCIE_RC_TYPE) return 0; /* clear MSE */ @@ -882,7 +881,7 @@ static int dra7xx_pcie_resume(struct device *dev) struct dw_pcie *pci = dra7xx->pci; u32 val; - if (dra7xx->mode != DW_PCIE_RC_TYPE) + if (pci->mode != DW_PCIE_RC_TYPE) return 0; /* set MSE */ diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index f972c760deee..98e1db751132 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include "../../pci.h" +#include "../pci-host-common.h" #include "pcie-designware.h" #define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9) @@ -137,6 +139,7 @@ struct imx_pcie_drvdata { const u32 mode_off[IMX_PCIE_MAX_INSTANCES]; const u32 mode_mask[IMX_PCIE_MAX_INSTANCES]; const struct pci_epc_features *epc_features; + int (*select_ref_clk_src)(struct imx_pcie *pcie); int (*init_phy)(struct imx_pcie *pcie); int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); @@ -152,7 +155,6 @@ struct imx_lut_data { struct imx_pcie { struct dw_pcie *pci; - struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; bool supports_clkreq; @@ -168,6 +170,8 @@ struct imx_pcie { u32 tx_swing_full; u32 tx_swing_low; struct regulator *vpcie; + struct regulator *vpcie_aux; + bool vpcie_aux_enabled; struct regulator *vph; void __iomem *phy_base; @@ -247,10 +251,26 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14; } -static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) +static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie) { bool ext = imx_pcie->enable_ext_refclk; + /* + * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is + * used to select reference clock connected to a pair of pads. + * + * Any change in this input must be followed by phy_reset assertion. + */ + + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + IMX95_PCIE_REF_USE_PAD, + ext ? IMX95_PCIE_REF_USE_PAD : 0); + + return 0; +} + +static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) +{ /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -269,13 +289,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) IMX95_PCIE_PHY_CR_PARA_SEL, IMX95_PCIE_PHY_CR_PARA_SEL); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, - IMX95_PCIE_REF_USE_PAD, - ext ? IMX95_PCIE_REF_USE_PAD : 0); - regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, - IMX95_PCIE_REF_CLKEN, - ext ? 0 : IMX95_PCIE_REF_CLKEN); - return 0; } @@ -665,14 +678,6 @@ static int imx_pcie_attach_pd(struct device *dev) return 0; } -static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) -{ - regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, - IMX6SX_GPR12_PCIE_TEST_POWERDOWN, - enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN); - return 0; -} - static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { if (enable) { @@ -732,7 +737,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { + bool ext = imx_pcie->enable_ext_refclk; + imx95_pcie_clkreq_override(imx_pcie, enable); + /* + * The ref_clk_en signal must remain de-asserted until the + * reference clock is running at appropriate frequency, at which + * point this bit can be asserted. For lower power states where + * the reference clock to the PHY is disabled, it may also be + * de-asserted. + * +------------------- -+--------+----------------+ + * | External clock mode | Enable | PCIE_REF_CLKEN | + * +---------------------+--------+----------------+ + * | TRUE | X | 1b'0 | + * +---------------------+--------+----------------+ + * | FALSE | TRUE | 1b'1 | + * +---------------------+--------+----------------+ + * | FALSE | FALSE | 1b'0 | + * +---------------------+--------+----------------+ + */ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, + IMX95_PCIE_REF_CLKEN, + ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN); + return 0; } @@ -786,6 +813,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) if (assert) regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12, + IMX6SX_GPR12_PCIE_TEST_POWERDOWN); /* Force PCIe PHY reset */ regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET, @@ -1221,16 +1251,70 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge, imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev)); } +static int imx_pcie_parse_legacy_binding(struct imx_pcie *pcie) +{ + struct device *dev = pcie->pci->dev; + struct pci_host_bridge *bridge = pcie->pci->pp.bridge; + struct pci_host_port *port; + struct pci_host_perst *perst; + struct gpio_desc *reset; + + reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); + if (IS_ERR(reset)) + return PTR_ERR(reset); + + if (!reset) + return 0; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + + INIT_LIST_HEAD(&port->perst); + perst->desc = reset; + INIT_LIST_HEAD(&perst->list); + list_add_tail(&perst->list, &port->perst); + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &bridge->ports); + + return devm_add_action_or_reset(dev, pci_host_common_delete_ports, + &bridge->ports); +} + +static void imx_pcie_vpcie_aux_disable(void *data) +{ + struct regulator *vpcie_aux = data; + + regulator_disable(vpcie_aux); +} + static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert) { + struct dw_pcie *pci = imx_pcie->pci; + struct pci_host_bridge *bridge = pci->pp.bridge; + struct pci_host_perst *perst; + struct pci_host_port *port; + + if (!bridge || list_empty(&bridge->ports)) + return; + if (assert) { - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1); - } else { - if (imx_pcie->reset_gpiod) { - msleep(PCIE_T_PVPERL_MS); - gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0); - msleep(PCIE_RESET_CONFIG_WAIT_MS); + list_for_each_entry(port, &bridge->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_direction_output(perst->desc, 1); } + } else { + mdelay(PCIE_T_PVPERL_MS); + list_for_each_entry(port, &bridge->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_direction_output(perst->desc, 0); + } + mdelay(PCIE_RESET_CONFIG_WAIT_MS); } } @@ -1239,8 +1323,47 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct device *dev = pci->dev; struct imx_pcie *imx_pcie = to_imx_pcie(pci); + struct pci_host_bridge *bridge = pp->bridge; int ret; + if (bridge && list_empty(&bridge->ports)) { + /* Parse Root Port nodes if present */ + ret = pci_host_common_parse_ports(dev, bridge); + if (ret) { + if (ret != -ENODEV) { + dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret); + return ret; + } + + /* + * Fall back to legacy binding for DT backwards + * compatibility + */ + ret = imx_pcie_parse_legacy_binding(imx_pcie); + if (ret) + return ret; + } + } + + imx_pcie_assert_perst(imx_pcie, true); + + /* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */ + if (imx_pcie->vpcie_aux && !imx_pcie->vpcie_aux_enabled) { + ret = regulator_enable(imx_pcie->vpcie_aux); + if (ret) { + dev_err(dev, "failed to enable vpcie_aux regulator: %d\n", + ret); + return ret; + } + imx_pcie->vpcie_aux_enabled = true; + + ret = devm_add_action_or_reset(dev, imx_pcie_vpcie_aux_disable, + imx_pcie->vpcie_aux); + if (ret) + return ret; + } + + /* Legacy regulator handling for DT backward compatibility. */ if (imx_pcie->vpcie) { ret = regulator_enable(imx_pcie->vpcie); if (ret) { @@ -1250,25 +1373,39 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) } } + ret = pci_pwrctrl_create_devices(dev); + if (ret) { + dev_err(dev, "failed to create pwrctrl devices\n"); + goto err_reg_disable; + } + + ret = pci_pwrctrl_power_on_devices(dev); + if (ret) { + dev_err(dev, "failed to power on pwrctrl devices\n"); + goto err_pwrctrl_destroy; + } + + ret = imx_pcie_clk_enable(imx_pcie); + if (ret) { + dev_err(dev, "unable to enable pcie clocks: %d\n", ret); + goto err_pwrctrl_power_off; + } + if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) { pp->bridge->enable_device = imx_pcie_enable_device; pp->bridge->disable_device = imx_pcie_disable_device; } + if (imx_pcie->drvdata->select_ref_clk_src) + imx_pcie->drvdata->select_ref_clk_src(imx_pcie); + imx_pcie_assert_core_reset(imx_pcie); - imx_pcie_assert_perst(imx_pcie, true); if (imx_pcie->drvdata->init_phy) imx_pcie->drvdata->init_phy(imx_pcie); imx_pcie_configure_type(imx_pcie); - ret = imx_pcie_clk_enable(imx_pcie); - if (ret) { - dev_err(dev, "unable to enable pcie clocks: %d\n", ret); - goto err_reg_disable; - } - if (imx_pcie->phy) { ret = phy_init(imx_pcie->phy); if (ret) { @@ -1313,6 +1450,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) phy_exit(imx_pcie->phy); err_clk_disable: imx_pcie_clk_disable(imx_pcie); +err_pwrctrl_power_off: + pci_pwrctrl_power_off_devices(dev); +err_pwrctrl_destroy: + if (ret != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(dev); err_reg_disable: if (imx_pcie->vpcie) regulator_disable(imx_pcie->vpcie); @@ -1331,6 +1473,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) } imx_pcie_clk_disable(imx_pcie); + pci_pwrctrl_power_off_devices(pci->dev); if (imx_pcie->vpcie) regulator_disable(imx_pcie->vpcie); } @@ -1677,13 +1820,6 @@ static int imx_pcie_probe(struct platform_device *pdev) return PTR_ERR(imx_pcie->phy_base); } - /* Fetch GPIOs */ - imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(imx_pcie->reset_gpiod)) - return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod), - "unable to get reset gpio\n"); - gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset"); - /* Fetch clocks */ imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks); if (imx_pcie->num_clks < 0) @@ -1781,9 +1917,13 @@ static int imx_pcie_probe(struct platform_device *pdev) of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq"); - ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); - if (ret < 0 && ret != -ENODEV) - return dev_err_probe(dev, ret, "failed to enable Vaux supply\n"); + imx_pcie->vpcie_aux = devm_regulator_get_optional(&pdev->dev, + "vpcie3v3aux"); + if (IS_ERR(imx_pcie->vpcie_aux)) { + if (PTR_ERR(imx_pcie->vpcie_aux) != -ENODEV) + return PTR_ERR(imx_pcie->vpcie_aux); + imx_pcie->vpcie_aux = NULL; + } imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); if (IS_ERR(imx_pcie->vpcie)) { @@ -1845,6 +1985,8 @@ static void imx_pcie_shutdown(struct platform_device *pdev) /* bring down link, so bootloader gets clean state in case of reboot */ imx_pcie_assert_core_reset(imx_pcie); imx_pcie_assert_perst(imx_pcie, true); + pci_pwrctrl_power_off_devices(&pdev->dev); + pci_pwrctrl_destroy_devices(&pdev->dev); } static const struct imx_pcie_drvdata drvdata[] = { @@ -1876,7 +2018,6 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .init_phy = imx6sx_pcie_init_phy, - .enable_ref_clk = imx6sx_pcie_enable_ref_clk, .core_reset = imx6sx_pcie_core_reset, .ops = &imx_pcie_host_ops, }, @@ -1966,6 +2107,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .enable_ref_clk = imx95_pcie_enable_ref_clk, .clr_clkreq_override = imx95_pcie_clr_clkreq_override, @@ -2021,6 +2163,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .ltssm_mask = IMX95_PCIE_LTSSM_EN, .mode_off[0] = IMX95_PE0_GEN_CTRL_1, .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE, + .select_ref_clk_src = imx95_pcie_select_ref_clk_src, .init_phy = imx95_pcie_init_phy, .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c index 0694084f612b..225d887cd0a3 100644 --- a/drivers/pci/controller/dwc/pci-meson.c +++ b/drivers/pci/controller/dwc/pci-meson.c @@ -204,7 +204,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev, return ERR_PTR(ret); } - devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk); + if (ret) + return ERR_PTR(ret); return clk; } @@ -451,6 +453,14 @@ static int meson_pcie_probe(struct platform_device *pdev) return ret; } +static void meson_pcie_remove(struct platform_device *pdev) +{ + struct meson_pcie *mp = platform_get_drvdata(pdev); + + dw_pcie_host_deinit(&mp->pci.pp); + meson_pcie_power_off(mp); +} + static const struct of_device_id meson_pcie_of_match[] = { { .compatible = "amlogic,axg-pcie", @@ -464,6 +474,7 @@ MODULE_DEVICE_TABLE(of, meson_pcie_of_match); static struct platform_driver meson_pcie_driver = { .probe = meson_pcie_probe, + .remove = meson_pcie_remove, .driver = { .name = "meson-pcie", .of_match_table = meson_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 345c281c74fe..ef8c8ce7c78c 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -253,7 +253,6 @@ static int al_pcie_config_prepare(struct al_pcie *pcie) u8 subordinate_bus; u8 secondary_bus; u32 cfg_control; - u32 reg; ft = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); if (!ft) @@ -285,14 +284,9 @@ static int al_pcie_config_prepare(struct al_pcie *pcie) CFG_CONTROL; cfg_control = al_pcie_controller_readl(pcie, cfg_control_offset); - - reg = cfg_control & - ~(CFG_CONTROL_SEC_BUS_MASK | CFG_CONTROL_SUBBUS_MASK); - - reg |= FIELD_PREP(CFG_CONTROL_SUBBUS_MASK, subordinate_bus) | - FIELD_PREP(CFG_CONTROL_SEC_BUS_MASK, secondary_bus); - - al_pcie_controller_writel(pcie, cfg_control_offset, reg); + FIELD_MODIFY(CFG_CONTROL_SUBBUS_MASK, &cfg_control, subordinate_bus); + FIELD_MODIFY(CFG_CONTROL_SEC_BUS_MASK, &cfg_control, secondary_bus); + al_pcie_controller_writel(pcie, cfg_control_offset, cfg_control); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 7e50e11fbffd..dee295f281a3 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -507,6 +507,13 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev) return amd_mdb_add_pcie_port(pcie, pdev); } +static void amd_mdb_pcie_shutdown(struct platform_device *pdev) +{ + struct amd_mdb_pcie *pcie = platform_get_drvdata(pdev); + + gpiod_set_value_cansleep(pcie->perst_gpio, 1); +} + static const struct of_device_id amd_mdb_pcie_of_match[] = { { .compatible = "amd,versal2-mdb-host", @@ -521,6 +528,7 @@ static struct platform_driver amd_mdb_pcie_driver = { .suppress_bind_attrs = true, }, .probe = amd_mdb_pcie_probe, + .shutdown = amd_mdb_pcie_shutdown, }; builtin_platform_driver(amd_mdb_pcie_driver); diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 55cb957ae1f3..5cd227dda9a1 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -34,7 +34,6 @@ struct artpec6_pcie { struct regmap *regmap; /* DT axis,syscon-pcie */ void __iomem *phy_base; /* DT phy */ enum artpec_pcie_variants variant; - enum dw_pcie_device_mode mode; }; struct artpec_pcie_of_data { @@ -100,7 +99,7 @@ static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr) struct dw_pcie_rp *pp = &pci->pp; struct dw_pcie_ep *ep = &pci->ep; - switch (artpec6_pcie->mode) { + switch (artpec6_pcie->pci->mode) { case DW_PCIE_RC_TYPE: return cpu_addr - pp->cfg0_base; case DW_PCIE_EP_TYPE: @@ -413,7 +412,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) artpec6_pcie->pci = pci; artpec6_pcie->variant = variant; - artpec6_pcie->mode = mode; + artpec6_pcie->pci->mode = mode; artpec6_pcie->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy"); @@ -428,7 +427,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, artpec6_pcie); - switch (artpec6_pcie->mode) { + switch (artpec6_pcie->pci->mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST)) return -ENODEV; @@ -464,7 +463,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode); + dev_err(dev, "INVALID device type %d\n", artpec6_pcie->pci->mode); } return 0; diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index d0884253be97..be0b84665c9e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -265,8 +265,7 @@ static ssize_t lane_detect_write(struct file *file, const char __user *buf, return ret; val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG); - val &= ~(LANE_SELECT); - val |= FIELD_PREP(LANE_SELECT, lane); + FIELD_MODIFY(LANE_SELECT, &val, lane); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG, val); return count; @@ -306,6 +305,7 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, u32 val, counter, vc_num, err_group, type_mask; int val_diff = 0; char *kern_buf; + int ret; err_group = err_inj_list[pdata->idx].err_inj_group; type_mask = err_inj_type_mask[err_group]; @@ -327,10 +327,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, return -EINVAL; } } else { - val = kstrtou32(kern_buf, 0, &counter); - if (val) { + ret = kstrtou32(kern_buf, 0, &counter); + if (ret) { kfree(kern_buf); - return val; + return ret; } } @@ -339,14 +339,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf, val |= ((err_inj_list[pdata->idx].err_inj_type << EINJ_TYPE_SHIFT) & type_mask); val |= FIELD_PREP(EINJ_COUNT, counter); - if (err_group == 1 || err_group == 4) { - val &= ~(EINJ_VAL_DIFF); - val |= FIELD_PREP(EINJ_VAL_DIFF, val_diff); - } - if (err_group == 4) { - val &= ~(EINJ_VC_NUM); - val |= FIELD_PREP(EINJ_VC_NUM, vc_num); - } + if (err_group == 1 || err_group == 4) + FIELD_MODIFY(EINJ_VAL_DIFF, &val, val_diff); + if (err_group == 4) + FIELD_MODIFY(EINJ_VC_NUM, &val, vc_num); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group), val); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ_ENABLE_REG, (0x1 << err_group)); @@ -362,9 +358,8 @@ static void set_event_number(struct dwc_pcie_rasdes_priv *pdata, val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); val &= ~EVENT_COUNTER_ENABLE; - val &= ~(EVENT_COUNTER_GROUP_SELECT | EVENT_COUNTER_EVENT_SELECT); - val |= FIELD_PREP(EVENT_COUNTER_GROUP_SELECT, event_list[pdata->idx].group_no); - val |= FIELD_PREP(EVENT_COUNTER_EVENT_SELECT, event_list[pdata->idx].event_no); + FIELD_MODIFY(EVENT_COUNTER_GROUP_SELECT, &val, event_list[pdata->idx].group_no); + FIELD_MODIFY(EVENT_COUNTER_EVENT_SELECT, &val, event_list[pdata->idx].event_no); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); } @@ -469,8 +464,7 @@ static ssize_t counter_lane_write(struct file *file, const char __user *buf, mutex_lock(&rinfo->reg_event_lock); set_event_number(pdata, pci, rinfo); val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG); - val &= ~(EVENT_COUNTER_LANE_SELECT); - val |= FIELD_PREP(EVENT_COUNTER_LANE_SELECT, lane); + FIELD_MODIFY(EVENT_COUNTER_LANE_SELECT, &val, lane); dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val); mutex_unlock(&rinfo->reg_event_lock); @@ -507,11 +501,6 @@ static int ltssm_status_show(struct seq_file *s, void *v) return 0; } -static int ltssm_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, ltssm_status_show, inode->i_private); -} - #define dwc_debugfs_create(name) \ debugfs_create_file(#name, 0644, rasdes_debug, pci, \ &dbg_ ## name ## _fops) @@ -548,15 +537,15 @@ static const struct file_operations dwc_pcie_counter_value_ops = { .read = counter_value_read, }; -static const struct file_operations dwc_pcie_ltssm_status_ops = { - .open = ltssm_status_open, - .read = seq_read, -}; +DEFINE_SHOW_ATTRIBUTE(ltssm_status); static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci) { struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info; + if (!rinfo) + return; + mutex_destroy(&rinfo->reg_event_lock); } @@ -642,7 +631,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir) static void dwc_pcie_ltssm_debugfs_init(struct dw_pcie *pci, struct dentry *dir) { debugfs_create_file("ltssm_status", 0444, dir, pci, - &dwc_pcie_ltssm_status_ops); + <ssm_status_fops); } static int dw_pcie_ptm_check_capability(void *drvdata) diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index d4dc3b24da60..7d2794945704 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "pcie-designware.h" @@ -584,9 +585,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, config_atu: if (!(flags & PCI_BASE_ADDRESS_SPACE)) - type = PCIE_ATU_TYPE_MEM; + type = PCIE_TLP_TYPE_MEM_RDWR; else - type = PCIE_ATU_TYPE_IO; + type = PCIE_TLP_TYPE_IO_RDWR; if (epf_bar->num_submap) ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); @@ -659,7 +660,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct dw_pcie_ob_atu_cfg atu = { 0 }; atu.func_no = func_no; - atu.type = PCIE_ATU_TYPE_MEM; + atu.type = PCIE_TLP_TYPE_MEM_RDWR; atu.parent_bus_addr = addr - pci->parent_bus_offset; atu.pci_addr = pci_addr; atu.size = size; @@ -707,8 +708,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, reg = ep_func->msi_cap + PCI_MSI_FLAGS; val = dw_pcie_ep_readw_dbi(ep, func_no, reg); - val &= ~PCI_MSI_FLAGS_QMASK; - val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, mmc); + FIELD_MODIFY(PCI_MSI_FLAGS_QMASK, &val, mmc); dw_pcie_dbi_ro_wr_en(pci); dw_pcie_ep_writew_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); @@ -817,6 +817,122 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) return ep->ops->get_features(ep); } +static const struct pci_epc_bar_rsvd_region * +dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep, + enum pci_epc_bar_rsvd_region_type type, + enum pci_barno *bar, + resource_size_t *bar_offset) +{ + const struct pci_epc_features *features; + const struct pci_epc_bar_desc *bar_desc; + const struct pci_epc_bar_rsvd_region *r; + int i, j; + + if (!ep->ops->get_features) + return NULL; + + features = ep->ops->get_features(ep); + if (!features) + return NULL; + + for (i = BAR_0; i <= BAR_5; i++) { + bar_desc = &features->bar[i]; + + if (!bar_desc->nr_rsvd_regions || !bar_desc->rsvd_regions) + continue; + + for (j = 0; j < bar_desc->nr_rsvd_regions; j++) { + r = &bar_desc->rsvd_regions[j]; + + if (r->type != type) + continue; + + if (bar) + *bar = i; + if (bar_offset) + *bar_offset = r->offset; + return r; + } + } + + return NULL; +} + +static int +dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_edma_chip *edma = &pci->edma; + + if (!pci->edma_reg_size) + return 0; + + if (edma->db_offset == ~0) + return 0; + + return 1; +} + +static int +dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources) +{ + struct dw_pcie_ep *ep = epc_get_drvdata(epc); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + const struct pci_epc_bar_rsvd_region *rsvd; + struct dw_edma_chip *edma = &pci->edma; + enum pci_barno dma_ctrl_bar = NO_BAR; + resource_size_t db_offset = edma->db_offset; + resource_size_t dma_ctrl_bar_offset = 0; + resource_size_t dma_reg_size; + int count; + + count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no); + if (count < 0) + return count; + + if (num_resources < count) + return -ENOSPC; + + if (!count) + return 0; + + dma_reg_size = pci->edma_reg_size; + + rsvd = dw_pcie_ep_find_bar_rsvd_region(ep, + PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO, + &dma_ctrl_bar, + &dma_ctrl_bar_offset); + if (rsvd && rsvd->size < dma_reg_size) + dma_reg_size = rsvd->size; + + /* + * For interrupt-emulation doorbells, report a standalone resource + * instead of bundling it into the DMA controller MMIO resource. + */ + if (range_end_overflows_t(resource_size_t, db_offset, + sizeof(u32), dma_reg_size)) + return -EINVAL; + + resources[0] = (struct pci_epc_aux_resource) { + .type = PCI_EPC_AUX_DOORBELL_MMIO, + .phys_addr = pci->edma_reg_phys + db_offset, + .size = sizeof(u32), + .bar = dma_ctrl_bar, + .bar_offset = dma_ctrl_bar != NO_BAR ? + dma_ctrl_bar_offset + db_offset : 0, + .u.db_mmio = { + .irq = edma->db_irq, + .data = 0, /* write 0 to assert */ + }, + }; + + return 0; +} + static const struct pci_epc_ops epc_ops = { .write_header = dw_pcie_ep_write_header, .set_bar = dw_pcie_ep_set_bar, @@ -832,6 +948,8 @@ static const struct pci_epc_ops epc_ops = { .start = dw_pcie_ep_start, .stop = dw_pcie_ep_stop, .get_features = dw_pcie_ep_get_features, + .get_aux_resources_count = dw_pcie_ep_get_aux_resources_count, + .get_aux_resources = dw_pcie_ep_get_aux_resources, }; /** diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index c9517a348836..06722259d2e3 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -16,9 +16,11 @@ #include #include #include +#include #include #include +#include "../pci-host-common.h" #include "../../pci.h" #include "pcie-designware.h" @@ -434,7 +436,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) * remaining buses need type 1 iATU configuration. */ atu.index = 0; - atu.type = PCIE_ATU_TYPE_CFG0; + atu.type = PCIE_TLP_TYPE_CFG0_RDWR; atu.parent_bus_addr = pp->cfg0_base + SZ_1M; /* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */ atu.size = SZ_1M; @@ -450,7 +452,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) /* Configure remaining buses in type 1 iATU configuration */ atu.index = 1; - atu.type = PCIE_ATU_TYPE_CFG1; + atu.type = PCIE_TLP_TYPE_CFG1_RDWR; atu.parent_bus_addr = pp->cfg0_base + SZ_2M; atu.size = (SZ_1M * bus_range_max) - SZ_2M; atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; @@ -703,8 +705,10 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) dwc_pcie_debugfs_deinit(pci); + pci_lock_rescan_remove(); pci_stop_root_bus(pp->bridge->bus); pci_remove_root_bus(pp->bridge->bus); + pci_unlock_rescan_remove(); dw_pcie_stop_link(pci); @@ -745,9 +749,9 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, PCIE_ATU_FUNC(PCI_FUNC(devfn)); if (pci_is_root_bus(bus->parent)) - type = PCIE_ATU_TYPE_CFG0; + type = PCIE_TLP_TYPE_CFG0_RDWR; else - type = PCIE_ATU_TYPE_CFG1; + type = PCIE_TLP_TYPE_CFG1_RDWR; atu.type = type; atu.parent_bus_addr = pp->cfg0_base - pci->parent_bus_offset; @@ -774,7 +778,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -800,7 +804,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, return ret; if (pp->cfg0_io_shared) { - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -908,7 +912,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (resource_type(entry->res) != IORESOURCE_MEM) continue; - atu.type = PCIE_ATU_TYPE_MEM; + atu.type = PCIE_TLP_TYPE_MEM_RDWR; atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; @@ -951,7 +955,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) if (pp->io_size) { if (ob_iatu_index < pci->num_ob_windows) { atu.index = ob_iatu_index; - atu.type = PCIE_ATU_TYPE_IO; + atu.type = PCIE_TLP_TYPE_IO_RDWR; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; atu.size = pp->io_size; @@ -1013,7 +1017,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) window_size = MIN(pci->region_limit + 1, res_size); ret = dw_pcie_prog_inbound_atu(pci, ib_iatu_index, - PCIE_ATU_TYPE_MEM, res_start, + PCIE_TLP_TYPE_MEM_RDWR, res_start, res_start - entry->offset, window_size); if (ret) { dev_err(pci->dev, "Failed to set DMA range %pr\n", @@ -1194,7 +1198,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) atu.code = PCIE_MSG_CODE_PME_TURN_OFF; atu.routing = PCIE_MSG_TYPE_R_BC; - atu.type = PCIE_ATU_TYPE_MSG; + atu.type = PCIE_TLP_TYPE_MSG; atu.size = resource_size(pci->pp.msg_res); atu.index = pci->pp.msg_atu_index; @@ -1218,18 +1222,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci) int dw_pcie_suspend_noirq(struct dw_pcie *pci) { - u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + bool pme_capable = false; int ret = 0; u32 val; if (!dw_pcie_link_up(pci)) goto stop_link; - /* - * If L1SS is supported, then do not put the link into L2 as some - * devices such as NVMe expect low resume latency. - */ - if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1) + if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable)) return 0; if (pci->pp.ops->pme_turn_off) { @@ -1273,6 +1273,15 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) udelay(1); stop_link: + /* + * TODO: "pme_capable" means some downstream device is wakeup- + * enabled and is capable of generating PME from D3cold, which + * requires auxiliary power. Instead of always skipping power off + * if PME is supported from D3cold, query the pwrctrl core and skip + * power off only if device supports PME from D3cold and Vaux is + * not supported. + */ + pci->pp.skip_pwrctrl_off = pme_capable; dw_pcie_stop_link(pci); if (pci->pp.ops->deinit) pci->pp.ops->deinit(&pci->pp); @@ -1290,8 +1299,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (!pci->suspended) return 0; - pci->suspended = false; - if (pci->pp.ops->init) { ret = pci->pp.ops->init(&pci->pp); if (ret) { @@ -1313,6 +1320,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (pci->pp.ops->post_init) pci->pp.ops->post_init(&pci->pp); + pci->suspended = false; + return 0; err_stop_link: diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index d103ab759c4e..14d94e761229 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -8,7 +8,6 @@ */ #include #include -#include #include #include #include @@ -22,7 +21,6 @@ struct dw_plat_pcie { struct dw_pcie *pci; - enum dw_pcie_device_mode mode; }; struct dw_plat_pcie_of_data { @@ -118,11 +116,11 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) pci->dev = dev; dw_plat_pcie->pci = pci; - dw_plat_pcie->mode = mode; + dw_plat_pcie->pci->mode = mode; platform_set_drvdata(pdev, dw_plat_pcie); - switch (dw_plat_pcie->mode) { + switch (dw_plat_pcie->pci->mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST)) return -ENODEV; @@ -148,7 +146,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode); + dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->pci->mode); ret = -EINVAL; break; } diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c11cf61b8319..ec4722ed9303 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -22,6 +22,7 @@ #include #include +#include "../pci-host-common.h" #include "../../pci.h" #include "pcie-designware.h" @@ -162,8 +163,12 @@ int dw_pcie_get_resources(struct dw_pcie *pci) pci->edma.reg_base = devm_ioremap_resource(pci->dev, res); if (IS_ERR(pci->edma.reg_base)) return PTR_ERR(pci->edma.reg_base); + pci->edma_reg_phys = res->start; + pci->edma_reg_size = resource_size(res); } else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) { pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET; + pci->edma_reg_phys = pci->atu_phys_addr + DEFAULT_DBI_DMA_OFFSET; + pci->edma_reg_size = pci->atu_size - DEFAULT_DBI_DMA_OFFSET; } } @@ -487,13 +492,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg static inline u32 dw_pcie_enable_ecrc(u32 val) { /* - * DWC versions 0x3530302a and 0x3536322a have a design issue where - * the 'TD' bit in the Control register-1 of the ATU outbound - * region acts like an override for the ECRC setting, i.e., the - * presence of TLP Digest (ECRC) in the outgoing TLPs is solely - * determined by this bit. This is contrary to the PCIe spec which - * says that the enablement of the ECRC is solely determined by the - * AER registers. + * DesignWare core versions prior to 5.10A have a design issue where the + * 'TD' bit in the Control register-1 of the ATU outbound region acts + * like an override for the ECRC setting, i.e., the presence of TLP + * Digest (ECRC) in the outgoing TLPs is solely determined by this + * bit. This is contrary to the PCIe spec which says that the + * enablement of the ECRC is solely determined by the AER + * registers. * * Because of this, even when the ECRC is enabled through AER * registers, the transactions going through ATU won't have TLP @@ -563,12 +568,12 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) && dw_pcie_ver_is_ge(pci, 460A)) val |= PCIE_ATU_INCREASE_REGION_SIZE; - if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A)) + if (!dw_pcie_ver_is_ge(pci, 510A)) val = dw_pcie_enable_ecrc(val); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); val = PCIE_ATU_ENABLE | atu->ctrl2; - if (atu->type == PCIE_ATU_TYPE_MSG) { + if (atu->type == PCIE_TLP_TYPE_MSG) { /* The data-less messages only for now */ val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code; } @@ -799,13 +804,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) return -ETIMEDOUT; } - /* - * As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link - * speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms - * after Link training completes before sending a Configuration Request. - */ - if (pci->max_link_speed > 2) - msleep(PCIE_RESET_CONFIG_WAIT_MS); + pci_host_common_link_train_delay(pci->max_link_speed); offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); @@ -938,8 +937,7 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes) cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP); - lnkcap &= ~PCI_EXP_LNKCAP_MLW; - lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes); + FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, num_lanes); dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap); } @@ -1249,6 +1247,34 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci) dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap); } +/* TODO: Need to handle multi Root Ports */ +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on) +{ + u8 scale, value; + u16 offset; + u32 val; + + if (!t_power_on) + return; + + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS); + if (!offset) + return; + + pcie_encode_t_power_on(t_power_on, &scale, &value); + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, offset + PCI_L1SS_CAP); + val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE); + FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, &val, scale); + FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val, value); + + dw_pcie_writel_dbi(pci, offset + PCI_L1SS_CAP, val); + + dw_pcie_dbi_ro_wr_dis(pci); +} + void dw_pcie_setup(struct dw_pcie *pci) { u32 val; diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 3e69ef60165b..de4b245b1758 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -35,6 +35,7 @@ #define DW_PCIE_VER_480A 0x3438302a #define DW_PCIE_VER_490A 0x3439302a #define DW_PCIE_VER_500A 0x3530302a +#define DW_PCIE_VER_510A 0x3531302a #define DW_PCIE_VER_520A 0x3532302a #define DW_PCIE_VER_540A 0x3534302a #define DW_PCIE_VER_562A 0x3536322a @@ -71,6 +72,8 @@ /* Synopsys-specific PCIe configuration registers */ #define PCIE_PORT_FORCE 0x708 +/* Bit[7:0] LINK_NUM: Link Number. Not used for endpoint */ +#define PORT_LINK_NUM_MASK GENMASK(7, 0) #define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23) #define PCIE_PORT_AFR 0x70C @@ -98,6 +101,28 @@ #define PCIE_PORT_LANE_SKEW 0x714 #define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0) +/* + * PCIE_TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number + * Register. + * + * This register holds the ack frequency, latency, replay, fast link + * scaling timers, and max function number values. + * + * Bit[30:29] FAST_LINK_SCALING_FACTOR: Fast Link Timer Scaling Factor. + * 0x0 (SF_1024): Scaling Factor is 1024 (1ms is 1us). + * When the LTSSM is in Config or L12 Entry State, 1ms + * timer is 2us, 2ms timer is 4us and 3ms timer is 6us. + * 0x1 (SF_256): Scaling Factor is 256 (1ms is 4us) + * 0x2 (SF_64): Scaling Factor is 64 (1ms is 16us) + * 0x3 (SF_16): Scaling Factor is 16 (1ms is 64us) + */ +#define PCIE_TIMER_CTRL_MAX_FUNC_NUM 0x718 +#define PORT_FLT_SF_MASK GENMASK(30, 29) +#define PORT_FLT_SF_VAL_1024 0x0 +#define PORT_FLT_SF_VAL_256 0x1 +#define PORT_FLT_SF_VAL_64 0x2 +#define PORT_FLT_SF_VAL_16 0x3 + #define PCIE_PORT_DEBUG0 0x728 #define PORT_LOGIC_LTSSM_STATE_MASK 0x3f #define PORT_LOGIC_LTSSM_STATE_L0 0x11 @@ -170,11 +195,6 @@ #define PCIE_ATU_VIEWPORT_SIZE 0x2C #define PCIE_ATU_REGION_CTRL1 0x000 #define PCIE_ATU_INCREASE_REGION_SIZE BIT(13) -#define PCIE_ATU_TYPE_MEM 0x0 -#define PCIE_ATU_TYPE_IO 0x2 -#define PCIE_ATU_TYPE_CFG0 0x4 -#define PCIE_ATU_TYPE_CFG1 0x5 -#define PCIE_ATU_TYPE_MSG 0x10 #define PCIE_ATU_TD BIT(8) #define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20) #define PCIE_ATU_REGION_CTRL2 0x004 @@ -450,6 +470,7 @@ struct dw_pcie_rp { bool ecam_enabled; bool native_ecam; bool skip_l23_ready; + bool skip_pwrctrl_off; }; struct dw_pcie_ep_ops { @@ -544,6 +565,8 @@ struct dw_pcie { int max_link_speed; u8 n_fts[2]; struct dw_edma_chip edma; + phys_addr_t edma_reg_phys; + resource_size_t edma_reg_size; bool l1ss_support; /* L1 PM Substates support */ struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS]; struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS]; @@ -605,6 +628,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, u8 bar, size_t size); void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index); void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci); +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on); void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); diff --git a/drivers/pci/controller/dwc/pcie-eswin.c b/drivers/pci/controller/dwc/pcie-eswin.c index 2845832b3824..ce8d64f8a395 100644 --- a/drivers/pci/controller/dwc/pcie-eswin.c +++ b/drivers/pci/controller/dwc/pcie-eswin.c @@ -211,8 +211,7 @@ static int eswin_pcie_host_init(struct dw_pcie_rp *pp) /* Configure Root Port type */ val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET); - val &= ~PCIEELBI_CTRL0_DEV_TYPE; - val |= FIELD_PREP(PCIEELBI_CTRL0_DEV_TYPE, PCI_EXP_TYPE_ROOT_PORT); + FIELD_MODIFY(PCIEELBI_CTRL0_DEV_TYPE, &val, PCI_EXP_TYPE_ROOT_PORT); writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET); list_for_each_entry(port, &pcie->ports, list) { diff --git a/drivers/pci/controller/dwc/pcie-fu740.c b/drivers/pci/controller/dwc/pcie-fu740.c index 66367252032b..d0a34f680397 100644 --- a/drivers/pci/controller/dwc/pcie-fu740.c +++ b/drivers/pci/controller/dwc/pcie-fu740.c @@ -13,7 +13,6 @@ #include #include -#include #include #include #include diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index c21906eced61..2674cd376f49 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -47,7 +47,6 @@ #define PCIE_APP_IRN_INTD BIT(16) #define PCIE_APP_IRN_MSG_LTR BIT(18) #define PCIE_APP_IRN_SYS_ERR_RC BIT(29) -#define PCIE_APP_INTX_OFST 12 #define PCIE_APP_IRN_INT \ (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ @@ -196,6 +195,13 @@ static void intel_pcie_device_rst_deassert(struct intel_pcie *pcie) gpiod_set_value_cansleep(pcie->reset_gpio, 0); } +static void intel_pcie_core_irq_enable(struct intel_pcie *pcie) +{ + pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); + pcie_app_wr(pcie, PCIE_APP_IRNCR, PCIE_APP_IRN_INT); + pcie_app_wr(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT); +} + static void intel_pcie_core_irq_disable(struct intel_pcie *pcie) { pcie_app_wr(pcie, PCIE_APP_IRNEN, 0); @@ -278,6 +284,16 @@ static void intel_pcie_turn_off(struct intel_pcie *pcie) pcie_rc_cfg_wr_mask(pcie, PCI_COMMAND, PCI_COMMAND_MEMORY, 0); } +static int intel_pcie_start_link(struct dw_pcie *pci) +{ + struct intel_pcie *pcie = dev_get_drvdata(pci->dev); + + intel_pcie_device_rst_deassert(pcie); + intel_pcie_ltssm_enable(pcie); + + return 0; +} + static int intel_pcie_host_setup(struct intel_pcie *pcie) { int ret; @@ -285,49 +301,33 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie) intel_pcie_core_rst_assert(pcie); intel_pcie_device_rst_assert(pcie); - - ret = phy_init(pcie->phy); - if (ret) - return ret; - intel_pcie_core_rst_deassert(pcie); + /* Controller clock must be provided earlier than PHY */ ret = clk_prepare_enable(pcie->core_clk); if (ret) { dev_err(pcie->pci.dev, "Core clock enable failed: %d\n", ret); goto clk_err; } - pci->atu_base = pci->dbi_base + 0xC0000; + ret = phy_init(pcie->phy); + if (ret) + goto phy_err; intel_pcie_ltssm_disable(pcie); intel_pcie_link_setup(pcie); intel_pcie_init_n_fts(pci); - ret = dw_pcie_setup_rc(&pci->pp); - if (ret) - goto app_init_err; - dw_pcie_upconfig_setup(pci); - intel_pcie_device_rst_deassert(pcie); - intel_pcie_ltssm_enable(pcie); - - ret = dw_pcie_wait_for_link(pci); - if (ret) - goto app_init_err; - - /* Enable integrated interrupts */ - pcie_app_wr_mask(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT, - PCIE_APP_IRN_INT); + intel_pcie_core_irq_enable(pcie); return 0; -app_init_err: +phy_err: clk_disable_unprepare(pcie->core_clk); clk_err: intel_pcie_core_rst_assert(pcie); - phy_exit(pcie->phy); return ret; } @@ -381,6 +381,7 @@ static int intel_pcie_rc_init(struct dw_pcie_rp *pp) } static const struct dw_pcie_ops intel_pcie_ops = { + .start_link = intel_pcie_start_link, }; static const struct dw_pcie_host_ops intel_pcie_dw_ops = { @@ -392,6 +393,7 @@ static int intel_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct intel_pcie *pcie; struct dw_pcie_rp *pp; + struct resource *res; struct dw_pcie *pci; int ret; @@ -416,6 +418,32 @@ static int intel_pcie_probe(struct platform_device *pdev) pci->ops = &intel_pcie_ops; pp->ops = &intel_pcie_dw_ops; + /* + * If the 'atu' region is not available in the devicetree, use the + * default offset from DBI region for backwards compatibility. The + * 'atu' region should always be specified in the devicetree, as + * this is a hardware-specific address that should not be defined + * in the driver. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu"); + if (!res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res); + if (IS_ERR(pci->dbi_base)) + return PTR_ERR(pci->dbi_base); + + pci->dbi_phys_addr = res->start; + pci->atu_base = devm_ioremap(dev, res->start + 0xC0000, SZ_4K); + if (!pci->atu_base) { + dev_err(dev, "failed to remap ATU space\n"); + return -ENOMEM; + } + + pci->atu_size = SZ_4K; + pci->atu_phys_addr = res->start + 0xC0000; + dev_warn(dev, "ATU region not specified in DT. Using default offset\n"); + } + ret = dw_pcie_host_init(pp); if (ret) { dev_err(dev, "Cannot initialize host\n"); diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 7cf2c312ecec..2459c4d66b88 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -58,7 +58,6 @@ struct keembay_pcie { struct dw_pcie pci; void __iomem *apb_base; - enum dw_pcie_device_mode mode; struct clk *clk_master; struct clk *clk_aux; @@ -117,7 +116,7 @@ static int keembay_pcie_start_link(struct dw_pcie *pci) u32 val; int ret; - if (pcie->mode == DW_PCIE_EP_TYPE) + if (pcie->pci.mode == DW_PCIE_EP_TYPE) return 0; keembay_pcie_ltssm_set(pcie, false); @@ -409,7 +408,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) pci->dev = dev; pci->ops = &keembay_pcie_ops; - pcie->mode = mode; + pcie->pci.mode = mode; pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb"); if (IS_ERR(pcie->apb_base)) @@ -417,7 +416,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); - switch (pcie->mode) { + switch (pcie->pci.mode) { case DW_PCIE_RC_TYPE: if (!IS_ENABLED(CONFIG_PCIE_KEEMBAY_HOST)) return -ENODEV; @@ -443,7 +442,7 @@ static int keembay_pcie_probe(struct platform_device *pdev) break; default: - dev_err(dev, "Invalid device type %d\n", pcie->mode); + dev_err(dev, "Invalid device type %d\n", pcie->pci.mode); return -ENODEV; } diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c index b3ec38099fa3..31e1169b8ab6 100644 --- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c @@ -139,8 +139,7 @@ static int s32g_init_pcie_controller(struct dw_pcie_rp *pp) /* Set RP mode */ val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1); - val &= ~DEVICE_TYPE_MASK; - val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT); + FIELD_MODIFY(DEVICE_TYPE_MASK, &val, PCI_EXP_TYPE_ROOT_PORT); /* Use default CRNS */ val &= ~SRIS_MODE; diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 5aa73c628737..0da73caf2011 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -30,20 +30,15 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci) reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; - reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; - reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, - speed - PCIE_SPEED_8_0GT); + FIELD_MODIFY(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, ®, + speed - PCIE_SPEED_8_0GT); dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); - reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | - GEN3_EQ_FMDC_N_EVALS | - GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA | - GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA); - reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | - FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5); + FIELD_MODIFY(GEN3_EQ_FMDC_T_MIN_PHASE23, ®, 0x1); + FIELD_MODIFY(GEN3_EQ_FMDC_N_EVALS, ®, 0xd); + FIELD_MODIFY(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, ®, 0x5); + FIELD_MODIFY(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, ®, 0x5); dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); @@ -61,14 +56,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci) u32 reg; reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_1_OFF); - reg &= ~(MARGINING_MAX_VOLTAGE_OFFSET | - MARGINING_NUM_VOLTAGE_STEPS | - MARGINING_MAX_TIMING_OFFSET | - MARGINING_NUM_TIMING_STEPS); - reg |= FIELD_PREP(MARGINING_MAX_VOLTAGE_OFFSET, 0x24) | - FIELD_PREP(MARGINING_NUM_VOLTAGE_STEPS, 0x78) | - FIELD_PREP(MARGINING_MAX_TIMING_OFFSET, 0x32) | - FIELD_PREP(MARGINING_NUM_TIMING_STEPS, 0x10); + FIELD_MODIFY(MARGINING_MAX_VOLTAGE_OFFSET, ®, 0x24); + FIELD_MODIFY(MARGINING_NUM_VOLTAGE_STEPS, ®, 0x78); + FIELD_MODIFY(MARGINING_MAX_TIMING_OFFSET, ®, 0x32); + FIELD_MODIFY(MARGINING_NUM_TIMING_STEPS, ®, 0x10); dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_1_OFF, reg); reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_2_OFF); @@ -76,13 +67,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci) MARGINING_SAMPLE_REPORTING_METHOD | MARGINING_IND_LEFT_RIGHT_TIMING | MARGINING_VOLTAGE_SUPPORTED; - reg &= ~(MARGINING_IND_UP_DOWN_VOLTAGE | - MARGINING_MAXLANES | - MARGINING_SAMPLE_RATE_TIMING | - MARGINING_SAMPLE_RATE_VOLTAGE); - reg |= FIELD_PREP(MARGINING_MAXLANES, pci->num_lanes) | - FIELD_PREP(MARGINING_SAMPLE_RATE_TIMING, 0x3f) | - FIELD_PREP(MARGINING_SAMPLE_RATE_VOLTAGE, 0x3f); + reg &= ~MARGINING_IND_UP_DOWN_VOLTAGE; + FIELD_MODIFY(MARGINING_MAXLANES, ®, pci->num_lanes); + FIELD_MODIFY(MARGINING_SAMPLE_RATE_TIMING, ®, 0x3f); + FIELD_MODIFY(MARGINING_SAMPLE_RATE_VOLTAGE, ®, 0x3f); dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_2_OFF, reg); } EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_lane_margining); diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 257c2bcb5f76..56184e6ca6e6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -494,15 +494,13 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) /* Set the L0s Exit Latency to 2us-4us = 0x6 */ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); - val &= ~PCI_EXP_LNKCAP_L0SEL; - val |= FIELD_PREP(PCI_EXP_LNKCAP_L0SEL, 0x6); + FIELD_MODIFY(PCI_EXP_LNKCAP_L0SEL, &val, 0x6); dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val); /* Set the L1 Exit Latency to be 32us-64 us = 0x6 */ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP); - val &= ~PCI_EXP_LNKCAP_L1EL; - val |= FIELD_PREP(PCI_EXP_LNKCAP_L1EL, 0x6); + FIELD_MODIFY(PCI_EXP_LNKCAP_L1EL, &val, 0x6); dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val); dw_pcie_dbi_ro_wr_dis(pci); diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index af6bf5cce65b..d8eb52857f69 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -71,6 +71,7 @@ /* ELBI registers */ #define ELBI_SYS_CTRL 0x04 +#define ELBI_SYS_STTS 0x08 /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 @@ -131,6 +132,7 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) +#define PARF_LTSSM_STATE_MASK GENMASK(5, 0) /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) @@ -144,6 +146,10 @@ /* ELBI_SYS_CTRL register fields */ #define ELBI_SYS_CTRL_LT_ENABLE BIT(0) +#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4) + +/* ELBI_SYS_STTS register fields */ +#define ELBI_SYS_STTS_LTSSM_STATE_MASK GENMASK(17, 12) /* AXI_MSTR_RESP_COMP_CTRL0 register fields */ #define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 @@ -245,6 +251,7 @@ struct qcom_pcie_ops { void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); + enum dw_pcie_ltssm (*get_ltssm)(struct qcom_pcie *pcie); }; /** @@ -269,6 +276,7 @@ struct qcom_pcie_perst { struct qcom_pcie_port { struct list_head list; struct phy *phy; + u32 l1ss_t_power_on; struct list_head perst; }; @@ -282,7 +290,7 @@ struct qcom_pcie { const struct qcom_pcie_cfg *cfg; struct dentry *debugfs; struct list_head ports; - bool suspended; + struct gpio_desc *reset; bool use_pm_opp; }; @@ -428,6 +436,15 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) writel(val, pci->elbi_base + ELBI_SYS_CTRL); } +static enum dw_pcie_ltssm qcom_pcie_2_1_0_get_ltssm(struct qcom_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + u32 val; + + val = readl(pci->elbi_base + ELBI_SYS_STTS); + return (enum dw_pcie_ltssm)FIELD_GET(ELBI_SYS_STTS_LTSSM_STATE_MASK, val); +} + 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; @@ -517,7 +534,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) u32 val; int ret; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -684,6 +701,12 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2; + u32 val; + + /* Force PHY to lowest power state*/ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies); @@ -716,7 +739,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) { u32 val; - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -780,6 +803,12 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; + u32 val; + + /* Force PHY to lowest power state*/ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); reset_control_bulk_assert(res->num_resets, res->resets); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -848,6 +877,12 @@ static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); } @@ -903,6 +938,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -998,7 +1034,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie) /* configure PCIe to RC mode */ writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE); - /* enable PCIe clocks and resets */ + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1069,6 +1105,12 @@ static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); @@ -1173,6 +1215,12 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie) static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0; + u32 val; + + /* Force PHY to lowest power state */ + val = readl(pcie->parf + PARF_PHY_CTRL); + val |= PHY_TEST_PWR_DOWN; + writel(val, pcie->parf + PARF_PHY_CTRL); clk_bulk_disable_unprepare(res->num_clks, res->clks); } @@ -1213,6 +1261,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) u32 val; int i; + /* Force PHY out of lowest power state */ val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; writel(val, pcie->parf + PARF_PHY_CTRL); @@ -1260,6 +1309,19 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci) return val & PCI_EXP_LNKSTA_DLLLA; } +static enum dw_pcie_ltssm qcom_pcie_get_ltssm(struct dw_pcie *pci) +{ + struct qcom_pcie *pcie = to_qcom_pcie(pci); + u32 val; + + if (pcie->cfg->ops->get_ltssm) + return pcie->cfg->ops->get_ltssm(pcie); + + val = readl(pcie->parf + PARF_LTSSM); + + return (enum dw_pcie_ltssm)FIELD_GET(PARF_LTSSM_STATE_MASK, val); +} + static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie) { struct qcom_pcie_port *port; @@ -1288,6 +1350,14 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie) return 0; } +static void qcom_pcie_configure_ports(struct qcom_pcie *pcie) +{ + struct qcom_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) + dw_pcie_program_t_power_on(pcie->pci, port->l1ss_t_power_on); +} + static int qcom_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -1304,13 +1374,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_deinit; - ret = pci_pwrctrl_create_devices(pci->dev); - if (ret) - goto err_disable_phy; + if (!pci->suspended) { + ret = pci_pwrctrl_create_devices(pci->dev); + if (ret) + goto err_disable_phy; + } - ret = pci_pwrctrl_power_on_devices(pci->dev); - if (ret) - goto err_pwrctrl_destroy; + if (!pp->skip_pwrctrl_off) { + ret = pci_pwrctrl_power_on_devices(pci->dev); + if (ret) + goto err_pwrctrl_destroy; + } if (pcie->cfg->ops->post_init) { ret = pcie->cfg->ops->post_init(pcie); @@ -1322,6 +1396,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX); dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC); + qcom_pcie_configure_ports(pcie); + qcom_pcie_perst_deassert(pcie); if (pcie->cfg->ops->config_sid) { @@ -1335,9 +1411,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) err_assert_reset: qcom_pcie_perst_assert(pcie); err_pwrctrl_power_off: - pci_pwrctrl_power_off_devices(pci->dev); + if (!pp->skip_pwrctrl_off) + pci_pwrctrl_power_off_devices(pci->dev); err_pwrctrl_destroy: - if (ret != -EPROBE_DEFER) + if (ret != -EPROBE_DEFER && !pci->suspended) pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); @@ -1354,11 +1431,14 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) qcom_pcie_perst_assert(pcie); - /* - * No need to destroy pwrctrl devices as this function only gets called - * during system suspend as of now. - */ - pci_pwrctrl_power_off_devices(pci->dev); + if (!pci->pp.skip_pwrctrl_off) { + /* + * No need to destroy pwrctrl devices as this function only + * gets called during system suspend as of now. + */ + pci_pwrctrl_power_off_devices(pci->dev); + } + qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -1368,14 +1448,35 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); + /* + * During system suspend, the Qcom RC driver may turn off the + * analog circuitry of PHY and remove controller votes to save + * power. If the link is in L1SS and the endpoint asserts CLKREQ# + * to exit L1SS, the time required to wake the system and restore + * the PHY/REFCLK may exceed the L1SS exit timing (L10_REFCLK_ON + + * T_COMMONMODE), resulting in Link Down (LDn) and a reset of the + * endpoint. Set this flag to indicate this limitation to client + * drivers so that they can avoid relying on device state being + * preserved during system suspend. + */ + pp->bridge->broken_l1ss_resume = true; + if (pcie->cfg->ops->host_post_init) pcie->cfg->ops->host_post_init(pcie); } +static void qcom_pcie_host_pme_turn_off(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + writel(ELBI_SYS_CTRL_PME_TURNOFF_MSG, pci->elbi_base + ELBI_SYS_CTRL); +} + static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { .init = qcom_pcie_host_init, .deinit = qcom_pcie_host_deinit, .post_init = qcom_pcie_host_post_init, + .pme_turn_off = qcom_pcie_host_pme_turn_off, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -1385,6 +1486,7 @@ static const struct qcom_pcie_ops ops_2_1_0 = { .post_init = qcom_pcie_post_init_2_1_0, .deinit = qcom_pcie_deinit_2_1_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */ @@ -1394,6 +1496,7 @@ static const struct qcom_pcie_ops ops_1_0_0 = { .post_init = qcom_pcie_post_init_1_0_0, .deinit = qcom_pcie_deinit_1_0_0, .ltssm_enable = qcom_pcie_2_1_0_ltssm_enable, + .get_ltssm = qcom_pcie_2_1_0_get_ltssm, }; /* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */ @@ -1473,6 +1576,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = { static const struct qcom_pcie_cfg cfg_1_34_0 = { .ops = &ops_1_9_0, .override_no_snoop = true, + .no_l0s = true, }; static const struct qcom_pcie_cfg cfg_2_1_0 = { @@ -1512,6 +1616,7 @@ static const struct qcom_pcie_cfg cfg_fw_managed = { static const struct dw_pcie_ops dw_pcie_ops = { .link_up = qcom_pcie_link_up, .start_link = qcom_pcie_start_link, + .get_ltssm = qcom_pcie_get_ltssm, }; static int qcom_pcie_icc_init(struct qcom_pcie *pcie) @@ -1613,6 +1718,22 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie) } } +static int qcom_pcie_set_max_opp(struct device *dev) +{ + unsigned long max_freq = ULONG_MAX; + struct dev_pm_opp *opp; + int ret; + + opp = dev_pm_opp_find_freq_floor(dev, &max_freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + ret = dev_pm_opp_set_opp(dev, opp); + dev_pm_opp_put(opp); + + return ret; +} + static int qcom_pcie_link_transition_count(struct seq_file *s, void *data) { struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private); @@ -1674,6 +1795,12 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) pci->dbi_base = cfg->win; pp->num_vectors = MSI_DEF_NUM_VECTORS; + /* + * dw_pcie_msi_host_init() is called directly here, bypassing + * dw_pcie_host_init() where pp->lock is normally initialized. + */ + raw_spin_lock_init(&pp->lock); + ret = dw_pcie_msi_host_init(pp); if (ret) return ret; @@ -1703,6 +1830,13 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, struct gpio_desc *reset; int ret; + if (pcie->reset) { + dev_warn_once(dev, + "Reusing PERST# from Root Complex node. DT needs to be fixed!\n"); + reset = pcie->reset; + goto skip_perst_parsing; + } + if (!of_find_property(np, "reset-gpios", NULL)) goto parse_child_node; @@ -1721,6 +1855,7 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, return PTR_ERR(reset); } +skip_perst_parsing: perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); if (!perst) return -ENOMEM; @@ -1764,6 +1899,9 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node if (ret) return ret; + /* TODO: Move to DWC core after multi Root Port support is added */ + of_property_read_u32(node, "t-power-on-us", &port->l1ss_t_power_on); + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); @@ -1778,6 +1916,13 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) struct device *dev = pcie->pci->dev; int ret = -ENODEV; + if (of_find_property(dev->of_node, "perst-gpios", NULL)) { + pcie->reset = devm_gpiod_get_optional(dev, "perst", + GPIOD_OUT_HIGH); + if (IS_ERR(pcie->reset)) + return PTR_ERR(pcie->reset); + } + for_each_available_child_of_node_scoped(dev->of_node, of_port) { if (!of_node_is_type(of_port, "pci")) continue; @@ -1804,7 +1949,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) struct device *dev = pcie->pci->dev; struct qcom_pcie_perst *perst; struct qcom_pcie_port *port; - struct gpio_desc *reset; struct phy *phy; int ret; @@ -1812,10 +1956,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) if (IS_ERR(phy)) return PTR_ERR(phy); - reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); - if (IS_ERR(reset)) - return PTR_ERR(reset); - ret = phy_init(phy); if (ret) return ret; @@ -1832,7 +1972,7 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); - perst->desc = reset; + perst->desc = pcie->reset; INIT_LIST_HEAD(&port->perst); INIT_LIST_HEAD(&perst->list); list_add_tail(&perst->list, &port->perst); @@ -1845,9 +1985,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) struct qcom_pcie_perst *perst, *tmp_perst; struct qcom_pcie_port *port, *tmp_port; const struct qcom_pcie_cfg *pcie_cfg; - unsigned long max_freq = ULONG_MAX; struct device *dev = &pdev->dev; - struct dev_pm_opp *opp; struct qcom_pcie *pcie; struct dw_pcie_rp *pp; struct resource *res; @@ -1951,21 +2089,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) * probe(), OPP will be updated using qcom_pcie_icc_opp_update(). */ if (!ret) { - opp = dev_pm_opp_find_freq_floor(dev, &max_freq); - if (IS_ERR(opp)) { - ret = PTR_ERR(opp); - dev_err_probe(pci->dev, ret, - "Unable to find max freq OPP\n"); - goto err_pm_runtime_put; - } else { - ret = dev_pm_opp_set_opp(dev, opp); - } - - dev_pm_opp_put(opp); + ret = qcom_pcie_set_max_opp(dev); if (ret) { - dev_err_probe(pci->dev, ret, - "Failed to set OPP for freq %lu\n", - max_freq); + dev_err_probe(dev, ret, "Failed to set max OPP\n"); goto err_pm_runtime_put; } @@ -2039,53 +2165,51 @@ static int qcom_pcie_suspend_noirq(struct device *dev) if (!pcie) return 0; - /* - * Set minimum bandwidth required to keep data path functional during - * suspend. - */ - if (pcie->icc_mem) { - ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); - if (ret) { - dev_err(dev, - "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", - ret); - return ret; - } - } + ret = dw_pcie_suspend_noirq(pcie->pci); + if (ret) + return ret; - /* - * Turn OFF the resources only for controllers without active PCIe - * devices. For controllers with active devices, the resources are kept - * ON and the link is expected to be in L0/L1 (sub)states. - * - * Turning OFF the resources for controllers with active PCIe devices - * will trigger access violation during the end of the suspend cycle, - * as kernel tries to access the PCIe devices config space for masking - * MSIs. - * - * Also, it is not desirable to put the link into L2/L3 state as that - * implies VDD supply will be removed and the devices may go into - * powerdown state. This will affect the lifetime of the storage devices - * like NVMe. - */ - if (!dw_pcie_link_up(pcie->pci)) { - qcom_pcie_host_deinit(&pcie->pci->pp); - pcie->suspended = true; - } + if (pcie->pci->suspended) { + ret = icc_disable(pcie->icc_mem); + if (ret) + dev_err(dev, "Failed to disable PCIe-MEM interconnect path: %d\n", ret); - /* - * Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM. - * Because on some platforms, DBI access can happen very late during the - * S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC - * error. - */ - if (pm_suspend_target_state != PM_SUSPEND_MEM) { ret = icc_disable(pcie->icc_cpu); if (ret) dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret); if (pcie->use_pm_opp) dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } else { + /* + * Set minimum bandwidth required to keep data path + * functional during suspend. + */ + if (pcie->icc_mem) { + ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1)); + if (ret) { + dev_err(dev, + "Failed to set bandwidth for PCIe-MEM interconnect path: %d\n", + ret); + return ret; + } + } + + /* + * Only disable CPU-PCIe interconnect path if the suspend + * is non-S2RAM. On some platforms, DBI access can happen + * very late during S2RAM and a non-active CPU-PCIe + * interconnect path may lead to NoC error. + */ + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + ret = icc_disable(pcie->icc_cpu); + if (ret) + dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", + ret); + + if (pcie->use_pm_opp) + dev_pm_opp_set_opp(pcie->pci->dev, NULL); + } } return ret; } @@ -2099,25 +2223,62 @@ static int qcom_pcie_resume_noirq(struct device *dev) if (!pcie) return 0; - if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->pci->suspended) { + if (pcie->use_pm_opp) { + ret = qcom_pcie_set_max_opp(dev); + if (ret) { + dev_err(dev, "Failed to set max OPP: %d\n", ret); + return ret; + } + } + ret = icc_enable(pcie->icc_cpu); if (ret) { dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret); return ret; } - } - if (pcie->suspended) { - ret = qcom_pcie_host_init(&pcie->pci->pp); - if (ret) - return ret; + ret = icc_enable(pcie->icc_mem); + if (ret) { + dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret); + goto disable_icc_cpu; + } - pcie->suspended = false; + /* + * Ignore -ENODEV & -EIO here since it is expected when no + * endpoint is connected to the PCIe link. + */ + ret = dw_pcie_resume_noirq(pcie->pci); + if (ret && ret != -ENODEV && ret != -EIO) + goto disable_icc_mem; + } else { + if (pm_suspend_target_state != PM_SUSPEND_MEM) { + if (pcie->use_pm_opp) { + ret = qcom_pcie_set_max_opp(dev); + if (ret) { + dev_err(dev, "Failed to set max OPP: %d\n", ret); + return ret; + } + } + + ret = icc_enable(pcie->icc_cpu); + if (ret) { + dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", + ret); + return ret; + } + } } qcom_pcie_icc_opp_update(pcie); return 0; +disable_icc_mem: + icc_disable(pcie->icc_mem); +disable_icc_cpu: + icc_disable(pcie->icc_cpu); + + return ret; } static const struct of_device_id qcom_pcie_match[] = { diff --git a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c index 55f61914a986..2d737b49ea8f 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194-acpi.c +++ b/drivers/pci/controller/dwc/pcie-tegra194-acpi.c @@ -86,11 +86,11 @@ static void __iomem *tegra194_map_bus(struct pci_bus *bus, if (bus->parent->number == cfg->busr.start) { if (PCI_SLOT(devfn) == 0) - type = PCIE_ATU_TYPE_CFG0; + type = PCIE_TLP_TYPE_CFG0_RDWR; else return NULL; } else { - type = PCIE_ATU_TYPE_CFG1; + type = PCIE_TLP_TYPE_CFG1_RDWR; } program_outbound_atu(pcie_ecam, 0, type, cfg->res.start, busdev, diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 9dcfa194050e..795cef5a915d 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -272,6 +272,7 @@ struct tegra_pcie_dw { u32 aspm_cmrt; u32 aspm_pwr_on_t; u32 aspm_l0s_enter_lat; + u32 aspm_l1_enter_lat; struct regulator *pex_ctl_supply; struct regulator *slot_ctl_3v3; @@ -715,6 +716,8 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie) val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR); val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK; val |= (pcie->aspm_l0s_enter_lat << PORT_AFR_L0S_ENTRANCE_LAT_SHIFT); + val &= ~PORT_AFR_L1_ENTRANCE_LAT_MASK; + val |= (pcie->aspm_l1_enter_lat << PORT_AFR_L1_ENTRANCE_LAT_SHIFT); val |= PORT_AFR_ENTER_ASPM; dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val); } @@ -872,8 +875,7 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); - val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC; - val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, 0x3ff); + FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, 0x3ff); val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE; dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); @@ -883,9 +885,8 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); - val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC; - val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, - pcie->of_data->gen4_preset_vec); + FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, + pcie->of_data->gen4_preset_vec); val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE; dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val); @@ -1115,6 +1116,7 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) { struct platform_device *pdev = to_platform_device(pcie->dev); struct device_node *np = pcie->dev->of_node; + u32 val; int ret; pcie->dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); @@ -1141,6 +1143,15 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie) dev_info(pcie->dev, "Failed to read ASPM L0s Entrance latency: %d\n", ret); + /* Default to max latency of 7. */ + pcie->aspm_l1_enter_lat = 7; + ret = of_property_read_u32(np, "aspm-l1-entry-delay-ns", &val); + if (!ret) { + u32 us = DIV_ROUND_UP(val, 1000); + + pcie->aspm_l1_enter_lat = min_t(u32, order_base_2(us), 7); + } + ret = of_property_read_u32(np, "num-lanes", &pcie->num_lanes); if (ret < 0) { dev_err(pcie->dev, "Failed to read num-lanes: %d\n", ret); diff --git a/drivers/pci/controller/dwc/pcie-ultrarisc.c b/drivers/pci/controller/dwc/pcie-ultrarisc.c new file mode 100644 index 000000000000..6ee661ceff67 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-ultrarisc.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DWC PCIe RC driver for UltraRISC SoCs + * + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define PCIE_CUS_CORE 0x400000 + +#define LTSSM_ENABLE BIT(7) +#define FAST_LINK_MODE BIT(12) +#define HOLD_PHY_RST BIT(14) +#define L1SUB_DISABLE BIT(15) + +#define ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS 0x6 + +static struct pci_ops ultrarisc_pci_ops = { + .map_bus = dw_pcie_own_conf_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct pci_host_bridge *bridge = pp->bridge; + u8 cap_exp; + u32 val; + + bridge->ops = &ultrarisc_pci_ops; + + if (dw_pcie_link_up(pci)) + return 0; + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val &= ~FAST_LINK_MODE; + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + val = dw_pcie_readl_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM); + FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64); + dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val); + + cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2); + FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT); + dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val); + + val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE); + FIELD_MODIFY(PORT_LINK_NUM_MASK, &val, 0); + dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val); + + val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_DEVCTL2); + FIELD_MODIFY(PCI_EXP_DEVCTL2_COMP_TIMEOUT, &val, + ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS); + dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_DEVCTL2, val); + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val &= ~(HOLD_PHY_RST | L1SUB_DISABLE); + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + return 0; +} + +static void ultrarisc_pcie_pme_turn_off(struct dw_pcie_rp *pp) +{ + /* + * DP1000 does not support sending PME_Turn_Off from the RC. + * Keep this callback empty to skip the generic MSG TLP path. + */ +} + +static const struct dw_pcie_host_ops ultrarisc_pcie_host_ops = { + .init = ultrarisc_pcie_host_init, + .pme_turn_off = ultrarisc_pcie_pme_turn_off, +}; + +static int ultrarisc_pcie_start_link(struct dw_pcie *pci) +{ + u32 val; + + val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE); + val |= LTSSM_ENABLE; + dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val); + + return 0; +} + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = ultrarisc_pcie_start_link, +}; + +static int ultrarisc_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_pcie_rp *pp; + struct dw_pcie *pci; + int ret; + + pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); + if (!pci) + return -ENOMEM; + + pci->dev = dev; + pci->ops = &dw_pcie_ops; + + /* Set a default value suitable for at most 16 in and 16 out windows */ + pci->atu_size = SZ_8K; + + pp = &pci->pp; + + platform_set_drvdata(pdev, pci); + + pp->num_vectors = MAX_MSI_IRQS; + /* No L2/L3 Ready indication is available on this platform */ + pp->skip_l23_ready = true; + pp->ops = &ultrarisc_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(dev, "Failed to initialize host\n"); + return ret; + } + + return 0; +} + +static int ultrarisc_pcie_suspend_noirq(struct device *dev) +{ + struct dw_pcie *pci = dev_get_drvdata(dev); + + return dw_pcie_suspend_noirq(pci); +} + +static int ultrarisc_pcie_resume_noirq(struct device *dev) +{ + struct dw_pcie *pci = dev_get_drvdata(dev); + + return dw_pcie_resume_noirq(pci); +} + +static const struct dev_pm_ops ultrarisc_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(ultrarisc_pcie_suspend_noirq, + ultrarisc_pcie_resume_noirq) +}; + +static const struct of_device_id ultrarisc_pcie_of_match[] = { + { + .compatible = "ultrarisc,dp1000-pcie", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ultrarisc_pcie_of_match); + +static struct platform_driver ultrarisc_pcie_driver = { + .driver = { + .name = "ultrarisc-pcie", + .of_match_table = ultrarisc_pcie_of_match, + .suppress_bind_attrs = true, + .pm = &ultrarisc_pcie_pm_ops, + }, + .probe = ultrarisc_pcie_probe, +}; +builtin_platform_driver(ultrarisc_pcie_driver); + +MODULE_DESCRIPTION("UltraRISC DP1000 DWC PCIe host controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/dwc/pcie-visconti.c b/drivers/pci/controller/dwc/pcie-visconti.c index cdeac6177143..f21775c3b3a1 100644 --- a/drivers/pci/controller/dwc/pcie-visconti.c +++ b/drivers/pci/controller/dwc/pcie-visconti.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index e34bea1ff0ac..fd9c7d53e8a7 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -26,6 +26,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" #include "../pci-bridge-emul.h" @@ -350,8 +351,10 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie) /* check if the link is up or not */ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (advk_pcie_link_up(pcie)) + if (advk_pcie_link_up(pcie)) { + pci_host_common_link_train_delay(pcie->link_gen); return 0; + } usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index d6258c1cffe5..2ce6f4b66133 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,178 @@ #include "pci-host-common.h" +/** + * pci_host_common_delete_ports - Cleanup function for port list + * @data: Pointer to the port list head + */ +void pci_host_common_delete_ports(void *data) +{ + struct list_head *ports = data; + struct pci_host_perst *perst, *tmp_perst; + struct pci_host_port *port, *tmp_port; + + list_for_each_entry_safe(port, tmp_port, ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); + list_del(&port->list); + } +} +EXPORT_SYMBOL_GPL(pci_host_common_delete_ports); + +/** + * pci_host_common_parse_perst - Parse PERST# from all nodes, depth first + * @dev: Device pointer + * @port: PCI host port + * @np: Device tree node to start parsing from + * + * Recursively parse PERST# GPIO from all PCIe bridge nodes starting from + * @np in a depth-first manner. + * + * Return: 0 on success, negative error code on failure. + */ +static int pci_host_common_parse_perst(struct device *dev, + struct pci_host_port *port, + struct device_node *np) +{ + struct pci_host_perst *perst; + struct gpio_desc *reset; + int ret; + + if (!of_property_present(np, "reset-gpios")) + goto parse_child_node; + + reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset", + GPIOD_ASIS, "PERST#"); + if (IS_ERR(reset)) { + /* + * FIXME: GPIOLIB currently supports exclusive GPIO access only. + * Non exclusive access is broken. But shared PERST# requires + * non-exclusive access. So once GPIOLIB properly supports it, + * implement it here. + */ + if (PTR_ERR(reset) == -EBUSY) + dev_err(dev, "Shared PERST# is not supported\n"); + + return PTR_ERR(reset); + } + + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + + INIT_LIST_HEAD(&perst->list); + perst->desc = reset; + list_add_tail(&perst->list, &port->perst); + +parse_child_node: + for_each_available_child_of_node_scoped(np, child) { + ret = pci_host_common_parse_perst(dev, port, child); + if (ret) + return ret; + } + + return 0; +} + +/** + * pci_host_common_parse_port - Parse a single Root Port node + * @dev: Device pointer + * @bridge: PCI host bridge + * @node: Device tree node of the Root Port + * + * Parse Root Port properties from the device tree. Currently it only + * handles the PERST# GPIO (including PERST# GPIOs from all PCIe bridge + * nodes under this Root Port), which is optional. + * + * NOTE: This helper fetches resources (like PERST# GPIO) optionally. If a + * controller driver has a hard dependency on certain resources (PHY, + * clocks, regulators, etc.), those resources MUST be modeled correctly in + * the DT binding and validated in DTS. This helper cannot enforce such + * dependencies and the driver may fail to operate if required resources + * are missing. + * + * Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding + * should be used), Other negative error codes on failure. + */ +static int pci_host_common_parse_port(struct device *dev, + struct pci_host_bridge *bridge, + struct device_node *node) +{ + struct pci_host_port *port; + int ret; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + INIT_LIST_HEAD(&port->perst); + + ret = pci_host_common_parse_perst(dev, port, node); + if (ret) + return ret; + + /* + * 1. PERST# found in RP or its child nodes - list is not empty, + * continue + * + * 2. PERST# not found in RP/children, but found in RC node - + * return -ENODEV to fallback legacy binding + * + * 3. PERST# not found anywhere - list is empty, continue (optional + * PERST#) + */ + if (list_empty(&port->perst)) { + if (of_property_present(dev->of_node, "reset-gpios") || + of_property_present(dev->of_node, "reset-gpio")) + return -ENODEV; + } + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &bridge->ports); + + return 0; +} + +/** + * pci_host_common_parse_ports - Parse Root Port nodes from device tree + * @dev: Device pointer + * @bridge: PCI host bridge + * + * Iterate through child nodes of the host bridge and parse Root Port + * properties (currently only reset GPIOs). + * + * Return: 0 on success, -ENODEV if no ports found or PERST# found in RC + * node (legacy binding should be used), Other negative error codes on + * failure. + */ +int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge) +{ + int ret = -ENODEV; + + for_each_available_child_of_node_scoped(dev->of_node, of_port) { + if (!of_node_is_type(of_port, "pci")) + continue; + ret = pci_host_common_parse_port(dev, bridge, of_port); + if (ret) + goto err_cleanup; + } + + if (ret) + return ret; + + return devm_add_action_or_reset(dev, pci_host_common_delete_ports, + &bridge->ports); + +err_cleanup: + pci_host_common_delete_ports(&bridge->ports); + return ret; +} +EXPORT_SYMBOL_GPL(pci_host_common_parse_ports); + +#define PCI_HOST_D3COLD_ALLOWED BIT(0) +#define PCI_HOST_PME_D3COLD_CAPABLE BIT(1) + + static void gen_pci_unmap_cfg(void *ptr) { pci_ecam_free((struct pci_config_window *)ptr); @@ -68,6 +241,10 @@ int pci_host_common_init(struct platform_device *pdev, if (IS_ERR(cfg)) return PTR_ERR(cfg); + /* Do not reassign bus numbers if probe only */ + if (!pci_has_flag(PCI_PROBE_ONLY)) + pci_add_flags(PCI_REASSIGN_ALL_BUS); + bridge->sysdata = cfg; bridge->ops = (struct pci_ops *)&ops->pci_ops; bridge->enable_device = ops->enable_device; @@ -106,5 +283,64 @@ void pci_host_common_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(pci_host_common_remove); +static int __pci_host_common_d3cold_possible(struct pci_dev *pdev, + void *userdata) +{ + u32 *flags = userdata; + + if (!pdev->dev.driver && !pci_is_enabled(pdev)) + return 0; + + if (pdev->current_state != PCI_D3hot) + goto exit; + + if (device_may_wakeup(&pdev->dev)) { + if (!pci_pme_capable(pdev, PCI_D3cold)) + goto exit; + else + *flags |= PCI_HOST_PME_D3COLD_CAPABLE; + } + + return 0; + +exit: + *flags &= ~PCI_HOST_D3COLD_ALLOWED; + + return -EOPNOTSUPP; +} + +/** + * pci_host_common_d3cold_possible - Determine whether the host bridge can + * transition the devices into D3cold. + * + * @bridge: PCI host bridge to check + * @pme_capable: Pointer to update if there is any device capable of generating + * PME from D3cold. + * + * Walk downstream PCIe endpoint devices and determine whether the host bridge + * is permitted to transition the devices into D3cold. + * + * Devices under host bridge can enter D3cold only if all active PCIe + * endpoints are in PCI_D3hot and any wakeup-enabled endpoint is capable of + * generating PME from D3cold. Inactive endpoints are ignored. + * + * The @pme_capable output allows PCIe controller drivers to apply + * platform-specific handling to preserve wakeup functionality. + * + * Return: %true if the host bridge may enter D3cold, otherwise %false. + */ +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, + bool *pme_capable) +{ + u32 flags = PCI_HOST_D3COLD_ALLOWED; + + pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags); + + *pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE); + + return !!(flags & PCI_HOST_D3COLD_ALLOWED); +} +EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible); + MODULE_DESCRIPTION("Common library for PCI host controller drivers"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h index b5075d4bd7eb..9f0f36a32221 100644 --- a/drivers/pci/controller/pci-host-common.h +++ b/drivers/pci/controller/pci-host-common.h @@ -10,8 +10,39 @@ #ifndef _PCI_HOST_COMMON_H #define _PCI_HOST_COMMON_H +#include +#include "../pci.h" + struct pci_ecam_ops; +/** + * struct pci_host_perst - PERST# GPIO descriptor + * @list: List node for linking multiple PERST# GPIOs + * @desc: GPIO descriptor for PERST# signal + * + * This structure holds a single PERST# GPIO descriptor. + */ +struct pci_host_perst { + struct list_head list; + struct gpio_desc *desc; +}; + +/** + * struct pci_host_port - Generic Root Port properties + * @list: List node for linking multiple ports + * @perst: List of PERST# GPIO descriptors for this port and its children + * + * This structure contains common properties that can be parsed from + * Root Port device tree nodes. + */ +struct pci_host_port { + struct list_head list; + struct list_head perst; +}; + +void pci_host_common_delete_ports(void *data); +int pci_host_common_parse_ports(struct device *dev, + struct pci_host_bridge *bridge); int pci_host_common_probe(struct platform_device *pdev); int pci_host_common_init(struct platform_device *pdev, struct pci_host_bridge *bridge, @@ -20,4 +51,21 @@ void pci_host_common_remove(struct platform_device *pdev); struct pci_config_window *pci_host_common_ecam_create(struct device *dev, struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops); + +bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge, + bool *pme_capable); + +/** + * pci_host_common_link_train_delay - Wait 100 ms if link speed > 5 GT/s + * @max_link_speed: the maximum link speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, ...) + * + * Must be called after Link training completes and before the first + * Configuration Request is sent. + */ +static inline void pci_host_common_link_train_delay(int max_link_speed) +{ + if (max_link_speed > 2) + msleep(PCIE_RESET_CONFIG_WAIT_MS); +} + #endif diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index bc630ab8a283..d0c643996476 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); +static const struct pci_device_id loongson_internal_bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, + { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, + { 0, }, +}; + /* * Some Loongson PCIe ports have hardware limitations on their Maximum Read * Request Size. They can't handle anything larger than this. Sane @@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev) { struct pci_bus *bus = pdev->bus; struct pci_dev *bridge; - static const struct pci_device_id bridge_devids[] = { - { PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) }, - { PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) }, - { 0, }, - }; /* look for the matching bridge */ while (!pci_is_root_bus(bus)) { bridge = bus->self; bus = bus->parent; - if (pci_match_id(bridge_devids, bridge)) { + if (pci_match_id(loongson_internal_bridge_devids, bridge)) { if (pcie_get_readrq(pdev) > 256) { pci_info(pdev, "limiting MRRS to 256\n"); pcie_set_readrq(pdev, 256); @@ -176,6 +177,42 @@ static void loongson_pci_msi_quirk(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_pci_msi_quirk); +/* + * Older steppings of the Loongson-3C6000 series incorrectly report the + * supported link speeds on their PCIe bridges (device IDs 0x3c19, + * 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds + * from 2.5 GT/s up to 16 GT/s. + */ +static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev) +{ + u8 old_supported_speeds = pdev->supported_speeds; + + switch (pdev->bus->max_bus_speed) { + case PCIE_SPEED_16_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_16_0GB; + fallthrough; + case PCIE_SPEED_8_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_8_0GB; + fallthrough; + case PCIE_SPEED_5_0GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_5_0GB; + fallthrough; + case PCIE_SPEED_2_5GT: + pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_2_5GB; + break; + default: + pci_warn(pdev, "unexpected max bus speed"); + + return; + } + + if (pdev->supported_speeds != old_supported_speeds) + pci_info(pdev, "fixed up supported link speeds: 0x%x => 0x%x", + old_supported_speeds, pdev->supported_speeds); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; @@ -230,11 +267,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, struct loongson_pci *priv = pci_bus_to_loongson_pci(bus); /* - * Do not read more than one device on the bus other than - * the host bus. + * Do not read more than one device on the internal bridges. */ if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) { - if (!pci_is_root_bus(bus) && (device > 0)) + if (!pci_is_root_bus(bus) && (device > 0) && + pci_match_id(loongson_internal_bridge_devids, bus->self)) return NULL; } diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index a72aa57591c0..69a9d77039e8 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -57,9 +57,9 @@ #define PCIE_CONF_DATA_OFF 0x18fc #define PCIE_INT_CAUSE_OFF 0x1900 #define PCIE_INT_UNMASK_OFF 0x1910 -#define PCIE_INT_INTX(i) BIT(24+i) -#define PCIE_INT_PM_PME BIT(28) -#define PCIE_INT_ALL_MASK GENMASK(31, 0) +#define PCIE_INT_INTX(i) BIT_U32(24 + (i)) +#define PCIE_INT_PM_PME BIT_U32(28) +#define PCIE_INT_ALL_MASK GENMASK_U32(31, 0) #define PCIE_CTRL_OFF 0x1a00 #define PCIE_CTRL_X1_MODE 0x0001 #define PCIE_CTRL_RC_MODE BIT(1) @@ -263,8 +263,7 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) * not set correctly then link with endpoint card is not established. */ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); - lnkcap &= ~PCI_EXP_LNKCAP_MLW; - lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, port->is_x4 ? 4 : 1); + FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, port->is_x4 ? 4 : 1); mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); /* Disable Root Bridge I/O space, memory space and bus mastering. */ diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 3dbb7adc421c..8ef56e2fabcf 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -864,11 +864,25 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) return 0; } +static void altera_pcie_disable_irq(struct altera_pcie *pcie) +{ + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || + pcie->pcie_data->version == ALTERA_PCIE_V2) { + /* Disable all P2A interrupts */ + cra_writel(pcie, 0, P2A_INT_ENABLE); + } else if (pcie->pcie_data->version == ALTERA_PCIE_V3) { + /* Disable port-level interrupts (CFG_AER, etc.) */ + writel(0, pcie->hip_base + + pcie->pcie_data->port_conf_offset + + pcie->pcie_data->port_irq_enable_offset); + } +} + static void altera_pcie_irq_teardown(struct altera_pcie *pcie) { + altera_pcie_disable_irq(pcie); irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); irq_domain_remove(pcie->irq_domain); - irq_dispose_mapping(pcie->irq); } static int altera_pcie_parse_dt(struct altera_pcie *pcie) @@ -891,7 +905,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie) if (pcie->irq < 0) return pcie->irq; - irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie); return 0; } @@ -1020,6 +1033,14 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } + /* + * The chained handler uses pcie->irq_domain, so set it only after the + * INTx domain has been created. + */ + irq_set_chained_handler_and_data(pcie->irq, + pcie->pcie_data->ops->rp_isr, + pcie); + if (pcie->pcie_data->version == ALTERA_PCIE_V1 || pcie->pcie_data->version == ALTERA_PCIE_V2) { /* clear all interrupts */ @@ -1037,7 +1058,16 @@ static int altera_pcie_probe(struct platform_device *pdev) bridge->busnr = pcie->root_bus_nr; bridge->ops = &altera_pcie_ops; - return pci_host_probe(bridge); + ret = pci_host_probe(bridge); + if (ret) + goto err_teardown_irq; + + return 0; + +err_teardown_irq: + altera_pcie_irq_teardown(pcie); + + return ret; } static void altera_pcie_remove(struct platform_device *pdev) @@ -1045,8 +1075,10 @@ static void altera_pcie_remove(struct platform_device *pdev) struct altera_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); altera_pcie_irq_teardown(pcie); } diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c index 6acfae7d026e..9aa9e14c6148 100644 --- a/drivers/pci/controller/pcie-aspeed.c +++ b/drivers/pci/controller/pcie-aspeed.c @@ -127,19 +127,19 @@ #define CFG0_READ_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ - PCIE_TLP_TYPE_CFG0_RD)) + PCIE_TLP_TYPE_CFG0_RDWR)) #define CFG0_WRITE_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ - PCIE_TLP_TYPE_CFG0_WR)) + PCIE_TLP_TYPE_CFG0_RDWR)) #define CFG1_READ_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \ - PCIE_TLP_TYPE_CFG1_RD)) + PCIE_TLP_TYPE_CFG1_RDWR)) #define CFG1_WRITE_FMTTYPE \ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ - PCIE_TLP_TYPE_CFG1_WR)) + PCIE_TLP_TYPE_CFG1_RDWR)) #define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */ #define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4))) #define TLP_GET_VALUE(x, y, z) \ diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 08a0e7091ced..8a0c353d2abf 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1894,8 +1894,10 @@ static void brcm_pcie_remove(struct platform_device *pdev) struct brcm_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); if (pcie->cfg->has_err_report) brcm_unregister_die_notifiers(pcie); diff --git a/drivers/pci/controller/pcie-iproc-bcma.c b/drivers/pci/controller/pcie-iproc-bcma.c index 99a99900444d..593418c2bc3a 100644 --- a/drivers/pci/controller/pcie-iproc-bcma.c +++ b/drivers/pci/controller/pcie-iproc-bcma.c @@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev) if (ret) return ret; - pcie->map_irq = iproc_bcma_pcie_map_irq; + bridge->map_irq = iproc_bcma_pcie_map_irq; bcma_set_drvdata(bdev, pcie); diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 0cb78c583c7e..4c9a0c4bb923 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) switch (pcie->type) { case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC_V2: - pcie->map_irq = NULL; + bridge->map_irq = NULL; break; default: break; diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index ccf71993ea35..afb3aa38ac6b 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1502,7 +1502,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) host->ops = &iproc_pcie_ops; host->sysdata = pcie; - host->map_irq = pcie->map_irq; ret = pci_host_probe(host); if (ret < 0) { @@ -1529,8 +1528,10 @@ void iproc_pcie_remove(struct iproc_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); + pci_unlock_rescan_remove(); iproc_pcie_msi_disable(pcie); diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index 969ded03b8c2..c4443f236ca3 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -61,7 +61,6 @@ struct iproc_msi; * @base_addr: PCIe host controller register base physical address * @mem: host bridge memory window resource * @phy: optional PHY device that controls the Serdes - * @map_irq: function callback to map interrupts * @ep_is_internal: indicates an internal emulated endpoint device is connected * @iproc_cfg_read: indicates the iProc config read function should be used * @rej_unconfig_pf: indicates the root complex needs to detect and reject @@ -91,7 +90,6 @@ struct iproc_pcie { phys_addr_t base_addr; struct resource mem; struct phy *phy; - int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; bool iproc_cfg_read; bool rej_unconfig_pf; diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b0accd828589..bcbd08c82a5c 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -30,6 +30,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" #define PCIE_BASE_CFG_REG 0x14 @@ -63,6 +64,12 @@ #define PCIE_BRG_RSTB BIT(2) #define PCIE_PE_RSTB BIT(3) +/* + * As described in the datasheet of MediaTek PCIe Gen3 controller, wait 10ms + * after setting PCIE_BRG_RSTB, and before accessing PCIe internal registers. + */ +#define PCIE_BRG_RST_RDY_MS 10 + #define PCIE_LTSSM_STATUS_REG 0x150 #define PCIE_LTSSM_STATE_MASK GENMASK(28, 24) #define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24) @@ -430,6 +437,21 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) return err; } + /* + * Some of MediaTek's chips won't output REFCLK when PCIE_PHY_RSTB is + * asserted, we have to de-assert MAC & PHY & BRG reset signals first + * to allow the REFCLK to be stable. While PCIE_BRG_RSTB is asserted, + * there is a short period during which the PCIe internal register + * cannot be accessed, so we need to wait 10ms here. + */ + msleep(PCIE_BRG_RST_RDY_MS); + + if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { + /* De-assert MAC, PHY and BRG reset signals */ + val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB); + writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); + } + /* * Described in PCIe CEM specification revision 6.0. * @@ -439,9 +461,8 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie) msleep(PCIE_T_PVPERL_MS); if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) { - /* De-assert reset signals */ - val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB | - PCIE_PE_RSTB); + /* De-assert PERST# signal */ + val &= ~PCIE_PE_RSTB; writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG); } @@ -494,8 +515,7 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) /* Set Link Control 2 (LNKCTL2) speed restriction, if any */ if (pcie->max_link_speed) { val = readl_relaxed(pcie->base + PCIE_CONF_LINK2_CTL_STS); - val &= ~PCIE_CONF_LINK2_LCR2_LINK_SPEED; - val |= FIELD_PREP(PCIE_CONF_LINK2_LCR2_LINK_SPEED, pcie->max_link_speed); + FIELD_MODIFY(PCIE_CONF_LINK2_LCR2_LINK_SPEED, &val, pcie->max_link_speed); writel_relaxed(val, pcie->base + PCIE_CONF_LINK2_CTL_STS); } @@ -570,6 +590,8 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) goto err_power_down_device; } + pci_host_common_link_train_delay(pcie->max_link_speed); + return 0; err_power_down_device: @@ -1222,8 +1244,8 @@ static int mtk_pcie_probe(struct platform_device *pdev) err = pci_pwrctrl_create_devices(pcie->dev); if (err) { - goto err_tear_down_irq; dev_err_probe(dev, err, "failed to create pwrctrl devices\n"); + goto err_tear_down_irq; } err = mtk_pcie_setup(pcie); @@ -1260,12 +1282,20 @@ static void mtk_pcie_remove(struct platform_device *pdev) pci_remove_root_bus(host->bus); pci_unlock_rescan_remove(); - pci_pwrctrl_power_off_devices(pcie->dev); + mtk_pcie_devices_power_down(pcie); mtk_pcie_power_down(pcie); pci_pwrctrl_destroy_devices(pcie->dev); mtk_pcie_irq_teardown(pcie); } +static void mtk_pcie_shutdown(struct platform_device *pdev) +{ + struct mtk_gen3_pcie *pcie = platform_get_drvdata(pdev); + + mtk_pcie_devices_power_down(pcie); + mtk_pcie_power_down(pcie); +} + static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie) { int i; @@ -1404,6 +1434,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, .remove = mtk_pcie_remove, + .shutdown = mtk_pcie_shutdown, .driver = { .name = "mtk-pcie-gen3", .of_match_table = mtk_pcie_of_match, diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 75722524fe74..8b57c3d75b94 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -7,6 +7,7 @@ * Honghui Zhang */ +#include #include #include #include @@ -61,7 +62,7 @@ /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c #define PCIE_FTS_NUM_MASK GENMASK(15, 8) -#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) +#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x) #define PCIE_FC_CREDIT 0x73c #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) @@ -111,10 +112,6 @@ #define APP_CFG_REQ BIT(0) #define APP_CPL_STATUS GENMASK(7, 5) -#define CFG_WRRD_TYPE_0 4 -#define CFG_WR_FMT 2 -#define CFG_RD_FMT 0 - #define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0)) #define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24)) #define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29)) @@ -175,6 +172,7 @@ struct mtk_pcie_soc { /** * struct mtk_pcie_port - PCIe port information * @base: IO mapped register base + * @phys_base: Physical address of the I/O register base region * @list: port list * @pcie: pointer to PCIe host info * @reset: pointer to port reset control @@ -196,6 +194,7 @@ struct mtk_pcie_soc { */ struct mtk_pcie_port { void __iomem *base; + phys_addr_t phys_base; struct list_head list; struct mtk_pcie *pcie; struct reset_control *reset; @@ -295,7 +294,7 @@ static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, u32 tmp; /* Write PCIe configuration transaction header for Cfgrd */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT), + writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_NO_DATA), port->base + PCIE_CFG_HEADER0); writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), @@ -325,7 +324,7 @@ static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn, int where, int size, u32 val) { /* Write PCIe configuration transaction header for Cfgwr */ - writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT), + writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_DATA), port->base + PCIE_CFG_HEADER0); writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1); writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus), @@ -405,7 +404,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) phys_addr_t addr; /* MT2712/MT7622 only support 32-bit MSI addresses */ - addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + addr = port->phys_base + PCIE_MSI_VECTOR; msg->address_hi = 0; msg->address_lo = lower_32_bits(addr); @@ -520,7 +519,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) u32 val; phys_addr_t msg_addr; - msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR); + msg_addr = port->phys_base + PCIE_MSI_VECTOR; val = lower_32_bits(msg_addr); writel(val, port->base + PCIE_IMSI_ADDR); @@ -529,23 +528,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port) writel(val, port->base + PCIE_INT_MASK); } +static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port) +{ + irq_set_chained_handler_and_data(port->irq, NULL, NULL); + + if (port->irq_domain) + irq_domain_remove(port->irq_domain); + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + if (port->inner_domain) + irq_domain_remove(port->inner_domain); + } + + irq_dispose_mapping(port->irq); +} + static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) { struct mtk_pcie_port *port, *tmp; - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - irq_set_chained_handler_and_data(port->irq, NULL, NULL); - - if (port->irq_domain) - irq_domain_remove(port->irq_domain); - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - if (port->inner_domain) - irq_domain_remove(port->inner_domain); - } - - irq_dispose_mapping(port->irq); - } + list_for_each_entry_safe(port, tmp, &pcie->ports, list) + mtk_pcie_irq_teardown_port(port); } static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, @@ -865,7 +868,7 @@ static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port) return mtk_pcie_startup_port_v2(port); } -static void mtk_pcie_enable_port(struct mtk_pcie_port *port) +static int mtk_pcie_enable_port(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; struct device *dev = pcie->dev; @@ -874,7 +877,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) err = clk_prepare_enable(port->sys_ck); if (err) { dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); - goto err_sys_clk; + return err; } err = clk_prepare_enable(port->ahb_ck); @@ -922,11 +925,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) goto err_phy_on; } - if (!pcie->soc->startup(port)) - return; + err = pcie->soc->startup(port); + if (err) { + dev_info(dev, "Port%d link down\n", port->slot); + goto err_soc_startup; + } - dev_info(dev, "Port%d link down\n", port->slot); + return 0; +err_soc_startup: phy_power_off(port->phy); err_phy_on: phy_exit(port->phy); @@ -942,8 +949,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port) clk_disable_unprepare(port->ahb_ck); err_ahb_clk: clk_disable_unprepare(port->sys_ck); -err_sys_clk: - mtk_pcie_port_free(port); + + return err; } static int mtk_pcie_parse_port(struct mtk_pcie *pcie, @@ -953,6 +960,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, struct mtk_pcie_port *port; struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); + struct resource *res; char name[20]; int err; @@ -961,7 +969,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie, return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); - port->base = devm_platform_ioremap_resource_byname(pdev, name); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) { + dev_err(dev, "failed to get port%d base\n", slot); + return -EINVAL; + } + + port->phys_base = res->start; + port->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(port->base)) { dev_err(dev, "failed to map port%d base\n", slot); return PTR_ERR(port->base); @@ -1109,8 +1124,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) return err; /* enable each port, and then check link status */ - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) { + mtk_pcie_irq_teardown_port(port); + mtk_pcie_port_free(port); + } + } /* power down PCIe subsys if slots are all empty (link down) */ if (list_empty(&pcie->ports)) @@ -1172,8 +1192,10 @@ static void mtk_pcie_remove(struct platform_device *pdev) struct mtk_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); + pci_lock_rescan_remove(); pci_stop_root_bus(host->bus); pci_remove_root_bus(host->bus); + pci_unlock_rescan_remove(); mtk_pcie_free_resources(pcie); mtk_pcie_irq_teardown(pcie); @@ -1209,14 +1231,18 @@ static int mtk_pcie_resume_noirq(struct device *dev) { struct mtk_pcie *pcie = dev_get_drvdata(dev); struct mtk_pcie_port *port, *tmp; + int err; if (list_empty(&pcie->ports)) return 0; clk_prepare_enable(pcie->free_ck); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - mtk_pcie_enable_port(port); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + err = mtk_pcie_enable_port(port); + if (err) + mtk_pcie_port_free(port); + } /* In case of EP was removed while system suspend. */ if (list_empty(&pcie->ports)) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 213028052aa5..cd9171eebc28 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -346,7 +346,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host) struct rcar_pcie *pcie = &host->pcie; struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host); struct resource_entry *win; - LIST_HEAD(res); int i = 0; /* Try setting 5 GT/s link speed */ diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index ee1822ca01db..d203c4876d30 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1012,8 +1012,10 @@ static void rockchip_pcie_remove(struct platform_device *pdev) struct rockchip_pcie *rockchip = dev_get_drvdata(dev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip); + pci_lock_rescan_remove(); pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); + pci_unlock_rescan_remove(); irq_domain_remove(rockchip->irq_domain); rockchip_pcie_deinit_phys(rockchip); diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index d86e7516dcc2..66f687304c1c 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -35,6 +35,7 @@ #include #include +#include "pci-host-common.h" #include "../pci.h" /* AXI registers */ @@ -1663,7 +1664,7 @@ rzg3s_pcie_host_setup(struct rzg3s_pcie_host *host, if (ret) dev_info(dev, "Failed to set max link speed\n"); - msleep(PCIE_RESET_CONFIG_WAIT_MS); + pci_host_common_link_train_delay(host->max_link_speed); return 0; diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index 3c2f68383010..f9a34f323ad8 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -640,8 +640,10 @@ EXPORT_SYMBOL_GPL(plda_pcie_host_init); void plda_pcie_host_deinit(struct plda_pcie_rp *port) { + pci_lock_rescan_remove(); pci_stop_root_bus(port->bridge->bus); pci_remove_root_bus(port->bridge->bus); + pci_unlock_rescan_remove(); plda_pcie_irq_domain_deinit(port); diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 2bdcc35b652c..5314aca2188a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb, struct pci_epc *epc; int ret; - if (db_count > MAX_DB_COUNT) - return -EINVAL; - ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; + if (!db_count || db_count > MAX_DB_COUNT) { + dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n", + db_count, MAX_DB_COUNT); + return -EINVAL; + } + if (msix) ret = epf_ntb_configure_msix(ntb, type, db_count); else @@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, u8 func_no, vfunc_no; struct pci_epc *epc; struct device *dev; - u32 db_count; int ret; ntb_epc = ntb->epc[type]; @@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, func_no = ntb_epc->func_no; vfunc_no = ntb_epc->vfunc_no; - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (1 - %d)\n", + ntb->db_count, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; epc = ntb_epc->epc; if (msi_capable) { - ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count); + ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count); if (ret) { dev_err(dev, "%s intf: MSI configuration failed\n", pci_epc_interface_string(type)); @@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, } if (msix_capable) { - ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count, + ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, ntb_epc->msix_bar, ntb_epc->msix_table_offset); if (ret) { diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 591d301fa89d..4802d4f80f78 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -94,6 +94,7 @@ struct pci_epf_test { bool dma_private; const struct pci_epc_features *epc_features; struct pci_epf_bar db_bar; + bool db_bar_programmed; size_t bar_size[PCI_STD_NUM_BARS]; }; @@ -733,7 +734,9 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, { u32 status = le32_to_cpu(reg->status); struct pci_epf *epf = epf_test->epf; + struct pci_epf_doorbell_msg *db; struct pci_epc *epc = epf->epc; + unsigned long irq_flags; struct msi_msg *msg; enum pci_barno bar; size_t offset; @@ -743,13 +746,28 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, if (ret) goto set_status_err; - msg = &epf->db_msg[0].msg; - bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1); - if (bar < BAR_0) - goto err_doorbell_cleanup; + db = &epf->db_msg[0]; + msg = &db->msg; + epf_test->db_bar_programmed = false; + + if (db->bar != NO_BAR) { + /* + * The doorbell target is already exposed via a platform-owned + * fixed BAR + */ + bar = db->bar; + offset = db->offset; + } else { + bar = pci_epc_get_next_free_bar(epf_test->epc_features, + epf_test->test_reg_bar + 1); + if (bar < BAR_0) + goto err_doorbell_cleanup; + } + + irq_flags = epf->db_msg[0].irq_flags | IRQF_ONESHOT; ret = request_threaded_irq(epf->db_msg[0].virq, NULL, - pci_epf_test_doorbell_handler, IRQF_ONESHOT, + pci_epf_test_doorbell_handler, irq_flags, "pci-ep-test-doorbell", epf_test); if (ret) { dev_err(&epf->dev, @@ -761,22 +779,30 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, reg->doorbell_data = cpu_to_le32(msg->data); reg->doorbell_bar = cpu_to_le32(bar); - msg = &epf->db_msg[0].msg; - ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo, - &epf_test->db_bar.phys_addr, &offset); + if (db->bar == NO_BAR) { + ret = pci_epf_align_inbound_addr(epf, bar, + ((u64)msg->address_hi << 32) | + msg->address_lo, + &epf_test->db_bar.phys_addr, + &offset); - if (ret) - goto err_free_irq; + if (ret) + goto err_free_irq; + } reg->doorbell_offset = cpu_to_le32(offset); - epf_test->db_bar.barno = bar; - epf_test->db_bar.size = epf->bar[bar].size; - epf_test->db_bar.flags = epf->bar[bar].flags; + if (db->bar == NO_BAR) { + epf_test->db_bar.barno = bar; + epf_test->db_bar.size = epf->bar[bar].size; + epf_test->db_bar.flags = epf->bar[bar].flags; - ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); - if (ret) - goto err_free_irq; + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); + if (ret) + goto err_free_irq; + + epf_test->db_bar_programmed = true; + } status |= STATUS_DOORBELL_ENABLE_SUCCESS; reg->status = cpu_to_le32(status); @@ -806,17 +832,23 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test, free_irq(epf->db_msg[0].virq, epf_test); pci_epf_test_doorbell_cleanup(epf_test); - /* - * The doorbell feature temporarily overrides the inbound translation - * to point to the address stored in epf_test->db_bar.phys_addr, i.e., - * it calls set_bar() twice without ever calling clear_bar(), as - * calling clear_bar() would clear the BAR's PCI address assigned by - * the host. Thus, when disabling the doorbell, restore the inbound - * translation to point to the memory allocated for the BAR. - */ - ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]); - if (ret) - goto set_status_err; + if (epf_test->db_bar_programmed) { + /* + * The doorbell feature temporarily overrides the inbound + * translation to point to the address stored in + * epf_test->db_bar.phys_addr, i.e., it calls set_bar() + * twice without ever calling clear_bar(), as calling + * clear_bar() would clear the BAR's PCI address assigned + * by the host. Thus, when disabling the doorbell, restore + * the inbound translation to point to the memory allocated + * for the BAR. + */ + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]); + if (ret) + goto set_status_err; + + epf_test->db_bar_programmed = false; + } status |= STATUS_DOORBELL_DISABLE_SUCCESS; reg->status = cpu_to_le32(status); diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 2256c3062b1a..c3caec927d74 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -37,6 +37,7 @@ */ #include +#include #include #include #include @@ -66,9 +67,11 @@ static struct workqueue_struct *kpcintb_workqueue; #define NTB_MW_OFFSET 2 #define DB_COUNT_MASK GENMASK(15, 0) #define MSIX_ENABLE BIT(16) -#define MAX_DB_COUNT 32 #define MAX_MW 4 +/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */ +#define VNTB_PEER_DB_WORK_BUDGET 5 + enum epf_ntb_bar { BAR_CONFIG, BAR_DB, @@ -79,6 +82,15 @@ enum epf_ntb_bar { VNTB_BAR_NUM, }; +enum epf_irq_slot { + EPF_IRQ_LINK = 0, + EPF_IRQ_RESERVED_DB, /* Historically skipped slot */ + EPF_IRQ_DB_START, +}; + +#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1) +#define MAX_DB_COUNT 32 + /* * +--------------------------------------------------+ Base * | | @@ -129,11 +141,18 @@ struct epf_ntb { u32 spad_count; u64 mws_size[MAX_MW]; atomic64_t db; + atomic64_t peer_db_pending; + struct work_struct peer_db_work; u32 vbus_number; u16 vntb_pid; u16 vntb_vid; bool linkup; + + /* + * True when doorbells are interrupt-driven (MSI or embedded), false + * when polled. + */ bool msi_doorbell; u32 spad_size; @@ -261,10 +280,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work) ntb = container_of(work, struct epf_ntb, cmd_handler.work); - for (i = 1; i < ntb->db_count && !ntb->msi_doorbell; i++) { + for (i = EPF_IRQ_DB_START; i < ntb->db_count && !ntb->msi_doorbell; + i++) { if (ntb->epf_db[i]) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); ntb->epf_db[i] = 0; } } @@ -330,10 +350,10 @@ static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data) struct epf_ntb *ntb = data; int i; - for (i = 1; i < ntb->db_count; i++) + for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++) if (irq == ntb->epf->db_msg[i].virq) { - atomic64_or(1 << (i - 1), &ntb->db); - ntb_db_event(&ntb->ntb, i); + atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); + ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); } return IRQ_HANDLED; @@ -483,7 +503,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; struct device *dev; - u32 db_count; int ret; dev = &ntb->epf->dev; @@ -495,14 +514,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return -EINVAL; } - db_count = ntb->db_count; - if (db_count > MAX_DB_COUNT) { - dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT); + if (ntb->db_count < MIN_DB_COUNT || ntb->db_count > MAX_DB_COUNT) { + dev_err(dev, "DB count %d out of range (%d - %d)\n", + ntb->db_count, MIN_DB_COUNT, MAX_DB_COUNT); return -EINVAL; } - ntb->db_count = db_count; - if (epc_features->msi_capable) { ret = pci_epc_set_msi(ntb->epf->epc, ntb->epf->func_no, @@ -517,6 +534,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) return 0; } +static bool epf_ntb_db_irq_is_duplicated(const struct pci_epf *epf, unsigned int idx) +{ + unsigned int i; + + for (i = 0; i < idx; i++) + if (epf->db_msg[i].virq == epf->db_msg[idx].virq) + return true; + + return false; +} + static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, struct pci_epf_bar *db_bar, const struct pci_epc_features *epc_features, @@ -533,9 +561,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, if (ret) return ret; + /* + * The doorbell target may already be exposed by a platform-owned fixed + * BAR. In that case, we must reuse it and the requested db_bar must + * match. + */ + if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) { + ret = -EINVAL; + goto err_free_doorbell; + } + for (req = 0; req < ntb->db_count; req++) { + /* Avoid requesting duplicate handlers */ + if (epf_ntb_db_irq_is_duplicated(epf, req)) + continue; + ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler, - 0, "pci_epf_vntb_db", ntb); + epf->db_msg[req].irq_flags, "pci_epf_vntb_db", + ntb); if (ret) { dev_err(&epf->dev, @@ -545,6 +588,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, } } + if (epf->db_msg[0].bar != NO_BAR) { + for (i = 0; i < ntb->db_count; i++) { + msg = &epf->db_msg[i].msg; + + if (epf->db_msg[i].bar != barno) { + ret = -EINVAL; + goto err_free_irq; + } + + ntb->reg->db_data[i] = msg->data; + ntb->reg->db_offset[i] = epf->db_msg[i].offset; + } + goto out; + } + + /* Program inbound mapping for the doorbell */ msg = &epf->db_msg[0].msg; high = 0; @@ -591,6 +650,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, ntb->reg->db_offset[i] = offset; } +out: ntb->reg->db_entry_size = 0; ntb->msi_doorbell = true; @@ -598,9 +658,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, return 0; err_free_irq: - for (req--; req >= 0; req--) + for (req--; req >= 0; req--) { + if (epf_ntb_db_irq_is_duplicated(epf, req)) + continue; free_irq(epf->db_msg[req].virq, ntb); + } +err_free_doorbell: pci_epf_free_doorbell(ntb->epf); return ret; } @@ -666,8 +730,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb) if (ntb->msi_doorbell) { int i; - for (i = 0; i < ntb->db_count; i++) + for (i = 0; i < ntb->db_count; i++) { + if (epf_ntb_db_irq_is_duplicated(ntb->epf, i)) + continue; free_irq(ntb->epf->db_msg[i].virq, ntb); + } } if (ntb->epf->db_msg) @@ -920,6 +987,9 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); + atomic64_set(&ntb->peer_db_pending, 0); + enable_work(&ntb->peer_db_work); + return 0; err_write_header: @@ -943,11 +1013,18 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) static void epf_ntb_epc_cleanup(struct epf_ntb *ntb) { disable_delayed_work_sync(&ntb->cmd_handler); + disable_work_sync(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); epf_ntb_mw_bar_clear(ntb, ntb->num_mws); epf_ntb_db_bar_clear(ntb); epf_ntb_config_sspad_bar_clear(ntb); } +static bool epf_ntb_epc_attached(struct epf_ntb *ntb) +{ + return ntb->epf->epc || ntb->epf->sec_epc; +} + #define EPF_NTB_R(_name) \ static ssize_t epf_ntb_##_name##_show(struct config_item *item, \ char *page) \ @@ -967,6 +1044,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ u32 val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtou32(page, 0, &val); \ if (ret) \ return ret; \ @@ -1009,6 +1089,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ u64 val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtou64(page, 0, &val); \ if (ret) \ return ret; \ @@ -1047,6 +1130,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \ int val; \ int ret; \ \ + if (epf_ntb_epc_attached(ntb)) \ + return -EOPNOTSUPP; \ + \ ret = kstrtoint(page, 0, &val); \ if (ret) \ return ret; \ @@ -1067,6 +1153,9 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item, u32 val; int ret; + if (epf_ntb_epc_attached(ntb)) + return -EOPNOTSUPP; + ret = kstrtou32(page, 0, &val); if (ret) return ret; @@ -1079,10 +1168,32 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item, return len; } +static ssize_t epf_ntb_db_count_store(struct config_item *item, + const char *page, size_t len) +{ + struct config_group *group = to_config_group(item); + struct epf_ntb *ntb = to_epf_ntb(group); + u32 val; + int ret; + + if (epf_ntb_epc_attached(ntb)) + return -EOPNOTSUPP; + + ret = kstrtou32(page, 0, &val); + if (ret) + return ret; + + if (val < MIN_DB_COUNT || val > MAX_DB_COUNT) + return -EINVAL; + + WRITE_ONCE(ntb->db_count, val); + + return len; +} + EPF_NTB_R(spad_count) EPF_NTB_W(spad_count) EPF_NTB_R(db_count) -EPF_NTB_W(db_count) EPF_NTB_R(num_mws) EPF_NTB_R(vbus_number) EPF_NTB_W(vbus_number) @@ -1251,9 +1362,48 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb) return ntb_ndev(ntb)->num_mws; } +static int vntb_epf_db_vector_count(struct ntb_dev *ntb) +{ + struct epf_ntb *ndev = ntb_ndev(ntb); + u32 db_count = READ_ONCE(ndev->db_count); + + /* + * db_count is the total number of doorbell slots exposed to + * the peer, including: + * - slot #0 reserved for link events + * - slot #1 historically unused (kept for protocol compatibility) + * + * Report only usable per-vector doorbell interrupts. + */ + if (db_count < MIN_DB_COUNT || db_count > MAX_DB_COUNT) + return 0; + + return db_count - EPF_IRQ_DB_START; +} + static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb) { - return BIT_ULL(ntb_ndev(ntb)->db_count) - 1; + int nr_vec = vntb_epf_db_vector_count(ntb); + + if (!nr_vec) + return 0; + + return GENMASK_ULL(nr_vec - 1, 0); +} + +static u64 vntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + int nr_vec; + + /* + * Doorbell vectors are numbered [0 .. nr_vec - 1], where nr_vec + * excludes the two reserved slots described above. + */ + nr_vec = vntb_epf_db_vector_count(ntb); + if (db_vector < 0 || db_vector >= nr_vec) + return 0; + + return BIT_ULL(db_vector); } static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits) @@ -1357,22 +1507,84 @@ static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32 return 0; } -static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +static void vntb_epf_peer_db_work(struct work_struct *work) { - u32 interrupt_num = ffs(db_bits) + 1; - struct epf_ntb *ntb = ntb_ndev(ndev); + struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work); + struct pci_epf *epf = ntb->epf; + unsigned int budget = VNTB_PEER_DB_WORK_BUDGET; u8 func_no, vfunc_no; + unsigned int db_bit; + u32 interrupt_num; + u64 db_bits; int ret; - func_no = ntb->epf->func_no; - vfunc_no = ntb->epf->vfunc_no; + if (!epf || !epf->epc) + return; - ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no, - PCI_IRQ_MSI, interrupt_num + 1); - if (ret) - dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n"); + func_no = epf->func_no; + vfunc_no = epf->vfunc_no; - return ret; + /* + * Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()). + * Limit the number of snapshots handled per run so we don't monopolize + * the workqueue under a doorbell storm. + */ + while (budget--) { + db_bits = atomic64_xchg(&ntb->peer_db_pending, 0); + if (!db_bits) + return; + + while (db_bits) { + /* + * pci_epc_raise_irq() for MSI expects a 1-based + * interrupt number. The first usable doorbell starts + * at EPF_IRQ_DB_START in the legacy slot layout. + * + * Legacy mapping (kept for compatibility): + * + * MSI #1 : link event (reserved) + * MSI #2 : unused (historical offset) + * MSI #3 : doorbell bit 0 (DB#0) + * MSI #4 : doorbell bit 1 (DB#1) + * ... + * + * Do not change this mapping to avoid breaking + * interoperability with older peers. + */ + db_bit = __ffs64(db_bits); + interrupt_num = db_bit + EPF_IRQ_DB_START + 1; + db_bits &= ~BIT_ULL(db_bit); + + ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, + PCI_IRQ_MSI, interrupt_num); + if (ret) + dev_err(&ntb->ntb.dev, + "Failed to raise IRQ for interrupt_num %u: %d\n", + interrupt_num, ret); + } + } + + if (atomic64_read(&ntb->peer_db_pending)) + queue_work(kpcintb_workqueue, &ntb->peer_db_work); +} + +static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + + db_bits &= vntb_epf_db_valid_mask(ndev); + if (!db_bits) + return 0; + + /* + * .peer_db_set() may be called from atomic context. pci_epc_raise_irq() + * can sleep (it takes epc->lock), so defer MSI raising to process + * context. Doorbell requests are coalesced in peer_db_pending. + */ + atomic64_or(db_bits, &ntb->peer_db_pending); + queue_work(kpcintb_workqueue, &ntb->peer_db_work); + + return 0; } static u64 vntb_epf_db_read(struct ntb_dev *ndev) @@ -1441,6 +1653,8 @@ static const struct ntb_dev_ops vntb_epf_ops = { .spad_count = vntb_epf_spad_count, .peer_mw_count = vntb_epf_peer_mw_count, .db_valid_mask = vntb_epf_db_valid_mask, + .db_vector_count = vntb_epf_db_vector_count, + .db_vector_mask = vntb_epf_db_vector_mask, .db_set_mask = vntb_epf_db_set_mask, .mw_set_trans = vntb_epf_mw_set_trans, .mw_clear_trans = vntb_epf_mw_clear_trans, @@ -1619,6 +1833,10 @@ static int epf_ntb_probe(struct pci_epf *epf, ntb->epf = epf; ntb->vbus_number = 0xff; + INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work); + disable_work(&ntb->peer_db_work); + atomic64_set(&ntb->peer_db_pending, 0); + /* Initially, no bar is assigned */ for (i = 0; i < VNTB_BAR_NUM; i++) ntb->epf_ntb_bar[i] = NO_BAR; diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 1395919571f8..0855c7930abb 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -6,8 +6,11 @@ * Author: Frank Li */ +#include +#include #include #include +#include #include #include #include @@ -35,23 +38,116 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) pci_epc_put(epc); } -int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) +static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) { + const struct pci_epc_aux_resource *doorbell = NULL; + struct pci_epf_doorbell_msg *msg; struct pci_epc *epc = epf->epc; - struct device *dev = &epf->dev; - struct irq_domain *domain; - void *msg; - int ret; - int i; + size_t map_size = 0, off = 0; + dma_addr_t iova_base = 0; + phys_addr_t phys_base; + int count, ret, i; + u64 addr; - /* TODO: Multi-EPF support */ - if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { - dev_err(dev, "MSI doorbell doesn't support multiple EPF\n"); + count = pci_epc_get_aux_resources_count(epc, epf->func_no, + epf->vfunc_no); + if (count < 0) + return count; + if (!count) + return -ENODEV; + + struct pci_epc_aux_resource *res __free(kfree) = + kcalloc(count, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + ret = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no, + res, count); + if (ret) + return ret; + + /* TODO: Support multiple DOORBELL_MMIO resources per EPC. */ + for (i = 0; i < count; i++) { + if (res[i].type != PCI_EPC_AUX_DOORBELL_MMIO) + continue; + + doorbell = &res[i]; + break; + } + if (!doorbell) + return -ENODEV; + addr = doorbell->phys_addr; + if (!IS_ALIGNED(addr, sizeof(u32))) return -EINVAL; + + /* + * Reuse the pre-exposed BAR window if available. Otherwise map the MMIO + * doorbell resource here. Any required IOMMU mapping is handled + * internally, matching the MSI doorbell semantics. + */ + if (doorbell->bar == NO_BAR) { + phys_base = addr & PAGE_MASK; + off = addr - phys_base; + map_size = PAGE_ALIGN(off + sizeof(u32)); + + iova_base = dma_map_resource(epc->dev.parent, phys_base, + map_size, DMA_FROM_DEVICE, 0); + if (dma_mapping_error(epc->dev.parent, iova_base)) + return -EIO; + + addr = iova_base + off; } - if (epf->db_msg) - return -EBUSY; + msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); + if (!msg) { + ret = -ENOMEM; + goto err_unmap; + } + + /* + * Embedded doorbell backends (e.g. DesignWare eDMA interrupt emulation) + * typically provide a single IRQ and do not offer per-doorbell + * distinguishable address/data pairs. The EPC aux resource therefore + * exposes one DOORBELL_MMIO entry (u.db_mmio.irq). + * + * Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells. + * For such backends we replicate the same address/data for each entry + * and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them + * as equivalent "kick" doorbells. + */ + for (i = 0; i < num_db; i++) + msg[i] = (struct pci_epf_doorbell_msg) { + .msg.address_lo = (u32)addr, + .msg.address_hi = (u32)(addr >> 32), + .msg.data = doorbell->u.db_mmio.data, + .virq = doorbell->u.db_mmio.irq, + .irq_flags = IRQF_SHARED, + .type = PCI_EPF_DOORBELL_EMBEDDED, + .bar = doorbell->bar, + .offset = (doorbell->bar == NO_BAR) ? 0 : + doorbell->bar_offset, + .iova_base = iova_base, + .iova_size = map_size, + }; + + epf->num_db = num_db; + epf->db_msg = msg; + return 0; + +err_unmap: + if (map_size) + dma_unmap_resource(epc->dev.parent, iova_base, map_size, + DMA_FROM_DEVICE, 0); + return ret; +} + +static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db) +{ + struct pci_epf_doorbell_msg *msg; + struct device *dev = &epf->dev; + struct pci_epc *epc = epf->epc; + struct irq_domain *domain; + int ret, i; domain = of_msi_map_get_device_domain(epc->dev.parent, 0, DOMAIN_BUS_PLATFORM_MSI); @@ -74,6 +170,12 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) if (!msg) return -ENOMEM; + for (i = 0; i < num_db; i++) + msg[i] = (struct pci_epf_doorbell_msg) { + .type = PCI_EPF_DOORBELL_MSI, + .bar = NO_BAR, + }; + epf->num_db = num_db; epf->db_msg = msg; @@ -90,13 +192,60 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) for (i = 0; i < num_db; i++) epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); - return ret; + return 0; +} + +int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) +{ + struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; + int ret; + + /* TODO: Multi-EPF support */ + if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { + dev_err(dev, "Doorbell doesn't support multiple EPF\n"); + return -EINVAL; + } + + if (epf->db_msg) + return -EBUSY; + + ret = pci_epf_alloc_doorbell_msi(epf, num_db); + if (!ret) + return 0; + + /* + * Fall back to embedded doorbell only when platform MSI is unavailable + * for this EPC. + */ + if (ret != -ENODEV) + return ret; + + ret = pci_epf_alloc_doorbell_embedded(epf, num_db); + if (ret) { + dev_err(dev, "Failed to allocate doorbell: %d\n", ret); + return ret; + } + + dev_info(dev, "Using embedded (DMA) doorbell fallback\n"); + return 0; } EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); void pci_epf_free_doorbell(struct pci_epf *epf) { - platform_device_msi_free_irqs_all(epf->epc->dev.parent); + struct pci_epf_doorbell_msg *msg0; + struct pci_epc *epc = epf->epc; + + if (!epf->db_msg) + return; + + msg0 = &epf->db_msg[0]; + if (msg0->type == PCI_EPF_DOORBELL_MSI) + platform_device_msi_free_irqs_all(epf->epc->dev.parent); + else if (msg0->type == PCI_EPF_DOORBELL_EMBEDDED && msg0->iova_size) + dma_unmap_resource(epc->dev.parent, msg0->iova_base, + msg0->iova_size, DMA_FROM_DEVICE, 0); kfree(epf->db_msg); epf->db_msg = NULL; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 6c3c58185fc5..831b40458dcd 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -156,6 +156,86 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, } EXPORT_SYMBOL_GPL(pci_epc_get_features); +/** + * pci_epc_get_aux_resources_count() - get the number of EPC-provided auxiliary resources + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * + * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control + * registers and/or descriptor memories can be exposed to the host by mapping + * them into BAR space. This helper queries how many such resources the backend + * provides. + * + * Return: the number of available resources on success, -EOPNOTSUPP if the + * backend does not support auxiliary resource queries, or another -errno on + * failure. + */ +int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no) +{ + int count; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!epc->ops->get_aux_resources_count) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + count = epc->ops->get_aux_resources_count(epc, func_no, + vfunc_no); + mutex_unlock(&epc->lock); + + return count; +} +EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources_count); + +/** + * pci_epc_get_aux_resources() - query EPC-provided auxiliary resources + * @epc: EPC device + * @func_no: function number + * @vfunc_no: virtual function number + * @resources: output array + * @num_resources: size of @resources array in entries + * + * Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control + * registers and/or descriptor memories can be exposed to the host by mapping + * them into BAR space. This helper queries the backend for such resources. + * + * Return: 0 on success, -EOPNOTSUPP if the backend does not support auxiliary + * resource queries, or another -errno on failure. + */ +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources) +{ + int ret; + + if (!resources || num_resources <= 0) + return -EINVAL; + + if (!epc || !epc->ops) + return -EINVAL; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!epc->ops->get_aux_resources) + return -EOPNOTSUPP; + + mutex_lock(&epc->lock); + ret = epc->ops->get_aux_resources(epc, func_no, vfunc_no, resources, + num_resources); + mutex_unlock(&epc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources); + /** * pci_epc_stop() - stop the PCI link * @epc: the link of the EPC device that has to be stopped diff --git a/drivers/pci/ide.c b/drivers/pci/ide.c index be74e8f0ae21..beb67b8fb5c5 100644 --- a/drivers/pci/ide.c +++ b/drivers/pci/ide.c @@ -170,8 +170,7 @@ void pci_ide_init(struct pci_dev *pdev) pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val); if (val & PCI_IDE_SEL_CTL_EN) continue; - val &= ~PCI_IDE_SEL_CTL_ID; - val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); + FIELD_MODIFY(PCI_IDE_SEL_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID); pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val); } @@ -182,8 +181,7 @@ void pci_ide_init(struct pci_dev *pdev) pci_read_config_dword(pdev, pos, &val); if (val & PCI_IDE_LINK_CTL_EN) continue; - val &= ~PCI_IDE_LINK_CTL_ID; - val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID); + FIELD_MODIFY(PCI_IDE_LINK_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID); pci_write_config_dword(pdev, pos, val); } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 91ac4e37ecb9..b0d24839c084 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -150,7 +150,7 @@ static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus) pci_remove_bus(virtbus); } -resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno) { if (!dev->is_physfn) return 0; @@ -938,16 +938,21 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev) return; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { int bar_idx, size; pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl); size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]); - ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_VF_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_VF_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(dev, pos + PCI_VF_REBAR_CTRL, ctrl); } } @@ -1084,7 +1089,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno) } } -resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, +resource_size_t __weak pcibios_iov_resource_alignment(const struct pci_dev *dev, int resno) { return pci_iov_resource_size(dev, resno); @@ -1100,7 +1105,8 @@ resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev, * the VF BAR size multiplied by the number of VFs. The alignment * is just the VF BAR size. */ -resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno) +resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, + int resno) { return pcibios_iov_resource_alignment(dev, resno); } diff --git a/drivers/pci/msi/msi.c b/drivers/pci/msi/msi.c index a3e0daff0988..209373c92e9e 100644 --- a/drivers/pci/msi/msi.c +++ b/drivers/pci/msi/msi.c @@ -201,8 +201,7 @@ static inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc, u16 msgctl; pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); - msgctl &= ~PCI_MSI_FLAGS_QSIZE; - msgctl |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, desc->pci.msi_attrib.multiple); + FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &msgctl, desc->pci.msi_attrib.multiple); pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl); pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo); @@ -532,9 +531,8 @@ void __pci_restore_msi_state(struct pci_dev *dev) pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); pci_msi_update_mask(entry, 0, 0); - control &= ~PCI_MSI_FLAGS_QSIZE; - control |= PCI_MSI_FLAGS_ENABLE | - FIELD_PREP(PCI_MSI_FLAGS_QSIZE, entry->pci.msi_attrib.multiple); + FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &control, entry->pci.msi_attrib.multiple); + control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control); } @@ -968,8 +966,7 @@ int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag) if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual) return -ENXIO; - msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST; - msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag); + FIELD_MODIFY(PCI_MSIX_ENTRY_CTRL_ST, &msi_desc->pci.msix_ctrl, tag); pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl); /* Flush the write */ readl(pci_msix_desc_addr(msi_desc)); diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 7c898542af8d..b2d5266f8653 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -262,6 +262,9 @@ int pcim_p2pdma_init(struct pci_dev *pdev) struct pci_p2pdma *p2p; int i, ret; + if (pdev->non_mappable_bars) + return -EOPNOTSUPP; + p2p = rcu_dereference_protected(pdev->p2pdma, 1); if (p2p) return 0; @@ -318,7 +321,8 @@ struct p2pdma_provider *pcim_p2pdma_provider(struct pci_dev *pdev, int bar) { struct pci_p2pdma *p2p; - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM) || + pdev->non_mappable_bars) return NULL; p2p = rcu_dereference_protected(pdev->p2pdma, 1); @@ -548,6 +552,16 @@ static const struct pci_p2pdma_whitelist_entry { {PCI_VENDOR_ID_INTEL, 0x2033, 0}, {PCI_VENDOR_ID_INTEL, 0x2020, 0}, {PCI_VENDOR_ID_INTEL, 0x09a2, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_GNRD, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAA_DMR, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX, 0}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0}, /* Google SoCs. */ {PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0}, {} diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e3f59001785a..f36778e62ac1 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -519,13 +519,6 @@ static void pci_device_remove(struct device *dev) /* Undo the runtime PM settings in local_pci_probe() */ pm_runtime_put_sync(dev); - /* - * If the device is still on, set the power state as "unknown", - * since it might change by the next time we load the driver. - */ - if (pci_dev->current_state == PCI_D0) - pci_dev->current_state = PCI_UNKNOWN; - /* * We would love to complain here if pci_dev->is_enabled is set, that * the driver should have called pci_disable_device(), but the @@ -900,7 +893,7 @@ static int pci_pm_suspend_noirq(struct device *dev) if (!pm) { pci_save_state(pci_dev); - goto Fixup; + goto set_unknown; } if (pm->suspend_noirq) { @@ -952,6 +945,7 @@ static int pci_pm_suspend_noirq(struct device *dev) goto Fixup; } +set_unknown: pci_pm_set_unknown_state(pci_dev); /* diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d37860841260..5ec0b245a69b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -37,8 +37,6 @@ #define ARCH_PCI_DEV_GROUPS #endif -static int sysfs_initialized; /* = 0 */ - /* show configuration fields */ #define pci_config_attr(field, format_string) \ static ssize_t \ @@ -177,7 +175,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, max = PCI_BRIDGE_RESOURCES; for (i = 0; i < max; i++) { - struct resource *res = &pci_dev->resource[i]; + struct resource *res = pci_resource_n(pci_dev, i); struct resource zerores = {}; /* For backwards compatibility */ @@ -674,11 +672,6 @@ static const struct attribute_group pcibus_group = { .attrs = pcibus_attrs, }; -const struct attribute_group *pcibus_groups[] = { - &pcibus_group, - NULL, -}; - static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -689,7 +682,7 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%u\n", (pdev == vga_dev)); return sysfs_emit(buf, "%u\n", - !!(pdev->resource[PCI_ROM_RESOURCE].flags & + !!(pci_resource_flags(pdev, PCI_ROM_RESOURCE) & IORESOURCE_ROM_SHADOW)); } static DEVICE_ATTR_RO(boot_vga); @@ -877,19 +870,6 @@ static const struct attribute_group pci_dev_config_attr_group = { .bin_size = pci_dev_config_attr_bin_size, }; -/* - * llseek operation for mmappable PCI resources. - * May be left unused if the arch doesn't provide them. - */ -static __maybe_unused loff_t -pci_llseek_resource(struct file *filep, - struct kobject *kobj __always_unused, - const struct bin_attribute *attr, - loff_t offset, int whence) -{ - return fixed_size_llseek(filep, offset, whence, attr->size); -} - #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space @@ -981,91 +961,148 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); } -/** - * pci_adjust_legacy_attr - adjustment of legacy file attributes - * @b: bus to create files under - * @mmap_type: I/O port or memory - * - * Stub implementation. Can be overridden by arch if necessary. - */ -void __weak pci_adjust_legacy_attr(struct pci_bus *b, - enum pci_mmap_state mmap_type) +bool __weak pci_legacy_has_sparse(struct pci_bus *bus, + enum pci_mmap_state type) { + return false; } -/** - * pci_create_legacy_files - create legacy I/O port and memory files - * @b: bus to create files under - * - * Some platforms allow access to legacy I/O port and ISA memory space on - * a per-bus basis. This routine creates the files and ties them into - * their associated read, write and mmap files from pci-sysfs.c - * - * On error unwind, but don't propagate the error to the caller - * as it is ok to set up the PCI bus without these files. - */ -void pci_create_legacy_files(struct pci_bus *b) +static inline umode_t __pci_legacy_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + enum pci_mmap_state type, + bool sparse) { - int error; + struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); - if (!sysfs_initialized) - return; + if (pci_legacy_has_sparse(bus, type) != sparse) + return 0; - b->legacy_io = kzalloc_objs(struct bin_attribute, 2, GFP_ATOMIC); - if (!b->legacy_io) - goto kzalloc_err; - - sysfs_bin_attr_init(b->legacy_io); - b->legacy_io->attr.name = "legacy_io"; - b->legacy_io->size = 0xffff; - b->legacy_io->attr.mode = 0600; - b->legacy_io->read = pci_read_legacy_io; - b->legacy_io->write = pci_write_legacy_io; - /* See pci_create_attr() for motivation */ - b->legacy_io->llseek = pci_llseek_resource; - b->legacy_io->mmap = pci_mmap_legacy_io; - b->legacy_io->f_mapping = iomem_get_mapping; - pci_adjust_legacy_attr(b, pci_mmap_io); - error = device_create_bin_file(&b->dev, b->legacy_io); - if (error) - goto legacy_io_err; - - /* Allocated above after the legacy_io struct */ - b->legacy_mem = b->legacy_io + 1; - sysfs_bin_attr_init(b->legacy_mem); - b->legacy_mem->attr.name = "legacy_mem"; - b->legacy_mem->size = 1024*1024; - b->legacy_mem->attr.mode = 0600; - b->legacy_mem->mmap = pci_mmap_legacy_mem; - /* See pci_create_attr() for motivation */ - b->legacy_mem->llseek = pci_llseek_resource; - b->legacy_mem->f_mapping = iomem_get_mapping; - pci_adjust_legacy_attr(b, pci_mmap_mem); - error = device_create_bin_file(&b->dev, b->legacy_mem); - if (error) - goto legacy_mem_err; - - return; - -legacy_mem_err: - device_remove_bin_file(&b->dev, b->legacy_io); -legacy_io_err: - kfree(b->legacy_io); - b->legacy_io = NULL; -kzalloc_err: - dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n"); + return a->attr.mode; } -void pci_remove_legacy_files(struct pci_bus *b) +static umode_t pci_legacy_io_is_visible(struct kobject *kobj, + const struct bin_attribute *a, int n) { - if (b->legacy_io) { - device_remove_bin_file(&b->dev, b->legacy_io); - device_remove_bin_file(&b->dev, b->legacy_mem); - kfree(b->legacy_io); /* both are allocated here */ - } + return __pci_legacy_is_visible(kobj, a, pci_mmap_io, false); } + +static umode_t pci_legacy_io_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_legacy_is_visible(kobj, a, pci_mmap_io, true); +} + +static umode_t pci_legacy_mem_is_visible(struct kobject *kobj, + const struct bin_attribute *a, int n) +{ + return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, false); +} + +static umode_t pci_legacy_mem_sparse_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, true); +} + +static loff_t pci_llseek_resource_legacy(struct file *filep, + struct kobject *kobj __always_unused, + const struct bin_attribute *attr, + loff_t offset, int whence) +{ + return fixed_size_llseek(filep, offset, whence, attr->size); +} + +static const struct bin_attribute pci_legacy_io_attr = { + .attr = { .name = "legacy_io", .mode = 0600 }, + .size = PCI_LEGACY_IO_SIZE, + .read = pci_read_legacy_io, + .write = pci_write_legacy_io, + .mmap = pci_mmap_legacy_io, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_io_sparse_attr = { + .attr = { .name = "legacy_io_sparse", .mode = 0600 }, + .size = PCI_LEGACY_IO_SIZE << 5, + .read = pci_read_legacy_io, + .write = pci_write_legacy_io, + .mmap = pci_mmap_legacy_io, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_mem_attr = { + .attr = { .name = "legacy_mem", .mode = 0600 }, + .size = PCI_LEGACY_MEM_SIZE, + .mmap = pci_mmap_legacy_mem, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute pci_legacy_mem_sparse_attr = { + .attr = { .name = "legacy_mem_sparse", .mode = 0600 }, + .size = PCI_LEGACY_MEM_SIZE << 5, + .mmap = pci_mmap_legacy_mem, + .llseek = pci_llseek_resource_legacy, + .f_mapping = iomem_get_mapping, +}; + +static const struct bin_attribute *const pci_legacy_io_attrs[] = { + &pci_legacy_io_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_io_sparse_attrs[] = { + &pci_legacy_io_sparse_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_mem_attrs[] = { + &pci_legacy_mem_attr, + NULL, +}; + +static const struct bin_attribute *const pci_legacy_mem_sparse_attrs[] = { + &pci_legacy_mem_sparse_attr, + NULL, +}; + +static const struct attribute_group pci_legacy_io_group = { + .bin_attrs = pci_legacy_io_attrs, + .is_bin_visible = pci_legacy_io_is_visible, +}; + +static const struct attribute_group pci_legacy_io_sparse_group = { + .bin_attrs = pci_legacy_io_sparse_attrs, + .is_bin_visible = pci_legacy_io_sparse_is_visible, +}; + +static const struct attribute_group pci_legacy_mem_group = { + .bin_attrs = pci_legacy_mem_attrs, + .is_bin_visible = pci_legacy_mem_is_visible, +}; + +static const struct attribute_group pci_legacy_mem_sparse_group = { + .bin_attrs = pci_legacy_mem_sparse_attrs, + .is_bin_visible = pci_legacy_mem_sparse_is_visible, +}; + #endif /* HAVE_PCI_LEGACY */ +const struct attribute_group *pcibus_groups[] = { + &pcibus_group, +#ifdef HAVE_PCI_LEGACY + &pci_legacy_io_group, + &pci_legacy_io_sparse_group, + &pci_legacy_mem_group, + &pci_legacy_mem_sparse_group, +#endif + NULL, +}; + #if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) /** * pci_mmap_resource - map a PCI resource into user memory space @@ -1082,20 +1119,24 @@ static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *a struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int bar = (unsigned long)attr->private; enum pci_mmap_state mmap_type; - struct resource *res = &pdev->resource[bar]; int ret; ret = security_locked_down(LOCKDOWN_PCI_ACCESS); if (ret) return ret; - if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) + if (!pci_resource_is_mem(pdev, bar) && + !(pci_resource_is_io(pdev, bar) && arch_can_pci_mmap_io())) + return -EIO; + + if (pci_resource_is_mem(pdev, bar) && + iomem_is_exclusive(pci_resource_start(pdev, bar))) return -EINVAL; if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS)) return -EINVAL; - mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + mmap_type = pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io; return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); } @@ -1123,6 +1164,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, int bar = (unsigned long)attr->private; unsigned long port = off; + if (!pci_resource_is_io(pdev, bar)) + return -EIO; + port += pci_resource_start(pdev, bar); if (port > pci_resource_end(pdev, bar)) @@ -1157,14 +1201,14 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, #endif } -static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj, +static ssize_t pci_read_resource(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { return pci_resource_io(filp, kobj, attr, buf, off, count, false); } -static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, +static ssize_t pci_write_resource(struct file *filp, struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { @@ -1177,127 +1221,192 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, return pci_resource_io(filp, kobj, attr, buf, off, count, true); } -/** - * pci_remove_resource_files - cleanup resource files - * @pdev: dev to cleanup - * - * If we created resource files for @pdev, remove them from sysfs and - * free their resources. - */ -static void pci_remove_resource_files(struct pci_dev *pdev) +static loff_t pci_llseek_resource(struct file *filep, + struct kobject *kobj, + const struct bin_attribute *attr, + loff_t offset, int whence) { - int i; + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + int bar = (unsigned long)attr->private; - for (i = 0; i < PCI_STD_NUM_BARS; i++) { - struct bin_attribute *res_attr; - - res_attr = pdev->res_attr[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - - res_attr = pdev->res_attr_wc[i]; - if (res_attr) { - sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); - kfree(res_attr); - } - } + return fixed_size_llseek(filep, offset, whence, + pci_resource_len(pdev, bar)); } -static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) -{ - /* allocate attribute structure, piggyback attribute name */ - int name_len = write_combine ? 13 : 10; - struct bin_attribute *res_attr; - char *res_attr_name; - int retval; +/* + * generic_file_llseek() consults f_mapping->host to determine + * the file size. As iomem_inode knows nothing about the + * attribute, it's not going to work, so override it as well. + */ +#if arch_can_pci_mmap_io() +# define __PCI_RESOURCE_IO_MMAP_ATTRS \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_uc, +#else +# define __PCI_RESOURCE_IO_MMAP_ATTRS +#endif - res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); - if (!res_attr) - return -ENOMEM; - - res_attr_name = (char *)(res_attr + 1); - - sysfs_bin_attr_init(res_attr); - if (write_combine) { - sprintf(res_attr_name, "resource%d_wc", num); - res_attr->mmap = pci_mmap_resource_wc; - } else { - sprintf(res_attr_name, "resource%d", num); - if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { - res_attr->read = pci_read_resource_io; - res_attr->write = pci_write_resource_io; - if (arch_can_pci_mmap_io()) - res_attr->mmap = pci_mmap_resource_uc; - } else { - res_attr->mmap = pci_mmap_resource_uc; - } - } - if (res_attr->mmap) { - res_attr->f_mapping = iomem_get_mapping; - /* - * generic_file_llseek() consults f_mapping->host to determine - * the file size. As iomem_inode knows nothing about the - * attribute, it's not going to work, so override it as well. - */ - res_attr->llseek = pci_llseek_resource; - } - res_attr->attr.name = res_attr_name; - res_attr->attr.mode = 0600; - res_attr->size = pci_resource_len(pdev, num); - res_attr->private = (void *)(unsigned long)num; - retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); - if (retval) { - kfree(res_attr); - return retval; - } - - if (write_combine) - pdev->res_attr_wc[num] = res_attr; - else - pdev->res_attr[num] = res_attr; - - return 0; +#define pci_dev_resource_io_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_io_attr = { \ + .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .read = pci_read_resource, \ + .write = pci_write_resource, \ + __PCI_RESOURCE_IO_MMAP_ATTRS \ } -/** - * pci_create_resource_files - create resource files in sysfs for @dev - * @pdev: dev in question - * - * Walk the resources in @pdev creating files for each resource available. - */ -static int pci_create_resource_files(struct pci_dev *pdev) -{ - int i; - int retval; +#define pci_dev_resource_uc_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_uc_attr = { \ + .attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_uc, \ +} + +#define pci_dev_resource_wc_attr(_bar) \ +static const struct bin_attribute dev_resource##_bar##_wc_attr = { \ + .attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \ + .private = (void *)(unsigned long)(_bar), \ + .f_mapping = iomem_get_mapping, \ + .llseek = pci_llseek_resource, \ + .mmap = pci_mmap_resource_wc, \ +} + +static inline umode_t +__pci_resource_attr_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int bar, bool write_combine, + unsigned long flags) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); - /* Skip devices with non-mappable BARs */ if (pdev->non_mappable_bars) return 0; - /* Expose the PCI resources from this device as files */ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { + if (!pci_resource_len(pdev, bar)) + return 0; - /* skip empty resources */ - if (!pci_resource_len(pdev, i)) - continue; + if ((pci_resource_flags(pdev, bar) & flags) != flags) + return 0; - retval = pci_create_attr(pdev, i, 0); - /* for prefetchable resources, create a WC mappable file */ - if (!retval && arch_can_pci_mmap_wc() && - pdev->resource[i].flags & IORESOURCE_PREFETCH) - retval = pci_create_attr(pdev, i, 1); - if (retval) { - pci_remove_resource_files(pdev); - return retval; - } - } - return 0; + if (write_combine && !arch_can_pci_mmap_wc()) + return 0; + + return a->attr.mode; } -#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */ -int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } -void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } + +static umode_t pci_dev_resource_io_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, false, + IORESOURCE_IO); +} + +static umode_t pci_dev_resource_uc_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, false, + IORESOURCE_MEM); +} + +static umode_t pci_dev_resource_wc_is_visible(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + return __pci_resource_attr_is_visible(kobj, a, n, true, + IORESOURCE_MEM | IORESOURCE_PREFETCH); +} + +static size_t pci_dev_resource_bin_size(struct kobject *kobj, + const struct bin_attribute *a, + int n) +{ + struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + + return pci_resource_len(pdev, n); +} + +pci_dev_resource_io_attr(0); +pci_dev_resource_io_attr(1); +pci_dev_resource_io_attr(2); +pci_dev_resource_io_attr(3); +pci_dev_resource_io_attr(4); +pci_dev_resource_io_attr(5); + +pci_dev_resource_uc_attr(0); +pci_dev_resource_uc_attr(1); +pci_dev_resource_uc_attr(2); +pci_dev_resource_uc_attr(3); +pci_dev_resource_uc_attr(4); +pci_dev_resource_uc_attr(5); + +pci_dev_resource_wc_attr(0); +pci_dev_resource_wc_attr(1); +pci_dev_resource_wc_attr(2); +pci_dev_resource_wc_attr(3); +pci_dev_resource_wc_attr(4); +pci_dev_resource_wc_attr(5); + +static const struct bin_attribute *const pci_dev_resource_io_attrs[] = { + &dev_resource0_io_attr, + &dev_resource1_io_attr, + &dev_resource2_io_attr, + &dev_resource3_io_attr, + &dev_resource4_io_attr, + &dev_resource5_io_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = { + &dev_resource0_uc_attr, + &dev_resource1_uc_attr, + &dev_resource2_uc_attr, + &dev_resource3_uc_attr, + &dev_resource4_uc_attr, + &dev_resource5_uc_attr, + NULL, +}; + +static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = { + &dev_resource0_wc_attr, + &dev_resource1_wc_attr, + &dev_resource2_wc_attr, + &dev_resource3_wc_attr, + &dev_resource4_wc_attr, + &dev_resource5_wc_attr, + NULL, +}; + +static const struct attribute_group pci_dev_resource_io_attr_group = { + .bin_attrs = pci_dev_resource_io_attrs, + .is_bin_visible = pci_dev_resource_io_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group pci_dev_resource_uc_attr_group = { + .bin_attrs = pci_dev_resource_uc_attrs, + .is_bin_visible = pci_dev_resource_uc_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group pci_dev_resource_wc_attr_group = { + .bin_attrs = pci_dev_resource_wc_attrs, + .is_bin_visible = pci_dev_resource_wc_is_visible, + .bin_size = pci_dev_resource_bin_size, +}; + +static const struct attribute_group *pci_dev_resource_attr_groups[] = { + &pci_dev_resource_io_attr_group, + &pci_dev_resource_uc_attr_group, + &pci_dev_resource_wc_attr_group, + NULL, +}; +#else +#define pci_dev_resource_attr_groups NULL #endif /** @@ -1309,18 +1418,19 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } * @off: file offset * @count: number of byte in input * - * writing anything except 0 enables it + * Writing a boolean value enables or disables the ROM display. */ static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); + bool enable; - if ((off == 0) && (*buf == '0') && (count == 2)) - pdev->rom_attr_enabled = 0; - else - pdev->rom_attr_enabled = 1; + if (kstrtobool(buf, &enable)) + return -EINVAL; + + pdev->rom_attr_enabled = enable; return count; } @@ -1552,6 +1662,7 @@ static const struct attribute_group pci_dev_reset_method_attr_group = { .is_visible = pci_dev_reset_attr_is_visible, }; +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) static ssize_t __resource_resize_show(struct device *dev, int n, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); @@ -1576,6 +1687,9 @@ static ssize_t __resource_resize_store(struct device *dev, int n, int ret; u16 cmd; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (kstrtoul(buf, 0, &size) < 0) return -EINVAL; @@ -1598,14 +1712,17 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_write_config_word(pdev, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); - pci_remove_resource_files(pdev); + sysfs_remove_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups); ret = pci_resize_resource(pdev, n, size, 0); + if (ret) + pci_warn(pdev, "Failed to resize BAR %d: %pe\n", + n, ERR_PTR(ret)); pci_assign_unassigned_bus_resources(bus); - if (pci_create_resource_files(pdev)) - pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n"); + if (sysfs_create_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups)) + pci_warn(pdev, "Failed to recreate resource groups after BAR resizing\n"); pci_write_config_word(pdev, PCI_COMMAND, cmd); pm_put: @@ -1648,7 +1765,7 @@ static struct attribute *resource_resize_attrs[] = { NULL, }; -static umode_t resource_resize_is_visible(struct kobject *kobj, +static umode_t resource_resize_attr_is_visible(struct kobject *kobj, struct attribute *a, int n) { struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); @@ -1656,54 +1773,11 @@ static umode_t resource_resize_is_visible(struct kobject *kobj, return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode; } -static const struct attribute_group pci_dev_resource_resize_group = { +static const struct attribute_group pci_dev_resource_resize_attr_group = { .attrs = resource_resize_attrs, - .is_visible = resource_resize_is_visible, + .is_visible = resource_resize_attr_is_visible, }; - -int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) -{ - if (!sysfs_initialized) - return -EACCES; - - return pci_create_resource_files(pdev); -} - -/** - * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files - * @pdev: device whose entries we should free - * - * Cleanup when @pdev is removed from sysfs. - */ -void pci_remove_sysfs_dev_files(struct pci_dev *pdev) -{ - if (!sysfs_initialized) - return; - - pci_remove_resource_files(pdev); -} - -static int __init pci_sysfs_init(void) -{ - struct pci_dev *pdev = NULL; - struct pci_bus *pbus = NULL; - int retval; - - sysfs_initialized = 1; - for_each_pci_dev(pdev) { - retval = pci_create_sysfs_dev_files(pdev); - if (retval) { - pci_dev_put(pdev); - return retval; - } - } - - while ((pbus = pci_find_next_bus(pbus))) - pci_create_legacy_files(pbus); - - return 0; -} -late_initcall(pci_sysfs_init); +#endif static struct attribute *pci_dev_dev_attrs[] = { &dev_attr_boot_vga.attr, @@ -1774,6 +1848,12 @@ static const struct attribute_group pci_dev_group = { const struct attribute_group *pci_dev_groups[] = { &pci_dev_group, +#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) + &pci_dev_resource_io_attr_group, + &pci_dev_resource_uc_attr_group, + &pci_dev_resource_wc_attr_group, + &pci_dev_resource_resize_attr_group, +#endif &pci_dev_config_attr_group, &pci_dev_rom_attr_group, &pci_dev_reset_attr_group, @@ -1785,7 +1865,6 @@ const struct attribute_group *pci_dev_groups[] = { #ifdef CONFIG_ACPI &pci_dev_acpi_attr_group, #endif - &pci_dev_resource_resize_group, ARCH_PCI_DEV_GROUPS NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d34266651ad0..77b17b13ee61 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "pci.h" DEFINE_MUTEX(pci_slot_mutex); @@ -120,17 +121,7 @@ unsigned long pci_hotplug_bus_size = DEFAULT_HOTPLUG_BUS_SIZE; /* PCIe MPS/MRRS strategy; can be overridden by kernel command-line param */ -#ifdef CONFIG_PCIE_BUS_TUNE_OFF -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF; -#elif defined CONFIG_PCIE_BUS_SAFE -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_SAFE; -#elif defined CONFIG_PCIE_BUS_PERFORMANCE -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE; -#elif defined CONFIG_PCIE_BUS_PEER2PEER -enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PEER2PEER; -#else enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT; -#endif /* * The default CLS is used if arch didn't set CLS explicitly and not @@ -1253,8 +1244,8 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) } if (delay > timeout) { - pci_warn(dev, "not ready %dms after %s; giving up\n", - delay - 1, reset_type); + pci_err(dev, "not ready %dms after %s; giving up\n", + delay - 1, reset_type); return -ENOTTY; } @@ -1300,7 +1291,18 @@ int pci_power_up(struct pci_dev *dev) bool need_restore; pci_power_t state; u16 pmcsr; + int ret; + /* + * When setting power state to D0, platform_pci_set_power_state() + * ensures main power is on. If it puts the device in D0, it also + * completes any required delays after the transition; if it leaves + * the device in D1, D2, or D3hot, we use the PM Capability to + * transition to D0. + * + * In all cases, the device is either Configuration-Ready or + * inaccessible upon return. + */ platform_pci_set_power_state(dev, PCI_D0); if (!dev->pm_cap) { @@ -1341,10 +1343,19 @@ int pci_power_up(struct pci_dev *dev) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0); /* Mandatory transition delays; see PCI PM 1.2. */ - if (state == PCI_D3hot) + if (state == PCI_D3hot) { pci_dev_d3_sleep(dev); - else if (state == PCI_D2) + if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) { + ret = pci_dev_wait(dev, "power up D3hot->D0uninitialized", + PCIE_RESET_READY_POLL_MS); + if (ret) { + dev->current_state = PCI_D3cold; + return -EIO; + } + } + } else if (state == PCI_D2) { udelay(PCI_PM_D2_DELAY); + } end: dev->current_state = PCI_D0; @@ -1514,9 +1525,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool pci_power_name(dev->current_state), pci_power_name(state)); - if (dev->bus->self) - pcie_aspm_pm_state_change(dev->bus->self, locked); - return 0; } @@ -1764,7 +1772,7 @@ int pci_save_state(struct pci_dev *dev) EXPORT_SYMBOL(pci_save_state); static void pci_restore_config_dword(struct pci_dev *pdev, int offset, - u32 saved_val, int retry, bool force) + u32 saved_val, bool force) { u32 val; @@ -1772,52 +1780,42 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset, if (!force && val == saved_val) return; - for (;;) { - pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", - offset, val, saved_val); - pci_write_config_dword(pdev, offset, saved_val); - if (retry-- <= 0) - return; + pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", offset, val, + saved_val); - pci_read_config_dword(pdev, offset, &val); - if (val == saved_val) - return; - - mdelay(1); - } + pci_write_config_dword(pdev, offset, saved_val); } static void pci_restore_config_space_range(struct pci_dev *pdev, - int start, int end, int retry, - bool force) + int start, int end, bool force) { int index; for (index = end; index >= start; index--) pci_restore_config_dword(pdev, 4 * index, pdev->saved_config_space[index], - retry, force); + force); } static void pci_restore_config_space(struct pci_dev *pdev) { if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) { - pci_restore_config_space_range(pdev, 10, 15, 0, false); + pci_restore_config_space_range(pdev, 10, 15, false); /* Restore BARs before the command register. */ - pci_restore_config_space_range(pdev, 4, 9, 10, false); - pci_restore_config_space_range(pdev, 0, 3, 0, false); + pci_restore_config_space_range(pdev, 4, 9, false); + pci_restore_config_space_range(pdev, 0, 3, false); } else if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - pci_restore_config_space_range(pdev, 12, 15, 0, false); + pci_restore_config_space_range(pdev, 12, 15, false); /* * Force rewriting of prefetch registers to avoid S3 resume * issues on Intel PCI bridges that occur when these * registers are not explicitly written. */ - pci_restore_config_space_range(pdev, 9, 11, 0, true); - pci_restore_config_space_range(pdev, 0, 8, 0, false); + pci_restore_config_space_range(pdev, 9, 11, true); + pci_restore_config_space_range(pdev, 0, 8, false); } else { - pci_restore_config_space_range(pdev, 0, 15, 0, false); + pci_restore_config_space_range(pdev, 0, 15, false); } } @@ -2899,6 +2897,40 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev) pm_runtime_put_sync(parent); } +/** + * pci_suspend_retains_context - Check if the platform can retain the device + * context during system suspend + * @pdev: PCI device to check + * + * Return: true if the platform can guarantee to retain the device context, + * false otherwise. + */ +bool pci_suspend_retains_context(struct pci_dev *pdev) +{ + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); + + /* + * If the platform firmware (like ACPI) is involved at the end of + * system suspend, device context may not be retained. + */ + if (pm_suspend_via_firmware()) + return false; + + /* + * Some host bridges power off the PHY to enter deep low-power + * modes during system suspend. Exiting L1SS from this condition + * may violate timing requirements and result in Link Down (LDn), + * which causes a reset of the device. On such platforms, the + * endpoint must be prepared for context loss. + */ + if (bridge && bridge->broken_l1ss_resume) + return false; + + /* Assume that the context is retained by default */ + return true; +} +EXPORT_SYMBOL_GPL(pci_suspend_retains_context); + static const struct dmi_system_id bridge_d3_blacklist[] = { #ifdef CONFIG_X86 { @@ -5770,8 +5802,7 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc) if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) return -EIO; - cmd &= ~PCI_X_CMD_MAX_READ; - cmd |= FIELD_PREP(PCI_X_CMD_MAX_READ, v); + FIELD_MODIFY(PCI_X_CMD_MAX_READ, &cmd, v); if (pci_write_config_word(dev, cap + PCI_X_CMD, cmd)) return -EIO; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e8ad27abb1cf..4469e1a77f3c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -71,10 +71,11 @@ struct pcie_tlp_log; #define PCIE_TLP_FMT_4DW_DATA 0x03 /* 4DW header, with data */ /* Type of TLP; PCIe r7.0, sec 2.2.1 */ -#define PCIE_TLP_TYPE_CFG0_RD 0x04 /* Config Type 0 Read Request */ -#define PCIE_TLP_TYPE_CFG0_WR 0x04 /* Config Type 0 Write Request */ -#define PCIE_TLP_TYPE_CFG1_RD 0x05 /* Config Type 1 Read Request */ -#define PCIE_TLP_TYPE_CFG1_WR 0x05 /* Config Type 1 Write Request */ +#define PCIE_TLP_TYPE_MEM_RDWR 0x00 /* Memory Read/Write Request */ +#define PCIE_TLP_TYPE_IO_RDWR 0x02 /* I/O Read/Write Request */ +#define PCIE_TLP_TYPE_CFG0_RDWR 0x04 /* Config Type 0 Read/Write Request */ +#define PCIE_TLP_TYPE_CFG1_RDWR 0x05 /* Config Type 1 Read/Write Request */ +#define PCIE_TLP_TYPE_MSG 0x10 /* Message With/Without data Request */ /* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */ #define PCIE_MSG_TYPE_R_RC 0 @@ -358,14 +359,6 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; } int pci_hp_add_bridge(struct pci_dev *dev); bool pci_hp_spurious_link_change(struct pci_dev *pdev); -#if defined(CONFIG_SYSFS) && defined(HAVE_PCI_LEGACY) -void pci_create_legacy_files(struct pci_bus *bus); -void pci_remove_legacy_files(struct pci_bus *bus); -#else -static inline void pci_create_legacy_files(struct pci_bus *bus) { } -static inline void pci_remove_legacy_files(struct pci_bus *bus) { } -#endif - /* Lock for read/write access to pci device and bus lists */ extern struct rw_semaphore pci_bus_sem; extern struct mutex pci_slot_mutex; @@ -392,17 +385,17 @@ static inline int pci_no_d1d2(struct pci_dev *dev) } +#ifdef HAVE_PCI_LEGACY +bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type); +#endif + #ifdef CONFIG_SYSFS -int pci_create_sysfs_dev_files(struct pci_dev *pdev); -void pci_remove_sysfs_dev_files(struct pci_dev *pdev); extern const struct attribute_group *pci_dev_groups[]; extern const struct attribute_group *pci_dev_attr_groups[]; extern const struct attribute_group *pcibus_groups[]; extern const struct attribute_group *pci_bus_groups[]; extern const struct attribute_group pci_doe_sysfs_group; #else -static inline int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; } -static inline void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { } #define pci_dev_groups NULL #define pci_dev_attr_groups NULL #define pcibus_groups NULL @@ -419,7 +412,7 @@ static inline bool pci_is_cardbus_bridge(struct pci_dev *dev) return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; } #ifdef CONFIG_CARDBUS -unsigned long pci_cardbus_resource_alignment(struct resource *res); +unsigned long pci_cardbus_resource_alignment(const struct resource *res); int pci_bus_size_cardbus_bridge(struct pci_bus *bus, struct list_head *realloc_head); int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, @@ -428,7 +421,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int pci_setup_cardbus(char *str); #else -static inline unsigned long pci_cardbus_resource_alignment(struct resource *res) +static inline unsigned long pci_cardbus_resource_alignment(const struct resource *res) { return 0; } @@ -515,7 +508,7 @@ int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); void __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *realloc_head, + struct list_head *add_list, struct list_head *fail_head); bool pci_bus_clip_resource(struct pci_dev *dev, int idx); void pci_walk_bus_locked(struct pci_bus *top, @@ -947,7 +940,8 @@ int pci_iov_init(struct pci_dev *dev); void pci_iov_release(struct pci_dev *dev); void pci_iov_remove(struct pci_dev *dev); void pci_iov_update_resource(struct pci_dev *dev, int resno); -resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno); +resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, + int resno); void pci_restore_iov_state(struct pci_dev *dev); int pci_iov_bus_range(struct pci_bus *bus); void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size); @@ -981,7 +975,7 @@ static inline int pci_iov_init(struct pci_dev *dev) static inline void pci_iov_release(struct pci_dev *dev) { } static inline void pci_iov_remove(struct pci_dev *dev) { } static inline void pci_iov_update_resource(struct pci_dev *dev, int resno) { } -static inline resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, +static inline resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev, int resno) { return 0; @@ -1043,17 +1037,8 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { } static inline void pci_resume_ptm(struct pci_dev *dev) { } #endif -static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, - struct resource *res) -{ - int resno = pci_resource_num(dev, res); - - if (pci_resource_is_iov(resno)) - return pci_sriov_resource_alignment(dev, resno); - if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) - return pci_cardbus_resource_alignment(res); - return resource_alignment(res); -} +resource_size_t pci_resource_alignment(const struct pci_dev *dev, + const struct resource *res); resource_size_t pci_min_window_alignment(struct pci_bus *bus, unsigned long type); @@ -1110,6 +1095,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked); void pcie_aspm_powersave_config_link(struct pci_dev *pdev); void pci_configure_ltr(struct pci_dev *pdev); void pci_bridge_reconfigure_ltr(struct pci_dev *pdev); +void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value); #else static inline void pcie_aspm_remove_cap(struct pci_dev *pdev, u32 lnkcap) { } static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { } @@ -1118,6 +1104,11 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked) static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { } static inline void pci_configure_ltr(struct pci_dev *pdev) { } static inline void pci_bridge_reconfigure_ltr(struct pci_dev *pdev) { } +static inline void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value) +{ + *scale = 0; + *value = 0; +} #endif #ifdef CONFIG_PCIE_ECRC diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 925373b98dff..172783e7f519 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -525,6 +525,46 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val) return 0; } +/** + * pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields + * @t_power_on_us: T_POWER_ON time in microseconds + * @scale: Encoded T_POWER_ON Scale (0..2) + * @value: Encoded T_POWER_ON Value + * + * T_POWER_ON is encoded as: + * T_POWER_ON(us) = scale_unit(us) * value + * + * where scale_unit is selected by @scale: + * 0: 2us + * 1: 10us + * 2: 100us + * + * If @t_power_on_us exceeds the maximum representable value, the result + * is clamped to the largest encodable T_POWER_ON. + * + * See PCIe r7.0, sec 7.8.3.2. + */ +void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value) +{ + u8 maxv = FIELD_MAX(PCI_L1SS_CAP_P_PWR_ON_VALUE); + + /* T_POWER_ON_Value ("value") is a 5-bit field with max value of 31. */ + if (t_power_on_us <= 2 * maxv) { + *scale = 0; /* Value times 2us */ + *value = DIV_ROUND_UP(t_power_on_us, 2); + } else if (t_power_on_us <= 10 * maxv) { + *scale = 1; /* Value times 10us */ + *value = DIV_ROUND_UP(t_power_on_us, 10); + } else if (t_power_on_us <= 100 * maxv) { + *scale = 2; /* value times 100us */ + *value = DIV_ROUND_UP(t_power_on_us, 100); + } else { + *scale = 2; + *value = maxv; + } +} +EXPORT_SYMBOL(pcie_encode_t_power_on); + /* * Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1 * register. Ports enter L1.2 when the most recent LTR value is greater diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index a41ffd1914de..bd3bd39f6372 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -152,8 +152,7 @@ static int __pci_enable_ptm(struct pci_dev *dev) pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl); ctrl |= PCI_PTM_CTRL_ENABLE; - ctrl &= ~PCI_PTM_GRANULARITY_MASK; - ctrl |= FIELD_PREP(PCI_PTM_GRANULARITY_MASK, dev->ptm_granularity); + FIELD_MODIFY(PCI_PTM_GRANULARITY_MASK, &ctrl, dev->ptm_granularity); if (dev->ptm_root) ctrl |= PCI_PTM_CTRL_ROOT; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc..dd0abbc63e18 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -660,6 +660,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge) { INIT_LIST_HEAD(&bridge->windows); INIT_LIST_HEAD(&bridge->dma_ranges); + INIT_LIST_HEAD(&bridge->ports); /* * We assume we can manage these PCIe features. Some systems may @@ -1073,9 +1074,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) dev_err(&bus->dev, "failed to add bus: %d\n", err); } - /* Create legacy_io and legacy_mem files for this bus */ - pci_create_legacy_files(bus); - if (parent) dev_info(parent, "PCI host bridge to bus %s\n", name); else @@ -1281,9 +1279,6 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, dev_err(&child->dev, "failed to add bus: %d\n", ret); } - /* Create legacy_io and legacy_mem files for this bus */ - pci_create_legacy_files(child); - return child; } diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index ce36e35681e8..71ad289fcb8e 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -18,6 +18,7 @@ #include "pci.h" static int proc_initialized; /* = 0 */ +static DEFINE_MUTEX(pci_proc_lock); static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence) { @@ -416,40 +417,64 @@ static const struct seq_operations proc_bus_pci_devices_op = { static struct proc_dir_entry *proc_bus_pci_dir; -int pci_proc_attach_device(struct pci_dev *dev) +static int __pci_proc_attach_bus(struct pci_bus *bus) { - struct pci_bus *bus = dev->bus; - struct proc_dir_entry *e; + struct proc_dir_entry *dir; char name[16]; + lockdep_assert_held(&pci_proc_lock); + if (!proc_initialized) return -EACCES; - if (!bus->procdir) { - if (pci_proc_domain(bus)) { - sprintf(name, "%04x:%02x", pci_domain_nr(bus), - bus->number); - } else { - sprintf(name, "%02x", bus->number); - } - bus->procdir = proc_mkdir(name, proc_bus_pci_dir); - if (!bus->procdir) - return -ENOMEM; - } + if (bus->procdir) + return 0; + + if (pci_proc_domain(bus)) + sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number); + else + sprintf(name, "%02x", bus->number); + + dir = proc_mkdir(name, proc_bus_pci_dir); + if (!dir) + return -ENOMEM; + + bus->procdir = dir; + + return 0; +} + +int pci_proc_attach_device(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct proc_dir_entry *entry; + char name[16]; + int ret; + + guard(mutex)(&pci_proc_lock); + + if (dev->procent) + return 0; + + ret = __pci_proc_attach_bus(bus); + if (ret) + return ret; sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir, - &proc_bus_pci_ops, dev); - if (!e) + entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, + bus->procdir, &proc_bus_pci_ops, dev); + if (!entry) return -ENOMEM; - proc_set_size(e, dev->cfg_size); - dev->procent = e; + + proc_set_size(entry, dev->cfg_size); + dev->procent = entry; return 0; } int pci_proc_detach_device(struct pci_dev *dev) { + guard(mutex)(&pci_proc_lock); proc_remove(dev->procent); dev->procent = NULL; return 0; @@ -457,19 +482,27 @@ int pci_proc_detach_device(struct pci_dev *dev) int pci_proc_detach_bus(struct pci_bus *bus) { + guard(mutex)(&pci_proc_lock); proc_remove(bus->procdir); + bus->procdir = NULL; return 0; } static int __init pci_proc_init(void) { struct pci_dev *dev = NULL; - proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); - proc_create_seq("devices", 0, proc_bus_pci_dir, - &proc_bus_pci_devices_op); - proc_initialized = 1; + + scoped_guard(mutex, &pci_proc_lock) { + proc_bus_pci_dir = proc_mkdir("bus/pci", NULL); + proc_create_seq("devices", 0, proc_bus_pci_dir, + &proc_bus_pci_devices_op); + proc_initialized = 1; + } + + pci_lock_rescan_remove(); for_each_pci_dev(dev) pci_proc_attach_device(dev); + pci_unlock_rescan_remove(); return 0; } diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 31246bac84f1..10d817aefb09 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -139,6 +139,48 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +/* + * Check whether the pwrctrl device really needs to be created or not. The + * pwrctrl device will only be created if the node satisfies below requirements: + * + * 1. Presence of compatible property with "pci" prefix to match against the + * pwrctrl driver (AND) + * 2. At least one of the power supplies defined in the devicetree node of the + * device (OR) in the remote endpoint parent node to indicate pwrctrl + * requirement. + */ +static bool pci_pwrctrl_is_required(struct device_node *np) +{ + struct device_node *endpoint; + const char *compat; + int ret; + + ret = of_property_read_string(np, "compatible", &compat); + if (ret < 0) + return false; + + if (!strstarts(compat, "pci")) + return false; + + if (of_pci_supply_present(np)) + return true; + + if (of_graph_is_present(np)) { + for_each_endpoint_of_node(np, endpoint) { + struct device_node *remote __free(device_node) = + of_graph_get_remote_port_parent(endpoint); + if (remote) { + if (of_pci_supply_present(remote)) { + of_node_put(endpoint); + return true; + } + } + } + } + + return false; +} + static int __pci_pwrctrl_power_off_device(struct device *dev) { struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); @@ -157,14 +199,19 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) for_each_available_child_of_node_scoped(np, child) pci_pwrctrl_power_off_device(child); + if (!pci_pwrctrl_is_required(np)) + return; + pdev = of_find_device_by_node(np); if (!pdev) return; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_off_device(&pdev->dev); - if (ret) - dev_err(&pdev->dev, "Failed to power off device: %d", ret); + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_off_device(&pdev->dev); + if (ret) + dev_err(&pdev->dev, "Failed to power off device: %d", ret); + } } platform_device_put(pdev); @@ -205,7 +252,7 @@ static int __pci_pwrctrl_power_on_device(struct device *dev) static int pci_pwrctrl_power_on_device(struct device_node *np) { struct platform_device *pdev; - int ret; + int ret = 0; for_each_available_child_of_node_scoped(np, child) { ret = pci_pwrctrl_power_on_device(child); @@ -213,16 +260,21 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) return ret; } + if (!pci_pwrctrl_is_required(np)) + return 0; + pdev = of_find_device_by_node(np); if (!pdev) return 0; - if (device_is_bound(&pdev->dev)) { - ret = __pci_pwrctrl_power_on_device(&pdev->dev); - } else { - /* FIXME: Use blocking wait instead of probe deferral */ - dev_dbg(&pdev->dev, "driver is not bound\n"); - ret = -EPROBE_DEFER; + scoped_guard(device, &pdev->dev) { + if (device_is_bound(&pdev->dev)) { + ret = __pci_pwrctrl_power_on_device(&pdev->dev); + } else { + /* FIXME: Use blocking wait instead of probe deferral */ + dev_dbg(&pdev->dev, "driver is not bound\n"); + ret = -EPROBE_DEFER; + } } platform_device_put(pdev); @@ -268,48 +320,6 @@ int pci_pwrctrl_power_on_devices(struct device *parent) } EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); -/* - * Check whether the pwrctrl device really needs to be created or not. The - * pwrctrl device will only be created if the node satisfies below requirements: - * - * 1. Presence of compatible property with "pci" prefix to match against the - * pwrctrl driver (AND) - * 2. At least one of the power supplies defined in the devicetree node of the - * device (OR) in the remote endpoint parent node to indicate pwrctrl - * requirement. - */ -static bool pci_pwrctrl_is_required(struct device_node *np) -{ - struct device_node *endpoint; - const char *compat; - int ret; - - ret = of_property_read_string(np, "compatible", &compat); - if (ret < 0) - return false; - - if (!strstarts(compat, "pci")) - return false; - - if (of_pci_supply_present(np)) - return true; - - if (of_graph_is_present(np)) { - for_each_endpoint_of_node(np, endpoint) { - struct device_node *remote __free(device_node) = - of_graph_get_remote_port_parent(endpoint); - if (remote) { - if (of_pci_supply_present(remote)) { - of_node_put(endpoint); - return true; - } - } - } - } - - return false; -} - static int pci_pwrctrl_create_device(struct device_node *np, struct device *parent) { diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c0242f3e9f06..b09f27f7846f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -80,11 +80,10 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) * Restrict the speed to 2.5GT/s then with the Target Link Speed field, * request a retrain and check the result. * - * If this turns out successful and we know by the Vendor:Device ID it is - * safe to do so, then lift the restriction, letting the devices negotiate - * a higher speed. Also check for a similar 2.5GT/s speed restriction the - * firmware may have already arranged and lift it with ports that already - * report their data link being up. + * If this turns out successful, or where a 2.5GT/s speed restriction has + * been previously arranged by the firmware and the port reports its link + * already being up, lift the restriction, in a hope it is safe to do so, + * letting the devices negotiate a higher speed. * * Otherwise revert the speed to the original setting and request a retrain * again to remove any residual state, ignoring the result as it's supposed @@ -95,51 +94,39 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) */ int pcie_failed_link_retrain(struct pci_dev *dev) { - static const struct pci_device_id ids[] = { - { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ - {} - }; - u16 lnksta, lnkctl2; + u16 lnksta, lnkctl2, oldlnkctl2; + enum pci_bus_speed speed_cap; int ret = -ENOTTY; if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) return ret; + speed_cap = pcie_get_speed_cap(dev); + if (speed_cap <= PCIE_SPEED_2_5GT) + return ret; + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) { - u16 oldlnkctl2; - pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); - - pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2); ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false); - if (ret) { - pci_info(dev, "retraining failed\n"); - pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), - true); - return ret; - } - - pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + goto err; } pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); - - if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && - (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && - pci_match_id(ids, dev)) { - u32 lnkcap; - + if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT) { pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); - pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); - ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false); - if (ret) { - pci_info(dev, "retraining failed\n"); - return ret; - } + ret = pcie_set_target_speed(dev, speed_cap, false); + if (ret) + goto err; } + return ret; +err: + pci_info(dev, "retraining failed\n"); + pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), true); return ret; } @@ -3790,6 +3777,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003e, quirk_no_bus_reset); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1103, quirk_no_bus_reset); /* WCN6855 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1107, quirk_no_bus_reset); /* WCN7850 */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x0308, quirk_no_bus_reset); /* SDX62/SDX65 */ /* * Root port on some Cavium CN8xxx chips do not successfully complete a bus @@ -5605,6 +5595,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap); * Intel 82579LM Gigabit Ethernet Controller 0x1502 * Intel 82579V Gigabit Ethernet Controller 0x1503 * Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter + * Mediatek MT7925 802.11be PCI Express Wireless Network Adapter */ static void quirk_no_flr(struct pci_dev *dev) { @@ -5619,6 +5610,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr); /* FLR may cause the SolidRun SNET DPU (rev 0x1) to hang */ static void quirk_no_flr_snet(struct pci_dev *dev) diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c index 39f8cf3b70d5..5bbdc9470a31 100644 --- a/drivers/pci/rebar.c +++ b/drivers/pci/rebar.c @@ -211,8 +211,7 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) return pos; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); if (pci_resource_is_iov(bar)) @@ -231,6 +230,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev) return; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl); for (i = 0; i < nbars; i++, pos += 8) { @@ -238,11 +240,13 @@ void pci_restore_rebar_state(struct pci_dev *pdev) int bar_idx, size; pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + if (PCI_POSSIBLE_ERROR(ctrl)) + return; + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; res = pci_resource_n(pdev, bar_idx); size = pci_rebar_bytes_to_size(resource_size(res)); - ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; - ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size); + FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size); pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); } } diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index e9d519993853..d8bffa21498a 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -26,7 +26,6 @@ static void pci_stop_dev(struct pci_dev *dev) device_release_driver(&dev->dev); pci_proc_detach_device(dev); - pci_remove_sysfs_dev_files(dev); of_pci_remove_node(dev); } @@ -66,8 +65,6 @@ void pci_remove_bus(struct pci_bus *bus) list_del(&bus->node); pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); - pci_remove_legacy_files(bus); - if (bus->ops->remove_bus) bus->ops->remove_bus(bus); diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index e18d3a4383ba..f2105c6ceef5 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -5,13 +5,40 @@ * (C) Copyright 2004 Jon Smirl * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes */ + +#include +#include #include #include +#include +#include #include +#include #include #include "pci.h" +#define PCI_ROM_HEADER_SIZE 0x1A +#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18 +#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15 +#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7) +#define PCI_ROM_IMAGE_LEN 0x10 +#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512 +#define PCI_ROM_IMAGE_SIGNATURE 0xAA55 + +/* Data structure signature is "PCIR" in ASCII representation */ +#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350 +#define PCI_ROM_DATA_STRUCT_LEN 0x0A + +/* + * Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at + * least 24 bytes (0x18), large enough to cover every fixed field this + * driver reads (up to the Indicator byte at offset 0x15). Reject smaller + * device-claimed lengths so the follow-up readers in pci_get_rom_size() + * cannot escape the mapped ROM window. + */ +#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18 + /** * pci_enable_rom - enable ROM decoding for a PCI device * @pdev: PCI device to enable @@ -69,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_rom); +static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image, + void __iomem *rom, size_t size, + bool expect_valid) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long header_end; + u16 signature; + + /* + * Per PCI Firmware r3.3, sec 5.1, each image must start on a + * 512-byte boundary and must contain the PCI Expansion ROM header. + * Because @rom is page-aligned (returned by ioremap()), checking + * 512-byte alignment of @image is equivalent to enforcing the + * spec's sector-aligned layout within the ROM. This also + * satisfies the natural-alignment requirement of readw() on archs + * such as arm64 that disallow unaligned IOMEM access. + */ + if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE)) + return false; + + if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1, + &header_end)) + return false; + + if (image < rom || header_end > rom_end) + return false; + + /* Standard PCI ROMs start out with these bytes 55 AA */ + signature = readw(image); + if (signature != PCI_ROM_IMAGE_SIGNATURE) { + if (expect_valid) { + pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n", + PCI_ROM_IMAGE_SIGNATURE, signature); + } else { + pci_info(pdev, "No more images in PCI ROM\n"); + } + return false; + } + + return true; +} + +static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds, + void __iomem *rom, size_t size) +{ + unsigned long rom_end = (unsigned long)rom + size - 1; + unsigned long end; + u32 signature; + u16 data_len; + + /* + * Some CPU architectures require IOMEM access addresses to be + * aligned, for example arm64, so since we're about to call + * readl(), check here for 4-byte alignment. + */ + if (!IS_ALIGNED((unsigned long)pds, 4)) + return false; + + if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1, + &end)) + return false; + + if (pds < rom || end > rom_end) + return false; + + signature = readl(pds); + if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) { + pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n", + PCI_ROM_DATA_STRUCT_SIGNATURE, signature); + return false; + } + + data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN); + if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX) + return false; + + if (check_add_overflow((unsigned long)pds, data_len - 1, &end)) + return false; + + if (end > rom_end) + return false; + + return true; +} + /** * pci_get_rom_size - obtain the actual size of the ROM image * @pdev: target PCI device @@ -84,37 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; - int last_image; unsigned int length; + bool last_image; image = rom; do { void __iomem *pds; - /* Standard PCI ROMs start out with these bytes 55 AA */ - if (readw(image) != 0xAA55) { - pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n", - readw(image)); + if (!pci_rom_header_valid(pdev, image, rom, size, true)) break; - } - /* get the PCI data structure and check its "PCIR" signature */ - pds = image + readw(image + 24); - if (readl(pds) != 0x52494350) { - pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n", - readl(pds)); + + /* Get the PCI data structure and check its "PCIR" signature */ + pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT); + if (!pci_rom_data_struct_valid(pdev, pds, rom, size)) break; - } - last_image = readb(pds + 21) & 0x80; - length = readw(pds + 16); - image += length * 512; - /* Avoid iterating through memory outside the resource window */ - if (image >= rom + size) + + last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) & + PCI_ROM_LAST_IMAGE_INDICATOR_BIT; + length = readw(pds + PCI_ROM_IMAGE_LEN); + image += length * PCI_ROM_IMAGE_SECTOR_SIZE; + + if (!last_image && + !pci_rom_header_valid(pdev, image, rom, size, false)) break; - if (!last_image) { - if (readw(image) != 0xAA55) { - pci_info(pdev, "No more image in the PCI ROM\n"); - break; - } - } } while (length && !last_image); /* never return a size larger than the PCI resource window */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 4cf120ebe5ad..c0a949f2c995 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -756,13 +756,13 @@ static void __assign_resources_sorted(struct list_head *head, } static void pdev_assign_resources_sorted(struct pci_dev *dev, - struct list_head *add_head, + struct list_head *add_list, struct list_head *fail_head) { LIST_HEAD(head); pdev_sort_resources(dev, &head); - __assign_resources_sorted(&head, add_head, fail_head); + __assign_resources_sorted(&head, add_list, fail_head); } @@ -1501,14 +1501,14 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev) } } -void __pci_bus_assign_resources(const struct pci_bus *bus, - struct list_head *realloc_head, - struct list_head *fail_head) +static void __pci_bus_assign_resources_one(const struct pci_bus *bus, + struct list_head *add_list, + struct list_head *fail_head) { struct pci_bus *b; struct pci_dev *dev; - pbus_assign_resources_sorted(bus, realloc_head, fail_head); + pbus_assign_resources_sorted(bus, add_list, fail_head); list_for_each_entry(dev, &bus->devices, bus_list) { pdev_assign_fixed_resources(dev); @@ -1517,7 +1517,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, if (!b) continue; - __pci_bus_assign_resources(b, realloc_head, fail_head); + __pci_bus_assign_resources_one(b, add_list, fail_head); switch (dev->hdr_type) { case PCI_HEADER_TYPE_BRIDGE: @@ -1537,6 +1537,16 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, } } +void __pci_bus_assign_resources(const struct pci_bus *bus, + struct list_head *add_list, + struct list_head *fail_head) +{ + __pci_bus_assign_resources_one(bus, add_list, fail_head); + + if (WARN_ON_ONCE(add_list && !list_empty(add_list))) + pci_dev_res_free_list(add_list); +} + void pci_bus_assign_resources(const struct pci_bus *bus) { __pci_bus_assign_resources(bus, NULL, NULL); @@ -1612,20 +1622,19 @@ void pci_bus_claim_resources(struct pci_bus *b) } EXPORT_SYMBOL(pci_bus_claim_resources); -static void __pci_bridge_assign_resources(const struct pci_dev *bridge, - struct list_head *add_head, +static void __pci_bridge_assign_resources(struct pci_dev *bridge, + struct list_head *add_list, struct list_head *fail_head) { struct pci_bus *b; - pdev_assign_resources_sorted((struct pci_dev *)bridge, - add_head, fail_head); + pdev_assign_resources_sorted(bridge, add_list, fail_head); b = bridge->subordinate; if (!b) return; - __pci_bus_assign_resources(b, add_head, fail_head); + __pci_bus_assign_resources(b, add_list, fail_head); switch (bridge->class >> 8) { case PCI_CLASS_BRIDGE_PCI: @@ -1641,6 +1650,9 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, pci_domain_nr(b), b->number); break; } + + if (WARN_ON_ONCE(add_list && !list_empty(add_list))) + pci_dev_res_free_list(add_list); } static void pci_bridge_release_resources(struct pci_bus *bus, @@ -2205,8 +2217,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) /* Depth last, allocate resources and update the hardware. */ __pci_bus_assign_resources(bus, add_list, &fail_head); - if (WARN_ON_ONCE(add_list && !list_empty(add_list))) - pci_dev_res_free_list(add_list); tried_times++; /* Any device complain? */ @@ -2268,8 +2278,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) pci_bridge_distribute_available_resources(bridge, &add_list); __pci_bridge_assign_resources(bridge, &add_list, &fail_head); - if (WARN_ON_ONCE(!list_empty(&add_list))) - pci_dev_res_free_list(&add_list); tried_times++; if (list_empty(&fail_head)) @@ -2303,7 +2311,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * unsigned long type = res->flags; struct pci_dev_resource *dev_res; struct pci_dev *bridge = NULL; - LIST_HEAD(added); + LIST_HEAD(add_list); LIST_HEAD(failed); unsigned int i; int ret = 0; @@ -2337,10 +2345,8 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * if (!bridge) return -ENOENT; - __pci_bus_size_bridges(bridge->subordinate, &added); - __pci_bridge_assign_resources(bridge, &added, &failed); - if (WARN_ON_ONCE(!list_empty(&added))) - pci_dev_res_free_list(&added); + __pci_bus_size_bridges(bridge->subordinate, &add_list); + __pci_bridge_assign_resources(bridge, &add_list, &failed); if (!list_empty(&failed)) { if (pci_required_resource_failed(&failed, type)) @@ -2473,7 +2479,5 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) __pci_bus_size_bridges(dev->subordinate, &add_list); up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); - if (WARN_ON_ONCE(!list_empty(&add_list))) - pci_dev_res_free_list(&add_list); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources); diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c index 1ebd13a1f730..dcb8e2b3f73b 100644 --- a/drivers/pci/setup-cardbus.c +++ b/drivers/pci/setup-cardbus.c @@ -22,7 +22,7 @@ static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; -unsigned long pci_cardbus_resource_alignment(struct resource *res) +unsigned long pci_cardbus_resource_alignment(const struct resource *res) { if (res->flags & IORESOURCE_IO) return pci_cardbus_io_size; @@ -253,8 +253,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */ - buses &= ~PCI_SEC_LATENCY_TIMER_MASK; - buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER); + FIELD_MODIFY(PCI_SEC_LATENCY_TIMER_MASK, &buses, CARDBUS_LATENCY_TIMER); /* We need to blast all three values with a single write */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 991d3ed543f5..376f09630a4a 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -169,6 +169,8 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return -EBUSY; } + pci_dbg(dev, "%s %pR: claiming\n", res_name, res); + return 0; } EXPORT_SYMBOL(pci_claim_resource); @@ -246,6 +248,18 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, return 0; } +resource_size_t pci_resource_alignment(const struct pci_dev *dev, + const struct resource *res) +{ + int resno = pci_resource_num(dev, res); + + if (pci_resource_is_iov(resno)) + return pci_sriov_resource_alignment(dev, resno); + if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS) + return pci_cardbus_resource_alignment(res); + return resource_alignment(res); +} + /* * For mem bridge windows, try to relocate tail remainder space to space * before res->start if there's enough free space there. This enables diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 93ebec94b763..41fc4b512708 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1852,6 +1852,22 @@ static const struct pci_device_id switchtec_pci_tbl[] = { SWITCHTEC_PCI_DEVICE(0x5552, SWITCHTEC_GEN5), /* PAXA 52XG5 */ SWITCHTEC_PCI_DEVICE(0x5536, SWITCHTEC_GEN5), /* PAXA 36XG5 */ SWITCHTEC_PCI_DEVICE(0x5528, SWITCHTEC_GEN5), /* PAXA 28XG5 */ + SWITCHTEC_PCI_DEVICE(0x6048, SWITCHTEC_GEN6), /* PFXs 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6064, SWITCHTEC_GEN6), /* PFXs 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6044, SWITCHTEC_GEN6), /* PFXs 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6060, SWITCHTEC_GEN6), /* PFXs 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6148, SWITCHTEC_GEN6), /* PSXs 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6164, SWITCHTEC_GEN6), /* PSXs 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6144, SWITCHTEC_GEN6), /* PSXs 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6160, SWITCHTEC_GEN6), /* PSXs 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6248, SWITCHTEC_GEN6), /* PFX 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6264, SWITCHTEC_GEN6), /* PFX 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6244, SWITCHTEC_GEN6), /* PFX 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6260, SWITCHTEC_GEN6), /* PFX 160XG6 */ + SWITCHTEC_PCI_DEVICE(0x6348, SWITCHTEC_GEN6), /* PSX 48XG6 */ + SWITCHTEC_PCI_DEVICE(0x6364, SWITCHTEC_GEN6), /* PSX 64XG6 */ + SWITCHTEC_PCI_DEVICE(0x6344, SWITCHTEC_GEN6), /* PSX 144XG6 */ + SWITCHTEC_PCI_DEVICE(0x6360, SWITCHTEC_GEN6), /* PSX 160XG6 */ SWITCHTEC_PCI100X_DEVICE(0x1001, SWITCHTEC_GEN4), /* PCI1001 16XG4 */ SWITCHTEC_PCI100X_DEVICE(0x1002, SWITCHTEC_GEN4), /* PCI1002 12XG4 */ SWITCHTEC_PCI100X_DEVICE(0x1003, SWITCHTEC_GEN4), /* PCI1003 16XG4 */ diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c index 91145e8d9d95..655ffd60e62f 100644 --- a/drivers/pci/tph.c +++ b/drivers/pci/tph.c @@ -139,8 +139,7 @@ static void set_ctrl_reg_req_en(struct pci_dev *pdev, u8 req_type) pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); - reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, req_type); + FIELD_MODIFY(PCI_TPH_CTRL_REQ_EN_MASK, ®, req_type); pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); } @@ -427,11 +426,8 @@ int pcie_enable_tph(struct pci_dev *pdev, int mode) /* Write them into TPH control register */ pci_read_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, ®); - reg &= ~PCI_TPH_CTRL_MODE_SEL_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_MODE_SEL_MASK, pdev->tph_mode); - - reg &= ~PCI_TPH_CTRL_REQ_EN_MASK; - reg |= FIELD_PREP(PCI_TPH_CTRL_REQ_EN_MASK, pdev->tph_req_type); + FIELD_MODIFY(PCI_TPH_CTRL_MODE_SEL_MASK, ®, pdev->tph_mode); + FIELD_MODIFY(PCI_TPH_CTRL_REQ_EN_MASK, ®, pdev->tph_req_type); pci_write_config_dword(pdev, pdev->tph_cap + PCI_TPH_CTRL, reg); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 3c73c9c0d4f7..f7930b3dfd0a 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -261,7 +261,7 @@ extern int allocate_resource(struct resource *root, struct resource *new, struct resource *lookup_resource(struct resource *root, resource_size_t start); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); -resource_size_t resource_alignment(struct resource *res); +resource_size_t resource_alignment(const struct resource *res); /** * resource_set_size - Calculate resource end address from size and start diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 1eca1264815b..f247cf9bcf1a 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -61,6 +61,47 @@ struct pci_epc_map { void __iomem *virt_addr; }; +/** + * enum pci_epc_aux_resource_type - auxiliary resource type identifiers + * @PCI_EPC_AUX_DOORBELL_MMIO: Doorbell MMIO, that might be outside the DMA + * controller register window + * + * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their + * register windows and descriptor memories into BAR space. This enum + * identifies the type of each exposable resource. + */ +enum pci_epc_aux_resource_type { + PCI_EPC_AUX_DOORBELL_MMIO, +}; + +/** + * struct pci_epc_aux_resource - a physical auxiliary resource that may be + * exposed for peer use + * @type: resource type, see enum pci_epc_aux_resource_type + * @phys_addr: physical base address of the resource + * @size: size of the resource in bytes + * @bar: BAR number where this resource is already exposed to the RC + * (NO_BAR if not) + * @bar_offset: offset within @bar where the resource starts (valid iff + * @bar != NO_BAR) + * @u: type-specific metadata + */ +struct pci_epc_aux_resource { + enum pci_epc_aux_resource_type type; + phys_addr_t phys_addr; + resource_size_t size; + enum pci_barno bar; + resource_size_t bar_offset; + + union { + /* PCI_EPC_AUX_DOORBELL_MMIO */ + struct { + int irq; /* IRQ number for the doorbell handler */ + u32 data; /* write value to ring the doorbell */ + } db_mmio; + } u; +}; + /** * struct pci_epc_ops - set of function pointers for performing EPC operations * @write_header: ops to populate configuration space header @@ -84,6 +125,9 @@ struct pci_epc_map { * @start: ops to start the PCI link * @stop: ops to stop the PCI link * @get_features: ops to get the features supported by the EPC + * @get_aux_resources_count: ops to get the number of controller-owned + * auxiliary resources + * @get_aux_resources: ops to retrieve controller-owned auxiliary resources * @owner: the module owner containing the ops */ struct pci_epc_ops { @@ -115,6 +159,11 @@ struct pci_epc_ops { void (*stop)(struct pci_epc *epc); const struct pci_epc_features* (*get_features)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); + int (*get_aux_resources_count)(struct pci_epc *epc, u8 func_no, + u8 vfunc_no); + int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources); struct module *owner; }; @@ -343,6 +392,11 @@ int pci_epc_start(struct pci_epc *epc); void pci_epc_stop(struct pci_epc *epc); const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no); +int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no, + u8 vfunc_no); +int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + struct pci_epc_aux_resource *resources, + int num_resources); enum pci_barno pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features); enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 7737a7c03260..8a6c64a35890 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -152,14 +152,41 @@ struct pci_epf_bar { struct pci_epf_bar_submap *submap; }; +enum pci_epf_doorbell_type { + PCI_EPF_DOORBELL_MSI = 0, + PCI_EPF_DOORBELL_EMBEDDED, +}; + /** * struct pci_epf_doorbell_msg - represents doorbell message - * @msg: MSI message - * @virq: IRQ number of this doorbell MSI message + * @msg: Doorbell address/data pair to be mapped into BAR space. + * For MSI-backed doorbells this is the MSI message, while for + * "embedded" doorbells this represents an MMIO write that asserts + * an interrupt on the EP side. + * @virq: IRQ number of this doorbell message + * @irq_flags: Required flags for request_irq()/request_threaded_irq(). + * Callers may OR-in additional flags (e.g. IRQF_ONESHOT). + * @type: Doorbell type. + * @bar: BAR number where the doorbell target is already exposed to the RC + * (NO_BAR if not) + * @offset: offset within @bar for the doorbell target (valid iff + * @bar != NO_BAR) + * @iova_base: Internal: base DMA address returned by dma_map_resource() for the + * embedded doorbell MMIO window (used only for unmapping). Valid + * when @type is PCI_EPF_DOORBELL_EMBEDDED and @iova_size is + * non-zero. + * @iova_size: Internal: size of the dma_map_resource() mapping at @iova_base. + * Zero when no mapping was created (e.g. pre-exposed fixed BAR). */ struct pci_epf_doorbell_msg { struct msi_msg msg; int virq; + unsigned long irq_flags; + enum pci_epf_doorbell_type type; + enum pci_barno bar; + resource_size_t offset; + dma_addr_t iova_base; + size_t iova_size; }; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c4454583c11..ebb5b9d76360 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -508,15 +509,13 @@ struct pci_dev { unsigned int no_command_memory:1; /* No PCI_COMMAND_MEMORY */ unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */ unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ - unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ + unsigned int non_mappable_bars:1; /* BARs can't be mapped by CPU or peers */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ spinlock_t pcie_cap_lock; /* Protects RMW ops in capability accessors */ u32 saved_config_space[16]; /* Config space saved at suspend time */ struct hlist_head saved_cap_space; - struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */ - struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ #ifdef CONFIG_HOTPLUG_PCI_PCIE unsigned int broken_cmd_compl:1; /* No compl for some cmds */ @@ -636,6 +635,7 @@ struct pci_host_bridge { int domain_nr; struct list_head windows; /* resource_entry */ struct list_head dma_ranges; /* dma ranges resource list */ + struct list_head ports; /* Root Port list (pci_host_port) */ #ifdef CONFIG_PCI_IDE u16 nr_ide_streams; /* Max streams possibly active in @ide_stream_ida */ struct ida ide_stream_ida; @@ -660,6 +660,8 @@ struct pci_host_bridge { unsigned int preserve_config:1; /* Preserve FW resource setup */ unsigned int size_windows:1; /* Enable root bus sizing */ unsigned int msi_domain:1; /* Bridge wants MSI domain */ + unsigned int broken_l1ss_resume:1; /* Resuming from L1SS during + system suspend is broken */ /* Resource alignment requirements */ resource_size_t (*align_resource)(struct pci_dev *dev, @@ -727,8 +729,6 @@ struct pci_bus { pci_bus_flags_t bus_flags; /* Inherited by child buses */ struct device *bridge; struct device dev; - struct bin_attribute *legacy_io; /* Legacy I/O for this bus */ - struct bin_attribute *legacy_mem; /* Legacy mem */ unsigned int is_added:1; unsigned int unsafe_warn:1; /* warned about RW1C config write */ unsigned int flit_mode:1; /* Link in Flit mode */ @@ -1171,6 +1171,10 @@ enum { /* These external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI +/* PCI legacy I/O port and memory address space sizes. */ +#define PCI_LEGACY_IO_SIZE (SZ_64K - 1) +#define PCI_LEGACY_MEM_SIZE SZ_1M + extern unsigned int pci_flags; static inline void pci_set_flags(int flags) { pci_flags = flags; } @@ -2086,6 +2090,8 @@ pci_release_mem_regions(struct pci_dev *pdev) pci_select_bars(pdev, IORESOURCE_MEM)); } +bool pci_suspend_retains_context(struct pci_dev *pdev); + #else /* CONFIG_PCI is not enabled */ static inline void pci_set_flags(int flags) { } @@ -2244,6 +2250,11 @@ pci_alloc_irq_vectors(struct pci_dev *dev, unsigned int min_vecs, static inline void pci_free_irq_vectors(struct pci_dev *dev) { } + +static inline bool pci_suspend_retains_context(struct pci_dev *pdev) +{ + return true; +} #endif /* CONFIG_PCI */ /* Include architecture-dependent settings and functions */ @@ -2300,6 +2311,31 @@ int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma); CONCATENATE(__pci_dev_for_each_res, COUNT_ARGS(__VA_ARGS__)) \ (dev, res, __VA_ARGS__) +/** + * pci_resource_is_io - check if a PCI resource is of I/O port type. + * @dev: PCI device to check. + * @resno: The resource number (BAR index) to check. + * + * Returns true if the resource type is I/O port. + */ +static inline bool pci_resource_is_io(const struct pci_dev *dev, int resno) +{ + return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_IO; +} + +/** + * pci_resource_is_mem - check if a PCI resource is of memory type. + * @dev: PCI device to check. + * @resno: The resource number (BAR index) to check. + * + * Returns true if the resource type is memory, including + * prefetchable memory. + */ +static inline bool pci_resource_is_mem(const struct pci_dev *dev, int resno) +{ + return resource_type(pci_resource_n(dev, resno)) == IORESOURCE_MEM; +} + /* * Similar to the helpers above, these manipulate per-pci_dev * driver-specific data. They are really just a wrapper around @@ -2506,11 +2542,6 @@ int pcibios_alloc_irq(struct pci_dev *dev); void pcibios_free_irq(struct pci_dev *dev); resource_size_t pcibios_default_alignment(void); -#if !defined(HAVE_PCI_MMAP) && !defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) -extern int pci_create_resource_files(struct pci_dev *dev); -extern void pci_remove_resource_files(struct pci_dev *dev); -#endif - #if defined(CONFIG_PCI_MMCONFIG) || defined(CONFIG_ACPI_MCFG) void __init pci_mmcfg_early_init(void); void __init pci_mmcfg_late_init(void); @@ -2540,7 +2571,7 @@ int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); int pci_sriov_get_totalvfs(struct pci_dev *dev); int pci_sriov_configure_simple(struct pci_dev *dev, int nr_virtfn); -resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno); +resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno); int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size); u32 pci_iov_vf_bar_get_sizes(struct pci_dev *dev, int resno, int num_vfs); void pci_vf_drivers_autoprobe(struct pci_dev *dev, bool probe); @@ -2548,7 +2579,8 @@ 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); +resource_size_t pcibios_iov_resource_alignment(const struct pci_dev *dev, + int resno); #else static inline int pci_iov_virtfn_bus(struct pci_dev *dev, int id) { @@ -2593,7 +2625,8 @@ static inline int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs) static inline int pci_sriov_get_totalvfs(struct pci_dev *dev) { return 0; } #define pci_sriov_configure_simple NULL -static inline resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) +static inline resource_size_t pci_iov_resource_size(const struct pci_dev *dev, + int resno) { return 0; } static inline int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size) { return -ENODEV; } diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 24cb42f66e4b..1c9d40e09107 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2732,6 +2732,9 @@ #define PCI_DEVICE_ID_INTEL_82815_MC 0x1130 #define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132 #define PCI_DEVICE_ID_INTEL_SST_TNG 0x119a +#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb +#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 +#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371FB_0 0x122e @@ -3052,6 +3055,11 @@ #define PCI_DEVICE_ID_INTEL_5400_FBD1 0x4036 #define PCI_DEVICE_ID_INTEL_HDA_TGL_H 0x43c8 #define PCI_DEVICE_ID_INTEL_HDA_DG1 0x490d +#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940 +#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942 +#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944 +#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946 +#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948 #define PCI_DEVICE_ID_INTEL_HDA_EHL_0 0x4b55 #define PCI_DEVICE_ID_INTEL_HDA_EHL_3 0x4b58 #define PCI_DEVICE_ID_INTEL_HDA_WCL 0x4d28 diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h index cdb58d61c152..724da6c08bf7 100644 --- a/include/linux/switchtec.h +++ b/include/linux/switchtec.h @@ -42,6 +42,7 @@ enum switchtec_gen { SWITCHTEC_GEN3, SWITCHTEC_GEN4, SWITCHTEC_GEN5, + SWITCHTEC_GEN6, }; struct mrpc_regs { diff --git a/kernel/resource.c b/kernel/resource.c index d02a53fb95d8..3d17e3196a3e 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -1238,7 +1238,7 @@ reserve_region_with_split(struct resource *root, resource_size_t start, * * Returns alignment on success, 0 (invalid alignment) on failure. */ -resource_size_t resource_alignment(struct resource *res) +resource_size_t resource_alignment(const struct resource *res) { switch (res->flags & (IORESOURCE_SIZEALIGN | IORESOURCE_STARTALIGN)) { case IORESOURCE_SIZEALIGN: