Merge tag 'pci-v7.2-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci

Pull pci updates from Bjorn Helgaas:
 "Enumeration:

   - Remove MPS/MRRS Kconfig settings (CONFIG_PCIE_BUS_*) that worked
     around a WiFi device defect; use a quirk or boot-time
     "pci=pcie_bus_tune_*" kernel parameter instead (Bjorn Helgaas)

   - Always lift 2.5GT/s restriction in PCIe failed link retraining to
     avoid clamping a link to 2.5GT/s after hot-plug changes the device
     (Maciej W. Rozycki)

   - Request bus reassignment when not probe-only to fix an enumeration
     regression on Marvell CN106XX and possibly other DT-based systems
     (Ratheesh Kannoth)

   - Fix procfs race between pci_proc_init() and pci_bus_add_device()
     that resulted in 'proc_dir_entry ... already registered' warnings
     and pointer corruption (Krzysztof Wilczyński)

   - Fix sysfs race that causes 'duplicate filename' warnings and boot
     panics by converting PCI resource files to static attributes
     (Krzysztof Wilczyński)

   - Expose sysfs 'resourceN_resize' attributes only on platforms with
     PCI mmap (Krzysztof Wilczyński)

   - Require CAP_SYS_ADMIN to write to sysfs 'resourceN_resize'
     attributes (Krzysztof Wilczyński)

   - Add security_locked_down(LOCKDOWN_PCI_ACCESS) to alpha PCI resource
     mmap path to match the generic path (Krzysztof Wilczyński)

   - Use kstrtobool() to parse the 'rom' attribute input to avoid the
     unexpected behavior of enabling the ROM when writing '0' with no
     trailing newline (Krzysztof Wilczyński)

  Resource management:

   - Improve resource claim logging for debuggability (Ilpo Järvinen)

   - Clean up several uses of const parameters (Ilpo Järvinen)

   - Check option ROM header signatures and lengths before accessing to
     avoid page faults and alignment faults (Guixin Liu)

  ASPM:

   - Don't reconfigure ASPM when entering low-power D-state; only do it
     when returning back to D0 (Carlos Bilbao)

  Power management:

   - During suspend, set power state to 'unknown' for all devices, not
     just those with drivers (Lukas Wunner)

   - Skip restoring Resizable BARs and VF Resizable BARs if device
     doesn't respond to config reads, to avoid invalid array accesses
     (Marco Nenciarini)

   - Add pci_suspend_retains_context() so drivers can tell whether
     devices retain internal state across suspend/resume, since some
     platforms reset devices on suspend; use this in nvme to avoid
     issues on Qcom RCs (Manivannan Sadhasivam)

  Power control:

   - Only to power on/off devices that actually support power control to
     avoid poking at incompatible devices mentioned in DT (Manivannan
     Sadhasivam)

  Virtualization and resets:

   - Log device readiness timeouts as errors, not warnings, because the
     device is likely unusable in this case (Bjorn Helgaas)

   - Wait for device readiness after soft reset (D3hot ->
     D0uninitialized transition), when the device may respond with
     Request Retry Status (RRS) if it needs more time to initialize
     (Bjorn Helgaas)

   - Drop unnecessary retries when restoring BARs because resets should
     now already include all required delays (Lukas Wunner)

   - Avoid FLR for MediaTek MT7925 WiFi, where FLR fails after a VM
     terminates uncleanly (Jose Ignacio Tornos Martinez)

   - Avoid SBR for Qualcomm WCN6855/WCN7850 WiFi, SDX62/SDX65 modems,
     which seem not to support it correctly (Jose Ignacio Tornos
     Martinez)

  Peer-to-peer DMA:

   - Prevent P2PDMA as well as CPU access to non-mappable BARs, e.g.,
     s390 ISM BARs (Matt Evans)

   - Add Intel QAT, DSA, IAA devices to whitelist (Lukas Wunner)

  Endpoint framework:

   - Add endpoint controller APIs for use by function drivers to
     discover auxiliary blocks like DMA engines (Koichiro Den)

   - Remember DesignWare eDMA engine base/size and expose them via the
     EPC aux-resource API (Koichiro Den)

   - Add endpoint embedded doorbell fallback, used if MSI allocation
     fails (Koichiro Den)

   - Validate BAR index and remove dead BAR read in endpoint doorbell
     test (Carlos Bilbao)

   - Unwind MSI/MSI-X vectors if NTB initialization fails part-way
     through (Koichiro Den)

   - Cache sleepable pci_irq_vector() value at ISR setup to avoid
     calling it from hardirq context (Koichiro Den)

   - Call sleepable pci_epc_raise_irq() from a work item instead of
     atomic context, e.g., when setting bits in NTB peer doorbells in
     the ntb_peer_db_set() path (Koichiro Den)

   - Report 0-based vNTB doorbell vector to account for link event 0 and
     historically skipped slot 1 (Koichiro Den)

   - Prevent configfs writes to vNTB db_count and other values that are
     already in use after EPC attach (Koichiro Den)

   - Account for vNTB db_valid reserved slots (link event 0 and
     historically skipped slot 1) so they don't appear as valid
     doorbells (Koichiro Den)

   - Implement vNTB .db_vector_count()/mask() for doorbells so clients
     can use multiple vectors and avoid thundering herds (Koichiro Den)

   - Report 0-based NTB doorbell vector to account for link event 0 and
     historically skipped slot 1 (Koichiro Den)

   - Fix doorbell bitmask and IRQ vector handling to clear only
     specified bits, use the correct vector for non-contiguous Linux IRQ
     numbers, and validate incoming vectors (Koichiro Den)

   - Implement NTB .db_vector_count()/mask() for doorbells so clients
     can use multiple vectors (Koichiro Den)

  Native PCIe controller infrastructure:

   - Add pci_host_common_link_train_delay() for the mandatory delay
     after > 5GT/s Link training completes and use it for cadence HPA,
     j721e, LGA; dwc; aardvark, mediatek-gen3, rzg3s (Hans Zhang)

   - Protect root bus removal with rescan lock in altera, brcmstb,
     cadence, dwc, iproc, mediatek, plda, rockchip to prevent
     use-after-free or crashes when racing with sysfs rescan or hotplug
     (Hans Zhang)

   - Add pci_host_common_parse_ports() for use by any native driver to
     parse Root Port properties (per-Link features like width, speed,
     PHY, power and reset control, etc should be described in Root Port
     stanzas, not the host bridge; currently only reset GPIOs
     implemented) (Sherry Sun)

  New native PCIe controller drivers:

   - Add DT binding and driver for UltraRISC DP1000 PCIe controller
     (Xincheng Zhang, Jia Wang)

  Altera PCIe controller driver:

   - Do not dispose of the parent IRQ mapping, which belongs to the
     parent interrupt controller (Mahesh Vaidya)

   - Fix chained IRQ handler ordering issue and resource leaks on probe
     failure (Mahesh Vaidya)

  AMD MDB PCIe controller driver:

   - Assert PERST# on shutdown so any connected Endpoints are held in
     reset during shutdown (Sai Krishna Musham)

  Amlogic Meson PCIe controller driver:

   - Propagate devm_add_action_or_reset() failure to fix probe error
     path (Shuvam Pandey)

   - Add .remove() callback to deinitialize the host bridge and power
     off the PHY (Shuvam Pandey)

  Broadcom iProc PCIe controller driver:

   - Restore .map_irq() assignment; its removal broke INTx on the iproc
     platform bus driver (Mark Tomlinson)

  Broadcom STB PCIe controller driver:

   - No change, but products using certain WiFi devices may be affected
     by removal of CONFIG_PCIE_BUS_* (see above)

  Freescale i.MX6 PCIe controller driver:

   - Move IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling into the core reset
     functions (Richard Zhu)

   - Assert PERST# before enabling regulators to ensure that even if
     power is enabled, endpoint stays inactive until REFCLK is stable
     (Sherry Sun)

   - Parse reset properties in Root Port nodes (falling back to host
     bridge) to help support Key E connectors and the pwrctrl framework
     (Sherry Sun)

   - Configure i.MX95 REF_USE_PAD before PHY reset (Richard Zhu)

   - Assert i.MX95 ref_clk_en after reference clock stabilizes (Richard
     Zhu)

   - Integrate new pwrctrl API for DTs with Root Port-level power
     supplies (Sherry Sun)

  Intel Gateway PCIe controller driver:

   - Enable clock before PHY init for correct ordering (Florian Eckert)

   - Add .start_link() callback so the driver works again (Florian
     Eckert)

   - Stop overwriting the ATU base address discovered by
     dw_pcie_get_resources() (Florian Eckert)

   - Add DT 'atu' region since this is hardware-specific, and fall back
     to driver default if lacking (Florian Eckert)

  Loongson PCIe controller driver:

   - Ignore downstream devices only on internal bridges to avoid
     Loongson hardware issue (Rong Zhang)

   - Quirk old Loongson-3C6000 bridges that advertise incorrect
     supported link speeds (Ziyao Li)

  Marvell MVEBU PCIe controller driver:

   - Use fixed-width interrupt masks to avoid truncation in 64-bit
     builds (Rosen Penev)

  MediaTek PCIe controller driver:

   - Use FIELD_PREP() to fix incorrect operator precedence in
     PCIE_FTS_NUM_L0 (Li RongQing)

   - Fix IRQ domain leak when port fails to enable (Manivannan
     Sadhasivam)

   - Use actual physical address for MSI message address instead of
     virt_to_phys() (Manivannan Sadhasivam)

   - Add EcoNet EN7528 to DT binding (Caleb James DeLisle)

  MediaTek PCIe Gen3 controller driver:

   - Deassert PCIE_PHY_RSTB so REFCLK is stable for at least 100ms
     (PCIE_T_PVPERL_MS) before deasserting PERST# (Jian Yang)

   - Add .shutdown() to assert PERST# before powering down device (Jian
     Yang)

   - Do full device power down on removal, including asserting PERST#,
     when removing driver (Chen-Yu Tsai)

   - Fix a 'failed to create pwrctrl devices' error message that was
     inadvertently skipped (Chen-Yu Tsai)

  NVIDIA Tegra194 PCIe controller driver:

   - Program the DesignWare PORT_AFR L1 entrance latency based on the
     'aspm-l1-entry-delay-ns' DT property (Manikanta Maddireddy)

  Qualcomm PCIe controller driver:

   - Add Eliza SoC compatible in DT binding (Krishna Chaitanya Chundru)

   - Set max OPP during resume so DBI register accesses don't fail with
     NoC errors (Qiang Yu)

   - Add pci_host_common_d3cold_possible() to determine whether
     downstream devices are already in D3hot and wakeup-enabled devices
     are capable of generating PME from D3cold (Krishna Chaitanya
     Chundru)

   - Add .get_ltssm() callback to get the LTSSM status without DBI,
     since DBI may be inaccessible after PME_Turn_Off (Krishna Chaitanya
     Chundru)

   - Power down PHY via PARF_PHY_CTRL before disabling rails/clocks to
     avoid power leakage (Krishna Chaitanya Chundru)

   - Decide whether suspend should put the link in L2 and power down
     using pci_host_common_d3cold_possible() instead of checking whether
     ASPM L1 is enabled (Krishna Chaitanya Chundru)

   - Add qcom D3cold support to tear down interconnect bandwidth and OPP
     votes (Krishna Chaitanya Chundru)

   - Handle unsupported mixed PERST#/PHY DT configurations, e.g., PHY in
     RP node while PERST# is in the RC node, but warn about the DT issue
     (Qiang Yu)

   - Program T_POWER_ON based on DT 't-power-on-us' property in case
     hardware advertises incorrect values (Krishna Chaitanya Chundru)

   - Disable ASPM L0s for SA8775P (Shawn Guo)

   - Initialize DWC MSI lock for firmware-managed ECAM hosts, which
     don't use the dw_pcie_host_init() path that initializes the lock
     (Yadu M G)

  Renesas RZ/G3S PCIe controller driver:

   - Add RZ/V2N DT support (Lad Prabhakar)

  SOPHGO PCIe controller driver:

   - Add 'dma-coherent' DT property for sg2042-pcie driver (Han Gao)

  Synopsys DesignWare PCIe controller driver:

   - Apply ECRC TLP Digest workaround for all DesignWare cores prior to
     5.10a, not just 4.90a and 5.00a (Manikanta Maddireddy)

   - Use common struct dw_pcie 'mode' rather than duplicating it in
     artpec6, dra7xx, dwc-pcie, and keembay driver structs (Hans Zhang)

   - Use DEFINE_SHOW_ATTRIBUTE for ltssm_status debugfs to reduce
     boilerplate and fix a seq_file memory leak by including a
     .release() callback (Hans Zhang)

   - Fix a signedness bug in fault injection test code (Dan Carpenter)

   - Avoid NULL pointer dereference when tearing down debugfs for
     controller that lacks RAS DES capability (Shuvam Pandey)

  MicroSemi Switchtec management driver:

   - Add Gen6 Device IDs (Ben Reed)

  Miscellaneous:

   - Remove unused gpio.h include from amd-mdb, designware-plat, fu740,
     visconti drivers (Andy Shevchenko)

   - Fix typos in documentation (josh ziegler)

   - Use FIELD_MODIFY() instead of open-coding it (Hans Zhang)"

* tag 'pci-v7.2-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (168 commits)
  PCI/sysfs: Use kstrtobool() to parse the ROM attribute input
  PCI/sysfs: Limit BAR resize attribute scope to platforms with PCI mmap
  PCI/sysfs: Remove pci_create_legacy_files() and pci_sysfs_init()
  PCI/sysfs: Convert legacy I/O and memory attributes to static definitions
  PCI/sysfs: Add __weak pci_legacy_has_sparse() helper
  alpha/PCI: Compute legacy size in pci_mmap_legacy_page_range()
  PCI: Add macros for legacy I/O and memory address space sizes
  PCI/sysfs: Remove pci_{create,remove}_sysfs_dev_files()
  alpha/PCI: Convert resource files to static attributes
  alpha/PCI: Add static PCI resource attribute macros
  alpha/PCI: Remove WARN from __pci_mmap_fits() and __legacy_mmap_fits()
  alpha/PCI: Fix __pci_mmap_fits() overflow for zero-length BARs
  alpha/PCI: Use PCI resource accessor macros
  alpha/PCI: Use BAR index in sysfs attr->private instead of resource pointer
  alpha/PCI: Add security_locked_down() check to pci_mmap_resource()
  PCI/sysfs: Limit pci_sysfs_init() late_initcall compile scope
  PCI/sysfs: Add stubs for pci_{create,remove}_sysfs_dev_files()
  PCI/sysfs: Warn about BAR resize failure in __resource_resize_store()
  PCI/sysfs: Convert PCI resource files to static attributes
  PCI/proc: Fix race between pci_proc_init() and pci_bus_add_device()
  ...
This commit is contained in:
Linus Torvalds
2026-06-24 12:48:43 -07:00
109 changed files with 3647 additions and 1316 deletions

View File

@@ -338,7 +338,7 @@ the PCI_IRQ_MSI and PCI_IRQ_MSIX flags will fail, so try to always
specify PCI_IRQ_INTX as well.
Drivers that have different interrupt handlers for MSI/MSI-X and
legacy INTx should chose the right one based on the msi_enabled
legacy INTx should choose the right one based on the msi_enabled
and msix_enabled flags in the pci_dev structure after calling
pci_alloc_irq_vectors.

View File

@@ -97,7 +97,7 @@ register its service with the PCI Express Port Bus driver (see
section 5.2.1 & 5.2.2). It is important that a service driver
initializes the pcie_port_service_driver data structure, included in
header file /include/linux/pcieport_if.h, before calling these APIs.
Failure to do so will result an identity mismatch, which prevents
Failure to do so will result in an identity mismatch, which prevents
the PCI Express Port Bus driver from loading a service driver.
pcie_port_service_register

View File

@@ -66,16 +66,34 @@ properties:
- const: dma
reset-gpio:
deprecated: true
description: Should specify the GPIO for controlling the PCI bus device
reset signal. It's not polarity aware and defaults to active-low reset
sequence (L=reset state, H=operation state) (optional required).
This property is deprecated, instead of referencing this property from the
host bridge node, use the reset-gpios property from the root port node.
reset-gpio-active-high:
deprecated: true
description: If present then the reset sequence using the GPIO
specified in the "reset-gpio" property is reversed (H=reset state,
L=operation state) (optional required).
This property is deprecated along with the reset-gpio property above, use
the reset-gpios property from the root port node.
type: boolean
pcie@0:
description:
Describe the i.MX6 PCIe Root Port.
type: object
$ref: /schemas/pci/pci-pci-bridge.yaml#
properties:
reg:
maxItems: 1
unevaluatedProperties: false
required:
- compatible
- reg
@@ -236,6 +254,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/imx6qdl-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
pcie: pcie@1ffc000 {
@@ -262,5 +281,18 @@ examples:
<&clks IMX6QDL_CLK_LVDS1_GATE>,
<&clks IMX6QDL_CLK_PCIE_REF_125M>;
clock-names = "pcie", "pcie_bus", "pcie_phy";
pcie_port0: pcie@0 {
compatible = "pciclass,0604";
device_type = "pci";
reg = <0x0 0x0 0x0 0x0 0x0>;
bus-range = <0x01 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
};
};
...

View File

@@ -27,16 +27,20 @@ properties:
- const: snps,dw-pcie
reg:
minItems: 3
items:
- description: Controller control and status registers.
- description: PCIe configuration registers.
- description: Controller application registers.
- description: Internal Address Translation Unit (iATU) registers.
reg-names:
minItems: 3
items:
- const: dbi
- const: config
- const: app
- const: atu
ranges:
maxItems: 1
@@ -95,8 +99,9 @@ examples:
#size-cells = <2>;
reg = <0xd0e00000 0x1000>,
<0xd2000000 0x800000>,
<0xd0a41000 0x1000>;
reg-names = "dbi", "config", "app";
<0xd0a41000 0x1000>,
<0xd0ec0000 0x1000>;
reg-names = "dbi", "config", "app", "atu";
linux,pci-domain = <0>;
max-link-speed = <4>;
bus-range = <0x00 0x08>;

View File

@@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- airoha,an7583-pcie
- econet,en7528-pcie
- mediatek,mt2712-pcie
- mediatek,mt7622-pcie
- mediatek,mt7629-pcie
@@ -226,6 +227,31 @@ allOf:
mediatek,pbus-csr: false
- if:
properties:
compatible:
contains:
const: econet,en7528-pcie
then:
properties:
clocks:
maxItems: 1
clock-names:
maxItems: 1
resets: false
reset-names: false
power-domains: false
mediatek,pbus-csr: false
required:
- phys
- phy-names
unevaluatedProperties: false
examples:

View File

@@ -20,6 +20,7 @@ properties:
- const: qcom,pcie-sm8550
- items:
- enum:
- qcom,eliza-pcie
- qcom,kaanapali-pcie
- qcom,sar2130p-pcie
- qcom,pcie-sm8650
@@ -91,6 +92,55 @@ required:
allOf:
- $ref: qcom,pcie-common.yaml#
- if:
properties:
compatible:
contains:
const: qcom,eliza-pcie
then:
properties:
reg:
minItems: 6
reg-names:
minItems: 6
- if:
properties:
compatible:
contains:
const: qcom,eliza-pcie
then:
properties:
clocks:
minItems: 8
maxItems: 8
clock-names:
minItems: 8
maxItems: 8
- if:
properties:
compatible:
contains:
const: qcom,eliza-pcie
then:
properties:
interrupts:
minItems: 9
interrupt-names:
minItems: 9
- if:
properties:
compatible:
contains:
const: qcom,eliza-pcie
then:
properties:
resets:
minItems: 2
reset-names:
minItems: 2
unevaluatedProperties: false

View File

@@ -4,21 +4,27 @@
$id: http://devicetree.org/schemas/pci/renesas,r9a08g045-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3S PCIe host controller
title: Renesas RZ/G3S PCIe host controller (and similar SoCs)
maintainers:
- Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
description:
Renesas RZ/G3{E,S} PCIe host controllers comply with PCIe
Base Specification 4.0 and support up to 5 GT/s (Gen2) for RZ/G3S and
up to 8 GT/s (Gen3) for RZ/G3E.
description: |
PCIe host controller found in Renesas RZ/G3S and similar SoCs complies
with PCIe Base Specification 4.0 and supports different link speeds
depending on the SoC variant:
- Gen2 (5 GT/s): RZ/G3S
- Gen3 (8 GT/s): RZ/G3E, RZ/V2N
properties:
compatible:
enum:
- renesas,r9a08g045-pcie # RZ/G3S
- renesas,r9a09g047-pcie # RZ/G3E
oneOf:
- enum:
- renesas,r9a08g045-pcie # RZ/G3S
- renesas,r9a09g047-pcie # RZ/G3E
- items:
- const: renesas,r9a09g056-pcie # RZ/V2N
- const: renesas,r9a09g047-pcie
reg:
maxItems: 1
@@ -152,6 +158,7 @@ patternProperties:
enum:
- 0x0033
- 0x0039
- 0x003b
clocks:
items:

View File

@@ -30,6 +30,8 @@ properties:
device-id:
const: 0x2042
dma-coherent: true
msi-parent: true
allOf:
@@ -60,5 +62,6 @@ examples:
vendor-id = <0x1f1c>;
device-id = <0x2042>;
cdns,no-bar-match-nbits = <48>;
dma-coherent;
msi-parent = <&msi>;
};

View File

@@ -0,0 +1,93 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/ultrarisc,dp1000-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: UltraRISC DP1000 PCIe Host Controller
description:
UltraRISC DP1000 SoC PCIe host controller is based on the DesignWare PCIe IP.
maintainers:
- Xincheng Zhang <zhangxincheng@ultrarisc.com>
- Jia Wang <wangjia@ultrarisc.com>
allOf:
- $ref: /schemas/pci/snps,dw-pcie.yaml#
properties:
compatible:
const: ultrarisc,dp1000-pcie
reg:
items:
- description: Data Bus Interface (DBI) registers.
- description: PCIe configuration space region.
reg-names:
items:
- const: dbi
- const: config
num-lanes:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [4, 16]
description: Number of lanes to use.
interrupts:
items:
- description: MSI interrupt
- description: Legacy INTA interrupt
- description: Legacy INTB interrupt
- description: Legacy INTC interrupt
- description: Legacy INTD interrupt
interrupt-names:
items:
- const: msi
- const: inta
- const: intb
- const: intc
- const: intd
required:
- compatible
- reg
- reg-names
- interrupts
- interrupt-names
unevaluatedProperties: false
examples:
- |
soc {
#address-cells = <2>;
#size-cells = <2>;
pcie@21000000 {
compatible = "ultrarisc,dp1000-pcie";
reg = <0x0 0x21000000 0x0 0x01000000>,
<0x0 0x4fff0000 0x0 0x00010000>;
reg-names = "dbi", "config";
ranges = <0x81000000 0x0 0x4fbf0000 0x0 0x4fbf0000 0x0 0x00400000>,
<0x82000000 0x0 0x40000000 0x0 0x40000000 0x0 0x0fbf0000>,
<0xc3000000 0x40 0x00000000 0x40 0x00000000 0xd 0x00000000>;
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
device_type = "pci";
dma-coherent;
bus-range = <0x0 0xff>;
num-lanes = <16>;
interrupt-parent = <&plic>;
interrupts = <43>, <44>, <45>, <46>, <47>;
interrupt-names = "msi", "inta", "intb", "intc", "intd";
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0x0 0x0 0x0 0x1 &plic 44>,
<0x0 0x0 0x0 0x2 &plic 45>,
<0x0 0x0 0x0 0x3 &plic 46>,
<0x0 0x0 0x0 0x4 &plic 47>;
};
};

View File

@@ -21050,6 +21050,14 @@ S: Maintained
F: Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
F: drivers/pci/controller/plda/pcie-starfive.c
PCIE DRIVER FOR ULTRARISC DP1000
M: Xincheng Zhang <zhangxincheng@ultrarisc.com>
M: Jia Wang <wangjia@ultrarisc.com>
L: linux-pci@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/pci/ultrarisc,dp1000-pcie.yaml
F: drivers/pci/controller/dwc/pcie-ultrarisc.c
PCIE ENDPOINT DRIVER FOR QUALCOMM
M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-pci@vger.kernel.org

View File

@@ -84,8 +84,17 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_state);
extern void pci_adjust_legacy_attr(struct pci_bus *bus,
enum pci_mmap_state mmap_type);
extern bool pci_legacy_has_sparse(struct pci_bus *bus,
enum pci_mmap_state type);
#define HAVE_PCI_LEGACY 1
extern const struct attribute_group pci_dev_resource_attr_group;
extern const struct attribute_group pci_dev_resource_sparse_attr_group;
extern const struct attribute_group pci_dev_resource_dense_attr_group;
#define ARCH_PCI_DEV_GROUPS \
&pci_dev_resource_attr_group, \
&pci_dev_resource_sparse_attr_group, \
&pci_dev_resource_dense_attr_group,
#endif /* __ALPHA_PCI_H */

View File

@@ -11,8 +11,7 @@
*/
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/security.h>
#include <linux/pci.h>
static int hose_mmap_page_range(struct pci_controller *hose,
@@ -36,20 +35,18 @@ static int hose_mmap_page_range(struct pci_controller *hose,
static int __pci_mmap_fits(struct pci_dev *pdev, int num,
struct vm_area_struct *vma, int sparse)
{
resource_size_t len = pci_resource_len(pdev, num);
unsigned long nr, start, size;
int shift = sparse ? 5 : 0;
if (!len)
return 0;
nr = vma_pages(vma);
start = vma->vm_pgoff;
size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
size = ((len - 1) >> (PAGE_SHIFT - shift)) + 1;
if (start < size && size - start >= nr)
return 1;
WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d "
"(size 0x%08lx)\n",
current->comm, sparse ? " sparse" : "", start, start + nr,
pci_name(pdev), num, size);
return 0;
return start < size && size - start >= nr;
}
/**
@@ -68,26 +65,25 @@ static int pci_mmap_resource(struct kobject *kobj,
struct vm_area_struct *vma, int sparse)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
struct resource *res = attr->private;
int barno = (unsigned long)attr->private;
enum pci_mmap_state mmap_type;
struct pci_bus_region bar;
int i;
int ret;
for (i = 0; i < PCI_STD_NUM_BARS; i++)
if (res == &pdev->resource[i])
break;
if (i >= PCI_STD_NUM_BARS)
return -ENODEV;
ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
if (ret)
return ret;
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
if (pci_resource_is_mem(pdev, barno) &&
iomem_is_exclusive(pci_resource_start(pdev, barno)))
return -EINVAL;
if (!__pci_mmap_fits(pdev, i, vma, sparse))
if (!__pci_mmap_fits(pdev, barno, vma, sparse))
return -EINVAL;
pcibios_resource_to_bus(pdev->bus, &bar, res);
pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, barno));
vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
mmap_type = pci_resource_is_mem(pdev, barno) ? pci_mmap_mem : pci_mmap_io;
return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
}
@@ -106,34 +102,26 @@ static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj,
return pci_mmap_resource(kobj, attr, vma, 0);
}
/**
* pci_remove_resource_files - cleanup resource files
* @pdev: pci_dev to cleanup
*
* If we created resource files for @dev, remove them from sysfs and
* free their resources.
*/
void pci_remove_resource_files(struct pci_dev *pdev)
{
int i;
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
struct bin_attribute *res_attr;
res_attr = pdev->res_attr[i];
if (res_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
res_attr = pdev->res_attr_wc[i];
if (res_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
}
#define __pci_dev_resource_attr(_bar, _name, _suffix, _mmap) \
static const struct bin_attribute \
pci_dev_resource##_bar##_suffix##_attr = { \
.attr = { .name = __stringify(_name), .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.mmap = (_mmap), \
}
#define pci_dev_resource_attr(_bar) \
__pci_dev_resource_attr(_bar, resource##_bar,, \
pci_mmap_resource_dense)
#define pci_dev_resource_sparse_attr(_bar) \
__pci_dev_resource_attr(_bar, resource##_bar##_sparse, _sparse, \
pci_mmap_resource_sparse)
#define pci_dev_resource_dense_attr(_bar) \
__pci_dev_resource_attr(_bar, resource##_bar##_dense, _dense, \
pci_mmap_resource_dense)
static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
{
struct pci_bus_region bar;
@@ -141,7 +129,7 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
long dense_offset;
unsigned long sparse_size;
pcibios_resource_to_bus(pdev->bus, &bar, &pdev->resource[num]);
pcibios_resource_to_bus(pdev->bus, &bar, pci_resource_n(pdev, num));
/* All core logic chips have 4G sparse address space, except
CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
@@ -153,109 +141,10 @@ static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
return bar.end < sparse_size;
}
static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
char *suffix, struct bin_attribute *res_attr,
unsigned long sparse)
{
size_t size = pci_resource_len(pdev, num);
sprintf(name, "resource%d%s", num, suffix);
res_attr->mmap = sparse ? pci_mmap_resource_sparse :
pci_mmap_resource_dense;
res_attr->attr.name = name;
res_attr->attr.mode = S_IRUSR | S_IWUSR;
res_attr->size = sparse ? size << 5 : size;
res_attr->private = &pdev->resource[num];
return sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
}
static int pci_create_attr(struct pci_dev *pdev, int num)
{
/* allocate attribute structure, piggyback attribute name */
int retval, nlen1, nlen2 = 0, res_count = 1;
unsigned long sparse_base, dense_base;
struct bin_attribute *attr;
struct pci_controller *hose = pdev->sysdata;
char *suffix, *attr_name;
suffix = ""; /* Assume bwx machine, normal resourceN files. */
nlen1 = 10;
if (pdev->resource[num].flags & IORESOURCE_MEM) {
sparse_base = hose->sparse_mem_base;
dense_base = hose->dense_mem_base;
if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) {
sparse_base = 0;
suffix = "_dense";
nlen1 = 16; /* resourceN_dense */
}
} else {
sparse_base = hose->sparse_io_base;
dense_base = hose->dense_io_base;
}
if (sparse_base) {
suffix = "_sparse";
nlen1 = 17;
if (dense_base) {
nlen2 = 16; /* resourceN_dense */
res_count = 2;
}
}
attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC);
if (!attr)
return -ENOMEM;
/* Create bwx, sparse or single dense file */
attr_name = (char *)(attr + res_count);
pdev->res_attr[num] = attr;
retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr,
sparse_base);
if (retval || res_count == 1)
return retval;
/* Create dense file */
attr_name += nlen1;
attr++;
pdev->res_attr_wc[num] = attr;
return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0);
}
/**
* pci_create_resource_files - create resource files in sysfs for @pdev
* @pdev: pci_dev in question
*
* Walk the resources in @dev creating files for each resource available.
*
* Return: %0 on success, or negative error code
*/
int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
int retval;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
retval = pci_create_attr(pdev, i);
if (retval) {
pci_remove_resource_files(pdev);
return retval;
}
}
return 0;
}
/* Legacy I/O bus mapping stuff. */
static int __legacy_mmap_fits(struct pci_controller *hose,
struct vm_area_struct *vma,
unsigned long res_size, int sparse)
static int __legacy_mmap_fits(struct vm_area_struct *vma,
unsigned long res_size)
{
unsigned long nr, start, size;
@@ -263,13 +152,7 @@ static int __legacy_mmap_fits(struct pci_controller *hose,
start = vma->vm_pgoff;
size = ((res_size - 1) >> PAGE_SHIFT) + 1;
if (start < size && size - start >= nr)
return 1;
WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d "
"(size 0x%08lx)\n",
current->comm, sparse ? " sparse" : "", start, start + nr,
hose->index, size);
return 0;
return start < size && size - start >= nr;
}
static inline int has_sparse(struct pci_controller *hose,
@@ -290,36 +173,22 @@ int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
int sparse = has_sparse(hose, mmap_type);
unsigned long res_size;
res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size :
bus->legacy_io->size;
if (!__legacy_mmap_fits(hose, vma, res_size, sparse))
res_size = (mmap_type == pci_mmap_mem) ? PCI_LEGACY_MEM_SIZE :
PCI_LEGACY_IO_SIZE;
if (sparse)
res_size <<= 5;
if (!__legacy_mmap_fits(vma, res_size))
return -EINVAL;
return hose_mmap_page_range(hose, vma, mmap_type, sparse);
}
/**
* pci_adjust_legacy_attr - adjustment of legacy file attributes
* @bus: bus to create files under
* @mmap_type: I/O port or memory
*
* Adjust file name and size for sparse mappings.
*/
void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type)
bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type)
{
struct pci_controller *hose = bus->sysdata;
if (!has_sparse(hose, mmap_type))
return;
if (mmap_type == pci_mmap_mem) {
bus->legacy_mem->attr.name = "legacy_mem_sparse";
bus->legacy_mem->size <<= 5;
} else {
bus->legacy_io->attr.name = "legacy_io_sparse";
bus->legacy_io->size <<= 5;
}
return;
return has_sparse(hose, type);
}
/* Legacy I/O bus read/write functions */
@@ -370,3 +239,166 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
}
return -EINVAL;
}
pci_dev_resource_attr(0);
pci_dev_resource_attr(1);
pci_dev_resource_attr(2);
pci_dev_resource_attr(3);
pci_dev_resource_attr(4);
pci_dev_resource_attr(5);
pci_dev_resource_sparse_attr(0);
pci_dev_resource_sparse_attr(1);
pci_dev_resource_sparse_attr(2);
pci_dev_resource_sparse_attr(3);
pci_dev_resource_sparse_attr(4);
pci_dev_resource_sparse_attr(5);
pci_dev_resource_dense_attr(0);
pci_dev_resource_dense_attr(1);
pci_dev_resource_dense_attr(2);
pci_dev_resource_dense_attr(3);
pci_dev_resource_dense_attr(4);
pci_dev_resource_dense_attr(5);
static inline enum pci_mmap_state pci_bar_mmap_type(struct pci_dev *pdev,
int bar)
{
return pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
}
static inline umode_t __pci_resource_attr_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
if (!pci_resource_len(pdev, bar))
return 0;
return a->attr.mode;
}
static umode_t pci_dev_resource_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
struct pci_controller *hose = pdev->sysdata;
if (has_sparse(hose, pci_bar_mmap_type(pdev, bar)))
return 0;
return __pci_resource_attr_is_visible(kobj, a, bar);
}
static umode_t pci_dev_resource_sparse_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
struct pci_controller *hose = pdev->sysdata;
enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
if (!has_sparse(hose, type))
return 0;
if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
return 0;
return __pci_resource_attr_is_visible(kobj, a, bar);
}
static umode_t pci_dev_resource_dense_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
struct pci_controller *hose = pdev->sysdata;
enum pci_mmap_state type = pci_bar_mmap_type(pdev, bar);
unsigned long dense_base;
if (!has_sparse(hose, type))
return 0;
if (type == pci_mmap_mem && !sparse_mem_mmap_fits(pdev, bar))
return __pci_resource_attr_is_visible(kobj, a, bar);
dense_base = (type == pci_mmap_mem) ? hose->dense_mem_base :
hose->dense_io_base;
if (!dense_base)
return 0;
return __pci_resource_attr_is_visible(kobj, a, bar);
}
static inline size_t __pci_dev_resource_bin_size(struct kobject *kobj,
int bar, bool sparse)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
size_t size = pci_resource_len(pdev, bar);
return sparse ? size << 5 : size;
}
static size_t pci_dev_resource_bin_size(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
return __pci_dev_resource_bin_size(kobj, bar, false);
}
static size_t pci_dev_resource_sparse_bin_size(struct kobject *kobj,
const struct bin_attribute *a,
int bar)
{
return __pci_dev_resource_bin_size(kobj, bar, true);
}
static const struct bin_attribute *const pci_dev_resource_attrs[] = {
&pci_dev_resource0_attr,
&pci_dev_resource1_attr,
&pci_dev_resource2_attr,
&pci_dev_resource3_attr,
&pci_dev_resource4_attr,
&pci_dev_resource5_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_sparse_attrs[] = {
&pci_dev_resource0_sparse_attr,
&pci_dev_resource1_sparse_attr,
&pci_dev_resource2_sparse_attr,
&pci_dev_resource3_sparse_attr,
&pci_dev_resource4_sparse_attr,
&pci_dev_resource5_sparse_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_dense_attrs[] = {
&pci_dev_resource0_dense_attr,
&pci_dev_resource1_dense_attr,
&pci_dev_resource2_dense_attr,
&pci_dev_resource3_dense_attr,
&pci_dev_resource4_dense_attr,
&pci_dev_resource5_dense_attr,
NULL,
};
const struct attribute_group pci_dev_resource_attr_group = {
.bin_attrs = pci_dev_resource_attrs,
.is_bin_visible = pci_dev_resource_is_visible,
.bin_size = pci_dev_resource_bin_size,
};
const struct attribute_group pci_dev_resource_sparse_attr_group = {
.bin_attrs = pci_dev_resource_sparse_attrs,
.is_bin_visible = pci_dev_resource_sparse_is_visible,
.bin_size = pci_dev_resource_sparse_bin_size,
};
const struct attribute_group pci_dev_resource_dense_attr_group = {
.bin_attrs = pci_dev_resource_dense_attrs,
.is_bin_visible = pci_dev_resource_dense_is_visible,
.bin_size = pci_dev_resource_bin_size,
};

View File

@@ -169,7 +169,7 @@ struct machdep_calls {
#ifdef CONFIG_PCI_IOV
void (*pcibios_fixup_sriov)(struct pci_dev *pdev);
resource_size_t (*pcibios_iov_resource_alignment)(struct pci_dev *, int resno);
resource_size_t (*pcibios_iov_resource_alignment)(const struct pci_dev *, int resno);
int (*pcibios_sriov_enable)(struct pci_dev *pdev, u16 num_vfs);
int (*pcibios_sriov_disable)(struct pci_dev *pdev);
#endif /* CONFIG_PCI_IOV */

View File

@@ -82,8 +82,6 @@ extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
struct vm_area_struct *vma,
enum pci_mmap_state mmap_state);
extern void pci_adjust_legacy_attr(struct pci_bus *bus,
enum pci_mmap_state mmap_type);
#define HAVE_PCI_LEGACY 1
extern void pcibios_claim_one_bus(struct pci_bus *b);

View File

@@ -254,7 +254,7 @@ resource_size_t pcibios_default_alignment(void)
}
#ifdef CONFIG_PCI_IOV
resource_size_t pcibios_iov_resource_alignment(struct pci_dev *pdev, int resno)
resource_size_t pcibios_iov_resource_alignment(const struct pci_dev *pdev, int resno)
{
if (ppc_md.pcibios_iov_resource_alignment)
return ppc_md.pcibios_iov_resource_alignment(pdev, resno);

View File

@@ -244,8 +244,8 @@ void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev)
}
}
resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev,
int resno)
resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev,
int resno)
{
resource_size_t align = pci_iov_resource_size(pdev, resno);
struct pnv_phb *phb = pci_bus_to_pnvhb(pdev->bus);

View File

@@ -251,13 +251,14 @@ struct pnv_iov_data {
struct resource holes[PCI_SRIOV_NUM_BARS];
};
static inline struct pnv_iov_data *pnv_iov_get(struct pci_dev *pdev)
static inline struct pnv_iov_data *pnv_iov_get(const struct pci_dev *pdev)
{
return pdev->dev.archdata.iov_data;
}
void pnv_pci_ioda_fixup_iov(struct pci_dev *pdev);
resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, int resno);
resource_size_t pnv_pci_iov_resource_alignment(const struct pci_dev *pdev,
int resno);
int pnv_pcibios_sriov_enable(struct pci_dev *pdev, u16 num_vfs);
int pnv_pcibios_sriov_disable(struct pci_dev *pdev);

View File

@@ -658,7 +658,8 @@ enum get_iov_fw_value_index {
WDW_SIZE = 3 /* Get Window Size */
};
static resource_size_t pseries_get_iov_fw_value(struct pci_dev *dev, int resno,
static resource_size_t pseries_get_iov_fw_value(const struct pci_dev *dev,
int resno,
enum get_iov_fw_value_index value)
{
const int *indexes;
@@ -788,7 +789,7 @@ static void pseries_pci_fixup_iov_resources(struct pci_dev *pdev)
pseries_disable_sriov_resources(pdev);
}
static resource_size_t pseries_pci_iov_resource_alignment(struct pci_dev *pdev,
static resource_size_t pseries_pci_iov_resource_alignment(const struct pci_dev *pdev,
int resno)
{
const __be32 *reg;

View File

@@ -29,15 +29,10 @@
#define ADF_4XXX_DEVICE_NAME "4xxx"
#define ADF_420XX_DEVICE_NAME "420xx"
#define ADF_6XXX_DEVICE_NAME "6xxx"
#define PCI_DEVICE_ID_INTEL_QAT_4XXX 0x4940
#define PCI_DEVICE_ID_INTEL_QAT_4XXXIOV 0x4941
#define PCI_DEVICE_ID_INTEL_QAT_401XX 0x4942
#define PCI_DEVICE_ID_INTEL_QAT_401XXIOV 0x4943
#define PCI_DEVICE_ID_INTEL_QAT_402XX 0x4944
#define PCI_DEVICE_ID_INTEL_QAT_402XXIOV 0x4945
#define PCI_DEVICE_ID_INTEL_QAT_420XX 0x4946
#define PCI_DEVICE_ID_INTEL_QAT_420XXIOV 0x4947
#define PCI_DEVICE_ID_INTEL_QAT_6XXX 0x4948
#define PCI_DEVICE_ID_INTEL_QAT_6XXX_IOV 0x4949
#define ADF_DEVICE_FUSECTL_OFFSET 0x40

View File

@@ -10,9 +10,6 @@
#endif
/* PCI Config */
#define PCI_DEVICE_ID_INTEL_DSA_GNRD 0x11fb
#define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212
#define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216
#define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d
#define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d

View File

@@ -1100,7 +1100,6 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA);
addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET);
bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
@@ -1108,6 +1107,11 @@ static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
if (bar < BAR_0 || bar >= PCI_STD_NUM_BARS) {
dev_err(dev, "BAR %d reported by endpoint out of range [0, %u]\n",
bar, PCI_STD_NUM_BARS - 1);
return -ERANGE;
}
writel(data, test->bar[bar] + addr);

View File

@@ -6,6 +6,7 @@
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -43,6 +44,18 @@
#define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4)
#define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4)
/*
* Legacy doorbell slot layout when paired with pci-epf-*ntb:
*
* slot 0 : reserved for link events
* slot 1 : unused (historical extra offset)
* slot 2 : DB#0
* slot 3 : DB#1
* ...
*
* Thus, NTB_EPF_MIN_DB_COUNT=3 means that we at least create vectors for
* doorbells DB#0 and DB#1.
*/
#define NTB_EPF_MIN_DB_COUNT 3
#define NTB_EPF_MAX_DB_COUNT 31
@@ -69,8 +82,21 @@ enum epf_ntb_bar {
NTB_BAR_NUM,
};
enum epf_irq_slot {
EPF_IRQ_LINK = 0,
EPF_IRQ_RESERVED_DB, /* Historically skipped slot */
EPF_IRQ_DB_START,
};
#define NTB_EPF_MAX_MW_COUNT (NTB_BAR_NUM - BAR_MW1)
struct ntb_epf_dev;
struct ntb_epf_irq_ctx {
struct ntb_epf_dev *ndev;
unsigned int irq_no;
};
struct ntb_epf_dev {
struct ntb_dev ntb;
struct device *dev;
@@ -90,8 +116,9 @@ struct ntb_epf_dev {
unsigned int self_spad;
unsigned int peer_spad;
int db_val;
atomic64_t db_val;
u64 db_valid_mask;
struct ntb_epf_irq_ctx irq_ctx[NTB_EPF_MAX_DB_COUNT + 1];
};
#define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb)
@@ -315,16 +342,29 @@ static int ntb_epf_link_disable(struct ntb_dev *ntb)
static irqreturn_t ntb_epf_vec_isr(int irq, void *dev)
{
struct ntb_epf_dev *ndev = dev;
int irq_no;
struct ntb_epf_irq_ctx *ctx = dev;
struct ntb_epf_dev *ndev = ctx->ndev;
unsigned int db_vector;
unsigned int irq_no = ctx->irq_no;
irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0);
ndev->db_val = irq_no + 1;
if (irq_no == 0)
if (irq_no == EPF_IRQ_LINK) {
ntb_link_event(&ndev->ntb);
else
ntb_db_event(&ndev->ntb, irq_no);
} else if (irq_no == EPF_IRQ_RESERVED_DB) {
dev_warn_ratelimited(ndev->dev,
"Unexpected reserved doorbell slot IRQ received\n");
} else {
db_vector = irq_no - EPF_IRQ_DB_START;
if (ndev->db_count < NTB_EPF_MIN_DB_COUNT ||
db_vector >= ndev->db_count - 1) {
dev_warn_ratelimited(ndev->dev,
"Unexpected doorbell vector %u (db_count %u)\n",
db_vector, ndev->db_count);
return IRQ_HANDLED;
}
atomic64_or(BIT_ULL(db_vector), &ndev->db_val);
ntb_db_event(&ndev->ntb, db_vector);
}
return IRQ_HANDLED;
}
@@ -350,31 +390,30 @@ static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
argument &= ~MSIX_ENABLE;
}
ndev->db_count = irq - 1;
for (i = 0; i < irq; i++) {
ndev->irq_ctx[i].ndev = ndev;
ndev->irq_ctx[i].irq_no = i;
ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
0, "ntb_epf", ndev);
0, "ntb_epf", &ndev->irq_ctx[i]);
if (ret) {
dev_err(dev, "Failed to request irq\n");
goto err_request_irq;
goto err_free_irq;
}
}
ndev->db_count = irq - 1;
ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL,
argument | irq);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
goto err_configure_db;
goto err_free_irq;
}
return 0;
err_configure_db:
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), ndev);
err_request_irq:
err_free_irq:
while (i--)
free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
return ret;
@@ -395,6 +434,36 @@ static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb)
return ntb_ndev(ntb)->db_valid_mask;
}
static int ntb_epf_db_vector_count(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
unsigned int db_count = ndev->db_count;
/*
* db_count includes an extra skipped slot due to the legacy
* doorbell layout. Expose only the real doorbell vectors.
*/
if (db_count < NTB_EPF_MIN_DB_COUNT)
return 0;
return db_count - 1;
}
static u64 ntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector)
{
int nr_vec;
/*
* db_count includes one skipped slot in the legacy layout. Valid
* doorbell vectors are therefore [0 .. (db_count - 2)].
*/
nr_vec = ntb_epf_db_vector_count(ntb);
if (db_vector < 0 || db_vector >= nr_vec)
return 0;
return BIT_ULL(db_vector);
}
static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
{
return 0;
@@ -473,6 +542,14 @@ static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
/*
* ffs() returns a 1-based bit index (bit 0 -> 1).
*
* With slot 0 reserved for link events, DB#0 would naturally map to
* slot 1. Historically an extra +1 offset was added, so DB#0 maps to
* slot 2 and slot 1 remains unused. Keep this mapping for
* backward-compatibility.
*/
u32 interrupt_num = ffs(db_bits) + 1;
struct device *dev = ndev->dev;
u32 db_entry_size;
@@ -499,7 +576,7 @@ static u64 ntb_epf_db_read(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
return ndev->db_val;
return atomic64_read(&ndev->db_val);
}
static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
@@ -511,7 +588,7 @@ static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
ndev->db_val = 0;
atomic64_and(~db_bits, &ndev->db_val);
return 0;
}
@@ -521,6 +598,8 @@ static const struct ntb_dev_ops ntb_epf_ops = {
.spad_count = ntb_epf_spad_count,
.peer_mw_count = ntb_epf_peer_mw_count,
.db_valid_mask = ntb_epf_db_valid_mask,
.db_vector_count = ntb_epf_db_vector_count,
.db_vector_mask = ntb_epf_db_vector_mask,
.db_set_mask = ntb_epf_db_set_mask,
.mw_set_trans = ntb_epf_mw_set_trans,
.mw_clear_trans = ntb_epf_mw_clear_trans,
@@ -552,6 +631,12 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
struct device *dev = ndev->dev;
int ret;
ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
return -EINVAL;
}
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
NTB_EPF_MAX_DB_COUNT + 1);
@@ -560,15 +645,13 @@ static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
return ret;
}
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
/*
* ndev->db_count includes an extra skipped slot due to the legacy
* doorbell layout, hence -1.
*/
ndev->db_valid_mask = BIT_ULL(ndev->db_count - 1) - 1;
ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
if (ndev->mw_count > NTB_EPF_MAX_MW_COUNT) {
dev_err(dev, "Unsupported MW count: %u\n", ndev->mw_count);
return -EINVAL;
}
return 0;
}
@@ -662,7 +745,7 @@ static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1);
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), ndev);
free_irq(pci_irq_vector(pdev, i), &ndev->irq_ctx[i]);
pci_free_irq_vectors(pdev);
}

View File

@@ -3950,6 +3950,7 @@ static int nvme_suspend(struct device *dev)
* use host managed nvme power settings for lowest idle power if
* possible. This should have quicker resume latency than a full device
* shutdown. But if the firmware is involved after the suspend or the
* platform has any limitation in waking from low power states or the
* device does not support any non-default power states, shut down the
* device fully.
*
@@ -3958,7 +3959,7 @@ static int nvme_suspend(struct device *dev)
* down, so as to allow the platform to achieve its minimum low-power
* state (which may not be possible if the link is up).
*/
if (pm_suspend_via_firmware() || !ctrl->npss ||
if (!pci_suspend_retains_context(pdev) || !ctrl->npss ||
!pcie_aspm_enabled(pdev) ||
(ndev->ctrl.quirks & NVME_QUIRK_SIMPLE_SUSPEND))
return nvme_disable_prepare_reset(ndev, true);

View File

@@ -251,63 +251,6 @@ config PCI_DYNAMIC_OF_NODES
Once this option is selected, the device tree nodes will be generated
for all PCI bridges.
choice
prompt "PCI Express hierarchy optimization setting"
default PCIE_BUS_DEFAULT
depends on EXPERT
help
MPS (Max Payload Size) and MRRS (Max Read Request Size) are PCIe
device parameters that affect performance and the ability to
support hotplug and peer-to-peer DMA.
The following choices set the MPS and MRRS optimization strategy
at compile-time. The choices are the same as those offered for
the kernel command-line parameter 'pci', i.e.,
'pci=pcie_bus_tune_off', 'pci=pcie_bus_safe',
'pci=pcie_bus_perf', and 'pci=pcie_bus_peer2peer'.
This is a compile-time setting and can be overridden by the above
command-line parameters. If unsure, choose PCIE_BUS_DEFAULT.
config PCIE_BUS_TUNE_OFF
bool "Tune Off"
help
Use the BIOS defaults; don't touch MPS at all. This is the same
as booting with 'pci=pcie_bus_tune_off'.
config PCIE_BUS_DEFAULT
bool "Default"
help
Default choice; ensure that the MPS matches upstream bridge.
config PCIE_BUS_SAFE
bool "Safe"
help
Use largest MPS that boot-time devices support. If you have a
closed system with no possibility of adding new devices, this
will use the largest MPS that's supported by all devices. This
is the same as booting with 'pci=pcie_bus_safe'.
config PCIE_BUS_PERFORMANCE
bool "Performance"
help
Use MPS and MRRS for best performance. Ensure that a given
device's MPS is no larger than its parent MPS, which allows us to
keep all switches/bridges to the max MPS supported by their
parent. This is the same as booting with 'pci=pcie_bus_perf'.
config PCIE_BUS_PEER2PEER
bool "Peer2peer"
help
Set MPS = 128 for all devices. MPS configuration effected by the
other options could cause the MPS on one root port to be
different than that of the MPS on another, which may cause
hot-added devices or peer-to-peer DMA to fail. Set MPS to the
smallest possible value (128B) system-wide to avoid these issues.
This is the same as booting with 'pci=pcie_bus_peer2peer'.
endchoice
config VGA_ARB
bool "VGA Arbitration" if EXPERT
default y

View File

@@ -354,7 +354,6 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_fixup_device(pci_fixup_final, dev);
if (pci_is_bridge(dev))
of_pci_make_dev_node(dev);
pci_create_sysfs_dev_files(dev);
pci_proc_attach_device(dev);
pci_bridge_d3_update(dev);

View File

@@ -206,6 +206,7 @@ static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie,
(pcie_get_link_speed(link_speed) == PCI_SPEED_UNKNOWN))
link_speed = 2;
pcie->cdns_pcie->max_link_speed = link_speed;
val = link_speed - 1;
ret = regmap_update_bits(syscon, offset, GENERATION_SEL_MASK, val);
if (ret)

View File

@@ -14,6 +14,7 @@
#include "pcie-cadence.h"
#include "pcie-cadence-host-common.h"
#include "../pci-host-common.h"
#define LINK_RETRAIN_TIMEOUT HZ
@@ -115,6 +116,9 @@ int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc,
if (!ret && rc->quirk_retrain_flag)
ret = cdns_pcie_retrain(pcie, pcie_link_up);
if (!ret)
pci_host_common_link_train_delay(pcie->max_link_speed);
return ret;
}
EXPORT_SYMBOL_GPL(cdns_pcie_host_start_link);

View File

@@ -15,6 +15,8 @@
#include "pcie-cadence.h"
#include "pcie-cadence-host-common.h"
#include "../pci-host-common.h"
#include "../../pci.h"
static u8 bar_aperture_mask[] = {
[RP_BAR0] = 0x3F,
@@ -304,6 +306,8 @@ int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc)
ret = cdns_pcie_host_wait_for_link(pcie, cdns_pcie_hpa_link_up);
if (ret)
dev_dbg(dev, "PCIe link never came up\n");
else
pci_host_common_link_train_delay(pcie->max_link_speed);
return ret;
}
@@ -313,6 +317,7 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
{
struct device *dev = rc->pcie.dev;
struct platform_device *pdev = to_platform_device(dev);
struct device_node *np = dev->of_node;
struct pci_host_bridge *bridge;
enum cdns_pcie_rp_bar bar;
struct cdns_pcie *pcie;
@@ -343,6 +348,9 @@ int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
rc->cfg_res = res;
}
if (pcie->max_link_speed < 1)
pcie->max_link_speed = of_pci_get_max_link_speed(np);
/* Put EROM Bar aperture to 0 */
cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_EROM, 0x0);

View File

@@ -13,6 +13,7 @@
#include "pcie-cadence.h"
#include "pcie-cadence-host-common.h"
#include "../../pci.h"
static u8 bar_aperture_mask[] = {
[RP_BAR0] = 0x1F,
@@ -365,8 +366,10 @@ void cdns_pcie_host_disable(struct cdns_pcie_rc *rc)
struct pci_host_bridge *bridge;
bridge = pci_host_bridge_from_priv(rc);
pci_lock_rescan_remove();
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
pci_unlock_rescan_remove();
cdns_pcie_host_deinit(rc);
cdns_pcie_host_link_disable(rc);
@@ -397,6 +400,9 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
rc->device_id = 0xffff;
of_property_read_u32(np, "device-id", &rc->device_id);
if (pcie->max_link_speed < 1)
pcie->max_link_speed = of_pci_get_max_link_speed(np);
pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
if (IS_ERR(pcie->reg_base)) {
dev_err(dev, "missing \"reg\"\n");

View File

@@ -14,6 +14,8 @@
#include <linux/phy/phy.h>
#include <linux/bitfield.h>
#include "../../pci.h"
/* High Performance Architecture (HPA) PCIe controller registers */
#define CDNS_PCIE_HPA_IP_REG_BANK 0x01000000
#define CDNS_PCIE_HPA_IP_CFG_CTRL_REG_BANK 0x01003C00
@@ -119,15 +121,15 @@
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r) (0x1008 + ((r) & 0x1F) * 0x0080)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(28, 24)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM \
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x0)
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MEM_RDWR)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO \
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x2)
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_IO_RDWR)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 \
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x4)
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG0_RDWR)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 \
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x5)
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_CFG1_RDWR)
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG \
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x10)
FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, PCIE_TLP_TYPE_MSG)
/* Region r Outbound PCIe Descriptor Register */
#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r) (0x100C + ((r) & 0x1F) * 0x0080)

View File

@@ -86,6 +86,7 @@ struct cdns_plat_pcie_of_data {
* @ops: Platform-specific ops to control various inputs from Cadence PCIe
* wrapper
* @cdns_pcie_reg_offsets: Register bank offsets for different SoC
* @max_link_speed: Maximum supported link speed
*/
struct cdns_pcie {
void __iomem *reg_base;
@@ -98,6 +99,7 @@ struct cdns_pcie {
struct device_link **link;
const struct cdns_pcie_ops *ops;
const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets;
int max_link_speed;
};
/**

View File

@@ -114,6 +114,7 @@ config PCI_IMX6_HOST
depends on PCI_MSI
select PCIE_DW_HOST
select PCI_IMX6
select PCI_PWRCTRL_GENERIC
help
Enables support for the PCIe controller in the i.MX SoCs to
work in Root Complex mode. The PCI controller on i.MX is based
@@ -560,4 +561,17 @@ config PCIE_VISCONTI_HOST
Say Y here if you want PCIe controller support on Toshiba Visconti SoC.
This driver supports TMPV7708 SoC.
config PCIE_ULTRARISC
tristate "UltraRISC PCIe host controller"
depends on ARCH_ULTRARISC || COMPILE_TEST
select PCIE_DW_HOST
select PCI_MSI
default y if ARCH_ULTRARISC
help
Enables support for the PCIe controller in the UltraRISC SoC.
This driver supports UR-DP1000 SoC.
By default, this symbol is enabled when ARCH_ULTRARISC is active,
requiring no further configuration on that platform.
endmenu

View File

@@ -39,6 +39,7 @@ obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
obj-$(CONFIG_PCIE_SPACEMIT_K1) += pcie-spacemit-k1.o
obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o
obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o
obj-$(CONFIG_PCIE_ULTRARISC) += pcie-ultrarisc.o
# The following drivers are for devices that use the generic ACPI
# pci_root.c driver but don't support standard ECAM config access.

View File

@@ -92,7 +92,6 @@ struct dra7xx_pcie {
struct phy **phy;
struct irq_domain *irq_domain;
struct clk *clk;
enum dw_pcie_device_mode mode;
};
struct dra7xx_pcie_of_data {
@@ -328,7 +327,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
dev_dbg(dev, "Link Request Reset\n");
if (reg & LINK_UP_EVT) {
if (dra7xx->mode == DW_PCIE_EP_TYPE)
if (dra7xx->pci->mode == DW_PCIE_EP_TYPE)
dw_pcie_ep_linkup(ep);
dev_dbg(dev, "Link-up state change\n");
}
@@ -828,7 +827,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
default:
dev_err(dev, "INVALID device type %d\n", mode);
}
dra7xx->mode = mode;
dra7xx->pci->mode = mode;
ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
@@ -841,7 +840,7 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
return 0;
err_deinit:
if (dra7xx->mode == DW_PCIE_RC_TYPE)
if (dra7xx->pci->mode == DW_PCIE_RC_TYPE)
dw_pcie_host_deinit(&dra7xx->pci->pp);
else
dw_pcie_ep_deinit(&dra7xx->pci->ep);
@@ -865,7 +864,7 @@ static int dra7xx_pcie_suspend(struct device *dev)
struct dw_pcie *pci = dra7xx->pci;
u32 val;
if (dra7xx->mode != DW_PCIE_RC_TYPE)
if (pci->mode != DW_PCIE_RC_TYPE)
return 0;
/* clear MSE */
@@ -882,7 +881,7 @@ static int dra7xx_pcie_resume(struct device *dev)
struct dw_pcie *pci = dra7xx->pci;
u32 val;
if (dra7xx->mode != DW_PCIE_RC_TYPE)
if (pci->mode != DW_PCIE_RC_TYPE)
return 0;
/* set MSE */

View File

@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -34,6 +35,7 @@
#include <linux/pm_runtime.h>
#include "../../pci.h"
#include "../pci-host-common.h"
#include "pcie-designware.h"
#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
@@ -137,6 +139,7 @@ struct imx_pcie_drvdata {
const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
const struct pci_epc_features *epc_features;
int (*select_ref_clk_src)(struct imx_pcie *pcie);
int (*init_phy)(struct imx_pcie *pcie);
int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
int (*core_reset)(struct imx_pcie *pcie, bool assert);
@@ -152,7 +155,6 @@ struct imx_lut_data {
struct imx_pcie {
struct dw_pcie *pci;
struct gpio_desc *reset_gpiod;
struct clk_bulk_data *clks;
int num_clks;
bool supports_clkreq;
@@ -168,6 +170,8 @@ struct imx_pcie {
u32 tx_swing_full;
u32 tx_swing_low;
struct regulator *vpcie;
struct regulator *vpcie_aux;
bool vpcie_aux_enabled;
struct regulator *vph;
void __iomem *phy_base;
@@ -247,10 +251,26 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie)
return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
}
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
{
bool ext = imx_pcie->enable_ext_refclk;
/*
* Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is
* used to select reference clock connected to a pair of pads.
*
* Any change in this input must be followed by phy_reset assertion.
*/
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
IMX95_PCIE_REF_USE_PAD,
ext ? IMX95_PCIE_REF_USE_PAD : 0);
return 0;
}
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
{
/*
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
* Through Beacon or PERST# De-assertion
@@ -269,13 +289,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
IMX95_PCIE_PHY_CR_PARA_SEL,
IMX95_PCIE_PHY_CR_PARA_SEL);
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
IMX95_PCIE_REF_USE_PAD,
ext ? IMX95_PCIE_REF_USE_PAD : 0);
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
IMX95_PCIE_REF_CLKEN,
ext ? 0 : IMX95_PCIE_REF_CLKEN);
return 0;
}
@@ -665,14 +678,6 @@ static int imx_pcie_attach_pd(struct device *dev)
return 0;
}
static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
return 0;
}
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
if (enable) {
@@ -732,7 +737,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable)
static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
bool ext = imx_pcie->enable_ext_refclk;
imx95_pcie_clkreq_override(imx_pcie, enable);
/*
* The ref_clk_en signal must remain de-asserted until the
* reference clock is running at appropriate frequency, at which
* point this bit can be asserted. For lower power states where
* the reference clock to the PHY is disabled, it may also be
* de-asserted.
* +------------------- -+--------+----------------+
* | External clock mode | Enable | PCIE_REF_CLKEN |
* +---------------------+--------+----------------+
* | TRUE | X | 1b'0 |
* +---------------------+--------+----------------+
* | FALSE | TRUE | 1b'1 |
* +---------------------+--------+----------------+
* | FALSE | FALSE | 1b'0 |
* +---------------------+--------+----------------+
*/
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
IMX95_PCIE_REF_CLKEN,
ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN);
return 0;
}
@@ -786,6 +813,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
if (assert)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
/* Force PCIe PHY reset */
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
@@ -1221,16 +1251,70 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge,
imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
}
static int imx_pcie_parse_legacy_binding(struct imx_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
struct pci_host_bridge *bridge = pcie->pci->pp.bridge;
struct pci_host_port *port;
struct pci_host_perst *perst;
struct gpio_desc *reset;
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
if (IS_ERR(reset))
return PTR_ERR(reset);
if (!reset)
return 0;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
INIT_LIST_HEAD(&port->perst);
perst->desc = reset;
INIT_LIST_HEAD(&perst->list);
list_add_tail(&perst->list, &port->perst);
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);
}
static void imx_pcie_vpcie_aux_disable(void *data)
{
struct regulator *vpcie_aux = data;
regulator_disable(vpcie_aux);
}
static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert)
{
struct dw_pcie *pci = imx_pcie->pci;
struct pci_host_bridge *bridge = pci->pp.bridge;
struct pci_host_perst *perst;
struct pci_host_port *port;
if (!bridge || list_empty(&bridge->ports))
return;
if (assert) {
gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1);
} else {
if (imx_pcie->reset_gpiod) {
msleep(PCIE_T_PVPERL_MS);
gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0);
msleep(PCIE_RESET_CONFIG_WAIT_MS);
list_for_each_entry(port, &bridge->ports, list) {
list_for_each_entry(perst, &port->perst, list)
gpiod_direction_output(perst->desc, 1);
}
} else {
mdelay(PCIE_T_PVPERL_MS);
list_for_each_entry(port, &bridge->ports, list) {
list_for_each_entry(perst, &port->perst, list)
gpiod_direction_output(perst->desc, 0);
}
mdelay(PCIE_RESET_CONFIG_WAIT_MS);
}
}
@@ -1239,8 +1323,47 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct device *dev = pci->dev;
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
struct pci_host_bridge *bridge = pp->bridge;
int ret;
if (bridge && list_empty(&bridge->ports)) {
/* Parse Root Port nodes if present */
ret = pci_host_common_parse_ports(dev, bridge);
if (ret) {
if (ret != -ENODEV) {
dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
return ret;
}
/*
* Fall back to legacy binding for DT backwards
* compatibility
*/
ret = imx_pcie_parse_legacy_binding(imx_pcie);
if (ret)
return ret;
}
}
imx_pcie_assert_perst(imx_pcie, true);
/* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */
if (imx_pcie->vpcie_aux && !imx_pcie->vpcie_aux_enabled) {
ret = regulator_enable(imx_pcie->vpcie_aux);
if (ret) {
dev_err(dev, "failed to enable vpcie_aux regulator: %d\n",
ret);
return ret;
}
imx_pcie->vpcie_aux_enabled = true;
ret = devm_add_action_or_reset(dev, imx_pcie_vpcie_aux_disable,
imx_pcie->vpcie_aux);
if (ret)
return ret;
}
/* Legacy regulator handling for DT backward compatibility. */
if (imx_pcie->vpcie) {
ret = regulator_enable(imx_pcie->vpcie);
if (ret) {
@@ -1250,25 +1373,39 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
}
}
ret = pci_pwrctrl_create_devices(dev);
if (ret) {
dev_err(dev, "failed to create pwrctrl devices\n");
goto err_reg_disable;
}
ret = pci_pwrctrl_power_on_devices(dev);
if (ret) {
dev_err(dev, "failed to power on pwrctrl devices\n");
goto err_pwrctrl_destroy;
}
ret = imx_pcie_clk_enable(imx_pcie);
if (ret) {
dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
goto err_pwrctrl_power_off;
}
if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) {
pp->bridge->enable_device = imx_pcie_enable_device;
pp->bridge->disable_device = imx_pcie_disable_device;
}
if (imx_pcie->drvdata->select_ref_clk_src)
imx_pcie->drvdata->select_ref_clk_src(imx_pcie);
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie_assert_perst(imx_pcie, true);
if (imx_pcie->drvdata->init_phy)
imx_pcie->drvdata->init_phy(imx_pcie);
imx_pcie_configure_type(imx_pcie);
ret = imx_pcie_clk_enable(imx_pcie);
if (ret) {
dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
goto err_reg_disable;
}
if (imx_pcie->phy) {
ret = phy_init(imx_pcie->phy);
if (ret) {
@@ -1313,6 +1450,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
phy_exit(imx_pcie->phy);
err_clk_disable:
imx_pcie_clk_disable(imx_pcie);
err_pwrctrl_power_off:
pci_pwrctrl_power_off_devices(dev);
err_pwrctrl_destroy:
if (ret != -EPROBE_DEFER)
pci_pwrctrl_destroy_devices(dev);
err_reg_disable:
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
@@ -1331,6 +1473,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
}
imx_pcie_clk_disable(imx_pcie);
pci_pwrctrl_power_off_devices(pci->dev);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
@@ -1677,13 +1820,6 @@ static int imx_pcie_probe(struct platform_device *pdev)
return PTR_ERR(imx_pcie->phy_base);
}
/* Fetch GPIOs */
imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(imx_pcie->reset_gpiod))
return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod),
"unable to get reset gpio\n");
gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset");
/* Fetch clocks */
imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks);
if (imx_pcie->num_clks < 0)
@@ -1781,9 +1917,13 @@ static int imx_pcie_probe(struct platform_device *pdev)
of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed);
imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq");
ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable Vaux supply\n");
imx_pcie->vpcie_aux = devm_regulator_get_optional(&pdev->dev,
"vpcie3v3aux");
if (IS_ERR(imx_pcie->vpcie_aux)) {
if (PTR_ERR(imx_pcie->vpcie_aux) != -ENODEV)
return PTR_ERR(imx_pcie->vpcie_aux);
imx_pcie->vpcie_aux = NULL;
}
imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie");
if (IS_ERR(imx_pcie->vpcie)) {
@@ -1845,6 +1985,8 @@ static void imx_pcie_shutdown(struct platform_device *pdev)
/* bring down link, so bootloader gets clean state in case of reboot */
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie_assert_perst(imx_pcie, true);
pci_pwrctrl_power_off_devices(&pdev->dev);
pci_pwrctrl_destroy_devices(&pdev->dev);
}
static const struct imx_pcie_drvdata drvdata[] = {
@@ -1876,7 +2018,6 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx6sx_pcie_init_phy,
.enable_ref_clk = imx6sx_pcie_enable_ref_clk,
.core_reset = imx6sx_pcie_core_reset,
.ops = &imx_pcie_host_ops,
},
@@ -1966,6 +2107,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
.core_reset = imx95_pcie_core_reset,
.init_phy = imx95_pcie_init_phy,
.select_ref_clk_src = imx95_pcie_select_ref_clk_src,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
.enable_ref_clk = imx95_pcie_enable_ref_clk,
.clr_clkreq_override = imx95_pcie_clr_clkreq_override,
@@ -2021,6 +2163,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
.mode_off[0] = IMX95_PE0_GEN_CTRL_1,
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
.select_ref_clk_src = imx95_pcie_select_ref_clk_src,
.init_phy = imx95_pcie_init_phy,
.core_reset = imx95_pcie_core_reset,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,

View File

@@ -204,7 +204,9 @@ static inline struct clk *meson_pcie_probe_clock(struct device *dev,
return ERR_PTR(ret);
}
devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk);
ret = devm_add_action_or_reset(dev, meson_pcie_disable_clock, clk);
if (ret)
return ERR_PTR(ret);
return clk;
}
@@ -451,6 +453,14 @@ static int meson_pcie_probe(struct platform_device *pdev)
return ret;
}
static void meson_pcie_remove(struct platform_device *pdev)
{
struct meson_pcie *mp = platform_get_drvdata(pdev);
dw_pcie_host_deinit(&mp->pci.pp);
meson_pcie_power_off(mp);
}
static const struct of_device_id meson_pcie_of_match[] = {
{
.compatible = "amlogic,axg-pcie",
@@ -464,6 +474,7 @@ MODULE_DEVICE_TABLE(of, meson_pcie_of_match);
static struct platform_driver meson_pcie_driver = {
.probe = meson_pcie_probe,
.remove = meson_pcie_remove,
.driver = {
.name = "meson-pcie",
.of_match_table = meson_pcie_of_match,

View File

@@ -253,7 +253,6 @@ static int al_pcie_config_prepare(struct al_pcie *pcie)
u8 subordinate_bus;
u8 secondary_bus;
u32 cfg_control;
u32 reg;
ft = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
if (!ft)
@@ -285,14 +284,9 @@ static int al_pcie_config_prepare(struct al_pcie *pcie)
CFG_CONTROL;
cfg_control = al_pcie_controller_readl(pcie, cfg_control_offset);
reg = cfg_control &
~(CFG_CONTROL_SEC_BUS_MASK | CFG_CONTROL_SUBBUS_MASK);
reg |= FIELD_PREP(CFG_CONTROL_SUBBUS_MASK, subordinate_bus) |
FIELD_PREP(CFG_CONTROL_SEC_BUS_MASK, secondary_bus);
al_pcie_controller_writel(pcie, cfg_control_offset, reg);
FIELD_MODIFY(CFG_CONTROL_SUBBUS_MASK, &cfg_control, subordinate_bus);
FIELD_MODIFY(CFG_CONTROL_SEC_BUS_MASK, &cfg_control, secondary_bus);
al_pcie_controller_writel(pcie, cfg_control_offset, cfg_control);
return 0;
}

View File

@@ -7,7 +7,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
@@ -507,6 +507,13 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev)
return amd_mdb_add_pcie_port(pcie, pdev);
}
static void amd_mdb_pcie_shutdown(struct platform_device *pdev)
{
struct amd_mdb_pcie *pcie = platform_get_drvdata(pdev);
gpiod_set_value_cansleep(pcie->perst_gpio, 1);
}
static const struct of_device_id amd_mdb_pcie_of_match[] = {
{
.compatible = "amd,versal2-mdb-host",
@@ -521,6 +528,7 @@ static struct platform_driver amd_mdb_pcie_driver = {
.suppress_bind_attrs = true,
},
.probe = amd_mdb_pcie_probe,
.shutdown = amd_mdb_pcie_shutdown,
};
builtin_platform_driver(amd_mdb_pcie_driver);

View File

@@ -34,7 +34,6 @@ struct artpec6_pcie {
struct regmap *regmap; /* DT axis,syscon-pcie */
void __iomem *phy_base; /* DT phy */
enum artpec_pcie_variants variant;
enum dw_pcie_device_mode mode;
};
struct artpec_pcie_of_data {
@@ -100,7 +99,7 @@ static u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr)
struct dw_pcie_rp *pp = &pci->pp;
struct dw_pcie_ep *ep = &pci->ep;
switch (artpec6_pcie->mode) {
switch (artpec6_pcie->pci->mode) {
case DW_PCIE_RC_TYPE:
return cpu_addr - pp->cfg0_base;
case DW_PCIE_EP_TYPE:
@@ -413,7 +412,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
artpec6_pcie->pci = pci;
artpec6_pcie->variant = variant;
artpec6_pcie->mode = mode;
artpec6_pcie->pci->mode = mode;
artpec6_pcie->phy_base =
devm_platform_ioremap_resource_byname(pdev, "phy");
@@ -428,7 +427,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, artpec6_pcie);
switch (artpec6_pcie->mode) {
switch (artpec6_pcie->pci->mode) {
case DW_PCIE_RC_TYPE:
if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST))
return -ENODEV;
@@ -464,7 +463,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
break;
default:
dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode);
dev_err(dev, "INVALID device type %d\n", artpec6_pcie->pci->mode);
}
return 0;

View File

@@ -265,8 +265,7 @@ static ssize_t lane_detect_write(struct file *file, const char __user *buf,
return ret;
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG);
val &= ~(LANE_SELECT);
val |= FIELD_PREP(LANE_SELECT, lane);
FIELD_MODIFY(LANE_SELECT, &val, lane);
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + SD_STATUS_L1LANE_REG, val);
return count;
@@ -306,6 +305,7 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf,
u32 val, counter, vc_num, err_group, type_mask;
int val_diff = 0;
char *kern_buf;
int ret;
err_group = err_inj_list[pdata->idx].err_inj_group;
type_mask = err_inj_type_mask[err_group];
@@ -327,10 +327,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf,
return -EINVAL;
}
} else {
val = kstrtou32(kern_buf, 0, &counter);
if (val) {
ret = kstrtou32(kern_buf, 0, &counter);
if (ret) {
kfree(kern_buf);
return val;
return ret;
}
}
@@ -339,14 +339,10 @@ static ssize_t err_inj_write(struct file *file, const char __user *buf,
val |= ((err_inj_list[pdata->idx].err_inj_type << EINJ_TYPE_SHIFT) & type_mask);
val |= FIELD_PREP(EINJ_COUNT, counter);
if (err_group == 1 || err_group == 4) {
val &= ~(EINJ_VAL_DIFF);
val |= FIELD_PREP(EINJ_VAL_DIFF, val_diff);
}
if (err_group == 4) {
val &= ~(EINJ_VC_NUM);
val |= FIELD_PREP(EINJ_VC_NUM, vc_num);
}
if (err_group == 1 || err_group == 4)
FIELD_MODIFY(EINJ_VAL_DIFF, &val, val_diff);
if (err_group == 4)
FIELD_MODIFY(EINJ_VC_NUM, &val, vc_num);
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ0_OFF + (0x4 * err_group), val);
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + ERR_INJ_ENABLE_REG, (0x1 << err_group));
@@ -362,9 +358,8 @@ static void set_event_number(struct dwc_pcie_rasdes_priv *pdata,
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
val &= ~EVENT_COUNTER_ENABLE;
val &= ~(EVENT_COUNTER_GROUP_SELECT | EVENT_COUNTER_EVENT_SELECT);
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SELECT, event_list[pdata->idx].group_no);
val |= FIELD_PREP(EVENT_COUNTER_EVENT_SELECT, event_list[pdata->idx].event_no);
FIELD_MODIFY(EVENT_COUNTER_GROUP_SELECT, &val, event_list[pdata->idx].group_no);
FIELD_MODIFY(EVENT_COUNTER_EVENT_SELECT, &val, event_list[pdata->idx].event_no);
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val);
}
@@ -469,8 +464,7 @@ static ssize_t counter_lane_write(struct file *file, const char __user *buf,
mutex_lock(&rinfo->reg_event_lock);
set_event_number(pdata, pci, rinfo);
val = dw_pcie_readl_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG);
val &= ~(EVENT_COUNTER_LANE_SELECT);
val |= FIELD_PREP(EVENT_COUNTER_LANE_SELECT, lane);
FIELD_MODIFY(EVENT_COUNTER_LANE_SELECT, &val, lane);
dw_pcie_writel_dbi(pci, rinfo->ras_cap_offset + RAS_DES_EVENT_COUNTER_CTRL_REG, val);
mutex_unlock(&rinfo->reg_event_lock);
@@ -507,11 +501,6 @@ static int ltssm_status_show(struct seq_file *s, void *v)
return 0;
}
static int ltssm_status_open(struct inode *inode, struct file *file)
{
return single_open(file, ltssm_status_show, inode->i_private);
}
#define dwc_debugfs_create(name) \
debugfs_create_file(#name, 0644, rasdes_debug, pci, \
&dbg_ ## name ## _fops)
@@ -548,15 +537,15 @@ static const struct file_operations dwc_pcie_counter_value_ops = {
.read = counter_value_read,
};
static const struct file_operations dwc_pcie_ltssm_status_ops = {
.open = ltssm_status_open,
.read = seq_read,
};
DEFINE_SHOW_ATTRIBUTE(ltssm_status);
static void dwc_pcie_rasdes_debugfs_deinit(struct dw_pcie *pci)
{
struct dwc_pcie_rasdes_info *rinfo = pci->debugfs->rasdes_info;
if (!rinfo)
return;
mutex_destroy(&rinfo->reg_event_lock);
}
@@ -642,7 +631,7 @@ static int dwc_pcie_rasdes_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
static void dwc_pcie_ltssm_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
{
debugfs_create_file("ltssm_status", 0444, dir, pci,
&dwc_pcie_ltssm_status_ops);
&ltssm_status_fops);
}
static int dw_pcie_ptm_check_capability(void *drvdata)

View File

@@ -9,6 +9,7 @@
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/of.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include "pcie-designware.h"
@@ -584,9 +585,9 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
config_atu:
if (!(flags & PCI_BASE_ADDRESS_SPACE))
type = PCIE_ATU_TYPE_MEM;
type = PCIE_TLP_TYPE_MEM_RDWR;
else
type = PCIE_ATU_TYPE_IO;
type = PCIE_TLP_TYPE_IO_RDWR;
if (epf_bar->num_submap)
ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar);
@@ -659,7 +660,7 @@ static int dw_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct dw_pcie_ob_atu_cfg atu = { 0 };
atu.func_no = func_no;
atu.type = PCIE_ATU_TYPE_MEM;
atu.type = PCIE_TLP_TYPE_MEM_RDWR;
atu.parent_bus_addr = addr - pci->parent_bus_offset;
atu.pci_addr = pci_addr;
atu.size = size;
@@ -707,8 +708,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
reg = ep_func->msi_cap + PCI_MSI_FLAGS;
val = dw_pcie_ep_readw_dbi(ep, func_no, reg);
val &= ~PCI_MSI_FLAGS_QMASK;
val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, mmc);
FIELD_MODIFY(PCI_MSI_FLAGS_QMASK, &val, mmc);
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_ep_writew_dbi(ep, func_no, reg, val);
dw_pcie_dbi_ro_wr_dis(pci);
@@ -817,6 +817,122 @@ dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
return ep->ops->get_features(ep);
}
static const struct pci_epc_bar_rsvd_region *
dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
enum pci_epc_bar_rsvd_region_type type,
enum pci_barno *bar,
resource_size_t *bar_offset)
{
const struct pci_epc_features *features;
const struct pci_epc_bar_desc *bar_desc;
const struct pci_epc_bar_rsvd_region *r;
int i, j;
if (!ep->ops->get_features)
return NULL;
features = ep->ops->get_features(ep);
if (!features)
return NULL;
for (i = BAR_0; i <= BAR_5; i++) {
bar_desc = &features->bar[i];
if (!bar_desc->nr_rsvd_regions || !bar_desc->rsvd_regions)
continue;
for (j = 0; j < bar_desc->nr_rsvd_regions; j++) {
r = &bar_desc->rsvd_regions[j];
if (r->type != type)
continue;
if (bar)
*bar = i;
if (bar_offset)
*bar_offset = r->offset;
return r;
}
}
return NULL;
}
static int
dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
u8 vfunc_no)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dw_edma_chip *edma = &pci->edma;
if (!pci->edma_reg_size)
return 0;
if (edma->db_offset == ~0)
return 0;
return 1;
}
static int
dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
const struct pci_epc_bar_rsvd_region *rsvd;
struct dw_edma_chip *edma = &pci->edma;
enum pci_barno dma_ctrl_bar = NO_BAR;
resource_size_t db_offset = edma->db_offset;
resource_size_t dma_ctrl_bar_offset = 0;
resource_size_t dma_reg_size;
int count;
count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no);
if (count < 0)
return count;
if (num_resources < count)
return -ENOSPC;
if (!count)
return 0;
dma_reg_size = pci->edma_reg_size;
rsvd = dw_pcie_ep_find_bar_rsvd_region(ep,
PCI_EPC_BAR_RSVD_DMA_CTRL_MMIO,
&dma_ctrl_bar,
&dma_ctrl_bar_offset);
if (rsvd && rsvd->size < dma_reg_size)
dma_reg_size = rsvd->size;
/*
* For interrupt-emulation doorbells, report a standalone resource
* instead of bundling it into the DMA controller MMIO resource.
*/
if (range_end_overflows_t(resource_size_t, db_offset,
sizeof(u32), dma_reg_size))
return -EINVAL;
resources[0] = (struct pci_epc_aux_resource) {
.type = PCI_EPC_AUX_DOORBELL_MMIO,
.phys_addr = pci->edma_reg_phys + db_offset,
.size = sizeof(u32),
.bar = dma_ctrl_bar,
.bar_offset = dma_ctrl_bar != NO_BAR ?
dma_ctrl_bar_offset + db_offset : 0,
.u.db_mmio = {
.irq = edma->db_irq,
.data = 0, /* write 0 to assert */
},
};
return 0;
}
static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
@@ -832,6 +948,8 @@ static const struct pci_epc_ops epc_ops = {
.start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop,
.get_features = dw_pcie_ep_get_features,
.get_aux_resources_count = dw_pcie_ep_get_aux_resources_count,
.get_aux_resources = dw_pcie_ep_get_aux_resources,
};
/**

View File

@@ -16,9 +16,11 @@
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
#include "../pci-host-common.h"
#include "../../pci.h"
#include "pcie-designware.h"
@@ -434,7 +436,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
* remaining buses need type 1 iATU configuration.
*/
atu.index = 0;
atu.type = PCIE_ATU_TYPE_CFG0;
atu.type = PCIE_TLP_TYPE_CFG0_RDWR;
atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
atu.size = SZ_1M;
@@ -450,7 +452,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
/* Configure remaining buses in type 1 iATU configuration */
atu.index = 1;
atu.type = PCIE_ATU_TYPE_CFG1;
atu.type = PCIE_TLP_TYPE_CFG1_RDWR;
atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
atu.size = (SZ_1M * bus_range_max) - SZ_2M;
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
@@ -703,8 +705,10 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
dwc_pcie_debugfs_deinit(pci);
pci_lock_rescan_remove();
pci_stop_root_bus(pp->bridge->bus);
pci_remove_root_bus(pp->bridge->bus);
pci_unlock_rescan_remove();
dw_pcie_stop_link(pci);
@@ -745,9 +749,9 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
PCIE_ATU_FUNC(PCI_FUNC(devfn));
if (pci_is_root_bus(bus->parent))
type = PCIE_ATU_TYPE_CFG0;
type = PCIE_TLP_TYPE_CFG0_RDWR;
else
type = PCIE_ATU_TYPE_CFG1;
type = PCIE_TLP_TYPE_CFG1_RDWR;
atu.type = type;
atu.parent_bus_addr = pp->cfg0_base - pci->parent_bus_offset;
@@ -774,7 +778,7 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn,
return ret;
if (pp->cfg0_io_shared) {
atu.type = PCIE_ATU_TYPE_IO;
atu.type = PCIE_TLP_TYPE_IO_RDWR;
atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset;
atu.pci_addr = pp->io_bus_addr;
atu.size = pp->io_size;
@@ -800,7 +804,7 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn,
return ret;
if (pp->cfg0_io_shared) {
atu.type = PCIE_ATU_TYPE_IO;
atu.type = PCIE_TLP_TYPE_IO_RDWR;
atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset;
atu.pci_addr = pp->io_bus_addr;
atu.size = pp->io_size;
@@ -908,7 +912,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
if (resource_type(entry->res) != IORESOURCE_MEM)
continue;
atu.type = PCIE_ATU_TYPE_MEM;
atu.type = PCIE_TLP_TYPE_MEM_RDWR;
atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset;
atu.pci_addr = entry->res->start - entry->offset;
@@ -951,7 +955,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
if (pp->io_size) {
if (ob_iatu_index < pci->num_ob_windows) {
atu.index = ob_iatu_index;
atu.type = PCIE_ATU_TYPE_IO;
atu.type = PCIE_TLP_TYPE_IO_RDWR;
atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset;
atu.pci_addr = pp->io_bus_addr;
atu.size = pp->io_size;
@@ -1013,7 +1017,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
window_size = MIN(pci->region_limit + 1, res_size);
ret = dw_pcie_prog_inbound_atu(pci, ib_iatu_index,
PCIE_ATU_TYPE_MEM, res_start,
PCIE_TLP_TYPE_MEM_RDWR, res_start,
res_start - entry->offset, window_size);
if (ret) {
dev_err(pci->dev, "Failed to set DMA range %pr\n",
@@ -1194,7 +1198,7 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
atu.code = PCIE_MSG_CODE_PME_TURN_OFF;
atu.routing = PCIE_MSG_TYPE_R_BC;
atu.type = PCIE_ATU_TYPE_MSG;
atu.type = PCIE_TLP_TYPE_MSG;
atu.size = resource_size(pci->pp.msg_res);
atu.index = pci->pp.msg_atu_index;
@@ -1218,18 +1222,14 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
bool pme_capable = false;
int ret = 0;
u32 val;
if (!dw_pcie_link_up(pci))
goto stop_link;
/*
* If L1SS is supported, then do not put the link into L2 as some
* devices such as NVMe expect low resume latency.
*/
if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
return 0;
if (pci->pp.ops->pme_turn_off) {
@@ -1273,6 +1273,15 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
udelay(1);
stop_link:
/*
* TODO: "pme_capable" means some downstream device is wakeup-
* enabled and is capable of generating PME from D3cold, which
* requires auxiliary power. Instead of always skipping power off
* if PME is supported from D3cold, query the pwrctrl core and skip
* power off only if device supports PME from D3cold and Vaux is
* not supported.
*/
pci->pp.skip_pwrctrl_off = pme_capable;
dw_pcie_stop_link(pci);
if (pci->pp.ops->deinit)
pci->pp.ops->deinit(&pci->pp);
@@ -1290,8 +1299,6 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
if (!pci->suspended)
return 0;
pci->suspended = false;
if (pci->pp.ops->init) {
ret = pci->pp.ops->init(&pci->pp);
if (ret) {
@@ -1313,6 +1320,8 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
if (pci->pp.ops->post_init)
pci->pp.ops->post_init(&pci->pp);
pci->suspended = false;
return 0;
err_stop_link:

View File

@@ -8,7 +8,6 @@
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -22,7 +21,6 @@
struct dw_plat_pcie {
struct dw_pcie *pci;
enum dw_pcie_device_mode mode;
};
struct dw_plat_pcie_of_data {
@@ -118,11 +116,11 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
pci->dev = dev;
dw_plat_pcie->pci = pci;
dw_plat_pcie->mode = mode;
dw_plat_pcie->pci->mode = mode;
platform_set_drvdata(pdev, dw_plat_pcie);
switch (dw_plat_pcie->mode) {
switch (dw_plat_pcie->pci->mode) {
case DW_PCIE_RC_TYPE:
if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST))
return -ENODEV;
@@ -148,7 +146,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
break;
default:
dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode);
dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->pci->mode);
ret = -EINVAL;
break;
}

View File

@@ -22,6 +22,7 @@
#include <linux/sizes.h>
#include <linux/types.h>
#include "../pci-host-common.h"
#include "../../pci.h"
#include "pcie-designware.h"
@@ -162,8 +163,12 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
pci->edma.reg_base = devm_ioremap_resource(pci->dev, res);
if (IS_ERR(pci->edma.reg_base))
return PTR_ERR(pci->edma.reg_base);
pci->edma_reg_phys = res->start;
pci->edma_reg_size = resource_size(res);
} else if (pci->atu_size >= 2 * DEFAULT_DBI_DMA_OFFSET) {
pci->edma.reg_base = pci->atu_base + DEFAULT_DBI_DMA_OFFSET;
pci->edma_reg_phys = pci->atu_phys_addr + DEFAULT_DBI_DMA_OFFSET;
pci->edma_reg_size = pci->atu_size - DEFAULT_DBI_DMA_OFFSET;
}
}
@@ -487,13 +492,13 @@ static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg
static inline u32 dw_pcie_enable_ecrc(u32 val)
{
/*
* DWC versions 0x3530302a and 0x3536322a have a design issue where
* the 'TD' bit in the Control register-1 of the ATU outbound
* region acts like an override for the ECRC setting, i.e., the
* presence of TLP Digest (ECRC) in the outgoing TLPs is solely
* determined by this bit. This is contrary to the PCIe spec which
* says that the enablement of the ECRC is solely determined by the
* AER registers.
* DesignWare core versions prior to 5.10A have a design issue where the
* 'TD' bit in the Control register-1 of the ATU outbound region acts
* like an override for the ECRC setting, i.e., the presence of TLP
* Digest (ECRC) in the outgoing TLPs is solely determined by this
* bit. This is contrary to the PCIe spec which says that the
* enablement of the ECRC is solely determined by the AER
* registers.
*
* Because of this, even when the ECRC is enabled through AER
* registers, the transactions going through ATU won't have TLP
@@ -563,12 +568,12 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&
dw_pcie_ver_is_ge(pci, 460A))
val |= PCIE_ATU_INCREASE_REGION_SIZE;
if (dw_pcie_ver_is(pci, 490A) || dw_pcie_ver_is(pci, 500A))
if (!dw_pcie_ver_is_ge(pci, 510A))
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
val = PCIE_ATU_ENABLE | atu->ctrl2;
if (atu->type == PCIE_ATU_TYPE_MSG) {
if (atu->type == PCIE_TLP_TYPE_MSG) {
/* The data-less messages only for now */
val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
}
@@ -799,13 +804,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
return -ETIMEDOUT;
}
/*
* As per PCIe r6.0, sec 6.6.1, a Downstream Port that supports Link
* speeds greater than 5.0 GT/s, software must wait a minimum of 100 ms
* after Link training completes before sending a Configuration Request.
*/
if (pci->max_link_speed > 2)
msleep(PCIE_RESET_CONFIG_WAIT_MS);
pci_host_common_link_train_delay(pci->max_link_speed);
offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA);
@@ -938,8 +937,7 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
lnkcap &= ~PCI_EXP_LNKCAP_MLW;
lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes);
FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, num_lanes);
dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap);
}
@@ -1249,6 +1247,34 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
}
/* TODO: Need to handle multi Root Ports */
void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on)
{
u8 scale, value;
u16 offset;
u32 val;
if (!t_power_on)
return;
offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
if (!offset)
return;
pcie_encode_t_power_on(t_power_on, &scale, &value);
dw_pcie_dbi_ro_wr_en(pci);
val = dw_pcie_readl_dbi(pci, offset + PCI_L1SS_CAP);
val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE);
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, &val, scale);
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, &val, value);
dw_pcie_writel_dbi(pci, offset + PCI_L1SS_CAP, val);
dw_pcie_dbi_ro_wr_dis(pci);
}
void dw_pcie_setup(struct dw_pcie *pci)
{
u32 val;

View File

@@ -35,6 +35,7 @@
#define DW_PCIE_VER_480A 0x3438302a
#define DW_PCIE_VER_490A 0x3439302a
#define DW_PCIE_VER_500A 0x3530302a
#define DW_PCIE_VER_510A 0x3531302a
#define DW_PCIE_VER_520A 0x3532302a
#define DW_PCIE_VER_540A 0x3534302a
#define DW_PCIE_VER_562A 0x3536322a
@@ -71,6 +72,8 @@
/* Synopsys-specific PCIe configuration registers */
#define PCIE_PORT_FORCE 0x708
/* Bit[7:0] LINK_NUM: Link Number. Not used for endpoint */
#define PORT_LINK_NUM_MASK GENMASK(7, 0)
#define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23)
#define PCIE_PORT_AFR 0x70C
@@ -98,6 +101,28 @@
#define PCIE_PORT_LANE_SKEW 0x714
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
/*
* PCIE_TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number
* Register.
*
* This register holds the ack frequency, latency, replay, fast link
* scaling timers, and max function number values.
*
* Bit[30:29] FAST_LINK_SCALING_FACTOR: Fast Link Timer Scaling Factor.
* 0x0 (SF_1024): Scaling Factor is 1024 (1ms is 1us).
* When the LTSSM is in Config or L12 Entry State, 1ms
* timer is 2us, 2ms timer is 4us and 3ms timer is 6us.
* 0x1 (SF_256): Scaling Factor is 256 (1ms is 4us)
* 0x2 (SF_64): Scaling Factor is 64 (1ms is 16us)
* 0x3 (SF_16): Scaling Factor is 16 (1ms is 64us)
*/
#define PCIE_TIMER_CTRL_MAX_FUNC_NUM 0x718
#define PORT_FLT_SF_MASK GENMASK(30, 29)
#define PORT_FLT_SF_VAL_1024 0x0
#define PORT_FLT_SF_VAL_256 0x1
#define PORT_FLT_SF_VAL_64 0x2
#define PORT_FLT_SF_VAL_16 0x3
#define PCIE_PORT_DEBUG0 0x728
#define PORT_LOGIC_LTSSM_STATE_MASK 0x3f
#define PORT_LOGIC_LTSSM_STATE_L0 0x11
@@ -170,11 +195,6 @@
#define PCIE_ATU_VIEWPORT_SIZE 0x2C
#define PCIE_ATU_REGION_CTRL1 0x000
#define PCIE_ATU_INCREASE_REGION_SIZE BIT(13)
#define PCIE_ATU_TYPE_MEM 0x0
#define PCIE_ATU_TYPE_IO 0x2
#define PCIE_ATU_TYPE_CFG0 0x4
#define PCIE_ATU_TYPE_CFG1 0x5
#define PCIE_ATU_TYPE_MSG 0x10
#define PCIE_ATU_TD BIT(8)
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
#define PCIE_ATU_REGION_CTRL2 0x004
@@ -450,6 +470,7 @@ struct dw_pcie_rp {
bool ecam_enabled;
bool native_ecam;
bool skip_l23_ready;
bool skip_pwrctrl_off;
};
struct dw_pcie_ep_ops {
@@ -544,6 +565,8 @@ struct dw_pcie {
int max_link_speed;
u8 n_fts[2];
struct dw_edma_chip edma;
phys_addr_t edma_reg_phys;
resource_size_t edma_reg_size;
bool l1ss_support; /* L1 PM Substates support */
struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS];
struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS];
@@ -605,6 +628,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
void dw_pcie_program_t_power_on(struct dw_pcie *pci, u32 t_power_on);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
int dw_pcie_edma_detect(struct dw_pcie *pci);

View File

@@ -211,8 +211,7 @@ static int eswin_pcie_host_init(struct dw_pcie_rp *pp)
/* Configure Root Port type */
val = readl_relaxed(pci->elbi_base + PCIEELBI_CTRL0_OFFSET);
val &= ~PCIEELBI_CTRL0_DEV_TYPE;
val |= FIELD_PREP(PCIEELBI_CTRL0_DEV_TYPE, PCI_EXP_TYPE_ROOT_PORT);
FIELD_MODIFY(PCIEELBI_CTRL0_DEV_TYPE, &val, PCI_EXP_TYPE_ROOT_PORT);
writel_relaxed(val, pci->elbi_base + PCIEELBI_CTRL0_OFFSET);
list_for_each_entry(port, &pcie->ports, list) {

View File

@@ -13,7 +13,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>

View File

@@ -47,7 +47,6 @@
#define PCIE_APP_IRN_INTD BIT(16)
#define PCIE_APP_IRN_MSG_LTR BIT(18)
#define PCIE_APP_IRN_SYS_ERR_RC BIT(29)
#define PCIE_APP_INTX_OFST 12
#define PCIE_APP_IRN_INT \
(PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \
@@ -196,6 +195,13 @@ static void intel_pcie_device_rst_deassert(struct intel_pcie *pcie)
gpiod_set_value_cansleep(pcie->reset_gpio, 0);
}
static void intel_pcie_core_irq_enable(struct intel_pcie *pcie)
{
pcie_app_wr(pcie, PCIE_APP_IRNEN, 0);
pcie_app_wr(pcie, PCIE_APP_IRNCR, PCIE_APP_IRN_INT);
pcie_app_wr(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT);
}
static void intel_pcie_core_irq_disable(struct intel_pcie *pcie)
{
pcie_app_wr(pcie, PCIE_APP_IRNEN, 0);
@@ -278,6 +284,16 @@ static void intel_pcie_turn_off(struct intel_pcie *pcie)
pcie_rc_cfg_wr_mask(pcie, PCI_COMMAND, PCI_COMMAND_MEMORY, 0);
}
static int intel_pcie_start_link(struct dw_pcie *pci)
{
struct intel_pcie *pcie = dev_get_drvdata(pci->dev);
intel_pcie_device_rst_deassert(pcie);
intel_pcie_ltssm_enable(pcie);
return 0;
}
static int intel_pcie_host_setup(struct intel_pcie *pcie)
{
int ret;
@@ -285,49 +301,33 @@ static int intel_pcie_host_setup(struct intel_pcie *pcie)
intel_pcie_core_rst_assert(pcie);
intel_pcie_device_rst_assert(pcie);
ret = phy_init(pcie->phy);
if (ret)
return ret;
intel_pcie_core_rst_deassert(pcie);
/* Controller clock must be provided earlier than PHY */
ret = clk_prepare_enable(pcie->core_clk);
if (ret) {
dev_err(pcie->pci.dev, "Core clock enable failed: %d\n", ret);
goto clk_err;
}
pci->atu_base = pci->dbi_base + 0xC0000;
ret = phy_init(pcie->phy);
if (ret)
goto phy_err;
intel_pcie_ltssm_disable(pcie);
intel_pcie_link_setup(pcie);
intel_pcie_init_n_fts(pci);
ret = dw_pcie_setup_rc(&pci->pp);
if (ret)
goto app_init_err;
dw_pcie_upconfig_setup(pci);
intel_pcie_device_rst_deassert(pcie);
intel_pcie_ltssm_enable(pcie);
ret = dw_pcie_wait_for_link(pci);
if (ret)
goto app_init_err;
/* Enable integrated interrupts */
pcie_app_wr_mask(pcie, PCIE_APP_IRNEN, PCIE_APP_IRN_INT,
PCIE_APP_IRN_INT);
intel_pcie_core_irq_enable(pcie);
return 0;
app_init_err:
phy_err:
clk_disable_unprepare(pcie->core_clk);
clk_err:
intel_pcie_core_rst_assert(pcie);
phy_exit(pcie->phy);
return ret;
}
@@ -381,6 +381,7 @@ static int intel_pcie_rc_init(struct dw_pcie_rp *pp)
}
static const struct dw_pcie_ops intel_pcie_ops = {
.start_link = intel_pcie_start_link,
};
static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
@@ -392,6 +393,7 @@ static int intel_pcie_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct intel_pcie *pcie;
struct dw_pcie_rp *pp;
struct resource *res;
struct dw_pcie *pci;
int ret;
@@ -416,6 +418,32 @@ static int intel_pcie_probe(struct platform_device *pdev)
pci->ops = &intel_pcie_ops;
pp->ops = &intel_pcie_dw_ops;
/*
* If the 'atu' region is not available in the devicetree, use the
* default offset from DBI region for backwards compatibility. The
* 'atu' region should always be specified in the devicetree, as
* this is a hardware-specific address that should not be defined
* in the driver.
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
if (!res) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
pci->dbi_phys_addr = res->start;
pci->atu_base = devm_ioremap(dev, res->start + 0xC0000, SZ_4K);
if (!pci->atu_base) {
dev_err(dev, "failed to remap ATU space\n");
return -ENOMEM;
}
pci->atu_size = SZ_4K;
pci->atu_phys_addr = res->start + 0xC0000;
dev_warn(dev, "ATU region not specified in DT. Using default offset\n");
}
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "Cannot initialize host\n");

View File

@@ -58,7 +58,6 @@
struct keembay_pcie {
struct dw_pcie pci;
void __iomem *apb_base;
enum dw_pcie_device_mode mode;
struct clk *clk_master;
struct clk *clk_aux;
@@ -117,7 +116,7 @@ static int keembay_pcie_start_link(struct dw_pcie *pci)
u32 val;
int ret;
if (pcie->mode == DW_PCIE_EP_TYPE)
if (pcie->pci.mode == DW_PCIE_EP_TYPE)
return 0;
keembay_pcie_ltssm_set(pcie, false);
@@ -409,7 +408,7 @@ static int keembay_pcie_probe(struct platform_device *pdev)
pci->dev = dev;
pci->ops = &keembay_pcie_ops;
pcie->mode = mode;
pcie->pci.mode = mode;
pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
if (IS_ERR(pcie->apb_base))
@@ -417,7 +416,7 @@ static int keembay_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcie);
switch (pcie->mode) {
switch (pcie->pci.mode) {
case DW_PCIE_RC_TYPE:
if (!IS_ENABLED(CONFIG_PCIE_KEEMBAY_HOST))
return -ENODEV;
@@ -443,7 +442,7 @@ static int keembay_pcie_probe(struct platform_device *pdev)
break;
default:
dev_err(dev, "Invalid device type %d\n", pcie->mode);
dev_err(dev, "Invalid device type %d\n", pcie->pci.mode);
return -ENODEV;
}

View File

@@ -139,8 +139,7 @@ static int s32g_init_pcie_controller(struct dw_pcie_rp *pp)
/* Set RP mode */
val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1);
val &= ~DEVICE_TYPE_MASK;
val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT);
FIELD_MODIFY(DEVICE_TYPE_MASK, &val, PCI_EXP_TYPE_ROOT_PORT);
/* Use default CRNS */
val &= ~SRIS_MODE;

View File

@@ -30,20 +30,15 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK,
speed - PCIE_SPEED_8_0GT);
FIELD_MODIFY(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, &reg,
speed - PCIE_SPEED_8_0GT);
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 |
GEN3_EQ_FMDC_N_EVALS |
GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA |
GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA);
reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) |
FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5);
FIELD_MODIFY(GEN3_EQ_FMDC_T_MIN_PHASE23, &reg, 0x1);
FIELD_MODIFY(GEN3_EQ_FMDC_N_EVALS, &reg, 0xd);
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, &reg, 0x5);
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, &reg, 0x5);
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
@@ -61,14 +56,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci)
u32 reg;
reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_1_OFF);
reg &= ~(MARGINING_MAX_VOLTAGE_OFFSET |
MARGINING_NUM_VOLTAGE_STEPS |
MARGINING_MAX_TIMING_OFFSET |
MARGINING_NUM_TIMING_STEPS);
reg |= FIELD_PREP(MARGINING_MAX_VOLTAGE_OFFSET, 0x24) |
FIELD_PREP(MARGINING_NUM_VOLTAGE_STEPS, 0x78) |
FIELD_PREP(MARGINING_MAX_TIMING_OFFSET, 0x32) |
FIELD_PREP(MARGINING_NUM_TIMING_STEPS, 0x10);
FIELD_MODIFY(MARGINING_MAX_VOLTAGE_OFFSET, &reg, 0x24);
FIELD_MODIFY(MARGINING_NUM_VOLTAGE_STEPS, &reg, 0x78);
FIELD_MODIFY(MARGINING_MAX_TIMING_OFFSET, &reg, 0x32);
FIELD_MODIFY(MARGINING_NUM_TIMING_STEPS, &reg, 0x10);
dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_1_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN4_LANE_MARGINING_2_OFF);
@@ -76,13 +67,10 @@ void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci)
MARGINING_SAMPLE_REPORTING_METHOD |
MARGINING_IND_LEFT_RIGHT_TIMING |
MARGINING_VOLTAGE_SUPPORTED;
reg &= ~(MARGINING_IND_UP_DOWN_VOLTAGE |
MARGINING_MAXLANES |
MARGINING_SAMPLE_RATE_TIMING |
MARGINING_SAMPLE_RATE_VOLTAGE);
reg |= FIELD_PREP(MARGINING_MAXLANES, pci->num_lanes) |
FIELD_PREP(MARGINING_SAMPLE_RATE_TIMING, 0x3f) |
FIELD_PREP(MARGINING_SAMPLE_RATE_VOLTAGE, 0x3f);
reg &= ~MARGINING_IND_UP_DOWN_VOLTAGE;
FIELD_MODIFY(MARGINING_MAXLANES, &reg, pci->num_lanes);
FIELD_MODIFY(MARGINING_SAMPLE_RATE_TIMING, &reg, 0x3f);
FIELD_MODIFY(MARGINING_SAMPLE_RATE_VOLTAGE, &reg, 0x3f);
dw_pcie_writel_dbi(pci, GEN4_LANE_MARGINING_2_OFF, reg);
}
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_lane_margining);

View File

@@ -494,15 +494,13 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
/* Set the L0s Exit Latency to 2us-4us = 0x6 */
offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
val &= ~PCI_EXP_LNKCAP_L0SEL;
val |= FIELD_PREP(PCI_EXP_LNKCAP_L0SEL, 0x6);
FIELD_MODIFY(PCI_EXP_LNKCAP_L0SEL, &val, 0x6);
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
/* Set the L1 Exit Latency to be 32us-64 us = 0x6 */
offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
val &= ~PCI_EXP_LNKCAP_L1EL;
val |= FIELD_PREP(PCI_EXP_LNKCAP_L1EL, 0x6);
FIELD_MODIFY(PCI_EXP_LNKCAP_L1EL, &val, 0x6);
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, val);
dw_pcie_dbi_ro_wr_dis(pci);

View File

@@ -71,6 +71,7 @@
/* ELBI registers */
#define ELBI_SYS_CTRL 0x04
#define ELBI_SYS_STTS 0x08
/* DBI registers */
#define AXI_MSTR_RESP_COMP_CTRL0 0x818
@@ -131,6 +132,7 @@
/* PARF_LTSSM register fields */
#define LTSSM_EN BIT(8)
#define PARF_LTSSM_STATE_MASK GENMASK(5, 0)
/* PARF_NO_SNOOP_OVERRIDE register fields */
#define WR_NO_SNOOP_OVERRIDE_EN BIT(1)
@@ -144,6 +146,10 @@
/* ELBI_SYS_CTRL register fields */
#define ELBI_SYS_CTRL_LT_ENABLE BIT(0)
#define ELBI_SYS_CTRL_PME_TURNOFF_MSG BIT(4)
/* ELBI_SYS_STTS register fields */
#define ELBI_SYS_STTS_LTSSM_STATE_MASK GENMASK(17, 12)
/* AXI_MSTR_RESP_COMP_CTRL0 register fields */
#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4
@@ -245,6 +251,7 @@ struct qcom_pcie_ops {
void (*deinit)(struct qcom_pcie *pcie);
void (*ltssm_enable)(struct qcom_pcie *pcie);
int (*config_sid)(struct qcom_pcie *pcie);
enum dw_pcie_ltssm (*get_ltssm)(struct qcom_pcie *pcie);
};
/**
@@ -269,6 +276,7 @@ struct qcom_pcie_perst {
struct qcom_pcie_port {
struct list_head list;
struct phy *phy;
u32 l1ss_t_power_on;
struct list_head perst;
};
@@ -282,7 +290,7 @@ struct qcom_pcie {
const struct qcom_pcie_cfg *cfg;
struct dentry *debugfs;
struct list_head ports;
bool suspended;
struct gpio_desc *reset;
bool use_pm_opp;
};
@@ -428,6 +436,15 @@ static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
writel(val, pci->elbi_base + ELBI_SYS_CTRL);
}
static enum dw_pcie_ltssm qcom_pcie_2_1_0_get_ltssm(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
u32 val;
val = readl(pci->elbi_base + ELBI_SYS_STTS);
return (enum dw_pcie_ltssm)FIELD_GET(ELBI_SYS_STTS_LTSSM_STATE_MASK, val);
}
static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_1_0 *res = &pcie->res.v2_1_0;
@@ -517,7 +534,7 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie)
u32 val;
int ret;
/* enable PCIe clocks and resets */
/* Force PHY out of lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val &= ~PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
@@ -684,6 +701,12 @@ static int qcom_pcie_get_resources_2_3_2(struct qcom_pcie *pcie)
static void qcom_pcie_deinit_2_3_2(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_3_2 *res = &pcie->res.v2_3_2;
u32 val;
/* Force PHY to lowest power state*/
val = readl(pcie->parf + PARF_PHY_CTRL);
val |= PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
@@ -716,7 +739,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie)
{
u32 val;
/* enable PCIe clocks and resets */
/* Force PHY out of lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val &= ~PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
@@ -780,6 +803,12 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie)
static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0;
u32 val;
/* Force PHY to lowest power state*/
val = readl(pcie->parf + PARF_PHY_CTRL);
val |= PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
reset_control_bulk_assert(res->num_resets, res->resets);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
@@ -848,6 +877,12 @@ static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie)
static void qcom_pcie_deinit_2_3_3(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3;
u32 val;
/* Force PHY to lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val |= PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
}
@@ -903,6 +938,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
u32 val;
/* Force PHY out of lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val &= ~PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
@@ -998,7 +1034,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
/* configure PCIe to RC mode */
writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE);
/* enable PCIe clocks and resets */
/* Force PHY out of lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val &= ~PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
@@ -1069,6 +1105,12 @@ static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie)
static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
u32 val;
/* Force PHY to lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val |= PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
@@ -1173,6 +1215,12 @@ static int qcom_pcie_get_resources_2_9_0(struct qcom_pcie *pcie)
static void qcom_pcie_deinit_2_9_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_9_0 *res = &pcie->res.v2_9_0;
u32 val;
/* Force PHY to lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val |= PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
}
@@ -1213,6 +1261,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
u32 val;
int i;
/* Force PHY out of lowest power state */
val = readl(pcie->parf + PARF_PHY_CTRL);
val &= ~PHY_TEST_PWR_DOWN;
writel(val, pcie->parf + PARF_PHY_CTRL);
@@ -1260,6 +1309,19 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci)
return val & PCI_EXP_LNKSTA_DLLLA;
}
static enum dw_pcie_ltssm qcom_pcie_get_ltssm(struct dw_pcie *pci)
{
struct qcom_pcie *pcie = to_qcom_pcie(pci);
u32 val;
if (pcie->cfg->ops->get_ltssm)
return pcie->cfg->ops->get_ltssm(pcie);
val = readl(pcie->parf + PARF_LTSSM);
return (enum dw_pcie_ltssm)FIELD_GET(PARF_LTSSM_STATE_MASK, val);
}
static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie)
{
struct qcom_pcie_port *port;
@@ -1288,6 +1350,14 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
return 0;
}
static void qcom_pcie_configure_ports(struct qcom_pcie *pcie)
{
struct qcom_pcie_port *port;
list_for_each_entry(port, &pcie->ports, list)
dw_pcie_program_t_power_on(pcie->pci, port->l1ss_t_power_on);
}
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -1304,13 +1374,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_deinit;
ret = pci_pwrctrl_create_devices(pci->dev);
if (ret)
goto err_disable_phy;
if (!pci->suspended) {
ret = pci_pwrctrl_create_devices(pci->dev);
if (ret)
goto err_disable_phy;
}
ret = pci_pwrctrl_power_on_devices(pci->dev);
if (ret)
goto err_pwrctrl_destroy;
if (!pp->skip_pwrctrl_off) {
ret = pci_pwrctrl_power_on_devices(pci->dev);
if (ret)
goto err_pwrctrl_destroy;
}
if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
@@ -1322,6 +1396,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
qcom_pcie_configure_ports(pcie);
qcom_pcie_perst_deassert(pcie);
if (pcie->cfg->ops->config_sid) {
@@ -1335,9 +1411,10 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
err_assert_reset:
qcom_pcie_perst_assert(pcie);
err_pwrctrl_power_off:
pci_pwrctrl_power_off_devices(pci->dev);
if (!pp->skip_pwrctrl_off)
pci_pwrctrl_power_off_devices(pci->dev);
err_pwrctrl_destroy:
if (ret != -EPROBE_DEFER)
if (ret != -EPROBE_DEFER && !pci->suspended)
pci_pwrctrl_destroy_devices(pci->dev);
err_disable_phy:
qcom_pcie_phy_power_off(pcie);
@@ -1354,11 +1431,14 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
qcom_pcie_perst_assert(pcie);
/*
* No need to destroy pwrctrl devices as this function only gets called
* during system suspend as of now.
*/
pci_pwrctrl_power_off_devices(pci->dev);
if (!pci->pp.skip_pwrctrl_off) {
/*
* No need to destroy pwrctrl devices as this function only
* gets called during system suspend as of now.
*/
pci_pwrctrl_power_off_devices(pci->dev);
}
qcom_pcie_phy_power_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
@@ -1368,14 +1448,35 @@ static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
/*
* During system suspend, the Qcom RC driver may turn off the
* analog circuitry of PHY and remove controller votes to save
* power. If the link is in L1SS and the endpoint asserts CLKREQ#
* to exit L1SS, the time required to wake the system and restore
* the PHY/REFCLK may exceed the L1SS exit timing (L10_REFCLK_ON +
* T_COMMONMODE), resulting in Link Down (LDn) and a reset of the
* endpoint. Set this flag to indicate this limitation to client
* drivers so that they can avoid relying on device state being
* preserved during system suspend.
*/
pp->bridge->broken_l1ss_resume = true;
if (pcie->cfg->ops->host_post_init)
pcie->cfg->ops->host_post_init(pcie);
}
static void qcom_pcie_host_pme_turn_off(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
writel(ELBI_SYS_CTRL_PME_TURNOFF_MSG, pci->elbi_base + ELBI_SYS_CTRL);
}
static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
.init = qcom_pcie_host_init,
.deinit = qcom_pcie_host_deinit,
.post_init = qcom_pcie_host_post_init,
.pme_turn_off = qcom_pcie_host_pme_turn_off,
};
/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */
@@ -1385,6 +1486,7 @@ static const struct qcom_pcie_ops ops_2_1_0 = {
.post_init = qcom_pcie_post_init_2_1_0,
.deinit = qcom_pcie_deinit_2_1_0,
.ltssm_enable = qcom_pcie_2_1_0_ltssm_enable,
.get_ltssm = qcom_pcie_2_1_0_get_ltssm,
};
/* Qcom IP rev.: 1.0.0 Synopsys IP rev.: 4.11a */
@@ -1394,6 +1496,7 @@ static const struct qcom_pcie_ops ops_1_0_0 = {
.post_init = qcom_pcie_post_init_1_0_0,
.deinit = qcom_pcie_deinit_1_0_0,
.ltssm_enable = qcom_pcie_2_1_0_ltssm_enable,
.get_ltssm = qcom_pcie_2_1_0_get_ltssm,
};
/* Qcom IP rev.: 2.3.2 Synopsys IP rev.: 4.21a */
@@ -1473,6 +1576,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = {
static const struct qcom_pcie_cfg cfg_1_34_0 = {
.ops = &ops_1_9_0,
.override_no_snoop = true,
.no_l0s = true,
};
static const struct qcom_pcie_cfg cfg_2_1_0 = {
@@ -1512,6 +1616,7 @@ static const struct qcom_pcie_cfg cfg_fw_managed = {
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
.start_link = qcom_pcie_start_link,
.get_ltssm = qcom_pcie_get_ltssm,
};
static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
@@ -1613,6 +1718,22 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)
}
}
static int qcom_pcie_set_max_opp(struct device *dev)
{
unsigned long max_freq = ULONG_MAX;
struct dev_pm_opp *opp;
int ret;
opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
if (IS_ERR(opp))
return PTR_ERR(opp);
ret = dev_pm_opp_set_opp(dev, opp);
dev_pm_opp_put(opp);
return ret;
}
static int qcom_pcie_link_transition_count(struct seq_file *s, void *data)
{
struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private);
@@ -1674,6 +1795,12 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
pci->dbi_base = cfg->win;
pp->num_vectors = MSI_DEF_NUM_VECTORS;
/*
* dw_pcie_msi_host_init() is called directly here, bypassing
* dw_pcie_host_init() where pp->lock is normally initialized.
*/
raw_spin_lock_init(&pp->lock);
ret = dw_pcie_msi_host_init(pp);
if (ret)
return ret;
@@ -1703,6 +1830,13 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
struct gpio_desc *reset;
int ret;
if (pcie->reset) {
dev_warn_once(dev,
"Reusing PERST# from Root Complex node. DT needs to be fixed!\n");
reset = pcie->reset;
goto skip_perst_parsing;
}
if (!of_find_property(np, "reset-gpios", NULL))
goto parse_child_node;
@@ -1721,6 +1855,7 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
return PTR_ERR(reset);
}
skip_perst_parsing:
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
@@ -1764,6 +1899,9 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
if (ret)
return ret;
/* TODO: Move to DWC core after multi Root Port support is added */
of_property_read_u32(node, "t-power-on-us", &port->l1ss_t_power_on);
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
@@ -1778,6 +1916,13 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
struct device *dev = pcie->pci->dev;
int ret = -ENODEV;
if (of_find_property(dev->of_node, "perst-gpios", NULL)) {
pcie->reset = devm_gpiod_get_optional(dev, "perst",
GPIOD_OUT_HIGH);
if (IS_ERR(pcie->reset))
return PTR_ERR(pcie->reset);
}
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
continue;
@@ -1804,7 +1949,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
struct device *dev = pcie->pci->dev;
struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
struct gpio_desc *reset;
struct phy *phy;
int ret;
@@ -1812,10 +1956,6 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
if (IS_ERR(phy))
return PTR_ERR(phy);
reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
if (IS_ERR(reset))
return PTR_ERR(reset);
ret = phy_init(phy);
if (ret)
return ret;
@@ -1832,7 +1972,7 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
perst->desc = reset;
perst->desc = pcie->reset;
INIT_LIST_HEAD(&port->perst);
INIT_LIST_HEAD(&perst->list);
list_add_tail(&perst->list, &port->perst);
@@ -1845,9 +1985,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
struct qcom_pcie_perst *perst, *tmp_perst;
struct qcom_pcie_port *port, *tmp_port;
const struct qcom_pcie_cfg *pcie_cfg;
unsigned long max_freq = ULONG_MAX;
struct device *dev = &pdev->dev;
struct dev_pm_opp *opp;
struct qcom_pcie *pcie;
struct dw_pcie_rp *pp;
struct resource *res;
@@ -1951,21 +2089,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
* probe(), OPP will be updated using qcom_pcie_icc_opp_update().
*/
if (!ret) {
opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
dev_err_probe(pci->dev, ret,
"Unable to find max freq OPP\n");
goto err_pm_runtime_put;
} else {
ret = dev_pm_opp_set_opp(dev, opp);
}
dev_pm_opp_put(opp);
ret = qcom_pcie_set_max_opp(dev);
if (ret) {
dev_err_probe(pci->dev, ret,
"Failed to set OPP for freq %lu\n",
max_freq);
dev_err_probe(dev, ret, "Failed to set max OPP\n");
goto err_pm_runtime_put;
}
@@ -2039,53 +2165,51 @@ static int qcom_pcie_suspend_noirq(struct device *dev)
if (!pcie)
return 0;
/*
* Set minimum bandwidth required to keep data path functional during
* suspend.
*/
if (pcie->icc_mem) {
ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1));
if (ret) {
dev_err(dev,
"Failed to set bandwidth for PCIe-MEM interconnect path: %d\n",
ret);
return ret;
}
}
ret = dw_pcie_suspend_noirq(pcie->pci);
if (ret)
return ret;
/*
* Turn OFF the resources only for controllers without active PCIe
* devices. For controllers with active devices, the resources are kept
* ON and the link is expected to be in L0/L1 (sub)states.
*
* Turning OFF the resources for controllers with active PCIe devices
* will trigger access violation during the end of the suspend cycle,
* as kernel tries to access the PCIe devices config space for masking
* MSIs.
*
* Also, it is not desirable to put the link into L2/L3 state as that
* implies VDD supply will be removed and the devices may go into
* powerdown state. This will affect the lifetime of the storage devices
* like NVMe.
*/
if (!dw_pcie_link_up(pcie->pci)) {
qcom_pcie_host_deinit(&pcie->pci->pp);
pcie->suspended = true;
}
if (pcie->pci->suspended) {
ret = icc_disable(pcie->icc_mem);
if (ret)
dev_err(dev, "Failed to disable PCIe-MEM interconnect path: %d\n", ret);
/*
* Only disable CPU-PCIe interconnect path if the suspend is non-S2RAM.
* Because on some platforms, DBI access can happen very late during the
* S2RAM and a non-active CPU-PCIe interconnect path may lead to NoC
* error.
*/
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
ret = icc_disable(pcie->icc_cpu);
if (ret)
dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n", ret);
if (pcie->use_pm_opp)
dev_pm_opp_set_opp(pcie->pci->dev, NULL);
} else {
/*
* Set minimum bandwidth required to keep data path
* functional during suspend.
*/
if (pcie->icc_mem) {
ret = icc_set_bw(pcie->icc_mem, 0, kBps_to_icc(1));
if (ret) {
dev_err(dev,
"Failed to set bandwidth for PCIe-MEM interconnect path: %d\n",
ret);
return ret;
}
}
/*
* Only disable CPU-PCIe interconnect path if the suspend
* is non-S2RAM. On some platforms, DBI access can happen
* very late during S2RAM and a non-active CPU-PCIe
* interconnect path may lead to NoC error.
*/
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
ret = icc_disable(pcie->icc_cpu);
if (ret)
dev_err(dev, "Failed to disable CPU-PCIe interconnect path: %d\n",
ret);
if (pcie->use_pm_opp)
dev_pm_opp_set_opp(pcie->pci->dev, NULL);
}
}
return ret;
}
@@ -2099,25 +2223,62 @@ static int qcom_pcie_resume_noirq(struct device *dev)
if (!pcie)
return 0;
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
if (pcie->pci->suspended) {
if (pcie->use_pm_opp) {
ret = qcom_pcie_set_max_opp(dev);
if (ret) {
dev_err(dev, "Failed to set max OPP: %d\n", ret);
return ret;
}
}
ret = icc_enable(pcie->icc_cpu);
if (ret) {
dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n", ret);
return ret;
}
}
if (pcie->suspended) {
ret = qcom_pcie_host_init(&pcie->pci->pp);
if (ret)
return ret;
ret = icc_enable(pcie->icc_mem);
if (ret) {
dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret);
goto disable_icc_cpu;
}
pcie->suspended = false;
/*
* Ignore -ENODEV & -EIO here since it is expected when no
* endpoint is connected to the PCIe link.
*/
ret = dw_pcie_resume_noirq(pcie->pci);
if (ret && ret != -ENODEV && ret != -EIO)
goto disable_icc_mem;
} else {
if (pm_suspend_target_state != PM_SUSPEND_MEM) {
if (pcie->use_pm_opp) {
ret = qcom_pcie_set_max_opp(dev);
if (ret) {
dev_err(dev, "Failed to set max OPP: %d\n", ret);
return ret;
}
}
ret = icc_enable(pcie->icc_cpu);
if (ret) {
dev_err(dev, "Failed to enable CPU-PCIe interconnect path: %d\n",
ret);
return ret;
}
}
}
qcom_pcie_icc_opp_update(pcie);
return 0;
disable_icc_mem:
icc_disable(pcie->icc_mem);
disable_icc_cpu:
icc_disable(pcie->icc_cpu);
return ret;
}
static const struct of_device_id qcom_pcie_match[] = {

View File

@@ -86,11 +86,11 @@ static void __iomem *tegra194_map_bus(struct pci_bus *bus,
if (bus->parent->number == cfg->busr.start) {
if (PCI_SLOT(devfn) == 0)
type = PCIE_ATU_TYPE_CFG0;
type = PCIE_TLP_TYPE_CFG0_RDWR;
else
return NULL;
} else {
type = PCIE_ATU_TYPE_CFG1;
type = PCIE_TLP_TYPE_CFG1_RDWR;
}
program_outbound_atu(pcie_ecam, 0, type, cfg->res.start, busdev,

View File

@@ -272,6 +272,7 @@ struct tegra_pcie_dw {
u32 aspm_cmrt;
u32 aspm_pwr_on_t;
u32 aspm_l0s_enter_lat;
u32 aspm_l1_enter_lat;
struct regulator *pex_ctl_supply;
struct regulator *slot_ctl_3v3;
@@ -715,6 +716,8 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK;
val |= (pcie->aspm_l0s_enter_lat << PORT_AFR_L0S_ENTRANCE_LAT_SHIFT);
val &= ~PORT_AFR_L1_ENTRANCE_LAT_MASK;
val |= (pcie->aspm_l1_enter_lat << PORT_AFR_L1_ENTRANCE_LAT_SHIFT);
val |= PORT_AFR_ENTER_ASPM;
dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val);
}
@@ -872,8 +875,7 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC;
val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, 0x3ff);
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, 0x3ff);
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
@@ -883,9 +885,8 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
val &= ~GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC;
val |= FIELD_PREP(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC,
pcie->of_data->gen4_preset_vec);
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val,
pcie->of_data->gen4_preset_vec);
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
@@ -1115,6 +1116,7 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
{
struct platform_device *pdev = to_platform_device(pcie->dev);
struct device_node *np = pcie->dev->of_node;
u32 val;
int ret;
pcie->dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
@@ -1141,6 +1143,15 @@ static int tegra_pcie_dw_parse_dt(struct tegra_pcie_dw *pcie)
dev_info(pcie->dev,
"Failed to read ASPM L0s Entrance latency: %d\n", ret);
/* Default to max latency of 7. */
pcie->aspm_l1_enter_lat = 7;
ret = of_property_read_u32(np, "aspm-l1-entry-delay-ns", &val);
if (!ret) {
u32 us = DIV_ROUND_UP(val, 1000);
pcie->aspm_l1_enter_lat = min_t(u32, order_base_2(us), 7);
}
ret = of_property_read_u32(np, "num-lanes", &pcie->num_lanes);
if (ret < 0) {
dev_err(pcie->dev, "Failed to read num-lanes: %d\n", ret);

View File

@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0
/*
* DWC PCIe RC driver for UltraRISC SoCs
*
* Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/resource.h>
#include "pcie-designware.h"
#define PCIE_CUS_CORE 0x400000
#define LTSSM_ENABLE BIT(7)
#define FAST_LINK_MODE BIT(12)
#define HOLD_PHY_RST BIT(14)
#define L1SUB_DISABLE BIT(15)
#define ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS 0x6
static struct pci_ops ultrarisc_pci_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write32,
};
static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct pci_host_bridge *bridge = pp->bridge;
u8 cap_exp;
u32 val;
bridge->ops = &ultrarisc_pci_ops;
if (dw_pcie_link_up(pci))
return 0;
val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE);
val &= ~FAST_LINK_MODE;
dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val);
val = dw_pcie_readl_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM);
FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64);
dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val);
cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2);
FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT);
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val);
val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
FIELD_MODIFY(PORT_LINK_NUM_MASK, &val, 0);
dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_DEVCTL2);
FIELD_MODIFY(PCI_EXP_DEVCTL2_COMP_TIMEOUT, &val,
ULTRARISC_PCIE_COMP_TIMEOUT_65_210MS);
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_DEVCTL2, val);
val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE);
val &= ~(HOLD_PHY_RST | L1SUB_DISABLE);
dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val);
return 0;
}
static void ultrarisc_pcie_pme_turn_off(struct dw_pcie_rp *pp)
{
/*
* DP1000 does not support sending PME_Turn_Off from the RC.
* Keep this callback empty to skip the generic MSG TLP path.
*/
}
static const struct dw_pcie_host_ops ultrarisc_pcie_host_ops = {
.init = ultrarisc_pcie_host_init,
.pme_turn_off = ultrarisc_pcie_pme_turn_off,
};
static int ultrarisc_pcie_start_link(struct dw_pcie *pci)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_CUS_CORE);
val |= LTSSM_ENABLE;
dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val);
return 0;
}
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = ultrarisc_pcie_start_link,
};
static int ultrarisc_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_pcie_rp *pp;
struct dw_pcie *pci;
int ret;
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
if (!pci)
return -ENOMEM;
pci->dev = dev;
pci->ops = &dw_pcie_ops;
/* Set a default value suitable for at most 16 in and 16 out windows */
pci->atu_size = SZ_8K;
pp = &pci->pp;
platform_set_drvdata(pdev, pci);
pp->num_vectors = MAX_MSI_IRQS;
/* No L2/L3 Ready indication is available on this platform */
pp->skip_l23_ready = true;
pp->ops = &ultrarisc_pcie_host_ops;
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "Failed to initialize host\n");
return ret;
}
return 0;
}
static int ultrarisc_pcie_suspend_noirq(struct device *dev)
{
struct dw_pcie *pci = dev_get_drvdata(dev);
return dw_pcie_suspend_noirq(pci);
}
static int ultrarisc_pcie_resume_noirq(struct device *dev)
{
struct dw_pcie *pci = dev_get_drvdata(dev);
return dw_pcie_resume_noirq(pci);
}
static const struct dev_pm_ops ultrarisc_pcie_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(ultrarisc_pcie_suspend_noirq,
ultrarisc_pcie_resume_noirq)
};
static const struct of_device_id ultrarisc_pcie_of_match[] = {
{
.compatible = "ultrarisc,dp1000-pcie",
},
{},
};
MODULE_DEVICE_TABLE(of, ultrarisc_pcie_of_match);
static struct platform_driver ultrarisc_pcie_driver = {
.driver = {
.name = "ultrarisc-pcie",
.of_match_table = ultrarisc_pcie_of_match,
.suppress_bind_attrs = true,
.pm = &ultrarisc_pcie_pm_ops,
},
.probe = ultrarisc_pcie_probe,
};
builtin_platform_driver(ultrarisc_pcie_driver);
MODULE_DESCRIPTION("UltraRISC DP1000 DWC PCIe host controller");
MODULE_LICENSE("GPL");

View File

@@ -10,7 +10,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/iopoll.h>

View File

@@ -26,6 +26,7 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include "pci-host-common.h"
#include "../pci.h"
#include "../pci-bridge-emul.h"
@@ -350,8 +351,10 @@ static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
/* check if the link is up or not */
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
if (advk_pcie_link_up(pcie))
if (advk_pcie_link_up(pcie)) {
pci_host_common_link_train_delay(pcie->link_gen);
return 0;
}
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
}

View File

@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@@ -17,6 +18,178 @@
#include "pci-host-common.h"
/**
* pci_host_common_delete_ports - Cleanup function for port list
* @data: Pointer to the port list head
*/
void pci_host_common_delete_ports(void *data)
{
struct list_head *ports = data;
struct pci_host_perst *perst, *tmp_perst;
struct pci_host_port *port, *tmp_port;
list_for_each_entry_safe(port, tmp_port, ports, list) {
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
list_del(&perst->list);
list_del(&port->list);
}
}
EXPORT_SYMBOL_GPL(pci_host_common_delete_ports);
/**
* pci_host_common_parse_perst - Parse PERST# from all nodes, depth first
* @dev: Device pointer
* @port: PCI host port
* @np: Device tree node to start parsing from
*
* Recursively parse PERST# GPIO from all PCIe bridge nodes starting from
* @np in a depth-first manner.
*
* Return: 0 on success, negative error code on failure.
*/
static int pci_host_common_parse_perst(struct device *dev,
struct pci_host_port *port,
struct device_node *np)
{
struct pci_host_perst *perst;
struct gpio_desc *reset;
int ret;
if (!of_property_present(np, "reset-gpios"))
goto parse_child_node;
reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
GPIOD_ASIS, "PERST#");
if (IS_ERR(reset)) {
/*
* FIXME: GPIOLIB currently supports exclusive GPIO access only.
* Non exclusive access is broken. But shared PERST# requires
* non-exclusive access. So once GPIOLIB properly supports it,
* implement it here.
*/
if (PTR_ERR(reset) == -EBUSY)
dev_err(dev, "Shared PERST# is not supported\n");
return PTR_ERR(reset);
}
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
INIT_LIST_HEAD(&perst->list);
perst->desc = reset;
list_add_tail(&perst->list, &port->perst);
parse_child_node:
for_each_available_child_of_node_scoped(np, child) {
ret = pci_host_common_parse_perst(dev, port, child);
if (ret)
return ret;
}
return 0;
}
/**
* pci_host_common_parse_port - Parse a single Root Port node
* @dev: Device pointer
* @bridge: PCI host bridge
* @node: Device tree node of the Root Port
*
* Parse Root Port properties from the device tree. Currently it only
* handles the PERST# GPIO (including PERST# GPIOs from all PCIe bridge
* nodes under this Root Port), which is optional.
*
* NOTE: This helper fetches resources (like PERST# GPIO) optionally. If a
* controller driver has a hard dependency on certain resources (PHY,
* clocks, regulators, etc.), those resources MUST be modeled correctly in
* the DT binding and validated in DTS. This helper cannot enforce such
* dependencies and the driver may fail to operate if required resources
* are missing.
*
* Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding
* should be used), Other negative error codes on failure.
*/
static int pci_host_common_parse_port(struct device *dev,
struct pci_host_bridge *bridge,
struct device_node *node)
{
struct pci_host_port *port;
int ret;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
INIT_LIST_HEAD(&port->perst);
ret = pci_host_common_parse_perst(dev, port, node);
if (ret)
return ret;
/*
* 1. PERST# found in RP or its child nodes - list is not empty,
* continue
*
* 2. PERST# not found in RP/children, but found in RC node -
* return -ENODEV to fallback legacy binding
*
* 3. PERST# not found anywhere - list is empty, continue (optional
* PERST#)
*/
if (list_empty(&port->perst)) {
if (of_property_present(dev->of_node, "reset-gpios") ||
of_property_present(dev->of_node, "reset-gpio"))
return -ENODEV;
}
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
return 0;
}
/**
* pci_host_common_parse_ports - Parse Root Port nodes from device tree
* @dev: Device pointer
* @bridge: PCI host bridge
*
* Iterate through child nodes of the host bridge and parse Root Port
* properties (currently only reset GPIOs).
*
* Return: 0 on success, -ENODEV if no ports found or PERST# found in RC
* node (legacy binding should be used), Other negative error codes on
* failure.
*/
int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge)
{
int ret = -ENODEV;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
continue;
ret = pci_host_common_parse_port(dev, bridge, of_port);
if (ret)
goto err_cleanup;
}
if (ret)
return ret;
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);
err_cleanup:
pci_host_common_delete_ports(&bridge->ports);
return ret;
}
EXPORT_SYMBOL_GPL(pci_host_common_parse_ports);
#define PCI_HOST_D3COLD_ALLOWED BIT(0)
#define PCI_HOST_PME_D3COLD_CAPABLE BIT(1)
static void gen_pci_unmap_cfg(void *ptr)
{
pci_ecam_free((struct pci_config_window *)ptr);
@@ -68,6 +241,10 @@ int pci_host_common_init(struct platform_device *pdev,
if (IS_ERR(cfg))
return PTR_ERR(cfg);
/* Do not reassign bus numbers if probe only */
if (!pci_has_flag(PCI_PROBE_ONLY))
pci_add_flags(PCI_REASSIGN_ALL_BUS);
bridge->sysdata = cfg;
bridge->ops = (struct pci_ops *)&ops->pci_ops;
bridge->enable_device = ops->enable_device;
@@ -106,5 +283,64 @@ void pci_host_common_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(pci_host_common_remove);
static int __pci_host_common_d3cold_possible(struct pci_dev *pdev,
void *userdata)
{
u32 *flags = userdata;
if (!pdev->dev.driver && !pci_is_enabled(pdev))
return 0;
if (pdev->current_state != PCI_D3hot)
goto exit;
if (device_may_wakeup(&pdev->dev)) {
if (!pci_pme_capable(pdev, PCI_D3cold))
goto exit;
else
*flags |= PCI_HOST_PME_D3COLD_CAPABLE;
}
return 0;
exit:
*flags &= ~PCI_HOST_D3COLD_ALLOWED;
return -EOPNOTSUPP;
}
/**
* pci_host_common_d3cold_possible - Determine whether the host bridge can
* transition the devices into D3cold.
*
* @bridge: PCI host bridge to check
* @pme_capable: Pointer to update if there is any device capable of generating
* PME from D3cold.
*
* Walk downstream PCIe endpoint devices and determine whether the host bridge
* is permitted to transition the devices into D3cold.
*
* Devices under host bridge can enter D3cold only if all active PCIe
* endpoints are in PCI_D3hot and any wakeup-enabled endpoint is capable of
* generating PME from D3cold. Inactive endpoints are ignored.
*
* The @pme_capable output allows PCIe controller drivers to apply
* platform-specific handling to preserve wakeup functionality.
*
* Return: %true if the host bridge may enter D3cold, otherwise %false.
*/
bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
bool *pme_capable)
{
u32 flags = PCI_HOST_D3COLD_ALLOWED;
pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags);
*pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE);
return !!(flags & PCI_HOST_D3COLD_ALLOWED);
}
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);
MODULE_DESCRIPTION("Common library for PCI host controller drivers");
MODULE_LICENSE("GPL v2");

View File

@@ -10,8 +10,39 @@
#ifndef _PCI_HOST_COMMON_H
#define _PCI_HOST_COMMON_H
#include <linux/delay.h>
#include "../pci.h"
struct pci_ecam_ops;
/**
* struct pci_host_perst - PERST# GPIO descriptor
* @list: List node for linking multiple PERST# GPIOs
* @desc: GPIO descriptor for PERST# signal
*
* This structure holds a single PERST# GPIO descriptor.
*/
struct pci_host_perst {
struct list_head list;
struct gpio_desc *desc;
};
/**
* struct pci_host_port - Generic Root Port properties
* @list: List node for linking multiple ports
* @perst: List of PERST# GPIO descriptors for this port and its children
*
* This structure contains common properties that can be parsed from
* Root Port device tree nodes.
*/
struct pci_host_port {
struct list_head list;
struct list_head perst;
};
void pci_host_common_delete_ports(void *data);
int pci_host_common_parse_ports(struct device *dev,
struct pci_host_bridge *bridge);
int pci_host_common_probe(struct platform_device *pdev);
int pci_host_common_init(struct platform_device *pdev,
struct pci_host_bridge *bridge,
@@ -20,4 +51,21 @@ void pci_host_common_remove(struct platform_device *pdev);
struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops);
bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
bool *pme_capable);
/**
* pci_host_common_link_train_delay - Wait 100 ms if link speed > 5 GT/s
* @max_link_speed: the maximum link speed (2 = 5.0 GT/s, 3 = 8.0 GT/s, ...)
*
* Must be called after Link training completes and before the first
* Configuration Request is sent.
*/
static inline void pci_host_common_link_train_delay(int max_link_speed)
{
if (max_link_speed > 2)
msleep(PCIE_RESET_CONFIG_WAIT_MS);
}
#endif

View File

@@ -80,6 +80,18 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON,
DEV_LS7A_LPC, system_bus_quirk);
static const struct pci_device_id loongson_internal_bridge_devids[] = {
{ PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) },
{ 0, },
};
/*
* Some Loongson PCIe ports have hardware limitations on their Maximum Read
* Request Size. They can't handle anything larger than this. Sane
@@ -92,24 +104,13 @@ static void loongson_set_min_mrrs_quirk(struct pci_dev *pdev)
{
struct pci_bus *bus = pdev->bus;
struct pci_dev *bridge;
static const struct pci_device_id bridge_devids[] = {
{ PCI_VDEVICE(LOONGSON, DEV_LS2K_PCIE_PORT0) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT0) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT1) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT2) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT3) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT4) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT5) },
{ PCI_VDEVICE(LOONGSON, DEV_LS7A_PCIE_PORT6) },
{ 0, },
};
/* look for the matching bridge */
while (!pci_is_root_bus(bus)) {
bridge = bus->self;
bus = bus->parent;
if (pci_match_id(bridge_devids, bridge)) {
if (pci_match_id(loongson_internal_bridge_devids, bridge)) {
if (pcie_get_readrq(pdev) > 256) {
pci_info(pdev, "limiting MRRS to 256\n");
pcie_set_readrq(pdev, 256);
@@ -176,6 +177,42 @@ static void loongson_pci_msi_quirk(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_PCIE_PORT5, loongson_pci_msi_quirk);
/*
* Older steppings of the Loongson-3C6000 series incorrectly report the
* supported link speeds on their PCIe bridges (device IDs 0x3c19,
* 0x3c29) as only 2.5 GT/s, despite the upstream bus supporting speeds
* from 2.5 GT/s up to 16 GT/s.
*/
static void loongson_pci_bridge_speed_quirk(struct pci_dev *pdev)
{
u8 old_supported_speeds = pdev->supported_speeds;
switch (pdev->bus->max_bus_speed) {
case PCIE_SPEED_16_0GT:
pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_16_0GB;
fallthrough;
case PCIE_SPEED_8_0GT:
pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_8_0GB;
fallthrough;
case PCIE_SPEED_5_0GT:
pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_5_0GB;
fallthrough;
case PCIE_SPEED_2_5GT:
pdev->supported_speeds |= PCI_EXP_LNKCAP2_SLS_2_5GB;
break;
default:
pci_warn(pdev, "unexpected max bus speed");
return;
}
if (pdev->supported_speeds != old_supported_speeds)
pci_info(pdev, "fixed up supported link speeds: 0x%x => 0x%x",
old_supported_speeds, pdev->supported_speeds);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c19, loongson_pci_bridge_speed_quirk);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_LOONGSON, 0x3c29, loongson_pci_bridge_speed_quirk);
static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus)
{
struct pci_config_window *cfg;
@@ -230,11 +267,11 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus,
struct loongson_pci *priv = pci_bus_to_loongson_pci(bus);
/*
* Do not read more than one device on the bus other than
* the host bus.
* Do not read more than one device on the internal bridges.
*/
if ((priv->data->flags & FLAG_DEV_FIX) && bus->self) {
if (!pci_is_root_bus(bus) && (device > 0))
if (!pci_is_root_bus(bus) && (device > 0) &&
pci_match_id(loongson_internal_bridge_devids, bus->self))
return NULL;
}

View File

@@ -57,9 +57,9 @@
#define PCIE_CONF_DATA_OFF 0x18fc
#define PCIE_INT_CAUSE_OFF 0x1900
#define PCIE_INT_UNMASK_OFF 0x1910
#define PCIE_INT_INTX(i) BIT(24+i)
#define PCIE_INT_PM_PME BIT(28)
#define PCIE_INT_ALL_MASK GENMASK(31, 0)
#define PCIE_INT_INTX(i) BIT_U32(24 + (i))
#define PCIE_INT_PM_PME BIT_U32(28)
#define PCIE_INT_ALL_MASK GENMASK_U32(31, 0)
#define PCIE_CTRL_OFF 0x1a00
#define PCIE_CTRL_X1_MODE 0x0001
#define PCIE_CTRL_RC_MODE BIT(1)
@@ -263,8 +263,7 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
* not set correctly then link with endpoint card is not established.
*/
lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
lnkcap &= ~PCI_EXP_LNKCAP_MLW;
lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, port->is_x4 ? 4 : 1);
FIELD_MODIFY(PCI_EXP_LNKCAP_MLW, &lnkcap, port->is_x4 ? 4 : 1);
mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
/* Disable Root Bridge I/O space, memory space and bus mastering. */

View File

@@ -864,11 +864,25 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
return 0;
}
static void altera_pcie_disable_irq(struct altera_pcie *pcie)
{
if (pcie->pcie_data->version == ALTERA_PCIE_V1 ||
pcie->pcie_data->version == ALTERA_PCIE_V2) {
/* Disable all P2A interrupts */
cra_writel(pcie, 0, P2A_INT_ENABLE);
} else if (pcie->pcie_data->version == ALTERA_PCIE_V3) {
/* Disable port-level interrupts (CFG_AER, etc.) */
writel(0, pcie->hip_base +
pcie->pcie_data->port_conf_offset +
pcie->pcie_data->port_irq_enable_offset);
}
}
static void altera_pcie_irq_teardown(struct altera_pcie *pcie)
{
altera_pcie_disable_irq(pcie);
irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
irq_domain_remove(pcie->irq_domain);
irq_dispose_mapping(pcie->irq);
}
static int altera_pcie_parse_dt(struct altera_pcie *pcie)
@@ -891,7 +905,6 @@ static int altera_pcie_parse_dt(struct altera_pcie *pcie)
if (pcie->irq < 0)
return pcie->irq;
irq_set_chained_handler_and_data(pcie->irq, pcie->pcie_data->ops->rp_isr, pcie);
return 0;
}
@@ -1020,6 +1033,14 @@ static int altera_pcie_probe(struct platform_device *pdev)
return ret;
}
/*
* The chained handler uses pcie->irq_domain, so set it only after the
* INTx domain has been created.
*/
irq_set_chained_handler_and_data(pcie->irq,
pcie->pcie_data->ops->rp_isr,
pcie);
if (pcie->pcie_data->version == ALTERA_PCIE_V1 ||
pcie->pcie_data->version == ALTERA_PCIE_V2) {
/* clear all interrupts */
@@ -1037,7 +1058,16 @@ static int altera_pcie_probe(struct platform_device *pdev)
bridge->busnr = pcie->root_bus_nr;
bridge->ops = &altera_pcie_ops;
return pci_host_probe(bridge);
ret = pci_host_probe(bridge);
if (ret)
goto err_teardown_irq;
return 0;
err_teardown_irq:
altera_pcie_irq_teardown(pcie);
return ret;
}
static void altera_pcie_remove(struct platform_device *pdev)
@@ -1045,8 +1075,10 @@ static void altera_pcie_remove(struct platform_device *pdev)
struct altera_pcie *pcie = platform_get_drvdata(pdev);
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
pci_lock_rescan_remove();
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
pci_unlock_rescan_remove();
altera_pcie_irq_teardown(pcie);
}

View File

@@ -127,19 +127,19 @@
#define CFG0_READ_FMTTYPE \
FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \
PCIE_TLP_TYPE_CFG0_RD))
PCIE_TLP_TYPE_CFG0_RDWR))
#define CFG0_WRITE_FMTTYPE \
FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \
PCIE_TLP_TYPE_CFG0_WR))
PCIE_TLP_TYPE_CFG0_RDWR))
#define CFG1_READ_FMTTYPE \
FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \
PCIE_TLP_TYPE_CFG1_RD))
PCIE_TLP_TYPE_CFG1_RDWR))
#define CFG1_WRITE_FMTTYPE \
FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \
PCIE_TLP_TYPE_CFG1_WR))
PCIE_TLP_TYPE_CFG1_RDWR))
#define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */
#define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4)))
#define TLP_GET_VALUE(x, y, z) \

View File

@@ -1894,8 +1894,10 @@ static void brcm_pcie_remove(struct platform_device *pdev)
struct brcm_pcie *pcie = platform_get_drvdata(pdev);
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
pci_lock_rescan_remove();
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
pci_unlock_rescan_remove();
if (pcie->cfg->has_err_report)
brcm_unregister_die_notifiers(pcie);

View File

@@ -64,7 +64,7 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
if (ret)
return ret;
pcie->map_irq = iproc_bcma_pcie_map_irq;
bridge->map_irq = iproc_bcma_pcie_map_irq;
bcma_set_drvdata(bdev, pcie);

View File

@@ -98,7 +98,7 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev)
switch (pcie->type) {
case IPROC_PCIE_PAXC:
case IPROC_PCIE_PAXC_V2:
pcie->map_irq = NULL;
bridge->map_irq = NULL;
break;
default:
break;

View File

@@ -1502,7 +1502,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
host->ops = &iproc_pcie_ops;
host->sysdata = pcie;
host->map_irq = pcie->map_irq;
ret = pci_host_probe(host);
if (ret < 0) {
@@ -1529,8 +1528,10 @@ void iproc_pcie_remove(struct iproc_pcie *pcie)
{
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
pci_lock_rescan_remove();
pci_stop_root_bus(host->bus);
pci_remove_root_bus(host->bus);
pci_unlock_rescan_remove();
iproc_pcie_msi_disable(pcie);

View File

@@ -61,7 +61,6 @@ struct iproc_msi;
* @base_addr: PCIe host controller register base physical address
* @mem: host bridge memory window resource
* @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts
* @ep_is_internal: indicates an internal emulated endpoint device is connected
* @iproc_cfg_read: indicates the iProc config read function should be used
* @rej_unconfig_pf: indicates the root complex needs to detect and reject
@@ -91,7 +90,6 @@ struct iproc_pcie {
phys_addr_t base_addr;
struct resource mem;
struct phy *phy;
int (*map_irq)(const struct pci_dev *, u8, u8);
bool ep_is_internal;
bool iproc_cfg_read;
bool rej_unconfig_pf;

View File

@@ -30,6 +30,7 @@
#include <linux/regmap.h>
#include <linux/reset.h>
#include "pci-host-common.h"
#include "../pci.h"
#define PCIE_BASE_CFG_REG 0x14
@@ -63,6 +64,12 @@
#define PCIE_BRG_RSTB BIT(2)
#define PCIE_PE_RSTB BIT(3)
/*
* As described in the datasheet of MediaTek PCIe Gen3 controller, wait 10ms
* after setting PCIE_BRG_RSTB, and before accessing PCIe internal registers.
*/
#define PCIE_BRG_RST_RDY_MS 10
#define PCIE_LTSSM_STATUS_REG 0x150
#define PCIE_LTSSM_STATE_MASK GENMASK(28, 24)
#define PCIE_LTSSM_STATE(val) ((val & PCIE_LTSSM_STATE_MASK) >> 24)
@@ -430,6 +437,21 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie)
return err;
}
/*
* Some of MediaTek's chips won't output REFCLK when PCIE_PHY_RSTB is
* asserted, we have to de-assert MAC & PHY & BRG reset signals first
* to allow the REFCLK to be stable. While PCIE_BRG_RSTB is asserted,
* there is a short period during which the PCIe internal register
* cannot be accessed, so we need to wait 10ms here.
*/
msleep(PCIE_BRG_RST_RDY_MS);
if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
/* De-assert MAC, PHY and BRG reset signals */
val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB);
writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
}
/*
* Described in PCIe CEM specification revision 6.0.
*
@@ -439,9 +461,8 @@ static int mtk_pcie_devices_power_up(struct mtk_gen3_pcie *pcie)
msleep(PCIE_T_PVPERL_MS);
if (!(pcie->soc->flags & SKIP_PCIE_RSTB)) {
/* De-assert reset signals */
val &= ~(PCIE_MAC_RSTB | PCIE_PHY_RSTB | PCIE_BRG_RSTB |
PCIE_PE_RSTB);
/* De-assert PERST# signal */
val &= ~PCIE_PE_RSTB;
writel_relaxed(val, pcie->base + PCIE_RST_CTRL_REG);
}
@@ -494,8 +515,7 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
/* Set Link Control 2 (LNKCTL2) speed restriction, if any */
if (pcie->max_link_speed) {
val = readl_relaxed(pcie->base + PCIE_CONF_LINK2_CTL_STS);
val &= ~PCIE_CONF_LINK2_LCR2_LINK_SPEED;
val |= FIELD_PREP(PCIE_CONF_LINK2_LCR2_LINK_SPEED, pcie->max_link_speed);
FIELD_MODIFY(PCIE_CONF_LINK2_LCR2_LINK_SPEED, &val, pcie->max_link_speed);
writel_relaxed(val, pcie->base + PCIE_CONF_LINK2_CTL_STS);
}
@@ -570,6 +590,8 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
goto err_power_down_device;
}
pci_host_common_link_train_delay(pcie->max_link_speed);
return 0;
err_power_down_device:
@@ -1222,8 +1244,8 @@ static int mtk_pcie_probe(struct platform_device *pdev)
err = pci_pwrctrl_create_devices(pcie->dev);
if (err) {
goto err_tear_down_irq;
dev_err_probe(dev, err, "failed to create pwrctrl devices\n");
goto err_tear_down_irq;
}
err = mtk_pcie_setup(pcie);
@@ -1260,12 +1282,20 @@ static void mtk_pcie_remove(struct platform_device *pdev)
pci_remove_root_bus(host->bus);
pci_unlock_rescan_remove();
pci_pwrctrl_power_off_devices(pcie->dev);
mtk_pcie_devices_power_down(pcie);
mtk_pcie_power_down(pcie);
pci_pwrctrl_destroy_devices(pcie->dev);
mtk_pcie_irq_teardown(pcie);
}
static void mtk_pcie_shutdown(struct platform_device *pdev)
{
struct mtk_gen3_pcie *pcie = platform_get_drvdata(pdev);
mtk_pcie_devices_power_down(pcie);
mtk_pcie_power_down(pcie);
}
static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie)
{
int i;
@@ -1404,6 +1434,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_of_match);
static struct platform_driver mtk_pcie_driver = {
.probe = mtk_pcie_probe,
.remove = mtk_pcie_remove,
.shutdown = mtk_pcie_shutdown,
.driver = {
.name = "mtk-pcie-gen3",
.of_match_table = mtk_pcie_of_match,

View File

@@ -7,6 +7,7 @@
* Honghui Zhang <honghui.zhang@mediatek.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/iopoll.h>
@@ -61,7 +62,7 @@
/* MediaTek specific configuration registers */
#define PCIE_FTS_NUM 0x70c
#define PCIE_FTS_NUM_MASK GENMASK(15, 8)
#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
#define PCIE_FTS_NUM_L0(x) FIELD_PREP(PCIE_FTS_NUM_MASK, x)
#define PCIE_FC_CREDIT 0x73c
#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
@@ -111,10 +112,6 @@
#define APP_CFG_REQ BIT(0)
#define APP_CPL_STATUS GENMASK(7, 5)
#define CFG_WRRD_TYPE_0 4
#define CFG_WR_FMT 2
#define CFG_RD_FMT 0
#define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0))
#define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24))
#define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29))
@@ -175,6 +172,7 @@ struct mtk_pcie_soc {
/**
* struct mtk_pcie_port - PCIe port information
* @base: IO mapped register base
* @phys_base: Physical address of the I/O register base region
* @list: port list
* @pcie: pointer to PCIe host info
* @reset: pointer to port reset control
@@ -196,6 +194,7 @@ struct mtk_pcie_soc {
*/
struct mtk_pcie_port {
void __iomem *base;
phys_addr_t phys_base;
struct list_head list;
struct mtk_pcie *pcie;
struct reset_control *reset;
@@ -295,7 +294,7 @@ static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn,
u32 tmp;
/* Write PCIe configuration transaction header for Cfgrd */
writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT),
writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_NO_DATA),
port->base + PCIE_CFG_HEADER0);
writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1);
writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus),
@@ -325,7 +324,7 @@ static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn,
int where, int size, u32 val)
{
/* Write PCIe configuration transaction header for Cfgwr */
writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT),
writel(CFG_HEADER_DW0(PCIE_TLP_TYPE_CFG0_RDWR, PCIE_TLP_FMT_3DW_DATA),
port->base + PCIE_CFG_HEADER0);
writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1);
writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus),
@@ -405,7 +404,7 @@ static void mtk_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
phys_addr_t addr;
/* MT2712/MT7622 only support 32-bit MSI addresses */
addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
addr = port->phys_base + PCIE_MSI_VECTOR;
msg->address_hi = 0;
msg->address_lo = lower_32_bits(addr);
@@ -520,7 +519,7 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
u32 val;
phys_addr_t msg_addr;
msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
msg_addr = port->phys_base + PCIE_MSI_VECTOR;
val = lower_32_bits(msg_addr);
writel(val, port->base + PCIE_IMSI_ADDR);
@@ -529,23 +528,27 @@ static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
writel(val, port->base + PCIE_INT_MASK);
}
static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port)
{
irq_set_chained_handler_and_data(port->irq, NULL, NULL);
if (port->irq_domain)
irq_domain_remove(port->irq_domain);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (port->inner_domain)
irq_domain_remove(port->inner_domain);
}
irq_dispose_mapping(port->irq);
}
static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie)
{
struct mtk_pcie_port *port, *tmp;
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
irq_set_chained_handler_and_data(port->irq, NULL, NULL);
if (port->irq_domain)
irq_domain_remove(port->irq_domain);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (port->inner_domain)
irq_domain_remove(port->inner_domain);
}
irq_dispose_mapping(port->irq);
}
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_irq_teardown_port(port);
}
static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@@ -865,7 +868,7 @@ static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port)
return mtk_pcie_startup_port_v2(port);
}
static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
static int mtk_pcie_enable_port(struct mtk_pcie_port *port)
{
struct mtk_pcie *pcie = port->pcie;
struct device *dev = pcie->dev;
@@ -874,7 +877,7 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
err = clk_prepare_enable(port->sys_ck);
if (err) {
dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot);
goto err_sys_clk;
return err;
}
err = clk_prepare_enable(port->ahb_ck);
@@ -922,11 +925,15 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
goto err_phy_on;
}
if (!pcie->soc->startup(port))
return;
err = pcie->soc->startup(port);
if (err) {
dev_info(dev, "Port%d link down\n", port->slot);
goto err_soc_startup;
}
dev_info(dev, "Port%d link down\n", port->slot);
return 0;
err_soc_startup:
phy_power_off(port->phy);
err_phy_on:
phy_exit(port->phy);
@@ -942,8 +949,8 @@ static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
clk_disable_unprepare(port->ahb_ck);
err_ahb_clk:
clk_disable_unprepare(port->sys_ck);
err_sys_clk:
mtk_pcie_port_free(port);
return err;
}
static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
@@ -953,6 +960,7 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
struct mtk_pcie_port *port;
struct device *dev = pcie->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
char name[20];
int err;
@@ -961,7 +969,14 @@ static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
return -ENOMEM;
snprintf(name, sizeof(name), "port%d", slot);
port->base = devm_platform_ioremap_resource_byname(pdev, name);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
if (!res) {
dev_err(dev, "failed to get port%d base\n", slot);
return -EINVAL;
}
port->phys_base = res->start;
port->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(port->base)) {
dev_err(dev, "failed to map port%d base\n", slot);
return PTR_ERR(port->base);
@@ -1109,8 +1124,13 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
return err;
/* enable each port, and then check link status */
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_enable_port(port);
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
err = mtk_pcie_enable_port(port);
if (err) {
mtk_pcie_irq_teardown_port(port);
mtk_pcie_port_free(port);
}
}
/* power down PCIe subsys if slots are all empty (link down) */
if (list_empty(&pcie->ports))
@@ -1172,8 +1192,10 @@ static void mtk_pcie_remove(struct platform_device *pdev)
struct mtk_pcie *pcie = platform_get_drvdata(pdev);
struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
pci_lock_rescan_remove();
pci_stop_root_bus(host->bus);
pci_remove_root_bus(host->bus);
pci_unlock_rescan_remove();
mtk_pcie_free_resources(pcie);
mtk_pcie_irq_teardown(pcie);
@@ -1209,14 +1231,18 @@ static int mtk_pcie_resume_noirq(struct device *dev)
{
struct mtk_pcie *pcie = dev_get_drvdata(dev);
struct mtk_pcie_port *port, *tmp;
int err;
if (list_empty(&pcie->ports))
return 0;
clk_prepare_enable(pcie->free_ck);
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_enable_port(port);
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
err = mtk_pcie_enable_port(port);
if (err)
mtk_pcie_port_free(port);
}
/* In case of EP was removed while system suspend. */
if (list_empty(&pcie->ports))

View File

@@ -346,7 +346,6 @@ static void rcar_pcie_hw_enable(struct rcar_pcie_host *host)
struct rcar_pcie *pcie = &host->pcie;
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(host);
struct resource_entry *win;
LIST_HEAD(res);
int i = 0;
/* Try setting 5 GT/s link speed */

View File

@@ -1012,8 +1012,10 @@ static void rockchip_pcie_remove(struct platform_device *pdev)
struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip);
pci_lock_rescan_remove();
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
pci_unlock_rescan_remove();
irq_domain_remove(rockchip->irq_domain);
rockchip_pcie_deinit_phys(rockchip);

View File

@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <linux/units.h>
#include "pci-host-common.h"
#include "../pci.h"
/* AXI registers */
@@ -1663,7 +1664,7 @@ rzg3s_pcie_host_setup(struct rzg3s_pcie_host *host,
if (ret)
dev_info(dev, "Failed to set max link speed\n");
msleep(PCIE_RESET_CONFIG_WAIT_MS);
pci_host_common_link_train_delay(host->max_link_speed);
return 0;

View File

@@ -640,8 +640,10 @@ EXPORT_SYMBOL_GPL(plda_pcie_host_init);
void plda_pcie_host_deinit(struct plda_pcie_rp *port)
{
pci_lock_rescan_remove();
pci_stop_root_bus(port->bridge->bus);
pci_remove_root_bus(port->bridge->bus);
pci_unlock_rescan_remove();
plda_pcie_irq_domain_deinit(port);

View File

@@ -559,12 +559,15 @@ static int epf_ntb_configure_db(struct epf_ntb *ntb,
struct pci_epc *epc;
int ret;
if (db_count > MAX_DB_COUNT)
return -EINVAL;
ntb_epc = ntb->epc[type];
epc = ntb_epc->epc;
if (!db_count || db_count > MAX_DB_COUNT) {
dev_err(&epc->dev, "DB count %d out of range (1 - %d)\n",
db_count, MAX_DB_COUNT);
return -EINVAL;
}
if (msix)
ret = epf_ntb_configure_msix(ntb, type, db_count);
else
@@ -1278,7 +1281,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
u8 func_no, vfunc_no;
struct pci_epc *epc;
struct device *dev;
u32 db_count;
int ret;
ntb_epc = ntb->epc[type];
@@ -1296,17 +1298,16 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
func_no = ntb_epc->func_no;
vfunc_no = ntb_epc->vfunc_no;
db_count = ntb->db_count;
if (db_count > MAX_DB_COUNT) {
dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT);
if (!ntb->db_count || ntb->db_count > MAX_DB_COUNT) {
dev_err(dev, "DB count %d out of range (1 - %d)\n",
ntb->db_count, MAX_DB_COUNT);
return -EINVAL;
}
ntb->db_count = db_count;
epc = ntb_epc->epc;
if (msi_capable) {
ret = pci_epc_set_msi(epc, func_no, vfunc_no, db_count);
ret = pci_epc_set_msi(epc, func_no, vfunc_no, ntb->db_count);
if (ret) {
dev_err(dev, "%s intf: MSI configuration failed\n",
pci_epc_interface_string(type));
@@ -1315,7 +1316,7 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb,
}
if (msix_capable) {
ret = pci_epc_set_msix(epc, func_no, vfunc_no, db_count,
ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count,
ntb_epc->msix_bar,
ntb_epc->msix_table_offset);
if (ret) {

View File

@@ -94,6 +94,7 @@ struct pci_epf_test {
bool dma_private;
const struct pci_epc_features *epc_features;
struct pci_epf_bar db_bar;
bool db_bar_programmed;
size_t bar_size[PCI_STD_NUM_BARS];
};
@@ -733,7 +734,9 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
{
u32 status = le32_to_cpu(reg->status);
struct pci_epf *epf = epf_test->epf;
struct pci_epf_doorbell_msg *db;
struct pci_epc *epc = epf->epc;
unsigned long irq_flags;
struct msi_msg *msg;
enum pci_barno bar;
size_t offset;
@@ -743,13 +746,28 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
if (ret)
goto set_status_err;
msg = &epf->db_msg[0].msg;
bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
if (bar < BAR_0)
goto err_doorbell_cleanup;
db = &epf->db_msg[0];
msg = &db->msg;
epf_test->db_bar_programmed = false;
if (db->bar != NO_BAR) {
/*
* The doorbell target is already exposed via a platform-owned
* fixed BAR
*/
bar = db->bar;
offset = db->offset;
} else {
bar = pci_epc_get_next_free_bar(epf_test->epc_features,
epf_test->test_reg_bar + 1);
if (bar < BAR_0)
goto err_doorbell_cleanup;
}
irq_flags = epf->db_msg[0].irq_flags | IRQF_ONESHOT;
ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
pci_epf_test_doorbell_handler, IRQF_ONESHOT,
pci_epf_test_doorbell_handler, irq_flags,
"pci-ep-test-doorbell", epf_test);
if (ret) {
dev_err(&epf->dev,
@@ -761,22 +779,30 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
reg->doorbell_data = cpu_to_le32(msg->data);
reg->doorbell_bar = cpu_to_le32(bar);
msg = &epf->db_msg[0].msg;
ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo,
&epf_test->db_bar.phys_addr, &offset);
if (db->bar == NO_BAR) {
ret = pci_epf_align_inbound_addr(epf, bar,
((u64)msg->address_hi << 32) |
msg->address_lo,
&epf_test->db_bar.phys_addr,
&offset);
if (ret)
goto err_free_irq;
if (ret)
goto err_free_irq;
}
reg->doorbell_offset = cpu_to_le32(offset);
epf_test->db_bar.barno = bar;
epf_test->db_bar.size = epf->bar[bar].size;
epf_test->db_bar.flags = epf->bar[bar].flags;
if (db->bar == NO_BAR) {
epf_test->db_bar.barno = bar;
epf_test->db_bar.size = epf->bar[bar].size;
epf_test->db_bar.flags = epf->bar[bar].flags;
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
if (ret)
goto err_free_irq;
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
if (ret)
goto err_free_irq;
epf_test->db_bar_programmed = true;
}
status |= STATUS_DOORBELL_ENABLE_SUCCESS;
reg->status = cpu_to_le32(status);
@@ -806,17 +832,23 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
free_irq(epf->db_msg[0].virq, epf_test);
pci_epf_test_doorbell_cleanup(epf_test);
/*
* The doorbell feature temporarily overrides the inbound translation
* to point to the address stored in epf_test->db_bar.phys_addr, i.e.,
* it calls set_bar() twice without ever calling clear_bar(), as
* calling clear_bar() would clear the BAR's PCI address assigned by
* the host. Thus, when disabling the doorbell, restore the inbound
* translation to point to the memory allocated for the BAR.
*/
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]);
if (ret)
goto set_status_err;
if (epf_test->db_bar_programmed) {
/*
* The doorbell feature temporarily overrides the inbound
* translation to point to the address stored in
* epf_test->db_bar.phys_addr, i.e., it calls set_bar()
* twice without ever calling clear_bar(), as calling
* clear_bar() would clear the BAR's PCI address assigned
* by the host. Thus, when disabling the doorbell, restore
* the inbound translation to point to the memory allocated
* for the BAR.
*/
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]);
if (ret)
goto set_status_err;
epf_test->db_bar_programmed = false;
}
status |= STATUS_DOORBELL_DISABLE_SUCCESS;
reg->status = cpu_to_le32(status);

View File

@@ -37,6 +37,7 @@
*/
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -66,9 +67,11 @@ static struct workqueue_struct *kpcintb_workqueue;
#define NTB_MW_OFFSET 2
#define DB_COUNT_MASK GENMASK(15, 0)
#define MSIX_ENABLE BIT(16)
#define MAX_DB_COUNT 32
#define MAX_MW 4
/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */
#define VNTB_PEER_DB_WORK_BUDGET 5
enum epf_ntb_bar {
BAR_CONFIG,
BAR_DB,
@@ -79,6 +82,15 @@ enum epf_ntb_bar {
VNTB_BAR_NUM,
};
enum epf_irq_slot {
EPF_IRQ_LINK = 0,
EPF_IRQ_RESERVED_DB, /* Historically skipped slot */
EPF_IRQ_DB_START,
};
#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
#define MAX_DB_COUNT 32
/*
* +--------------------------------------------------+ Base
* | |
@@ -129,11 +141,18 @@ struct epf_ntb {
u32 spad_count;
u64 mws_size[MAX_MW];
atomic64_t db;
atomic64_t peer_db_pending;
struct work_struct peer_db_work;
u32 vbus_number;
u16 vntb_pid;
u16 vntb_vid;
bool linkup;
/*
* True when doorbells are interrupt-driven (MSI or embedded), false
* when polled.
*/
bool msi_doorbell;
u32 spad_size;
@@ -261,10 +280,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
ntb = container_of(work, struct epf_ntb, cmd_handler.work);
for (i = 1; i < ntb->db_count && !ntb->msi_doorbell; i++) {
for (i = EPF_IRQ_DB_START; i < ntb->db_count && !ntb->msi_doorbell;
i++) {
if (ntb->epf_db[i]) {
atomic64_or(1 << (i - 1), &ntb->db);
ntb_db_event(&ntb->ntb, i);
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
ntb->epf_db[i] = 0;
}
}
@@ -330,10 +350,10 @@ static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data)
struct epf_ntb *ntb = data;
int i;
for (i = 1; i < ntb->db_count; i++)
for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++)
if (irq == ntb->epf->db_msg[i].virq) {
atomic64_or(1 << (i - 1), &ntb->db);
ntb_db_event(&ntb->ntb, i);
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
}
return IRQ_HANDLED;
@@ -483,7 +503,6 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
{
const struct pci_epc_features *epc_features;
struct device *dev;
u32 db_count;
int ret;
dev = &ntb->epf->dev;
@@ -495,14 +514,12 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
return -EINVAL;
}
db_count = ntb->db_count;
if (db_count > MAX_DB_COUNT) {
dev_err(dev, "DB count cannot be more than %d\n", MAX_DB_COUNT);
if (ntb->db_count < MIN_DB_COUNT || ntb->db_count > MAX_DB_COUNT) {
dev_err(dev, "DB count %d out of range (%d - %d)\n",
ntb->db_count, MIN_DB_COUNT, MAX_DB_COUNT);
return -EINVAL;
}
ntb->db_count = db_count;
if (epc_features->msi_capable) {
ret = pci_epc_set_msi(ntb->epf->epc,
ntb->epf->func_no,
@@ -517,6 +534,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
return 0;
}
static bool epf_ntb_db_irq_is_duplicated(const struct pci_epf *epf, unsigned int idx)
{
unsigned int i;
for (i = 0; i < idx; i++)
if (epf->db_msg[i].virq == epf->db_msg[idx].virq)
return true;
return false;
}
static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
struct pci_epf_bar *db_bar,
const struct pci_epc_features *epc_features,
@@ -533,9 +561,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
if (ret)
return ret;
/*
* The doorbell target may already be exposed by a platform-owned fixed
* BAR. In that case, we must reuse it and the requested db_bar must
* match.
*/
if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) {
ret = -EINVAL;
goto err_free_doorbell;
}
for (req = 0; req < ntb->db_count; req++) {
/* Avoid requesting duplicate handlers */
if (epf_ntb_db_irq_is_duplicated(epf, req))
continue;
ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler,
0, "pci_epf_vntb_db", ntb);
epf->db_msg[req].irq_flags, "pci_epf_vntb_db",
ntb);
if (ret) {
dev_err(&epf->dev,
@@ -545,6 +588,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
}
}
if (epf->db_msg[0].bar != NO_BAR) {
for (i = 0; i < ntb->db_count; i++) {
msg = &epf->db_msg[i].msg;
if (epf->db_msg[i].bar != barno) {
ret = -EINVAL;
goto err_free_irq;
}
ntb->reg->db_data[i] = msg->data;
ntb->reg->db_offset[i] = epf->db_msg[i].offset;
}
goto out;
}
/* Program inbound mapping for the doorbell */
msg = &epf->db_msg[0].msg;
high = 0;
@@ -591,6 +650,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
ntb->reg->db_offset[i] = offset;
}
out:
ntb->reg->db_entry_size = 0;
ntb->msi_doorbell = true;
@@ -598,9 +658,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
return 0;
err_free_irq:
for (req--; req >= 0; req--)
for (req--; req >= 0; req--) {
if (epf_ntb_db_irq_is_duplicated(epf, req))
continue;
free_irq(epf->db_msg[req].virq, ntb);
}
err_free_doorbell:
pci_epf_free_doorbell(ntb->epf);
return ret;
}
@@ -666,8 +730,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
if (ntb->msi_doorbell) {
int i;
for (i = 0; i < ntb->db_count; i++)
for (i = 0; i < ntb->db_count; i++) {
if (epf_ntb_db_irq_is_duplicated(ntb->epf, i))
continue;
free_irq(ntb->epf->db_msg[i].virq, ntb);
}
}
if (ntb->epf->db_msg)
@@ -920,6 +987,9 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler);
queue_work(kpcintb_workqueue, &ntb->cmd_handler.work);
atomic64_set(&ntb->peer_db_pending, 0);
enable_work(&ntb->peer_db_work);
return 0;
err_write_header:
@@ -943,11 +1013,18 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
static void epf_ntb_epc_cleanup(struct epf_ntb *ntb)
{
disable_delayed_work_sync(&ntb->cmd_handler);
disable_work_sync(&ntb->peer_db_work);
atomic64_set(&ntb->peer_db_pending, 0);
epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
epf_ntb_db_bar_clear(ntb);
epf_ntb_config_sspad_bar_clear(ntb);
}
static bool epf_ntb_epc_attached(struct epf_ntb *ntb)
{
return ntb->epf->epc || ntb->epf->sec_epc;
}
#define EPF_NTB_R(_name) \
static ssize_t epf_ntb_##_name##_show(struct config_item *item, \
char *page) \
@@ -967,6 +1044,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \
u32 val; \
int ret; \
\
if (epf_ntb_epc_attached(ntb)) \
return -EOPNOTSUPP; \
\
ret = kstrtou32(page, 0, &val); \
if (ret) \
return ret; \
@@ -1009,6 +1089,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \
u64 val; \
int ret; \
\
if (epf_ntb_epc_attached(ntb)) \
return -EOPNOTSUPP; \
\
ret = kstrtou64(page, 0, &val); \
if (ret) \
return ret; \
@@ -1047,6 +1130,9 @@ static ssize_t epf_ntb_##_name##_store(struct config_item *item, \
int val; \
int ret; \
\
if (epf_ntb_epc_attached(ntb)) \
return -EOPNOTSUPP; \
\
ret = kstrtoint(page, 0, &val); \
if (ret) \
return ret; \
@@ -1067,6 +1153,9 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item,
u32 val;
int ret;
if (epf_ntb_epc_attached(ntb))
return -EOPNOTSUPP;
ret = kstrtou32(page, 0, &val);
if (ret)
return ret;
@@ -1079,10 +1168,32 @@ static ssize_t epf_ntb_num_mws_store(struct config_item *item,
return len;
}
static ssize_t epf_ntb_db_count_store(struct config_item *item,
const char *page, size_t len)
{
struct config_group *group = to_config_group(item);
struct epf_ntb *ntb = to_epf_ntb(group);
u32 val;
int ret;
if (epf_ntb_epc_attached(ntb))
return -EOPNOTSUPP;
ret = kstrtou32(page, 0, &val);
if (ret)
return ret;
if (val < MIN_DB_COUNT || val > MAX_DB_COUNT)
return -EINVAL;
WRITE_ONCE(ntb->db_count, val);
return len;
}
EPF_NTB_R(spad_count)
EPF_NTB_W(spad_count)
EPF_NTB_R(db_count)
EPF_NTB_W(db_count)
EPF_NTB_R(num_mws)
EPF_NTB_R(vbus_number)
EPF_NTB_W(vbus_number)
@@ -1251,9 +1362,48 @@ static int vntb_epf_peer_mw_count(struct ntb_dev *ntb)
return ntb_ndev(ntb)->num_mws;
}
static int vntb_epf_db_vector_count(struct ntb_dev *ntb)
{
struct epf_ntb *ndev = ntb_ndev(ntb);
u32 db_count = READ_ONCE(ndev->db_count);
/*
* db_count is the total number of doorbell slots exposed to
* the peer, including:
* - slot #0 reserved for link events
* - slot #1 historically unused (kept for protocol compatibility)
*
* Report only usable per-vector doorbell interrupts.
*/
if (db_count < MIN_DB_COUNT || db_count > MAX_DB_COUNT)
return 0;
return db_count - EPF_IRQ_DB_START;
}
static u64 vntb_epf_db_valid_mask(struct ntb_dev *ntb)
{
return BIT_ULL(ntb_ndev(ntb)->db_count) - 1;
int nr_vec = vntb_epf_db_vector_count(ntb);
if (!nr_vec)
return 0;
return GENMASK_ULL(nr_vec - 1, 0);
}
static u64 vntb_epf_db_vector_mask(struct ntb_dev *ntb, int db_vector)
{
int nr_vec;
/*
* Doorbell vectors are numbered [0 .. nr_vec - 1], where nr_vec
* excludes the two reserved slots described above.
*/
nr_vec = vntb_epf_db_vector_count(ntb);
if (db_vector < 0 || db_vector >= nr_vec)
return 0;
return BIT_ULL(db_vector);
}
static int vntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
@@ -1357,22 +1507,84 @@ static int vntb_epf_peer_spad_write(struct ntb_dev *ndev, int pidx, int idx, u32
return 0;
}
static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits)
static void vntb_epf_peer_db_work(struct work_struct *work)
{
u32 interrupt_num = ffs(db_bits) + 1;
struct epf_ntb *ntb = ntb_ndev(ndev);
struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work);
struct pci_epf *epf = ntb->epf;
unsigned int budget = VNTB_PEER_DB_WORK_BUDGET;
u8 func_no, vfunc_no;
unsigned int db_bit;
u32 interrupt_num;
u64 db_bits;
int ret;
func_no = ntb->epf->func_no;
vfunc_no = ntb->epf->vfunc_no;
if (!epf || !epf->epc)
return;
ret = pci_epc_raise_irq(ntb->epf->epc, func_no, vfunc_no,
PCI_IRQ_MSI, interrupt_num + 1);
if (ret)
dev_err(&ntb->ntb.dev, "Failed to raise IRQ\n");
func_no = epf->func_no;
vfunc_no = epf->vfunc_no;
return ret;
/*
* Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()).
* Limit the number of snapshots handled per run so we don't monopolize
* the workqueue under a doorbell storm.
*/
while (budget--) {
db_bits = atomic64_xchg(&ntb->peer_db_pending, 0);
if (!db_bits)
return;
while (db_bits) {
/*
* pci_epc_raise_irq() for MSI expects a 1-based
* interrupt number. The first usable doorbell starts
* at EPF_IRQ_DB_START in the legacy slot layout.
*
* Legacy mapping (kept for compatibility):
*
* MSI #1 : link event (reserved)
* MSI #2 : unused (historical offset)
* MSI #3 : doorbell bit 0 (DB#0)
* MSI #4 : doorbell bit 1 (DB#1)
* ...
*
* Do not change this mapping to avoid breaking
* interoperability with older peers.
*/
db_bit = __ffs64(db_bits);
interrupt_num = db_bit + EPF_IRQ_DB_START + 1;
db_bits &= ~BIT_ULL(db_bit);
ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no,
PCI_IRQ_MSI, interrupt_num);
if (ret)
dev_err(&ntb->ntb.dev,
"Failed to raise IRQ for interrupt_num %u: %d\n",
interrupt_num, ret);
}
}
if (atomic64_read(&ntb->peer_db_pending))
queue_work(kpcintb_workqueue, &ntb->peer_db_work);
}
static int vntb_epf_peer_db_set(struct ntb_dev *ndev, u64 db_bits)
{
struct epf_ntb *ntb = ntb_ndev(ndev);
db_bits &= vntb_epf_db_valid_mask(ndev);
if (!db_bits)
return 0;
/*
* .peer_db_set() may be called from atomic context. pci_epc_raise_irq()
* can sleep (it takes epc->lock), so defer MSI raising to process
* context. Doorbell requests are coalesced in peer_db_pending.
*/
atomic64_or(db_bits, &ntb->peer_db_pending);
queue_work(kpcintb_workqueue, &ntb->peer_db_work);
return 0;
}
static u64 vntb_epf_db_read(struct ntb_dev *ndev)
@@ -1441,6 +1653,8 @@ static const struct ntb_dev_ops vntb_epf_ops = {
.spad_count = vntb_epf_spad_count,
.peer_mw_count = vntb_epf_peer_mw_count,
.db_valid_mask = vntb_epf_db_valid_mask,
.db_vector_count = vntb_epf_db_vector_count,
.db_vector_mask = vntb_epf_db_vector_mask,
.db_set_mask = vntb_epf_db_set_mask,
.mw_set_trans = vntb_epf_mw_set_trans,
.mw_clear_trans = vntb_epf_mw_clear_trans,
@@ -1619,6 +1833,10 @@ static int epf_ntb_probe(struct pci_epf *epf,
ntb->epf = epf;
ntb->vbus_number = 0xff;
INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work);
disable_work(&ntb->peer_db_work);
atomic64_set(&ntb->peer_db_pending, 0);
/* Initially, no bar is assigned */
for (i = 0; i < VNTB_BAR_NUM; i++)
ntb->epf_ntb_bar[i] = NO_BAR;

View File

@@ -6,8 +6,11 @@
* Author: Frank Li <Frank.Li@nxp.com>
*/
#include <linux/align.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/msi.h>
@@ -35,23 +38,116 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
pci_epc_put(epc);
}
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
{
const struct pci_epc_aux_resource *doorbell = NULL;
struct pci_epf_doorbell_msg *msg;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct irq_domain *domain;
void *msg;
int ret;
int i;
size_t map_size = 0, off = 0;
dma_addr_t iova_base = 0;
phys_addr_t phys_base;
int count, ret, i;
u64 addr;
/* TODO: Multi-EPF support */
if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
dev_err(dev, "MSI doorbell doesn't support multiple EPF\n");
count = pci_epc_get_aux_resources_count(epc, epf->func_no,
epf->vfunc_no);
if (count < 0)
return count;
if (!count)
return -ENODEV;
struct pci_epc_aux_resource *res __free(kfree) =
kcalloc(count, sizeof(*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
ret = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no,
res, count);
if (ret)
return ret;
/* TODO: Support multiple DOORBELL_MMIO resources per EPC. */
for (i = 0; i < count; i++) {
if (res[i].type != PCI_EPC_AUX_DOORBELL_MMIO)
continue;
doorbell = &res[i];
break;
}
if (!doorbell)
return -ENODEV;
addr = doorbell->phys_addr;
if (!IS_ALIGNED(addr, sizeof(u32)))
return -EINVAL;
/*
* Reuse the pre-exposed BAR window if available. Otherwise map the MMIO
* doorbell resource here. Any required IOMMU mapping is handled
* internally, matching the MSI doorbell semantics.
*/
if (doorbell->bar == NO_BAR) {
phys_base = addr & PAGE_MASK;
off = addr - phys_base;
map_size = PAGE_ALIGN(off + sizeof(u32));
iova_base = dma_map_resource(epc->dev.parent, phys_base,
map_size, DMA_FROM_DEVICE, 0);
if (dma_mapping_error(epc->dev.parent, iova_base))
return -EIO;
addr = iova_base + off;
}
if (epf->db_msg)
return -EBUSY;
msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL);
if (!msg) {
ret = -ENOMEM;
goto err_unmap;
}
/*
* Embedded doorbell backends (e.g. DesignWare eDMA interrupt emulation)
* typically provide a single IRQ and do not offer per-doorbell
* distinguishable address/data pairs. The EPC aux resource therefore
* exposes one DOORBELL_MMIO entry (u.db_mmio.irq).
*
* Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells.
* For such backends we replicate the same address/data for each entry
* and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them
* as equivalent "kick" doorbells.
*/
for (i = 0; i < num_db; i++)
msg[i] = (struct pci_epf_doorbell_msg) {
.msg.address_lo = (u32)addr,
.msg.address_hi = (u32)(addr >> 32),
.msg.data = doorbell->u.db_mmio.data,
.virq = doorbell->u.db_mmio.irq,
.irq_flags = IRQF_SHARED,
.type = PCI_EPF_DOORBELL_EMBEDDED,
.bar = doorbell->bar,
.offset = (doorbell->bar == NO_BAR) ? 0 :
doorbell->bar_offset,
.iova_base = iova_base,
.iova_size = map_size,
};
epf->num_db = num_db;
epf->db_msg = msg;
return 0;
err_unmap:
if (map_size)
dma_unmap_resource(epc->dev.parent, iova_base, map_size,
DMA_FROM_DEVICE, 0);
return ret;
}
static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db)
{
struct pci_epf_doorbell_msg *msg;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
struct irq_domain *domain;
int ret, i;
domain = of_msi_map_get_device_domain(epc->dev.parent, 0,
DOMAIN_BUS_PLATFORM_MSI);
@@ -74,6 +170,12 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
if (!msg)
return -ENOMEM;
for (i = 0; i < num_db; i++)
msg[i] = (struct pci_epf_doorbell_msg) {
.type = PCI_EPF_DOORBELL_MSI,
.bar = NO_BAR,
};
epf->num_db = num_db;
epf->db_msg = msg;
@@ -90,13 +192,60 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
for (i = 0; i < num_db; i++)
epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i);
return ret;
return 0;
}
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
{
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
int ret;
/* TODO: Multi-EPF support */
if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
dev_err(dev, "Doorbell doesn't support multiple EPF\n");
return -EINVAL;
}
if (epf->db_msg)
return -EBUSY;
ret = pci_epf_alloc_doorbell_msi(epf, num_db);
if (!ret)
return 0;
/*
* Fall back to embedded doorbell only when platform MSI is unavailable
* for this EPC.
*/
if (ret != -ENODEV)
return ret;
ret = pci_epf_alloc_doorbell_embedded(epf, num_db);
if (ret) {
dev_err(dev, "Failed to allocate doorbell: %d\n", ret);
return ret;
}
dev_info(dev, "Using embedded (DMA) doorbell fallback\n");
return 0;
}
EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell);
void pci_epf_free_doorbell(struct pci_epf *epf)
{
platform_device_msi_free_irqs_all(epf->epc->dev.parent);
struct pci_epf_doorbell_msg *msg0;
struct pci_epc *epc = epf->epc;
if (!epf->db_msg)
return;
msg0 = &epf->db_msg[0];
if (msg0->type == PCI_EPF_DOORBELL_MSI)
platform_device_msi_free_irqs_all(epf->epc->dev.parent);
else if (msg0->type == PCI_EPF_DOORBELL_EMBEDDED && msg0->iova_size)
dma_unmap_resource(epc->dev.parent, msg0->iova_base,
msg0->iova_size, DMA_FROM_DEVICE, 0);
kfree(epf->db_msg);
epf->db_msg = NULL;

View File

@@ -156,6 +156,86 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
}
EXPORT_SYMBOL_GPL(pci_epc_get_features);
/**
* pci_epc_get_aux_resources_count() - get the number of EPC-provided auxiliary resources
* @epc: EPC device
* @func_no: function number
* @vfunc_no: virtual function number
*
* Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control
* registers and/or descriptor memories can be exposed to the host by mapping
* them into BAR space. This helper queries how many such resources the backend
* provides.
*
* Return: the number of available resources on success, -EOPNOTSUPP if the
* backend does not support auxiliary resource queries, or another -errno on
* failure.
*/
int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
u8 vfunc_no)
{
int count;
if (!epc || !epc->ops)
return -EINVAL;
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->get_aux_resources_count)
return -EOPNOTSUPP;
mutex_lock(&epc->lock);
count = epc->ops->get_aux_resources_count(epc, func_no,
vfunc_no);
mutex_unlock(&epc->lock);
return count;
}
EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources_count);
/**
* pci_epc_get_aux_resources() - query EPC-provided auxiliary resources
* @epc: EPC device
* @func_no: function number
* @vfunc_no: virtual function number
* @resources: output array
* @num_resources: size of @resources array in entries
*
* Some EPC backends integrate auxiliary blocks (e.g. DMA engines) whose control
* registers and/or descriptor memories can be exposed to the host by mapping
* them into BAR space. This helper queries the backend for such resources.
*
* Return: 0 on success, -EOPNOTSUPP if the backend does not support auxiliary
* resource queries, or another -errno on failure.
*/
int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources)
{
int ret;
if (!resources || num_resources <= 0)
return -EINVAL;
if (!epc || !epc->ops)
return -EINVAL;
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->get_aux_resources)
return -EOPNOTSUPP;
mutex_lock(&epc->lock);
ret = epc->ops->get_aux_resources(epc, func_no, vfunc_no, resources,
num_resources);
mutex_unlock(&epc->lock);
return ret;
}
EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources);
/**
* pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped

View File

@@ -170,8 +170,7 @@ void pci_ide_init(struct pci_dev *pdev)
pci_read_config_dword(pdev, pos + PCI_IDE_SEL_CTL, &val);
if (val & PCI_IDE_SEL_CTL_EN)
continue;
val &= ~PCI_IDE_SEL_CTL_ID;
val |= FIELD_PREP(PCI_IDE_SEL_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
FIELD_MODIFY(PCI_IDE_SEL_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID);
pci_write_config_dword(pdev, pos + PCI_IDE_SEL_CTL, val);
}
@@ -182,8 +181,7 @@ void pci_ide_init(struct pci_dev *pdev)
pci_read_config_dword(pdev, pos, &val);
if (val & PCI_IDE_LINK_CTL_EN)
continue;
val &= ~PCI_IDE_LINK_CTL_ID;
val |= FIELD_PREP(PCI_IDE_LINK_CTL_ID, PCI_IDE_RESERVED_STREAM_ID);
FIELD_MODIFY(PCI_IDE_LINK_CTL_ID, &val, PCI_IDE_RESERVED_STREAM_ID);
pci_write_config_dword(pdev, pos, val);
}

View File

@@ -150,7 +150,7 @@ static void virtfn_remove_bus(struct pci_bus *physbus, struct pci_bus *virtbus)
pci_remove_bus(virtbus);
}
resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno)
resource_size_t pci_iov_resource_size(const struct pci_dev *dev, int resno)
{
if (!dev->is_physfn)
return 0;
@@ -938,16 +938,21 @@ static void sriov_restore_vf_rebar_state(struct pci_dev *dev)
return;
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
if (PCI_POSSIBLE_ERROR(ctrl))
return;
nbars = FIELD_GET(PCI_VF_REBAR_CTRL_NBAR_MASK, ctrl);
for (i = 0; i < nbars; i++, pos += 8) {
int bar_idx, size;
pci_read_config_dword(dev, pos + PCI_VF_REBAR_CTRL, &ctrl);
if (PCI_POSSIBLE_ERROR(ctrl))
return;
bar_idx = FIELD_GET(PCI_VF_REBAR_CTRL_BAR_IDX, ctrl);
size = pci_rebar_bytes_to_size(dev->sriov->barsz[bar_idx]);
ctrl &= ~PCI_VF_REBAR_CTRL_BAR_SIZE;
ctrl |= FIELD_PREP(PCI_VF_REBAR_CTRL_BAR_SIZE, size);
FIELD_MODIFY(PCI_VF_REBAR_CTRL_BAR_SIZE, &ctrl, size);
pci_write_config_dword(dev, pos + PCI_VF_REBAR_CTRL, ctrl);
}
}
@@ -1084,7 +1089,7 @@ void pci_iov_update_resource(struct pci_dev *dev, int resno)
}
}
resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
resource_size_t __weak pcibios_iov_resource_alignment(const struct pci_dev *dev,
int resno)
{
return pci_iov_resource_size(dev, resno);
@@ -1100,7 +1105,8 @@ resource_size_t __weak pcibios_iov_resource_alignment(struct pci_dev *dev,
* the VF BAR size multiplied by the number of VFs. The alignment
* is just the VF BAR size.
*/
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno)
resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev,
int resno)
{
return pcibios_iov_resource_alignment(dev, resno);
}

View File

@@ -201,8 +201,7 @@ static inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc,
u16 msgctl;
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
msgctl &= ~PCI_MSI_FLAGS_QSIZE;
msgctl |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, desc->pci.msi_attrib.multiple);
FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &msgctl, desc->pci.msi_attrib.multiple);
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo);
@@ -532,9 +531,8 @@ void __pci_restore_msi_state(struct pci_dev *dev)
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
pci_msi_update_mask(entry, 0, 0);
control &= ~PCI_MSI_FLAGS_QSIZE;
control |= PCI_MSI_FLAGS_ENABLE |
FIELD_PREP(PCI_MSI_FLAGS_QSIZE, entry->pci.msi_attrib.multiple);
FIELD_MODIFY(PCI_MSI_FLAGS_QSIZE, &control, entry->pci.msi_attrib.multiple);
control |= PCI_MSI_FLAGS_ENABLE;
pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
}
@@ -968,8 +966,7 @@ int pci_msix_write_tph_tag(struct pci_dev *pdev, unsigned int index, u16 tag)
if (!msi_desc || msi_desc->pci.msi_attrib.is_virtual)
return -ENXIO;
msi_desc->pci.msix_ctrl &= ~PCI_MSIX_ENTRY_CTRL_ST;
msi_desc->pci.msix_ctrl |= FIELD_PREP(PCI_MSIX_ENTRY_CTRL_ST, tag);
FIELD_MODIFY(PCI_MSIX_ENTRY_CTRL_ST, &msi_desc->pci.msix_ctrl, tag);
pci_msix_write_vector_ctrl(msi_desc, msi_desc->pci.msix_ctrl);
/* Flush the write */
readl(pci_msix_desc_addr(msi_desc));

View File

@@ -262,6 +262,9 @@ int pcim_p2pdma_init(struct pci_dev *pdev)
struct pci_p2pdma *p2p;
int i, ret;
if (pdev->non_mappable_bars)
return -EOPNOTSUPP;
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
if (p2p)
return 0;
@@ -318,7 +321,8 @@ struct p2pdma_provider *pcim_p2pdma_provider(struct pci_dev *pdev, int bar)
{
struct pci_p2pdma *p2p;
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM) ||
pdev->non_mappable_bars)
return NULL;
p2p = rcu_dereference_protected(pdev->p2pdma, 1);
@@ -548,6 +552,16 @@ static const struct pci_p2pdma_whitelist_entry {
{PCI_VENDOR_ID_INTEL, 0x2033, 0},
{PCI_VENDOR_ID_INTEL, 0x2020, 0},
{PCI_VENDOR_ID_INTEL, 0x09a2, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_SPR0, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAX_SPR0, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_GNRD, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DSA_DMR, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IAA_DMR, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_4XXX, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_401XX, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_402XX, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_420XX, 0},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QAT_6XXX, 0},
/* Google SoCs. */
{PCI_VENDOR_ID_GOOGLE, PCI_ANY_ID, 0},
{}

View File

@@ -519,13 +519,6 @@ static void pci_device_remove(struct device *dev)
/* Undo the runtime PM settings in local_pci_probe() */
pm_runtime_put_sync(dev);
/*
* If the device is still on, set the power state as "unknown",
* since it might change by the next time we load the driver.
*/
if (pci_dev->current_state == PCI_D0)
pci_dev->current_state = PCI_UNKNOWN;
/*
* We would love to complain here if pci_dev->is_enabled is set, that
* the driver should have called pci_disable_device(), but the
@@ -900,7 +893,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
if (!pm) {
pci_save_state(pci_dev);
goto Fixup;
goto set_unknown;
}
if (pm->suspend_noirq) {
@@ -952,6 +945,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
goto Fixup;
}
set_unknown:
pci_pm_set_unknown_state(pci_dev);
/*

View File

@@ -37,8 +37,6 @@
#define ARCH_PCI_DEV_GROUPS
#endif
static int sysfs_initialized; /* = 0 */
/* show configuration fields */
#define pci_config_attr(field, format_string) \
static ssize_t \
@@ -177,7 +175,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
max = PCI_BRIDGE_RESOURCES;
for (i = 0; i < max; i++) {
struct resource *res = &pci_dev->resource[i];
struct resource *res = pci_resource_n(pci_dev, i);
struct resource zerores = {};
/* For backwards compatibility */
@@ -674,11 +672,6 @@ static const struct attribute_group pcibus_group = {
.attrs = pcibus_attrs,
};
const struct attribute_group *pcibus_groups[] = {
&pcibus_group,
NULL,
};
static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -689,7 +682,7 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "%u\n", (pdev == vga_dev));
return sysfs_emit(buf, "%u\n",
!!(pdev->resource[PCI_ROM_RESOURCE].flags &
!!(pci_resource_flags(pdev, PCI_ROM_RESOURCE) &
IORESOURCE_ROM_SHADOW));
}
static DEVICE_ATTR_RO(boot_vga);
@@ -877,19 +870,6 @@ static const struct attribute_group pci_dev_config_attr_group = {
.bin_size = pci_dev_config_attr_bin_size,
};
/*
* llseek operation for mmappable PCI resources.
* May be left unused if the arch doesn't provide them.
*/
static __maybe_unused loff_t
pci_llseek_resource(struct file *filep,
struct kobject *kobj __always_unused,
const struct bin_attribute *attr,
loff_t offset, int whence)
{
return fixed_size_llseek(filep, offset, whence, attr->size);
}
#ifdef HAVE_PCI_LEGACY
/**
* pci_read_legacy_io - read byte(s) from legacy I/O port space
@@ -981,91 +961,148 @@ static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
}
/**
* pci_adjust_legacy_attr - adjustment of legacy file attributes
* @b: bus to create files under
* @mmap_type: I/O port or memory
*
* Stub implementation. Can be overridden by arch if necessary.
*/
void __weak pci_adjust_legacy_attr(struct pci_bus *b,
enum pci_mmap_state mmap_type)
bool __weak pci_legacy_has_sparse(struct pci_bus *bus,
enum pci_mmap_state type)
{
return false;
}
/**
* pci_create_legacy_files - create legacy I/O port and memory files
* @b: bus to create files under
*
* Some platforms allow access to legacy I/O port and ISA memory space on
* a per-bus basis. This routine creates the files and ties them into
* their associated read, write and mmap files from pci-sysfs.c
*
* On error unwind, but don't propagate the error to the caller
* as it is ok to set up the PCI bus without these files.
*/
void pci_create_legacy_files(struct pci_bus *b)
static inline umode_t __pci_legacy_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
enum pci_mmap_state type,
bool sparse)
{
int error;
struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
if (!sysfs_initialized)
return;
if (pci_legacy_has_sparse(bus, type) != sparse)
return 0;
b->legacy_io = kzalloc_objs(struct bin_attribute, 2, GFP_ATOMIC);
if (!b->legacy_io)
goto kzalloc_err;
sysfs_bin_attr_init(b->legacy_io);
b->legacy_io->attr.name = "legacy_io";
b->legacy_io->size = 0xffff;
b->legacy_io->attr.mode = 0600;
b->legacy_io->read = pci_read_legacy_io;
b->legacy_io->write = pci_write_legacy_io;
/* See pci_create_attr() for motivation */
b->legacy_io->llseek = pci_llseek_resource;
b->legacy_io->mmap = pci_mmap_legacy_io;
b->legacy_io->f_mapping = iomem_get_mapping;
pci_adjust_legacy_attr(b, pci_mmap_io);
error = device_create_bin_file(&b->dev, b->legacy_io);
if (error)
goto legacy_io_err;
/* Allocated above after the legacy_io struct */
b->legacy_mem = b->legacy_io + 1;
sysfs_bin_attr_init(b->legacy_mem);
b->legacy_mem->attr.name = "legacy_mem";
b->legacy_mem->size = 1024*1024;
b->legacy_mem->attr.mode = 0600;
b->legacy_mem->mmap = pci_mmap_legacy_mem;
/* See pci_create_attr() for motivation */
b->legacy_mem->llseek = pci_llseek_resource;
b->legacy_mem->f_mapping = iomem_get_mapping;
pci_adjust_legacy_attr(b, pci_mmap_mem);
error = device_create_bin_file(&b->dev, b->legacy_mem);
if (error)
goto legacy_mem_err;
return;
legacy_mem_err:
device_remove_bin_file(&b->dev, b->legacy_io);
legacy_io_err:
kfree(b->legacy_io);
b->legacy_io = NULL;
kzalloc_err:
dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n");
return a->attr.mode;
}
void pci_remove_legacy_files(struct pci_bus *b)
static umode_t pci_legacy_io_is_visible(struct kobject *kobj,
const struct bin_attribute *a, int n)
{
if (b->legacy_io) {
device_remove_bin_file(&b->dev, b->legacy_io);
device_remove_bin_file(&b->dev, b->legacy_mem);
kfree(b->legacy_io); /* both are allocated here */
}
return __pci_legacy_is_visible(kobj, a, pci_mmap_io, false);
}
static umode_t pci_legacy_io_sparse_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
return __pci_legacy_is_visible(kobj, a, pci_mmap_io, true);
}
static umode_t pci_legacy_mem_is_visible(struct kobject *kobj,
const struct bin_attribute *a, int n)
{
return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, false);
}
static umode_t pci_legacy_mem_sparse_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
return __pci_legacy_is_visible(kobj, a, pci_mmap_mem, true);
}
static loff_t pci_llseek_resource_legacy(struct file *filep,
struct kobject *kobj __always_unused,
const struct bin_attribute *attr,
loff_t offset, int whence)
{
return fixed_size_llseek(filep, offset, whence, attr->size);
}
static const struct bin_attribute pci_legacy_io_attr = {
.attr = { .name = "legacy_io", .mode = 0600 },
.size = PCI_LEGACY_IO_SIZE,
.read = pci_read_legacy_io,
.write = pci_write_legacy_io,
.mmap = pci_mmap_legacy_io,
.llseek = pci_llseek_resource_legacy,
.f_mapping = iomem_get_mapping,
};
static const struct bin_attribute pci_legacy_io_sparse_attr = {
.attr = { .name = "legacy_io_sparse", .mode = 0600 },
.size = PCI_LEGACY_IO_SIZE << 5,
.read = pci_read_legacy_io,
.write = pci_write_legacy_io,
.mmap = pci_mmap_legacy_io,
.llseek = pci_llseek_resource_legacy,
.f_mapping = iomem_get_mapping,
};
static const struct bin_attribute pci_legacy_mem_attr = {
.attr = { .name = "legacy_mem", .mode = 0600 },
.size = PCI_LEGACY_MEM_SIZE,
.mmap = pci_mmap_legacy_mem,
.llseek = pci_llseek_resource_legacy,
.f_mapping = iomem_get_mapping,
};
static const struct bin_attribute pci_legacy_mem_sparse_attr = {
.attr = { .name = "legacy_mem_sparse", .mode = 0600 },
.size = PCI_LEGACY_MEM_SIZE << 5,
.mmap = pci_mmap_legacy_mem,
.llseek = pci_llseek_resource_legacy,
.f_mapping = iomem_get_mapping,
};
static const struct bin_attribute *const pci_legacy_io_attrs[] = {
&pci_legacy_io_attr,
NULL,
};
static const struct bin_attribute *const pci_legacy_io_sparse_attrs[] = {
&pci_legacy_io_sparse_attr,
NULL,
};
static const struct bin_attribute *const pci_legacy_mem_attrs[] = {
&pci_legacy_mem_attr,
NULL,
};
static const struct bin_attribute *const pci_legacy_mem_sparse_attrs[] = {
&pci_legacy_mem_sparse_attr,
NULL,
};
static const struct attribute_group pci_legacy_io_group = {
.bin_attrs = pci_legacy_io_attrs,
.is_bin_visible = pci_legacy_io_is_visible,
};
static const struct attribute_group pci_legacy_io_sparse_group = {
.bin_attrs = pci_legacy_io_sparse_attrs,
.is_bin_visible = pci_legacy_io_sparse_is_visible,
};
static const struct attribute_group pci_legacy_mem_group = {
.bin_attrs = pci_legacy_mem_attrs,
.is_bin_visible = pci_legacy_mem_is_visible,
};
static const struct attribute_group pci_legacy_mem_sparse_group = {
.bin_attrs = pci_legacy_mem_sparse_attrs,
.is_bin_visible = pci_legacy_mem_sparse_is_visible,
};
#endif /* HAVE_PCI_LEGACY */
const struct attribute_group *pcibus_groups[] = {
&pcibus_group,
#ifdef HAVE_PCI_LEGACY
&pci_legacy_io_group,
&pci_legacy_io_sparse_group,
&pci_legacy_mem_group,
&pci_legacy_mem_sparse_group,
#endif
NULL,
};
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
/**
* pci_mmap_resource - map a PCI resource into user memory space
@@ -1082,20 +1119,24 @@ static int pci_mmap_resource(struct kobject *kobj, const struct bin_attribute *a
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
int bar = (unsigned long)attr->private;
enum pci_mmap_state mmap_type;
struct resource *res = &pdev->resource[bar];
int ret;
ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
if (ret)
return ret;
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
if (!pci_resource_is_mem(pdev, bar) &&
!(pci_resource_is_io(pdev, bar) && arch_can_pci_mmap_io()))
return -EIO;
if (pci_resource_is_mem(pdev, bar) &&
iomem_is_exclusive(pci_resource_start(pdev, bar)))
return -EINVAL;
if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS))
return -EINVAL;
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
mmap_type = pci_resource_is_mem(pdev, bar) ? pci_mmap_mem : pci_mmap_io;
return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
}
@@ -1123,6 +1164,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
int bar = (unsigned long)attr->private;
unsigned long port = off;
if (!pci_resource_is_io(pdev, bar))
return -EIO;
port += pci_resource_start(pdev, bar);
if (port > pci_resource_end(pdev, bar))
@@ -1157,14 +1201,14 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
#endif
}
static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj,
static ssize_t pci_read_resource(struct file *filp, struct kobject *kobj,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
return pci_resource_io(filp, kobj, attr, buf, off, count, false);
}
static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
static ssize_t pci_write_resource(struct file *filp, struct kobject *kobj,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
@@ -1177,127 +1221,192 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
return pci_resource_io(filp, kobj, attr, buf, off, count, true);
}
/**
* pci_remove_resource_files - cleanup resource files
* @pdev: dev to cleanup
*
* If we created resource files for @pdev, remove them from sysfs and
* free their resources.
*/
static void pci_remove_resource_files(struct pci_dev *pdev)
static loff_t pci_llseek_resource(struct file *filep,
struct kobject *kobj,
const struct bin_attribute *attr,
loff_t offset, int whence)
{
int i;
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
int bar = (unsigned long)attr->private;
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
struct bin_attribute *res_attr;
res_attr = pdev->res_attr[i];
if (res_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
res_attr = pdev->res_attr_wc[i];
if (res_attr) {
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
}
return fixed_size_llseek(filep, offset, whence,
pci_resource_len(pdev, bar));
}
static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
{
/* allocate attribute structure, piggyback attribute name */
int name_len = write_combine ? 13 : 10;
struct bin_attribute *res_attr;
char *res_attr_name;
int retval;
/*
* generic_file_llseek() consults f_mapping->host to determine
* the file size. As iomem_inode knows nothing about the
* attribute, it's not going to work, so override it as well.
*/
#if arch_can_pci_mmap_io()
# define __PCI_RESOURCE_IO_MMAP_ATTRS \
.f_mapping = iomem_get_mapping, \
.llseek = pci_llseek_resource, \
.mmap = pci_mmap_resource_uc,
#else
# define __PCI_RESOURCE_IO_MMAP_ATTRS
#endif
res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
if (!res_attr)
return -ENOMEM;
res_attr_name = (char *)(res_attr + 1);
sysfs_bin_attr_init(res_attr);
if (write_combine) {
sprintf(res_attr_name, "resource%d_wc", num);
res_attr->mmap = pci_mmap_resource_wc;
} else {
sprintf(res_attr_name, "resource%d", num);
if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
res_attr->read = pci_read_resource_io;
res_attr->write = pci_write_resource_io;
if (arch_can_pci_mmap_io())
res_attr->mmap = pci_mmap_resource_uc;
} else {
res_attr->mmap = pci_mmap_resource_uc;
}
}
if (res_attr->mmap) {
res_attr->f_mapping = iomem_get_mapping;
/*
* generic_file_llseek() consults f_mapping->host to determine
* the file size. As iomem_inode knows nothing about the
* attribute, it's not going to work, so override it as well.
*/
res_attr->llseek = pci_llseek_resource;
}
res_attr->attr.name = res_attr_name;
res_attr->attr.mode = 0600;
res_attr->size = pci_resource_len(pdev, num);
res_attr->private = (void *)(unsigned long)num;
retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
if (retval) {
kfree(res_attr);
return retval;
}
if (write_combine)
pdev->res_attr_wc[num] = res_attr;
else
pdev->res_attr[num] = res_attr;
return 0;
#define pci_dev_resource_io_attr(_bar) \
static const struct bin_attribute dev_resource##_bar##_io_attr = { \
.attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.read = pci_read_resource, \
.write = pci_write_resource, \
__PCI_RESOURCE_IO_MMAP_ATTRS \
}
/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @pdev: dev in question
*
* Walk the resources in @pdev creating files for each resource available.
*/
static int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
int retval;
#define pci_dev_resource_uc_attr(_bar) \
static const struct bin_attribute dev_resource##_bar##_uc_attr = { \
.attr = { .name = "resource" __stringify(_bar), .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.f_mapping = iomem_get_mapping, \
.llseek = pci_llseek_resource, \
.mmap = pci_mmap_resource_uc, \
}
#define pci_dev_resource_wc_attr(_bar) \
static const struct bin_attribute dev_resource##_bar##_wc_attr = { \
.attr = { .name = "resource" __stringify(_bar) "_wc", .mode = 0600 }, \
.private = (void *)(unsigned long)(_bar), \
.f_mapping = iomem_get_mapping, \
.llseek = pci_llseek_resource, \
.mmap = pci_mmap_resource_wc, \
}
static inline umode_t
__pci_resource_attr_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int bar, bool write_combine,
unsigned long flags)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
/* Skip devices with non-mappable BARs */
if (pdev->non_mappable_bars)
return 0;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_STD_NUM_BARS; i++) {
if (!pci_resource_len(pdev, bar))
return 0;
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
if ((pci_resource_flags(pdev, bar) & flags) != flags)
return 0;
retval = pci_create_attr(pdev, i, 0);
/* for prefetchable resources, create a WC mappable file */
if (!retval && arch_can_pci_mmap_wc() &&
pdev->resource[i].flags & IORESOURCE_PREFETCH)
retval = pci_create_attr(pdev, i, 1);
if (retval) {
pci_remove_resource_files(pdev);
return retval;
}
}
return 0;
if (write_combine && !arch_can_pci_mmap_wc())
return 0;
return a->attr.mode;
}
#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */
int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
static umode_t pci_dev_resource_io_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
return __pci_resource_attr_is_visible(kobj, a, n, false,
IORESOURCE_IO);
}
static umode_t pci_dev_resource_uc_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
return __pci_resource_attr_is_visible(kobj, a, n, false,
IORESOURCE_MEM);
}
static umode_t pci_dev_resource_wc_is_visible(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
return __pci_resource_attr_is_visible(kobj, a, n, true,
IORESOURCE_MEM | IORESOURCE_PREFETCH);
}
static size_t pci_dev_resource_bin_size(struct kobject *kobj,
const struct bin_attribute *a,
int n)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
return pci_resource_len(pdev, n);
}
pci_dev_resource_io_attr(0);
pci_dev_resource_io_attr(1);
pci_dev_resource_io_attr(2);
pci_dev_resource_io_attr(3);
pci_dev_resource_io_attr(4);
pci_dev_resource_io_attr(5);
pci_dev_resource_uc_attr(0);
pci_dev_resource_uc_attr(1);
pci_dev_resource_uc_attr(2);
pci_dev_resource_uc_attr(3);
pci_dev_resource_uc_attr(4);
pci_dev_resource_uc_attr(5);
pci_dev_resource_wc_attr(0);
pci_dev_resource_wc_attr(1);
pci_dev_resource_wc_attr(2);
pci_dev_resource_wc_attr(3);
pci_dev_resource_wc_attr(4);
pci_dev_resource_wc_attr(5);
static const struct bin_attribute *const pci_dev_resource_io_attrs[] = {
&dev_resource0_io_attr,
&dev_resource1_io_attr,
&dev_resource2_io_attr,
&dev_resource3_io_attr,
&dev_resource4_io_attr,
&dev_resource5_io_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_uc_attrs[] = {
&dev_resource0_uc_attr,
&dev_resource1_uc_attr,
&dev_resource2_uc_attr,
&dev_resource3_uc_attr,
&dev_resource4_uc_attr,
&dev_resource5_uc_attr,
NULL,
};
static const struct bin_attribute *const pci_dev_resource_wc_attrs[] = {
&dev_resource0_wc_attr,
&dev_resource1_wc_attr,
&dev_resource2_wc_attr,
&dev_resource3_wc_attr,
&dev_resource4_wc_attr,
&dev_resource5_wc_attr,
NULL,
};
static const struct attribute_group pci_dev_resource_io_attr_group = {
.bin_attrs = pci_dev_resource_io_attrs,
.is_bin_visible = pci_dev_resource_io_is_visible,
.bin_size = pci_dev_resource_bin_size,
};
static const struct attribute_group pci_dev_resource_uc_attr_group = {
.bin_attrs = pci_dev_resource_uc_attrs,
.is_bin_visible = pci_dev_resource_uc_is_visible,
.bin_size = pci_dev_resource_bin_size,
};
static const struct attribute_group pci_dev_resource_wc_attr_group = {
.bin_attrs = pci_dev_resource_wc_attrs,
.is_bin_visible = pci_dev_resource_wc_is_visible,
.bin_size = pci_dev_resource_bin_size,
};
static const struct attribute_group *pci_dev_resource_attr_groups[] = {
&pci_dev_resource_io_attr_group,
&pci_dev_resource_uc_attr_group,
&pci_dev_resource_wc_attr_group,
NULL,
};
#else
#define pci_dev_resource_attr_groups NULL
#endif
/**
@@ -1309,18 +1418,19 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
* @off: file offset
* @count: number of byte in input
*
* writing anything except 0 enables it
* Writing a boolean value enables or disables the ROM display.
*/
static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj,
const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
bool enable;
if ((off == 0) && (*buf == '0') && (count == 2))
pdev->rom_attr_enabled = 0;
else
pdev->rom_attr_enabled = 1;
if (kstrtobool(buf, &enable))
return -EINVAL;
pdev->rom_attr_enabled = enable;
return count;
}
@@ -1552,6 +1662,7 @@ static const struct attribute_group pci_dev_reset_method_attr_group = {
.is_visible = pci_dev_reset_attr_is_visible,
};
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
static ssize_t __resource_resize_show(struct device *dev, int n, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -1576,6 +1687,9 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
int ret;
u16 cmd;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (kstrtoul(buf, 0, &size) < 0)
return -EINVAL;
@@ -1598,14 +1712,17 @@ static ssize_t __resource_resize_store(struct device *dev, int n,
pci_write_config_word(pdev, PCI_COMMAND,
cmd & ~PCI_COMMAND_MEMORY);
pci_remove_resource_files(pdev);
sysfs_remove_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups);
ret = pci_resize_resource(pdev, n, size, 0);
if (ret)
pci_warn(pdev, "Failed to resize BAR %d: %pe\n",
n, ERR_PTR(ret));
pci_assign_unassigned_bus_resources(bus);
if (pci_create_resource_files(pdev))
pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n");
if (sysfs_create_groups(&pdev->dev.kobj, pci_dev_resource_attr_groups))
pci_warn(pdev, "Failed to recreate resource groups after BAR resizing\n");
pci_write_config_word(pdev, PCI_COMMAND, cmd);
pm_put:
@@ -1648,7 +1765,7 @@ static struct attribute *resource_resize_attrs[] = {
NULL,
};
static umode_t resource_resize_is_visible(struct kobject *kobj,
static umode_t resource_resize_attr_is_visible(struct kobject *kobj,
struct attribute *a, int n)
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
@@ -1656,54 +1773,11 @@ static umode_t resource_resize_is_visible(struct kobject *kobj,
return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode;
}
static const struct attribute_group pci_dev_resource_resize_group = {
static const struct attribute_group pci_dev_resource_resize_attr_group = {
.attrs = resource_resize_attrs,
.is_visible = resource_resize_is_visible,
.is_visible = resource_resize_attr_is_visible,
};
int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
{
if (!sysfs_initialized)
return -EACCES;
return pci_create_resource_files(pdev);
}
/**
* pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
* @pdev: device whose entries we should free
*
* Cleanup when @pdev is removed from sysfs.
*/
void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
{
if (!sysfs_initialized)
return;
pci_remove_resource_files(pdev);
}
static int __init pci_sysfs_init(void)
{
struct pci_dev *pdev = NULL;
struct pci_bus *pbus = NULL;
int retval;
sysfs_initialized = 1;
for_each_pci_dev(pdev) {
retval = pci_create_sysfs_dev_files(pdev);
if (retval) {
pci_dev_put(pdev);
return retval;
}
}
while ((pbus = pci_find_next_bus(pbus)))
pci_create_legacy_files(pbus);
return 0;
}
late_initcall(pci_sysfs_init);
#endif
static struct attribute *pci_dev_dev_attrs[] = {
&dev_attr_boot_vga.attr,
@@ -1774,6 +1848,12 @@ static const struct attribute_group pci_dev_group = {
const struct attribute_group *pci_dev_groups[] = {
&pci_dev_group,
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
&pci_dev_resource_io_attr_group,
&pci_dev_resource_uc_attr_group,
&pci_dev_resource_wc_attr_group,
&pci_dev_resource_resize_attr_group,
#endif
&pci_dev_config_attr_group,
&pci_dev_rom_attr_group,
&pci_dev_reset_attr_group,
@@ -1785,7 +1865,6 @@ const struct attribute_group *pci_dev_groups[] = {
#ifdef CONFIG_ACPI
&pci_dev_acpi_attr_group,
#endif
&pci_dev_resource_resize_group,
ARCH_PCI_DEV_GROUPS
NULL,
};

View File

@@ -33,6 +33,7 @@
#include <asm/dma.h>
#include <linux/aer.h>
#include <linux/bitfield.h>
#include <linux/suspend.h>
#include "pci.h"
DEFINE_MUTEX(pci_slot_mutex);
@@ -120,17 +121,7 @@ unsigned long pci_hotplug_bus_size = DEFAULT_HOTPLUG_BUS_SIZE;
/* PCIe MPS/MRRS strategy; can be overridden by kernel command-line param */
#ifdef CONFIG_PCIE_BUS_TUNE_OFF
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_TUNE_OFF;
#elif defined CONFIG_PCIE_BUS_SAFE
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_SAFE;
#elif defined CONFIG_PCIE_BUS_PERFORMANCE
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE;
#elif defined CONFIG_PCIE_BUS_PEER2PEER
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PEER2PEER;
#else
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_DEFAULT;
#endif
/*
* The default CLS is used if arch didn't set CLS explicitly and not
@@ -1253,8 +1244,8 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
}
if (delay > timeout) {
pci_warn(dev, "not ready %dms after %s; giving up\n",
delay - 1, reset_type);
pci_err(dev, "not ready %dms after %s; giving up\n",
delay - 1, reset_type);
return -ENOTTY;
}
@@ -1300,7 +1291,18 @@ int pci_power_up(struct pci_dev *dev)
bool need_restore;
pci_power_t state;
u16 pmcsr;
int ret;
/*
* When setting power state to D0, platform_pci_set_power_state()
* ensures main power is on. If it puts the device in D0, it also
* completes any required delays after the transition; if it leaves
* the device in D1, D2, or D3hot, we use the PM Capability to
* transition to D0.
*
* In all cases, the device is either Configuration-Ready or
* inaccessible upon return.
*/
platform_pci_set_power_state(dev, PCI_D0);
if (!dev->pm_cap) {
@@ -1341,10 +1343,19 @@ int pci_power_up(struct pci_dev *dev)
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0);
/* Mandatory transition delays; see PCI PM 1.2. */
if (state == PCI_D3hot)
if (state == PCI_D3hot) {
pci_dev_d3_sleep(dev);
else if (state == PCI_D2)
if (!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) {
ret = pci_dev_wait(dev, "power up D3hot->D0uninitialized",
PCIE_RESET_READY_POLL_MS);
if (ret) {
dev->current_state = PCI_D3cold;
return -EIO;
}
}
} else if (state == PCI_D2) {
udelay(PCI_PM_D2_DELAY);
}
end:
dev->current_state = PCI_D0;
@@ -1514,9 +1525,6 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool
pci_power_name(dev->current_state),
pci_power_name(state));
if (dev->bus->self)
pcie_aspm_pm_state_change(dev->bus->self, locked);
return 0;
}
@@ -1764,7 +1772,7 @@ int pci_save_state(struct pci_dev *dev)
EXPORT_SYMBOL(pci_save_state);
static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
u32 saved_val, int retry, bool force)
u32 saved_val, bool force)
{
u32 val;
@@ -1772,52 +1780,42 @@ static void pci_restore_config_dword(struct pci_dev *pdev, int offset,
if (!force && val == saved_val)
return;
for (;;) {
pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n",
offset, val, saved_val);
pci_write_config_dword(pdev, offset, saved_val);
if (retry-- <= 0)
return;
pci_dbg(pdev, "restore config %#04x: %#010x -> %#010x\n", offset, val,
saved_val);
pci_read_config_dword(pdev, offset, &val);
if (val == saved_val)
return;
mdelay(1);
}
pci_write_config_dword(pdev, offset, saved_val);
}
static void pci_restore_config_space_range(struct pci_dev *pdev,
int start, int end, int retry,
bool force)
int start, int end, bool force)
{
int index;
for (index = end; index >= start; index--)
pci_restore_config_dword(pdev, 4 * index,
pdev->saved_config_space[index],
retry, force);
force);
}
static void pci_restore_config_space(struct pci_dev *pdev)
{
if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
pci_restore_config_space_range(pdev, 10, 15, 0, false);
pci_restore_config_space_range(pdev, 10, 15, false);
/* Restore BARs before the command register. */
pci_restore_config_space_range(pdev, 4, 9, 10, false);
pci_restore_config_space_range(pdev, 0, 3, 0, false);
pci_restore_config_space_range(pdev, 4, 9, false);
pci_restore_config_space_range(pdev, 0, 3, false);
} else if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
pci_restore_config_space_range(pdev, 12, 15, 0, false);
pci_restore_config_space_range(pdev, 12, 15, false);
/*
* Force rewriting of prefetch registers to avoid S3 resume
* issues on Intel PCI bridges that occur when these
* registers are not explicitly written.
*/
pci_restore_config_space_range(pdev, 9, 11, 0, true);
pci_restore_config_space_range(pdev, 0, 8, 0, false);
pci_restore_config_space_range(pdev, 9, 11, true);
pci_restore_config_space_range(pdev, 0, 8, false);
} else {
pci_restore_config_space_range(pdev, 0, 15, 0, false);
pci_restore_config_space_range(pdev, 0, 15, false);
}
}
@@ -2899,6 +2897,40 @@ void pci_config_pm_runtime_put(struct pci_dev *pdev)
pm_runtime_put_sync(parent);
}
/**
* pci_suspend_retains_context - Check if the platform can retain the device
* context during system suspend
* @pdev: PCI device to check
*
* Return: true if the platform can guarantee to retain the device context,
* false otherwise.
*/
bool pci_suspend_retains_context(struct pci_dev *pdev)
{
struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
/*
* If the platform firmware (like ACPI) is involved at the end of
* system suspend, device context may not be retained.
*/
if (pm_suspend_via_firmware())
return false;
/*
* Some host bridges power off the PHY to enter deep low-power
* modes during system suspend. Exiting L1SS from this condition
* may violate timing requirements and result in Link Down (LDn),
* which causes a reset of the device. On such platforms, the
* endpoint must be prepared for context loss.
*/
if (bridge && bridge->broken_l1ss_resume)
return false;
/* Assume that the context is retained by default */
return true;
}
EXPORT_SYMBOL_GPL(pci_suspend_retains_context);
static const struct dmi_system_id bridge_d3_blacklist[] = {
#ifdef CONFIG_X86
{
@@ -5770,8 +5802,7 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc)
if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC))
return -EIO;
cmd &= ~PCI_X_CMD_MAX_READ;
cmd |= FIELD_PREP(PCI_X_CMD_MAX_READ, v);
FIELD_MODIFY(PCI_X_CMD_MAX_READ, &cmd, v);
if (pci_write_config_word(dev, cap + PCI_X_CMD, cmd))
return -EIO;
}

View File

@@ -71,10 +71,11 @@ struct pcie_tlp_log;
#define PCIE_TLP_FMT_4DW_DATA 0x03 /* 4DW header, with data */
/* Type of TLP; PCIe r7.0, sec 2.2.1 */
#define PCIE_TLP_TYPE_CFG0_RD 0x04 /* Config Type 0 Read Request */
#define PCIE_TLP_TYPE_CFG0_WR 0x04 /* Config Type 0 Write Request */
#define PCIE_TLP_TYPE_CFG1_RD 0x05 /* Config Type 1 Read Request */
#define PCIE_TLP_TYPE_CFG1_WR 0x05 /* Config Type 1 Write Request */
#define PCIE_TLP_TYPE_MEM_RDWR 0x00 /* Memory Read/Write Request */
#define PCIE_TLP_TYPE_IO_RDWR 0x02 /* I/O Read/Write Request */
#define PCIE_TLP_TYPE_CFG0_RDWR 0x04 /* Config Type 0 Read/Write Request */
#define PCIE_TLP_TYPE_CFG1_RDWR 0x05 /* Config Type 1 Read/Write Request */
#define PCIE_TLP_TYPE_MSG 0x10 /* Message With/Without data Request */
/* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */
#define PCIE_MSG_TYPE_R_RC 0
@@ -358,14 +359,6 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; }
int pci_hp_add_bridge(struct pci_dev *dev);
bool pci_hp_spurious_link_change(struct pci_dev *pdev);
#if defined(CONFIG_SYSFS) && defined(HAVE_PCI_LEGACY)
void pci_create_legacy_files(struct pci_bus *bus);
void pci_remove_legacy_files(struct pci_bus *bus);
#else
static inline void pci_create_legacy_files(struct pci_bus *bus) { }
static inline void pci_remove_legacy_files(struct pci_bus *bus) { }
#endif
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
extern struct mutex pci_slot_mutex;
@@ -392,17 +385,17 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
}
#ifdef HAVE_PCI_LEGACY
bool pci_legacy_has_sparse(struct pci_bus *bus, enum pci_mmap_state type);
#endif
#ifdef CONFIG_SYSFS
int pci_create_sysfs_dev_files(struct pci_dev *pdev);
void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern const struct attribute_group *pci_dev_groups[];
extern const struct attribute_group *pci_dev_attr_groups[];
extern const struct attribute_group *pcibus_groups[];
extern const struct attribute_group *pci_bus_groups[];
extern const struct attribute_group pci_doe_sysfs_group;
#else
static inline int pci_create_sysfs_dev_files(struct pci_dev *pdev) { return 0; }
static inline void pci_remove_sysfs_dev_files(struct pci_dev *pdev) { }
#define pci_dev_groups NULL
#define pci_dev_attr_groups NULL
#define pcibus_groups NULL
@@ -419,7 +412,7 @@ static inline bool pci_is_cardbus_bridge(struct pci_dev *dev)
return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
}
#ifdef CONFIG_CARDBUS
unsigned long pci_cardbus_resource_alignment(struct resource *res);
unsigned long pci_cardbus_resource_alignment(const struct resource *res);
int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
struct list_head *realloc_head);
int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
@@ -428,7 +421,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
int pci_setup_cardbus(char *str);
#else
static inline unsigned long pci_cardbus_resource_alignment(struct resource *res)
static inline unsigned long pci_cardbus_resource_alignment(const struct resource *res)
{
return 0;
}
@@ -515,7 +508,7 @@ int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,
void __pci_bus_size_bridges(struct pci_bus *bus,
struct list_head *realloc_head);
void __pci_bus_assign_resources(const struct pci_bus *bus,
struct list_head *realloc_head,
struct list_head *add_list,
struct list_head *fail_head);
bool pci_bus_clip_resource(struct pci_dev *dev, int idx);
void pci_walk_bus_locked(struct pci_bus *top,
@@ -947,7 +940,8 @@ int pci_iov_init(struct pci_dev *dev);
void pci_iov_release(struct pci_dev *dev);
void pci_iov_remove(struct pci_dev *dev);
void pci_iov_update_resource(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev,
int resno);
void pci_restore_iov_state(struct pci_dev *dev);
int pci_iov_bus_range(struct pci_bus *bus);
void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size);
@@ -981,7 +975,7 @@ static inline int pci_iov_init(struct pci_dev *dev)
static inline void pci_iov_release(struct pci_dev *dev) { }
static inline void pci_iov_remove(struct pci_dev *dev) { }
static inline void pci_iov_update_resource(struct pci_dev *dev, int resno) { }
static inline resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev,
static inline resource_size_t pci_sriov_resource_alignment(const struct pci_dev *dev,
int resno)
{
return 0;
@@ -1043,17 +1037,8 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { }
static inline void pci_resume_ptm(struct pci_dev *dev) { }
#endif
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
struct resource *res)
{
int resno = pci_resource_num(dev, res);
if (pci_resource_is_iov(resno))
return pci_sriov_resource_alignment(dev, resno);
if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
return pci_cardbus_resource_alignment(res);
return resource_alignment(res);
}
resource_size_t pci_resource_alignment(const struct pci_dev *dev,
const struct resource *res);
resource_size_t pci_min_window_alignment(struct pci_bus *bus,
unsigned long type);
@@ -1110,6 +1095,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked);
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
void pci_configure_ltr(struct pci_dev *pdev);
void pci_bridge_reconfigure_ltr(struct pci_dev *pdev);
void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value);
#else
static inline void pcie_aspm_remove_cap(struct pci_dev *pdev, u32 lnkcap) { }
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
@@ -1118,6 +1104,11 @@ static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev, bool locked)
static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
static inline void pci_configure_ltr(struct pci_dev *pdev) { }
static inline void pci_bridge_reconfigure_ltr(struct pci_dev *pdev) { }
static inline void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value)
{
*scale = 0;
*value = 0;
}
#endif
#ifdef CONFIG_PCIE_ECRC

View File

@@ -525,6 +525,46 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
return 0;
}
/**
* pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields
* @t_power_on_us: T_POWER_ON time in microseconds
* @scale: Encoded T_POWER_ON Scale (0..2)
* @value: Encoded T_POWER_ON Value
*
* T_POWER_ON is encoded as:
* T_POWER_ON(us) = scale_unit(us) * value
*
* where scale_unit is selected by @scale:
* 0: 2us
* 1: 10us
* 2: 100us
*
* If @t_power_on_us exceeds the maximum representable value, the result
* is clamped to the largest encodable T_POWER_ON.
*
* See PCIe r7.0, sec 7.8.3.2.
*/
void pcie_encode_t_power_on(u32 t_power_on_us, u8 *scale, u8 *value)
{
u8 maxv = FIELD_MAX(PCI_L1SS_CAP_P_PWR_ON_VALUE);
/* T_POWER_ON_Value ("value") is a 5-bit field with max value of 31. */
if (t_power_on_us <= 2 * maxv) {
*scale = 0; /* Value times 2us */
*value = DIV_ROUND_UP(t_power_on_us, 2);
} else if (t_power_on_us <= 10 * maxv) {
*scale = 1; /* Value times 10us */
*value = DIV_ROUND_UP(t_power_on_us, 10);
} else if (t_power_on_us <= 100 * maxv) {
*scale = 2; /* value times 100us */
*value = DIV_ROUND_UP(t_power_on_us, 100);
} else {
*scale = 2;
*value = maxv;
}
}
EXPORT_SYMBOL(pcie_encode_t_power_on);
/*
* Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1
* register. Ports enter L1.2 when the most recent LTR value is greater

View File

@@ -152,8 +152,7 @@ static int __pci_enable_ptm(struct pci_dev *dev)
pci_read_config_dword(dev, ptm + PCI_PTM_CTRL, &ctrl);
ctrl |= PCI_PTM_CTRL_ENABLE;
ctrl &= ~PCI_PTM_GRANULARITY_MASK;
ctrl |= FIELD_PREP(PCI_PTM_GRANULARITY_MASK, dev->ptm_granularity);
FIELD_MODIFY(PCI_PTM_GRANULARITY_MASK, &ctrl, dev->ptm_granularity);
if (dev->ptm_root)
ctrl |= PCI_PTM_CTRL_ROOT;

View File

@@ -660,6 +660,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
{
INIT_LIST_HEAD(&bridge->windows);
INIT_LIST_HEAD(&bridge->dma_ranges);
INIT_LIST_HEAD(&bridge->ports);
/*
* We assume we can manage these PCIe features. Some systems may
@@ -1073,9 +1074,6 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
dev_err(&bus->dev, "failed to add bus: %d\n", err);
}
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(bus);
if (parent)
dev_info(parent, "PCI host bridge to bus %s\n", name);
else
@@ -1281,9 +1279,6 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
dev_err(&child->dev, "failed to add bus: %d\n", ret);
}
/* Create legacy_io and legacy_mem files for this bus */
pci_create_legacy_files(child);
return child;
}

View File

@@ -18,6 +18,7 @@
#include "pci.h"
static int proc_initialized; /* = 0 */
static DEFINE_MUTEX(pci_proc_lock);
static loff_t proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
{
@@ -416,40 +417,64 @@ static const struct seq_operations proc_bus_pci_devices_op = {
static struct proc_dir_entry *proc_bus_pci_dir;
int pci_proc_attach_device(struct pci_dev *dev)
static int __pci_proc_attach_bus(struct pci_bus *bus)
{
struct pci_bus *bus = dev->bus;
struct proc_dir_entry *e;
struct proc_dir_entry *dir;
char name[16];
lockdep_assert_held(&pci_proc_lock);
if (!proc_initialized)
return -EACCES;
if (!bus->procdir) {
if (pci_proc_domain(bus)) {
sprintf(name, "%04x:%02x", pci_domain_nr(bus),
bus->number);
} else {
sprintf(name, "%02x", bus->number);
}
bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
if (!bus->procdir)
return -ENOMEM;
}
if (bus->procdir)
return 0;
if (pci_proc_domain(bus))
sprintf(name, "%04x:%02x", pci_domain_nr(bus), bus->number);
else
sprintf(name, "%02x", bus->number);
dir = proc_mkdir(name, proc_bus_pci_dir);
if (!dir)
return -ENOMEM;
bus->procdir = dir;
return 0;
}
int pci_proc_attach_device(struct pci_dev *dev)
{
struct pci_bus *bus = dev->bus;
struct proc_dir_entry *entry;
char name[16];
int ret;
guard(mutex)(&pci_proc_lock);
if (dev->procent)
return 0;
ret = __pci_proc_attach_bus(bus);
if (ret)
return ret;
sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
e = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR, bus->procdir,
&proc_bus_pci_ops, dev);
if (!e)
entry = proc_create_data(name, S_IFREG | S_IRUGO | S_IWUSR,
bus->procdir, &proc_bus_pci_ops, dev);
if (!entry)
return -ENOMEM;
proc_set_size(e, dev->cfg_size);
dev->procent = e;
proc_set_size(entry, dev->cfg_size);
dev->procent = entry;
return 0;
}
int pci_proc_detach_device(struct pci_dev *dev)
{
guard(mutex)(&pci_proc_lock);
proc_remove(dev->procent);
dev->procent = NULL;
return 0;
@@ -457,19 +482,27 @@ int pci_proc_detach_device(struct pci_dev *dev)
int pci_proc_detach_bus(struct pci_bus *bus)
{
guard(mutex)(&pci_proc_lock);
proc_remove(bus->procdir);
bus->procdir = NULL;
return 0;
}
static int __init pci_proc_init(void)
{
struct pci_dev *dev = NULL;
proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
proc_create_seq("devices", 0, proc_bus_pci_dir,
&proc_bus_pci_devices_op);
proc_initialized = 1;
scoped_guard(mutex, &pci_proc_lock) {
proc_bus_pci_dir = proc_mkdir("bus/pci", NULL);
proc_create_seq("devices", 0, proc_bus_pci_dir,
&proc_bus_pci_devices_op);
proc_initialized = 1;
}
pci_lock_rescan_remove();
for_each_pci_dev(dev)
pci_proc_attach_device(dev);
pci_unlock_rescan_remove();
return 0;
}

View File

@@ -139,6 +139,48 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
/*
* Check whether the pwrctrl device really needs to be created or not. The
* pwrctrl device will only be created if the node satisfies below requirements:
*
* 1. Presence of compatible property with "pci" prefix to match against the
* pwrctrl driver (AND)
* 2. At least one of the power supplies defined in the devicetree node of the
* device (OR) in the remote endpoint parent node to indicate pwrctrl
* requirement.
*/
static bool pci_pwrctrl_is_required(struct device_node *np)
{
struct device_node *endpoint;
const char *compat;
int ret;
ret = of_property_read_string(np, "compatible", &compat);
if (ret < 0)
return false;
if (!strstarts(compat, "pci"))
return false;
if (of_pci_supply_present(np))
return true;
if (of_graph_is_present(np)) {
for_each_endpoint_of_node(np, endpoint) {
struct device_node *remote __free(device_node) =
of_graph_get_remote_port_parent(endpoint);
if (remote) {
if (of_pci_supply_present(remote)) {
of_node_put(endpoint);
return true;
}
}
}
}
return false;
}
static int __pci_pwrctrl_power_off_device(struct device *dev)
{
struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
@@ -157,14 +199,19 @@ static void pci_pwrctrl_power_off_device(struct device_node *np)
for_each_available_child_of_node_scoped(np, child)
pci_pwrctrl_power_off_device(child);
if (!pci_pwrctrl_is_required(np))
return;
pdev = of_find_device_by_node(np);
if (!pdev)
return;
if (device_is_bound(&pdev->dev)) {
ret = __pci_pwrctrl_power_off_device(&pdev->dev);
if (ret)
dev_err(&pdev->dev, "Failed to power off device: %d", ret);
scoped_guard(device, &pdev->dev) {
if (device_is_bound(&pdev->dev)) {
ret = __pci_pwrctrl_power_off_device(&pdev->dev);
if (ret)
dev_err(&pdev->dev, "Failed to power off device: %d", ret);
}
}
platform_device_put(pdev);
@@ -205,7 +252,7 @@ static int __pci_pwrctrl_power_on_device(struct device *dev)
static int pci_pwrctrl_power_on_device(struct device_node *np)
{
struct platform_device *pdev;
int ret;
int ret = 0;
for_each_available_child_of_node_scoped(np, child) {
ret = pci_pwrctrl_power_on_device(child);
@@ -213,16 +260,21 @@ static int pci_pwrctrl_power_on_device(struct device_node *np)
return ret;
}
if (!pci_pwrctrl_is_required(np))
return 0;
pdev = of_find_device_by_node(np);
if (!pdev)
return 0;
if (device_is_bound(&pdev->dev)) {
ret = __pci_pwrctrl_power_on_device(&pdev->dev);
} else {
/* FIXME: Use blocking wait instead of probe deferral */
dev_dbg(&pdev->dev, "driver is not bound\n");
ret = -EPROBE_DEFER;
scoped_guard(device, &pdev->dev) {
if (device_is_bound(&pdev->dev)) {
ret = __pci_pwrctrl_power_on_device(&pdev->dev);
} else {
/* FIXME: Use blocking wait instead of probe deferral */
dev_dbg(&pdev->dev, "driver is not bound\n");
ret = -EPROBE_DEFER;
}
}
platform_device_put(pdev);
@@ -268,48 +320,6 @@ int pci_pwrctrl_power_on_devices(struct device *parent)
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices);
/*
* Check whether the pwrctrl device really needs to be created or not. The
* pwrctrl device will only be created if the node satisfies below requirements:
*
* 1. Presence of compatible property with "pci" prefix to match against the
* pwrctrl driver (AND)
* 2. At least one of the power supplies defined in the devicetree node of the
* device (OR) in the remote endpoint parent node to indicate pwrctrl
* requirement.
*/
static bool pci_pwrctrl_is_required(struct device_node *np)
{
struct device_node *endpoint;
const char *compat;
int ret;
ret = of_property_read_string(np, "compatible", &compat);
if (ret < 0)
return false;
if (!strstarts(compat, "pci"))
return false;
if (of_pci_supply_present(np))
return true;
if (of_graph_is_present(np)) {
for_each_endpoint_of_node(np, endpoint) {
struct device_node *remote __free(device_node) =
of_graph_get_remote_port_parent(endpoint);
if (remote) {
if (of_pci_supply_present(remote)) {
of_node_put(endpoint);
return true;
}
}
}
}
return false;
}
static int pci_pwrctrl_create_device(struct device_node *np,
struct device *parent)
{

View File

@@ -80,11 +80,10 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta)
* Restrict the speed to 2.5GT/s then with the Target Link Speed field,
* request a retrain and check the result.
*
* If this turns out successful and we know by the Vendor:Device ID it is
* safe to do so, then lift the restriction, letting the devices negotiate
* a higher speed. Also check for a similar 2.5GT/s speed restriction the
* firmware may have already arranged and lift it with ports that already
* report their data link being up.
* If this turns out successful, or where a 2.5GT/s speed restriction has
* been previously arranged by the firmware and the port reports its link
* already being up, lift the restriction, in a hope it is safe to do so,
* letting the devices negotiate a higher speed.
*
* Otherwise revert the speed to the original setting and request a retrain
* again to remove any residual state, ignoring the result as it's supposed
@@ -95,51 +94,39 @@ static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta)
*/
int pcie_failed_link_retrain(struct pci_dev *dev)
{
static const struct pci_device_id ids[] = {
{ PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */
{}
};
u16 lnksta, lnkctl2;
u16 lnksta, lnkctl2, oldlnkctl2;
enum pci_bus_speed speed_cap;
int ret = -ENOTTY;
if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) ||
!pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting)
return ret;
speed_cap = pcie_get_speed_cap(dev);
if (speed_cap <= PCIE_SPEED_2_5GT)
return ret;
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2);
if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) {
u16 oldlnkctl2;
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2);
ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false);
if (ret) {
pci_info(dev, "retraining failed\n");
pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2),
true);
return ret;
}
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
if (ret)
goto err;
}
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2);
if ((lnksta & PCI_EXP_LNKSTA_DLLLA) &&
(lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT &&
pci_match_id(ids, dev)) {
u32 lnkcap;
if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT) {
pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n");
pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
ret = pcie_set_target_speed(dev, PCIE_LNKCAP_SLS2SPEED(lnkcap), false);
if (ret) {
pci_info(dev, "retraining failed\n");
return ret;
}
ret = pcie_set_target_speed(dev, speed_cap, false);
if (ret)
goto err;
}
return ret;
err:
pci_info(dev, "retraining failed\n");
pcie_set_target_speed(dev, PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2), true);
return ret;
}
@@ -3790,6 +3777,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0034, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003e, quirk_no_bus_reset);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1103, quirk_no_bus_reset); /* WCN6855 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x1107, quirk_no_bus_reset); /* WCN7850 */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_QCOM, 0x0308, quirk_no_bus_reset); /* SDX62/SDX65 */
/*
* Root port on some Cavium CN8xxx chips do not successfully complete a bus
@@ -5605,6 +5595,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
* Intel 82579LM Gigabit Ethernet Controller 0x1502
* Intel 82579V Gigabit Ethernet Controller 0x1503
* Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter
* Mediatek MT7925 802.11be PCI Express Wireless Network Adapter
*/
static void quirk_no_flr(struct pci_dev *dev)
{
@@ -5619,6 +5610,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr);
/* FLR may cause the SolidRun SNET DPU (rev 0x1) to hang */
static void quirk_no_flr_snet(struct pci_dev *dev)

View File

@@ -211,8 +211,7 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
return pos;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size);
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
if (pci_resource_is_iov(bar))
@@ -231,6 +230,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
return;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
if (PCI_POSSIBLE_ERROR(ctrl))
return;
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
for (i = 0; i < nbars; i++, pos += 8) {
@@ -238,11 +240,13 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
int bar_idx, size;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
if (PCI_POSSIBLE_ERROR(ctrl))
return;
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
res = pci_resource_n(pdev, bar_idx);
size = pci_rebar_bytes_to_size(resource_size(res));
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
FIELD_MODIFY(PCI_REBAR_CTRL_BAR_SIZE, &ctrl, size);
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
}
}

View File

@@ -26,7 +26,6 @@ static void pci_stop_dev(struct pci_dev *dev)
device_release_driver(&dev->dev);
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
of_pci_remove_node(dev);
}
@@ -66,8 +65,6 @@ void pci_remove_bus(struct pci_bus *bus)
list_del(&bus->node);
pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
pci_remove_legacy_files(bus);
if (bus->ops->remove_bus)
bus->ops->remove_bus(bus);

View File

@@ -5,13 +5,40 @@
* (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
* (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
*/
#include <linux/align.h>
#include <linux/bits.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include "pci.h"
#define PCI_ROM_HEADER_SIZE 0x1A
#define PCI_ROM_POINTER_TO_DATA_STRUCT 0x18
#define PCI_ROM_LAST_IMAGE_INDICATOR 0x15
#define PCI_ROM_LAST_IMAGE_INDICATOR_BIT BIT(7)
#define PCI_ROM_IMAGE_LEN 0x10
#define PCI_ROM_IMAGE_SECTOR_SIZE SZ_512
#define PCI_ROM_IMAGE_SIGNATURE 0xAA55
/* Data structure signature is "PCIR" in ASCII representation */
#define PCI_ROM_DATA_STRUCT_SIGNATURE 0x52494350
#define PCI_ROM_DATA_STRUCT_LEN 0x0A
/*
* Per PCI Firmware r3.3, sec 5.1.3, a conformant PCI Data Structure is at
* least 24 bytes (0x18), large enough to cover every fixed field this
* driver reads (up to the Indicator byte at offset 0x15). Reject smaller
* device-claimed lengths so the follow-up readers in pci_get_rom_size()
* cannot escape the mapped ROM window.
*/
#define PCI_ROM_DATA_STRUCT_MIN_LEN 0x18
/**
* pci_enable_rom - enable ROM decoding for a PCI device
* @pdev: PCI device to enable
@@ -69,6 +96,91 @@ void pci_disable_rom(struct pci_dev *pdev)
}
EXPORT_SYMBOL_GPL(pci_disable_rom);
static bool pci_rom_header_valid(struct pci_dev *pdev, void __iomem *image,
void __iomem *rom, size_t size,
bool expect_valid)
{
unsigned long rom_end = (unsigned long)rom + size - 1;
unsigned long header_end;
u16 signature;
/*
* Per PCI Firmware r3.3, sec 5.1, each image must start on a
* 512-byte boundary and must contain the PCI Expansion ROM header.
* Because @rom is page-aligned (returned by ioremap()), checking
* 512-byte alignment of @image is equivalent to enforcing the
* spec's sector-aligned layout within the ROM. This also
* satisfies the natural-alignment requirement of readw() on archs
* such as arm64 that disallow unaligned IOMEM access.
*/
if (!IS_ALIGNED((unsigned long)image, PCI_ROM_IMAGE_SECTOR_SIZE))
return false;
if (check_add_overflow((unsigned long)image, PCI_ROM_HEADER_SIZE - 1,
&header_end))
return false;
if (image < rom || header_end > rom_end)
return false;
/* Standard PCI ROMs start out with these bytes 55 AA */
signature = readw(image);
if (signature != PCI_ROM_IMAGE_SIGNATURE) {
if (expect_valid) {
pci_info(pdev, "Invalid PCI ROM header signature: expecting %#06x, got %#06x\n",
PCI_ROM_IMAGE_SIGNATURE, signature);
} else {
pci_info(pdev, "No more images in PCI ROM\n");
}
return false;
}
return true;
}
static bool pci_rom_data_struct_valid(struct pci_dev *pdev, void __iomem *pds,
void __iomem *rom, size_t size)
{
unsigned long rom_end = (unsigned long)rom + size - 1;
unsigned long end;
u32 signature;
u16 data_len;
/*
* Some CPU architectures require IOMEM access addresses to be
* aligned, for example arm64, so since we're about to call
* readl(), check here for 4-byte alignment.
*/
if (!IS_ALIGNED((unsigned long)pds, 4))
return false;
if (check_add_overflow((unsigned long)pds, PCI_ROM_DATA_STRUCT_LEN + 1,
&end))
return false;
if (pds < rom || end > rom_end)
return false;
signature = readl(pds);
if (signature != PCI_ROM_DATA_STRUCT_SIGNATURE) {
pci_info(pdev, "Invalid PCI ROM data signature: expecting %#010x, got %#010x\n",
PCI_ROM_DATA_STRUCT_SIGNATURE, signature);
return false;
}
data_len = readw(pds + PCI_ROM_DATA_STRUCT_LEN);
if (data_len < PCI_ROM_DATA_STRUCT_MIN_LEN || data_len == U16_MAX)
return false;
if (check_add_overflow((unsigned long)pds, data_len - 1, &end))
return false;
if (end > rom_end)
return false;
return true;
}
/**
* pci_get_rom_size - obtain the actual size of the ROM image
* @pdev: target PCI device
@@ -84,37 +196,28 @@ static size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom,
size_t size)
{
void __iomem *image;
int last_image;
unsigned int length;
bool last_image;
image = rom;
do {
void __iomem *pds;
/* Standard PCI ROMs start out with these bytes 55 AA */
if (readw(image) != 0xAA55) {
pci_info(pdev, "Invalid PCI ROM header signature: expecting 0xaa55, got %#06x\n",
readw(image));
if (!pci_rom_header_valid(pdev, image, rom, size, true))
break;
}
/* get the PCI data structure and check its "PCIR" signature */
pds = image + readw(image + 24);
if (readl(pds) != 0x52494350) {
pci_info(pdev, "Invalid PCI ROM data signature: expecting 0x52494350, got %#010x\n",
readl(pds));
/* Get the PCI data structure and check its "PCIR" signature */
pds = image + readw(image + PCI_ROM_POINTER_TO_DATA_STRUCT);
if (!pci_rom_data_struct_valid(pdev, pds, rom, size))
break;
}
last_image = readb(pds + 21) & 0x80;
length = readw(pds + 16);
image += length * 512;
/* Avoid iterating through memory outside the resource window */
if (image >= rom + size)
last_image = readb(pds + PCI_ROM_LAST_IMAGE_INDICATOR) &
PCI_ROM_LAST_IMAGE_INDICATOR_BIT;
length = readw(pds + PCI_ROM_IMAGE_LEN);
image += length * PCI_ROM_IMAGE_SECTOR_SIZE;
if (!last_image &&
!pci_rom_header_valid(pdev, image, rom, size, false))
break;
if (!last_image) {
if (readw(image) != 0xAA55) {
pci_info(pdev, "No more image in the PCI ROM\n");
break;
}
}
} while (length && !last_image);
/* never return a size larger than the PCI resource window */

View File

@@ -756,13 +756,13 @@ static void __assign_resources_sorted(struct list_head *head,
}
static void pdev_assign_resources_sorted(struct pci_dev *dev,
struct list_head *add_head,
struct list_head *add_list,
struct list_head *fail_head)
{
LIST_HEAD(head);
pdev_sort_resources(dev, &head);
__assign_resources_sorted(&head, add_head, fail_head);
__assign_resources_sorted(&head, add_list, fail_head);
}
@@ -1501,14 +1501,14 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev)
}
}
void __pci_bus_assign_resources(const struct pci_bus *bus,
struct list_head *realloc_head,
struct list_head *fail_head)
static void __pci_bus_assign_resources_one(const struct pci_bus *bus,
struct list_head *add_list,
struct list_head *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
pbus_assign_resources_sorted(bus, realloc_head, fail_head);
pbus_assign_resources_sorted(bus, add_list, fail_head);
list_for_each_entry(dev, &bus->devices, bus_list) {
pdev_assign_fixed_resources(dev);
@@ -1517,7 +1517,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
if (!b)
continue;
__pci_bus_assign_resources(b, realloc_head, fail_head);
__pci_bus_assign_resources_one(b, add_list, fail_head);
switch (dev->hdr_type) {
case PCI_HEADER_TYPE_BRIDGE:
@@ -1537,6 +1537,16 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
}
}
void __pci_bus_assign_resources(const struct pci_bus *bus,
struct list_head *add_list,
struct list_head *fail_head)
{
__pci_bus_assign_resources_one(bus, add_list, fail_head);
if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
pci_dev_res_free_list(add_list);
}
void pci_bus_assign_resources(const struct pci_bus *bus)
{
__pci_bus_assign_resources(bus, NULL, NULL);
@@ -1612,20 +1622,19 @@ void pci_bus_claim_resources(struct pci_bus *b)
}
EXPORT_SYMBOL(pci_bus_claim_resources);
static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
struct list_head *add_head,
static void __pci_bridge_assign_resources(struct pci_dev *bridge,
struct list_head *add_list,
struct list_head *fail_head)
{
struct pci_bus *b;
pdev_assign_resources_sorted((struct pci_dev *)bridge,
add_head, fail_head);
pdev_assign_resources_sorted(bridge, add_list, fail_head);
b = bridge->subordinate;
if (!b)
return;
__pci_bus_assign_resources(b, add_head, fail_head);
__pci_bus_assign_resources(b, add_list, fail_head);
switch (bridge->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
@@ -1641,6 +1650,9 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
pci_domain_nr(b), b->number);
break;
}
if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
pci_dev_res_free_list(add_list);
}
static void pci_bridge_release_resources(struct pci_bus *bus,
@@ -2205,8 +2217,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
/* Depth last, allocate resources and update the hardware. */
__pci_bus_assign_resources(bus, add_list, &fail_head);
if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
pci_dev_res_free_list(add_list);
tried_times++;
/* Any device complain? */
@@ -2268,8 +2278,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
pci_bridge_distribute_available_resources(bridge, &add_list);
__pci_bridge_assign_resources(bridge, &add_list, &fail_head);
if (WARN_ON_ONCE(!list_empty(&add_list)))
pci_dev_res_free_list(&add_list);
tried_times++;
if (list_empty(&fail_head))
@@ -2303,7 +2311,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
unsigned long type = res->flags;
struct pci_dev_resource *dev_res;
struct pci_dev *bridge = NULL;
LIST_HEAD(added);
LIST_HEAD(add_list);
LIST_HEAD(failed);
unsigned int i;
int ret = 0;
@@ -2337,10 +2345,8 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
if (!bridge)
return -ENOENT;
__pci_bus_size_bridges(bridge->subordinate, &added);
__pci_bridge_assign_resources(bridge, &added, &failed);
if (WARN_ON_ONCE(!list_empty(&added)))
pci_dev_res_free_list(&added);
__pci_bus_size_bridges(bridge->subordinate, &add_list);
__pci_bridge_assign_resources(bridge, &add_list, &failed);
if (!list_empty(&failed)) {
if (pci_required_resource_failed(&failed, type))
@@ -2473,7 +2479,5 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
__pci_bus_size_bridges(dev->subordinate, &add_list);
up_read(&pci_bus_sem);
__pci_bus_assign_resources(bus, &add_list, NULL);
if (WARN_ON_ONCE(!list_empty(&add_list)))
pci_dev_res_free_list(&add_list);
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources);

View File

@@ -22,7 +22,7 @@
static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
unsigned long pci_cardbus_resource_alignment(struct resource *res)
unsigned long pci_cardbus_resource_alignment(const struct resource *res)
{
if (res->flags & IORESOURCE_IO)
return pci_cardbus_io_size;
@@ -253,8 +253,7 @@ int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
* yenta.c forces a secondary latency timer of 176.
* Copy that behaviour here.
*/
buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER);
FIELD_MODIFY(PCI_SEC_LATENCY_TIMER_MASK, &buses, CARDBUS_LATENCY_TIMER);
/* We need to blast all three values with a single write */
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);

View File

@@ -169,6 +169,8 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
return -EBUSY;
}
pci_dbg(dev, "%s %pR: claiming\n", res_name, res);
return 0;
}
EXPORT_SYMBOL(pci_claim_resource);
@@ -246,6 +248,18 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
return 0;
}
resource_size_t pci_resource_alignment(const struct pci_dev *dev,
const struct resource *res)
{
int resno = pci_resource_num(dev, res);
if (pci_resource_is_iov(resno))
return pci_sriov_resource_alignment(dev, resno);
if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
return pci_cardbus_resource_alignment(res);
return resource_alignment(res);
}
/*
* For mem bridge windows, try to relocate tail remainder space to space
* before res->start if there's enough free space there. This enables

Some files were not shown because too many files have changed in this diff Show More