diff options
118 files changed, 6649 insertions, 2512 deletions
diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst index 0741c8cbd74e..4697377adeae 100644 --- a/Documentation/PCI/endpoint/pci-endpoint.rst +++ b/Documentation/PCI/endpoint/pci-endpoint.rst @@ -95,6 +95,30 @@ by the PCI endpoint function driver. Register space of the function driver is usually configured using this API. + Some endpoint controllers also support calling pci_epc_set_bar() again + for the same BAR (without calling pci_epc_clear_bar()) to update inbound + address translations after the host has programmed the BAR base address. + Endpoint function drivers can check this capability via the + dynamic_inbound_mapping EPC feature bit. + + When pci_epf_bar.num_submap is non-zero, the endpoint function driver is + requesting BAR subrange mapping using pci_epf_bar.submap. This requires + the EPC to advertise support via the subrange_mapping EPC feature bit. + + When an EPF driver wants to make use of the inbound subrange mapping + feature, it requires that the BAR base address has been programmed by + the host during enumeration. Thus, it needs to call pci_epc_set_bar() + twice for the same BAR (requires dynamic_inbound_mapping): first with + num_submap set to zero and configuring the BAR size, then after the PCIe + link is up and the host enumerates the endpoint and programs the BAR + base address, again with num_submap set to non-zero value. + + Note that when making use of the inbound subrange mapping feature, the + EPF driver must not call pci_epc_clear_bar() between the two + pci_epc_set_bar() calls, because clearing the BAR can clear/disable the + BAR register or BAR decode on the endpoint while the host still expects + the assigned BAR address to remain valid. + * pci_epc_clear_bar() The PCI endpoint function driver should use pci_epc_clear_bar() to reset diff --git a/Documentation/PCI/endpoint/pci-test-howto.rst b/Documentation/PCI/endpoint/pci-test-howto.rst index dd66858cde46..a822866b1fb0 100644 --- a/Documentation/PCI/endpoint/pci-test-howto.rst +++ b/Documentation/PCI/endpoint/pci-test-howto.rst @@ -84,6 +84,25 @@ device, the following commands can be used:: # echo 32 > functions/pci_epf_test/func1/msi_interrupts # echo 2048 > functions/pci_epf_test/func1/msix_interrupts +By default, pci-epf-test uses the following BAR sizes:: + + # grep . functions/pci_epf_test/func1/pci_epf_test.0/bar?_size + functions/pci_epf_test/func1/pci_epf_test.0/bar0_size:131072 + functions/pci_epf_test/func1/pci_epf_test.0/bar1_size:131072 + functions/pci_epf_test/func1/pci_epf_test.0/bar2_size:131072 + functions/pci_epf_test/func1/pci_epf_test.0/bar3_size:131072 + functions/pci_epf_test/func1/pci_epf_test.0/bar4_size:131072 + functions/pci_epf_test/func1/pci_epf_test.0/bar5_size:1048576 + +The user can override a default value using e.g.:: + # echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar1_size + +Overriding the default BAR sizes can only be done before binding the +pci-epf-test device to a PCI endpoint controller driver. + +Note: Some endpoint controllers might have fixed-size BARs or reserved BARs; +for such controllers, the corresponding BAR size in configfs will be ignored. + Binding pci-epf-test Device to EP Controller -------------------------------------------- diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst index 9a7a2f0a6849..3679f5c30254 100644 --- a/Documentation/PCI/endpoint/pci-vntb-howto.rst +++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst @@ -52,14 +52,14 @@ pci-epf-vntb device, the following commands can be used:: # cd /sys/kernel/config/pci_ep/ # mkdir functions/pci_epf_vntb/func1 -The "mkdir func1" above creates the pci-epf-ntb function device that will +The "mkdir func1" above creates the pci-epf-vntb function device that will be probed by pci_epf_vntb driver. The PCI endpoint framework populates the directory with the following configurable fields:: - # ls functions/pci_epf_ntb/func1 - baseclass_code deviceid msi_interrupts pci-epf-ntb.0 + # ls functions/pci_epf_vntb/func1 + baseclass_code deviceid msi_interrupts pci-epf-vntb.0 progif_code secondary subsys_id vendorid cache_line_size interrupt_pin msix_interrupts primary revid subclass_code subsys_vendor_id @@ -111,13 +111,13 @@ A sample configuration for virtual NTB driver for virtual PCI bus:: # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid # echo 0x10 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vbus_number -Binding pci-epf-ntb Device to EP Controller +Binding pci-epf-vntb Device to EP Controller -------------------------------------------- NTB function device should be attached to PCI endpoint controllers connected to the host. - # ln -s controllers/5f010000.pcie_ep functions/pci-epf-ntb/func1/primary + # ln -s controllers/5f010000.pcie_ep functions/pci_epf_vntb/func1/primary Once the above step is completed, the PCI endpoint controllers are ready to establish a link with the host. @@ -139,7 +139,7 @@ lspci Output at Host side ------------------------- Note that the devices listed here correspond to the values populated in -"Creating pci-epf-ntb Device" section above:: +"Creating pci-epf-vntb Device" section above:: # lspci 00:00.0 PCI bridge: Freescale Semiconductor Inc Device 0000 (rev 01) @@ -152,7 +152,7 @@ lspci Output at EP Side / Virtual PCI bus ----------------------------------------- Note that the devices listed here correspond to the values populated in -"Creating pci-epf-ntb Device" section above:: +"Creating pci-epf-vntb Device" section above:: # lspci 10:00.0 Unassigned class [ffff]: Dawicontrol Computersysteme GmbH Device 1234 (rev ff) diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst index 0692c9aec66f..667ebe2156b4 100644 --- a/Documentation/PCI/msi-howto.rst +++ b/Documentation/PCI/msi-howto.rst @@ -98,7 +98,7 @@ function:: which allocates up to max_vecs interrupt vectors for a PCI device. It returns the number of vectors allocated or a negative error. If the device -has a requirements for a minimum number of vectors the driver can pass a +has a requirement for a minimum number of vectors the driver can pass a min_vecs argument set to this limit, and the PCI core will return -ENOSPC if it can't meet the minimum number of vectors. @@ -127,7 +127,7 @@ not be able to allocate as many vectors for MSI as it could for MSI-X. On some platforms, MSI interrupts must all be targeted at the same set of CPUs whereas MSI-X interrupts can all be targeted at different CPUs. -If a device supports neither MSI-X or MSI it will fall back to a single +If a device supports neither MSI-X nor MSI it will fall back to a single legacy IRQ vector. The typical usage of MSI or MSI-X interrupts is to allocate as many vectors @@ -203,7 +203,7 @@ How to tell whether MSI/MSI-X is enabled on a device ---------------------------------------------------- Using 'lspci -v' (as root) may show some devices with "MSI", "Message -Signalled Interrupts" or "MSI-X" capabilities. Each of these capabilities +Signaled Interrupts" or "MSI-X" capabilities. Each of these capabilities has an 'Enable' flag which is followed with either "+" (enabled) or "-" (disabled). diff --git a/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml b/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml new file mode 100644 index 000000000000..d9478249418a --- /dev/null +++ b/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml @@ -0,0 +1,182 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/aspeed,ast2600-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED PCIe Root Complex Controller + +maintainers: + - Jacky Chou <jacky_chou@aspeedtech.com> + +description: + The ASPEED PCIe Root Complex controller provides PCI Express Root Complex + functionality for ASPEED SoCs, such as the AST2600 and AST2700. + This controller enables connectivity to PCIe endpoint devices, supporting + memory and I/O windows, MSI and INTx interrupts, and integration with + the SoC's clock, reset, and pinctrl subsystems. On AST2600, the PCIe Root + Port device number is always 8. + +properties: + compatible: + enum: + - aspeed,ast2600-pcie + - aspeed,ast2700-pcie + + reg: + maxItems: 1 + + ranges: + minItems: 2 + maxItems: 2 + + interrupts: + maxItems: 1 + description: INTx and MSI interrupt + + resets: + items: + - description: PCIe controller reset + + reset-names: + items: + - const: h2x + + aspeed,ahbc: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the ASPEED AHB Controller (AHBC) syscon node. + This reference is used by the PCIe controller to access + system-level configuration registers related to the AHB bus. + To enable AHB access for the PCIe controller. + + aspeed,pciecfg: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the ASPEED PCIe configuration syscon node. + This reference allows the PCIe controller to access + SoC-specific PCIe configuration registers. There are the others + functions such PCIe RC and PCIe EP will use this common register + to configure the SoC interfaces. + + interrupt-controller: true + +patternProperties: + "^pcie@[0-9a-f]+,0$": + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + resets: + items: + - description: PERST# signal + + reset-names: + items: + - const: perst + + clocks: + maxItems: 1 + + phys: + maxItems: 1 + + required: + - resets + - reset-names + - clocks + - phys + - ranges + + unevaluatedProperties: false + +allOf: + - $ref: /schemas/pci/pci-host-bridge.yaml# + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + - if: + properties: + compatible: + contains: + const: aspeed,ast2600-pcie + then: + required: + - aspeed,ahbc + else: + properties: + aspeed,ahbc: false + - if: + properties: + compatible: + contains: + const: aspeed,ast2700-pcie + then: + required: + - aspeed,pciecfg + else: + properties: + aspeed,pciecfg: false + +required: + - reg + - interrupts + - bus-range + - ranges + - resets + - reset-names + - msi-controller + - interrupt-controller + - interrupt-map-mask + - interrupt-map + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/clock/ast2600-clock.h> + + pcie0: pcie@1e770000 { + compatible = "aspeed,ast2600-pcie"; + device_type = "pci"; + reg = <0x1e770000 0x100>; + #address-cells = <3>; + #size-cells = <2>; + interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>; + bus-range = <0x00 0xff>; + + ranges = <0x01000000 0x0 0x00018000 0x00018000 0x0 0x00008000 + 0x02000000 0x0 0x60000000 0x60000000 0x0 0x20000000>; + + resets = <&syscon ASPEED_RESET_H2X>; + reset-names = "h2x"; + + #interrupt-cells = <1>; + msi-controller; + + aspeed,ahbc = <&ahbc>; + + interrupt-controller; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie0 0>, + <0 0 0 2 &pcie0 1>, + <0 0 0 3 &pcie0 2>, + <0 0 0 4 &pcie0 3>; + + pcie@8,0 { + compatible = "pciclass,0604"; + reg = <0x00004000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + resets = <&syscon ASPEED_RESET_PCIE_RC_O>; + reset-names = "perst"; + clocks = <&syscon ASPEED_CLK_GATE_BCLK>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcierc1_default>; + phys = <&pcie_phy1>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index ca5f2970f217..12a01f7a5744 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -44,7 +44,7 @@ properties: clock-names: minItems: 3 - maxItems: 5 + maxItems: 6 interrupts: minItems: 1 @@ -212,14 +212,17 @@ allOf: then: properties: clocks: - maxItems: 5 + minItems: 5 + maxItems: 6 clock-names: + minItems: 5 items: - const: pcie - const: pcie_bus - const: pcie_phy - const: pcie_aux - const: ref + - const: extref # Optional unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/pci/loongson.yaml b/Documentation/devicetree/bindings/pci/loongson.yaml index e5bba63aa947..26e77218b901 100644 --- a/Documentation/devicetree/bindings/pci/loongson.yaml +++ b/Documentation/devicetree/bindings/pci/loongson.yaml @@ -32,6 +32,8 @@ properties: minItems: 1 maxItems: 3 + msi-parent: true + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml index 0278845701ce..4db700fc36ba 100644 --- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml +++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml @@ -48,6 +48,7 @@ properties: oneOf: - items: - enum: + - mediatek,mt7981-pcie - mediatek,mt7986-pcie - mediatek,mt8188-pcie - mediatek,mt8195-pcie diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml new file mode 100644 index 000000000000..eb5b81d1defc --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml @@ -0,0 +1,170 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-apq8064.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm APQ8064/IPQ8064 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-apq8064 + - qcom,pcie-ipq8064 + - qcom,pcie-ipq8064-v2 + + reg: + maxItems: 4 + + reg-names: + items: + - const: dbi + - const: elbi + - const: parf + - const: config + + clocks: + minItems: 3 + maxItems: 5 + + clock-names: + minItems: 3 + items: + - const: core # Clocks the pcie hw block + - const: iface # Configuration AHB clock + - const: phy + - const: aux + - const: ref + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + + resets: + minItems: 5 + maxItems: 6 + + reset-names: + minItems: 5 + items: + - const: axi + - const: ahb + - const: por + - const: pci + - const: phy + - const: ext + + vdda-supply: + description: A phandle to the core analog power supply + + vdda_phy-supply: + description: A phandle to the core analog power supply for PHY + + vdda_refclk-supply: + description: A phandle to the core analog power supply for IC which generates reference clock + +required: + - resets + - reset-names + - vdda-supply + - vdda_phy-supply + - vdda_refclk-supply + +allOf: + - $ref: qcom,pcie-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - qcom,pcie-apq8064 + then: + properties: + clocks: + maxItems: 3 + clock-names: + maxItems: 3 + resets: + maxItems: 5 + reset-names: + maxItems: 5 + else: + properties: + clocks: + minItems: 5 + clock-names: + minItems: 5 + resets: + minItems: 6 + reset-names: + minItems: 6 + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-msm8960.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/qcom,gcc-msm8960.h> + + pcie@1b500000 { + compatible = "qcom,pcie-apq8064"; + reg = <0x1b500000 0x1000>, + <0x1b502000 0x80>, + <0x1b600000 0x100>, + <0x0ff00000 0x100000>; + reg-names = "dbi", "elbi", "parf", "config"; + ranges = <0x81000000 0x0 0x00000000 0x0fe00000 0x0 0x00100000>, /* I/O */ + <0x82000000 0x0 0x08000000 0x08000000 0x0 0x07e00000>; /* mem */ + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc PCIE_A_CLK>, + <&gcc PCIE_H_CLK>, + <&gcc PCIE_PHY_REF_CLK>; + clock-names = "core", "iface", "phy"; + + interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + resets = <&gcc PCIE_ACLK_RESET>, + <&gcc PCIE_HCLK_RESET>, + <&gcc PCIE_POR_RESET>, + <&gcc PCIE_PCI_RESET>, + <&gcc PCIE_PHY_RESET>; + reset-names = "axi", "ahb", "por", "pci", "phy"; + + perst-gpios = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>; + vdda-supply = <&pm8921_s3>; + vdda_phy-supply = <&pm8921_lvs6>; + vdda_refclk-supply = <&v3p3_fixed>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml new file mode 100644 index 000000000000..a6403a3de076 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-apq8084.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm APQ8084 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-apq8084 + + reg: + minItems: 4 + maxItems: 5 + + reg-names: + minItems: 4 + items: + - const: parf + - const: dbi + - const: elbi + - const: config + - const: mhi + + clocks: + maxItems: 4 + + clock-names: + items: + - const: iface # Configuration AHB clock + - const: master_bus # Master AXI clock + - const: slave_bus # Slave AXI clock + - const: aux + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + + resets: + maxItems: 1 + + reset-names: + items: + - const: core + + vdda-supply: + description: A phandle to the core analog power supply + +required: + - power-domains + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/gpio/gpio.h> + pcie@fc520000 { + compatible = "qcom,pcie-apq8084"; + reg = <0xfc520000 0x2000>, + <0xff000000 0x1000>, + <0xff001000 0x1000>, + <0xff002000 0x2000>; + reg-names = "parf", "dbi", "elbi", "config"; + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x81000000 0 0 0xff200000 0 0x00100000>, + <0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; + interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&gcc 324>, + <&gcc 325>, + <&gcc 327>, + <&gcc 323>; + clock-names = "iface", "master_bus", "slave_bus", "aux"; + resets = <&gcc 81>; + reset-names = "core"; + power-domains = <&gcc 1>; + vdda-supply = <&pma8084_l3>; + phys = <&pciephy0>; + phy-names = "pciephy"; + perst-gpios = <&tlmm 70 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&pcie0_pins_default>; + pinctrl-names = "default"; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml new file mode 100644 index 000000000000..fd6ecd1c43a1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq4019.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ4019 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-ipq4019 + + reg: + maxItems: 4 + + reg-names: + items: + - const: dbi + - const: elbi + - const: parf + - const: config + + clocks: + maxItems: 3 + + clock-names: + items: + - const: aux + - const: master_bus # Master AXI clock + - const: slave_bus # Slave AXI clock + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + + resets: + maxItems: 12 + + reset-names: + items: + - const: axi_m # AXI master reset + - const: axi_s # AXI slave reset + - const: pipe + - const: axi_m_vmid + - const: axi_s_xpu + - const: parf + - const: phy + - const: axi_m_sticky # AXI master sticky reset + - const: pipe_sticky + - const: pwr + - const: ahb + - const: phy_ahb + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-ipq4019.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pcie@40000000 { + compatible = "qcom,pcie-ipq4019"; + reg = <0x40000000 0xf1d>, + <0x40000f20 0xa8>, + <0x80000 0x2000>, + <0x40100000 0x1000>; + reg-names = "dbi", "elbi", "parf", "config"; + ranges = <0x81000000 0x0 0x00000000 0x40200000 0x0 0x00100000>, + <0x82000000 0x0 0x40300000 0x40300000 0x0 0x00d00000>; + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_PCIE_AHB_CLK>, + <&gcc GCC_PCIE_AXI_M_CLK>, + <&gcc GCC_PCIE_AXI_S_CLK>; + clock-names = "aux", + "master_bus", + "slave_bus"; + + interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + resets = <&gcc PCIE_AXI_M_ARES>, + <&gcc PCIE_AXI_S_ARES>, + <&gcc PCIE_PIPE_ARES>, + <&gcc PCIE_AXI_M_VMIDMT_ARES>, + <&gcc PCIE_AXI_S_XPU_ARES>, + <&gcc PCIE_PARF_XPU_ARES>, + <&gcc PCIE_PHY_ARES>, + <&gcc PCIE_AXI_M_STICKY_ARES>, + <&gcc PCIE_PIPE_STICKY_ARES>, + <&gcc PCIE_PWR_ARES>, + <&gcc PCIE_AHB_ARES>, + <&gcc PCIE_PHY_AHB_ARES>; + reset-names = "axi_m", + "axi_s", + "pipe", + "axi_m_vmid", + "axi_s_xpu", + "parf", + "phy", + "axi_m_sticky", + "pipe_sticky", + "pwr", + "ahb", + "phy_ahb"; + + perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml new file mode 100644 index 000000000000..20c2c946f474 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq5018.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ5018 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-ipq5018 + + reg: + minItems: 5 + maxItems: 6 + + reg-names: + minItems: 5 + items: + - const: dbi + - const: elbi + - const: atu + - const: parf + - const: config + - const: mhi + + clocks: + maxItems: 6 + + clock-names: + items: + - const: iface # PCIe to SysNOC BIU clock + - const: axi_m # AXI Master clock + - const: axi_s # AXI Slave clock + - const: ahb + - const: aux + - const: axi_bridge + + interrupts: + maxItems: 9 + + interrupt-names: + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + resets: + maxItems: 8 + + reset-names: + items: + - const: pipe + - const: sleep + - const: sticky # Core sticky reset + - const: axi_m # AXI master reset + - const: axi_s # AXI slave reset + - const: ahb + - const: axi_m_sticky # AXI master sticky reset + - const: axi_s_sticky # AXI slave sticky reset + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-ipq5018.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/qcom,gcc-ipq5018.h> + + pcie@a0000000 { + compatible = "qcom,pcie-ipq5018"; + reg = <0xa0000000 0xf1d>, + <0xa0000f20 0xa8>, + <0xa0001000 0x1000>, + <0x00080000 0x3000>, + <0xa0100000 0x1000>, + <0x00083000 0x1000>; + reg-names = "dbi", + "elbi", + "atu", + "parf", + "config", + "mhi"; + ranges = <0x01000000 0 0x00000000 0xa0200000 0 0x00100000>, + <0x02000000 0 0xa0300000 0xa0300000 0 0x10000000>; + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <2>; + #address-cells = <3>; + #size-cells = <2>; + + /* The controller supports Gen3, but the connected PHY is Gen2-capable */ + max-link-speed = <2>; + + clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>, + <&gcc GCC_PCIE0_AXI_M_CLK>, + <&gcc GCC_PCIE0_AXI_S_CLK>, + <&gcc GCC_PCIE0_AHB_CLK>, + <&gcc GCC_PCIE0_AUX_CLK>, + <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>; + clock-names = "iface", + "axi_m", + "axi_s", + "ahb", + "aux", + "axi_bridge"; + + msi-map = <0x0 &v2m0 0x0 0xff8>; + + interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; + + phys = <&pcie0_phy>; + phy-names = "pciephy"; + + resets = <&gcc GCC_PCIE0_PIPE_ARES>, + <&gcc GCC_PCIE0_SLEEP_ARES>, + <&gcc GCC_PCIE0_CORE_STICKY_ARES>, + <&gcc GCC_PCIE0_AXI_MASTER_ARES>, + <&gcc GCC_PCIE0_AXI_SLAVE_ARES>, + <&gcc GCC_PCIE0_AHB_ARES>, + <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>, + <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>; + reset-names = "pipe", + "sleep", + "sticky", + "axi_m", + "axi_s", + "ahb", + "axi_m_sticky", + "axi_s_sticky"; + + perst-gpios = <&tlmm 15 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 16 GPIO_ACTIVE_LOW>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml new file mode 100644 index 000000000000..6843570eb051 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq6018.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ6018 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-ipq6018 + - qcom,pcie-ipq8074-gen3 + + reg: + minItems: 5 + maxItems: 6 + + reg-names: + minItems: 5 + items: + - const: dbi + - const: elbi + - const: atu + - const: parf + - const: config + - const: mhi + + clocks: + maxItems: 5 + + clock-names: + items: + - const: iface # PCIe to SysNOC BIU clock + - const: axi_m # AXI Master clock + - const: axi_s # AXI Slave clock + - const: axi_bridge + - const: rchng + + interrupts: + maxItems: 9 + + interrupt-names: + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + resets: + maxItems: 8 + + reset-names: + items: + - const: pipe + - const: sleep + - const: sticky # Core sticky reset + - const: axi_m # AXI master reset + - const: axi_s # AXI slave reset + - const: ahb + - const: axi_m_sticky # AXI master sticky reset + - const: axi_s_sticky # AXI slave sticky reset + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-ipq6018.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/qcom,gcc-ipq6018.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@20000000 { + compatible = "qcom,pcie-ipq6018"; + reg = <0x0 0x20000000 0x0 0xf1d>, + <0x0 0x20000f20 0x0 0xa8>, + <0x0 0x20001000 0x0 0x1000>, + <0x0 0x80000 0x0 0x4000>, + <0x0 0x20100000 0x0 0x1000>; + reg-names = "dbi", "elbi", "atu", "parf", "config"; + ranges = <0x81000000 0x0 0x00000000 0x0 0x20200000 0x0 0x10000>, + <0x82000000 0x0 0x20220000 0x0 0x20220000 0x0 0xfde0000>; + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + max-link-speed = <3>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>, + <&gcc GCC_PCIE0_AXI_M_CLK>, + <&gcc GCC_PCIE0_AXI_S_CLK>, + <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>, + <&gcc PCIE0_RCHNG_CLK>; + clock-names = "iface", + "axi_m", + "axi_s", + "axi_bridge", + "rchng"; + + interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 0 GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 0 GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 0 GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + phys = <&pcie_phy>; + phy-names = "pciephy"; + + resets = <&gcc GCC_PCIE0_PIPE_ARES>, + <&gcc GCC_PCIE0_SLEEP_ARES>, + <&gcc GCC_PCIE0_CORE_STICKY_ARES>, + <&gcc GCC_PCIE0_AXI_MASTER_ARES>, + <&gcc GCC_PCIE0_AXI_SLAVE_ARES>, + <&gcc GCC_PCIE0_AHB_ARES>, + <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>, + <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>; + reset-names = "pipe", + "sleep", + "sticky", + "axi_m", + "axi_s", + "ahb", + "axi_m_sticky", + "axi_s_sticky"; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml new file mode 100644 index 000000000000..da975f943a7b --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml @@ -0,0 +1,165 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq8074.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ8074 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-ipq8074 + + reg: + maxItems: 4 + + reg-names: + items: + - const: dbi + - const: elbi + - const: parf + - const: config + + clocks: + maxItems: 5 + + clock-names: + items: + - const: iface # PCIe to SysNOC BIU clock + - const: axi_m # AXI Master clock + - const: axi_s # AXI Slave clock + - const: ahb + - const: aux + + interrupts: + maxItems: 9 + + interrupt-names: + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + resets: + maxItems: 7 + + reset-names: + items: + - const: pipe + - const: sleep + - const: sticky # Core sticky reset + - const: axi_m # AXI master reset + - const: axi_s # AXI slave reset + - const: ahb + - const: axi_m_sticky # AXI master sticky reset + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-ipq8074.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pcie@10000000 { + compatible = "qcom,pcie-ipq8074"; + reg = <0x10000000 0xf1d>, + <0x10000f20 0xa8>, + <0x00088000 0x2000>, + <0x10100000 0x1000>; + reg-names = "dbi", "elbi", "parf", "config"; + ranges = <0x81000000 0x0 0x00000000 0x10200000 0x0 0x10000>, /* I/O */ + <0x82000000 0x0 0x10220000 0x10220000 0x0 0xfde0000>; /* MEM */ + + device_type = "pci"; + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + max-link-speed = <2>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>, + <&gcc GCC_PCIE1_AXI_M_CLK>, + <&gcc GCC_PCIE1_AXI_S_CLK>, + <&gcc GCC_PCIE1_AHB_CLK>, + <&gcc GCC_PCIE1_AUX_CLK>; + clock-names = "iface", + "axi_m", + "axi_s", + "ahb", + "aux"; + + interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + phys = <&pcie_qmp1>; + phy-names = "pciephy"; + + resets = <&gcc GCC_PCIE1_PIPE_ARES>, + <&gcc GCC_PCIE1_SLEEP_ARES>, + <&gcc GCC_PCIE1_CORE_STICKY_ARES>, + <&gcc GCC_PCIE1_AXI_MASTER_ARES>, + <&gcc GCC_PCIE1_AXI_SLAVE_ARES>, + <&gcc GCC_PCIE1_AHB_ARES>, + <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>; + reset-names = "pipe", + "sleep", + "sticky", + "axi_m", + "axi_s", + "ahb", + "axi_m_sticky"; + + perst-gpios = <&tlmm 58 GPIO_ACTIVE_LOW>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml new file mode 100644 index 000000000000..4be342cc04e1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml @@ -0,0 +1,183 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq9574.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IPQ9574 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + oneOf: + - enum: + - qcom,pcie-ipq9574 + - items: + - enum: + - qcom,pcie-ipq5332 + - qcom,pcie-ipq5424 + - const: qcom,pcie-ipq9574 + + reg: + maxItems: 6 + + reg-names: + items: + - const: dbi + - const: elbi + - const: atu + - const: parf + - const: config + - const: mhi + + clocks: + maxItems: 6 + + clock-names: + items: + - const: axi_m # AXI Master clock + - const: axi_s # AXI Slave clock + - const: axi_bridge + - const: rchng + - const: ahb + - const: aux + + interrupts: + minItems: 8 + maxItems: 9 + + interrupt-names: + minItems: 8 + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + resets: + maxItems: 8 + + reset-names: + items: + - const: pipe + - const: sticky # Core sticky reset + - const: axi_s_sticky # AXI Slave Sticky reset + - const: axi_s # AXI slave reset + - const: axi_m_sticky # AXI Master Sticky reset + - const: axi_m # AXI master reset + - const: aux + - const: ahb + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,ipq9574-gcc.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interconnect/qcom,ipq9574.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/reset/qcom,ipq9574-gcc.h> + + pcie@10000000 { + compatible = "qcom,pcie-ipq9574"; + reg = <0x10000000 0xf1d>, + <0x10000f20 0xa8>, + <0x10001000 0x1000>, + <0x000f8000 0x4000>, + <0x10100000 0x1000>, + <0x000fe000 0x1000>; + reg-names = "dbi", + "elbi", + "atu", + "parf", + "config", + "mhi"; + ranges = <0x01000000 0x0 0x00000000 0x10200000 0x0 0x100000>, + <0x02000000 0x0 0x10300000 0x10300000 0x0 0x7d00000>; + + device_type = "pci"; + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_PCIE1_AXI_M_CLK>, + <&gcc GCC_PCIE1_AXI_S_CLK>, + <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>, + <&gcc GCC_PCIE1_RCHNG_CLK>, + <&gcc GCC_PCIE1_AHB_CLK>, + <&gcc GCC_PCIE1_AUX_CLK>; + clock-names = "axi_m", + "axi_s", + "axi_bridge", + "rchng", + "ahb", + "aux"; + + interconnects = <&gcc MASTER_ANOC_PCIE1 &gcc SLAVE_ANOC_PCIE1>, + <&gcc MASTER_SNOC_PCIE1 &gcc SLAVE_SNOC_PCIE1>; + interconnect-names = "pcie-mem", "cpu-pcie"; + + interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &intc 0 GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &intc 0 GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &intc 0 GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>; + + resets = <&gcc GCC_PCIE1_PIPE_ARES>, + <&gcc GCC_PCIE1_CORE_STICKY_ARES>, + <&gcc GCC_PCIE1_AXI_S_STICKY_ARES>, + <&gcc GCC_PCIE1_AXI_S_ARES>, + <&gcc GCC_PCIE1_AXI_M_STICKY_ARES>, + <&gcc GCC_PCIE1_AXI_M_ARES>, + <&gcc GCC_PCIE1_AUX_ARES>, + <&gcc GCC_PCIE1_AHB_ARES>; + reset-names = "pipe", + "sticky", + "axi_s_sticky", + "axi_s", + "axi_m_sticky", + "axi_m", + "aux", + "ahb"; + + phys = <&pcie1_phy>; + phy-names = "pciephy"; + + perst-gpios = <&tlmm 26 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 27 GPIO_ACTIVE_LOW>; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml new file mode 100644 index 000000000000..f2081ae1593f --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml @@ -0,0 +1,156 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-msm8996.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm MSM8996 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + oneOf: + - enum: + - qcom,pcie-msm8996 + - items: + - const: qcom,pcie-msm8998 + - const: qcom,pcie-msm8996 + + reg: + minItems: 4 + maxItems: 5 + + reg-names: + minItems: 4 + items: + - const: parf + - const: dbi + - const: elbi + - const: config + - const: mhi + + clocks: + maxItems: 5 + + clock-names: + items: + - const: pipe # Pipe Clock driving internal logic + - const: aux + - const: cfg + - const: bus_master # Master AXI clock + - const: bus_slave # Slave AXI clock + + interrupts: + minItems: 8 + maxItems: 9 + + interrupt-names: + minItems: 8 + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + vdda-supply: + description: A phandle to the core analog power supply + + vddpe-3v3-supply: + description: A phandle to the PCIe endpoint power supply + +required: + - power-domains + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-msm8996.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pcie@600000 { + compatible = "qcom,pcie-msm8996"; + reg = <0x00600000 0x2000>, + <0x0c000000 0xf1d>, + <0x0c000f20 0xa8>, + <0x0c100000 0x100000>; + reg-names = "parf", "dbi", "elbi", "config"; + ranges = <0x01000000 0x0 0x00000000 0x0c200000 0x0 0x100000>, + <0x02000000 0x0 0x0c300000 0x0c300000 0x0 0xd00000>; + + device_type = "pci"; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + linux,pci-domain = <0>; + + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>, + <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave"; + + interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 411 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 412 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pcie0_state_on>; + pinctrl-1 = <&pcie0_state_off>; + + phys = <&pciephy_0>; + phy-names = "pciephy"; + + power-domains = <&gcc PCIE0_GDSC>; + + perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>; + vddpe-3v3-supply = <&wlan_en>; + vdda-supply = <&vreg_l28a_0p925>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml new file mode 100644 index 000000000000..99b3ed43b87c --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-qcs404.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCS404 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-qcs404 + + reg: + maxItems: 4 + + reg-names: + items: + - const: dbi + - const: elbi + - const: parf + - const: config + + clocks: + maxItems: 4 + + clock-names: + items: + - const: iface # AHB clock + - const: aux + - const: master_bus # AXI Master clock + - const: slave_bus # AXI Slave clock + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: msi + + resets: + maxItems: 6 + + reset-names: + items: + - const: axi_m # AXI Master reset + - const: axi_s # AXI Slave reset + - const: axi_m_sticky # AXI Master Sticky reset + - const: pipe_sticky + - const: pwr + - const: ahb + +required: + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-qcs404.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pcie@10000000 { + compatible = "qcom,pcie-qcs404"; + reg = <0x10000000 0xf1d>, + <0x10000f20 0xa8>, + <0x07780000 0x2000>, + <0x10001000 0x2000>; + reg-names = "dbi", "elbi", "parf", "config"; + ranges = <0x81000000 0x0 0x00000000 0x10003000 0x0 0x00010000>, /* I/O */ + <0x82000000 0x0 0x10013000 0x10013000 0x0 0x007ed000>; /* memory */ + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>; + clock-names = "iface", "aux", "master_bus", "slave_bus"; + + interrupts = <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + phys = <&pcie_phy>; + phy-names = "pciephy"; + + perst-gpios = <&tlmm 43 GPIO_ACTIVE_LOW>; + + resets = <&gcc GCC_PCIE_0_AXI_MASTER_ARES>, + <&gcc GCC_PCIE_0_AXI_SLAVE_ARES>, + <&gcc GCC_PCIE_0_AXI_MASTER_STICKY_ARES>, + <&gcc GCC_PCIE_0_CORE_STICKY_ARES>, + <&gcc GCC_PCIE_0_BCR>, + <&gcc GCC_PCIE_0_AHB_ARES>; + reset-names = "axi_m", + "axi_s", + "axi_m_sticky", + "pipe_sticky", + "pwr", + "ahb"; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml deleted file mode 100644 index 6a7c410c9fc3..000000000000 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml +++ /dev/null @@ -1,168 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/pci/qcom,pcie-sc8180x.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm SC8180x PCI Express Root Complex - -maintainers: - - Bjorn Andersson <andersson@kernel.org> - - Manivannan Sadhasivam <mani@kernel.org> - -description: - Qualcomm SC8180x SoC PCIe root complex controller is based on the Synopsys - DesignWare PCIe IP. - -properties: - compatible: - const: qcom,pcie-sc8180x - - reg: - minItems: 5 - maxItems: 6 - - reg-names: - minItems: 5 - items: - - const: parf # Qualcomm specific registers - - const: dbi # DesignWare PCIe registers - - const: elbi # External local bus interface registers - - const: atu # ATU address space - - const: config # PCIe configuration space - - const: mhi # MHI registers - - clocks: - minItems: 6 - maxItems: 6 - - clock-names: - items: - - const: pipe # PIPE clock - - const: aux # Auxiliary clock - - const: cfg # Configuration clock - - const: bus_master # Master AXI clock - - const: bus_slave # Slave AXI clock - - const: slave_q2a # Slave Q2A clock - - interrupts: - minItems: 8 - maxItems: 9 - - interrupt-names: - minItems: 8 - items: - - const: msi0 - - const: msi1 - - const: msi2 - - const: msi3 - - const: msi4 - - const: msi5 - - const: msi6 - - const: msi7 - - const: global - - resets: - maxItems: 1 - - reset-names: - items: - - const: pci - -allOf: - - $ref: qcom,pcie-common.yaml# - -unevaluatedProperties: false - -examples: - - | - #include <dt-bindings/clock/qcom,gcc-sc8180x.h> - #include <dt-bindings/interconnect/qcom,sc8180x.h> - #include <dt-bindings/interrupt-controller/arm-gic.h> - - soc { - #address-cells = <2>; - #size-cells = <2>; - - pcie@1c00000 { - compatible = "qcom,pcie-sc8180x"; - reg = <0 0x01c00000 0 0x3000>, - <0 0x60000000 0 0xf1d>, - <0 0x60000f20 0 0xa8>, - <0 0x60001000 0 0x1000>, - <0 0x60100000 0 0x100000>; - reg-names = "parf", - "dbi", - "elbi", - "atu", - "config"; - ranges = <0x01000000 0x0 0x60200000 0x0 0x60200000 0x0 0x100000>, - <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0x3d00000>; - - bus-range = <0x00 0xff>; - device_type = "pci"; - linux,pci-domain = <0>; - num-lanes = <2>; - - #address-cells = <3>; - #size-cells = <2>; - - assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>; - assigned-clock-rates = <19200000>; - - clocks = <&gcc GCC_PCIE_0_PIPE_CLK>, - <&gcc GCC_PCIE_0_AUX_CLK>, - <&gcc GCC_PCIE_0_CFG_AHB_CLK>, - <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_AXI_CLK>, - <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>; - clock-names = "pipe", - "aux", - "cfg", - "bus_master", - "bus_slave", - "slave_q2a"; - - dma-coherent; - - interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, - <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "msi0", - "msi1", - "msi2", - "msi3", - "msi4", - "msi5", - "msi6", - "msi7", - "global"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ - <0 0 0 2 &intc 0 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ - <0 0 0 3 &intc 0 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ - <0 0 0 4 &intc 0 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ - - interconnects = <&aggre2_noc MASTER_PCIE 0 &mc_virt SLAVE_EBI_CH0 0>, - <&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_PCIE_0 0>; - interconnect-names = "pcie-mem", "cpu-pcie"; - - iommu-map = <0x0 &apps_smmu 0x1d80 0x1>, - <0x100 &apps_smmu 0x1d81 0x1>; - - phys = <&pcie0_phy>; - phy-names = "pciephy"; - - power-domains = <&gcc PCIE_0_GDSC>; - - resets = <&gcc GCC_PCIE_0_BCR>; - reset-names = "pci"; - }; - }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml new file mode 100644 index 000000000000..1ec9e4f3ff57 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-sdm845.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SDM845 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-sdm845 + + reg: + minItems: 4 + maxItems: 5 + + reg-names: + minItems: 4 + items: + - const: parf + - const: dbi + - const: elbi + - const: config + - const: mhi + + clocks: + minItems: 7 + maxItems: 8 + + clock-names: + minItems: 7 + items: + - const: pipe + - const: aux + - const: cfg + - const: bus_master # Master AXI clock + - const: bus_slave # Slave AXI clock + - const: slave_q2a + - enum: [ ref, tbu ] + - const: tbu + + interrupts: + minItems: 8 + maxItems: 9 + + interrupt-names: + minItems: 8 + items: + - const: msi0 + - const: msi1 + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: global + + resets: + maxItems: 1 + + reset-names: + items: + - const: pci + +required: + - power-domains + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-sdm845.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + pcie@1c00000 { + compatible = "qcom,pcie-sdm845"; + reg = <0x0 0x01c00000 0x0 0x2000>, + <0x0 0x60000000 0x0 0xf1d>, + <0x0 0x60000f20 0x0 0xa8>, + <0x0 0x60100000 0x0 0x100000>, + <0x0 0x01c07000 0x0 0x1000>; + reg-names = "parf", "dbi", "elbi", "config", "mhi"; + ranges = <0x01000000 0x0 0x00000000 0x0 0x60200000 0x0 0x100000>, + <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0xd00000>; + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + + #address-cells = <3>; + #size-cells = <2>; + + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>, + <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "tbu"; + + interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi0", + "msi1", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "global"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 0 GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 0 GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 0 GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + iommu-map = <0x0 &apps_smmu 0x1c10 0x1>, + <0x100 &apps_smmu 0x1c11 0x1>, + <0x200 &apps_smmu 0x1c12 0x1>, + <0x300 &apps_smmu 0x1c13 0x1>, + <0x400 &apps_smmu 0x1c14 0x1>, + <0x500 &apps_smmu 0x1c15 0x1>, + <0x600 &apps_smmu 0x1c16 0x1>, + <0x700 &apps_smmu 0x1c17 0x1>, + <0x800 &apps_smmu 0x1c18 0x1>, + <0x900 &apps_smmu 0x1c19 0x1>, + <0xa00 &apps_smmu 0x1c1a 0x1>, + <0xb00 &apps_smmu 0x1c1b 0x1>, + <0xc00 &apps_smmu 0x1c1c 0x1>, + <0xd00 &apps_smmu 0x1c1d 0x1>, + <0xe00 &apps_smmu 0x1c1e 0x1>, + <0xf00 &apps_smmu 0x1c1f 0x1>; + + power-domains = <&gcc PCIE_0_GDSC>; + + phys = <&pcie0_phy>; + phy-names = "pciephy"; + + resets = <&gcc GCC_PCIE_0_BCR>; + reset-names = "pci"; + + perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 134 GPIO_ACTIVE_HIGH>; + + vddpe-3v3-supply = <&pcie0_3p3v_dual>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml new file mode 100644 index 000000000000..7f6fd81e7ed0 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml @@ -0,0 +1,172 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,pcie-sdx55.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SDX55 PCI Express Root Complex + +maintainers: + - Bjorn Andersson <andersson@kernel.org> + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + enum: + - qcom,pcie-sdx55 + + reg: + minItems: 5 + maxItems: 6 + + reg-names: + minItems: 5 + items: + - const: parf + - const: dbi + - const: elbi + - const: atu + - const: config + - const: mhi + + clocks: + maxItems: 7 + + clock-names: + items: + - const: pipe + - const: aux + - const: cfg + - const: bus_master # Master AXI clock + - const: bus_slave # Slave AXI clock + - const: slave_q2a + - const: sleep + + interrupts: + maxItems: 8 + + interrupt-names: + items: + - const: msi + - const: msi2 + - const: msi3 + - const: msi4 + - const: msi5 + - const: msi6 + - const: msi7 + - const: msi8 + + resets: + maxItems: 1 + + reset-names: + items: + - const: pci + +required: + - power-domains + - resets + - reset-names + +allOf: + - $ref: qcom,pcie-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,gcc-sdx55.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pcie@1c00000 { + compatible = "qcom,pcie-sdx55"; + reg = <0x01c00000 0x3000>, + <0x40000000 0xf1d>, + <0x40000f20 0xc8>, + <0x40001000 0x1000>, + <0x40100000 0x100000>; + reg-names = "parf", + "dbi", + "elbi", + "atu", + "config"; + ranges = <0x01000000 0x0 0x00000000 0x40200000 0x0 0x100000>, + <0x02000000 0x0 0x40300000 0x40300000 0x0 0x3fd00000>; + + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + + #address-cells = <3>; + #size-cells = <2>; + + interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi", + "msi2", + "msi3", + "msi4", + "msi5", + "msi6", + "msi7", + "msi8"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc GCC_PCIE_PIPE_CLK>, + <&gcc GCC_PCIE_AUX_CLK>, + <&gcc GCC_PCIE_CFG_AHB_CLK>, + <&gcc GCC_PCIE_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_SLV_AXI_CLK>, + <&gcc GCC_PCIE_SLV_Q2A_AXI_CLK>, + <&gcc GCC_PCIE_SLEEP_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "sleep"; + + assigned-clocks = <&gcc GCC_PCIE_AUX_CLK>; + assigned-clock-rates = <19200000>; + + iommu-map = <0x0 &apps_smmu 0x0200 0x1>, + <0x100 &apps_smmu 0x0201 0x1>, + <0x200 &apps_smmu 0x0202 0x1>, + <0x300 &apps_smmu 0x0203 0x1>, + <0x400 &apps_smmu 0x0204 0x1>; + + power-domains = <&gcc PCIE_GDSC>; + + phys = <&pcie_phy>; + phy-names = "pciephy"; + + resets = <&gcc GCC_PCIE_BCR>; + reset-names = "pci"; + + perst-gpios = <&tlmm 57 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>; + + pcie@0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + bus-range = <0x01 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml index 6a5421e4f19d..ea29d0900a25 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml @@ -17,6 +17,7 @@ description: properties: compatible: oneOf: + - const: qcom,pcie-sc8180x - const: qcom,pcie-sm8150 - items: - enum: diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml index 62c674ca0cf7..3d3b9f309a73 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml @@ -16,7 +16,12 @@ description: properties: compatible: - const: qcom,pcie-x1e80100 + oneOf: + - const: qcom,pcie-x1e80100 + - items: + - enum: + - qcom,glymur-pcie + - const: qcom,pcie-x1e80100 reg: minItems: 6 diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml deleted file mode 100644 index c61930441be0..000000000000 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml +++ /dev/null @@ -1,782 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/pci/qcom,pcie.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm PCI express root complex - -maintainers: - - Bjorn Andersson <bjorn.andersson@linaro.org> - - Manivannan Sadhasivam <mani@kernel.org> - -description: | - Qualcomm PCIe root complex controller is based on the Synopsys DesignWare - PCIe IP. - -properties: - compatible: - oneOf: - - enum: - - qcom,pcie-apq8064 - - qcom,pcie-apq8084 - - qcom,pcie-ipq4019 - - qcom,pcie-ipq5018 - - qcom,pcie-ipq6018 - - qcom,pcie-ipq8064 - - qcom,pcie-ipq8064-v2 - - qcom,pcie-ipq8074 - - qcom,pcie-ipq8074-gen3 - - qcom,pcie-ipq9574 - - qcom,pcie-msm8996 - - qcom,pcie-qcs404 - - qcom,pcie-sdm845 - - qcom,pcie-sdx55 - - items: - - enum: - - qcom,pcie-ipq5332 - - qcom,pcie-ipq5424 - - const: qcom,pcie-ipq9574 - - items: - - const: qcom,pcie-msm8998 - - const: qcom,pcie-msm8996 - - reg: - minItems: 4 - maxItems: 6 - - reg-names: - minItems: 4 - maxItems: 6 - - interrupts: - minItems: 1 - maxItems: 9 - - interrupt-names: - minItems: 1 - maxItems: 9 - - iommu-map: - minItems: 1 - maxItems: 16 - - # Common definitions for clocks, clock-names and reset. - # Platform constraints are described later. - clocks: - minItems: 3 - maxItems: 13 - - clock-names: - minItems: 3 - maxItems: 13 - - dma-coherent: true - - interconnects: - maxItems: 2 - - interconnect-names: - items: - - const: pcie-mem - - const: cpu-pcie - - resets: - minItems: 1 - maxItems: 12 - - reset-names: - minItems: 1 - maxItems: 12 - - vdda-supply: - description: A phandle to the core analog power supply - - vdda_phy-supply: - description: A phandle to the core analog power supply for PHY - - vdda_refclk-supply: - description: A phandle to the core analog power supply for IC which generates reference clock - - vddpe-3v3-supply: - description: A phandle to the PCIe endpoint power supply - - phys: - maxItems: 1 - - phy-names: - items: - - const: pciephy - - power-domains: - maxItems: 1 - - perst-gpios: - description: GPIO controlled connection to PERST# signal - maxItems: 1 - - required-opps: - maxItems: 1 - - wake-gpios: - description: GPIO controlled connection to WAKE# signal - maxItems: 1 - -required: - - compatible - - reg - - reg-names - - interrupt-map-mask - - interrupt-map - - clocks - - clock-names - -anyOf: - - required: - - interrupts - - interrupt-names - - "#interrupt-cells" - - required: - - msi-map - -allOf: - - $ref: /schemas/pci/pci-host-bridge.yaml# - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8064 - - qcom,pcie-ipq4019 - - qcom,pcie-ipq8064 - - qcom,pcie-ipq8064v2 - - qcom,pcie-ipq8074 - - qcom,pcie-qcs404 - then: - properties: - reg: - minItems: 4 - maxItems: 4 - reg-names: - items: - - const: dbi # DesignWare PCIe registers - - const: elbi # External local bus interface registers - - const: parf # Qualcomm specific registers - - const: config # PCIe configuration space - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq5018 - - qcom,pcie-ipq6018 - - qcom,pcie-ipq8074-gen3 - - qcom,pcie-ipq9574 - then: - properties: - reg: - minItems: 5 - maxItems: 6 - reg-names: - minItems: 5 - items: - - const: dbi # DesignWare PCIe registers - - const: elbi # External local bus interface registers - - const: atu # ATU address space - - const: parf # Qualcomm specific registers - - const: config # PCIe configuration space - - const: mhi # MHI registers - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8084 - - qcom,pcie-msm8996 - - qcom,pcie-sdm845 - then: - properties: - reg: - minItems: 4 - maxItems: 5 - reg-names: - minItems: 4 - items: - - const: parf # Qualcomm specific registers - - const: dbi # DesignWare PCIe registers - - const: elbi # External local bus interface registers - - const: config # PCIe configuration space - - const: mhi # MHI registers - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-sdx55 - then: - properties: - reg: - minItems: 5 - maxItems: 6 - reg-names: - minItems: 5 - items: - - const: parf # Qualcomm specific registers - - const: dbi # DesignWare PCIe registers - - const: elbi # External local bus interface registers - - const: atu # ATU address space - - const: config # PCIe configuration space - - const: mhi # MHI registers - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8064 - - qcom,pcie-ipq8064 - - qcom,pcie-ipq8064v2 - then: - properties: - clocks: - minItems: 3 - maxItems: 5 - clock-names: - minItems: 3 - items: - - const: core # Clocks the pcie hw block - - const: iface # Configuration AHB clock - - const: phy # Clocks the pcie PHY block - - const: aux # Clocks the pcie AUX block, not on apq8064 - - const: ref # Clocks the pcie ref block, not on apq8064 - resets: - minItems: 5 - maxItems: 6 - reset-names: - minItems: 5 - items: - - const: axi # AXI reset - - const: ahb # AHB reset - - const: por # POR reset - - const: pci # PCI reset - - const: phy # PHY reset - - const: ext # EXT reset, not on apq8064 - required: - - vdda-supply - - vdda_phy-supply - - vdda_refclk-supply - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8084 - then: - properties: - clocks: - minItems: 4 - maxItems: 4 - clock-names: - items: - - const: iface # Configuration AHB clock - - const: master_bus # Master AXI clock - - const: slave_bus # Slave AXI clock - - const: aux # Auxiliary (AUX) clock - resets: - maxItems: 1 - reset-names: - items: - - const: core # Core reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq4019 - then: - properties: - clocks: - minItems: 3 - maxItems: 3 - clock-names: - items: - - const: aux # Auxiliary (AUX) clock - - const: master_bus # Master AXI clock - - const: slave_bus # Slave AXI clock - resets: - minItems: 12 - maxItems: 12 - reset-names: - items: - - const: axi_m # AXI master reset - - const: axi_s # AXI slave reset - - const: pipe # PIPE reset - - const: axi_m_vmid # VMID reset - - const: axi_s_xpu # XPU reset - - const: parf # PARF reset - - const: phy # PHY reset - - const: axi_m_sticky # AXI sticky reset - - const: pipe_sticky # PIPE sticky reset - - const: pwr # PWR reset - - const: ahb # AHB reset - - const: phy_ahb # PHY AHB reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq5018 - then: - properties: - clocks: - minItems: 6 - maxItems: 6 - clock-names: - items: - - const: iface # PCIe to SysNOC BIU clock - - const: axi_m # AXI Master clock - - const: axi_s # AXI Slave clock - - const: ahb # AHB clock - - const: aux # Auxiliary clock - - const: axi_bridge # AXI bridge clock - resets: - minItems: 8 - maxItems: 8 - reset-names: - items: - - const: pipe # PIPE reset - - const: sleep # Sleep reset - - const: sticky # Core sticky reset - - const: axi_m # AXI master reset - - const: axi_s # AXI slave reset - - const: ahb # AHB reset - - const: axi_m_sticky # AXI master sticky reset - - const: axi_s_sticky # AXI slave sticky reset - interrupts: - minItems: 9 - maxItems: 9 - interrupt-names: - items: - - const: msi0 - - const: msi1 - - const: msi2 - - const: msi3 - - const: msi4 - - const: msi5 - - const: msi6 - - const: msi7 - - const: global - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-msm8996 - then: - properties: - clocks: - minItems: 5 - maxItems: 5 - clock-names: - items: - - const: pipe # Pipe Clock driving internal logic - - const: aux # Auxiliary (AUX) clock - - const: cfg # Configuration clock - - const: bus_master # Master AXI clock - - const: bus_slave # Slave AXI clock - resets: false - reset-names: false - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq8074 - then: - properties: - clocks: - minItems: 5 - maxItems: 5 - clock-names: - items: - - const: iface # PCIe to SysNOC BIU clock - - const: axi_m # AXI Master clock - - const: axi_s # AXI Slave clock - - const: ahb # AHB clock - - const: aux # Auxiliary clock - resets: - minItems: 7 - maxItems: 7 - reset-names: - items: - - const: pipe # PIPE reset - - const: sleep # Sleep reset - - const: sticky # Core Sticky reset - - const: axi_m # AXI Master reset - - const: axi_s # AXI Slave reset - - const: ahb # AHB Reset - - const: axi_m_sticky # AXI Master Sticky reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq6018 - - qcom,pcie-ipq8074-gen3 - then: - properties: - clocks: - minItems: 5 - maxItems: 5 - clock-names: - items: - - const: iface # PCIe to SysNOC BIU clock - - const: axi_m # AXI Master clock - - const: axi_s # AXI Slave clock - - const: axi_bridge # AXI bridge clock - - const: rchng - resets: - minItems: 8 - maxItems: 8 - reset-names: - items: - - const: pipe # PIPE reset - - const: sleep # Sleep reset - - const: sticky # Core Sticky reset - - const: axi_m # AXI Master reset - - const: axi_s # AXI Slave reset - - const: ahb # AHB Reset - - const: axi_m_sticky # AXI Master Sticky reset - - const: axi_s_sticky # AXI Slave Sticky reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq9574 - then: - properties: - clocks: - minItems: 6 - maxItems: 6 - clock-names: - items: - - const: axi_m # AXI Master clock - - const: axi_s # AXI Slave clock - - const: axi_bridge - - const: rchng - - const: ahb - - const: aux - - resets: - minItems: 8 - maxItems: 8 - reset-names: - items: - - const: pipe # PIPE reset - - const: sticky # Core Sticky reset - - const: axi_s_sticky # AXI Slave Sticky reset - - const: axi_s # AXI Slave reset - - const: axi_m_sticky # AXI Master Sticky reset - - const: axi_m # AXI Master reset - - const: aux # AUX Reset - - const: ahb # AHB Reset - - interrupts: - minItems: 8 - interrupt-names: - minItems: 8 - items: - - const: msi0 - - const: msi1 - - const: msi2 - - const: msi3 - - const: msi4 - - const: msi5 - - const: msi6 - - const: msi7 - - const: global - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-qcs404 - then: - properties: - clocks: - minItems: 4 - maxItems: 4 - clock-names: - items: - - const: iface # AHB clock - - const: aux # Auxiliary clock - - const: master_bus # AXI Master clock - - const: slave_bus # AXI Slave clock - resets: - minItems: 6 - maxItems: 6 - reset-names: - items: - - const: axi_m # AXI Master reset - - const: axi_s # AXI Slave reset - - const: axi_m_sticky # AXI Master Sticky reset - - const: pipe_sticky # PIPE sticky reset - - const: pwr # PWR reset - - const: ahb # AHB reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-sdm845 - then: - oneOf: - # Unfortunately the "optional" ref clock is used in the middle of the list - - properties: - clocks: - minItems: 8 - maxItems: 8 - clock-names: - items: - - const: pipe # PIPE clock - - const: aux # Auxiliary clock - - const: cfg # Configuration clock - - const: bus_master # Master AXI clock - - const: bus_slave # Slave AXI clock - - const: slave_q2a # Slave Q2A clock - - const: ref # REFERENCE clock - - const: tbu # PCIe TBU clock - - properties: - clocks: - minItems: 7 - maxItems: 7 - clock-names: - items: - - const: pipe # PIPE clock - - const: aux # Auxiliary clock - - const: cfg # Configuration clock - - const: bus_master # Master AXI clock - - const: bus_slave # Slave AXI clock - - const: slave_q2a # Slave Q2A clock - - const: tbu # PCIe TBU clock - properties: - resets: - maxItems: 1 - reset-names: - items: - - const: pci # PCIe core reset - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-sdx55 - then: - properties: - clocks: - minItems: 7 - maxItems: 7 - clock-names: - items: - - const: pipe # PIPE clock - - const: aux # Auxiliary clock - - const: cfg # Configuration clock - - const: bus_master # Master AXI clock - - const: bus_slave # Slave AXI clock - - const: slave_q2a # Slave Q2A clock - - const: sleep # PCIe Sleep clock - resets: - maxItems: 1 - reset-names: - items: - - const: pci # PCIe core reset - - - if: - not: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8064 - - qcom,pcie-ipq4019 - - qcom,pcie-ipq5018 - - qcom,pcie-ipq8064 - - qcom,pcie-ipq8064v2 - - qcom,pcie-ipq8074 - - qcom,pcie-ipq8074-gen3 - - qcom,pcie-ipq9574 - - qcom,pcie-qcs404 - then: - required: - - power-domains - - - if: - not: - properties: - compatible: - contains: - enum: - - qcom,pcie-msm8996 - then: - required: - - resets - - reset-names - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-ipq6018 - - qcom,pcie-ipq8074 - - qcom,pcie-ipq8074-gen3 - - qcom,pcie-msm8996 - - qcom,pcie-msm8998 - - qcom,pcie-sdm845 - then: - oneOf: - - properties: - interrupts: - maxItems: 1 - interrupt-names: - items: - - const: msi - - properties: - interrupts: - minItems: 8 - maxItems: 9 - interrupt-names: - minItems: 8 - items: - - const: msi0 - - const: msi1 - - const: msi2 - - const: msi3 - - const: msi4 - - const: msi5 - - const: msi6 - - const: msi7 - - const: global - - - if: - properties: - compatible: - contains: - enum: - - qcom,pcie-apq8064 - - qcom,pcie-apq8084 - - qcom,pcie-ipq4019 - - qcom,pcie-ipq8064 - - qcom,pcie-ipq8064-v2 - - qcom,pcie-qcs404 - then: - properties: - interrupts: - maxItems: 1 - interrupt-names: - items: - - const: msi - -unevaluatedProperties: false - -examples: - - | - #include <dt-bindings/interrupt-controller/arm-gic.h> - pcie@1b500000 { - compatible = "qcom,pcie-ipq8064"; - reg = <0x1b500000 0x1000>, - <0x1b502000 0x80>, - <0x1b600000 0x100>, - <0x0ff00000 0x100000>; - reg-names = "dbi", "elbi", "parf", "config"; - device_type = "pci"; - linux,pci-domain = <0>; - bus-range = <0x00 0xff>; - num-lanes = <1>; - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000>, - <0x82000000 0 0 0x08000000 0 0x07e00000>; - interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "msi"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&gcc 41>, - <&gcc 43>, - <&gcc 44>, - <&gcc 42>, - <&gcc 248>; - clock-names = "core", "iface", "phy", "aux", "ref"; - resets = <&gcc 27>, - <&gcc 26>, - <&gcc 25>, - <&gcc 24>, - <&gcc 23>, - <&gcc 22>; - reset-names = "axi", "ahb", "por", "pci", "phy", "ext"; - pinctrl-0 = <&pcie_pins_default>; - pinctrl-names = "default"; - vdda-supply = <&pm8921_s3>; - vdda_phy-supply = <&pm8921_lvs6>; - vdda_refclk-supply = <&ext_3p3v>; - }; - - | - #include <dt-bindings/interrupt-controller/arm-gic.h> - #include <dt-bindings/gpio/gpio.h> - pcie@fc520000 { - compatible = "qcom,pcie-apq8084"; - reg = <0xfc520000 0x2000>, - <0xff000000 0x1000>, - <0xff001000 0x1000>, - <0xff002000 0x2000>; - reg-names = "parf", "dbi", "elbi", "config"; - device_type = "pci"; - linux,pci-domain = <0>; - bus-range = <0x00 0xff>; - num-lanes = <1>; - #address-cells = <3>; - #size-cells = <2>; - ranges = <0x81000000 0 0 0xff200000 0 0x00100000>, - <0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; - interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "msi"; - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&gcc 324>, - <&gcc 325>, - <&gcc 327>, - <&gcc 323>; - clock-names = "iface", "master_bus", "slave_bus", "aux"; - resets = <&gcc 81>; - reset-names = "core"; - power-domains = <&gcc 1>; - vdda-supply = <&pma8084_l3>; - phys = <&pciephy0>; - phy-names = "pciephy"; - perst-gpios = <&tlmm 70 GPIO_ACTIVE_LOW>; - pinctrl-0 = <&pcie0_pins_default>; - pinctrl-names = "default"; - }; -... diff --git a/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml new file mode 100644 index 000000000000..e338797d5dc2 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/qcom,sa8255p-pcie-ep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm firmware managed PCIe Endpoint Controller + +description: + Qualcomm SA8255p SoC PCIe endpoint controller is based on the Synopsys + DesignWare PCIe IP which is managed by firmware. + +maintainers: + - Manivannan Sadhasivam <mani@kernel.org> + +properties: + compatible: + const: qcom,sa8255p-pcie-ep + + reg: + items: + - description: Qualcomm-specific PARF configuration registers + - description: DesignWare PCIe registers + - description: External local bus interface registers + - description: Address Translation Unit (ATU) registers + - description: Memory region used to map remote RC address space + - description: BAR memory region + - description: DMA register space + + reg-names: + items: + - const: parf + - const: dbi + - const: elbi + - const: atu + - const: addr_space + - const: mmio + - const: dma + + interrupts: + items: + - description: PCIe Global interrupt + - description: PCIe Doorbell interrupt + - description: DMA interrupt + + interrupt-names: + items: + - const: global + - const: doorbell + - const: dma + + iommus: + maxItems: 1 + + reset-gpios: + description: GPIO used as PERST# input signal + maxItems: 1 + + wake-gpios: + description: GPIO used as WAKE# output signal + maxItems: 1 + + power-domains: + maxItems: 1 + + dma-coherent: true + + num-lanes: + default: 2 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - reset-gpios + - power-domains + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + soc { + #address-cells = <2>; + #size-cells = <2>; + pcie1_ep: pcie-ep@1c10000 { + compatible = "qcom,sa8255p-pcie-ep"; + reg = <0x0 0x01c10000 0x0 0x3000>, + <0x0 0x60000000 0x0 0xf20>, + <0x0 0x60000f20 0x0 0xa8>, + <0x0 0x60001000 0x0 0x4000>, + <0x0 0x60200000 0x0 0x100000>, + <0x0 0x01c13000 0x0 0x1000>, + <0x0 0x60005000 0x0 0x2000>; + reg-names = "parf", "dbi", "elbi", "atu", "addr_space", "mmio", "dma"; + interrupts = <GIC_SPI 518 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 474 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "global", "doorbell", "dma"; + reset-gpios = <&tlmm 4 GPIO_ACTIVE_LOW>; + wake-gpios = <&tlmm 5 GPIO_ACTIVE_LOW>; + dma-coherent; + iommus = <&pcie_smmu 0x80 0x7f>; + power-domains = <&scmi6_pd 1>; + num-lanes = <4>; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml index 6339a76499b2..2c4dc04f9984 100644 --- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml +++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml @@ -106,6 +106,12 @@ properties: be connected to a single source of the periodic signal). const: ref - description: + Some dwc wrappers (like i.MX95 PCIes) have two reference clock + inputs, one from an internal PLL, the other from an off-chip crystal + oscillator. If present, 'extref' refers to a reference clock from + an external oscillator. + const: extref + - description: Clock for the PHY registers interface. Originally this is a PHY-viewport-based interface, but some platform may have specifically designed one. diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml index c07b0ed51613..8a2f1eef51bd 100644 --- a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml @@ -51,7 +51,7 @@ properties: phy-names: const: pcie-phy - interrupt-controller: + legacy-interrupt-controller: type: object additionalProperties: false @@ -111,7 +111,7 @@ examples: <0 0 0 3 &pcie_intc 2>, <0 0 0 4 &pcie_intc 3>; - pcie_intc: interrupt-controller { + pcie_intc: legacy-interrupt-controller { #address-cells = <0>; interrupt-controller; #interrupt-cells = <1>; diff --git a/Documentation/trace/events-pci.rst b/Documentation/trace/events-pci.rst new file mode 100644 index 000000000000..03ff4ad30ddf --- /dev/null +++ b/Documentation/trace/events-pci.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========================== +Subsystem Trace Points: PCI +=========================== + +Overview +======== +The PCI tracing system provides tracepoints to monitor critical hardware events +that can impact system performance and reliability. These events normally show +up here: + + /sys/kernel/tracing/events/pci + +Cf. include/trace/events/pci.h for the events definitions. + +Available Tracepoints +===================== + +pci_hp_event +------------ + +Monitors PCI hotplug events including card insertion/removal and link +state changes. +:: + + pci_hp_event "%s slot:%s, event:%s\n" + +**Event Types**: + +* ``LINK_UP`` - PCIe link established +* ``LINK_DOWN`` - PCIe link lost +* ``CARD_PRESENT`` - Card detected in slot +* ``CARD_NOT_PRESENT`` - Card removed from slot + +**Example Usage**:: + + # Enable the tracepoint + echo 1 > /sys/kernel/debug/tracing/events/pci/pci_hp_event/enable + + # Monitor events (the following output is generated when a device is hotplugged) + cat /sys/kernel/debug/tracing/trace_pipe + irq/51-pciehp-88 [001] ..... 1311.177459: pci_hp_event: 0000:00:02.0 slot:10, event:CARD_PRESENT + + irq/51-pciehp-88 [001] ..... 1311.177566: pci_hp_event: 0000:00:02.0 slot:10, event:LINK_UP + +pcie_link_event +--------------- + +Monitors PCIe link speed changes and provides detailed link status information. +:: + + pcie_link_event "%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n" + +**Parameters**: + +* ``type`` - PCIe device type (4=Root Port, etc.) +* ``reason`` - Reason for link change: + + - ``0`` - Link retrain + - ``1`` - Bus enumeration + - ``2`` - Bandwidth notification enable + - ``3`` - Bandwidth notification IRQ + - ``4`` - Hotplug event + + +**Example Usage**:: + + # Enable the tracepoint + echo 1 > /sys/kernel/debug/tracing/events/pci/pcie_link_event/enable + + # Monitor events (the following output is generated when a device is hotplugged) + cat /sys/kernel/debug/tracing/trace_pipe + irq/51-pciehp-88 [001] ..... 381.545386: pcie_link_event: 0000:00:02.0 type:4, reason:4, cur_bus_speed:20, max_bus_speed:23, width:1, flit_mode:0, status:DLLLA diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst index cfd8128ac56d..338bc4d7cfab 100644 --- a/Documentation/trace/index.rst +++ b/Documentation/trace/index.rst @@ -54,6 +54,7 @@ applications. events-power events-nmi events-msr + events-pci boottime-trace histogram histogram-design diff --git a/MAINTAINERS b/MAINTAINERS index 2a13e8e6dbcd..d4b1d09d12ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3922,6 +3922,14 @@ S: Maintained F: Documentation/devicetree/bindings/media/aspeed,video-engine.yaml F: drivers/media/platform/aspeed/ +ASPEED PCIE CONTROLLER DRIVER +M: Jacky Chou <jacky_chou@aspeedtech.com> +L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml +F: drivers/pci/controller/pcie-aspeed.c + ASUS EC HARDWARE MONITOR DRIVER M: Eugene Shalygin <eugene.shalygin@gmail.com> L: linux-hwmon@vger.kernel.org @@ -20503,6 +20511,7 @@ L: linux-pci@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml +F: Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml F: drivers/pci/controller/dwc/pcie-qcom-common.c F: drivers/pci/controller/dwc/pcie-qcom-ep.c diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c index b203a93deac5..aca907a62bb5 100644 --- a/drivers/cpuidle/cpuidle-tegra.c +++ b/drivers/cpuidle/cpuidle-tegra.c @@ -336,6 +336,7 @@ void tegra_cpuidle_pcie_irqs_in_use(void) pr_info("disabling CC6 state, since PCIe IRQs are in use\n"); tegra_cpuidle_disable_state(TEGRA_CC6); } +EXPORT_SYMBOL_GPL(tegra_cpuidle_pcie_irqs_in_use); static void tegra_cpuidle_setup_tegra114_c7_state(void) { diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 1c0fd185114f..74ab5b5b9011 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -39,6 +39,8 @@ #define COMMAND_COPY BIT(5) #define COMMAND_ENABLE_DOORBELL BIT(6) #define COMMAND_DISABLE_DOORBELL BIT(7) +#define COMMAND_BAR_SUBRANGE_SETUP BIT(8) +#define COMMAND_BAR_SUBRANGE_CLEAR BIT(9) #define PCI_ENDPOINT_TEST_STATUS 0x8 #define STATUS_READ_SUCCESS BIT(0) @@ -55,6 +57,10 @@ #define STATUS_DOORBELL_ENABLE_FAIL BIT(11) #define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12) #define STATUS_DOORBELL_DISABLE_FAIL BIT(13) +#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS BIT(14) +#define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15) +#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16) +#define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) #define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c #define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10 @@ -77,6 +83,7 @@ #define CAP_MSI BIT(1) #define CAP_MSIX BIT(2) #define CAP_INTX BIT(3) +#define CAP_SUBRANGE_MAPPING BIT(4) #define PCI_ENDPOINT_TEST_DB_BAR 0x34 #define PCI_ENDPOINT_TEST_DB_OFFSET 0x38 @@ -100,6 +107,8 @@ #define PCI_DEVICE_ID_ROCKCHIP_RK3588 0x3588 +#define PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB 2 + static DEFINE_IDA(pci_endpoint_test_ida); #define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \ @@ -414,6 +423,193 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test) return 0; } +static u8 pci_endpoint_test_subrange_sig_byte(enum pci_barno barno, + unsigned int subno) +{ + return 0x50 + (barno * 8) + subno; +} + +static u8 pci_endpoint_test_subrange_test_byte(enum pci_barno barno, + unsigned int subno) +{ + return 0xa0 + (barno * 8) + subno; +} + +static int pci_endpoint_test_bar_subrange_cmd(struct pci_endpoint_test *test, + enum pci_barno barno, u32 command, + u32 ok_bit, u32 fail_bit) +{ + struct pci_dev *pdev = test->pdev; + struct device *dev = &pdev->dev; + int irq_type = test->irq_type; + u32 status; + + if (irq_type < PCITEST_IRQ_TYPE_INTX || + irq_type > PCITEST_IRQ_TYPE_MSIX) { + dev_err(dev, "Invalid IRQ type\n"); + return -EINVAL; + } + + reinit_completion(&test->irq_raised); + + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1); + /* Reuse SIZE as a command parameter: bar number. */ + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, barno); + pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, command); + + if (!wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000))) + return -ETIMEDOUT; + + status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); + if (status & fail_bit) + return -EIO; + + if (!(status & ok_bit)) + return -EIO; + + return 0; +} + +static int pci_endpoint_test_bar_subrange_setup(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + return pci_endpoint_test_bar_subrange_cmd(test, barno, + COMMAND_BAR_SUBRANGE_SETUP, + STATUS_BAR_SUBRANGE_SETUP_SUCCESS, + STATUS_BAR_SUBRANGE_SETUP_FAIL); +} + +static int pci_endpoint_test_bar_subrange_clear(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + return pci_endpoint_test_bar_subrange_cmd(test, barno, + COMMAND_BAR_SUBRANGE_CLEAR, + STATUS_BAR_SUBRANGE_CLEAR_SUCCESS, + STATUS_BAR_SUBRANGE_CLEAR_FAIL); +} + +static int pci_endpoint_test_bar_subrange(struct pci_endpoint_test *test, + enum pci_barno barno) +{ + u32 nsub = PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB; + struct device *dev = &test->pdev->dev; + size_t sub_size, buf_size; + resource_size_t bar_size; + void __iomem *bar_addr; + void *read_buf = NULL; + int ret, clear_ret; + size_t off, chunk; + u32 i, exp, val; + u8 pattern; + + if (!(test->ep_caps & CAP_SUBRANGE_MAPPING)) + return -EOPNOTSUPP; + + /* + * The test register BAR is not safe to reprogram and write/read + * over its full size. BAR_TEST already special-cases it to a tiny + * range. For subrange mapping tests, let's simply skip it. + */ + if (barno == test->test_reg_bar) + return -EBUSY; + + bar_size = pci_resource_len(test->pdev, barno); + if (!bar_size) + return -ENODATA; + + bar_addr = test->bar[barno]; + if (!bar_addr) + return -ENOMEM; + + ret = pci_endpoint_test_bar_subrange_setup(test, barno); + if (ret) + return ret; + + if (bar_size % nsub || bar_size / nsub > SIZE_MAX) { + ret = -EINVAL; + goto out_clear; + } + + sub_size = bar_size / nsub; + if (sub_size < sizeof(u32)) { + ret = -ENOSPC; + goto out_clear; + } + + /* Limit the temporary buffer size */ + buf_size = min_t(size_t, sub_size, SZ_1M); + + read_buf = kmalloc(buf_size, GFP_KERNEL); + if (!read_buf) { + ret = -ENOMEM; + goto out_clear; + } + + /* + * Step 1: verify EP-provided signature per subrange. This detects + * whether the EP actually applied the submap order. + */ + for (i = 0; i < nsub; i++) { + exp = (u32)pci_endpoint_test_subrange_sig_byte(barno, i) * + 0x01010101U; + val = ioread32(bar_addr + (i * sub_size)); + if (val != exp) { + dev_err(dev, + "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n", + barno, i, (size_t)i * sub_size, exp, val); + ret = -EIO; + goto out_clear; + } + val = ioread32(bar_addr + (i * sub_size) + sub_size - sizeof(u32)); + if (val != exp) { + dev_err(dev, + "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n", + barno, i, + ((size_t)i * sub_size) + sub_size - sizeof(u32), + exp, val); + ret = -EIO; + goto out_clear; + } + } + + /* Step 2: write unique pattern per subrange (write all first). */ + for (i = 0; i < nsub; i++) { + pattern = pci_endpoint_test_subrange_test_byte(barno, i); + memset_io(bar_addr + (i * sub_size), pattern, sub_size); + } + + /* Step 3: read back and verify (read all after all writes). */ + for (i = 0; i < nsub; i++) { + pattern = pci_endpoint_test_subrange_test_byte(barno, i); + for (off = 0; off < sub_size; off += chunk) { + void *bad; + + chunk = min_t(size_t, buf_size, sub_size - off); + memcpy_fromio(read_buf, bar_addr + (i * sub_size) + off, + chunk); + bad = memchr_inv(read_buf, pattern, chunk); + if (bad) { + size_t bad_off = (u8 *)bad - (u8 *)read_buf; + + dev_err(dev, + "BAR%d subrange%u data mismatch @%#zx (pattern %#02x)\n", + barno, i, (size_t)i * sub_size + off + bad_off, + pattern); + ret = -EIO; + goto out_clear; + } + } + } + +out_clear: + kfree(read_buf); + clear_ret = pci_endpoint_test_bar_subrange_clear(test, barno); + return ret ?: clear_ret; +} + static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test) { u32 val; @@ -936,12 +1132,17 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case PCITEST_BAR: + case PCITEST_BAR_SUBRANGE: bar = arg; if (bar <= NO_BAR || bar > BAR_5) goto ret; if (is_am654_pci_dev(pdev) && bar == BAR_0) goto ret; - ret = pci_endpoint_test_bar(test, bar); + + if (cmd == PCITEST_BAR) + ret = pci_endpoint_test_bar(test, bar); + else + ret = pci_endpoint_test_bar_subrange(test, bar); break; case PCITEST_BARS: ret = pci_endpoint_test_bars(test); diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e10cfe5a280b..41ebc3b9a518 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_TSM) += tsm.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_NPEM) += npem.o obj-$(CONFIG_PCIE_TPH) += tph.o +obj-$(CONFIG_CARDBUS) += setup-cardbus.o # Endpoint library must be initialized before its users obj-$(CONFIG_PCI_ENDPOINT) += endpoint/ @@ -47,3 +48,6 @@ obj-y += controller/ obj-y += switch/ subdir-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG + +CFLAGS_trace.o := -I$(src) +obj-$(CONFIG_TRACING) += trace.o diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 4383a36fd6ca..51af9e6c541c 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -15,6 +15,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -344,7 +345,6 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } void pci_bus_add_device(struct pci_dev *dev) { struct device_node *dn = dev->dev.of_node; - struct platform_device *pdev; /* * Can not put in pci_device_add yet because resources @@ -362,22 +362,11 @@ void pci_bus_add_device(struct pci_dev *dev) pci_save_state(dev); /* - * If the PCI device is associated with a pwrctrl device with a - * power supply, create a device link between the PCI device and - * pwrctrl device. This ensures that pwrctrl drivers are probed - * before PCI client drivers. + * Enable runtime PM, which potentially allows the device to + * suspend immediately, only after the PCI state has been + * configured completely. */ - pdev = of_find_device_by_node(dn); - if (pdev) { - if (of_pci_supply_present(dn)) { - if (!device_link_add(&dev->dev, &pdev->dev, - DL_FLAG_AUTOREMOVE_CONSUMER)) { - pci_err(dev, "failed to add device link to power control device %s\n", - pdev->name); - } - } - put_device(&pdev->dev); - } + pm_runtime_enable(&dev->dev); if (!dn || of_device_is_available(dn)) pci_dev_allow_binding(dev); diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index c254d2b8bf17..5aaed8ac6e44 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -58,6 +58,22 @@ config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE || COMPILE_TEST +config PCIE_ASPEED + bool "ASPEED PCIe controller" + depends on ARCH_ASPEED || COMPILE_TEST + depends on OF + depends on PCI_MSI + select IRQ_MSI_LIB + help + Enable this option to support the PCIe controller found on ASPEED + SoCs. + + This driver provides initialization and management for PCIe + Root Complex functionality, including INTx and MSI support. + + Select Y if your platform uses an ASPEED SoC and requires PCIe + connectivity. + config PCIE_BRCMSTB tristate "Broadcom Brcmstb PCIe controller" depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCMBCA || \ @@ -232,7 +248,7 @@ config PCI_HYPERV_INTERFACE driver. config PCI_TEGRA - bool "NVIDIA Tegra PCIe controller" + tristate "NVIDIA Tegra PCIe controller" depends on ARCH_TEGRA || COMPILE_TEST depends on PCI_MSI select IRQ_MSI_LIB @@ -243,6 +259,7 @@ config PCI_TEGRA config PCIE_RCAR_HOST bool "Renesas R-Car PCIe controller (host mode)" depends on ARCH_RENESAS || COMPILE_TEST + depends on OF depends on PCI_MSI select IRQ_MSI_LIB help diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 229929a945c2..ac8db283f0fe 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCIE_ASPEED) += pcie-aspeed.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index ecd1b0312400..6f2501479c70 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -620,9 +620,11 @@ static int j721e_pcie_probe(struct platform_device *pdev) gpiod_set_value_cansleep(pcie->reset_gpio, 1); } - ret = cdns_pcie_host_setup(rc); - if (ret < 0) - goto err_pcie_setup; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_setup(rc); + if (ret < 0) + goto err_pcie_setup; + } break; case PCI_MODE_EP: @@ -632,9 +634,11 @@ static int j721e_pcie_probe(struct platform_device *pdev) goto err_get_sync; } - ret = cdns_pcie_ep_setup(ep); - if (ret < 0) - goto err_pcie_setup; + if (IS_ENABLED(CONFIG_PCI_J721E_EP)) { + ret = cdns_pcie_ep_setup(ep); + if (ret < 0) + goto err_pcie_setup; + } break; } @@ -659,10 +663,11 @@ static void j721e_pcie_remove(struct platform_device *pdev) struct cdns_pcie_ep *ep; struct cdns_pcie_rc *rc; - if (pcie->mode == PCI_MODE_RC) { + if (IS_ENABLED(CONFIG_PCI_J721E_HOST) && + pcie->mode == PCI_MODE_RC) { rc = container_of(cdns_pcie, struct cdns_pcie_rc, pcie); cdns_pcie_host_disable(rc); - } else { + } else if (IS_ENABLED(CONFIG_PCI_J721E_EP)) { ep = container_of(cdns_pcie, struct cdns_pcie_ep, pcie); cdns_pcie_ep_disable(ep); } @@ -728,10 +733,12 @@ static int j721e_pcie_resume_noirq(struct device *dev) gpiod_set_value_cansleep(pcie->reset_gpio, 1); } - ret = cdns_pcie_host_link_setup(rc); - if (ret < 0) { - clk_disable_unprepare(pcie->refclk); - return ret; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_link_setup(rc); + if (ret < 0) { + clk_disable_unprepare(pcie->refclk); + return ret; + } } /* @@ -741,10 +748,12 @@ static int j721e_pcie_resume_noirq(struct device *dev) for (enum cdns_pcie_rp_bar bar = RP_BAR0; bar <= RP_NO_BAR; bar++) rc->avail_ib_bar[bar] = true; - ret = cdns_pcie_host_init(rc); - if (ret) { - clk_disable_unprepare(pcie->refclk); - return ret; + if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) { + ret = cdns_pcie_host_init(rc); + if (ret) { + clk_disable_unprepare(pcie->refclk); + return ret; + } } } diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c index 15415d7f35ee..2b0211870f02 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c @@ -173,11 +173,21 @@ int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a, const struct list_head *b) { struct resource_entry *entry1, *entry2; + u64 size1, size2; entry1 = container_of(a, struct resource_entry, node); entry2 = container_of(b, struct resource_entry, node); - return resource_size(entry2->res) - resource_size(entry1->res); + size1 = resource_size(entry1->res); + size2 = resource_size(entry2->res); + + if (size1 > size2) + return -1; + + if (size1 < size2) + return 1; + + return 0; } EXPORT_SYMBOL_GPL(cdns_pcie_host_dma_ranges_cmp); diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index e6f1a4ac0fb7..a1eada56edba 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -13,13 +13,13 @@ u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap) { return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg, PCI_CAPABILITY_LIST, - cap, pcie); + cap, NULL, pcie); } EXPORT_SYMBOL_GPL(cdns_pcie_find_capability); u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap) { - return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, pcie); + return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, NULL, pcie); } EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability); diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 519b59422b47..d0aa031397fa 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -228,7 +228,7 @@ config PCIE_TEGRA194 config PCIE_TEGRA194_HOST tristate "NVIDIA Tegra194 (and later) PCIe controller (host mode)" - depends on ARCH_TEGRA_194_SOC || COMPILE_TEST + depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST depends on PCI_MSI select PCIE_DW_HOST select PHY_TEGRA194_P2U @@ -243,7 +243,7 @@ config PCIE_TEGRA194_HOST config PCIE_TEGRA194_EP tristate "NVIDIA Tegra194 (and later) PCIe controller (endpoint mode)" - depends on ARCH_TEGRA_194_SOC || COMPILE_TEST + depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST depends on PCI_ENDPOINT select PCIE_DW_EP select PHY_TEGRA194_P2U diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 01cfd9aeb0b8..d5d26229063f 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features dra7xx_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, }; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 4668fc9648bf..a5b8d0b71677 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -52,6 +52,8 @@ #define IMX95_PCIE_REF_CLKEN BIT(23) #define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9) #define IMX95_PCIE_SS_RW_REG_1 0xf4 +#define IMX95_PCIE_CLKREQ_OVERRIDE_EN BIT(8) +#define IMX95_PCIE_CLKREQ_OVERRIDE_VAL BIT(9) #define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31) #define IMX95_PE0_GEN_CTRL_1 0x1050 @@ -114,6 +116,7 @@ enum imx_pcie_variants { #define IMX_PCIE_FLAG_BROKEN_SUSPEND BIT(9) #define IMX_PCIE_FLAG_HAS_LUT BIT(10) #define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11) +#define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12) #define imx_check_flag(pci, val) (pci->drvdata->flags & val) @@ -136,6 +139,7 @@ struct imx_pcie_drvdata { int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable); int (*core_reset)(struct imx_pcie *pcie, bool assert); int (*wait_pll_lock)(struct imx_pcie *pcie); + void (*clr_clkreq_override)(struct imx_pcie *pcie); const struct dw_pcie_host_ops *ops; }; @@ -149,6 +153,8 @@ struct imx_pcie { struct gpio_desc *reset_gpiod; struct clk_bulk_data *clks; int num_clks; + bool supports_clkreq; + bool enable_ext_refclk; struct regmap *iomuxc_gpr; u16 msi_ctrl; u32 controller_id; @@ -241,6 +247,8 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie) static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie) { + bool ext = imx_pcie->enable_ext_refclk; + /* * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready * Through Beacon or PERST# De-assertion @@ -259,13 +267,12 @@ 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, 0); - regmap_update_bits(imx_pcie->iomuxc_gpr, - IMX95_PCIE_SS_RW_REG_0, + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL, + ext ? IMX95_PCIE_REF_USE_PAD : 0, + IMX95_PCIE_REF_USE_PAD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, IMX95_PCIE_REF_CLKEN, - IMX95_PCIE_REF_CLKEN); + ext ? 0 : IMX95_PCIE_REF_CLKEN); return 0; } @@ -685,7 +692,7 @@ static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } -static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +static void imx8mm_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) { int offset = imx_pcie_grp_offset(imx_pcie); @@ -695,6 +702,11 @@ static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) regmap_update_bits(imx_pcie->iomuxc_gpr, offset, IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN, enable ? IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0); +} + +static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx8mm_pcie_clkreq_override(imx_pcie, enable); return 0; } @@ -706,6 +718,32 @@ static int imx7d_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) return 0; } +static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable) +{ + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_EN, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_EN : 0); + regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1, + IMX95_PCIE_CLKREQ_OVERRIDE_VAL, + enable ? IMX95_PCIE_CLKREQ_OVERRIDE_VAL : 0); +} + +static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) +{ + imx95_pcie_clkreq_override(imx_pcie, enable); + return 0; +} + +static void imx8mm_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx8mm_pcie_clkreq_override(imx_pcie, false); +} + +static void imx95_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie) +{ + imx95_pcie_clkreq_override(imx_pcie, false); +} + static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie) { struct dw_pcie *pci = imx_pcie->pci; @@ -1322,6 +1360,12 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp) dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val); dw_pcie_dbi_ro_wr_dis(pci); } + + /* Clear CLKREQ# override if supports_clkreq is true and link is up */ + if (dw_pcie_link_up(pci) && imx_pcie->supports_clkreq) { + if (imx_pcie->drvdata->clr_clkreq_override) + imx_pcie->drvdata->clr_clkreq_override(imx_pcie); + } } /* @@ -1387,6 +1431,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features imx8m_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, @@ -1396,6 +1441,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = { }; static const struct pci_epc_features imx8q_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, @@ -1416,6 +1462,7 @@ static const struct pci_epc_features imx8q_pcie_epc_features = { * BAR5 | Enable | 32-bit | 64 KB | Programmable Size */ static const struct pci_epc_features imx95_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, }, .align = SZ_4K, @@ -1602,7 +1649,7 @@ static int imx_pcie_probe(struct platform_device *pdev) struct imx_pcie *imx_pcie; struct device_node *np; struct device_node *node = dev->of_node; - int ret, domain; + int i, ret, domain; u16 val; imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL); @@ -1653,6 +1700,9 @@ static int imx_pcie_probe(struct platform_device *pdev) if (imx_pcie->num_clks < 0) return dev_err_probe(dev, imx_pcie->num_clks, "failed to get clocks\n"); + for (i = 0; i < imx_pcie->num_clks; i++) + if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0) + imx_pcie->enable_ext_refclk = true; if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) { imx_pcie->phy = devm_phy_get(dev, "pcie-phy"); @@ -1740,6 +1790,7 @@ static int imx_pcie_probe(struct platform_device *pdev) /* Limit link speed */ pci->max_link_speed = 1; 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) @@ -1777,6 +1828,8 @@ static int imx_pcie_probe(struct platform_device *pdev) */ imx_pcie_add_lut_by_rid(imx_pcie, 0); } else { + if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY)) + pci->pp.skip_l23_ready = true; pci->pp.use_atu_msg = true; ret = dw_pcie_host_init(&pci->pp); if (ret < 0) @@ -1838,6 +1891,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX6QP, .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_SUPPORTS_SUSPEND, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", @@ -1854,6 +1908,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .variant = IMX7D, .flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND | IMX_PCIE_FLAG_HAS_APP_RESET | + IMX_PCIE_FLAG_SKIP_L23_READY | IMX_PCIE_FLAG_HAS_PHY_RESET, .gpr = "fsl,imx7d-iomuxc-gpr", .mode_off[0] = IOMUXC_GPR12, @@ -1873,6 +1928,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE, .init_phy = imx8mq_pcie_init_phy, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MM] = { .variant = IMX8MM, @@ -1883,6 +1939,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8MP] = { .variant = IMX8MP, @@ -1893,6 +1950,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .mode_off[0] = IOMUXC_GPR12, .mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE, .enable_ref_clk = imx8mm_pcie_enable_ref_clk, + .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override, }, [IMX8Q] = { .variant = IMX8Q, @@ -1913,6 +1971,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .init_phy = imx95_pcie_init_phy, .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, }, [IMX8MQ_EP] = { .variant = IMX8MQ_EP, @@ -1969,6 +2029,7 @@ static const struct imx_pcie_drvdata drvdata[] = { .core_reset = imx95_pcie_core_reset, .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock, .epc_features = &imx95_pcie_epc_features, + .enable_ref_clk = imx95_pcie_enable_ref_clk, .mode = DW_PCIE_EP_TYPE, }, }; diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index f86d9111f863..20fa4dadb82a 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -930,6 +930,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features ks_pcie_am654_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .type = BAR_RESERVED, }, diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index f4a136ee2daf..e994b75986c3 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -370,6 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features artpec6_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, }; diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c index 0fbf86c0b97e..0d1340c9b364 100644 --- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c +++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c @@ -443,63 +443,13 @@ static ssize_t counter_value_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos); } -static const char *ltssm_status_string(enum dw_pcie_ltssm ltssm) -{ - const char *str; - - switch (ltssm) { -#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); - DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); - default: - str = "DW_PCIE_LTSSM_UNKNOWN"; - break; - } - - return str + strlen("DW_PCIE_LTSSM_"); -} - static int ltssm_status_show(struct seq_file *s, void *v) { struct dw_pcie *pci = s->private; enum dw_pcie_ltssm val; val = dw_pcie_get_ltssm(pci); - seq_printf(s, "%s (0x%02x)\n", ltssm_status_string(val), val); + seq_printf(s, "%s (0x%02x)\n", dw_pcie_ltssm_status_string(val), val); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 19571ac2b961..7e7844ff0f7e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -72,47 +72,15 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) { return PCI_FIND_NEXT_CAP(dw_pcie_ep_read_cfg, PCI_CAPABILITY_LIST, - cap, ep, func_no); + cap, NULL, ep, func_no); } -/** - * dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list - * @pci: DWC PCI device - * @prev_cap: Capability preceding the capability that should be hidden - * @cap: Capability that should be hidden - * - * Return: 0 if success, errno otherwise. - */ -int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap) +static u16 dw_pcie_ep_find_ext_capability(struct dw_pcie_ep *ep, + u8 func_no, u8 cap) { - u16 prev_cap_offset, cap_offset; - u32 prev_cap_header, cap_header; - - prev_cap_offset = dw_pcie_find_ext_capability(pci, prev_cap); - if (!prev_cap_offset) - return -EINVAL; - - prev_cap_header = dw_pcie_readl_dbi(pci, prev_cap_offset); - cap_offset = PCI_EXT_CAP_NEXT(prev_cap_header); - cap_header = dw_pcie_readl_dbi(pci, cap_offset); - - /* cap must immediately follow prev_cap. */ - if (PCI_EXT_CAP_ID(cap_header) != cap) - return -EINVAL; - - /* Clear next ptr. */ - prev_cap_header &= ~GENMASK(31, 20); - - /* Set next ptr to next ptr of cap. */ - prev_cap_header |= cap_header & GENMASK(31, 20); - - dw_pcie_dbi_ro_wr_en(pci); - dw_pcie_writel_dbi(pci, prev_cap_offset, prev_cap_header); - dw_pcie_dbi_ro_wr_dis(pci); - - return 0; + return PCI_FIND_NEXT_EXT_CAP(dw_pcie_ep_read_cfg, 0, + cap, NULL, ep, func_no); } -EXPORT_SYMBOL_GPL(dw_pcie_ep_hide_ext_capability); static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_header *hdr) @@ -139,18 +107,23 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, return 0; } -static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, - dma_addr_t parent_bus_addr, enum pci_barno bar, - size_t size) +/* BAR Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type, + dma_addr_t parent_bus_addr, enum pci_barno bar, + size_t size) { int ret; u32 free_win; struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); - if (!ep->bar_to_atu[bar]) + if (!ep_func) + return -EINVAL; + + if (!ep_func->bar_to_atu[bar]) free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows); else - free_win = ep->bar_to_atu[bar] - 1; + free_win = ep_func->bar_to_atu[bar] - 1; if (free_win >= pci->num_ib_windows) { dev_err(pci->dev, "No free inbound window\n"); @@ -168,12 +141,190 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type, * Always increment free_win before assignment, since value 0 is used to identify * unallocated mapping. */ - ep->bar_to_atu[bar] = free_win + 1; + ep_func->bar_to_atu[bar] = free_win + 1; set_bit(free_win, ep->ib_window_map); return 0; } +static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci_barno bar) +{ + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + struct device *dev = pci->dev; + unsigned int i, num; + u32 atu_index; + u32 *indexes; + + if (!ep_func) + return; + + /* Tear down the BAR Match Mode mapping, if any. */ + if (ep_func->bar_to_atu[bar]) { + atu_index = ep_func->bar_to_atu[bar] - 1; + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); + clear_bit(atu_index, ep->ib_window_map); + ep_func->bar_to_atu[bar] = 0; + } + + /* Tear down all Address Match Mode mappings, if any. */ + indexes = ep_func->ib_atu_indexes[bar]; + num = ep_func->num_ib_atu_indexes[bar]; + ep_func->ib_atu_indexes[bar] = NULL; + ep_func->num_ib_atu_indexes[bar] = 0; + if (!indexes) + return; + for (i = 0; i < num; i++) { + dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]); + clear_bit(indexes[i], ep->ib_window_map); + } + devm_kfree(dev, indexes); +} + +static u64 dw_pcie_ep_read_bar_assigned(struct dw_pcie_ep *ep, u8 func_no, + enum pci_barno bar, int flags) +{ + u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); + u32 lo, hi; + u64 addr; + + lo = dw_pcie_ep_readl_dbi(ep, func_no, reg); + + if (flags & PCI_BASE_ADDRESS_SPACE) + return lo & PCI_BASE_ADDRESS_IO_MASK; + + addr = lo & PCI_BASE_ADDRESS_MEM_MASK; + if (!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) + return addr; + + hi = dw_pcie_ep_readl_dbi(ep, func_no, reg + 4); + return addr | ((u64)hi << 32); +} + +static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep, + const struct pci_epf_bar_submap *submap, + unsigned int num_submap, size_t bar_size) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + u32 align = pci->region_align; + size_t off = 0; + unsigned int i; + size_t size; + + if (!align || !IS_ALIGNED(bar_size, align)) + return -EINVAL; + + /* + * The submap array order defines the BAR layout (submap[0] starts + * at offset 0 and each entry immediately follows the previous + * one). Here, validate that it forms a strict, gapless + * decomposition of the BAR: + * - each entry has a non-zero size + * - sizes, implicit offsets and phys_addr are aligned to + * pci->region_align + * - each entry lies within the BAR range + * - the entries exactly cover the whole BAR + * + * Note: dw_pcie_prog_inbound_atu() also checks alignment for the + * PCI address and the target phys_addr, but validating up-front + * avoids partially programming iATU windows in vain. + */ + for (i = 0; i < num_submap; i++) { + size = submap[i].size; + + if (!size) + return -EINVAL; + + if (!IS_ALIGNED(size, align) || !IS_ALIGNED(off, align)) + return -EINVAL; + + if (!IS_ALIGNED(submap[i].phys_addr, align)) + return -EINVAL; + + if (off > bar_size || size > bar_size - off) + return -EINVAL; + + off += size; + } + if (off != bar_size) + return -EINVAL; + + return 0; +} + +/* Address Match Mode inbound iATU mapping */ +static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type, + const struct pci_epf_bar *epf_bar) +{ + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + const struct pci_epf_bar_submap *submap = epf_bar->submap; + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar = epf_bar->barno; + struct device *dev = pci->dev; + u64 pci_addr, parent_bus_addr; + u64 size, base, off = 0; + int free_win, ret; + unsigned int i; + u32 *indexes; + + if (!ep_func || !epf_bar->num_submap || !submap || !epf_bar->size) + return -EINVAL; + + ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap, + epf_bar->size); + if (ret) + return ret; + + base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar, epf_bar->flags); + if (!base) { + dev_err(dev, + "BAR%u not assigned, cannot set up sub-range mappings\n", + bar); + return -EINVAL; + } + + indexes = devm_kcalloc(dev, epf_bar->num_submap, sizeof(*indexes), + GFP_KERNEL); + if (!indexes) + return -ENOMEM; + + ep_func->ib_atu_indexes[bar] = indexes; + ep_func->num_ib_atu_indexes[bar] = 0; + + for (i = 0; i < epf_bar->num_submap; i++) { + size = submap[i].size; + parent_bus_addr = submap[i].phys_addr; + + if (off > (~0ULL) - base) { + ret = -EINVAL; + goto err; + } + + pci_addr = base + off; + off += size; + + free_win = find_first_zero_bit(ep->ib_window_map, + pci->num_ib_windows); + if (free_win >= pci->num_ib_windows) { + ret = -ENOSPC; + goto err; + } + + ret = dw_pcie_prog_inbound_atu(pci, free_win, type, + parent_bus_addr, pci_addr, size); + if (ret) + goto err; + + set_bit(free_win, ep->ib_window_map); + indexes[i] = free_win; + ep_func->num_ib_atu_indexes[bar] = i + 1; + } + return 0; +err: + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + return ret; +} + static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, struct dw_pcie_ob_atu_cfg *atu) { @@ -204,35 +355,34 @@ static void dw_pcie_ep_clear_bar(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); enum pci_barno bar = epf_bar->barno; - u32 atu_index = ep->bar_to_atu[bar] - 1; + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); - if (!ep->bar_to_atu[bar]) + if (!ep_func || !ep_func->epf_bar[bar]) return; __dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags); - dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index); - clear_bit(atu_index, ep->ib_window_map); - ep->epf_bar[bar] = NULL; - ep->bar_to_atu[bar] = 0; + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + + ep_func->epf_bar[bar] = NULL; } -static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci, +static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie_ep *ep, u8 func_no, enum pci_barno bar) { u32 reg, bar_index; unsigned int offset, nbars; int i; - offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR); if (!offset) return offset; - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg); for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) { - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); bar_index = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, reg); if (bar_index == bar) return offset; @@ -253,7 +403,7 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no, u32 rebar_cap, rebar_ctrl; int ret; - rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar); + rebar_offset = dw_pcie_ep_get_rebar_offset(ep, func_no, bar); if (!rebar_offset) return -EINVAL; @@ -283,16 +433,16 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no, * 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes" * bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB. */ - rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL); + rebar_ctrl = dw_pcie_ep_readl_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL); rebar_ctrl &= ~GENMASK(31, 16); - dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl); + dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl); /* * The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically * updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR * Example for 32-bit Memory BAR0" in DWC EP databook 5.96a. */ - dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap); + dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CAP, rebar_cap); dw_pcie_dbi_ro_wr_dis(pci); @@ -341,12 +491,16 @@ static int dw_pcie_ep_set_bar(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_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); enum pci_barno bar = epf_bar->barno; size_t size = epf_bar->size; enum pci_epc_bar_type bar_type; int flags = epf_bar->flags; int ret, type; + if (!ep_func) + return -EINVAL; + /* * DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs * 1 and 2 to form a 64-bit BAR. @@ -360,21 +514,38 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, * calling clear_bar() would clear the BAR's PCI address assigned by the * host). */ - if (ep->epf_bar[bar]) { + if (ep_func->epf_bar[bar]) { /* * We can only dynamically change a BAR if the new BAR size and * BAR flags do not differ from the existing configuration. */ - if (ep->epf_bar[bar]->barno != bar || - ep->epf_bar[bar]->size != size || - ep->epf_bar[bar]->flags != flags) + if (ep_func->epf_bar[bar]->barno != bar || + ep_func->epf_bar[bar]->size != size || + ep_func->epf_bar[bar]->flags != flags) return -EINVAL; /* + * When dynamically changing a BAR, tear down any existing + * mappings before re-programming. + */ + if (ep_func->epf_bar[bar]->num_submap || epf_bar->num_submap) + dw_pcie_ep_clear_ib_maps(ep, func_no, bar); + + /* * When dynamically changing a BAR, skip writing the BAR reg, as * that would clear the BAR's PCI address assigned by the host. */ goto config_atu; + } else { + /* + * Subrange mapping is an update-only operation. The BAR + * must have been configured once without submaps so that + * subsequent set_bar() calls can update inbound mappings + * without touching the BAR register (and clobbering the + * host-assigned address). + */ + if (epf_bar->num_submap) + return -EINVAL; } bar_type = dw_pcie_ep_get_bar_type(ep, bar); @@ -408,12 +579,16 @@ config_atu: else type = PCIE_ATU_TYPE_IO; - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar, - size); + if (epf_bar->num_submap) + ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar); + else + ret = dw_pcie_ep_ib_atu_bar(ep, func_no, type, + epf_bar->phys_addr, bar, size); + if (ret) return ret; - ep->epf_bar[bar] = epf_bar; + ep_func->epf_bar[bar] = epf_bar; return 0; } @@ -601,6 +776,16 @@ static void dw_pcie_ep_stop(struct pci_epc *epc) struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + /* + * Tear down the dedicated outbound window used for MSI + * generation. This avoids leaking an iATU window across + * endpoint stop/start cycles. + */ + if (ep->msi_iatu_mapped) { + dw_pcie_ep_unmap_addr(epc, 0, 0, ep->msi_mem_phys); + ep->msi_iatu_mapped = false; + } + dw_pcie_stop_link(pci); } @@ -702,14 +887,37 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no, msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower; msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset); - ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr, - map_size); - if (ret) - return ret; - writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset); + /* + * Program the outbound iATU once and keep it enabled. + * + * The spec warns that updating iATU registers while there are + * operations in flight on the AXI bridge interface is not + * supported, so we avoid reprogramming the region on every MSI, + * specifically unmapping immediately after writel(). + */ + if (!ep->msi_iatu_mapped) { + ret = dw_pcie_ep_map_addr(epc, func_no, 0, + ep->msi_mem_phys, msg_addr, + map_size); + if (ret) + return ret; + + ep->msi_iatu_mapped = true; + ep->msi_msg_addr = msg_addr; + ep->msi_map_size = map_size; + } else if (WARN_ON_ONCE(ep->msi_msg_addr != msg_addr || + ep->msi_map_size != map_size)) { + /* + * The host changed the MSI target address or the required + * mapping size changed. Reprogramming the iATU at runtime is + * unsafe on this controller, so bail out instead of trying to + * update the existing region. + */ + return -EINVAL; + } - dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys); + writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset); return 0; } @@ -775,7 +983,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset); tbl_offset &= PCI_MSIX_TABLE_OFFSET; - msix_tbl = ep->epf_bar[bir]->addr + tbl_offset; + msix_tbl = ep_func->epf_bar[bir]->addr + tbl_offset; msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr; msg_data = msix_tbl[(interrupt_num - 1)].msg_data; vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl; @@ -836,20 +1044,20 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep) } EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit); -static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +static void dw_pcie_ep_init_rebar_registers(struct dw_pcie_ep *ep, u8 func_no) { - struct dw_pcie_ep *ep = &pci->ep; - unsigned int offset; - unsigned int nbars; + struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no); + unsigned int offset, nbars; enum pci_barno bar; u32 reg, i, val; - offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR); + if (!ep_func) + return; - dw_pcie_dbi_ro_wr_en(pci); + offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR); if (offset) { - reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg); /* @@ -870,16 +1078,28 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) * the controller when RESBAR_CAP_REG is written, which * is why RESBAR_CAP_REG is written here. */ - val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL); + val = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL); bar = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, val); - if (ep->epf_bar[bar]) - pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val); + if (ep_func->epf_bar[bar]) + pci_epc_bar_size_to_rebar_cap(ep_func->epf_bar[bar]->size, &val); else val = BIT(4); - dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, val); + dw_pcie_ep_writel_dbi(ep, func_no, offset + PCI_REBAR_CAP, val); } } +} + +static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci) +{ + struct dw_pcie_ep *ep = &pci->ep; + u8 funcs = ep->epc->max_functions; + u8 func_no; + + dw_pcie_dbi_ro_wr_en(pci); + + for (func_no = 0; func_no < funcs; func_no++) + dw_pcie_ep_init_rebar_registers(ep, func_no); dw_pcie_setup(pci); dw_pcie_dbi_ro_wr_dis(pci); @@ -967,6 +1187,18 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep) if (ep->ops->init) ep->ops->init(ep); + /* + * PCIe r6.0, section 7.9.15 states that for endpoints that support + * PTM, this capability structure is required in exactly one + * function, which controls the PTM behavior of all PTM capable + * functions. This indicates the PTM capability structure + * represents controller-level registers rather than per-function + * registers. + * + * Therefore, PTM capability registers are configured using the + * standard DBI accessors, instead of func_no indexed per-function + * accessors. + */ ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM); /* @@ -1087,6 +1319,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep) struct device *dev = pci->dev; INIT_LIST_HEAD(&ep->func_list); + ep->msi_iatu_mapped = false; + ep->msi_msg_addr = 0; + ep->msi_map_size = 0; epc = devm_pci_epc_create(dev, &epc_ops); if (IS_ERR(epc)) { diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index f116591975ff..6ae6189e9b8a 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -244,7 +244,7 @@ void dw_pcie_msi_init(struct dw_pcie_rp *pp) u64 msi_target = (u64)pp->msi_data; u32 ctrl, num_ctrls; - if (!pci_msi_enabled() || !pp->has_msi_ctrl) + if (!pci_msi_enabled() || !pp->use_imsi_rx) return; num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; @@ -356,10 +356,20 @@ int dw_pcie_msi_host_init(struct dw_pcie_rp *pp) * order not to miss MSI TLPs from those devices the MSI target * address has to be within the lowest 4GB. * - * Note until there is a better alternative found the reservation is - * done by allocating from the artificially limited DMA-coherent - * memory. + * Per DWC databook r6.21a, section 3.10.2.3, the incoming MWr TLP + * targeting the MSI_CTRL_ADDR is terminated by the iMSI-RX and never + * appears on the AXI bus. So MSI_CTRL_ADDR address doesn't need to be + * mapped and can be any memory that doesn't get allocated for the BAR + * memory. Since most of the platforms provide 32-bit address for + * 'config' region, try cfg0_base as the first option for the MSI target + * address if it's a 32-bit address. Otherwise, try 32-bit and 64-bit + * coherent memory allocation one by one. */ + if (!(pp->cfg0_base & GENMASK_ULL(63, 32))) { + pp->msi_data = pp->cfg0_base; + return 0; + } + ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); if (!ret) msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data, @@ -582,15 +592,15 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) } if (pci_msi_enabled()) { - pp->has_msi_ctrl = !(pp->ops->msi_init || + pp->use_imsi_rx = !(pp->ops->msi_init || of_property_present(np, "msi-parent") || of_property_present(np, "msi-map")); /* - * For the has_msi_ctrl case the default assignment is handled + * For the use_imsi_rx case the default assignment is handled * in the dw_pcie_msi_host_init(). */ - if (!pp->has_msi_ctrl && !pp->num_vectors) { + if (!pp->use_imsi_rx && !pp->num_vectors) { pp->num_vectors = MSI_DEF_NUM_VECTORS; } else if (pp->num_vectors > MAX_MSI_IRQS) { dev_err(dev, "Invalid number of vectors\n"); @@ -602,7 +612,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) ret = pp->ops->msi_init(pp); if (ret < 0) goto err_deinit_host; - } else if (pp->has_msi_ctrl) { + } else if (pp->use_imsi_rx) { ret = dw_pcie_msi_host_init(pp); if (ret < 0) goto err_deinit_host; @@ -620,14 +630,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_free_msi; - if (pp->ecam_enabled) { - ret = dw_pcie_config_ecam_iatu(pp); - if (ret) { - dev_err(dev, "Failed to configure iATU in ECAM mode\n"); - goto err_free_msi; - } - } - /* * Allocate the resource for MSG TLP before programming the iATU * outbound window in dw_pcie_setup_rc(). Since the allocation depends @@ -655,13 +657,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) } /* - * Note: Skip the link up delay only when a Link Up IRQ is present. - * If there is no Link Up IRQ, we should not bypass the delay - * because that would require users to manually rescan for devices. + * Only fail on timeout error. Other errors indicate the device may + * become available later, so continue without failing. */ - if (!pp->use_linkup_irq) - /* Ignore errors, the link may come up later */ - dw_pcie_wait_for_link(pci); + ret = dw_pcie_wait_for_link(pci); + if (ret == -ETIMEDOUT) + goto err_stop_link; ret = pci_host_probe(bridge); if (ret) @@ -681,7 +682,7 @@ err_remove_edma: dw_pcie_edma_remove(pci); err_free_msi: - if (pp->has_msi_ctrl) + if (pp->use_imsi_rx) dw_pcie_free_msi(pp); err_deinit_host: @@ -709,7 +710,7 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) dw_pcie_edma_remove(pci); - if (pp->has_msi_ctrl) + if (pp->use_imsi_rx) dw_pcie_free_msi(pp); if (pp->ops->deinit) @@ -846,19 +847,10 @@ static void __iomem *dw_pcie_ecam_conf_map_bus(struct pci_bus *bus, unsigned int return pci->dbi_base + where; } -static int dw_pcie_op_assert_perst(struct pci_bus *bus, bool assert) -{ - struct dw_pcie_rp *pp = bus->sysdata; - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - return dw_pcie_assert_perst(pci, assert); -} - static struct pci_ops dw_pcie_ops = { .map_bus = dw_pcie_own_conf_map_bus, .read = pci_generic_config_read, .write = pci_generic_config_write, - .assert_perst = dw_pcie_op_assert_perst, }; static struct pci_ops dw_pcie_ecam_ops = { @@ -872,9 +864,10 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dw_pcie_ob_atu_cfg atu = { 0 }; struct resource_entry *entry; + int ob_iatu_index; + int ib_iatu_index; int i, ret; - /* Note the very first outbound ATU is used for CFG IOs */ if (!pci->num_ob_windows) { dev_err(pci->dev, "No outbound iATU found\n"); return -EINVAL; @@ -890,37 +883,74 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) for (i = 0; i < pci->num_ib_windows; i++) dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i); - i = 0; + /* + * NOTE: For outbound address translation, outbound iATU at index 0 is + * reserved for CFG IOs (dw_pcie_other_conf_map_bus()), thus start at + * index 1. + * + * If using ECAM, outbound iATU at index 0 and index 1 is reserved for + * CFG IOs. + */ + if (pp->ecam_enabled) { + ob_iatu_index = 2; + ret = dw_pcie_config_ecam_iatu(pp); + if (ret) { + dev_err(pci->dev, "Failed to configure iATU in ECAM mode\n"); + return ret; + } + } else { + ob_iatu_index = 1; + } + resource_list_for_each_entry(entry, &pp->bridge->windows) { + resource_size_t res_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; - if (pci->num_ob_windows <= ++i) - break; - - atu.index = i; atu.type = PCIE_ATU_TYPE_MEM; atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; /* Adjust iATU size if MSG TLP region was allocated before */ if (pp->msg_res && pp->msg_res->parent == entry->res) - atu.size = resource_size(entry->res) - + res_size = resource_size(entry->res) - resource_size(pp->msg_res); else - atu.size = resource_size(entry->res); + res_size = resource_size(entry->res); + + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (ob_iatu_index >= pci->num_ob_windows) { + dev_err(pci->dev, "Cannot add outbound window for region: %pr\n", + entry->res); + return -ENOMEM; + } - ret = dw_pcie_prog_outbound_atu(pci, &atu); - if (ret) { - dev_err(pci->dev, "Failed to set MEM range %pr\n", - entry->res); - return ret; + atu.index = ob_iatu_index; + atu.size = MIN(pci->region_limit + 1, res_size); + + ret = dw_pcie_prog_outbound_atu(pci, &atu); + if (ret) { + dev_err(pci->dev, "Failed to set MEM range %pr\n", + entry->res); + return ret; + } + + ob_iatu_index++; + atu.parent_bus_addr += atu.size; + atu.pci_addr += atu.size; + res_size -= atu.size; } } if (pp->io_size) { - if (pci->num_ob_windows > ++i) { - atu.index = i; + if (ob_iatu_index < pci->num_ob_windows) { + atu.index = ob_iatu_index; atu.type = PCIE_ATU_TYPE_IO; atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset; atu.pci_addr = pp->io_bus_addr; @@ -932,40 +962,71 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) entry->res); return ret; } + ob_iatu_index++; } else { + /* + * If there are not enough outbound windows to give I/O + * space its own iATU, the outbound iATU at index 0 will + * be shared between I/O space and CFG IOs, by + * temporarily reconfiguring the iATU to CFG space, in + * order to do a CFG IO, and then immediately restoring + * it to I/O space. This is only implemented when using + * dw_pcie_other_conf_map_bus(), which is not the case + * when using ECAM. + */ + if (pp->ecam_enabled) { + dev_err(pci->dev, "Cannot add outbound window for I/O\n"); + return -ENOMEM; + } pp->cfg0_io_shared = true; } } - if (pci->num_ob_windows <= i) - dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n", - pci->num_ob_windows); - - pp->msg_atu_index = i; + if (pp->use_atu_msg) { + if (ob_iatu_index >= pci->num_ob_windows) { + dev_err(pci->dev, "Cannot add outbound window for MSG TLP\n"); + return -ENOMEM; + } + pp->msg_atu_index = ob_iatu_index++; + } - i = 0; + ib_iatu_index = 0; resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { + resource_size_t res_start, res_size, window_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; - if (pci->num_ib_windows <= i) - break; + res_size = resource_size(entry->res); + res_start = entry->res->start; + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (ib_iatu_index >= pci->num_ib_windows) { + dev_err(pci->dev, "Cannot add inbound window for region: %pr\n", + entry->res); + return -ENOMEM; + } - ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, - entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); - if (ret) { - dev_err(pci->dev, "Failed to set DMA range %pr\n", - entry->res); - return ret; + 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, + res_start - entry->offset, window_size); + if (ret) { + dev_err(pci->dev, "Failed to set DMA range %pr\n", + entry->res); + return ret; + } + + ib_iatu_index++; + res_start += window_size; + res_size -= window_size; } } - if (pci->num_ib_windows <= i) - dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n", - pci->num_ib_windows); - return 0; } @@ -1087,7 +1148,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) * the platform uses its own address translation component rather than * ATU, so we should not program the ATU here. */ - if (pp->bridge->child_ops == &dw_child_pcie_ops) { + if (pp->bridge->child_ops == &dw_child_pcie_ops || pp->ecam_enabled) { ret = dw_pcie_iatu_setup(pp); if (ret) return ret; @@ -1104,6 +1165,17 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) dw_pcie_dbi_ro_wr_dis(pci); + /* + * The iMSI-RX module does not support receiving MSI or MSI-X generated + * by the Root Port. If iMSI-RX is used as the MSI controller, remove + * the MSI and MSI-X capabilities of the Root Port to allow the drivers + * to fall back to INTx instead. + */ + if (pp->use_imsi_rx) { + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI); + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX); + } + return 0; } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); @@ -1147,8 +1219,11 @@ 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); + int ret = 0; u32 val; - int ret; + + if (!dw_pcie_link_up(pci)) + goto stop_link; /* * If L1SS is supported, then do not put the link into L2 as some @@ -1165,6 +1240,16 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) return ret; } + /* + * Some SoCs do not support reading the LTSSM register after + * PME_Turn_Off broadcast. For those SoCs, skip waiting for L2/L3 Ready + * state and wait 10ms as recommended in PCIe spec r6.0, sec 5.3.3.2.1. + */ + if (pci->pp.skip_l23_ready) { + mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000); + goto stop_link; + } + ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE || val <= DW_PCIE_LTSSM_DETECT_WAIT, @@ -1183,6 +1268,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) */ udelay(1); +stop_link: dw_pcie_stop_link(pci); if (pci->pp.ops->deinit) pci->pp.ops->deinit(&pci->pp); @@ -1220,6 +1306,9 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) if (ret) return ret; + if (pci->pp.ops->post_init) + pci->pp.ops->post_init(&pci->pp); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 12f41886c65d..8530746ec5cb 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -61,6 +61,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features dw_plat_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, }; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 75fc8b767fcc..5741c09dde7f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -226,16 +226,70 @@ void dw_pcie_version_detect(struct dw_pcie *pci) u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap) { return PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, - pci); + NULL, pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_capability); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) { - return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, pci); + return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, NULL, pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); +void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap) +{ + u8 cap_pos, pre_pos, next_pos; + u16 reg; + + cap_pos = PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, + &pre_pos, pci); + if (!cap_pos) + return; + + reg = dw_pcie_readw_dbi(pci, cap_pos); + next_pos = (reg & 0xff00) >> 8; + + dw_pcie_dbi_ro_wr_en(pci); + if (pre_pos == PCI_CAPABILITY_LIST) + dw_pcie_writeb_dbi(pci, PCI_CAPABILITY_LIST, next_pos); + else + dw_pcie_writeb_dbi(pci, pre_pos + 1, next_pos); + dw_pcie_dbi_ro_wr_dis(pci); +} +EXPORT_SYMBOL_GPL(dw_pcie_remove_capability); + +void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap) +{ + int cap_pos, next_pos, pre_pos; + u32 pre_header, header; + + cap_pos = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, &pre_pos, pci); + if (!cap_pos) + return; + + header = dw_pcie_readl_dbi(pci, cap_pos); + + /* + * If the first cap at offset PCI_CFG_SPACE_SIZE is removed, + * only set its capid to zero as it cannot be skipped. + */ + if (cap_pos == PCI_CFG_SPACE_SIZE) { + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, cap_pos, header & 0xffff0000); + dw_pcie_dbi_ro_wr_dis(pci); + return; + } + + pre_header = dw_pcie_readl_dbi(pci, pre_pos); + next_pos = PCI_EXT_CAP_NEXT(header); + + dw_pcie_dbi_ro_wr_en(pci); + dw_pcie_writel_dbi(pci, pre_pos, + (pre_header & 0xfffff) | (next_pos << 20)); + dw_pcie_dbi_ro_wr_dis(pci); +} +EXPORT_SYMBOL_GPL(dw_pcie_remove_ext_capability); + static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, u16 vsec_id) { @@ -246,7 +300,7 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, return 0; while ((vsec = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, vsec, - PCI_EXT_CAP_ID_VNDR, pci))) { + PCI_EXT_CAP_ID_VNDR, NULL, pci))) { header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER); if (PCI_VNDR_HEADER_ID(header) == vsec_id) return vsec; @@ -478,6 +532,9 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u32 retries, val; u64 limit_addr; + if (atu->index >= pci->num_ob_windows) + return -ENOSPC; + limit_addr = parent_bus_addr + atu->size - 1; if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) || @@ -551,6 +608,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type, u64 limit_addr = pci_addr + size - 1; u32 retries, val; + if (index >= pci->num_ib_windows) + return -ENOSPC; + if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) || !IS_ALIGNED(parent_bus_addr, pci->region_align) || !IS_ALIGNED(pci_addr, pci->region_align) || !size) { @@ -639,9 +699,69 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm) +{ + const char *str; + + switch (ltssm) { +#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_1); + DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_2); + default: + str = "DW_PCIE_LTSSM_UNKNOWN"; + break; + } + + return str + strlen("DW_PCIE_LTSSM_"); +} + +/** + * dw_pcie_wait_for_link - Wait for the PCIe link to be up + * @pci: DWC instance + * + * Returns: 0 if link is up, -ENODEV if device is not found, -EIO if the device + * is found but not active and -ETIMEDOUT if the link fails to come up for other + * reasons. + */ int dw_pcie_wait_for_link(struct dw_pcie *pci) { - u32 offset, val; + u32 offset, val, ltssm; int retries; /* Check if the link is up or not */ @@ -653,7 +773,29 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) } if (retries >= PCIE_LINK_WAIT_MAX_RETRIES) { - dev_info(pci->dev, "Phy link never came up\n"); + /* + * If the link is in Detect.Quiet or Detect.Active state, it + * indicates that no device is detected. + */ + ltssm = dw_pcie_get_ltssm(pci); + if (ltssm == DW_PCIE_LTSSM_DETECT_QUIET || + ltssm == DW_PCIE_LTSSM_DETECT_ACT) { + dev_info(pci->dev, "Device not found\n"); + return -ENODEV; + + /* + * If the link is in POLL.{Active/Compliance} state, then the + * device is found to be connected to the bus, but it is not + * active i.e., the device firmware might not yet initialized. + */ + } else if (ltssm == DW_PCIE_LTSSM_POLL_ACTIVE || + ltssm == DW_PCIE_LTSSM_POLL_COMPLIANCE) { + dev_info(pci->dev, "Device found, but not active\n"); + return -EIO; + } + + dev_err(pci->dev, "Link failed to come up. LTSSM: %s\n", + dw_pcie_ltssm_status_string(ltssm)); return -ETIMEDOUT; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 403f6cfe8191..ae6389dd9caa 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -305,6 +305,10 @@ /* Default eDMA LLP memory size */ #define DMA_LLP_MEM_SIZE PAGE_SIZE +/* Common struct pci_epc_feature bits among DWC EP glue drivers */ +#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \ + .subrange_mapping = true + struct dw_pcie; struct dw_pcie_rp; struct dw_pcie_ep; @@ -388,6 +392,10 @@ enum dw_pcie_ltssm { DW_PCIE_LTSSM_RCVRY_EQ2 = 0x22, DW_PCIE_LTSSM_RCVRY_EQ3 = 0x23, + /* Vendor glue drivers provide pseudo L1 substates from get_ltssm() */ + DW_PCIE_LTSSM_L1_1 = 0x141, + DW_PCIE_LTSSM_L1_2 = 0x142, + DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF, }; @@ -412,7 +420,7 @@ struct dw_pcie_host_ops { }; struct dw_pcie_rp { - bool has_msi_ctrl:1; + bool use_imsi_rx:1; bool cfg0_io_shared:1; u64 cfg0_base; void __iomem *va_cfg0_base; @@ -434,11 +442,11 @@ struct dw_pcie_rp { bool use_atu_msg; int msg_atu_index; struct resource *msg_res; - bool use_linkup_irq; struct pci_eq_presets presets; struct pci_config_window *cfg; bool ecam_enabled; bool native_ecam; + bool skip_l23_ready; }; struct dw_pcie_ep_ops { @@ -463,6 +471,12 @@ struct dw_pcie_ep_func { u8 func_no; u8 msi_cap; /* MSI capability offset */ u8 msix_cap; /* MSI-X capability offset */ + u8 bar_to_atu[PCI_STD_NUM_BARS]; + struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; + + /* Only for Address Match Mode inbound iATU */ + u32 *ib_atu_indexes[PCI_STD_NUM_BARS]; + unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS]; }; struct dw_pcie_ep { @@ -472,13 +486,16 @@ struct dw_pcie_ep { phys_addr_t phys_base; size_t addr_size; size_t page_size; - u8 bar_to_atu[PCI_STD_NUM_BARS]; phys_addr_t *outbound_addr; unsigned long *ib_window_map; unsigned long *ob_window_map; void __iomem *msi_mem; phys_addr_t msi_mem_phys; - struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS]; + + /* MSI outbound iATU state */ + bool msi_iatu_mapped; + u64 msi_msg_addr; + size_t msi_map_size; }; struct dw_pcie_ops { @@ -493,7 +510,6 @@ struct dw_pcie_ops { enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie); int (*start_link)(struct dw_pcie *pcie); void (*stop_link)(struct dw_pcie *pcie); - int (*assert_perst)(struct dw_pcie *pcie, bool assert); }; struct debugfs_info { @@ -562,6 +578,8 @@ void dw_pcie_version_detect(struct dw_pcie *pci); u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap); +void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap); +void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap); u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci); u16 dw_pcie_find_ptm_capability(struct dw_pcie *pci); @@ -798,14 +816,6 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci) pci->ops->stop_link(pci); } -static inline int dw_pcie_assert_perst(struct dw_pcie *pci, bool assert) -{ - if (pci->ops && pci->ops->assert_perst) - return pci->ops->assert_perst(pci, assert); - - return 0; -} - static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) { u32 val; @@ -818,6 +828,8 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci) return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val); } +const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm); + #ifdef CONFIG_PCIE_DW_HOST int dw_pcie_suspend_noirq(struct dw_pcie *pci); int dw_pcie_resume_noirq(struct dw_pcie *pci); @@ -896,7 +908,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no, int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no, u16 interrupt_num); void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar); -int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap); struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no); #else @@ -954,12 +965,6 @@ static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) { } -static inline int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, - u8 prev_cap, u8 cap) -{ - return 0; -} - static inline struct dw_pcie_ep_func * dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no) { diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index f8605fe61a41..5b17da63151d 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -68,6 +68,11 @@ #define PCIE_CLKREQ_NOT_READY FIELD_PREP_WM16(BIT(0), 0) #define PCIE_CLKREQ_PULL_DOWN FIELD_PREP_WM16(GENMASK(13, 12), 1) +/* RASDES TBA information */ +#define PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN 0x154 +#define PCIE_CLIENT_CDM_RASDES_TBA_L1_1 BIT(4) +#define PCIE_CLIENT_CDM_RASDES_TBA_L1_2 BIT(5) + /* Hot Reset Control Register */ #define PCIE_CLIENT_HOT_RESET_CTRL 0x180 #define PCIE_LTSSM_APP_DLY2_EN BIT(1) @@ -80,6 +85,8 @@ #define PCIE_LINKUP_MASK GENMASK(17, 16) #define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0) +#define PCIE_TYPE0_HDR_DBI2_OFFSET 0x100000 + struct rockchip_pcie { struct dw_pcie pci; void __iomem *apb_base; @@ -181,11 +188,26 @@ static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip) return 0; } -static u32 rockchip_pcie_get_ltssm(struct rockchip_pcie *rockchip) +static u32 rockchip_pcie_get_ltssm_reg(struct rockchip_pcie *rockchip) { return rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS); } +static enum dw_pcie_ltssm rockchip_pcie_get_ltssm(struct dw_pcie *pci) +{ + struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); + u32 val = rockchip_pcie_readl_apb(rockchip, + PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN); + + if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_1) + return DW_PCIE_LTSSM_L1_1; + + if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_2) + return DW_PCIE_LTSSM_L1_2; + + return rockchip_pcie_get_ltssm_reg(rockchip) & PCIE_LTSSM_STATUS_MASK; +} + static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip) { rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM, @@ -201,7 +223,7 @@ static void rockchip_pcie_disable_ltssm(struct rockchip_pcie *rockchip) static bool rockchip_pcie_link_up(struct dw_pcie *pci) { struct rockchip_pcie *rockchip = to_rockchip_pcie(pci); - u32 val = rockchip_pcie_get_ltssm(rockchip); + u32 val = rockchip_pcie_get_ltssm_reg(rockchip); return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP; } @@ -292,6 +314,8 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) if (irq < 0) return irq; + pci->dbi_base2 = pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET; + ret = rockchip_pcie_init_irq_domain(rockchip); if (ret < 0) dev_err(dev, "failed to init irq domain\n"); @@ -302,6 +326,10 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp) rockchip_pcie_configure_l1ss(pci); rockchip_pcie_enable_l0s(pci); + /* Disable Root Ports BAR0 and BAR1 as they report bogus size */ + dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0); + dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0); + return 0; } @@ -327,9 +355,7 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep) if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep")) return; - if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI, - PCI_EXT_CAP_ID_ATS)) - dev_err(dev, "failed to hide ATS capability\n"); + dw_pcie_remove_ext_capability(pci, PCI_EXT_CAP_ID_ATS); } static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep) @@ -364,6 +390,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .msix_capable = true, @@ -384,6 +411,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { * BARs) would be overwritten, resulting in (all other BARs) no longer working. */ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .msix_capable = true, @@ -485,36 +513,9 @@ static const struct dw_pcie_ops dw_pcie_ops = { .link_up = rockchip_pcie_link_up, .start_link = rockchip_pcie_start_link, .stop_link = rockchip_pcie_stop_link, + .get_ltssm = rockchip_pcie_get_ltssm, }; -static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg) -{ - struct rockchip_pcie *rockchip = arg; - struct dw_pcie *pci = &rockchip->pci; - struct dw_pcie_rp *pp = &pci->pp; - struct device *dev = pci->dev; - u32 reg; - - reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC); - rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); - - dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg); - dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip)); - - if (reg & PCIE_RDLH_LINK_UP_CHGED) { - if (rockchip_pcie_link_up(pci)) { - msleep(PCIE_RESET_CONFIG_WAIT_MS); - dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); - /* Rescan the bus to enumerate endpoint devices */ - pci_lock_rescan_remove(); - pci_rescan_bus(pp->bridge->bus); - pci_unlock_rescan_remove(); - } - } - - return IRQ_HANDLED; -} - static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) { struct rockchip_pcie *rockchip = arg; @@ -526,7 +527,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC); dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg); - dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip)); + dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm_reg(rockchip)); if (reg & PCIE_LINK_REQ_RST_NOT_INT) { dev_dbg(dev, "hot reset or link-down reset\n"); @@ -547,29 +548,14 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg) return IRQ_HANDLED; } -static int rockchip_pcie_configure_rc(struct platform_device *pdev, - struct rockchip_pcie *rockchip) +static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip) { - struct device *dev = &pdev->dev; struct dw_pcie_rp *pp; - int irq, ret; u32 val; if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_HOST)) return -ENODEV; - irq = platform_get_irq_byname(pdev, "sys"); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(dev, irq, NULL, - rockchip_pcie_rc_sys_irq_thread, - IRQF_ONESHOT, "pcie-sys-rc", rockchip); - if (ret) { - dev_err(dev, "failed to request PCIe sys IRQ\n"); - return ret; - } - /* LTSSM enable control mode */ val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1); rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL); @@ -580,19 +566,8 @@ static int rockchip_pcie_configure_rc(struct platform_device *pdev, pp = &rockchip->pci.pp; pp->ops = &rockchip_pcie_host_ops; - pp->use_linkup_irq = true; - - ret = dw_pcie_host_init(pp); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - /* unmask DLL up/down indicator */ - val = FIELD_PREP_WM16(PCIE_RDLH_LINK_UP_CHGED, 0); - rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC); - - return ret; + return dw_pcie_host_init(pp); } static int rockchip_pcie_configure_ep(struct platform_device *pdev, @@ -711,7 +686,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev) switch (data->mode) { case DW_PCIE_RC_TYPE: - ret = rockchip_pcie_configure_rc(pdev, rockchip); + ret = rockchip_pcie_configure_rc(rockchip); if (ret) goto deinit_clk; break; diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 60e74ac782af..2666a9c3d67e 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -309,6 +309,7 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features keembay_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .only_64bit = true, }, diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c index 47745749f75c..b3ec38099fa3 100644 --- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c +++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c @@ -282,12 +282,12 @@ static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp) ret = s32g_pcie_parse_port(s32g_pp, of_port); if (ret) - goto err_port; + break; } -err_port: - list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) - list_del(&port->list); + if (ret) + list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) + list_del(&port->list); return ret; } diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index f1bc0ac81a92..18460f01b2c6 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -168,11 +168,13 @@ enum qcom_pcie_ep_link_status { * @hdma_support: HDMA support on this SoC * @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache snooping * @disable_mhi_ram_parity_check: Disable MHI RAM data parity error check + * @firmware_managed: Set if the controller is firmware managed */ struct qcom_pcie_ep_cfg { bool hdma_support; bool override_no_snoop; bool disable_mhi_ram_parity_check; + bool firmware_managed; }; /** @@ -377,6 +379,14 @@ err_disable_clk: static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep) { + struct device *dev = pcie_ep->pci.dev; + + pm_runtime_put(dev); + + /* Skip resource disablement if controller is firmware-managed */ + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + return; + icc_set_bw(pcie_ep->icc_mem, 0, 0); phy_power_off(pcie_ep->phy); phy_exit(pcie_ep->phy); @@ -390,12 +400,24 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) u32 val, offset; int ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Failed to enable device: %d\n", ret); + return ret; + } + + /* Skip resource enablement if controller is firmware-managed */ + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + goto skip_resources_enable; + ret = qcom_pcie_enable_resources(pcie_ep); if (ret) { dev_err(dev, "Failed to enable resources: %d\n", ret); + pm_runtime_put(dev); return ret; } +skip_resources_enable: /* Perform cleanup that requires refclk */ pci_epc_deinit_notify(pci->ep.epc); dw_pcie_ep_cleanup(&pci->ep); @@ -630,6 +652,17 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev, return ret; } + pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN); + if (IS_ERR(pcie_ep->reset)) + return PTR_ERR(pcie_ep->reset); + + pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(pcie_ep->wake)) + return PTR_ERR(pcie_ep->wake); + + if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed) + return 0; + pcie_ep->num_clks = devm_clk_bulk_get_all(dev, &pcie_ep->clks); if (pcie_ep->num_clks < 0) { dev_err(dev, "Failed to get clocks\n"); @@ -640,14 +673,6 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev, if (IS_ERR(pcie_ep->core_reset)) return PTR_ERR(pcie_ep->core_reset); - pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN); - if (IS_ERR(pcie_ep->reset)) - return PTR_ERR(pcie_ep->reset); - - pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW); - if (IS_ERR(pcie_ep->wake)) - return PTR_ERR(pcie_ep->wake); - pcie_ep->phy = devm_phy_optional_get(dev, "pciephy"); if (IS_ERR(pcie_ep->phy)) ret = PTR_ERR(pcie_ep->phy); @@ -820,6 +845,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) } static const struct pci_epc_features qcom_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .align = SZ_4K, @@ -874,6 +900,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie_ep); + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + ret = qcom_pcie_ep_get_resources(pdev, pcie_ep); if (ret) return ret; @@ -894,6 +926,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) goto err_disable_irqs; } + ret = pm_runtime_put_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to suspend device: %d\n", ret); + goto err_disable_irqs; + } + pcie_ep->debugfs = debugfs_create_dir(name, NULL); qcom_pcie_ep_init_debugfs(pcie_ep); @@ -930,7 +968,15 @@ static const struct qcom_pcie_ep_cfg cfg_1_34_0 = { .disable_mhi_ram_parity_check = true, }; +static const struct qcom_pcie_ep_cfg cfg_1_34_0_fw_managed = { + .hdma_support = true, + .override_no_snoop = true, + .disable_mhi_ram_parity_check = true, + .firmware_managed = true, +}; + static const struct of_device_id qcom_pcie_ep_match[] = { + { .compatible = "qcom,sa8255p-pcie-ep", .data = &cfg_1_34_0_fw_managed}, { .compatible = "qcom,sa8775p-pcie-ep", .data = &cfg_1_34_0}, { .compatible = "qcom,sdx55-pcie-ep", }, { .compatible = "qcom,sm8450-pcie-ep", }, diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 5a318487b2b3..67a16af69ddc 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -24,6 +24,7 @@ #include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pci-ecam.h> +#include <linux/pci-pwrctrl.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> @@ -55,9 +56,6 @@ #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 #define PARF_Q2A_FLUSH 0x1ac #define PARF_LTSSM 0x1b0 -#define PARF_INT_ALL_STATUS 0x224 -#define PARF_INT_ALL_CLEAR 0x228 -#define PARF_INT_ALL_MASK 0x22c #define PARF_SID_OFFSET 0x234 #define PARF_BDF_TRANSLATE_CFG 0x24c #define PARF_DBI_BASE_ADDR_V2 0x350 @@ -134,10 +132,6 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) -/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */ -#define PARF_INT_ALL_LINK_UP BIT(13) -#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) - /* PARF_NO_SNOOP_OVERRIDE register fields */ #define WR_NO_SNOOP_OVERRIDE_EN BIT(1) #define RD_NO_SNOOP_OVERRIDE_EN BIT(3) @@ -267,10 +261,15 @@ struct qcom_pcie_cfg { bool no_l0s; }; +struct qcom_pcie_perst { + struct list_head list; + struct gpio_desc *desc; +}; + struct qcom_pcie_port { struct list_head list; - struct gpio_desc *reset; struct phy *phy; + struct list_head perst; }; struct qcom_pcie { @@ -289,27 +288,30 @@ struct qcom_pcie { #define to_qcom_pcie(x) dev_get_drvdata((x)->dev) -static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert) +static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert) { + struct qcom_pcie_perst *perst; struct qcom_pcie_port *port; int val = assert ? 1 : 0; - list_for_each_entry(port, &pcie->ports, list) - gpiod_set_value_cansleep(port->reset, val); + list_for_each_entry(port, &pcie->ports, list) { + list_for_each_entry(perst, &port->perst, list) + gpiod_set_value_cansleep(perst->desc, val); + } usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } -static void qcom_ep_reset_assert(struct qcom_pcie *pcie) +static void qcom_pcie_perst_assert(struct qcom_pcie *pcie) { - qcom_perst_assert(pcie, true); + __qcom_pcie_perst_assert(pcie, true); } -static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) +static void qcom_pcie_perst_deassert(struct qcom_pcie *pcie) { - /* Ensure that PERST has been asserted for at least 100 ms */ + /* Ensure that PERST# has been asserted for at least 100 ms */ msleep(PCIE_T_PVPERL_MS); - qcom_perst_assert(pcie, false); + __qcom_pcie_perst_assert(pcie, false); } static int qcom_pcie_start_link(struct dw_pcie *pci) @@ -641,18 +643,6 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_assert_perst(struct dw_pcie *pci, bool assert) -{ - struct qcom_pcie *pcie = to_qcom_pcie(pci); - - if (assert) - qcom_ep_reset_assert(pcie); - else - qcom_ep_reset_deassert(pcie); - - return 0; -} - static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie) { u32 val; @@ -1299,7 +1289,7 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) struct qcom_pcie *pcie = to_qcom_pcie(pci); int ret; - qcom_ep_reset_assert(pcie); + qcom_pcie_perst_assert(pcie); ret = pcie->cfg->ops->init(pcie); if (ret) @@ -1309,15 +1299,25 @@ 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; + + 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); if (ret) - goto err_disable_phy; + goto err_pwrctrl_power_off; } qcom_pcie_clear_aspm_l0s(pcie->pci); + dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX); + dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC); - qcom_ep_reset_deassert(pcie); + qcom_pcie_perst_deassert(pcie); if (pcie->cfg->ops->config_sid) { ret = pcie->cfg->ops->config_sid(pcie); @@ -1328,7 +1328,12 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) return 0; err_assert_reset: - qcom_ep_reset_assert(pcie); + qcom_pcie_perst_assert(pcie); +err_pwrctrl_power_off: + pci_pwrctrl_power_off_devices(pci->dev); +err_pwrctrl_destroy: + if (ret != -EPROBE_DEFER) + pci_pwrctrl_destroy_devices(pci->dev); err_disable_phy: qcom_pcie_phy_power_off(pcie); err_deinit: @@ -1342,7 +1347,13 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); - qcom_ep_reset_assert(pcie); + 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); qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -1496,7 +1507,6 @@ 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, - .assert_perst = qcom_pcie_assert_perst, }; static int qcom_pcie_icc_init(struct qcom_pcie *pcie) @@ -1635,37 +1645,11 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie) qcom_pcie_link_transition_count); } -static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data) -{ - struct qcom_pcie *pcie = data; - struct dw_pcie_rp *pp = &pcie->pci->pp; - struct device *dev = pcie->pci->dev; - u32 status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS); - - writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR); - - if (FIELD_GET(PARF_INT_ALL_LINK_UP, status)) { - msleep(PCIE_RESET_CONFIG_WAIT_MS); - dev_dbg(dev, "Received Link up event. Starting enumeration!\n"); - /* Rescan the bus to enumerate endpoint devices */ - pci_lock_rescan_remove(); - pci_rescan_bus(pp->bridge->bus); - pci_unlock_rescan_remove(); - - qcom_pcie_icc_opp_update(pcie); - } else { - dev_WARN_ONCE(dev, 1, "Received unknown event. INT_STATUS: 0x%08x\n", - status); - } - - return IRQ_HANDLED; -} - static void qcom_pci_free_msi(void *ptr) { struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr; - if (pp && pp->has_msi_ctrl) + if (pp && pp->use_imsi_rx) dw_pcie_free_msi(pp); } @@ -1689,7 +1673,7 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg) if (ret) return ret; - pp->has_msi_ctrl = true; + pp->use_imsi_rx = true; dw_pcie_msi_init(pp); return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp); @@ -1704,18 +1688,58 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = { } }; -static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node) +/* Parse PERST# from all nodes in depth first manner starting from @np */ +static int qcom_pcie_parse_perst(struct qcom_pcie *pcie, + struct qcom_pcie_port *port, + struct device_node *np) { struct device *dev = pcie->pci->dev; - struct qcom_pcie_port *port; + struct qcom_pcie_perst *perst; struct gpio_desc *reset; - struct phy *phy; int ret; - reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node), - "reset", GPIOD_OUT_HIGH, "PERST#"); - if (IS_ERR(reset)) + if (!of_find_property(np, "reset-gpios", NULL)) + goto parse_child_node; + + reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset", + GPIOD_OUT_HIGH, "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 = qcom_pcie_parse_perst(pcie, port, child); + if (ret) + return ret; + } + + return 0; +} + +static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node) +{ + struct device *dev = pcie->pci->dev; + struct qcom_pcie_port *port; + struct phy *phy; + int ret; phy = devm_of_phy_get(dev, node, NULL); if (IS_ERR(phy)) @@ -1729,7 +1753,12 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node if (ret) return ret; - port->reset = reset; + INIT_LIST_HEAD(&port->perst); + + ret = qcom_pcie_parse_perst(pcie, port, node); + if (ret) + return ret; + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); @@ -1739,9 +1768,10 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) { + struct qcom_pcie_perst *perst, *tmp_perst; + struct qcom_pcie_port *port, *tmp_port; struct device *dev = pcie->pci->dev; - struct qcom_pcie_port *port, *tmp; - int ret = -ENOENT; + int ret = -ENODEV; for_each_available_child_of_node_scoped(dev->of_node, of_port) { if (!of_node_is_type(of_port, "pci")) @@ -1754,7 +1784,9 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) return ret; err_port_del: - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); phy_exit(port->phy); list_del(&port->list); } @@ -1765,6 +1797,7 @@ err_port_del: 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; @@ -1786,27 +1819,35 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) if (!port) return -ENOMEM; - port->reset = reset; + perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL); + if (!perst) + return -ENOMEM; + port->phy = phy; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); + perst->desc = reset; + INIT_LIST_HEAD(&port->perst); + INIT_LIST_HEAD(&perst->list); + list_add_tail(&perst->list, &port->perst); + return 0; } 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 qcom_pcie_port *port, *tmp; struct device *dev = &pdev->dev; struct dev_pm_opp *opp; struct qcom_pcie *pcie; struct dw_pcie_rp *pp; struct resource *res; struct dw_pcie *pci; - int ret, irq; - char *name; + int ret; pcie_cfg = of_device_get_match_data(dev); if (!pcie_cfg) { @@ -1939,7 +1980,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) ret = qcom_pcie_parse_ports(pcie); if (ret) { - if (ret != -ENOENT) { + if (ret != -ENODEV) { dev_err_probe(pci->dev, ret, "Failed to parse Root Port: %d\n", ret); goto err_pm_runtime_put; @@ -1957,37 +1998,12 @@ static int qcom_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); - irq = platform_get_irq_byname_optional(pdev, "global"); - if (irq > 0) - pp->use_linkup_irq = true; - ret = dw_pcie_host_init(pp); if (ret) { - dev_err(dev, "cannot initialize host\n"); + dev_err_probe(dev, ret, "cannot initialize host\n"); goto err_phy_exit; } - name = devm_kasprintf(dev, GFP_KERNEL, "qcom_pcie_global_irq%d", - pci_domain_nr(pp->bridge->bus)); - if (!name) { - ret = -ENOMEM; - goto err_host_deinit; - } - - if (irq > 0) { - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - qcom_pcie_global_irq_thread, - IRQF_ONESHOT, name, pcie); - if (ret) { - dev_err_probe(&pdev->dev, ret, - "Failed to request Global IRQ\n"); - goto err_host_deinit; - } - - writel_relaxed(PARF_INT_ALL_LINK_UP | PARF_INT_MSI_DEV_0_7, - pcie->parf + PARF_INT_ALL_MASK); - } - qcom_pcie_icc_opp_update(pcie); if (pcie->mhi) @@ -1995,10 +2011,10 @@ static int qcom_pcie_probe(struct platform_device *pdev) return 0; -err_host_deinit: - dw_pcie_host_deinit(pp); err_phy_exit: - list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) { + list_for_each_entry_safe(perst, tmp_perst, &port->perst, list) + list_del(&perst->list); phy_exit(port->phy); list_del(&port->list); } diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 80778917d2dd..a6912e85e4dd 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -420,6 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features rcar_gen4_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c index ad4baaa34ffa..044088898819 100644 --- a/drivers/pci/controller/dwc/pcie-sophgo.c +++ b/drivers/pci/controller/dwc/pcie-sophgo.c @@ -161,6 +161,22 @@ static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp) raw_spin_unlock_irqrestore(&pp->lock, flags); } +static void sophgo_pcie_disable_l0s_l1(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + u32 offset, val; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + + dw_pcie_dbi_ro_wr_en(pci); + + val = dw_pcie_readl_dbi(pci, PCI_EXP_LNKCAP + offset); + val &= ~(PCI_EXP_LNKCAP_ASPM_L0S | PCI_EXP_LNKCAP_ASPM_L1); + dw_pcie_writel_dbi(pci, PCI_EXP_LNKCAP + offset, val); + + dw_pcie_dbi_ro_wr_dis(pci); +} + static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) { int irq; @@ -171,6 +187,8 @@ static int sophgo_pcie_host_init(struct dw_pcie_rp *pp) irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp); + sophgo_pcie_disable_l0s_l1(pp); + sophgo_pcie_msi_enable(pp); return 0; diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c index 2cecf32d2b0f..c1944b40ce02 100644 --- a/drivers/pci/controller/dwc/pcie-stm32-ep.c +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c @@ -70,6 +70,7 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features stm32_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .msi_capable = true, .align = SZ_64K, }; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 0ddeef70726d..06571d806ab3 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1988,6 +1988,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features tegra_pcie_epc_features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = true, .msi_capable = true, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c index d6e73811216e..d52753060970 100644 --- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c +++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c @@ -420,6 +420,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = { .init = uniphier_pcie_pro5_init_ep, .wait = NULL, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false, @@ -438,6 +439,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = { .init = uniphier_pcie_nx1_init_ep, .wait = uniphier_pcie_nx1_wait_ep, .features = { + DWC_EPC_COMMON_FEATURES, .linkup_notifier = false, .msi_capable = true, .msix_capable = false, diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c index c473e7c03bac..d6258c1cffe5 100644 --- a/drivers/pci/controller/pci-host-common.c +++ b/drivers/pci/controller/pci-host-common.c @@ -32,7 +32,7 @@ struct pci_config_window *pci_host_common_ecam_create(struct device *dev, err = of_address_to_resource(dev->of_node, 0, &cfgres); if (err) { - dev_err(dev, "missing \"reg\" property\n"); + dev_err(dev, "missing or malformed \"reg\" property\n"); return ERR_PTR(err); } diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 942ddfca3bf6..512309763d1f 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2545,12 +2545,6 @@ static const struct seq_operations tegra_pcie_ports_sops = { DEFINE_SEQ_ATTRIBUTE(tegra_pcie_ports); -static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie) -{ - debugfs_remove_recursive(pcie->debugfs); - pcie->debugfs = NULL; -} - static void tegra_pcie_debugfs_init(struct tegra_pcie *pcie) { pcie->debugfs = debugfs_create_dir("pcie", NULL); @@ -2624,29 +2618,6 @@ put_resources: return err; } -static void tegra_pcie_remove(struct platform_device *pdev) -{ - struct tegra_pcie *pcie = platform_get_drvdata(pdev); - struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); - struct tegra_pcie_port *port, *tmp; - - if (IS_ENABLED(CONFIG_DEBUG_FS)) - tegra_pcie_debugfs_exit(pcie); - - pci_stop_root_bus(host->bus); - pci_remove_root_bus(host->bus); - pm_runtime_put_sync(pcie->dev); - pm_runtime_disable(pcie->dev); - - if (IS_ENABLED(CONFIG_PCI_MSI)) - tegra_pcie_msi_teardown(pcie); - - tegra_pcie_put_resources(pcie); - - list_for_each_entry_safe(port, tmp, &pcie->ports, list) - tegra_pcie_port_free(port); -} - static int tegra_pcie_pm_suspend(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); @@ -2750,6 +2721,8 @@ static struct platform_driver tegra_pcie_driver = { .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, - .remove = tegra_pcie_remove, }; -module_platform_driver(tegra_pcie_driver); +builtin_platform_driver(tegra_pcie_driver); +MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA PCI host controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c new file mode 100644 index 000000000000..3e1a39d1e648 --- /dev/null +++ b/drivers/pci/controller/pcie-aspeed.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 Aspeed Technology Inc. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqchip/irq-msi-lib.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/phy/pcie.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include "../pci.h" + +#define MAX_MSI_HOST_IRQS 64 +#define ASPEED_RESET_RC_WAIT_MS 10 + +/* AST2600 AHBC Registers */ +#define ASPEED_AHBC_KEY 0x00 +#define ASPEED_AHBC_UNLOCK_KEY 0xaeed1a03 +#define ASPEED_AHBC_UNLOCK 0x01 +#define ASPEED_AHBC_ADDR_MAPPING 0x8c +#define ASPEED_PCIE_RC_MEMORY_EN BIT(5) + +/* AST2600 H2X Controller Registers */ +#define ASPEED_H2X_INT_STS 0x08 +#define ASPEED_PCIE_TX_IDLE_CLEAR BIT(0) +#define ASPEED_PCIE_INTX_STS GENMASK(3, 0) +#define ASPEED_H2X_HOST_RX_DESC_DATA 0x0c +#define ASPEED_H2X_TX_DESC0 0x10 +#define ASPEED_H2X_TX_DESC1 0x14 +#define ASPEED_H2X_TX_DESC2 0x18 +#define ASPEED_H2X_TX_DESC3 0x1c +#define ASPEED_H2X_TX_DESC_DATA 0x20 +#define ASPEED_H2X_STS 0x24 +#define ASPEED_PCIE_TX_IDLE BIT(31) +#define ASPEED_PCIE_STATUS_OF_TX GENMASK(25, 24) +#define ASPEED_PCIE_RC_H_TX_COMPLETE BIT(25) +#define ASPEED_PCIE_TRIGGER_TX BIT(0) +#define ASPEED_H2X_AHB_ADDR_CONFIG0 0x60 +#define ASPEED_AHB_REMAP_LO_ADDR(x) (x & GENMASK(15, 4)) +#define ASPEED_AHB_MASK_LO_ADDR(x) FIELD_PREP(GENMASK(31, 20), x) +#define ASPEED_H2X_AHB_ADDR_CONFIG1 0x64 +#define ASPEED_AHB_REMAP_HI_ADDR(x) (x) +#define ASPEED_H2X_AHB_ADDR_CONFIG2 0x68 +#define ASPEED_AHB_MASK_HI_ADDR(x) (x) +#define ASPEED_H2X_DEV_CTRL 0xc0 +#define ASPEED_PCIE_RX_DMA_EN BIT(9) +#define ASPEED_PCIE_RX_LINEAR BIT(8) +#define ASPEED_PCIE_RX_MSI_SEL BIT(7) +#define ASPEED_PCIE_RX_MSI_EN BIT(6) +#define ASPEED_PCIE_UNLOCK_RX_BUFF BIT(4) +#define ASPEED_PCIE_WAIT_RX_TLP_CLR BIT(2) +#define ASPEED_PCIE_RC_RX_ENABLE BIT(1) +#define ASPEED_PCIE_RC_ENABLE BIT(0) +#define ASPEED_H2X_DEV_STS 0xc8 +#define ASPEED_PCIE_RC_RX_DONE_ISR BIT(4) +#define ASPEED_H2X_DEV_RX_DESC_DATA 0xcc +#define ASPEED_H2X_DEV_RX_DESC1 0xd4 +#define ASPEED_H2X_DEV_TX_TAG 0xfc +#define ASPEED_RC_TLP_TX_TAG_NUM 0x28 + +/* AST2700 H2X */ +#define ASPEED_H2X_CTRL 0x00 +#define ASPEED_H2X_BRIDGE_EN BIT(0) +#define ASPEED_H2X_BRIDGE_DIRECT_EN BIT(1) +#define ASPEED_H2X_CFGE_INT_STS 0x08 +#define ASPEED_CFGE_TX_IDLE BIT(0) +#define ASPEED_CFGE_RX_BUSY BIT(1) +#define ASPEED_H2X_CFGI_TLP 0x20 +#define ASPEED_CFGI_BYTE_EN_MASK GENMASK(19, 16) +#define ASPEED_CFGI_BYTE_EN(x) \ + FIELD_PREP(ASPEED_CFGI_BYTE_EN_MASK, (x)) +#define ASPEED_H2X_CFGI_WR_DATA 0x24 +#define ASPEED_CFGI_WRITE BIT(20) +#define ASPEED_H2X_CFGI_CTRL 0x28 +#define ASPEED_CFGI_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGI_RET_DATA 0x2c +#define ASPEED_H2X_CFGE_TLP_1ST 0x30 +#define ASPEED_H2X_CFGE_TLP_NEXT 0x34 +#define ASPEED_H2X_CFGE_CTRL 0x38 +#define ASPEED_CFGE_TLP_FIRE BIT(0) +#define ASPEED_H2X_CFGE_RET_DATA 0x3c +#define ASPEED_H2X_REMAP_PREF_ADDR 0x70 +#define ASPEED_REMAP_PREF_ADDR_63_32(x) (x) +#define ASPEED_H2X_REMAP_PCI_ADDR_HI 0x74 +#define ASPEED_REMAP_PCI_ADDR_63_32(x) (((x) >> 32) & GENMASK(31, 0)) +#define ASPEED_H2X_REMAP_PCI_ADDR_LO 0x78 +#define ASPEED_REMAP_PCI_ADDR_31_12(x) ((x) & GENMASK(31, 12)) + +/* AST2700 SCU */ +#define ASPEED_SCU_60 0x60 +#define ASPEED_RC_E2M_PATH_EN BIT(0) +#define ASPEED_RC_H2XS_PATH_EN BIT(16) +#define ASPEED_RC_H2XD_PATH_EN BIT(17) +#define ASPEED_RC_H2XX_PATH_EN BIT(18) +#define ASPEED_RC_UPSTREAM_MEM_EN BIT(19) +#define ASPEED_SCU_64 0x64 +#define ASPEED_RC0_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(7, 0), x) +#define ASPEED_RC0_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(15, 8), x) +#define ASPEED_RC1_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(23, 16), x) +#define ASPEED_RC1_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(31, 24), x) +#define ASPEED_SCU_70 0x70 +#define ASPEED_DISABLE_EP_FUNC 0 + +/* Macro to combine Fmt and Type into the 8-bit field */ +#define ASPEED_TLP_FMT_TYPE(fmt, type) ((((fmt) & 0x7) << 5) | ((type) & 0x1f)) +#define ASPEED_TLP_COMMON_FIELDS GENMASK(31, 24) + +/* Completion status */ +#define CPL_STS(x) FIELD_GET(GENMASK(15, 13), (x)) +/* TLP configuration type 0 and type 1 */ +#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)) +#define CFG0_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG0_WR)) +#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)) +#define CFG1_WRITE_FMTTYPE \ + FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \ + ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \ + PCIE_TLP_TYPE_CFG1_WR)) +#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) \ + (((x) >> ((((z) % 4)) * 8)) & GENMASK((8 * (y)) - 1, 0)) +#define TLP_SET_VALUE(x, y, z) \ + ((((x) & GENMASK((8 * (y)) - 1, 0)) << ((((z) % 4)) * 8))) +#define AST2600_TX_DESC1_VALUE 0x00002000 +#define AST2700_TX_DESC1_VALUE 0x00401000 + +/** + * struct aspeed_pcie_port - PCIe port information + * @list: port list + * @pcie: pointer to PCIe host info + * @clk: pointer to the port clock gate + * @phy: pointer to PCIe PHY + * @perst: pointer to port reset control + * @slot: port slot + */ +struct aspeed_pcie_port { + struct list_head list; + struct aspeed_pcie *pcie; + struct clk *clk; + struct phy *phy; + struct reset_control *perst; + u32 slot; +}; + +/** + * struct aspeed_pcie - PCIe RC information + * @host: pointer to PCIe host bridge + * @dev: pointer to device structure + * @reg: PCIe host register base address + * @ahbc: pointer to AHHC register map + * @cfg: pointer to Aspeed PCIe configuration register map + * @platform: platform specific information + * @ports: list of PCIe ports + * @tx_tag: current TX tag for the port + * @root_bus_nr: bus number of the host bridge + * @h2xrst: pointer to H2X reset control + * @intx_domain: IRQ domain for INTx interrupts + * @msi_domain: IRQ domain for MSI interrupts + * @lock: mutex to protect MSI bitmap variable + * @msi_irq_in_use: bitmap to track used MSI host IRQs + * @clear_msi_twice: AST2700 workaround to clear MSI status twice + */ +struct aspeed_pcie { + struct pci_host_bridge *host; + struct device *dev; + void __iomem *reg; + struct regmap *ahbc; + struct regmap *cfg; + const struct aspeed_pcie_rc_platform *platform; + struct list_head ports; + + u8 tx_tag; + u8 root_bus_nr; + + struct reset_control *h2xrst; + + struct irq_domain *intx_domain; + struct irq_domain *msi_domain; + struct mutex lock; + DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_HOST_IRQS); + + bool clear_msi_twice; /* AST2700 workaround */ +}; + +/** + * struct aspeed_pcie_rc_platform - Platform information + * @setup: initialization function + * @pcie_map_ranges: function to map PCIe address ranges + * @reg_intx_en: INTx enable register offset + * @reg_intx_sts: INTx status register offset + * @reg_msi_en: MSI enable register offset + * @reg_msi_sts: MSI enable register offset + * @msi_address: HW fixed MSI address + */ +struct aspeed_pcie_rc_platform { + int (*setup)(struct platform_device *pdev); + void (*pcie_map_ranges)(struct aspeed_pcie *pcie, u64 pci_addr); + int reg_intx_en; + int reg_intx_sts; + int reg_msi_en; + int reg_msi_sts; + u32 msi_address; +}; + +static void aspeed_pcie_intx_irq_ack(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en |= BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_mask(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en &= ~BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static void aspeed_pcie_intx_irq_unmask(struct irq_data *d) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d); + int intx_en = pcie->platform->reg_intx_en; + u32 en; + + en = readl(pcie->reg + intx_en); + en |= BIT(d->hwirq); + writel(en, pcie->reg + intx_en); +} + +static struct irq_chip aspeed_intx_irq_chip = { + .name = "INTx", + .irq_ack = aspeed_pcie_intx_irq_ack, + .irq_mask = aspeed_pcie_intx_irq_mask, + .irq_unmask = aspeed_pcie_intx_irq_unmask, +}; + +static int aspeed_pcie_intx_map(struct irq_domain *domain, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_and_handler(irq, &aspeed_intx_irq_chip, handle_level_irq); + irq_set_chip_data(irq, domain->host_data); + irq_set_status_flags(irq, IRQ_LEVEL); + + return 0; +} + +static const struct irq_domain_ops aspeed_intx_domain_ops = { + .map = aspeed_pcie_intx_map, +}; + +static irqreturn_t aspeed_pcie_intr_handler(int irq, void *dev_id) +{ + struct aspeed_pcie *pcie = dev_id; + const struct aspeed_pcie_rc_platform *platform = pcie->platform; + unsigned long status; + unsigned long intx; + u32 bit; + int i; + + intx = FIELD_GET(ASPEED_PCIE_INTX_STS, + readl(pcie->reg + platform->reg_intx_sts)); + for_each_set_bit(bit, &intx, PCI_NUM_INTX) + generic_handle_domain_irq(pcie->intx_domain, bit); + + for (i = 0; i < 2; i++) { + int msi_sts_reg = platform->reg_msi_sts + (i * 4); + + status = readl(pcie->reg + msi_sts_reg); + writel(status, pcie->reg + msi_sts_reg); + + /* + * AST2700 workaround: + * The MSI status needs to clear one more time. + */ + if (pcie->clear_msi_twice) + writel(status, pcie->reg + msi_sts_reg); + + for_each_set_bit(bit, &status, 32) { + bit += (i * 32); + generic_handle_domain_irq(pcie->msi_domain, bit); + } + } + + return IRQ_HANDLED; +} + +static u32 aspeed_pcie_get_bdf_offset(struct pci_bus *bus, unsigned int devfn, + int where) +{ + return ((bus->number) << 24) | (PCI_SLOT(devfn) << 19) | + (PCI_FUNC(devfn) << 16) | (where & ~3); +} + +static int aspeed_ast2600_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, u32 fmt_type, + bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 bdf_offset, cfg_val, isr; + int ret; + + bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where); + + /* Driver may set unlock RX buffer before triggering next TX config */ + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + writel(ASPEED_PCIE_UNLOCK_RX_BUFF | cfg_val, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + cfg_val = fmt_type | CFG_PAYLOAD_SIZE; + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC0); + + cfg_val = AST2600_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC1); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_TX_DESC2); + writel(0, pcie->reg + ASPEED_H2X_TX_DESC3); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_TX_DESC_DATA); + + cfg_val = readl(pcie->reg + ASPEED_H2X_STS); + cfg_val |= ASPEED_PCIE_TRIGGER_TX; + writel(cfg_val, pcie->reg + ASPEED_H2X_STS); + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_STS, cfg_val, + (cfg_val & ASPEED_PCIE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), cfg_val); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + cfg_val = readl(pcie->reg + ASPEED_H2X_INT_STS); + cfg_val |= ASPEED_PCIE_TX_IDLE_CLEAR; + writel(cfg_val, pcie->reg + ASPEED_H2X_INT_STS); + + cfg_val = readl(pcie->reg + ASPEED_H2X_STS); + switch (cfg_val & ASPEED_PCIE_STATUS_OF_TX) { + case ASPEED_PCIE_RC_H_TX_COMPLETE: + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_DEV_STS, isr, + (isr & ASPEED_PCIE_RC_RX_DONE_ISR), 0, + 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), isr); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + if (!write) { + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_RX_DESC1); + if (CPL_STS(cfg_val) != PCIE_CPL_STS_SUCCESS) { + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } else { + *val = readl(pcie->reg + + ASPEED_H2X_DEV_RX_DESC_DATA); + } + } + break; + case ASPEED_PCIE_STATUS_OF_TX: + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + default: + *val = readl(pcie->reg + ASPEED_H2X_HOST_RX_DESC_DATA); + break; + } + + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL); + cfg_val |= ASPEED_PCIE_UNLOCK_RX_BUFF; + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_CTRL); + + *val = TLP_GET_VALUE(*val, size, where); + + ret = PCIBIOS_SUCCESSFUL; +out: + cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_STS); + writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_STS); + pcie->tx_tag = (pcie->tx_tag + 1) % 0x8; + return ret; +} + +static int aspeed_ast2600_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) != 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG0_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_child_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, val, + CFG1_READ_FMTTYPE, false); +} + +static int aspeed_ast2600_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2600 has only one Root Port on the root bus. + */ + if (PCI_SLOT(devfn) != 8) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG0_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2600_child_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return aspeed_ast2600_conf(bus, devfn, where, size, &val, + CFG1_WRITE_FMTTYPE, true); +} + +static int aspeed_ast2700_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 cfg_val; + + cfg_val = ASPEED_CFGI_BYTE_EN(TLP_HEADER_BYTE_EN(size, where)) | + (where & ~3); + if (write) + cfg_val |= ASPEED_CFGI_WRITE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGI_TLP); + + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGI_WR_DATA); + writel(ASPEED_CFGI_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGI_CTRL); + *val = readl(pcie->reg + ASPEED_H2X_CFGI_RET_DATA); + *val = TLP_GET_VALUE(*val, size, where); + + return PCIBIOS_SUCCESSFUL; +} + +static int aspeed_ast2700_child_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val, + bool write) +{ + struct aspeed_pcie *pcie = bus->sysdata; + u32 bdf_offset, status, cfg_val; + int ret; + + bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where); + + cfg_val = CFG_PAYLOAD_SIZE; + if (write) + cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ? + CFG0_WRITE_FMTTYPE : + CFG1_WRITE_FMTTYPE; + else + cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ? + CFG0_READ_FMTTYPE : + CFG1_READ_FMTTYPE; + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_1ST); + + cfg_val = AST2700_TX_DESC1_VALUE | + FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) | + TLP_HEADER_BYTE_EN(size, where); + writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + + writel(bdf_offset, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + if (write) + writel(TLP_SET_VALUE(*val, size, where), + pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT); + writel(ASPEED_CFGE_TX_IDLE | ASPEED_CFGE_RX_BUSY, + pcie->reg + ASPEED_H2X_CFGE_INT_STS); + writel(ASPEED_CFGE_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGE_CTRL); + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_TX_IDLE), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR tx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + + ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status, + (status & ASPEED_CFGE_RX_BUSY), 0, 50); + if (ret) { + dev_err(pcie->dev, + "%02x:%02x.%d CR rx timeout sts: 0x%08x\n", + bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status); + ret = PCIBIOS_SET_FAILED; + PCI_SET_ERROR_RESPONSE(val); + goto out; + } + *val = readl(pcie->reg + ASPEED_H2X_CFGE_RET_DATA); + *val = TLP_GET_VALUE(*val, size, where); + + ret = PCIBIOS_SUCCESSFUL; +out: + writel(status, pcie->reg + ASPEED_H2X_CFGE_INT_STS); + pcie->tx_tag = (pcie->tx_tag + 1) % 0xf; + return ret; +} + +static int aspeed_ast2700_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_child_rd_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, val, false); +} + +static int aspeed_ast2700_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + /* + * AST2700 has only one Root Port on the root bus. + */ + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + return aspeed_ast2700_config(bus, devfn, where, size, &val, true); +} + +static int aspeed_ast2700_child_wr_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return aspeed_ast2700_child_config(bus, devfn, where, size, &val, true); +} + +static struct pci_ops aspeed_ast2600_pcie_ops = { + .read = aspeed_ast2600_rd_conf, + .write = aspeed_ast2600_wr_conf, +}; + +static struct pci_ops aspeed_ast2600_pcie_child_ops = { + .read = aspeed_ast2600_child_rd_conf, + .write = aspeed_ast2600_child_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_ops = { + .read = aspeed_ast2700_rd_conf, + .write = aspeed_ast2700_wr_conf, +}; + +static struct pci_ops aspeed_ast2700_pcie_child_ops = { + .read = aspeed_ast2700_child_rd_conf, + .write = aspeed_ast2700_child_wr_conf, +}; + +static void aspeed_irq_compose_msi_msg(struct irq_data *data, + struct msi_msg *msg) +{ + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data); + + msg->address_hi = 0; + msg->address_lo = pcie->platform->msi_address; + msg->data = data->hwirq; +} + +static struct irq_chip aspeed_msi_bottom_irq_chip = { + .name = "ASPEED MSI", + .irq_compose_msi_msg = aspeed_irq_compose_msi_msg, +}; + +static int aspeed_irq_msi_domain_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *args) +{ + struct aspeed_pcie *pcie = domain->host_data; + int bit; + int i; + + guard(mutex)(&pcie->lock); + + bit = bitmap_find_free_region(pcie->msi_irq_in_use, MAX_MSI_HOST_IRQS, + get_count_order(nr_irqs)); + + if (bit < 0) + return -ENOSPC; + + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, bit + i, + &aspeed_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, NULL, + NULL); + } + + return 0; +} + +static void aspeed_irq_msi_domain_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data); + + guard(mutex)(&pcie->lock); + + bitmap_release_region(pcie->msi_irq_in_use, data->hwirq, + get_count_order(nr_irqs)); +} + +static const struct irq_domain_ops aspeed_msi_domain_ops = { + .alloc = aspeed_irq_msi_domain_alloc, + .free = aspeed_irq_msi_domain_free, +}; + +#define ASPEED_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \ + MSI_FLAG_USE_DEF_CHIP_OPS | \ + MSI_FLAG_NO_AFFINITY) + +#define ASPEED_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \ + MSI_FLAG_MULTI_PCI_MSI | \ + MSI_FLAG_PCI_MSIX) + +static const struct msi_parent_ops aspeed_msi_parent_ops = { + .required_flags = ASPEED_MSI_FLAGS_REQUIRED, + .supported_flags = ASPEED_MSI_FLAGS_SUPPORTED, + .bus_select_token = DOMAIN_BUS_PCI_MSI, + .chip_flags = MSI_CHIP_FLAG_SET_ACK, + .prefix = "ASPEED-", + .init_dev_msi_info = msi_lib_init_dev_msi_info, +}; + +static int aspeed_pcie_msi_init(struct aspeed_pcie *pcie) +{ + writel(~0, pcie->reg + pcie->platform->reg_msi_en); + writel(~0, pcie->reg + pcie->platform->reg_msi_en + 0x04); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts); + writel(~0, pcie->reg + pcie->platform->reg_msi_sts + 0x04); + + struct irq_domain_info info = { + .fwnode = dev_fwnode(pcie->dev), + .ops = &aspeed_msi_domain_ops, + .host_data = pcie, + .size = MAX_MSI_HOST_IRQS, + }; + + pcie->msi_domain = msi_create_parent_irq_domain(&info, + &aspeed_msi_parent_ops); + if (!pcie->msi_domain) + return dev_err_probe(pcie->dev, -ENOMEM, + "failed to create MSI domain\n"); + + return 0; +} + +static void aspeed_pcie_msi_free(struct aspeed_pcie *pcie) +{ + if (pcie->msi_domain) { + irq_domain_remove(pcie->msi_domain); + pcie->msi_domain = NULL; + } +} + +static void aspeed_pcie_irq_domain_free(void *d) +{ + struct aspeed_pcie *pcie = d; + + if (pcie->intx_domain) { + irq_domain_remove(pcie->intx_domain); + pcie->intx_domain = NULL; + } + aspeed_pcie_msi_free(pcie); +} + +static int aspeed_pcie_init_irq_domain(struct aspeed_pcie *pcie) +{ + int ret; + + pcie->intx_domain = irq_domain_add_linear(pcie->dev->of_node, + PCI_NUM_INTX, + &aspeed_intx_domain_ops, + pcie); + if (!pcie->intx_domain) { + ret = dev_err_probe(pcie->dev, -ENOMEM, + "failed to get INTx IRQ domain\n"); + goto err; + } + + writel(0, pcie->reg + pcie->platform->reg_intx_en); + writel(~0, pcie->reg + pcie->platform->reg_intx_sts); + + ret = aspeed_pcie_msi_init(pcie); + if (ret) + goto err; + + return 0; +err: + aspeed_pcie_irq_domain_free(pcie); + return ret; +} + +static int aspeed_pcie_port_init(struct aspeed_pcie_port *port) +{ + struct aspeed_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; + int ret; + + ret = clk_prepare_enable(port->clk); + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock for slot (%d)\n", + port->slot); + + ret = phy_init(port->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to init phy pcie for slot (%d)\n", + port->slot); + + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); + if (ret) + return dev_err_probe(dev, ret, + "failed to set phy mode for slot (%d)\n", + port->slot); + + reset_control_deassert(port->perst); + msleep(PCIE_RESET_CONFIG_WAIT_MS); + + return 0; +} + +static void aspeed_host_reset(struct aspeed_pcie *pcie) +{ + reset_control_assert(pcie->h2xrst); + mdelay(ASPEED_RESET_RC_WAIT_MS); + reset_control_deassert(pcie->h2xrst); +} + +static void aspeed_pcie_map_ranges(struct aspeed_pcie *pcie) +{ + struct pci_host_bridge *bridge = pcie->host; + struct resource_entry *window; + + resource_list_for_each_entry(window, &bridge->windows) { + u64 pci_addr; + + if (resource_type(window->res) != IORESOURCE_MEM) + continue; + + pci_addr = window->res->start - window->offset; + pcie->platform->pcie_map_ranges(pcie, pci_addr); + break; + } +} + +static void aspeed_ast2600_pcie_map_ranges(struct aspeed_pcie *pcie, + u64 pci_addr) +{ + u32 pci_addr_lo = pci_addr & GENMASK(31, 0); + u32 pci_addr_hi = (pci_addr >> 32) & GENMASK(31, 0); + + pci_addr_lo >>= 16; + writel(ASPEED_AHB_REMAP_LO_ADDR(pci_addr_lo) | + ASPEED_AHB_MASK_LO_ADDR(0xe00), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG0); + writel(ASPEED_AHB_REMAP_HI_ADDR(pci_addr_hi), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG1); + writel(ASPEED_AHB_MASK_HI_ADDR(~0), + pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG2); +} + +static int aspeed_ast2600_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie = platform_get_drvdata(pdev); + struct device *dev = pcie->dev; + + pcie->ahbc = syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,ahbc"); + if (IS_ERR(pcie->ahbc)) + return dev_err_probe(dev, PTR_ERR(pcie->ahbc), + "failed to map ahbc base\n"); + + aspeed_host_reset(pcie); + + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK_KEY); + regmap_update_bits(pcie->ahbc, ASPEED_AHBC_ADDR_MAPPING, + ASPEED_PCIE_RC_MEMORY_EN, ASPEED_PCIE_RC_MEMORY_EN); + regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK); + + writel(ASPEED_H2X_BRIDGE_EN, pcie->reg + ASPEED_H2X_CTRL); + + writel(ASPEED_PCIE_RX_DMA_EN | ASPEED_PCIE_RX_LINEAR | + ASPEED_PCIE_RX_MSI_SEL | ASPEED_PCIE_RX_MSI_EN | + ASPEED_PCIE_WAIT_RX_TLP_CLR | ASPEED_PCIE_RC_RX_ENABLE | + ASPEED_PCIE_RC_ENABLE, + pcie->reg + ASPEED_H2X_DEV_CTRL); + + writel(ASPEED_RC_TLP_TX_TAG_NUM, pcie->reg + ASPEED_H2X_DEV_TX_TAG); + + pcie->host->ops = &aspeed_ast2600_pcie_ops; + pcie->host->child_ops = &aspeed_ast2600_pcie_child_ops; + + return 0; +} + +static void aspeed_ast2700_pcie_map_ranges(struct aspeed_pcie *pcie, + u64 pci_addr) +{ + writel(ASPEED_REMAP_PCI_ADDR_31_12(pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_LO); + writel(ASPEED_REMAP_PCI_ADDR_63_32(pci_addr), + pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_HI); +} + +static int aspeed_ast2700_setup(struct platform_device *pdev) +{ + struct aspeed_pcie *pcie = platform_get_drvdata(pdev); + struct device *dev = pcie->dev; + + pcie->cfg = syscon_regmap_lookup_by_phandle(dev->of_node, + "aspeed,pciecfg"); + if (IS_ERR(pcie->cfg)) + return dev_err_probe(dev, PTR_ERR(pcie->cfg), + "failed to map pciecfg base\n"); + + regmap_update_bits(pcie->cfg, ASPEED_SCU_60, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN, + ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN | + ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN | + ASPEED_RC_UPSTREAM_MEM_EN); + regmap_write(pcie->cfg, ASPEED_SCU_64, + ASPEED_RC0_DECODE_DMA_BASE(0) | + ASPEED_RC0_DECODE_DMA_LIMIT(0xff) | + ASPEED_RC1_DECODE_DMA_BASE(0) | + ASPEED_RC1_DECODE_DMA_LIMIT(0xff)); + regmap_write(pcie->cfg, ASPEED_SCU_70, ASPEED_DISABLE_EP_FUNC); + + aspeed_host_reset(pcie); + + writel(0, pcie->reg + ASPEED_H2X_CTRL); + writel(ASPEED_H2X_BRIDGE_EN | ASPEED_H2X_BRIDGE_DIRECT_EN, + pcie->reg + ASPEED_H2X_CTRL); + + /* Prepare for 64-bit BAR pref */ + writel(ASPEED_REMAP_PREF_ADDR_63_32(0x3), + pcie->reg + ASPEED_H2X_REMAP_PREF_ADDR); + + pcie->host->ops = &aspeed_ast2700_pcie_ops; + pcie->host->child_ops = &aspeed_ast2700_pcie_child_ops; + pcie->clear_msi_twice = true; + + return 0; +} + +static void aspeed_pcie_reset_release(void *d) +{ + struct reset_control *perst = d; + + if (!perst) + return; + + reset_control_put(perst); +} + +static int aspeed_pcie_parse_port(struct aspeed_pcie *pcie, + struct device_node *node, + int slot) +{ + struct aspeed_pcie_port *port; + struct device *dev = pcie->dev; + int ret; + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->clk = devm_get_clk_from_child(dev, node, NULL); + if (IS_ERR(port->clk)) + return dev_err_probe(dev, PTR_ERR(port->clk), + "failed to get pcie%d clock\n", slot); + + port->phy = devm_of_phy_get(dev, node, NULL); + if (IS_ERR(port->phy)) + return dev_err_probe(dev, PTR_ERR(port->phy), + "failed to get phy pcie%d\n", slot); + + port->perst = of_reset_control_get_exclusive(node, "perst"); + if (IS_ERR(port->perst)) + return dev_err_probe(dev, PTR_ERR(port->perst), + "failed to get pcie%d reset control\n", + slot); + ret = devm_add_action_or_reset(dev, aspeed_pcie_reset_release, + port->perst); + if (ret) + return ret; + reset_control_assert(port->perst); + + port->slot = slot; + port->pcie = pcie; + + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + + ret = aspeed_pcie_port_init(port); + if (ret) + return ret; + + return 0; +} + +static int aspeed_pcie_parse_dt(struct aspeed_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; + int ret; + + for_each_available_child_of_node_scoped(node, child) { + int slot; + const char *type; + + ret = of_property_read_string(child, "device_type", &type); + if (ret || strcmp(type, "pci")) + continue; + + ret = of_pci_get_devfn(child); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to parse devfn\n"); + + slot = PCI_SLOT(ret); + + ret = aspeed_pcie_parse_port(pcie, child, slot); + if (ret) + return ret; + } + + if (list_empty(&pcie->ports)) + return dev_err_probe(dev, -ENODEV, + "No PCIe port found in DT\n"); + + return 0; +} + +static int aspeed_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_host_bridge *host; + struct aspeed_pcie *pcie; + struct resource_entry *entry; + const struct aspeed_pcie_rc_platform *md; + int irq, ret; + + md = of_device_get_match_data(dev); + if (!md) + return -ENODEV; + + host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); + if (!host) + return -ENOMEM; + + pcie = pci_host_bridge_priv(host); + pcie->dev = dev; + pcie->tx_tag = 0; + platform_set_drvdata(pdev, pcie); + + pcie->platform = md; + pcie->host = host; + INIT_LIST_HEAD(&pcie->ports); + + /* Get root bus num for cfg command to decide tlp type 0 or type 1 */ + entry = resource_list_first_type(&host->windows, IORESOURCE_BUS); + if (entry) + pcie->root_bus_nr = entry->res->start; + + pcie->reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pcie->reg)) + return PTR_ERR(pcie->reg); + + pcie->h2xrst = devm_reset_control_get_exclusive(dev, "h2x"); + if (IS_ERR(pcie->h2xrst)) + return dev_err_probe(dev, PTR_ERR(pcie->h2xrst), + "failed to get h2x reset\n"); + + ret = devm_mutex_init(dev, &pcie->lock); + if (ret) + return dev_err_probe(dev, ret, "failed to init mutex\n"); + + ret = pcie->platform->setup(pdev); + if (ret) + return dev_err_probe(dev, ret, "failed to setup PCIe RC\n"); + + aspeed_pcie_map_ranges(pcie); + + ret = aspeed_pcie_parse_dt(pcie); + if (ret) + return ret; + + host->sysdata = pcie; + + ret = aspeed_pcie_init_irq_domain(pcie); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_add_action_or_reset(dev, aspeed_pcie_irq_domain_free, pcie); + if (ret) + return ret; + + ret = devm_request_irq(dev, irq, aspeed_pcie_intr_handler, IRQF_SHARED, + dev_name(dev), pcie); + if (ret) + return ret; + + return pci_host_probe(host); +} + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2600 = { + .setup = aspeed_ast2600_setup, + .pcie_map_ranges = aspeed_ast2600_pcie_map_ranges, + .reg_intx_en = 0xc4, + .reg_intx_sts = 0xc8, + .reg_msi_en = 0xe0, + .reg_msi_sts = 0xe8, + .msi_address = 0x1e77005c, +}; + +static const struct aspeed_pcie_rc_platform pcie_rc_ast2700 = { + .setup = aspeed_ast2700_setup, + .pcie_map_ranges = aspeed_ast2700_pcie_map_ranges, + .reg_intx_en = 0x40, + .reg_intx_sts = 0x48, + .reg_msi_en = 0x50, + .reg_msi_sts = 0x58, + .msi_address = 0x000000f0, +}; + +static const struct of_device_id aspeed_pcie_of_match[] = { + { .compatible = "aspeed,ast2600-pcie", .data = &pcie_rc_ast2600 }, + { .compatible = "aspeed,ast2700-pcie", .data = &pcie_rc_ast2700 }, + {} +}; + +static struct platform_driver aspeed_pcie_driver = { + .driver = { + .name = "aspeed-pcie", + .of_match_table = aspeed_pcie_of_match, + .suppress_bind_attrs = true, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = aspeed_pcie_probe, +}; + +builtin_platform_driver(aspeed_pcie_driver); + +MODULE_AUTHOR("Jacky Chou <jacky_chou@aspeedtech.com>"); +MODULE_DESCRIPTION("ASPEED PCIe Root Complex"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 4b78b6528f9f..5defa5cc4c2b 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -585,8 +585,10 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port, if (IS_ENABLED(CONFIG_PCI_MSI)) { ret = mtk_pcie_allocate_msi_domains(port); - if (ret) + if (ret) { + irq_domain_remove(port->irq_domain); return ret; + } } return 0; diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 83ec66a70823..2809112e6317 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -73,6 +73,7 @@ #define RZG3S_PCI_PINTRCVIE_INTX(i) BIT(i) #define RZG3S_PCI_PINTRCVIE_MSI BIT(4) +/* Register is R/W1C, it doesn't require locking. */ #define RZG3S_PCI_PINTRCVIS 0x114 #define RZG3S_PCI_PINTRCVIS_INTX(i) BIT(i) #define RZG3S_PCI_PINTRCVIS_MSI BIT(4) @@ -114,6 +115,8 @@ #define RZG3S_PCI_MSIRE_ENA BIT(0) #define RZG3S_PCI_MSIRM(id) (0x608 + (id) * 0x10) + +/* Register is R/W1C, it doesn't require locking. */ #define RZG3S_PCI_MSIRS(id) (0x60c + (id) * 0x10) #define RZG3S_PCI_AWBASEL(id) (0x1000 + (id) * 0x20) @@ -439,28 +442,9 @@ static void __iomem *rzg3s_pcie_root_map_bus(struct pci_bus *bus, return host->pcie + where; } -/* Serialized by 'pci_lock' */ -static int rzg3s_pcie_root_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct rzg3s_pcie_host *host = bus->sysdata; - int ret; - - /* Enable access control to the CFGU */ - writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN, - host->axi + RZG3S_PCI_PERM); - - ret = pci_generic_config_write(bus, devfn, where, size, val); - - /* Disable access control to the CFGU */ - writel_relaxed(0, host->axi + RZG3S_PCI_PERM); - - return ret; -} - static struct pci_ops rzg3s_pcie_root_ops = { .read = pci_generic_config_read, - .write = rzg3s_pcie_root_write, + .write = pci_generic_config_write, .map_bus = rzg3s_pcie_root_map_bus, }; @@ -526,8 +510,6 @@ static void rzg3s_pcie_msi_irq_ack(struct irq_data *d) u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG; u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG; - guard(raw_spinlock_irqsave)(&host->hw_lock); - writel_relaxed(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id)); } @@ -859,8 +841,6 @@ static void rzg3s_pcie_intx_irq_ack(struct irq_data *d) { struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d); - guard(raw_spinlock_irqsave)(&host->hw_lock); - rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS, RZG3S_PCI_PINTRCVIS_INTX(d->hwirq), RZG3S_PCI_PINTRCVIS_INTX(d->hwirq)); @@ -1065,14 +1045,14 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host) writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L); writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U); + /* Disable access control to the CFGU */ + writel_relaxed(0, host->axi + RZG3S_PCI_PERM); + /* Update bus info */ writeb_relaxed(primary_bus, host->pcie + PCI_PRIMARY_BUS); writeb_relaxed(secondary_bus, host->pcie + PCI_SECONDARY_BUS); writeb_relaxed(subordinate_bus, host->pcie + PCI_SUBORDINATE_BUS); - /* Disable access control to the CFGU */ - writel_relaxed(0, host->axi + RZG3S_PCI_PERM); - return 0; } @@ -1162,7 +1142,8 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host) static int rzg3s_pcie_host_parse_port(struct rzg3s_pcie_host *host) { - struct device_node *of_port = of_get_next_child(host->dev->of_node, NULL); + struct device_node *of_port __free(device_node) = + of_get_next_child(host->dev->of_node, NULL); struct rzg3s_pcie_port *port = &host->port; int ret; diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c index 937ea6ae1ac4..4aa139abac16 100644 --- a/drivers/pci/controller/pcie-xilinx.c +++ b/drivers/pci/controller/pcie-xilinx.c @@ -302,9 +302,10 @@ static int xilinx_allocate_msi_domains(struct xilinx_pcie *pcie) return 0; } -static void xilinx_free_msi_domains(struct xilinx_pcie *pcie) +static void xilinx_free_irq_domains(struct xilinx_pcie *pcie) { irq_domain_remove(pcie->msi_domain); + irq_domain_remove(pcie->leg_domain); } /* INTx Functions */ @@ -480,8 +481,10 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie *pcie) phys_addr_t pa = ALIGN_DOWN(virt_to_phys(pcie), SZ_4K); ret = xilinx_allocate_msi_domains(pcie); - if (ret) + if (ret) { + irq_domain_remove(pcie->leg_domain); return ret; + } pcie_write(pcie, upper_32_bits(pa), XILINX_PCIE_REG_MSIBASE1); pcie_write(pcie, lower_32_bits(pa), XILINX_PCIE_REG_MSIBASE2); @@ -600,7 +603,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev) err = pci_host_probe(bridge); if (err) - xilinx_free_msi_domains(pcie); + xilinx_free_irq_domains(pcie); return err; } diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c index 3caf53c6c082..298036c3e7f9 100644 --- a/drivers/pci/controller/plda/pcie-starfive.c +++ b/drivers/pci/controller/plda/pcie-starfive.c @@ -55,7 +55,7 @@ struct starfive_jh7110_pcie { struct reset_control *resets; struct clk_bulk_data *clks; struct regmap *reg_syscon; - struct gpio_desc *power_gpio; + struct regulator *vpcie3v3; struct gpio_desc *reset_gpio; struct phy *phy; @@ -153,11 +153,13 @@ static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie, return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio), "failed to get perst-gpio\n"); - pcie->power_gpio = devm_gpiod_get_optional(dev, "enable", - GPIOD_OUT_LOW); - if (IS_ERR(pcie->power_gpio)) - return dev_err_probe(dev, PTR_ERR(pcie->power_gpio), - "failed to get power-gpio\n"); + pcie->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3"); + if (IS_ERR(pcie->vpcie3v3)) { + if (PTR_ERR(pcie->vpcie3v3) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(pcie->vpcie3v3), + "failed to get vpcie3v3 regulator\n"); + pcie->vpcie3v3 = NULL; + } return 0; } @@ -270,8 +272,8 @@ static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda) container_of(plda, struct starfive_jh7110_pcie, plda); starfive_pcie_clk_rst_deinit(pcie); - if (pcie->power_gpio) - gpiod_set_value_cansleep(pcie->power_gpio, 0); + if (pcie->vpcie3v3) + regulator_disable(pcie->vpcie3v3); starfive_pcie_disable_phy(pcie); } @@ -304,8 +306,11 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda) if (ret) return ret; - if (pcie->power_gpio) - gpiod_set_value_cansleep(pcie->power_gpio, 1); + if (pcie->vpcie3v3) { + ret = regulator_enable(pcie->vpcie3v3); + if (ret) + dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n"); + } if (pcie->reset_gpio) gpiod_set_value_cansleep(pcie->reset_gpio, 1); diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c index 9f4190501395..f075e7881c3a 100644 --- a/drivers/pci/devres.c +++ b/drivers/pci/devres.c @@ -469,9 +469,6 @@ static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev, if (!legacy_iomap_table) return -ENOMEM; - /* The legacy mechanism doesn't allow for duplicate mappings. */ - WARN_ON(legacy_iomap_table[bar]); - legacy_iomap_table[bar] = mapping; return 0; diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c index 6643a88c7a0c..27de533f0571 100644 --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -686,7 +686,7 @@ static int pci_epf_mhi_dma_init(struct pci_epf_mhi *epf_mhi) goto err_release_tx; } - epf_mhi->dma_wq = alloc_workqueue("pci_epf_mhi_dma_wq", 0, 0); + epf_mhi->dma_wq = alloc_workqueue("pci_epf_mhi_dma_wq", WQ_PERCPU, 0); if (!epf_mhi->dma_wq) { ret = -ENOMEM; goto err_release_rx; diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index e01a98e74d21..a3a588e522e7 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -2124,8 +2124,13 @@ static int __init epf_ntb_init(void) { int ret; - kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | - WQ_HIGHPRI, 0); + kpcintb_workqueue = alloc_workqueue("kpcintb", + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0); + if (!kpcintb_workqueue) { + pr_err("Failed to allocate kpcintb workqueue\n"); + return -ENOMEM; + } + ret = pci_epf_register_driver(&epf_ntb_driver); if (ret) { destroy_workqueue(kpcintb_workqueue); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index debd235253c5..0cb7af0919dc 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -33,6 +33,8 @@ #define COMMAND_COPY BIT(5) #define COMMAND_ENABLE_DOORBELL BIT(6) #define COMMAND_DISABLE_DOORBELL BIT(7) +#define COMMAND_BAR_SUBRANGE_SETUP BIT(8) +#define COMMAND_BAR_SUBRANGE_CLEAR BIT(9) #define STATUS_READ_SUCCESS BIT(0) #define STATUS_READ_FAIL BIT(1) @@ -48,6 +50,10 @@ #define STATUS_DOORBELL_ENABLE_FAIL BIT(11) #define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12) #define STATUS_DOORBELL_DISABLE_FAIL BIT(13) +#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS BIT(14) +#define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15) +#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16) +#define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) #define FLAG_USE_DMA BIT(0) @@ -57,12 +63,16 @@ #define CAP_MSI BIT(1) #define CAP_MSIX BIT(2) #define CAP_INTX BIT(3) +#define CAP_SUBRANGE_MAPPING BIT(4) + +#define PCI_EPF_TEST_BAR_SUBRANGE_NSUB 2 static struct workqueue_struct *kpcitest_workqueue; struct pci_epf_test { void *reg[PCI_STD_NUM_BARS]; struct pci_epf *epf; + struct config_group group; enum pci_barno test_reg_bar; size_t msix_table_offset; struct delayed_work cmd_handler; @@ -76,6 +86,7 @@ struct pci_epf_test { bool dma_private; const struct pci_epc_features *epc_features; struct pci_epf_bar db_bar; + size_t bar_size[PCI_STD_NUM_BARS]; }; struct pci_epf_test_reg { @@ -102,7 +113,8 @@ static struct pci_epf_header test_header = { .interrupt_pin = PCI_INTERRUPT_INTA, }; -static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; +/* default BAR sizes, can be overridden by the user using configfs */ +static size_t default_bar_size[] = { 131072, 131072, 131072, 131072, 131072, 1048576 }; static void pci_epf_test_dma_callback(void *param) { @@ -806,6 +818,155 @@ set_status_err: reg->status = cpu_to_le32(status); } +static u8 pci_epf_test_subrange_sig_byte(enum pci_barno barno, + unsigned int subno) +{ + return 0x50 + (barno * 8) + subno; +} + +static void pci_epf_test_bar_subrange_setup(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) +{ + struct pci_epf_bar_submap *submap, *old_submap; + struct pci_epf *epf = epf_test->epf; + struct pci_epc *epc = epf->epc; + struct pci_epf_bar *bar; + unsigned int nsub = PCI_EPF_TEST_BAR_SUBRANGE_NSUB, old_nsub; + /* reg->size carries BAR number for BAR_SUBRANGE_* commands. */ + enum pci_barno barno = le32_to_cpu(reg->size); + u32 status = le32_to_cpu(reg->status); + unsigned int i, phys_idx; + size_t sub_size; + u8 *addr; + int ret; + + if (barno >= PCI_STD_NUM_BARS) { + dev_err(&epf->dev, "Invalid barno: %d\n", barno); + goto err; + } + + /* Host side should've avoided test_reg_bar, this is a safeguard. */ + if (barno == epf_test->test_reg_bar) { + dev_err(&epf->dev, "test_reg_bar cannot be used for subrange test\n"); + goto err; + } + + if (!epf_test->epc_features->dynamic_inbound_mapping || + !epf_test->epc_features->subrange_mapping) { + dev_err(&epf->dev, "epc driver does not support subrange mapping\n"); + goto err; + } + + bar = &epf->bar[barno]; + if (!bar->size || !bar->addr) { + dev_err(&epf->dev, "bar size/addr (%zu/%p) is invalid\n", + bar->size, bar->addr); + goto err; + } + + if (bar->size % nsub) { + dev_err(&epf->dev, "BAR size %zu is not divisible by %u\n", + bar->size, nsub); + goto err; + } + + sub_size = bar->size / nsub; + + submap = kcalloc(nsub, sizeof(*submap), GFP_KERNEL); + if (!submap) + goto err; + + for (i = 0; i < nsub; i++) { + /* Swap the two halves so RC can verify ordering. */ + phys_idx = i ^ 1; + submap[i].phys_addr = bar->phys_addr + (phys_idx * sub_size); + submap[i].size = sub_size; + } + + old_submap = bar->submap; + old_nsub = bar->num_submap; + + bar->submap = submap; + bar->num_submap = nsub; + + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar); + if (ret) { + dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret); + bar->submap = old_submap; + bar->num_submap = old_nsub; + kfree(submap); + goto err; + } + kfree(old_submap); + + /* + * Fill deterministic signatures into the physical regions that + * each BAR subrange maps to. RC verifies these to ensure the + * submap order is really applied. + */ + addr = (u8 *)bar->addr; + for (i = 0; i < nsub; i++) { + phys_idx = i ^ 1; + memset(addr + (phys_idx * sub_size), + pci_epf_test_subrange_sig_byte(barno, i), + sub_size); + } + + status |= STATUS_BAR_SUBRANGE_SETUP_SUCCESS; + reg->status = cpu_to_le32(status); + return; + +err: + status |= STATUS_BAR_SUBRANGE_SETUP_FAIL; + reg->status = cpu_to_le32(status); +} + +static void pci_epf_test_bar_subrange_clear(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) +{ + struct pci_epf *epf = epf_test->epf; + struct pci_epf_bar_submap *submap; + struct pci_epc *epc = epf->epc; + /* reg->size carries BAR number for BAR_SUBRANGE_* commands. */ + enum pci_barno barno = le32_to_cpu(reg->size); + u32 status = le32_to_cpu(reg->status); + struct pci_epf_bar *bar; + unsigned int nsub; + int ret; + + if (barno >= PCI_STD_NUM_BARS) { + dev_err(&epf->dev, "Invalid barno: %d\n", barno); + goto err; + } + + bar = &epf->bar[barno]; + submap = bar->submap; + nsub = bar->num_submap; + + if (!submap || !nsub) + goto err; + + bar->submap = NULL; + bar->num_submap = 0; + + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar); + if (ret) { + bar->submap = submap; + bar->num_submap = nsub; + dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret); + goto err; + } + kfree(submap); + + status |= STATUS_BAR_SUBRANGE_CLEAR_SUCCESS; + reg->status = cpu_to_le32(status); + return; + +err: + status |= STATUS_BAR_SUBRANGE_CLEAR_FAIL; + reg->status = cpu_to_le32(status); +} + static void pci_epf_test_cmd_handler(struct work_struct *work) { u32 command; @@ -861,6 +1022,14 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) pci_epf_test_disable_doorbell(epf_test, reg); pci_epf_test_raise_irq(epf_test, reg); break; + case COMMAND_BAR_SUBRANGE_SETUP: + pci_epf_test_bar_subrange_setup(epf_test, reg); + pci_epf_test_raise_irq(epf_test, reg); + break; + case COMMAND_BAR_SUBRANGE_CLEAR: + pci_epf_test_bar_subrange_clear(epf_test, reg); + pci_epf_test_raise_irq(epf_test, reg); + break; default: dev_err(dev, "Invalid command 0x%x\n", command); break; @@ -933,6 +1102,10 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf) if (epf_test->epc_features->intx_capable) caps |= CAP_INTX; + if (epf_test->epc_features->dynamic_inbound_mapping && + epf_test->epc_features->subrange_mapping) + caps |= CAP_SUBRANGE_MAPPING; + reg->caps = cpu_to_le32(caps); } @@ -1070,7 +1243,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) if (epc_features->bar[bar].type == BAR_FIXED) test_reg_size = epc_features->bar[bar].fixed_size; else - test_reg_size = bar_size[bar]; + test_reg_size = epf_test->bar_size[bar]; base = pci_epf_alloc_space(epf, test_reg_size, bar, epc_features, PRIMARY_INTERFACE); @@ -1142,6 +1315,94 @@ static void pci_epf_test_unbind(struct pci_epf *epf) pci_epf_test_free_space(epf); } +#define PCI_EPF_TEST_BAR_SIZE_R(_name, _id) \ +static ssize_t pci_epf_test_##_name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct pci_epf_test *epf_test = \ + container_of(group, struct pci_epf_test, group); \ + \ + return sysfs_emit(page, "%zu\n", epf_test->bar_size[_id]); \ +} + +#define PCI_EPF_TEST_BAR_SIZE_W(_name, _id) \ +static ssize_t pci_epf_test_##_name##_store(struct config_item *item, \ + const char *page, \ + size_t len) \ +{ \ + struct config_group *group = to_config_group(item); \ + struct pci_epf_test *epf_test = \ + container_of(group, struct pci_epf_test, group); \ + int val, ret; \ + \ + /* \ + * BAR sizes can only be modified before binding to an EPC, \ + * because pci_epf_test_alloc_space() is called in .bind(). \ + */ \ + if (epf_test->epf->epc) \ + return -EOPNOTSUPP; \ + \ + ret = kstrtouint(page, 0, &val); \ + if (ret) \ + return ret; \ + \ + if (!is_power_of_2(val)) \ + return -EINVAL; \ + \ + epf_test->bar_size[_id] = val; \ + \ + return len; \ +} + +PCI_EPF_TEST_BAR_SIZE_R(bar0_size, BAR_0) +PCI_EPF_TEST_BAR_SIZE_W(bar0_size, BAR_0) +PCI_EPF_TEST_BAR_SIZE_R(bar1_size, BAR_1) +PCI_EPF_TEST_BAR_SIZE_W(bar1_size, BAR_1) +PCI_EPF_TEST_BAR_SIZE_R(bar2_size, BAR_2) +PCI_EPF_TEST_BAR_SIZE_W(bar2_size, BAR_2) +PCI_EPF_TEST_BAR_SIZE_R(bar3_size, BAR_3) +PCI_EPF_TEST_BAR_SIZE_W(bar3_size, BAR_3) +PCI_EPF_TEST_BAR_SIZE_R(bar4_size, BAR_4) +PCI_EPF_TEST_BAR_SIZE_W(bar4_size, BAR_4) +PCI_EPF_TEST_BAR_SIZE_R(bar5_size, BAR_5) +PCI_EPF_TEST_BAR_SIZE_W(bar5_size, BAR_5) + +CONFIGFS_ATTR(pci_epf_test_, bar0_size); +CONFIGFS_ATTR(pci_epf_test_, bar1_size); +CONFIGFS_ATTR(pci_epf_test_, bar2_size); +CONFIGFS_ATTR(pci_epf_test_, bar3_size); +CONFIGFS_ATTR(pci_epf_test_, bar4_size); +CONFIGFS_ATTR(pci_epf_test_, bar5_size); + +static struct configfs_attribute *pci_epf_test_attrs[] = { + &pci_epf_test_attr_bar0_size, + &pci_epf_test_attr_bar1_size, + &pci_epf_test_attr_bar2_size, + &pci_epf_test_attr_bar3_size, + &pci_epf_test_attr_bar4_size, + &pci_epf_test_attr_bar5_size, + NULL, +}; + +static const struct config_item_type pci_epf_test_group_type = { + .ct_attrs = pci_epf_test_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *pci_epf_test_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct pci_epf_test *epf_test = epf_get_drvdata(epf); + struct config_group *epf_group = &epf_test->group; + struct device *dev = &epf->dev; + + config_group_init_type_name(epf_group, dev_name(dev), + &pci_epf_test_group_type); + + return epf_group; +} + static const struct pci_epf_device_id pci_epf_test_ids[] = { { .name = "pci_epf_test", @@ -1154,6 +1415,7 @@ static int pci_epf_test_probe(struct pci_epf *epf, { struct pci_epf_test *epf_test; struct device *dev = &epf->dev; + enum pci_barno bar; epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL); if (!epf_test) @@ -1161,6 +1423,8 @@ static int pci_epf_test_probe(struct pci_epf *epf, epf->header = &test_header; epf_test->epf = epf; + for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) + epf_test->bar_size[bar] = default_bar_size[bar]; INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler); @@ -1173,6 +1437,7 @@ static int pci_epf_test_probe(struct pci_epf *epf, static const struct pci_epf_ops ops = { .unbind = pci_epf_test_unbind, .bind = pci_epf_test_bind, + .add_cfs = pci_epf_test_add_cfs, }; static struct pci_epf_driver test_driver = { @@ -1188,7 +1453,7 @@ static int __init pci_epf_test_init(void) int ret; kpcitest_workqueue = alloc_workqueue("kpcitest", - WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0); if (!kpcitest_workqueue) { pr_err("Failed to allocate the kpcitest work queue\n"); return -ENOMEM; diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 3ecc5059f92b..20a400e83439 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1651,8 +1651,13 @@ static int __init epf_ntb_init(void) { int ret; - kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM | - WQ_HIGHPRI, 0); + kpcintb_workqueue = alloc_workqueue("kpcintb", + WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0); + if (!kpcintb_workqueue) { + pr_err("Failed to allocate kpcintb workqueue\n"); + return -ENOMEM; + } + ret = pci_epf_register_driver(&epf_ntb_driver); if (ret) { destroy_workqueue(kpcintb_workqueue); diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index ef50c82e647f..8b392a8363bb 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -23,7 +23,6 @@ struct pci_epf_group { struct config_group group; struct config_group primary_epc_group; struct config_group secondary_epc_group; - struct delayed_work cfs_work; struct pci_epf *epf; int index; }; @@ -69,8 +68,8 @@ static int pci_secondary_epc_epf_link(struct config_item *epf_item, return 0; } -static void pci_secondary_epc_epf_unlink(struct config_item *epc_item, - struct config_item *epf_item) +static void pci_secondary_epc_epf_unlink(struct config_item *epf_item, + struct config_item *epc_item) { struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); @@ -103,7 +102,7 @@ static struct config_group secondary_epc_group = &epf_group->secondary_epc_group; config_group_init_type_name(secondary_epc_group, "secondary", &pci_secondary_epc_type); - configfs_register_group(&epf_group->group, secondary_epc_group); + configfs_add_default_group(secondary_epc_group, &epf_group->group); return secondary_epc_group; } @@ -133,8 +132,8 @@ static int pci_primary_epc_epf_link(struct config_item *epf_item, return 0; } -static void pci_primary_epc_epf_unlink(struct config_item *epc_item, - struct config_item *epf_item) +static void pci_primary_epc_epf_unlink(struct config_item *epf_item, + struct config_item *epc_item) { struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent); struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); @@ -166,7 +165,7 @@ static struct config_group config_group_init_type_name(primary_epc_group, "primary", &pci_primary_epc_type); - configfs_register_group(&epf_group->group, primary_epc_group); + configfs_add_default_group(primary_epc_group, &epf_group->group); return primary_epc_group; } @@ -570,15 +569,13 @@ static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) return; } - configfs_register_group(&epf_group->group, group); + configfs_add_default_group(group, &epf_group->group); } -static void pci_epf_cfs_work(struct work_struct *work) +static void pci_epf_cfs_add_sub_groups(struct pci_epf_group *epf_group) { - struct pci_epf_group *epf_group; struct config_group *group; - epf_group = container_of(work, struct pci_epf_group, cfs_work.work); group = pci_ep_cfs_add_primary_group(epf_group); if (IS_ERR(group)) { pr_err("failed to create 'primary' EPC interface\n"); @@ -637,9 +634,7 @@ static struct config_group *pci_epf_make(struct config_group *group, kfree(epf_name); - INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work); - queue_delayed_work(system_wq, &epf_group->cfs_work, - msecs_to_jiffies(1)); + pci_epf_cfs_add_sub_groups(epf_group); return &epf_group->group; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index ca7f19cc973a..068155819c57 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -596,6 +596,14 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, if (!epc_features) return -EINVAL; + if (epf_bar->num_submap && !epf_bar->submap) + return -EINVAL; + + if (epf_bar->num_submap && + !(epc_features->dynamic_inbound_mapping && + epc_features->subrange_mapping)) + return -EINVAL; + if (epc_features->bar[bar].type == BAR_RESIZABLE && (epf_bar->size < SZ_1M || (u64)epf_bar->size > (SZ_128G * 1024))) return -EINVAL; diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index bcc938d4420f..7805f697a02c 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -19,6 +19,7 @@ #include <linux/types.h> #include <linux/pm_runtime.h> #include <linux/pci.h> +#include <trace/events/pci.h> #include "../pci.h" #include "pciehp.h" @@ -244,12 +245,20 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) case ON_STATE: ctrl->state = POWEROFF_STATE; mutex_unlock(&ctrl->state_lock); - if (events & PCI_EXP_SLTSTA_DLLSC) + if (events & PCI_EXP_SLTSTA_DLLSC) { ctrl_info(ctrl, "Slot(%s): Link Down\n", slot_name(ctrl)); - if (events & PCI_EXP_SLTSTA_PDC) + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_LINK_DOWN); + } + if (events & PCI_EXP_SLTSTA_PDC) { ctrl_info(ctrl, "Slot(%s): Card not present\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_NOT_PRESENT); + } pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); break; default: @@ -269,6 +278,9 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) INDICATOR_NOOP); ctrl_info(ctrl, "Slot(%s): Card not present\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_NOT_PRESENT); } mutex_unlock(&ctrl->state_lock); return; @@ -281,12 +293,19 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) case OFF_STATE: ctrl->state = POWERON_STATE; mutex_unlock(&ctrl->state_lock); - if (present) + if (present) { ctrl_info(ctrl, "Slot(%s): Card present\n", slot_name(ctrl)); - if (link_active) - ctrl_info(ctrl, "Slot(%s): Link Up\n", - slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_CARD_PRESENT); + } + if (link_active) { + ctrl_info(ctrl, "Slot(%s): Link Up\n", slot_name(ctrl)); + trace_pci_hp_event(pci_name(ctrl->pcie->port), + slot_name(ctrl), + PCI_HOTPLUG_LINK_UP); + } ctrl->request_result = pciehp_enable_slot(ctrl); break; default: diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index bcc51b26d03d..ad5f28f6a8b1 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -320,7 +320,8 @@ int pciehp_check_link_status(struct controller *ctrl) } pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &linksta2); - __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status, linksta2); + __pcie_update_link_speed(ctrl->pcie->port->subordinate, PCIE_HOTPLUG, + lnk_status, linksta2); if (!found) { ctrl_info(ctrl, "Slot(%s): No device found\n", diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index c5345bff9a55..35f1758126c6 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c @@ -802,7 +802,7 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn) } /* Allocate workqueue for this slot's interrupt handling */ - php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); + php_slot->wq = alloc_workqueue("pciehp-%s", WQ_PERCPU, 0, php_slot->name); if (!php_slot->wq) { SLOT_WARN(php_slot, "Cannot alloc workqueue\n"); kfree(php_slot->name); diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 0c341453afc6..56308515ecba 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -80,7 +80,8 @@ static int init_slots(struct controller *ctrl) slot->device = ctrl->slot_device_offset + i; slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); - slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number); + slot->wq = alloc_workqueue("shpchp-%d", WQ_PERCPU, 0, + slot->number); if (!slot->wq) { retval = -ENOMEM; goto error_slot; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 00784a60ba80..4a659c34935e 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -495,7 +495,9 @@ static ssize_t sriov_numvfs_store(struct device *dev, if (num_vfs == 0) { /* disable VFs */ + pci_lock_rescan_remove(); ret = pdev->driver->sriov_configure(pdev, 0); + pci_unlock_rescan_remove(); goto exit; } @@ -507,7 +509,9 @@ static ssize_t sriov_numvfs_store(struct device *dev, goto exit; } + pci_lock_rescan_remove(); ret = pdev->driver->sriov_configure(pdev, num_vfs); + pci_unlock_rescan_remove(); if (ret < 0) goto exit; @@ -629,18 +633,15 @@ static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) if (dev->no_vf_scan) return 0; - pci_lock_rescan_remove(); for (i = 0; i < num_vfs; i++) { rc = pci_iov_add_virtfn(dev, i); if (rc) goto failed; } - pci_unlock_rescan_remove(); return 0; failed: while (i--) pci_iov_remove_virtfn(dev, i); - pci_unlock_rescan_remove(); return rc; } @@ -765,10 +766,8 @@ static void sriov_del_vfs(struct pci_dev *dev) struct pci_sriov *iov = dev->sriov; int i; - pci_lock_rescan_remove(); for (i = 0; i < iov->num_VFs; i++) pci_iov_remove_virtfn(dev, i); - pci_unlock_rescan_remove(); } static void sriov_disable(struct pci_dev *dev) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 3579265f1198..9bb5f258759b 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -867,6 +867,7 @@ bool of_pci_supply_present(struct device_node *np) return false; } +EXPORT_SYMBOL_GPL(of_pci_supply_present); #endif /* CONFIG_PCI */ diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 4a2fc7ab42c3..79a414fd6623 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -147,11 +147,19 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj, * we have just allocated the page no one else should be * using it. */ - VM_WARN_ON_ONCE_PAGE(!page_ref_count(page), page); + VM_WARN_ON_ONCE_PAGE(page_ref_count(page), page); set_page_count(page, 1); ret = vm_insert_page(vma, vaddr, page); if (ret) { gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len); + + /* + * Reset the page count. We don't use put_page() + * because we don't want to trigger the + * p2pdma_folio_free() path. + */ + set_page_count(page, 0); + percpu_ref_put(ref); return ret; } percpu_ref_get(ref); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 72c101c2926f..218e1fc1c1e2 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -272,21 +272,6 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record, return AE_OK; } -static bool pcie_root_rcb_set(struct pci_dev *dev) -{ - struct pci_dev *rp = pcie_find_root_port(dev); - u16 lnkctl; - - if (!rp) - return false; - - pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl); - if (lnkctl & PCI_EXP_LNKCTL_RCB) - return true; - - return false; -} - /* _HPX PCI Express Setting Record (Type 2) */ struct hpx_type2 { u32 revision; @@ -312,6 +297,7 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) { int pos; u32 reg32; + const struct pci_host_bridge *host; if (!hpx) return; @@ -319,6 +305,15 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) if (!pci_is_pcie(dev)) return; + host = pci_find_host_bridge(dev->bus); + + /* + * Only do the _HPX Type 2 programming if OS owns PCIe native + * hotplug but not AER. + */ + if (!host->native_pcie_hotplug || host->native_aer) + return; + if (hpx->revision > 1) { pci_warn(dev, "PCIe settings rev %d not supported\n", hpx->revision); @@ -326,33 +321,27 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx) } /* - * Don't allow _HPX to change MPS or MRRS settings. We manage - * those to make sure they're consistent with the rest of the - * platform. + * We only allow _HPX to program DEVCTL bits related to AER, namely + * PCI_EXP_DEVCTL_CERE, PCI_EXP_DEVCTL_NFERE, PCI_EXP_DEVCTL_FERE, + * and PCI_EXP_DEVCTL_URRE. + * + * The rest of DEVCTL is managed by the OS to make sure it's + * consistent with the rest of the platform. */ - hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD | - PCI_EXP_DEVCTL_READRQ; - hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD | - PCI_EXP_DEVCTL_READRQ); + hpx->pci_exp_devctl_and |= ~PCI_EXP_AER_FLAGS; + hpx->pci_exp_devctl_or &= PCI_EXP_AER_FLAGS; /* Initialize Device Control Register */ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, ~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or); - /* Initialize Link Control Register */ + /* Log if _HPX attempts to modify Link Control Register */ if (pcie_cap_has_lnkctl(dev)) { - - /* - * If the Root Port supports Read Completion Boundary of - * 128, set RCB to 128. Otherwise, clear it. - */ - hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB; - hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB; - if (pcie_root_rcb_set(dev)) - hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB; - - pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, - ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or); + if (hpx->pci_exp_lnkctl_and != 0xffff || + hpx->pci_exp_lnkctl_or != 0) + pci_info(dev, "_HPX attempts Link Control setting (AND %#06x OR %#06x)\n", + hpx->pci_exp_lnkctl_and, + hpx->pci_exp_lnkctl_or); } /* Find Advanced Error Reporting Enhanced Capability */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a9590601835a..47887d55994a 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1679,6 +1679,14 @@ static int pci_dma_configure(struct device *dev) ret = acpi_dma_configure(dev, acpi_get_dma_attr(adev)); } + /* + * Attempt to enable ACS regardless of capability because some Root + * Ports (e.g. those quirked with *_intel_pch_acs_*) do not have + * the standard ACS capability but still support ACS via those + * quirks. + */ + pci_enable_acs(to_pci_dev(dev)); + pci_put_host_bridge_device(bridge); /* @drv may not be valid when we're called from the IOMMU layer */ @@ -1730,34 +1738,6 @@ const struct bus_type pci_bus_type = { }; EXPORT_SYMBOL(pci_bus_type); -#ifdef CONFIG_PCIEPORTBUS -static int pcie_port_bus_match(struct device *dev, const struct device_driver *drv) -{ - struct pcie_device *pciedev; - const struct pcie_port_service_driver *driver; - - if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(drv); - - if (driver->service != pciedev->service) - return 0; - - if (driver->port_type != PCIE_ANY_PORT && - driver->port_type != pci_pcie_type(pciedev->port)) - return 0; - - return 1; -} - -const struct bus_type pcie_port_bus_type = { - .name = "pci_express", - .match = pcie_port_bus_match, -}; -#endif - static int __init pci_driver_init(void) { int ret; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c2df915ad2d2..363187ba4f56 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -181,7 +181,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, struct resource zerores = {}; /* For backwards compatibility */ - if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END && + if (pci_resource_is_bridge_win(i) && res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) res = &zerores; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a0ba42ae7ee0..f3244630bfd0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -14,6 +14,7 @@ #include <linux/dmi.h> #include <linux/init.h> #include <linux/iommu.h> +#include <linux/lockdep.h> #include <linux/msi.h> #include <linux/of.h> #include <linux/pci.h> @@ -101,12 +102,6 @@ bool pci_reset_supported(struct pci_dev *dev) int pci_domains_supported = 1; #endif -#define DEFAULT_CARDBUS_IO_SIZE (256) -#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024) -/* pci=cbmemsize=nnM,cbiosize=nn can override this */ -unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; -unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; - #define DEFAULT_HOTPLUG_IO_SIZE (256) #define DEFAULT_HOTPLUG_MMIO_SIZE (2*1024*1024) #define DEFAULT_HOTPLUG_MMIO_PREF_SIZE (2*1024*1024) @@ -428,7 +423,7 @@ found: static u8 __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap) { - return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, bus, devfn); + return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, NULL, bus, devfn); } u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap) @@ -533,7 +528,7 @@ u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 start, int cap) return 0; return PCI_FIND_NEXT_EXT_CAP(pci_bus_read_config, start, cap, - dev->bus, dev->devfn); + NULL, dev->bus, dev->devfn); } EXPORT_SYMBOL_GPL(pci_find_next_ext_capability); @@ -602,7 +597,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) mask = HT_5BIT_CAP_MASK; pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, - PCI_CAP_ID_HT, dev->bus, dev->devfn); + PCI_CAP_ID_HT, NULL, dev->bus, dev->devfn); while (pos) { rc = pci_read_config_byte(dev, pos + 3, &cap); if (rc != PCIBIOS_SUCCESSFUL) @@ -613,7 +608,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos + PCI_CAP_LIST_NEXT, - PCI_CAP_ID_HT, dev->bus, + PCI_CAP_ID_HT, NULL, dev->bus, dev->devfn); } @@ -894,7 +889,6 @@ static const char *disable_acs_redir_param; static const char *config_acs_param; struct pci_acs { - u16 cap; u16 ctrl; u16 fw_ctrl; }; @@ -997,27 +991,27 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps, static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps) { /* Source Validation */ - caps->ctrl |= (caps->cap & PCI_ACS_SV); + caps->ctrl |= (dev->acs_capabilities & PCI_ACS_SV); /* P2P Request Redirect */ - caps->ctrl |= (caps->cap & PCI_ACS_RR); + caps->ctrl |= (dev->acs_capabilities & PCI_ACS_RR); /* P2P Completion Redirect */ - caps->ctrl |= (caps->cap & PCI_ACS_CR); + caps->ctrl |= (dev->acs_capabilities & PCI_ACS_CR); /* Upstream Forwarding */ - caps->ctrl |= (caps->cap & PCI_ACS_UF); + caps->ctrl |= (dev->acs_capabilities & PCI_ACS_UF); /* Enable Translation Blocking for external devices and noats */ if (pci_ats_disabled() || dev->external_facing || dev->untrusted) - caps->ctrl |= (caps->cap & PCI_ACS_TB); + caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB); } /** * pci_enable_acs - enable ACS if hardware support it * @dev: the PCI device */ -static void pci_enable_acs(struct pci_dev *dev) +void pci_enable_acs(struct pci_dev *dev) { struct pci_acs caps; bool enable_acs = false; @@ -1033,7 +1027,6 @@ static void pci_enable_acs(struct pci_dev *dev) if (!pos) return; - pci_read_config_word(dev, pos + PCI_ACS_CAP, &caps.cap); pci_read_config_word(dev, pos + PCI_ACS_CTRL, &caps.ctrl); caps.fw_ctrl = caps.ctrl; @@ -1490,6 +1483,9 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool || (state == PCI_D2 && !dev->d2_support)) return -EIO; + if (dev->current_state == state) + return 0; + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); if (PCI_POSSIBLE_ERROR(pmcsr)) { pci_err(dev, "Unable to change power state from %s to %s, device inaccessible\n", @@ -2258,7 +2254,7 @@ void pcie_clear_device_status(struct pci_dev *dev) */ void pcie_clear_root_pme_status(struct pci_dev *dev) { - pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); + pcie_capability_write_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME); } /** @@ -3198,8 +3194,14 @@ void pci_pm_init(struct pci_dev *dev) poweron: pci_pm_power_up_and_verify_state(dev); pm_runtime_forbid(&dev->dev); + + /* + * Runtime PM will be enabled for the device when it has been fully + * configured, but since its parent and suppliers may suspend in + * the meantime, prevent them from doing so by changing the + * device's runtime PM status to "active". + */ pm_runtime_set_active(&dev->dev); - pm_runtime_enable(&dev->dev); } static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop) @@ -3516,7 +3518,7 @@ void pci_configure_ari(struct pci_dev *dev) static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { int pos; - u16 cap, ctrl; + u16 ctrl; pos = pdev->acs_cap; if (!pos) @@ -3527,8 +3529,7 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) * or only required if controllable. Features missing from the * capability field can therefore be assumed as hard-wired enabled. */ - pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap); - acs_flags &= (cap | PCI_ACS_EC); + acs_flags &= (pdev->acs_capabilities | PCI_ACS_EC); pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); return (ctrl & acs_flags) == acs_flags; @@ -3649,15 +3650,15 @@ bool pci_acs_path_enabled(struct pci_dev *start, */ void pci_acs_init(struct pci_dev *dev) { + int pos; + dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); + pos = dev->acs_cap; + if (!pos) + return; - /* - * Attempt to enable ACS regardless of capability because some Root - * Ports (e.g. those quirked with *_intel_pch_acs_*) do not have - * the standard ACS capability but still support ACS via those - * quirks. - */ - pci_enable_acs(dev); + pci_read_config_word(dev, pos + PCI_ACS_CAP, &dev->acs_capabilities); + pci_disable_broken_acs_cap(dev); } /** @@ -4584,7 +4585,7 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt) * Link Speed. */ if (pdev->subordinate) - pcie_update_link_speed(pdev->subordinate); + pcie_update_link_speed(pdev->subordinate, PCIE_LINK_RETRAIN); return rc; } @@ -4656,7 +4657,7 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active) * spec says 100 ms, but firmware can lower it and we allow drivers to * increase it as well. * - * Called with @pci_bus_sem locked for reading. + * Context: Called with @pci_bus_sem locked for reading. */ static int pci_bus_max_d3cold_delay(const struct pci_bus *bus) { @@ -4664,6 +4665,8 @@ static int pci_bus_max_d3cold_delay(const struct pci_bus *bus) int min_delay = 100; int max_delay = 0; + lockdep_assert_held(&pci_bus_sem); + list_for_each_entry(pdev, &bus->devices, bus_list) { if (pdev->d3cold_delay < min_delay) min_delay = pdev->d3cold_delay; @@ -5018,6 +5021,7 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) * races with ->remove() by the device lock, which must be held by * the caller. */ + device_lock_assert(&dev->dev); if (err_handler && err_handler->reset_prepare) err_handler->reset_prepare(dev); else if (dev->driver) @@ -5088,7 +5092,9 @@ const struct pci_reset_fn_method pci_reset_fn_methods[] = { * device including MSI, bus mastering, BARs, decoding IO and memory spaces, * etc. * - * Returns 0 if the device function was successfully reset or negative if the + * Context: The caller must hold the device lock. + * + * Return: 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. */ int __pci_reset_function_locked(struct pci_dev *dev) @@ -5097,6 +5103,7 @@ int __pci_reset_function_locked(struct pci_dev *dev) const struct pci_reset_fn_method *method; might_sleep(); + device_lock_assert(&dev->dev); /* * A reset method returns -ENOTTY if it doesn't support this device and @@ -5219,13 +5226,17 @@ EXPORT_SYMBOL_GPL(pci_reset_function); * over the reset. It also differs from pci_reset_function() in that it * requires the PCI device lock to be held. * - * Returns 0 if the device function was successfully reset or negative if the + * Context: The caller must hold the device lock. + * + * Return: 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. */ int pci_reset_function_locked(struct pci_dev *dev) { int rc; + device_lock_assert(&dev->dev); + if (!pci_reset_supported(dev)) return -ENOTTY; @@ -5341,10 +5352,9 @@ unlock: /* Do any devices on or below this slot prevent a bus reset? */ static bool pci_slot_resettable(struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = slot->bus->self; - if (slot->bus->self && - (slot->bus->self->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)) + if (bridge && (bridge->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)) return false; list_for_each_entry(dev, &slot->bus->devices, bus_list) { @@ -5361,7 +5371,10 @@ static bool pci_slot_resettable(struct pci_slot *slot) /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = slot->bus->self; + + if (bridge) + pci_dev_lock(bridge); list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) @@ -5376,7 +5389,7 @@ static void pci_slot_lock(struct pci_slot *slot) /* Unlock devices from the bottom of the tree up */ static void pci_slot_unlock(struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = slot->bus->self; list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) @@ -5386,21 +5399,25 @@ static void pci_slot_unlock(struct pci_slot *slot) else pci_dev_unlock(dev); } + + if (bridge) + pci_dev_unlock(bridge); } /* Return 1 on successful lock, 0 on contention */ static int pci_slot_trylock(struct pci_slot *slot) { - struct pci_dev *dev; + struct pci_dev *dev, *bridge = slot->bus->self; + + if (bridge && !pci_dev_trylock(bridge)) + return 0; list_for_each_entry(dev, &slot->bus->devices, bus_list) { if (!dev->slot || dev->slot != slot) continue; if (dev->subordinate) { - if (!pci_bus_trylock(dev->subordinate)) { - pci_dev_unlock(dev); + if (!pci_bus_trylock(dev->subordinate)) goto unlock; - } } else if (!pci_dev_trylock(dev)) goto unlock; } @@ -5416,6 +5433,9 @@ unlock: else pci_dev_unlock(dev); } + + if (bridge) + pci_dev_unlock(bridge); return 0; } @@ -6642,7 +6662,7 @@ static void of_pci_bus_release_domain_nr(struct device *parent, int domain_nr) return; /* Release domain from IDA where it was allocated. */ - if (of_get_pci_domain_nr(parent->of_node) == domain_nr) + if (parent && of_get_pci_domain_nr(parent->of_node) == domain_nr) ida_free(&pci_domain_nr_static_ida, domain_nr); else ida_free(&pci_domain_nr_dynamic_ida, domain_nr); @@ -6681,7 +6701,9 @@ static int __init pci_setup(char *str) if (k) *k++ = 0; if (*str && (str = pcibios_setup(str)) && *str) { - if (!strcmp(str, "nomsi")) { + if (!pci_setup_cardbus(str)) { + /* Function handled the parameters */ + } else if (!strcmp(str, "nomsi")) { pci_no_msi(); } else if (!strncmp(str, "noats", 5)) { pr_info("PCIe: ATS is disabled\n"); @@ -6700,10 +6722,6 @@ static int __init pci_setup(char *str) pcie_ari_disabled = true; } else if (!strncmp(str, "notph", 5)) { pci_no_tph(); - } else if (!strncmp(str, "cbiosize=", 9)) { - pci_cardbus_io_size = memparse(str + 9, &str); - } else if (!strncmp(str, "cbmemsize=", 10)) { - pci_cardbus_mem_size = memparse(str + 10, &str); } else if (!strncmp(str, "resource_alignment=", 19)) { resource_alignment_param = str + 19; } else if (!strncmp(str, "ecrc=", 5)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0e67014aa001..c8a0522e2e1f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -5,6 +5,7 @@ #include <linux/align.h> #include <linux/bitfield.h> #include <linux/pci.h> +#include <trace/events/pci.h> struct pcie_tlp_log; @@ -63,6 +64,18 @@ struct pcie_tlp_log; #define PCIE_LINK_WAIT_MAX_RETRIES 10 #define PCIE_LINK_WAIT_SLEEP_MS 90 +/* Format of TLP; PCIe r7.0, sec 2.2.1 */ +#define PCIE_TLP_FMT_3DW_NO_DATA 0x00 /* 3DW header, no data */ +#define PCIE_TLP_FMT_4DW_NO_DATA 0x01 /* 4DW header, no data */ +#define PCIE_TLP_FMT_3DW_DATA 0x02 /* 3DW header, with data */ +#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 */ + /* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */ #define PCIE_MSG_TYPE_R_RC 0 #define PCIE_MSG_TYPE_R_ADDR 1 @@ -84,10 +97,16 @@ struct pcie_tlp_log; #define PCIE_MSG_CODE_DEASSERT_INTC 0x26 #define PCIE_MSG_CODE_DEASSERT_INTD 0x27 +/* Cpl. status of Complete; PCIe r7.0, sec 2.2.9.1 */ +#define PCIE_CPL_STS_SUCCESS 0x00 /* Successful Completion */ + #define PCI_BUS_BRIDGE_IO_WINDOW 0 #define PCI_BUS_BRIDGE_MEM_WINDOW 1 #define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2 +#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) + extern const unsigned char pcie_link_speed[]; extern bool pci_early_dump; @@ -103,17 +122,21 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); * @read_cfg: Function pointer for reading PCI config space * @start: Starting position to begin search * @cap: Capability ID to find + * @prev_ptr: Pointer to store position of preceding capability (optional) * @args: Arguments to pass to read_cfg function * - * Search the capability list in PCI config space to find @cap. + * Search the capability list in PCI config space to find @cap. If + * found, update *prev_ptr with the position of the preceding capability + * (if prev_ptr != NULL) * Implements TTL (time-to-live) protection against infinite loops. * * Return: Position of the capability if found, 0 otherwise. */ -#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...) \ +#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, prev_ptr, args...) \ ({ \ int __ttl = PCI_FIND_CAP_TTL; \ - u8 __id, __found_pos = 0; \ + u8 __id, __found_pos = 0; \ + u8 __prev_pos = (start); \ u8 __pos = (start); \ u16 __ent; \ \ @@ -132,9 +155,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); \ if (__id == (cap)) { \ __found_pos = __pos; \ + if (prev_ptr != NULL) \ + *(u8 *)prev_ptr = __prev_pos; \ break; \ } \ \ + __prev_pos = __pos; \ __pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, __ent); \ } \ __found_pos; \ @@ -146,21 +172,26 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); * @read_cfg: Function pointer for reading PCI config space * @start: Starting position to begin search (0 for initial search) * @cap: Extended capability ID to find + * @prev_ptr: Pointer to store position of preceding capability (optional) * @args: Arguments to pass to read_cfg function * * Search the extended capability list in PCI config space to find @cap. + * If found, update *prev_ptr with the position of the preceding capability + * (if prev_ptr != NULL) * Implements TTL protection against infinite loops using a calculated * maximum search count. * * Return: Position of the capability if found, 0 otherwise. */ -#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, args...) \ +#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, prev_ptr, args...) \ ({ \ u16 __pos = (start) ?: PCI_CFG_SPACE_SIZE; \ u16 __found_pos = 0; \ + u16 __prev_pos; \ int __ttl, __ret; \ u32 __header; \ \ + __prev_pos = __pos; \ __ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; \ while (__ttl-- > 0 && __pos >= PCI_CFG_SPACE_SIZE) { \ __ret = read_cfg##_dword(args, __pos, &__header); \ @@ -172,9 +203,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev); \ if (PCI_EXT_CAP_ID(__header) == (cap) && __pos != start) {\ __found_pos = __pos; \ + if (prev_ptr != NULL) \ + *(u16 *)prev_ptr = __prev_pos; \ break; \ } \ \ + __prev_pos = __pos; \ __pos = PCI_EXT_CAP_NEXT(__header); \ } \ __found_pos; \ @@ -242,6 +276,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev); void pci_pm_init(struct pci_dev *dev); void pci_ea_init(struct pci_dev *dev); +bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub); void pci_msi_init(struct pci_dev *dev); void pci_msix_init(struct pci_dev *dev); bool pci_bridge_d3_possible(struct pci_dev *dev); @@ -376,8 +411,40 @@ extern unsigned long pci_hotplug_io_size; extern unsigned long pci_hotplug_mmio_size; extern unsigned long pci_hotplug_mmio_pref_size; extern unsigned long pci_hotplug_bus_size; -extern unsigned long pci_cardbus_io_size; -extern unsigned long pci_cardbus_mem_size; + +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); +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, + u32 buses, int max, + unsigned int available_buses, int pass); +int pci_setup_cardbus(char *str); + +#else +static inline unsigned long pci_cardbus_resource_alignment(struct resource *res) +{ + return 0; +} +static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus, + struct list_head *realloc_head) +{ + return -EOPNOTSUPP; +} +static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, + struct pci_dev *dev, + u32 buses, int max, + unsigned int available_buses, + int pass) +{ + return max; +} +static inline int pci_setup_cardbus(char *str) { return -ENOENT; } +#endif /* CONFIG_CARDBUS */ /** * pci_match_one_device - Tell if a PCI device structure has a matching @@ -432,7 +499,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout); bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout); -int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout); int pci_setup_device(struct pci_dev *dev); void __pci_size_stdbars(struct pci_dev *dev, int count, @@ -440,6 +506,10 @@ void __pci_size_stdbars(struct pci_dev *dev, int count, int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg, u32 *sizes); void pci_configure_ari(struct pci_dev *dev); + +int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, + struct resource *res, resource_size_t add_size, + resource_size_t min_align); void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head); void __pci_bus_assign_resources(const struct pci_bus *bus, @@ -452,6 +522,11 @@ void pci_walk_bus_locked(struct pci_bus *top, const char *pci_resource_name(struct pci_dev *dev, unsigned int i); bool pci_resource_is_optional(const struct pci_dev *dev, int resno); +static inline bool pci_resource_is_bridge_win(int resno) +{ + return resno >= PCI_BRIDGE_RESOURCES && + resno <= PCI_BRIDGE_RESOURCE_END; +} /** * pci_resource_num - Reverse lookup resource number from device resources @@ -475,6 +550,7 @@ static inline int pci_resource_num(const struct pci_dev *dev, return resno; } +void pbus_validate_busn(struct pci_bus *bus); struct resource *pbus_select_window(struct pci_bus *bus, const struct resource *res); void pci_reassigndev_resource_alignment(struct pci_dev *dev); @@ -555,12 +631,28 @@ const char *pci_speed_string(enum pci_bus_speed speed); void __pcie_print_link_status(struct pci_dev *dev, bool verbose); void pcie_report_downtraining(struct pci_dev *dev); -static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta, u16 linksta2) +enum pcie_link_change_reason { + PCIE_LINK_RETRAIN, + PCIE_ADD_BUS, + PCIE_BWCTRL_ENABLE, + PCIE_BWCTRL_IRQ, + PCIE_HOTPLUG, +}; + +static inline void __pcie_update_link_speed(struct pci_bus *bus, + enum pcie_link_change_reason reason, + u16 linksta, u16 linksta2) { bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS]; bus->flit_mode = (linksta2 & PCI_EXP_LNKSTA2_FLIT) ? 1 : 0; + + trace_pcie_link_event(bus, + reason, + FIELD_GET(PCI_EXP_LNKSTA_NLW, linksta), + linksta & PCI_EXP_LNKSTA_LINK_STATUS_MASK); } -void pcie_update_link_speed(struct pci_bus *bus); + +void pcie_update_link_speed(struct pci_bus *bus, enum pcie_link_change_reason reason); /* Single Root I/O Virtualization */ struct pci_sriov { @@ -924,8 +1016,6 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { } static inline void pci_resume_ptm(struct pci_dev *dev) { } #endif -unsigned long pci_cardbus_resource_alignment(struct resource *); - static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, struct resource *res) { @@ -939,10 +1029,12 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev, } void pci_acs_init(struct pci_dev *dev); +void pci_enable_acs(struct pci_dev *dev); #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); int pci_dev_specific_enable_acs(struct pci_dev *dev); int pci_dev_specific_disable_acs_redir(struct pci_dev *dev); +void pci_disable_broken_acs_cap(struct pci_dev *pdev); int pcie_failed_link_retrain(struct pci_dev *dev); #else static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, @@ -958,6 +1050,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) { return -ENOTTY; } +static inline void pci_disable_broken_acs_cap(struct pci_dev *dev) { } static inline int pcie_failed_link_retrain(struct pci_dev *dev) { return -ENOTTY; diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index 71ee4f5064de..2b0ac6bfdd76 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -239,9 +239,6 @@ void pcie_ecrc_get_policy(char *str) } #endif /* CONFIG_PCIE_ECRC */ -#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \ - PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) - int pcie_aer_is_native(struct pci_dev *dev) { struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); @@ -1608,6 +1605,20 @@ static void aer_disable_irq(struct pci_dev *pdev) pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32); } +static int clear_status_iter(struct pci_dev *dev, void *data) +{ + u16 devctl; + + /* Skip if pci_enable_pcie_error_reporting() hasn't been called yet */ + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &devctl); + if (!(devctl & PCI_EXP_AER_FLAGS)) + return 0; + + pci_aer_clear_status(dev); + pcie_clear_device_status(dev); + return 0; +} + /** * aer_enable_rootport - enable Root Port's interrupts when receiving messages * @rpc: pointer to a Root Port data structure @@ -1629,9 +1640,19 @@ static void aer_enable_rootport(struct aer_rpc *rpc) pcie_capability_clear_word(pdev, PCI_EXP_RTCTL, SYSTEM_ERROR_INTR_ON_MESG_MASK); - /* Clear error status */ + /* Clear error status of this Root Port or RCEC */ pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32); + + /* Clear error status of agents reporting to this Root Port or RCEC */ + if (reg32 & AER_ERR_STATUS_MASK) { + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_EC) + pcie_walk_rcec(pdev, clear_status_iter, NULL); + else if (pdev->subordinate) + pci_walk_bus(pdev->subordinate, clear_status_iter, + NULL); + } + pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, ®32); pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32); pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, ®32); diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c index 36f939f23d34..c4c8d260bf96 100644 --- a/drivers/pci/pcie/bwctrl.c +++ b/drivers/pci/pcie/bwctrl.c @@ -199,7 +199,7 @@ static void pcie_bwnotif_enable(struct pcie_device *srv) * Update after enabling notifications & clearing status bits ensures * link speed is up to date. */ - pcie_update_link_speed(port->subordinate); + pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_ENABLE); } static void pcie_bwnotif_disable(struct pci_dev *port) @@ -234,7 +234,7 @@ static irqreturn_t pcie_bwnotif_irq(int irq, void *context) * speed (inside pcie_update_link_speed()) after LBMS has been * cleared to avoid missing link speed changes. */ - pcie_update_link_speed(port->subordinate); + pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_IRQ); return IRQ_HANDLED; } @@ -250,6 +250,9 @@ static int pcie_bwnotif_probe(struct pcie_device *srv) struct pci_dev *port = srv->port; int ret; + if (port->no_bw_notif) + return -ENODEV; + /* Can happen if we run out of bus numbers during enumeration. */ if (!port->subordinate) return -ENODEV; diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c index 38a41ccf79b9..88af0dacf351 100644 --- a/drivers/pci/pcie/portdrv.c +++ b/drivers/pci/pcie/portdrv.c @@ -508,23 +508,35 @@ static void pcie_port_device_remove(struct pci_dev *dev) pci_free_irq_vectors(dev); } +static int pcie_port_bus_match(struct device *dev, const struct device_driver *drv) +{ + struct pcie_device *pciedev = to_pcie_device(dev); + const struct pcie_port_service_driver *driver = to_service_driver(drv); + + if (driver->service != pciedev->service) + return 0; + + if (driver->port_type != PCIE_ANY_PORT && + driver->port_type != pci_pcie_type(pciedev->port)) + return 0; + + return 1; +} + /** - * pcie_port_probe_service - probe driver for given PCI Express port service + * pcie_port_bus_probe - probe driver for given PCI Express port service * @dev: PCI Express port service device to probe against * * If PCI Express port service driver is registered with * pcie_port_service_register(), this function will be called by the driver core * whenever match is found between the driver and a port service device. */ -static int pcie_port_probe_service(struct device *dev) +static int pcie_port_bus_probe(struct device *dev) { struct pcie_device *pciedev; struct pcie_port_service_driver *driver; int status; - if (!dev || !dev->driver) - return -ENODEV; - driver = to_service_driver(dev->driver); if (!driver || !driver->probe) return -ENODEV; @@ -539,7 +551,7 @@ static int pcie_port_probe_service(struct device *dev) } /** - * pcie_port_remove_service - detach driver from given PCI Express port service + * pcie_port_bus_remove - detach driver from given PCI Express port service * @dev: PCI Express port service device to handle * * If PCI Express port service driver is registered with @@ -547,33 +559,25 @@ static int pcie_port_probe_service(struct device *dev) * when device_unregister() is called for the port service device associated * with the driver. */ -static int pcie_port_remove_service(struct device *dev) +static void pcie_port_bus_remove(struct device *dev) { struct pcie_device *pciedev; struct pcie_port_service_driver *driver; - if (!dev || !dev->driver) - return 0; - pciedev = to_pcie_device(dev); driver = to_service_driver(dev->driver); - if (driver && driver->remove) { + if (driver && driver->remove) driver->remove(pciedev); - put_device(dev); - } - return 0; + + put_device(dev); } -/** - * pcie_port_shutdown_service - shut down given PCI Express port service - * @dev: PCI Express port service device to handle - * - * If PCI Express port service driver is registered with - * pcie_port_service_register(), this function will be called by the driver core - * when device_shutdown() is called for the port service device associated - * with the driver. - */ -static void pcie_port_shutdown_service(struct device *dev) {} +const struct bus_type pcie_port_bus_type = { + .name = "pci_express", + .match = pcie_port_bus_match, + .probe = pcie_port_bus_probe, + .remove = pcie_port_bus_remove, +}; /** * pcie_port_service_register - register PCI Express port service driver @@ -586,9 +590,6 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) new->driver.name = new->name; new->driver.bus = &pcie_port_bus_type; - new->driver.probe = pcie_port_probe_service; - new->driver.remove = pcie_port_remove_service; - new->driver.shutdown = pcie_port_shutdown_service; return driver_register(&new->driver); } diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c index ed0f9691e7d1..c7c61869bc9c 100644 --- a/drivers/pci/pcie/ptm.c +++ b/drivers/pci/pcie/ptm.c @@ -542,8 +542,10 @@ struct pci_ptm_debugfs *pcie_ptm_create_debugfs(struct device *dev, void *pdata, return NULL; dirname = devm_kasprintf(dev, GFP_KERNEL, "pcie_ptm_%s", dev_name(dev)); - if (!dirname) + if (!dirname) { + kfree(ptm_debugfs); return NULL; + } ptm_debugfs->debugfs = debugfs_create_dir(dirname, NULL); ptm_debugfs->pdata = pdata; @@ -574,6 +576,7 @@ void pcie_ptm_destroy_debugfs(struct pci_ptm_debugfs *ptm_debugfs) mutex_destroy(&ptm_debugfs->lock); debugfs_remove_recursive(ptm_debugfs->debugfs); + kfree(ptm_debugfs); } EXPORT_SYMBOL_GPL(pcie_ptm_destroy_debugfs); #endif diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a2bff57176a3..7711f579fa1d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/pci_hotplug.h> #include <linux/slab.h> +#include <linux/sprintf.h> #include <linux/module.h> #include <linux/cpumask.h> #include <linux/aer.h> @@ -22,11 +23,9 @@ #include <linux/irqdomain.h> #include <linux/pm_runtime.h> #include <linux/bitfield.h> +#include <trace/events/pci.h> #include "pci.h" -#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ -#define CARDBUS_RESERVE_BUSNR 3 - static struct resource busn_resource = { .name = "PCI busn", .start = 0, @@ -287,8 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8) && sz64 > 0x100000000ULL) { res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; - res->start = 0; - res->end = 0; + resource_set_range(res, 0, 0); pci_err(dev, "%s: can't handle BAR larger than 4GB (size %#010llx)\n", res_name, (unsigned long long)sz64); goto out; @@ -297,8 +295,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if ((sizeof(pci_bus_addr_t) < 8) && l) { /* Above 32-bit boundary; try to reallocate */ res->flags |= IORESOURCE_UNSET; - res->start = 0; - res->end = sz64 - 1; + resource_set_range(res, 0, sz64); pci_info(dev, "%s: can't handle BAR above 4GB (bus address %#010llx)\n", res_name, (unsigned long long)l64); goto out; @@ -525,8 +522,8 @@ static void pci_read_bridge_windows(struct pci_dev *bridge) pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &buses); res.flags = IORESOURCE_BUS; - res.start = (buses >> 8) & 0xff; - res.end = (buses >> 16) & 0xff; + res.start = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses); + res.end = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses); pci_info(bridge, "PCI bridge to %pR%s\n", &res, bridge->transparent ? " (subtractive decode)" : ""); @@ -824,14 +821,16 @@ const char *pci_speed_string(enum pci_bus_speed speed) } EXPORT_SYMBOL_GPL(pci_speed_string); -void pcie_update_link_speed(struct pci_bus *bus) +void pcie_update_link_speed(struct pci_bus *bus, + enum pcie_link_change_reason reason) { struct pci_dev *bridge = bus->self; u16 linksta, linksta2; pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); pcie_capability_read_word(bridge, PCI_EXP_LNKSTA2, &linksta2); - __pcie_update_link_speed(bus, linksta, linksta2); + + __pcie_update_link_speed(bus, reason, linksta, linksta2); } EXPORT_SYMBOL_GPL(pcie_update_link_speed); @@ -918,7 +917,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; - pcie_update_link_speed(bus); + pcie_update_link_speed(bus, PCIE_ADD_BUS); } } @@ -1313,6 +1312,26 @@ static void pci_enable_rrs_sv(struct pci_dev *pdev) static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, unsigned int available_buses); + +void pbus_validate_busn(struct pci_bus *bus) +{ + struct pci_bus *upstream = bus->parent; + struct pci_dev *bridge = bus->self; + + /* Check that all devices are accessible */ + while (upstream->parent) { + if ((bus->busn_res.end > upstream->busn_res.end) || + (bus->number > upstream->busn_res.end) || + (bus->number < upstream->number) || + (bus->busn_res.end < upstream->number)) { + pci_info(bridge, "devices behind bridge are unusable because %pR cannot be assigned for them\n", + &bus->busn_res); + break; + } + upstream = upstream->parent; + } +} + /** * pci_ea_fixed_busnrs() - Read fixed Secondary and Subordinate bus * numbers from EA capability. @@ -1324,7 +1343,7 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, * and subordinate bus numbers, return true with the bus numbers in @sec * and @sub. Otherwise return false. */ -static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) +bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub) { int ea, offset; u32 dw; @@ -1378,8 +1397,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, int pass) { struct pci_bus *child; - int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); - u32 buses, i, j = 0; + u32 buses; u16 bctl; u8 primary, secondary, subordinate; int broken = 0; @@ -1394,9 +1412,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pm_runtime_get_sync(&dev->dev); pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); - primary = buses & 0xFF; - secondary = (buses >> 8) & 0xFF; - subordinate = (buses >> 16) & 0xFF; + primary = FIELD_GET(PCI_PRIMARY_BUS_MASK, buses); + secondary = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses); + subordinate = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses); pci_dbg(dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n", secondary, subordinate, pass); @@ -1423,8 +1441,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT); - if ((secondary || subordinate) && !pcibios_assign_all_busses() && - !is_cardbus && !broken) { + if (pci_is_cardbus_bridge(dev)) { + max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max, + available_buses, + pass); + goto out; + } + + if ((secondary || subordinate) && + !pcibios_assign_all_busses() && !broken) { unsigned int cmax, buses; /* @@ -1466,7 +1491,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken || is_cardbus) + if (pcibios_assign_all_busses() || broken) /* * Temporarily disable forwarding of the @@ -1477,7 +1502,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, * ranges. */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, - buses & ~0xffffff); + buses & PCI_SEC_LATENCY_TIMER_MASK); goto out; } @@ -1508,59 +1533,16 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, if (available_buses) available_buses--; - buses = (buses & 0xff000000) - | ((unsigned int)(child->primary) << 0) - | ((unsigned int)(child->busn_res.start) << 8) - | ((unsigned int)(child->busn_res.end) << 16); - - /* - * yenta.c forces a secondary latency timer of 176. - * Copy that behaviour here. - */ - if (is_cardbus) { - buses &= ~0xff000000; - buses |= CARDBUS_LATENCY_TIMER << 24; - } + buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) | + FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) | + FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) | + FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end); /* We need to blast all three values with a single write */ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); - if (!is_cardbus) { - child->bridge_ctl = bctl; - max = pci_scan_child_bus_extend(child, available_buses); - } else { - - /* - * For CardBus bridges, we leave 4 bus numbers as - * cards with a PCI-to-PCI bridge can be inserted - * later. - */ - for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) { - struct pci_bus *parent = bus; - if (pci_find_bus(pci_domain_nr(bus), - max+i+1)) - break; - while (parent->parent) { - if ((!pcibios_assign_all_busses()) && - (parent->busn_res.end > max) && - (parent->busn_res.end <= max+i)) { - j = 1; - } - parent = parent->parent; - } - if (j) { - - /* - * Often, there are two CardBus - * bridges -- try to leave one - * valid bus number for each one. - */ - i /= 2; - break; - } - } - max += i; - } + child->bridge_ctl = bctl; + max = pci_scan_child_bus_extend(child, available_buses); /* * Set subordinate bus number to its real value. @@ -1572,23 +1554,10 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } + scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x", + pci_domain_nr(bus), child->number); - sprintf(child->name, - (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"), - pci_domain_nr(bus), child->number); - - /* Check that all devices are accessible */ - while (bus->parent) { - if ((child->busn_res.end > bus->busn_res.end) || - (child->number > bus->busn_res.end) || - (child->number < bus->number) || - (child->busn_res.end < bus->number)) { - dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n", - &child->busn_res); - break; - } - bus = bus->parent; - } + pbus_validate_busn(child); out: /* Clear errors in the Secondary Status Register */ @@ -2277,7 +2246,8 @@ int pci_configure_extended_tags(struct pci_dev *dev, void *ign) u16 ctl; int ret; - if (!pci_is_pcie(dev)) + /* PCI_EXP_DEVCTL_EXT_TAG is RsvdP in VFs */ + if (!pci_is_pcie(dev) || dev->is_virtfn) return 0; ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap); @@ -2417,6 +2387,37 @@ static void pci_configure_serr(struct pci_dev *dev) } } +static void pci_configure_rcb(struct pci_dev *dev) +{ + struct pci_dev *rp; + u16 rp_lnkctl; + + /* + * Per PCIe r7.0, sec 7.5.3.7, RCB is only meaningful in Root Ports + * (where it is read-only), Endpoints, and Bridges. It may only be + * set for Endpoints and Bridges if it is set in the Root Port. For + * Endpoints, it is 'RsvdP' for Virtual Functions. + */ + if (!pci_is_pcie(dev) || + pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM || + pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM || + pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC || + dev->is_virtfn) + return; + + /* Root Port often not visible to virtualized guests */ + rp = pcie_find_root_port(dev); + if (!rp) + return; + + pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &rp_lnkctl); + pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_RCB, + (rp_lnkctl & PCI_EXP_LNKCTL_RCB) ? + PCI_EXP_LNKCTL_RCB : 0); +} + static void pci_configure_device(struct pci_dev *dev) { pci_configure_mps(dev); @@ -2426,6 +2427,7 @@ static void pci_configure_device(struct pci_dev *dev) pci_configure_aspm_l1ss(dev); pci_configure_eetlp_prefix(dev); pci_configure_serr(dev); + pci_configure_rcb(dev); pci_acpi_program_hp_params(dev); } @@ -2554,72 +2556,10 @@ bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, int timeout) { -#ifdef CONFIG_PCI_QUIRKS - struct pci_dev *bridge = bus->self; - - /* - * Certain IDT switches have an issue where they improperly trigger - * ACS Source Validation errors on completions for config reads. - */ - if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT && - bridge->device == 0x80b5) - return pci_idt_bus_quirk(bus, devfn, l, timeout); -#endif - return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout); } EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); -#if IS_ENABLED(CONFIG_PCI_PWRCTRL) -static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) -{ - struct pci_host_bridge *host = pci_find_host_bridge(bus); - struct platform_device *pdev; - struct device_node *np; - - np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn); - if (!np) - return NULL; - - pdev = of_find_device_by_node(np); - if (pdev) { - put_device(&pdev->dev); - goto err_put_of_node; - } - - /* - * First check whether the pwrctrl device really needs to be created or - * not. This is decided based on at least one of the power supplies - * being defined in the devicetree node of the device. - */ - if (!of_pci_supply_present(np)) { - pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name); - goto err_put_of_node; - } - - /* Now create the pwrctrl device */ - pdev = of_platform_device_create(np, NULL, &host->dev); - if (!pdev) { - pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name); - goto err_put_of_node; - } - - of_node_put(np); - - return pdev; - -err_put_of_node: - of_node_put(np); - - return NULL; -} -#else -static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) -{ - return NULL; -} -#endif - /* * Read the config data for a PCI device, sanity-check it, * and fill in the dev structure. @@ -2629,15 +2569,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) struct pci_dev *dev; u32 l; - /* - * Create pwrctrl device (if required) for the PCI device to handle the - * power state. If the pwrctrl device is created, then skip scanning - * further as the pwrctrl core will rescan the bus after powering on - * the device. - */ - if (pci_pwrctrl_create_device(bus, devfn)) - return NULL; - if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) return NULL; diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig index e0f999f299bb..cd3aa15bad00 100644 --- a/drivers/pci/pwrctrl/Kconfig +++ b/drivers/pci/pwrctrl/Kconfig @@ -13,6 +13,7 @@ config PCI_PWRCTRL_PWRSEQ config PCI_PWRCTRL_SLOT tristate "PCI Power Control driver for PCI slots" + select POWER_SEQUENCING select PCI_PWRCTRL help Say Y here to enable the PCI Power Control driver to control the power diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 6bdbfed584d6..6f7dea6746e0 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -3,14 +3,22 @@ * Copyright (C) 2024 Linaro Ltd. */ +#define dev_fmt(fmt) "pwrctrl: " fmt + #include <linux/device.h> #include <linux/export.h> #include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> #include <linux/pci.h> #include <linux/pci-pwrctrl.h> +#include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> +#include "../pci.h" + static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -38,16 +46,6 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; } -static void rescan_work_func(struct work_struct *work) -{ - struct pci_pwrctrl *pwrctrl = container_of(work, - struct pci_pwrctrl, work); - - pci_lock_rescan_remove(); - pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus); - pci_unlock_rescan_remove(); -} - /** * pci_pwrctrl_init() - Initialize the PCI power control context struct * @@ -57,7 +55,7 @@ static void rescan_work_func(struct work_struct *work) void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev) { pwrctrl->dev = dev; - INIT_WORK(&pwrctrl->work, rescan_work_func); + dev_set_drvdata(dev, pwrctrl); } EXPORT_SYMBOL_GPL(pci_pwrctrl_init); @@ -87,8 +85,6 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl) if (ret) return ret; - schedule_work(&pwrctrl->work); - return 0; } EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready); @@ -101,8 +97,6 @@ EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready); */ void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl) { - cancel_work_sync(&pwrctrl->work); - /* * We don't have to delete the link here. Typically, this function * is only called when the power control device is being detached. If @@ -145,6 +139,242 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +static int __pci_pwrctrl_power_off_device(struct device *dev) +{ + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); + + if (!pwrctrl) + return 0; + + return pwrctrl->power_off(pwrctrl); +} + +static void pci_pwrctrl_power_off_device(struct device_node *np) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_power_off_device(child); + + 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); + } + + platform_device_put(pdev); +} + +/** + * pci_pwrctrl_power_off_devices - Power off pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively traverse all pwrctrl devices for the devicetree hierarchy + * below the specified PCI host controller and power them off in a depth + * first manner. + */ +void pci_pwrctrl_power_off_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_power_off_device(child); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices); + +static int __pci_pwrctrl_power_on_device(struct device *dev) +{ + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); + + if (!pwrctrl) + return 0; + + return pwrctrl->power_on(pwrctrl); +} + +/* + * Power on the devices in a depth first manner. Before powering on the device, + * make sure its driver is bound. + */ +static int pci_pwrctrl_power_on_device(struct device_node *np) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) { + ret = pci_pwrctrl_power_on_device(child); + if (ret) + return ret; + } + + 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; + } + + platform_device_put(pdev); + + return ret; +} + +/** + * pci_pwrctrl_power_on_devices - Power on pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively traverse all pwrctrl devices for the devicetree hierarchy + * below the specified PCI host controller and power them on in a depth + * first manner. On error, all powered on devices will be powered off. + * + * Return: 0 on success, -EPROBE_DEFER if any pwrctrl driver is not bound, an + * appropriate error code otherwise. + */ +int pci_pwrctrl_power_on_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + struct device_node *child = NULL; + int ret; + + for_each_available_child_of_node(np, child) { + ret = pci_pwrctrl_power_on_device(child); + if (ret) + goto err_power_off; + } + + return 0; + +err_power_off: + for_each_available_child_of_node_scoped(np, tmp) { + if (tmp == child) + break; + pci_pwrctrl_power_off_device(tmp); + } + of_node_put(child); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices); + +static int pci_pwrctrl_create_device(struct device_node *np, + struct device *parent) +{ + struct platform_device *pdev; + int ret; + + for_each_available_child_of_node_scoped(np, child) { + ret = pci_pwrctrl_create_device(child, parent); + if (ret) + return ret; + } + + /* Bail out if the platform device is already available for the node */ + pdev = of_find_device_by_node(np); + if (pdev) { + platform_device_put(pdev); + return 0; + } + + /* + * Sanity check to make sure that the node has the compatible property + * to allow driver binding. + */ + if (!of_property_present(np, "compatible")) + return 0; + + /* + * Check whether the pwrctrl device really needs to be created or not. + * This is decided based on at least one of the power supplies defined + * in the devicetree node of the device or the graph property. + */ + if (!of_pci_supply_present(np) && !of_graph_is_present(np)) { + dev_dbg(parent, "Skipping OF node: %s\n", np->name); + return 0; + } + + /* Now create the pwrctrl device */ + pdev = of_platform_device_create(np, NULL, parent); + if (!pdev) { + dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name); + return -EINVAL; + } + + return 0; +} + +/** + * pci_pwrctrl_create_devices - Create pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively create pwrctrl devices for the devicetree hierarchy below + * the specified PCI host controller in a depth first manner. On error, all + * created devices will be destroyed. + * + * Return: 0 on success, negative error number on error. + */ +int pci_pwrctrl_create_devices(struct device *parent) +{ + int ret; + + for_each_available_child_of_node_scoped(parent->of_node, child) { + ret = pci_pwrctrl_create_device(child, parent); + if (ret) { + pci_pwrctrl_destroy_devices(parent); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices); + +static void pci_pwrctrl_destroy_device(struct device_node *np) +{ + struct platform_device *pdev; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_destroy_device(child); + + pdev = of_find_device_by_node(np); + if (!pdev) + return; + + of_device_unregister(pdev); + platform_device_put(pdev); + + of_node_clear_flag(np, OF_POPULATED); +} + +/** + * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices + * + * @parent: PCI host controller device + * + * Recursively destroy pwrctrl devices for the devicetree hierarchy below + * the specified PCI host controller in a depth first manner. + */ +void pci_pwrctrl_destroy_devices(struct device *parent) +{ + struct device_node *np = parent->of_node; + + for_each_available_child_of_node_scoped(np, child) + pci_pwrctrl_destroy_device(child); +} +EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices); + MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>"); MODULE_DESCRIPTION("PCI Device Power Control core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c index 4e664e7b8dd2..0d0377283c37 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c @@ -13,12 +13,12 @@ #include <linux/slab.h> #include <linux/types.h> -struct pci_pwrctrl_pwrseq_data { - struct pci_pwrctrl ctx; +struct pwrseq_pwrctrl { + struct pci_pwrctrl pwrctrl; struct pwrseq_desc *pwrseq; }; -struct pci_pwrctrl_pwrseq_pdata { +struct pwrseq_pwrctrl_pdata { const char *target; /* * Called before doing anything else to perform device-specific @@ -27,7 +27,7 @@ struct pci_pwrctrl_pwrseq_pdata { int (*validate_device)(struct device *dev); }; -static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev) +static int pwrseq_pwrctrl_qcm_wcn_validate_device(struct device *dev) { /* * Old device trees for some platforms already define wifi nodes for @@ -47,22 +47,38 @@ static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev) return 0; } -static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata = { +static const struct pwrseq_pwrctrl_pdata pwrseq_pwrctrl_qcom_wcn_pdata = { .target = "wlan", - .validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device, + .validate_device = pwrseq_pwrctrl_qcm_wcn_validate_device, }; -static void devm_pci_pwrctrl_pwrseq_power_off(void *data) +static int pwrseq_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) { - struct pwrseq_desc *pwrseq = data; + struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl, + struct pwrseq_pwrctrl, pwrctrl); - pwrseq_power_off(pwrseq); + return pwrseq_power_on(pwrseq->pwrseq); } -static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) +static int pwrseq_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl) { - const struct pci_pwrctrl_pwrseq_pdata *pdata; - struct pci_pwrctrl_pwrseq_data *data; + struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl, + struct pwrseq_pwrctrl, pwrctrl); + + return pwrseq_power_off(pwrseq->pwrseq); +} + +static void devm_pwrseq_pwrctrl_power_off(void *data) +{ + struct pwrseq_pwrctrl *pwrseq = data; + + pwrseq_pwrctrl_power_off(&pwrseq->pwrctrl); +} + +static int pwrseq_pwrctrl_probe(struct platform_device *pdev) +{ + const struct pwrseq_pwrctrl_pdata *pdata; + struct pwrseq_pwrctrl *pwrseq; struct device *dev = &pdev->dev; int ret; @@ -76,28 +92,26 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) return ret; } - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) + pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL); + if (!pwrseq) return -ENOMEM; - data->pwrseq = devm_pwrseq_get(dev, pdata->target); - if (IS_ERR(data->pwrseq)) - return dev_err_probe(dev, PTR_ERR(data->pwrseq), + pwrseq->pwrseq = devm_pwrseq_get(dev, pdata->target); + if (IS_ERR(pwrseq->pwrseq)) + return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq), "Failed to get the power sequencer\n"); - ret = pwrseq_power_on(data->pwrseq); - if (ret) - return dev_err_probe(dev, ret, - "Failed to power-on the device\n"); - - ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off, - data->pwrseq); + ret = devm_add_action_or_reset(dev, devm_pwrseq_pwrctrl_power_off, + pwrseq); if (ret) return ret; - pci_pwrctrl_init(&data->ctx, dev); + pwrseq->pwrctrl.power_on = pwrseq_pwrctrl_power_on; + pwrseq->pwrctrl.power_off = pwrseq_pwrctrl_power_off; + + pci_pwrctrl_init(&pwrseq->pwrctrl, dev); - ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx); + ret = devm_pci_pwrctrl_device_set_ready(dev, &pwrseq->pwrctrl); if (ret) return dev_err_probe(dev, ret, "Failed to register the pwrctrl wrapper\n"); @@ -105,34 +119,34 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id pci_pwrctrl_pwrseq_of_match[] = { +static const struct of_device_id pwrseq_pwrctrl_of_match[] = { { /* ATH11K in QCA6390 package. */ .compatible = "pci17cb,1101", - .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, + .data = &pwrseq_pwrctrl_qcom_wcn_pdata, }, { /* ATH11K in WCN6855 package. */ .compatible = "pci17cb,1103", - .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, + .data = &pwrseq_pwrctrl_qcom_wcn_pdata, }, { /* ATH12K in WCN7850 package. */ .compatible = "pci17cb,1107", - .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata, + .data = &pwrseq_pwrctrl_qcom_wcn_pdata, }, { } }; -MODULE_DEVICE_TABLE(of, pci_pwrctrl_pwrseq_of_match); +MODULE_DEVICE_TABLE(of, pwrseq_pwrctrl_of_match); -static struct platform_driver pci_pwrctrl_pwrseq_driver = { +static struct platform_driver pwrseq_pwrctrl_driver = { .driver = { .name = "pci-pwrctrl-pwrseq", - .of_match_table = pci_pwrctrl_pwrseq_of_match, + .of_match_table = pwrseq_pwrctrl_of_match, }, - .probe = pci_pwrctrl_pwrseq_probe, + .probe = pwrseq_pwrctrl_probe, }; -module_platform_driver(pci_pwrctrl_pwrseq_driver); +module_platform_driver(pwrseq_pwrctrl_driver); MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>"); MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices"); diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c index ec423432ac65..488e1ec34a7f 100644 --- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c +++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c @@ -59,7 +59,7 @@ #define TC9563_POWER_CONTROL_OVREN 0x82b2c8 #define TC9563_GPIO_MASK 0xfffffff3 -#define TC9563_GPIO_DEASSERT_BITS 0xc /* Bits to clear for GPIO deassert */ +#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */ #define TC9563_TX_MARGIN_MIN_UA 400000 @@ -69,7 +69,7 @@ */ #define TC9563_OSC_STAB_DELAY_US (10 * USEC_PER_MSEC) -#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 nanoseconds */ +#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */ struct tc9563_pwrctrl_reg_setting { unsigned int offset; @@ -105,13 +105,13 @@ static const char *const tc9563_supply_names[TC9563_PWRCTL_MAX_SUPPLY] = { "vddio18", }; -struct tc9563_pwrctrl_ctx { +struct tc9563_pwrctrl { + struct pci_pwrctrl pwrctrl; struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY]; struct tc9563_pwrctrl_cfg cfg[TC9563_MAX]; struct gpio_desc *reset_gpio; struct i2c_adapter *adapter; struct i2c_client *client; - struct pci_pwrctrl pwrctrl; }; /* @@ -217,7 +217,8 @@ static int tc9563_pwrctrl_i2c_read(struct i2c_client *client, } static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client, - const struct tc9563_pwrctrl_reg_setting *seq, int len) + const struct tc9563_pwrctrl_reg_setting *seq, + int len) { int ret, i; @@ -230,10 +231,10 @@ static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client, return 0; } -static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port]; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; const struct tc9563_pwrctrl_reg_setting *seq; int ret, len; @@ -248,16 +249,17 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx, len = ARRAY_SIZE(dsp2_pwroff_seq); } - ret = tc9563_pwrctrl_i2c_bulk_write(ctx->client, seq, len); + ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len); if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, - common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq)); + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq, + ARRAY_SIZE(common_pwroff_seq)); } -static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, - enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns) +static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563, + enum tc9563_pwrctrl_ports port, + bool is_l1, u32 ns) { u32 rd_val, units; int ret; @@ -269,30 +271,38 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx, units = ns / TC9563_L0S_L1_DELAY_UNIT_NS; if (port == TC9563_ETHERNET) { - ret = tc9563_pwrctrl_i2c_read(ctx->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val); + ret = tc9563_pwrctrl_i2c_read(tc9563->client, + TC9563_EMBEDDED_ETH_DELAY, + &rd_val); if (ret) return ret; if (is_l1) - rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L1_DELAY_MASK); + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L1_DELAY_MASK); else - rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK); + rd_val = u32_replace_bits(rd_val, units, + TC9563_ETH_L0S_DELAY_MASK); - return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_EMBEDDED_ETH_DELAY, rd_val); + return tc9563_pwrctrl_i2c_write(tc9563->client, + TC9563_EMBEDDED_ETH_DELAY, + rd_val); } - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port)); + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, + BIT(port)); if (ret) return ret; - return tc9563_pwrctrl_i2c_write(ctx->client, - is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units); + return tc9563_pwrctrl_i2c_write(tc9563->client, + is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, + units); } -static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { - u32 amp = ctx->cfg[port].tx_amp; + u32 amp = tc9563->cfg[port].tx_amp; int port_access; if (amp < TC9563_TX_MARGIN_MIN_UA) @@ -321,13 +331,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx, {TC9563_TX_MARGIN, amp}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq)); + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq, + ARRAY_SIZE(tx_amp_seq)); } -static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port]; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; int port_access, lane_access = 0x3; u32 phy_rate = 0x21; @@ -364,14 +375,14 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx, {TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0}, }; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, - disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq)); + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq, + ARRAY_SIZE(disable_dfe_seq)); } -static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, +static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563, enum tc9563_pwrctrl_ports port) { - u8 *nfts = ctx->cfg[port].nfts; + u8 *nfts = tc9563->cfg[port].nfts; struct tc9563_pwrctrl_reg_setting nfts_seq[] = { {TC9563_NFTS_2_5_GT, nfts[0]}, {TC9563_NFTS_5_GT, nfts[1]}, @@ -381,30 +392,35 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx, if (!nfts[0]) return 0; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port)); + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT, + BIT(port)); if (ret) return ret; - return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, ARRAY_SIZE(nfts_seq)); + return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq, + ARRAY_SIZE(nfts_seq)); } -static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, bool deassert) +static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563, + bool deassert) { int ret, val; - ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK); + ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG, + TC9563_GPIO_MASK); if (ret) return ret; val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0; - return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val); + return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val); } -static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct device_node *node, +static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl *tc9563, + struct device_node *node, enum tc9563_pwrctrl_ports port) { - struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port]; + struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port]; int ret; /* Disable port if the status of the port is disabled. */ @@ -434,128 +450,137 @@ static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct return 0; } -static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx) +static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl) { - gpiod_set_value(ctx->reset_gpio, 1); + struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl, + struct tc9563_pwrctrl, pwrctrl); + + gpiod_set_value(tc9563->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies); - regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + return 0; } -static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx) +static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) { + struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl, + struct tc9563_pwrctrl, pwrctrl); + struct device *dev = tc9563->pwrctrl.dev; struct tc9563_pwrctrl_cfg *cfg; int ret, i; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); + ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies), + tc9563->supplies); if (ret < 0) - return dev_err_probe(ctx->pwrctrl.dev, ret, "cannot enable regulators\n"); + return dev_err_probe(dev, ret, "cannot enable regulators\n"); - gpiod_set_value(ctx->reset_gpio, 0); + gpiod_set_value(tc9563->reset_gpio, 0); fsleep(TC9563_OSC_STAB_DELAY_US); - ret = tc9563_pwrctrl_assert_deassert_reset(ctx, false); + ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, false); if (ret) goto power_off; for (i = 0; i < TC9563_MAX; i++) { - cfg = &ctx->cfg[i]; - ret = tc9563_pwrctrl_disable_port(ctx, i); + cfg = &tc9563->cfg[i]; + ret = tc9563_pwrctrl_disable_port(tc9563, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Disabling port failed\n"); + dev_err(dev, "Disabling port failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay); + ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting L0s entry delay failed\n"); + dev_err(dev, "Setting L0s entry delay failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay); + ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting L1 entry delay failed\n"); + dev_err(dev, "Setting L1 entry delay failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i); + ret = tc9563_pwrctrl_set_tx_amplitude(tc9563, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting Tx amplitude failed\n"); + dev_err(dev, "Setting Tx amplitude failed\n"); goto power_off; } - ret = tc9563_pwrctrl_set_nfts(ctx, i); + ret = tc9563_pwrctrl_set_nfts(tc9563, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Setting N_FTS failed\n"); + dev_err(dev, "Setting N_FTS failed\n"); goto power_off; } - ret = tc9563_pwrctrl_disable_dfe(ctx, i); + ret = tc9563_pwrctrl_disable_dfe(tc9563, i); if (ret) { - dev_err(ctx->pwrctrl.dev, "Disabling DFE failed\n"); + dev_err(dev, "Disabling DFE failed\n"); goto power_off; } } - ret = tc9563_pwrctrl_assert_deassert_reset(ctx, true); + ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true); if (!ret) return 0; power_off: - tc9563_pwrctrl_power_off(ctx); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); return ret; } static int tc9563_pwrctrl_probe(struct platform_device *pdev) { - struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent); - struct pci_bus *bus = bridge->bus; + struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; enum tc9563_pwrctrl_ports port; - struct tc9563_pwrctrl_ctx *ctx; + struct tc9563_pwrctrl *tc9563; struct device_node *i2c_node; int ret, addr; - ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) + tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL); + if (!tc9563) return -ENOMEM; - ret = of_property_read_u32_index(pdev->dev.of_node, "i2c-parent", 1, &addr); + ret = of_property_read_u32_index(node, "i2c-parent", 1, &addr); if (ret) return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n"); i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0); - ctx->adapter = of_find_i2c_adapter_by_node(i2c_node); + tc9563->adapter = of_find_i2c_adapter_by_node(i2c_node); of_node_put(i2c_node); - if (!ctx->adapter) + if (!tc9563->adapter) return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n"); - ctx->client = i2c_new_dummy_device(ctx->adapter, addr); - if (IS_ERR(ctx->client)) { + tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr); + if (IS_ERR(tc9563->client)) { dev_err(dev, "Failed to create I2C client\n"); - i2c_put_adapter(ctx->adapter); - return PTR_ERR(ctx->client); + put_device(&tc9563->adapter->dev); + return PTR_ERR(tc9563->client); } for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++) - ctx->supplies[i].supply = tc9563_supply_names[i]; + tc9563->supplies[i].supply = tc9563_supply_names[i]; - ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, ctx->supplies); + ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, + tc9563->supplies); if (ret) { dev_err_probe(dev, ret, "failed to get supply regulator\n"); goto remove_i2c; } - ctx->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH); - if (IS_ERR(ctx->reset_gpio)) { - ret = dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "failed to get resx GPIO\n"); + tc9563->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH); + if (IS_ERR(tc9563->reset_gpio)) { + ret = dev_err_probe(dev, PTR_ERR(tc9563->reset_gpio), "failed to get resx GPIO\n"); goto remove_i2c; } - pci_pwrctrl_init(&ctx->pwrctrl, dev); + pci_pwrctrl_init(&tc9563->pwrctrl, dev); port = TC9563_USP; - ret = tc9563_pwrctrl_parse_device_dt(ctx, pdev->dev.of_node, port); + ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port); if (ret) { dev_err(dev, "failed to parse device tree properties: %d\n", ret); goto remove_i2c; @@ -563,18 +588,20 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) /* * Downstream ports are always children of the upstream port. - * The first node represents DSP1, the second node represents DSP2, and so on. + * The first node represents DSP1, the second node represents DSP2, + * and so on. */ - for_each_child_of_node_scoped(pdev->dev.of_node, child) { + for_each_child_of_node_scoped(node, child) { port++; - ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port); + ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port); if (ret) break; /* Embedded ethernet device are under DSP3 */ if (port == TC9563_DSP3) { for_each_child_of_node_scoped(child, child1) { port++; - ret = tc9563_pwrctrl_parse_device_dt(ctx, child1, port); + ret = tc9563_pwrctrl_parse_device_dt(tc9563, + child1, port); if (ret) break; } @@ -585,45 +612,32 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev) goto remove_i2c; } - if (bridge->ops->assert_perst) { - ret = bridge->ops->assert_perst(bus, true); - if (ret) - goto remove_i2c; - } + tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on; + tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off; - ret = tc9563_pwrctrl_bring_up(ctx); - if (ret) - goto remove_i2c; - - if (bridge->ops->assert_perst) { - ret = bridge->ops->assert_perst(bus, false); - if (ret) - goto power_off; - } - - ret = devm_pci_pwrctrl_device_set_ready(dev, &ctx->pwrctrl); + ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl); if (ret) goto power_off; - platform_set_drvdata(pdev, ctx); - return 0; power_off: - tc9563_pwrctrl_power_off(ctx); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); remove_i2c: - i2c_unregister_device(ctx->client); - i2c_put_adapter(ctx->adapter); + i2c_unregister_device(tc9563->client); + put_device(&tc9563->adapter->dev); return ret; } static void tc9563_pwrctrl_remove(struct platform_device *pdev) { - struct tc9563_pwrctrl_ctx *ctx = platform_get_drvdata(pdev); + struct pci_pwrctrl *pwrctrl = dev_get_drvdata(&pdev->dev); + struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl, + struct tc9563_pwrctrl, pwrctrl); - tc9563_pwrctrl_power_off(ctx); - i2c_unregister_device(ctx->client); - i2c_put_adapter(ctx->adapter); + tc9563_pwrctrl_power_off(&tc9563->pwrctrl); + i2c_unregister_device(tc9563->client); + put_device(&tc9563->adapter->dev); } static const struct of_device_id tc9563_pwrctrl_of_match[] = { diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 3320494b62d8..082af81efe25 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -8,36 +8,84 @@ #include <linux/device.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/pci-pwrctrl.h> #include <linux/platform_device.h> +#include <linux/pwrseq/consumer.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> -struct pci_pwrctrl_slot_data { - struct pci_pwrctrl ctx; +struct slot_pwrctrl { + struct pci_pwrctrl pwrctrl; struct regulator_bulk_data *supplies; int num_supplies; + struct clk *clk; + struct pwrseq_desc *pwrseq; }; -static void devm_pci_pwrctrl_slot_power_off(void *data) +static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) { - struct pci_pwrctrl_slot_data *slot = data; + struct slot_pwrctrl *slot = container_of(pwrctrl, + struct slot_pwrctrl, pwrctrl); + int ret; + + if (slot->pwrseq) { + pwrseq_power_on(slot->pwrseq); + return 0; + } + + ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); + if (ret < 0) { + dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n"); + return ret; + } + + return clk_prepare_enable(slot->clk); +} + +static int slot_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl) +{ + struct slot_pwrctrl *slot = container_of(pwrctrl, + struct slot_pwrctrl, pwrctrl); + + if (slot->pwrseq) { + pwrseq_power_off(slot->pwrseq); + return 0; + } regulator_bulk_disable(slot->num_supplies, slot->supplies); + clk_disable_unprepare(slot->clk); + + return 0; +} + +static void devm_slot_pwrctrl_release(void *data) +{ + struct slot_pwrctrl *slot = data; + + slot_pwrctrl_power_off(&slot->pwrctrl); regulator_bulk_free(slot->num_supplies, slot->supplies); } -static int pci_pwrctrl_slot_probe(struct platform_device *pdev) +static int slot_pwrctrl_probe(struct platform_device *pdev) { - struct pci_pwrctrl_slot_data *slot; + struct slot_pwrctrl *slot; struct device *dev = &pdev->dev; - struct clk *clk; int ret; slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL); if (!slot) return -ENOMEM; + if (of_graph_is_present(dev_of_node(dev))) { + slot->pwrseq = devm_pwrseq_get(dev, "pcie"); + if (IS_ERR(slot->pwrseq)) + return dev_err_probe(dev, PTR_ERR(slot->pwrseq), + "Failed to get the power sequencer\n"); + + goto skip_resources; + } + ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &slot->supplies); if (ret < 0) { @@ -46,49 +94,46 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) } slot->num_supplies = ret; - ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to enable slot regulators\n"); - regulator_bulk_free(slot->num_supplies, slot->supplies); - return ret; + + slot->clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(slot->clk)) { + return dev_err_probe(dev, PTR_ERR(slot->clk), + "Failed to enable slot clock\n"); } - ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off, - slot); +skip_resources: + slot->pwrctrl.power_on = slot_pwrctrl_power_on; + slot->pwrctrl.power_off = slot_pwrctrl_power_off; + + ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot); if (ret) return ret; - clk = devm_clk_get_optional_enabled(dev, NULL); - if (IS_ERR(clk)) { - return dev_err_probe(dev, PTR_ERR(clk), - "Failed to enable slot clock\n"); - } - - pci_pwrctrl_init(&slot->ctx, dev); + pci_pwrctrl_init(&slot->pwrctrl, dev); - ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx); + ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl); if (ret) return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); return 0; } -static const struct of_device_id pci_pwrctrl_slot_of_match[] = { +static const struct of_device_id slot_pwrctrl_of_match[] = { { .compatible = "pciclass,0604", }, { } }; -MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match); +MODULE_DEVICE_TABLE(of, slot_pwrctrl_of_match); -static struct platform_driver pci_pwrctrl_slot_driver = { +static struct platform_driver slot_pwrctrl_driver = { .driver = { .name = "pci-pwrctrl-slot", - .of_match_table = pci_pwrctrl_slot_of_match, + .of_match_table = slot_pwrctrl_of_match, }, - .probe = pci_pwrctrl_slot_probe, + .probe = slot_pwrctrl_probe, }; -module_platform_driver(pci_pwrctrl_slot_driver); +module_platform_driver(slot_pwrctrl_driver); MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots"); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6df24dd32754..48946cca4be7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1361,6 +1361,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82380FB, quirk DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x605, quirk_transparent_bridge); /* + * Enabling Link Bandwidth Management Interrupts (BW notifications) can cause + * boot hangs on P45. + */ +static void quirk_p45_bw_notifications(struct pci_dev *dev) +{ + dev->no_bw_notif = 1; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e21, quirk_p45_bw_notifications); + +/* * Common misconfiguration of the MediaGX/Geode PCI master that will reduce * PCI bandwidth from 70MB/s to 25MB/s. See the GXM/GXLV/GX1 datasheets * found at http://www.national.com/analog for info on what these bits do. @@ -3750,6 +3760,14 @@ static void quirk_no_bus_reset(struct pci_dev *dev) } /* + * After asserting Secondary Bus Reset to downstream devices via a GB10 + * Root Port, the link may not retrain correctly. + * https://lore.kernel.org/r/20251113084441.2124737-1-Johnny-CC.Chang@mediatek.com + */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, 0x22CE, quirk_no_bus_reset); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, 0x22D0, quirk_no_bus_reset); + +/* * Some NVIDIA GPU devices do not work with bus reset, SBR needs to be * prevented for those affected devices. */ @@ -3792,6 +3810,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CAVIUM, 0xa100, quirk_no_bus_reset); */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, 0xb005, quirk_no_bus_reset); +/* + * Reports from users making use of PCI device assignment with ASM1164 + * controllers indicate an issue with bus reset where the device fails to + * retrain. The issue appears more common in configurations with multiple + * controllers. The device does indicate PM reset support (NoSoftRst-), + * therefore this still leaves a viable reset method. + * https://forum.proxmox.com/threads/problems-with-pcie-passthrough-with-two-identical-devices.149003/ + */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1164, quirk_no_bus_reset); + static void quirk_no_pm_reset(struct pci_dev *dev) { /* @@ -4471,6 +4499,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9084, quirk_bridge_cavm_thrx2_pcie_root); /* + * AST1150 doesn't use a real PCI bus and always forwards the requester ID + * from downstream devices. + */ +static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev) +{ + pdev->dev_flags |= PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASPEED, 0x1150, quirk_aspeed_pci_bridge_no_alias); + +/* * Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero) * class code. Fix it. */ @@ -5124,6 +5162,10 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs }, /* QCOM SA8775P root port */ { PCI_VENDOR_ID_QCOM, 0x0115, pci_quirk_qcom_rp_acs }, + /* QCOM Hamoa root port */ + { PCI_VENDOR_ID_QCOM, 0x0111, pci_quirk_qcom_rp_acs }, + /* QCOM Glymur root port */ + { PCI_VENDOR_ID_QCOM, 0x0120, pci_quirk_qcom_rp_acs }, /* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */ { PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs }, /* Intel PCH root ports */ @@ -5598,6 +5640,7 @@ static void quirk_no_ext_tags(struct pci_dev *pdev) pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL); } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1004, quirk_no_ext_tags); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1005, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0132, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0141, quirk_no_ext_tags); @@ -5796,7 +5839,7 @@ DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, /* * Some IDT switches incorrectly flag an ACS Source Validation error on - * completions for config read requests even though PCIe r4.0, sec + * completions for config read requests even though PCIe r7.0, sec * 6.12.1.1, says that completions are never affected by ACS Source * Validation. Here's the text of IDT 89H32H8G3-YC, erratum #36: * @@ -5809,44 +5852,20 @@ DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, * * The workaround suggested by IDT is to issue a config write to the * downstream device before issuing the first config read. This allows the - * downstream device to capture its bus and device numbers (see PCIe r4.0, - * sec 2.2.9), thus avoiding the ACS error on the completion. + * downstream device to capture its bus and device numbers (see PCIe r7.0, + * sec 2.2.9.1), thus avoiding the ACS error on the completion. * * However, we don't know when the device is ready to accept the config - * write, so we do config reads until we receive a non-Config Request Retry - * Status, then do the config write. - * - * To avoid hitting the erratum when doing the config reads, we disable ACS - * SV around this process. + * write, and the issue affects resets of the switch as well as enumeration, + * so disable use of ACS SV for these devices altogether. */ -int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout) +void pci_disable_broken_acs_cap(struct pci_dev *pdev) { - int pos; - u16 ctrl = 0; - bool found; - struct pci_dev *bridge = bus->self; - - pos = bridge->acs_cap; - - /* Disable ACS SV before initial config reads */ - if (pos) { - pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl); - if (ctrl & PCI_ACS_SV) - pci_write_config_word(bridge, pos + PCI_ACS_CTRL, - ctrl & ~PCI_ACS_SV); + if (pdev->vendor == PCI_VENDOR_ID_IDT && + (pdev->device == 0x80b5 || pdev->device == 0x8090)) { + pci_info(pdev, "Disabling broken ACS SV; downstream device isolation reduced\n"); + pdev->acs_capabilities &= ~PCI_ACS_SV; } - - found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout); - - /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */ - if (found) - pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0); - - /* Re-enable ACS_SV if it was previously enabled */ - if (ctrl & PCI_ACS_SV) - pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl); - - return found; } /* @@ -6205,6 +6224,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303, pci_fixup_pericom_acs_store_forward); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2303, pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404, + pci_fixup_pericom_acs_store_forward); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404, + pci_fixup_pericom_acs_store_forward); static void nvidia_ion_ahci_fixup(struct pci_dev *pdev) { diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 417a9ea59117..e9d519993853 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -17,25 +17,6 @@ static void pci_free_resources(struct pci_dev *dev) } } -static void pci_pwrctrl_unregister(struct device *dev) -{ - struct device_node *np; - struct platform_device *pdev; - - np = dev_of_node(dev); - if (!np) - return; - - pdev = of_find_device_by_node(np); - if (!pdev) - return; - - of_device_unregister(pdev); - put_device(&pdev->dev); - - of_node_clear_flag(np, OF_POPULATED); -} - static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); @@ -73,7 +54,6 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_ide_destroy(dev); pcie_aspm_exit_link_state(dev); pci_bridge_d3_update(dev); - pci_pwrctrl_unregister(&dev->dev); pci_free_resources(dev); put_device(&dev->dev); } diff --git a/drivers/pci/search.c b/drivers/pci/search.c index e6e84dc62e82..e3d3177fce54 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -86,6 +86,8 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, case PCI_EXP_TYPE_DOWNSTREAM: continue; case PCI_EXP_TYPE_PCI_BRIDGE: + if (tmp->dev_flags & PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS) + continue; ret = fn(tmp, PCI_DEVID(tmp->subordinate->number, PCI_DEVFN(0, 0)), data); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index a61d38777cdc..32aa72456a44 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -14,10 +14,12 @@ * tighter packing. Prefetchable range support. */ +#include <linux/align.h> #include <linux/bitops.h> #include <linux/bug.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/errno.h> @@ -47,7 +49,7 @@ struct pci_dev_resource { unsigned long flags; }; -static void free_list(struct list_head *head) +static void pci_dev_res_free_list(struct list_head *head) { struct pci_dev_resource *dev_res, *tmp; @@ -58,16 +60,16 @@ static void free_list(struct list_head *head) } /** - * add_to_list() - Add a new resource tracker to the list + * pci_dev_res_add_to_list() - Add a new resource tracker to the list * @head: Head of the list * @dev: Device to which the resource belongs * @res: Resource to be tracked * @add_size: Additional size to be optionally added to the resource * @min_align: Minimum memory window alignment */ -static int add_to_list(struct list_head *head, struct pci_dev *dev, - struct resource *res, resource_size_t add_size, - resource_size_t min_align) +int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev, + struct resource *res, resource_size_t add_size, + resource_size_t min_align) { struct pci_dev_resource *tmp; @@ -88,7 +90,8 @@ static int add_to_list(struct list_head *head, struct pci_dev *dev, return 0; } -static void remove_from_list(struct list_head *head, struct resource *res) +static void pci_dev_res_remove_from_list(struct list_head *head, + struct resource *res) { struct pci_dev_resource *dev_res, *tmp; @@ -123,25 +126,21 @@ static resource_size_t get_res_add_size(struct list_head *head, return dev_res ? dev_res->add_size : 0; } -static resource_size_t get_res_add_align(struct list_head *head, - struct resource *res) -{ - struct pci_dev_resource *dev_res; - - dev_res = res_to_dev_res(head, res); - return dev_res ? dev_res->min_align : 0; -} - -static void restore_dev_resource(struct pci_dev_resource *dev_res) +static void pci_dev_res_restore(struct pci_dev_resource *dev_res) { struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; + int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); - if (WARN_ON_ONCE(res->parent)) + if (WARN_ON_ONCE(resource_assigned(res))) return; res->start = dev_res->start; res->end = dev_res->end; res->flags = dev_res->flags; + + pci_dbg(dev, "%s %pR: resource restored\n", res_name, res); } /* @@ -168,7 +167,7 @@ static struct resource *find_bus_resource_of_type(struct pci_bus *bus, if ((r->flags & type_mask) != type) continue; - if (!r->parent) + if (!resource_assigned(r)) return r; if (!r_assigned) r_assigned = r; @@ -271,7 +270,7 @@ static struct resource *pbus_select_window_for_type(struct pci_bus *bus, struct resource *pbus_select_window(struct pci_bus *bus, const struct resource *res) { - if (res->parent) + if (resource_assigned(res)) return res->parent; return pbus_select_window_for_type(bus, res->flags); @@ -302,8 +301,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) if (!res->flags) return false; - if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END && - res->flags & IORESOURCE_DISABLED) + if (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED) return false; return true; @@ -311,7 +309,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res) { - if (res->parent) + if (resource_assigned(res)) return false; if (res->flags & IORESOURCE_PCI_FIXED) @@ -380,19 +378,24 @@ bool pci_resource_is_optional(const struct pci_dev *dev, int resno) return true; if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE)) return true; + if (pci_resource_is_bridge_win(resno) && !resource_size(res)) + return true; return false; } -static inline void reset_resource(struct pci_dev *dev, struct resource *res) +static void reset_resource(struct pci_dev *dev, struct resource *res) { int idx = pci_resource_num(dev, res); + const char *res_name = pci_resource_name(dev, idx); - if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) { + if (pci_resource_is_bridge_win(idx)) { res->flags |= IORESOURCE_UNSET; return; } + pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res); + res->start = 0; res->end = 0; res->flags = 0; @@ -413,7 +416,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, struct list_head *head) { struct pci_dev_resource *add_res, *tmp; - struct pci_dev_resource *dev_res; struct pci_dev *dev; struct resource *res; const char *res_name; @@ -421,8 +423,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head, int idx; list_for_each_entry_safe(add_res, tmp, realloc_head, list) { - bool found_match = false; - res = add_res->res; dev = add_res->dev; idx = pci_resource_num(dev, res); @@ -431,24 +431,18 @@ static void reassign_resources_sorted(struct list_head *realloc_head, * Skip resource that failed the earlier assignment and is * not optional as it would just fail again. */ - if (!res->parent && resource_size(res) && + if (!resource_assigned(res) && resource_size(res) && !pci_resource_is_optional(dev, idx)) goto out; /* Skip this resource if not found in head list */ - list_for_each_entry(dev_res, head, list) { - if (dev_res->res == res) { - found_match = true; - break; - } - } - if (!found_match) /* Just skip */ + if (!res_to_dev_res(head, res)) continue; res_name = pci_resource_name(dev, idx); add_size = add_res->add_size; align = add_res->min_align; - if (!res->parent) { + if (!resource_assigned(res)) { resource_set_range(res, align, resource_size(res) + add_size); if (pci_assign_resource(dev, idx)) { @@ -456,7 +450,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, "%s %pR: ignoring failure in optional allocation\n", res_name, res); } - } else if (add_size > 0) { + } else if (add_size > 0 || !IS_ALIGNED(res->start, align)) { res->flags |= add_res->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN); if (pci_reassign_resource(dev, idx, add_size, align)) @@ -505,9 +499,9 @@ static void assign_requested_resources_sorted(struct list_head *head, if (pci_assign_resource(dev, idx)) { if (fail_head) { - add_to_list(fail_head, dev, res, - 0 /* don't care */, - 0 /* don't care */); + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } } } @@ -604,11 +598,11 @@ static void __assign_resources_sorted(struct list_head *head, LIST_HEAD(local_fail_head); LIST_HEAD(dummy_head); struct pci_dev_resource *save_res; - struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; + struct pci_dev_resource *dev_res, *tmp_res, *dev_res2, *addsize_res; struct resource *res; struct pci_dev *dev; unsigned long fail_type; - resource_size_t add_align, align; + resource_size_t align; if (!realloc_head) realloc_head = &dummy_head; @@ -619,8 +613,9 @@ static void __assign_resources_sorted(struct list_head *head, /* Save original start, end, flags etc at first */ list_for_each_entry(dev_res, head, list) { - if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) { - free_list(&save_head); + if (pci_dev_res_add_to_list(&save_head, dev_res->dev, + dev_res->res, 0, 0)) { + pci_dev_res_free_list(&save_head); goto assign; } } @@ -629,8 +624,11 @@ static void __assign_resources_sorted(struct list_head *head, list_for_each_entry_safe(dev_res, tmp_res, head, list) { res = dev_res->res; - res->end += get_res_add_size(realloc_head, res); + addsize_res = res_to_dev_res(realloc_head, res); + if (!addsize_res) + continue; + res->end += addsize_res->add_size; /* * There are two kinds of additional resources in the list: * 1. bridge resource -- IORESOURCE_STARTALIGN @@ -640,8 +638,8 @@ static void __assign_resources_sorted(struct list_head *head, if (!(res->flags & IORESOURCE_STARTALIGN)) continue; - add_align = get_res_add_align(realloc_head, res); - + if (addsize_res->min_align <= res->start) + continue; /* * The "head" list is sorted by alignment so resources with * bigger alignment will be assigned first. After we @@ -649,17 +647,15 @@ static void __assign_resources_sorted(struct list_head *head, * need to reorder the list by alignment to make it * consistent. */ - if (add_align > res->start) { - resource_set_range(res, add_align, resource_size(res)); - - list_for_each_entry(dev_res2, head, list) { - align = pci_resource_alignment(dev_res2->dev, - dev_res2->res); - if (add_align > align) { - list_move_tail(&dev_res->list, - &dev_res2->list); - break; - } + resource_set_range(res, addsize_res->min_align, + resource_size(res)); + + list_for_each_entry(dev_res2, head, list) { + align = pci_resource_alignment(dev_res2->dev, + dev_res2->res); + if (addsize_res->min_align > align) { + list_move_tail(&dev_res->list, &dev_res2->list); + break; } } @@ -672,8 +668,9 @@ assign: if (list_empty(&local_fail_head)) { /* Remove head list from realloc_head list */ list_for_each_entry(dev_res, head, list) - remove_from_list(realloc_head, dev_res->res); - free_list(&save_head); + pci_dev_res_remove_from_list(realloc_head, + dev_res->res); + pci_dev_res_free_list(&save_head); goto out; } @@ -683,13 +680,13 @@ assign: list_for_each_entry(save_res, &save_head, list) { struct resource *res = save_res->res; - if (res->parent) + if (resource_assigned(res)) continue; - restore_dev_resource(save_res); + pci_dev_res_restore(save_res); } - free_list(&local_fail_head); - free_list(&save_head); + pci_dev_res_free_list(&local_fail_head); + pci_dev_res_free_list(&save_head); goto out; } @@ -699,28 +696,29 @@ assign: list_for_each_entry_safe(dev_res, tmp_res, head, list) { res = dev_res->res; - if (res->parent && !pci_need_to_release(fail_type, res)) { + if (resource_assigned(res) && + !pci_need_to_release(fail_type, res)) { /* Remove it from realloc_head list */ - remove_from_list(realloc_head, res); - remove_from_list(&save_head, res); + pci_dev_res_remove_from_list(realloc_head, res); + pci_dev_res_remove_from_list(&save_head, res); list_del(&dev_res->list); kfree(dev_res); } } - free_list(&local_fail_head); + pci_dev_res_free_list(&local_fail_head); /* Release assigned resource */ list_for_each_entry(dev_res, head, list) { res = dev_res->res; dev = dev_res->dev; pci_release_resource(dev, pci_resource_num(dev, res)); - restore_dev_resource(dev_res); + pci_dev_res_restore(dev_res); } /* Restore start/end/flags from saved list */ list_for_each_entry(save_res, &save_head, list) - restore_dev_resource(save_res); - free_list(&save_head); + pci_dev_res_restore(save_res); + pci_dev_res_free_list(&save_head); /* Satisfy the must-have resource requests */ assign_requested_resources_sorted(head, NULL, false); @@ -735,19 +733,19 @@ out: res = dev_res->res; dev = dev_res->dev; - if (res->parent) + if (resource_assigned(res)) continue; if (fail_head) { - add_to_list(fail_head, dev, res, - 0 /* don't care */, - 0 /* don't care */); + pci_dev_res_add_to_list(fail_head, dev, res, + 0 /* don't care */, + 0 /* don't care */); } reset_resource(dev, res); } - free_list(head); + pci_dev_res_free_list(head); } static void pdev_assign_resources_sorted(struct pci_dev *dev, @@ -774,61 +772,6 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, __assign_resources_sorted(&head, realloc_head, fail_head); } -void pci_setup_cardbus(struct pci_bus *bus) -{ - struct pci_dev *bridge = bus->self; - struct resource *res; - struct pci_bus_region region; - - pci_info(bridge, "CardBus bridge to %pR\n", - &bus->busn_res); - - res = bus->resource[0]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { - /* - * The IO resource is allocated a range twice as large as it - * would normally need. This allows us to set both IO regs. - */ - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, - region.start); - pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, - region.end); - } - - res = bus->resource[1]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, - region.start); - pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, - region.end); - } - - res = bus->resource[2]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, - region.start); - pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, - region.end); - } - - res = bus->resource[3]; - pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { - pci_info(bridge, " bridge window %pR\n", res); - pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, - region.start); - pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, - region.end); - } -} -EXPORT_SYMBOL(pci_setup_cardbus); - /* * Initialize bridges with base/limit values we have collected. PCI-to-PCI * Bridge Architecture Specification rev. 1.1 (1998) requires that if there @@ -860,7 +803,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_IO) { + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; @@ -892,7 +835,7 @@ static void pci_setup_bridge_mmio(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_MEM) { + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; pci_info(bridge, " %s %pR\n", res_name, res); @@ -921,7 +864,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->parent && res->flags & IORESOURCE_PREFETCH) { + if (resource_assigned(res) && res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; if (res->flags & IORESOURCE_MEM_64) { @@ -992,7 +935,7 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i) { int ret = -EINVAL; - if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) + if (!pci_resource_is_bridge_win(i)) return 0; if (pci_claim_resource(bridge, i) == 0) @@ -1068,18 +1011,11 @@ static resource_size_t calculate_iosize(resource_size_t size, static resource_size_t calculate_memsize(resource_size_t size, resource_size_t min_size, - resource_size_t add_size, resource_size_t children_add_size, - resource_size_t old_size, resource_size_t align) { - if (size < min_size) - size = min_size; - if (old_size == 1) - old_size = 0; - - size = max(size, add_size) + children_add_size; - return ALIGN(max(size, old_size), align); + size = max(size, min_size) + children_add_size; + return ALIGN(size, align); } resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, @@ -1117,8 +1053,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) * pbus_size_io() - Size the I/O window of a given bus * * @bus: The bus - * @min_size: The minimum I/O window that must be allocated - * @add_size: Additional optional I/O window + * @add_size: Additional I/O window * @realloc_head: Track the additional I/O window on this list * * Sizing the I/O windows of the PCI-PCI bridge is trivial, since these @@ -1126,8 +1061,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type) * devices are limited to 256 bytes. We must be careful with the ISA * aliasing though. */ -static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, - resource_size_t add_size, +static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size, struct list_head *realloc_head) { struct pci_dev *dev; @@ -1140,7 +1074,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, return; /* If resource is already assigned, nothing more to do */ - if (b_res->parent) + if (resource_assigned(b_res)) return; min_align = window_alignment(bus, IORESOURCE_IO); @@ -1150,7 +1084,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, pci_dev_for_each_resource(dev, r) { unsigned long r_size; - if (r->parent || !(r->flags & IORESOURCE_IO)) + if (resource_assigned(r) || !(r->flags & IORESOURCE_IO)) continue; if (!pdev_resource_assignable(dev, r)) @@ -1172,7 +1106,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, } } - size0 = calculate_iosize(size, min_size, size1, 0, 0, + size0 = calculate_iosize(size, realloc_head ? 0 : add_size, size1, 0, 0, resource_size(b_res), min_align); if (size0) @@ -1180,7 +1114,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, size1 = size0; if (realloc_head && (add_size > 0 || children_add_size > 0)) { - size1 = calculate_iosize(size, min_size, size1, add_size, + size1 = calculate_iosize(size, 0, size1, add_size, children_add_size, resource_size(b_res), min_align); } @@ -1197,8 +1131,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { b_res->flags &= ~IORESOURCE_DISABLED; - add_to_list(realloc_head, bus->self, b_res, size1-size0, - min_align); + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + size1 - size0, min_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", b_res, &bus->busn_res, (unsigned long long) size1 - size0); @@ -1227,65 +1161,90 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, return min_align; } -/** - * pbus_upstream_space_available - Check no upstream resource limits allocation - * @bus: The bus - * @res: The resource to help select the correct bridge window - * @size: The size required from the bridge window - * @align: Required alignment for the resource - * - * Check that @size can fit inside the upstream bridge resources that are - * already assigned. Select the upstream bridge window based on the type of - * @res. - * - * Return: %true if enough space is available on all assigned upstream +/* + * Calculate bridge window head alignment that leaves no gaps in between * resources. */ -static bool pbus_upstream_space_available(struct pci_bus *bus, - struct resource *res, - resource_size_t size, - resource_size_t align) -{ - struct resource_constraint constraint = { - .max = RESOURCE_SIZE_MAX, - .align = align, - }; - struct pci_bus *downstream = bus; - - while ((bus = bus->parent)) { - if (pci_is_root_bus(bus)) - break; +static resource_size_t calculate_head_align(resource_size_t *aligns, + int max_order) +{ + resource_size_t head_align = 1; + resource_size_t remainder = 0; + int order; - res = pbus_select_window(bus, res); - if (!res) - return false; - if (!res->parent) - continue; + /* Take the largest alignment as the starting point. */ + head_align <<= max_order + __ffs(SZ_1M); - if (resource_size(res) >= size) { - struct resource gap = {}; + for (order = max_order - 1; order >= 0; order--) { + resource_size_t align1 = 1; - if (find_resource_space(res, &gap, size, &constraint) == 0) { - gap.flags = res->flags; - pci_dbg(bus->self, - "Assigned bridge window %pR to %pR free space at %pR\n", - res, &bus->busn_res, &gap); - return true; - } + align1 <<= order + __ffs(SZ_1M); + + /* + * Account smaller resources with alignment < max_order that + * could be used to fill head room if alignment less than + * max_order is used. + */ + remainder += aligns[order]; + + /* + * Test if head fill is enough to satisfy the alignment of + * the larger resources after reducing the alignment. + */ + while ((head_align > align1) && (remainder >= head_align / 2)) { + head_align /= 2; + remainder -= head_align; } + } + + return head_align; +} + +/* + * pbus_size_mem_optional - Account optional resources in bridge window + * + * Account an optional resource or the optional part of the resource in bridge + * window size. + * + * Return: %true if the resource is entirely optional. + */ +static bool pbus_size_mem_optional(struct pci_dev *dev, int resno, + resource_size_t align, + struct list_head *realloc_head, + resource_size_t *add_align, + resource_size_t *children_add_size) +{ + struct resource *res = pci_resource_n(dev, resno); + bool optional = pci_resource_is_optional(dev, resno); + resource_size_t r_size = resource_size(res); + struct pci_dev_resource *dev_res; - if (bus->self) { - pci_info(bus->self, - "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", - res, &bus->busn_res, - (unsigned long long)size, - pci_name(downstream->self), - &downstream->busn_res); + if (!realloc_head) + return false; + + if (!optional) { + /* + * Only bridges have optional sizes in realloc_head at this + * point. As res_to_dev_res() walks the entire realloc_head + * list, skip calling it when known unnecessary. + */ + if (!pci_resource_is_bridge_win(resno)) + return false; + + dev_res = res_to_dev_res(realloc_head, res); + if (dev_res) { + *children_add_size += dev_res->add_size; + *add_align = max(*add_align, dev_res->min_align); } return false; } + /* Put SRIOV requested res to the optional list */ + pci_dev_res_add_to_list(realloc_head, dev, res, 0, align); + *children_add_size += r_size; + *add_align = max(align, *add_align); + return true; } @@ -1293,12 +1252,11 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, * pbus_size_mem() - Size the memory window of a given bus * * @bus: The bus - * @type: The type of bridge resource - * @min_size: The minimum memory window that must be allocated - * @add_size: Additional optional memory window + * @b_res: The bridge window resource + * @add_size: Additional memory window * @realloc_head: Track the additional memory window on this list * - * Calculate the size of the bus resource for @type and minimal alignment + * Calculate the size of the bridge window @b_res and minimal alignment * which guarantees that all child resources fit in this size. * * Set the bus resource start/end to indicate the required size if there an @@ -1307,30 +1265,24 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, * Add optional resource requests to the @realloc_head list if it is * supplied. */ -static void pbus_size_mem(struct pci_bus *bus, unsigned long type, - resource_size_t min_size, - resource_size_t add_size, - struct list_head *realloc_head) +static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res, + resource_size_t add_size, + struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t min_align, win_align, align, size, size0, size1 = 0; - resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */ + resource_size_t aligns[28] = {}; /* Alignments from 1MB to 128TB */ int order, max_order; - struct resource *b_res = pbus_select_window_for_type(bus, type); resource_size_t children_add_size = 0; - resource_size_t children_add_align = 0; resource_size_t add_align = 0; - resource_size_t relaxed_align; - resource_size_t old_size; if (!b_res) return; /* If resource is already assigned, nothing more to do */ - if (b_res->parent) + if (resource_assigned(b_res)) return; - memset(aligns, 0, sizeof(aligns)); max_order = 0; size = 0; @@ -1348,88 +1300,50 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, if (b_res != pbus_select_window(bus, r)) continue; - r_size = resource_size(r); - - /* Put SRIOV requested res to the optional list */ - if (realloc_head && pci_resource_is_optional(dev, i)) { - add_align = max(pci_resource_alignment(dev, r), add_align); - add_to_list(realloc_head, dev, r, 0, 0 /* Don't care */); - children_add_size += r_size; - continue; - } - + align = pci_resource_alignment(dev, r); /* * aligns[0] is for 1MB (since bridge memory * windows are always at least 1MB aligned), so * keep "order" from being negative for smaller * resources. */ - align = pci_resource_alignment(dev, r); - order = __ffs(align) - __ffs(SZ_1M); - if (order < 0) - order = 0; + order = max_t(int, __ffs(align) - __ffs(SZ_1M), 0); if (order >= ARRAY_SIZE(aligns)) { pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n", r_name, r, (unsigned long long) align); r->flags = 0; continue; } + + if (pbus_size_mem_optional(dev, i, align, + realloc_head, &add_align, + &children_add_size)) + continue; + + r_size = resource_size(r); size += max(r_size, align); - /* - * Exclude ranges with size > align from calculation of - * the alignment. - */ - if (r_size <= align) - aligns[order] += align; + + aligns[order] += align; if (order > max_order) max_order = order; - - if (realloc_head) { - children_add_size += get_res_add_size(realloc_head, r); - children_add_align = get_res_add_align(realloc_head, r); - add_align = max(add_align, children_add_align); - } } } - old_size = resource_size(b_res); win_align = window_alignment(bus, b_res->flags); - min_align = calculate_mem_align(aligns, max_order); + min_align = calculate_head_align(aligns, max_order); min_align = max(min_align, win_align); - size0 = calculate_memsize(size, min_size, 0, 0, old_size, min_align); + size0 = calculate_memsize(size, realloc_head ? 0 : add_size, + 0, win_align); if (size0) { resource_set_range(b_res, min_align, size0); b_res->flags &= ~IORESOURCE_DISABLED; } - if (bus->self && size0 && - !pbus_upstream_space_available(bus, b_res, size0, min_align)) { - relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); - relaxed_align = max(relaxed_align, win_align); - min_align = min(min_align, relaxed_align); - size0 = calculate_memsize(size, min_size, 0, 0, old_size, win_align); - resource_set_range(b_res, min_align, size0); - pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", - b_res, &bus->busn_res); - } - if (realloc_head && (add_size > 0 || children_add_size > 0)) { add_align = max(min_align, add_align); - size1 = calculate_memsize(size, min_size, add_size, children_add_size, - old_size, add_align); - - if (bus->self && size1 && - !pbus_upstream_space_available(bus, b_res, size1, add_align)) { - relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); - relaxed_align = max(relaxed_align, win_align); - min_align = min(min_align, relaxed_align); - size1 = calculate_memsize(size, min_size, add_size, children_add_size, - old_size, win_align); - pci_info(bus->self, - "bridge window %pR to %pR requires relaxed alignment rules\n", - b_res, &bus->busn_res); - } + size1 = calculate_memsize(size, add_size, children_add_size, + win_align); } if (!size0 && !size1) { @@ -1442,121 +1356,24 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type, resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; - if (bus->self && size1 > size0 && realloc_head) { + if (bus->self && realloc_head && (size1 > size0 || add_align > min_align)) { b_res->flags &= ~IORESOURCE_DISABLED; - add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); + add_size = size1 > size0 ? size1 - size0 : 0; + pci_dev_res_add_to_list(realloc_head, bus->self, b_res, + add_size, add_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", b_res, &bus->busn_res, - (unsigned long long) (size1 - size0), + (unsigned long long) add_size, (unsigned long long) add_align); } } -unsigned long pci_cardbus_resource_alignment(struct resource *res) -{ - if (res->flags & IORESOURCE_IO) - return pci_cardbus_io_size; - if (res->flags & IORESOURCE_MEM) - return pci_cardbus_mem_size; - return 0; -} - -static void pci_bus_size_cardbus(struct pci_bus *bus, - struct list_head *realloc_head) -{ - struct pci_dev *bridge = bus->self; - struct resource *b_res; - resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; - u16 ctrl; - - b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; - if (b_res->parent) - goto handle_b_res_1; - /* - * Reserve some resources for CardBus. We reserve a fixed amount - * of bus space for CardBus bridges. - */ - resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); - b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); - } - -handle_b_res_1: - b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; - if (b_res->parent) - goto handle_b_res_2; - resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); - b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_io_size; - add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, - pci_cardbus_io_size); - } - -handle_b_res_2: - /* MEM1 must not be pref MMIO */ - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { - ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; - pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - } - - /* Check whether prefetchable memory is supported by this bridge. */ - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { - ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; - pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); - pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); - } - - b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; - if (b_res->parent) - goto handle_b_res_3; - /* - * If we have prefetchable memory support, allocate two regions. - * Otherwise, allocate one region of twice the size. - */ - if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - resource_set_range(b_res, pci_cardbus_mem_size, - pci_cardbus_mem_size); - b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | - IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= pci_cardbus_mem_size; - add_to_list(realloc_head, bridge, b_res, - pci_cardbus_mem_size, pci_cardbus_mem_size); - } - - /* Reduce that to half */ - b_res_3_size = pci_cardbus_mem_size; - } - -handle_b_res_3: - b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; - if (b_res->parent) - goto handle_done; - resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); - b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; - if (realloc_head) { - b_res->end -= b_res_3_size; - add_to_list(realloc_head, bridge, b_res, b_res_3_size, - pci_cardbus_mem_size); - } - -handle_done: - ; -} - void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; resource_size_t additional_io_size = 0, additional_mmio_size = 0, additional_mmio_pref_size = 0; - struct resource *pref; + struct resource *b_res; struct pci_host_bridge *host; int hdr_type; @@ -1567,7 +1384,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) switch (dev->hdr_type) { case PCI_HEADER_TYPE_CARDBUS: - pci_bus_size_cardbus(b, realloc_head); + if (pci_bus_size_cardbus_bridge(b, realloc_head)) + continue; break; case PCI_HEADER_TYPE_BRIDGE: @@ -1582,12 +1400,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) host = to_pci_host_bridge(bus->bridge); if (!host->size_windows) return; - pci_bus_for_each_resource(bus, pref) - if (pref && (pref->flags & IORESOURCE_PREFETCH)) - break; hdr_type = -1; /* Intentionally invalid - not a PCI device. */ } else { - pref = &bus->self->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; hdr_type = bus->self->hdr_type; } @@ -1605,20 +1419,21 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) } fallthrough; default: - pbus_size_io(bus, realloc_head ? 0 : additional_io_size, - additional_io_size, realloc_head); - - if (pref && (pref->flags & IORESOURCE_PREFETCH)) { - pbus_size_mem(bus, - IORESOURCE_MEM | IORESOURCE_PREFETCH | - (pref->flags & IORESOURCE_MEM_64), - realloc_head ? 0 : additional_mmio_pref_size, - additional_mmio_pref_size, realloc_head); + pbus_size_io(bus, additional_io_size, realloc_head); + + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM | + IORESOURCE_PREFETCH | + IORESOURCE_MEM_64); + if (b_res && (b_res->flags & IORESOURCE_PREFETCH)) { + pbus_size_mem(bus, b_res, additional_mmio_pref_size, + realloc_head); } - pbus_size_mem(bus, IORESOURCE_MEM, - realloc_head ? 0 : additional_mmio_size, - additional_mmio_size, realloc_head); + b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM); + if (b_res) { + pbus_size_mem(bus, b_res, additional_mmio_size, + realloc_head); + } break; } } @@ -1656,12 +1471,13 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev) pci_dev_for_each_resource(dev, r) { struct pci_bus *b; - if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) || + if (resource_assigned(r) || + !(r->flags & IORESOURCE_PCI_FIXED) || !(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) continue; b = dev->bus; - while (b && !r->parent) { + while (b && !resource_assigned(r)) { assign_fixed_resource_on_bus(b, r); b = b->parent; } @@ -1693,7 +1509,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus, break; case PCI_HEADER_TYPE_CARDBUS: - pci_setup_cardbus(b); + pci_setup_cardbus_bridge(b); break; default: @@ -1717,7 +1533,7 @@ static void pci_claim_device_resources(struct pci_dev *dev) for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { struct resource *r = &dev->resource[i]; - if (!r->flags || r->parent) + if (!r->flags || resource_assigned(r)) continue; pci_claim_resource(dev, i); @@ -1731,7 +1547,9 @@ static void pci_claim_bridge_resources(struct pci_dev *dev) for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { struct resource *r = &dev->resource[i]; - if (!r->flags || r->parent) + if (!r->flags || resource_assigned(r)) + continue; + if (r->flags & IORESOURCE_DISABLED) continue; pci_claim_bridge_resource(dev, i); @@ -1798,7 +1616,7 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, break; case PCI_CLASS_BRIDGE_CARDBUS: - pci_setup_cardbus(b); + pci_setup_cardbus_bridge(b); break; default: @@ -1814,7 +1632,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, struct pci_dev *dev = bus->self; int idx, ret; - if (!b_win->parent) + if (!resource_assigned(b_win)) return; idx = pci_resource_num(dev, b_win); @@ -2010,7 +1828,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, { resource_size_t add_size, size = resource_size(res); - if (res->parent) + if (resource_assigned(res)) return; if (!new_size) @@ -2032,7 +1850,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res, /* If the resource is part of the add_list, remove it now */ if (add_list) - remove_from_list(add_list, res); + pci_dev_res_remove_from_list(add_list, res); } static void remove_dev_resource(struct resource *avail, struct pci_dev *dev, @@ -2100,7 +1918,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * window. */ align = pci_resource_alignment(bridge, res); - if (!res->parent && align) + if (!resource_assigned(res) && align) available[i].start = min(ALIGN(available[i].start, align), available[i].end + 1); @@ -2284,9 +2102,9 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head, /* Restore size and flags */ list_for_each_entry(fail_res, fail_head, list) - restore_dev_resource(fail_res); + pci_dev_res_restore(fail_res); - free_list(fail_head); + pci_dev_res_free_list(fail_head); } /* @@ -2333,7 +2151,7 @@ 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))) - free_list(add_list); + pci_dev_res_free_list(add_list); tried_times++; /* Any device complain? */ @@ -2348,7 +2166,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); } - free_list(&fail_head); + pci_dev_res_free_list(&fail_head); break; } @@ -2396,7 +2214,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) __pci_bridge_assign_resources(bridge, &add_list, &fail_head); if (WARN_ON_ONCE(!list_empty(&add_list))) - free_list(&add_list); + pci_dev_res_free_list(&add_list); tried_times++; if (list_empty(&fail_head)) @@ -2404,7 +2222,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) if (tried_times >= 2) { /* Still fail, don't need to try more */ - free_list(&fail_head); + pci_dev_res_free_list(&fail_head); break; } @@ -2445,7 +2263,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * /* Ignore BARs which are still in use */ if (!res->child) { - ret = add_to_list(saved, bridge, res, 0, 0); + ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0); if (ret) return ret; @@ -2467,12 +2285,12 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource * __pci_bus_size_bridges(bridge->subordinate, &added); __pci_bridge_assign_resources(bridge, &added, &failed); if (WARN_ON_ONCE(!list_empty(&added))) - free_list(&added); + pci_dev_res_free_list(&added); if (!list_empty(&failed)) { if (pci_required_resource_failed(&failed, type)) ret = -ENOSPC; - free_list(&failed); + pci_dev_res_free_list(&failed); if (ret) return ret; @@ -2528,7 +2346,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size if (b_win != pbus_select_window(bus, r)) continue; - ret = add_to_list(&saved, pdev, r, 0, 0); + ret = pci_dev_res_add_to_list(&saved, pdev, r, 0, 0); if (ret) goto restore; pci_release_resource(pdev, i); @@ -2546,7 +2364,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size out: up_read(&pci_bus_sem); - free_list(&saved); + pci_dev_res_free_list(&saved); return ret; restore: @@ -2565,12 +2383,12 @@ restore: i = pci_resource_num(dev, res); - if (res->parent) { + if (resource_assigned(res)) { release_child_resources(res); pci_release_resource(dev, i); } - restore_dev_resource(dev_res); + pci_dev_res_restore(dev_res); if (pci_claim_resource(dev, i)) continue; @@ -2601,6 +2419,6 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); if (WARN_ON_ONCE(!list_empty(&add_list))) - free_list(&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 new file mode 100644 index 000000000000..1ebd13a1f730 --- /dev/null +++ b/drivers/pci/setup-cardbus.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cardbus bridge setup routines. + */ + +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <linux/sprintf.h> +#include <linux/types.h> + +#include "pci.h" + +#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ +#define CARDBUS_RESERVE_BUSNR 3 + +#define DEFAULT_CARDBUS_IO_SIZE SZ_256 +#define DEFAULT_CARDBUS_MEM_SIZE SZ_64M +/* pci=cbmemsize=nnM,cbiosize=nn can override this */ +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) +{ + if (res->flags & IORESOURCE_IO) + return pci_cardbus_io_size; + if (res->flags & IORESOURCE_MEM) + return pci_cardbus_mem_size; + return 0; +} + +int pci_bus_size_cardbus_bridge(struct pci_bus *bus, + struct list_head *realloc_head) +{ + struct pci_dev *bridge = bus->self; + struct resource *b_res; + resource_size_t b_res_3_size = pci_cardbus_mem_size * 2; + u16 ctrl; + + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_1; + /* + * Reserve some resources for CardBus. We reserve a fixed amount + * of bus space for CardBus bridges. + */ + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_io_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_io_size, + pci_cardbus_io_size); + } + +handle_b_res_1: + b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_2; + resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size); + b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_io_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_io_size, + pci_cardbus_io_size); + } + +handle_b_res_2: + /* MEM1 must not be pref MMIO */ + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) { + ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1; + pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + } + + /* Check whether prefetchable memory is supported by this bridge. */ + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) { + ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0; + pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl); + pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl); + } + + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW]; + if (resource_assigned(b_res)) + goto handle_b_res_3; + /* + * If we have prefetchable memory support, allocate two regions. + * Otherwise, allocate one region of twice the size. + */ + if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { + resource_set_range(b_res, pci_cardbus_mem_size, + pci_cardbus_mem_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | + IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= pci_cardbus_mem_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + pci_cardbus_mem_size, + pci_cardbus_mem_size); + } + + /* Reduce that to half */ + b_res_3_size = pci_cardbus_mem_size; + } + +handle_b_res_3: + b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW]; + if (resource_assigned(b_res)) + goto handle_done; + resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size); + b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN; + if (realloc_head) { + b_res->end -= b_res_3_size; + pci_dev_res_add_to_list(realloc_head, bridge, b_res, + b_res_3_size, pci_cardbus_mem_size); + } + +handle_done: + return 0; +} + +void pci_setup_cardbus_bridge(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + struct resource *res; + struct pci_bus_region region; + + pci_info(bridge, "CardBus bridge to %pR\n", + &bus->busn_res); + + res = bus->resource[0]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { + /* + * The IO resource is allocated a range twice as large as it + * would normally need. This allows us to set both IO regs. + */ + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_IO_BASE_0, + region.start); + pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0, + region.end); + } + + res = bus->resource[1]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_IO) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, + region.start); + pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1, + region.end); + } + + res = bus->resource[2]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, + region.start); + pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0, + region.end); + } + + res = bus->resource[3]; + pcibios_resource_to_bus(bridge->bus, ®ion, res); + if (resource_assigned(res) && res->flags & IORESOURCE_MEM) { + pci_info(bridge, " bridge window %pR\n", res); + pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, + region.start); + pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1, + region.end); + } +} +EXPORT_SYMBOL(pci_setup_cardbus_bridge); + +int pci_setup_cardbus(char *str) +{ + if (!strncmp(str, "cbiosize=", 9)) { + pci_cardbus_io_size = memparse(str + 9, &str); + return 0; + } else if (!strncmp(str, "cbmemsize=", 10)) { + pci_cardbus_mem_size = memparse(str + 10, &str); + return 0; + } + + return -ENOENT; +} + +int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev, + u32 buses, int max, + unsigned int available_buses, int pass) +{ + struct pci_bus *child; + bool fixed_buses; + u8 fixed_sec, fixed_sub; + int next_busnr; + u32 i, j = 0; + + /* + * We need to assign a number to this bus which we always do in the + * second pass. + */ + if (!pass) { + /* + * Temporarily disable forwarding of the configuration + * cycles on all bridges in this bus segment to avoid + * possible conflicts in the second pass between two bridges + * programmed with overlapping bus ranges. + */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, + buses & PCI_SEC_LATENCY_TIMER_MASK); + return max; + } + + /* Clear errors */ + pci_write_config_word(dev, PCI_STATUS, 0xffff); + + /* Read bus numbers from EA Capability (if present) */ + fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub); + if (fixed_buses) + next_busnr = fixed_sec; + else + next_busnr = max + 1; + + /* + * Prevent assigning a bus number that already exists. This can + * happen when a bridge is hot-plugged, so in this case we only + * re-scan this bus. + */ + child = pci_find_bus(pci_domain_nr(bus), next_busnr); + if (!child) { + child = pci_add_new_bus(bus, dev, next_busnr); + if (!child) + return max; + pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end); + } + max++; + if (available_buses) + available_buses--; + + buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) | + FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) | + FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) | + FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end); + + /* + * 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); + + /* We need to blast all three values with a single write */ + pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); + + /* + * For CardBus bridges, we leave 4 bus numbers as cards with a + * PCI-to-PCI bridge can be inserted later. + */ + for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) { + struct pci_bus *parent = bus; + + if (pci_find_bus(pci_domain_nr(bus), max + i + 1)) + break; + + while (parent->parent) { + if (!pcibios_assign_all_busses() && + (parent->busn_res.end > max) && + (parent->busn_res.end <= max + i)) { + j = 1; + } + parent = parent->parent; + } + if (j) { + /* + * Often, there are two CardBus bridges -- try to + * leave one valid bus number for each one. + */ + i /= 2; + break; + } + } + max += i; + + /* + * Set subordinate bus number to its real value. If fixed + * subordinate bus number exists from EA capability then use it. + */ + if (fixed_buses) + max = fixed_sub; + pci_bus_update_busn_res_end(child, max); + pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); + + scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x", + pci_domain_nr(bus), child->number); + + pbus_validate_busn(child); + + return max; +} diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index e5fcadfc58b0..bb2aef373d6f 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -359,7 +359,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; - if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END) + if (pci_resource_is_bridge_win(resno)) res->flags &= ~IORESOURCE_DISABLED; pci_info(dev, "%s %pR: assigned\n", res_name, res); diff --git a/drivers/pci/trace.c b/drivers/pci/trace.c new file mode 100644 index 000000000000..cf11abca8602 --- /dev/null +++ b/drivers/pci/trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tracepoints for PCI system + * + * Copyright (C) 2025 Alibaba Corporation + */ + +#include <linux/pci.h> + +#define CREATE_TRACE_POINTS +#include <trace/events/pci.h> diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 923ed23570a0..34c4eaee7dfc 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -779,7 +779,7 @@ static void yenta_allocate_resources(struct yenta_socket *socket) IORESOURCE_MEM, PCI_CB_MEMORY_BASE_1, PCI_CB_MEMORY_LIMIT_1); if (program) - pci_setup_cardbus(socket->dev->subordinate); + pci_setup_cardbus_bridge(socket->dev->subordinate); } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 889dc4426271..bd3a63555594 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -21,7 +21,6 @@ static const char hcd_name[] = "ehci-pci"; /* defined here to avoid adding to pci_ids.h for single instance use */ #define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 -#define PCI_VENDOR_ID_ASPEED 0x1a03 #define PCI_DEVICE_ID_ASPEED_EHCI 0x2603 /*-------------------------------------------------------------------------*/ diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 9afa30f9346f..60ca6a49839c 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -338,7 +338,7 @@ static inline bool resource_union(const struct resource *r1, const struct resour * Check if this resource is added to a resource tree or detached. Caller is * responsible for not racing assignment. */ -static inline bool resource_assigned(struct resource *res) +static inline bool resource_assigned(const struct resource *res) { return res->parent; } diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 4286bfdbfdfa..c021c7af175f 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -223,6 +223,13 @@ struct pci_epc_bar_desc { /** * struct pci_epc_features - features supported by a EPC device per function * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up + * @dynamic_inbound_mapping: indicate if the EPC device supports updating + * inbound mappings for an already configured BAR + * (i.e. allow calling pci_epc_set_bar() again + * without first calling pci_epc_clear_bar()) + * @subrange_mapping: indicate if the EPC device can map inbound subranges for a + * BAR. This feature depends on @dynamic_inbound_mapping + * feature. * @msi_capable: indicate if the endpoint function has MSI capability * @msix_capable: indicate if the endpoint function has MSI-X capability * @intx_capable: indicate if the endpoint can raise INTx interrupts @@ -231,6 +238,8 @@ struct pci_epc_bar_desc { */ struct pci_epc_features { unsigned int linkup_notifier : 1; + unsigned int dynamic_inbound_mapping : 1; + unsigned int subrange_mapping : 1; unsigned int msi_capable : 1; unsigned int msix_capable : 1; unsigned int intx_capable : 1; diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 48f68c4dcfa5..7737a7c03260 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -111,6 +111,22 @@ struct pci_epf_driver { #define to_pci_epf_driver(drv) container_of_const((drv), struct pci_epf_driver, driver) /** + * struct pci_epf_bar_submap - BAR subrange for inbound mapping + * @phys_addr: target physical/DMA address for this subrange + * @size: the size of the subrange to be mapped + * + * When pci_epf_bar.num_submap is >0, pci_epf_bar.submap describes the + * complete BAR layout. This allows an EPC driver to program multiple + * inbound translation windows for a single BAR when supported by the + * controller. The array order defines the BAR layout (submap[0] at offset + * 0, and each immediately follows the previous one). + */ +struct pci_epf_bar_submap { + dma_addr_t phys_addr; + size_t size; +}; + +/** * struct pci_epf_bar - represents the BAR of EPF device * @phys_addr: physical address that should be mapped to the BAR * @addr: virtual address corresponding to the @phys_addr @@ -119,6 +135,9 @@ struct pci_epf_driver { * requirement * @barno: BAR number * @flags: flags that are set for the BAR + * @num_submap: number of entries in @submap + * @submap: array of subrange descriptors allocated by the caller. See + * struct pci_epf_bar_submap for the semantics in detail. */ struct pci_epf_bar { dma_addr_t phys_addr; @@ -127,6 +146,10 @@ struct pci_epf_bar { size_t mem_size; enum pci_barno barno; int flags; + + /* Optional sub-range mapping */ + unsigned int num_submap; + struct pci_epf_bar_submap *submap; }; /** diff --git a/include/linux/pci-p2pdma.h b/include/linux/pci-p2pdma.h index 517e121d2598..873de20a2247 100644 --- a/include/linux/pci-p2pdma.h +++ b/include/linux/pci-p2pdma.h @@ -20,6 +20,8 @@ struct scatterlist; * struct p2pdma_provider * * A p2pdma provider is a range of MMIO address space available to the CPU. + * @owner: Device to which this provider belongs. + * @bus_offset: Bus offset for p2p communication. */ struct p2pdma_provider { struct device *owner; diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h index 4aefc7901cd1..1192a2527521 100644 --- a/include/linux/pci-pwrctrl.h +++ b/include/linux/pci-pwrctrl.h @@ -31,6 +31,8 @@ struct device_link; /** * struct pci_pwrctrl - PCI device power control context. * @dev: Address of the power controlling device. + * @power_on: Callback to power on the power controlling device. + * @power_off: Callback to power off the power controlling device. * * An object of this type must be allocated by the PCI power control device and * passed to the pwrctrl subsystem to trigger a bus rescan and setup a device @@ -38,6 +40,8 @@ struct device_link; */ struct pci_pwrctrl { struct device *dev; + int (*power_on)(struct pci_pwrctrl *pwrctrl); + int (*power_off)(struct pci_pwrctrl *pwrctrl); /* private: internal use only */ struct notifier_block nb; @@ -50,5 +54,15 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl); void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl); int devm_pci_pwrctrl_device_set_ready(struct device *dev, struct pci_pwrctrl *pwrctrl); - +#if IS_ENABLED(CONFIG_PCI_PWRCTRL) +int pci_pwrctrl_create_devices(struct device *parent); +void pci_pwrctrl_destroy_devices(struct device *parent); +int pci_pwrctrl_power_on_devices(struct device *parent); +void pci_pwrctrl_power_off_devices(struct device *parent); +#else +static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; } +static void pci_pwrctrl_destroy_devices(struct device *parent) { } +static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; } +static void pci_pwrctrl_power_off_devices(struct device *parent) { } +#endif #endif /* __PCI_PWRCTRL_H__ */ diff --git a/include/linux/pci.h b/include/linux/pci.h index b30631673b5b..edf792a79193 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -248,6 +248,11 @@ enum pci_dev_flags { PCI_DEV_FLAGS_HAS_MSI_MASKING = (__force pci_dev_flags_t) (1 << 12), /* Device requires write to PCI_MSIX_ENTRY_DATA before any MSIX reads */ PCI_DEV_FLAGS_MSIX_TOUCH_ENTRY_DATA_FIRST = (__force pci_dev_flags_t) (1 << 13), + /* + * PCIe to PCI bridge does not create RID aliases because the bridge is + * integrated with the downstream devices and doesn't use real PCI. + */ + PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14), }; enum pci_irq_reroute_variant { @@ -413,6 +418,7 @@ struct pci_dev { user sysfs */ unsigned int clear_retrain_link:1; /* Need to clear Retrain Link bit manually */ + unsigned int no_bw_notif:1; /* BW notifications may cause issues */ unsigned int d3hot_delay; /* D3hot->D0 transition time in ms */ unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ @@ -564,6 +570,7 @@ struct pci_dev { struct pci_tsm *tsm; /* TSM operation state */ #endif u16 acs_cap; /* ACS Capability offset */ + u16 acs_capabilities; /* ACS Capabilities */ u8 supported_speeds; /* Supported Link Speeds Vector */ phys_addr_t rom; /* Physical address if not from BAR */ size_t romlen; /* Length if not from BAR */ @@ -860,7 +867,6 @@ struct pci_ops { void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); - int (*assert_perst)(struct pci_bus *bus, bool assert); }; /* @@ -1250,7 +1256,11 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev); void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev); void pci_stop_root_bus(struct pci_bus *bus); void pci_remove_root_bus(struct pci_bus *bus); -void pci_setup_cardbus(struct pci_bus *bus); +#ifdef CONFIG_CARDBUS +void pci_setup_cardbus_bridge(struct pci_bus *bus); +#else +static inline void pci_setup_cardbus_bridge(struct pci_bus *bus) { } +#endif void pcibios_setup_bridge(struct pci_bus *bus, unsigned long type); void pci_sort_breadthfirst(void); #define dev_is_pci(d) ((d)->bus == &pci_bus_type) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 5ed7846639bf..efd6dee093e0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2583,6 +2583,8 @@ #define PCI_DEVICE_ID_NETRONOME_NFP3800_VF 0x3803 #define PCI_DEVICE_ID_NETRONOME_NFP6000_VF 0x6003 +#define PCI_VENDOR_ID_ASPEED 0x1a03 + #define PCI_VENDOR_ID_QMI 0x1a32 #define PCI_VENDOR_ID_AZWAVE 0x1a3b diff --git a/include/trace/events/pci.h b/include/trace/events/pci.h new file mode 100644 index 000000000000..9a9122f62fd3 --- /dev/null +++ b/include/trace/events/pci.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM pci + +#if !defined(_TRACE_HW_EVENT_PCI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HW_EVENT_PCI_H + +#include <uapi/linux/pci_regs.h> +#include <linux/tracepoint.h> + +#define PCI_HOTPLUG_EVENT \ + EM(PCI_HOTPLUG_LINK_UP, "LINK_UP") \ + EM(PCI_HOTPLUG_LINK_DOWN, "LINK_DOWN") \ + EM(PCI_HOTPLUG_CARD_PRESENT, "CARD_PRESENT") \ + EMe(PCI_HOTPLUG_CARD_NOT_PRESENT, "CARD_NOT_PRESENT") + +/* Enums require being exported to userspace, for user tool parsing */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +PCI_HOTPLUG_EVENT + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} + +/* + * Note: For generic PCI hotplug events, we pass already-resolved strings + * (port_name, slot) instead of driver-specific structures like 'struct + * controller'. This is because different PCI hotplug drivers (pciehp, cpqphp, + * ibmphp, shpchp) define their own versions of 'struct controller' with + * different fields and helper functions. Using driver-specific structures would + * make the tracepoint interface non-generic and cause compatibility issues + * across different drivers. + */ +TRACE_EVENT(pci_hp_event, + + TP_PROTO(const char *port_name, + const char *slot, + const int event), + + TP_ARGS(port_name, slot, event), + + TP_STRUCT__entry( + __string( port_name, port_name ) + __string( slot, slot ) + __field( int, event ) + ), + + TP_fast_assign( + __assign_str(port_name); + __assign_str(slot); + __entry->event = event; + ), + + TP_printk("%s slot:%s, event:%s\n", + __get_str(port_name), + __get_str(slot), + __print_symbolic(__entry->event, PCI_HOTPLUG_EVENT) + ) +); + +#define PCI_EXP_LNKSTA_LINK_STATUS_MASK (PCI_EXP_LNKSTA_LBMS | \ + PCI_EXP_LNKSTA_LABS | \ + PCI_EXP_LNKSTA_LT | \ + PCI_EXP_LNKSTA_DLLLA) + +#define LNKSTA_FLAGS \ + { PCI_EXP_LNKSTA_LT, "LT"}, \ + { PCI_EXP_LNKSTA_DLLLA, "DLLLA"}, \ + { PCI_EXP_LNKSTA_LBMS, "LBMS"}, \ + { PCI_EXP_LNKSTA_LABS, "LABS"} + +TRACE_EVENT(pcie_link_event, + + TP_PROTO(struct pci_bus *bus, + unsigned int reason, + unsigned int width, + unsigned int status + ), + + TP_ARGS(bus, reason, width, status), + + TP_STRUCT__entry( + __string( port_name, pci_name(bus->self)) + __field( unsigned int, type ) + __field( unsigned int, reason ) + __field( unsigned int, cur_bus_speed ) + __field( unsigned int, max_bus_speed ) + __field( unsigned int, width ) + __field( unsigned int, flit_mode ) + __field( unsigned int, link_status ) + ), + + TP_fast_assign( + __assign_str(port_name); + __entry->type = pci_pcie_type(bus->self); + __entry->reason = reason; + __entry->cur_bus_speed = bus->cur_bus_speed; + __entry->max_bus_speed = bus->max_bus_speed; + __entry->width = width; + __entry->flit_mode = bus->flit_mode; + __entry->link_status = status; + ), + + TP_printk("%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n", + __get_str(port_name), + __entry->type, + __entry->reason, + __entry->cur_bus_speed, + __entry->max_bus_speed, + __entry->width, + __entry->flit_mode, + __print_flags((unsigned long)__entry->link_status, "|", + LNKSTA_FLAGS) + ) +); + +#endif /* _TRACE_HW_EVENT_PCI_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/uapi/linux/pci.h b/include/uapi/linux/pci.h index a769eefc5139..4f150028965d 100644 --- a/include/uapi/linux/pci.h +++ b/include/uapi/linux/pci.h @@ -39,4 +39,11 @@ #define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) /* Set mmap state to MEM space. */ #define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) /* Enable/disable write-combining. */ +enum pci_hotplug_event { + PCI_HOTPLUG_LINK_UP, + PCI_HOTPLUG_LINK_DOWN, + PCI_HOTPLUG_CARD_PRESENT, + PCI_HOTPLUG_CARD_NOT_PRESENT, +}; + #endif /* _UAPILINUX_PCI_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 3add74ae2594..8be55ece2a21 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -132,6 +132,11 @@ #define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ #define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ #define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +/* Masks for dword-sized processing of Bus Number and Sec Latency Timer fields */ +#define PCI_PRIMARY_BUS_MASK 0x000000ff +#define PCI_SECONDARY_BUS_MASK 0x0000ff00 +#define PCI_SUBORDINATE_BUS_MASK 0x00ff0000 +#define PCI_SEC_LATENCY_TIMER_MASK 0xff000000 #define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ #define PCI_IO_LIMIT 0x1d #define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h index d6023a45a9d0..710f8842223f 100644 --- a/include/uapi/linux/pcitest.h +++ b/include/uapi/linux/pcitest.h @@ -22,6 +22,7 @@ #define PCITEST_GET_IRQTYPE _IO('P', 0x9) #define PCITEST_BARS _IO('P', 0xa) #define PCITEST_DOORBELL _IO('P', 0xb) +#define PCITEST_BAR_SUBRANGE _IO('P', 0xc) #define PCITEST_CLEAR_IRQ _IO('P', 0x10) #define PCITEST_IRQ_TYPE_UNDEFINED -1 diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 4c01e9d5ccc7..c2258b133939 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -1913,6 +1913,7 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs) irq_domain_free_irq_data(virq, nr_irqs); irq_free_descs(virq, nr_irqs); } +EXPORT_SYMBOL_GPL(irq_domain_free_irqs); static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq) { diff --git a/kernel/resource.c b/kernel/resource.c index e4e9bac12e6e..c5f03ac78e44 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -82,7 +82,7 @@ static struct resource *next_resource(struct resource *p, bool skip_children, #ifdef CONFIG_PROC_FS -enum { MAX_IORES_LEVEL = 5 }; +enum { MAX_IORES_LEVEL = 8 }; static void *r_start(struct seq_file *m, loff_t *pos) __acquires(resource_lock) diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c index 23aac6f97061..eecb776c33af 100644 --- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c +++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c @@ -70,6 +70,23 @@ TEST_F(pci_ep_bar, BAR_TEST) EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno); } +TEST_F(pci_ep_bar, BAR_SUBRANGE_TEST) +{ + int ret; + + pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO); + ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type"); + + pci_ep_ioctl(PCITEST_BAR_SUBRANGE, variant->barno); + if (ret == -ENODATA) + SKIP(return, "BAR is disabled"); + if (ret == -EBUSY) + SKIP(return, "BAR is test register space"); + if (ret == -EOPNOTSUPP) + SKIP(return, "Subrange map is not supported"); + EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno); +} + FIXTURE(pci_ep_basic) { int fd; |
